New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Break reference cycles in the edge triggers #727
Merged
Merged
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Rationale: * ClockCycles shares only one line of implementation with _Edge, and it's easier just to repeat it. * ClockCycles doesn't make much sense as a subclass of _Edge * _Edge is private anyway, so no consumer of the public API will care
eric-wieser
commented
Dec 16, 2018
_RisingOrFallingEdge.__init__(self, signal, rising=False) | ||
|
||
class Edge(_EdgeBase): | ||
""" Triggers on either edge in a signal """ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a bonus, this now gives these names docstrings, where previously help(cocotb.triggers.FallingEdge)
would give nothing
Previously there was a cyclic reference between `Signal._r_edge -> _Edge.signal -> ...` This takes the same approach as 94eeaba, using a weak dictionary to ensure edges keep their signal alive, but not vice versa.
eric-wieser
force-pushed
the
edge-weakref
branch
from
December 16, 2018 06:24
b5b5069
to
435f7c9
Compare
Thanks @imphil for the fast approval! |
eric-wieser
added a commit
to eric-wieser/cocotb
that referenced
this pull request
Dec 20, 2018
…by a misunderstanding of how __new__ works In those patches, I declared classes that were roughly ```python class SomeClass: def __new__(cls, arg): try: return existing[arg] except KeyError: return super(SomeClass, cls).__new__(cls, arg) def __init__(self, arg): self.arg = arg self.state = 0 ``` This approach has a fatal flaw (cocotbgh-729), with function calls shown in the following code: ```python A = SomeClass(1) # SomeClass.__new__(SomeClass, 1) -> A # A.__init__(1) B = SomeClass(1) # SomeClass.__new__(SomeClass, 1) -> A # reusing the existing instance # A.__init__(1) # uh oh, we reset A.state ``` We need to override class-construction without allowing `__init__` to run a second time. One option would be to just remove `__init__` entirely, and move the contents into `__new__`. The other option, which I take in this patch, is to introduce a metaclass overriding the `__call__` operator on class types. I'm not convinced this is the best approach, but it does fix the problem.
eric-wieser
added a commit
to eric-wieser/cocotb
that referenced
this pull request
Dec 20, 2018
…by a misunderstanding of how __new__ works In those patches, I declared classes that were roughly ```python class SomeClass: def __new__(cls, arg): try: return existing[arg] except KeyError: return super(SomeClass, cls).__new__(cls, arg) def __init__(self, arg): self.arg = arg self.state = 0 ``` This approach has a fatal flaw (cocotbgh-729), with function calls shown in the following code: ```python A = SomeClass(1) # SomeClass.__new__(SomeClass, 1) -> A # A.__init__(1) B = SomeClass(1) # SomeClass.__new__(SomeClass, 1) -> A # reusing the existing instance # A.__init__(1) # uh oh, we reset A.state ``` We need to override class-construction without allowing `__init__` to run a second time. This introduces a simple metaclass to allow overriding the behavior of `RisingEdge.__call__(...)`
eric-wieser
added a commit
to eric-wieser/cocotb
that referenced
this pull request
Jan 7, 2019
…by a misunderstanding of how __new__ works In those patches, I declared classes that were roughly ```python class SomeClass: def __new__(cls, arg): try: return existing[arg] except KeyError: return super(SomeClass, cls).__new__(cls, arg) def __init__(self, arg): self.arg = arg self.state = 0 ``` This approach has a fatal flaw (cocotbgh-729), with function calls shown in the following code: ```python A = SomeClass(1) # SomeClass.__new__(SomeClass, 1) -> A # A.__init__(1) B = SomeClass(1) # SomeClass.__new__(SomeClass, 1) -> A # reusing the existing instance # A.__init__(1) # uh oh, we reset A.state ``` We need to override class-construction without allowing `__init__` to run a second time. This introduces a simple metaclass to allow overriding the behavior of `RisingEdge.__call__(...)`
eric-wieser
added a commit
to eric-wieser/cocotb
that referenced
this pull request
Jan 7, 2019
…by a misunderstanding of how __new__ works In those patches, I declared classes that were roughly ```python class SomeClass: def __new__(cls, arg): try: return existing[arg] except KeyError: return super(SomeClass, cls).__new__(cls, arg) def __init__(self, arg): self.arg = arg self.state = 0 ``` This approach has a fatal flaw (cocotbgh-729), with function calls shown in the following code: ```python A = SomeClass(1) # SomeClass.__new__(SomeClass, 1) -> A # A.__init__(1) B = SomeClass(1) # SomeClass.__new__(SomeClass, 1) -> A # reusing the existing instance # A.__init__(1) # uh oh, we reset A.state ``` We need to override class-construction without allowing `__init__` to run a second time. One option would be to just remove `__init__` entirely, and move the contents into `__new__`. The other option, which I take in this patch, is to introduce a metaclass overriding the `__call__` operator on class types. I'm not convinced this is the best approach, but it does fix the problem.
eric-wieser
added a commit
to eric-wieser/cocotb
that referenced
this pull request
Jan 7, 2019
…by a misunderstanding of how __new__ works In those patches, I declared classes that were roughly ```python class SomeClass: def __new__(cls, arg): try: return existing[arg] except KeyError: return super(SomeClass, cls).__new__(cls, arg) def __init__(self, arg): self.arg = arg self.state = 0 ``` This approach has a fatal flaw (cocotbgh-729), with function calls shown in the following code: ```python A = SomeClass(1) # SomeClass.__new__(SomeClass, 1) -> A # A.__init__(1) B = SomeClass(1) # SomeClass.__new__(SomeClass, 1) -> A # reusing the existing instance # A.__init__(1) # uh oh, we reset A.state ``` We need to override class-construction without allowing `__init__` to run a second time. This introduces a simple metaclass to allow overriding the behavior of `RisingEdge.__call__(...)`
eric-wieser
added a commit
to eric-wieser/cocotb
that referenced
this pull request
Jan 7, 2019
…by a misunderstanding of how __new__ works In those patches, I declared classes that were roughly ```python class SomeClass: def __new__(cls, arg): try: return existing[arg] except KeyError: return super(SomeClass, cls).__new__(cls, arg) def __init__(self, arg): self.arg = arg self.state = 0 ``` This approach has a fatal flaw (cocotbgh-729), with function calls shown in the following code: ```python A = SomeClass(1) B = SomeClass(1) ``` We need to override class-construction without allowing `__init__` to run a second time. One option would be to just remove `__init__` entirely, and move the contents into `__new__`. The other option, which I take in this patch, is to introduce a metaclass overriding the `__call__` operator on class types. I'm not convinced this is the best approach, but it does fix the problem.
eric-wieser
added a commit
to eric-wieser/cocotb
that referenced
this pull request
Jan 7, 2019
…by a misunderstanding of how __new__ works In those patches, I declared classes that were roughly ```python class SomeClass: def __new__(cls, arg): try: return existing[arg] except KeyError: return super(SomeClass, cls).__new__(cls, arg) def __init__(self, arg): self.arg = arg self.state = 0 ``` This approach has a fatal flaw (cocotbgh-729), with function calls shown in the following code: ```python A = SomeClass(1) # SomeClass.__new__(SomeClass, 1) -> A # A.__init__(1) B = SomeClass(1) # SomeClass.__new__(SomeClass, 1) -> A # reusing the existing instance # A.__init__(1) # uh oh, we reset A.state ``` For the sake of simplicity, this commit undoes the merger between the factory functions and the classes themselves, while keeping the weakref behavior. In future, it would be good to explore the more complex approach taken in cocotbgh-733, which ensures that `isinstance(Join(), Join)`, which this patch does not. Since this property was not true before cocotbgh-723 and cocotbgh-727 anyway, we can afford to revert that improvement in order to get a release out the door.
eric-wieser
added a commit
to eric-wieser/cocotb
that referenced
this pull request
Jan 8, 2019
…by a misunderstanding of how __new__ works In those patches, I declared classes that were roughly ```python class SomeClass: def __new__(cls, arg): try: return existing[arg] except KeyError: return super(SomeClass, cls).__new__(cls, arg) def __init__(self, arg): self.arg = arg self.state = 0 ``` This approach has a fatal flaw (cocotbgh-729), with function calls shown in the following code: ```python A = SomeClass(1) # SomeClass.__new__(SomeClass, 1) -> A # A.__init__(1) B = SomeClass(1) # SomeClass.__new__(SomeClass, 1) -> A # reusing the existing instance # A.__init__(1) # uh oh, we reset A.state ``` For the sake of simplicity, this commit undoes the merger between the factory functions and the classes themselves, while keeping the weakref behavior. In future, it would be good to explore the more complex approach taken in cocotbgh-733, which ensures that `isinstance(Join(), Join)`, which this patch does not. Since this property was not true before cocotbgh-723 and cocotbgh-727 anyway, we can afford to revert that improvement in order to get a release out the door.
eric-wieser
added a commit
to eric-wieser/cocotb
that referenced
this pull request
Jan 8, 2019
…by a misunderstanding of how __new__ works In those patches, I declared classes that were roughly ```python class SomeClass: def __new__(cls, arg): try: return existing[arg] except KeyError: return super(SomeClass, cls).__new__(cls, arg) def __init__(self, arg): self.arg = arg self.state = 0 ``` This approach has a fatal flaw (cocotbgh-729), with function calls shown in the following code: ```python A = SomeClass(1) # SomeClass.__new__(SomeClass, 1) -> A # A.__init__(1) B = SomeClass(1) # SomeClass.__new__(SomeClass, 1) -> A # reusing the existing instance # A.__init__(1) # uh oh, we reset A.state ``` For the sake of simplicity, this commit undoes the merger between the factory functions and the classes themselves, while keeping the weakref behavior. In future, it would be good to explore the more complex approach taken in cocotbgh-733, which ensures that `isinstance(Join(), Join)`, which this patch does not. Since this property was not true before cocotbgh-723 and cocotbgh-727 anyway, we can afford to revert that improvement in order to get a release out the door.
ktbarrett
pushed a commit
to ktbarrett/cocotb
that referenced
this pull request
Mar 6, 2020
Break reference cycles in the edge triggers
ktbarrett
pushed a commit
to ktbarrett/cocotb
that referenced
this pull request
Mar 6, 2020
…by a misunderstanding of how __new__ works In those patches, I declared classes that were roughly ```python class SomeClass: def __new__(cls, arg): try: return existing[arg] except KeyError: return super(SomeClass, cls).__new__(cls, arg) def __init__(self, arg): self.arg = arg self.state = 0 ``` This approach has a fatal flaw (cocotbgh-729), with function calls shown in the following code: ```python A = SomeClass(1) B = SomeClass(1) ``` We need to override class-construction without allowing `__init__` to run a second time. One option would be to just remove `__init__` entirely, and move the contents into `__new__`. The other option, which I take in this patch, is to introduce a metaclass overriding the `__call__` operator on class types. I'm not convinced this is the best approach, but it does fix the problem.
ktbarrett
pushed a commit
to ktbarrett/cocotb
that referenced
this pull request
Mar 6, 2020
Fix a regression introduced in cocotbgh-727 and cocotbgh-723, caused by a misunderstanding of how __new__ works
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Noticed in the comments of #723.