Improve singleton scheduler pattern, add TrampolineScheduler#433
Conversation
49cc83e to
e114046
Compare
|
I think we should keep |
|
@dbrattli Good suggestion, thanks! I've managed to get things working with TrampolineScheduler as base class of CurrentThreadScheduler -- the only slightly awkward point is that we have to explicitly avoid calling |
dbrattli
left a comment
There was a problem hiding this comment.
I think this is much better than having a single scheduler trying to be 2 different things which can be confusing for users. Some expect a singleton, other don't. Great work to resolve this issue!
|
Thanks for review! The distinction between these schedulers is fairly subtle, I hope that won’t be too confusing... |
|
@erikkemperman thanks for taking the time to solve the issue. I think making two schedulers ( Maybe one question is why enforcing the "current thread" behavior for the You get the following classes: CurrentThreadScheduler.singleton # a classmethod
CurrentThreadScheduler # the current TrampolineScheduler inplementation
TrampolineScheduler # trampoline behavior with a single queue |
rx/scheduler/trampolinescheduler.py
Outdated
| local = self._local.get(thread) | ||
| if local is None: | ||
| local = _Local() | ||
| self._local[thread] = local |
There was a problem hiding this comment.
It's not mandatory, but could be simplified by using set_default method from dict, i.e.:
local = self._local.setdefault(thread, _Local())There was a problem hiding this comment.
Also should writing to self._local weak dict be protected by a lock?
|
@MichaelSchneeberger I think your suggestions makes a lot of sense, but I'll have to see if it's easy to tack onto this PR. I suspect the sub-class structure proposed by @dbrattli might no longer be a natural one. @jcafhe Thanks for review, I'll take your remarks into account! |
a1bf9d6 to
f9dd8df
Compare
|
OK, I think I more or less have it... I've moved the logic of checking idle state and managing the queue into the The Note that this requires a slightly more complicated I've made a "private" subclass of In the middle, as it were, are regular I believe this covers pretty much all bases, although I am slightly worried that users might not properly understand the subtle distinctions here... @jcafhe |
|
@erikkemperman Ok, thanks for the insight 👍 . |
That's a good question. Maybe one that can be dealt with later? I added a new issue #445 that reveals a problem with threading.local. Maybe it is safer to just not use it. Implementing the behavior by ourselves means that we have more control of what actually happens. |
|
@dbrattli You approved this PR earlier, so I just wanted to check if the later changes are all right with you? |
00ddce2 to
8b27dc5
Compare
|
Yes, please merge |
|
Done. Thanks! |
I hope this might resolve issue #383.
This improves the singleton pattern, so it uses
.instance()instead of a separately imported variable. I've kept the trick with__new__, to make sure that the singleton is enforced even if people call the constructor (I think this is the closest we can get to achieving what a "private" constructor might otherwise). This now also accounts for inheritance, if you extend one of these schedulers you'll still get a singleton, but of the right type.Tests are added for all of this as well, including for how I interpret the difference in execution order of actions scheduled on "nested" schedulers.
Then, as suggested, I've added a new
TrampolineScheduler, which replicates the old behaviour of theCurrentThreadScheduler(or that was the intention, anyway). I'm not too excited about that name, but don't have any better suggestions either.Would it make sense to switch this around, so the
CurrentThreadScheduleressentially stays the same as in 1.x, and we'd introduce a new scheduler (which would be used internally)?@MichaelSchneeberger Could you see if something like this would satisfy your requirements?