-
-
Notifications
You must be signed in to change notification settings - Fork 726
Implemented some experimental event loops with nextTick() support. #234
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
Conversation
* ExtEventLoop - based on the ext-event PHP extension * StreamSelectNextTickLoop - based on stream_select
Thanks for the effort @jmalloc! I wanted to give Right before entering a detailed review of the code as it would take time to do it properly, I have a couple of questions aimed at better understanding the reasons for some choices:
Strictly related to my third point, in all honesty I'm still of the idea that we should aim to have |
Thanks, and you're welcome :) I had found myself adding undesirable hacks to my own projects to try to emulate nextTick() so I thought it was about time I tried to do it properly.
|
|
Everything you mention here sounds perfectly reasonable, I'll update the implementation and we can move from there. I wont be attempting signal handling in this branch/PR, but I do need it for my own purposes so I figured I'd start another discussion/PR for that later unless it's something that's outright unwanted in React (which would be a shame since it'd mean I'd have to have custom loop implementations again :P) |
1, 2, 3 + 4: I've made a quick attempt at improving some of the issues you've raised. At least for the moment I've retained both the abstract class (though smaller) and the interface. I'll do my best to get to 5: In regards to Edit: clarity. |
*/ | ||
protected function isEmpty() | ||
{ | ||
return $this->timers->isEmpty() |
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.
Checking if $this->readStreams
and $this->writeStreams
have no elements is a cheaper operation than doing the same for $this->timers
, we should really move $this->timers->isEmpty()
to the rightmost side of the short-circuit evaluation so that it gets executed only if the two arrays are empty:
return !$this->readStreams && !$this->writeStreams && $this->timers->isEmpty();
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.
Noted, in the most recent pushed I have endeavoured to ensure that least-costly checks are always performed first.
Sure it makes perfectly sense to omit it right now, we should focus on getting
The introduction of
I do agree that |
Thanks for the extended feedback @nrk, I hope to get a chance to make some more progress on this today :) |
…mSelectNextTickLoop to StreamSelectLoop.
I've just pushed the next round of changes, there's a bit to go over but I've got to leave the office at the moment. I'll provide a summary of the changes as soon as I get a chance. |
Removing the abstract base class had the intended effect of allowing for some optimisations. I've tried to be mindful of performing least-cost operations first, unrolling unnecessary loops, keeping dispatch to a minimum etc, though I'm sure there is further room for improvement. I have a question about the way the closure-destroys-self problem is handled in the existing |
} | ||
} | ||
|
||
return true; |
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.
Spurious return true
in waitForStreamActivity
? It doesn't seem to be used anywhere, and I don't see how it could be useful anyway.
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.
Oops! a leftover appendage from some intermediate refactoring. Gone now.
The first test I do is the Autobahn WebSocket Fuzzing test. I'll see if I can make one with a php client + server w/o minimal dependancies. |
I'm not able to reproduce any weird issue using wrk against a basic HTTP server emitting a 65k payload on each response. EDIT: This is the output of wrk using LibEventLoop and a 128kb payload on each request:
|
One last thing I'd discuss before thinking about merging this PR, more like a conceptual issue: we have a mismatch with the visibility of member variables and methods in our loop classes, in this case there's no point in having protected methods when all of our member variables are private. The only valid reason for having private members would be to prevent developers from accessing certain core parts of the base classes when extended, so we should either decide if we want to seal our classes for good by marking them as When I ask myself "what's the point of extending one of the loop classes?" I can't really find a convincing answer so I'd just mark them Thoughts? |
I agree that there isn't any apparent reason to extend these implementations for production code. I think all of the methods and properties that are currently protected should be private. On the flip-side, PHPUnit, Phake, etc extend from classes to produce mocks, and for that reason I tend to avoid the use of |
As per our discussion in IRC my above issue was caused from a miss-managed merge conflict by yours truly. I do want to discuss another topic before this is merged and that is naming convention. Python 3.4 introduced a new async API and their equivalent methods are I find those method names to be far more intuitive. I'm not settled on them, but I'd like to open up a discussion to set a name on these methods for React. Looking at the API I wouldn't imagine |
For example extending Encouraging a SOLID design and discouraging sub-classing is a good thing IMHO, but intentionally inhibiting sounds like a bad idea to me. +1 for keeping this as-is. The PR looks really good to me, can't wait to give it a try 👍 |
IIRC Edit on reflection, |
Fair enough, your example of
OK let's drop the idea of marking loop classes
Testing shouldn't really be the driving force behind these kind of decisions, furthermore in this specific case using a concrete loop class instead of the interface for type-hinting would be so blatantly weird more than esoteric. Well anyway, let's keep classes as they are without |
About naming...
As I've said before, probably on IRC, we definitely can (and probably should) come up with our own method names.
We actually all agree on Having said that, I still can't think of any decent alternative. |
I like the Python names better than the Node names and would like to see them if we can't come up with a better name ourselves. The floor is open to all suggestions. @jmalloc
Do you know any way around this? For testing purposes it would be nice to be able to have both installed. |
I haven't tried, sorry - I've been testing pecl/event on OSX and pecl/libevent on the Vagrant box.
Unless I missed it, this PEP doesn't appear to define an equivalent to |
Skip pecl and build from sources. Inconvenient, but it works.
Finally took a look at the above mentioned PEP and as @jmalloc says there doesn't seem to be an equivalent to If everyone agrees, I'd take a few more days of testing so that we can think about the name |
@jmalloc: as proposed by @igorw on IRC, it would be useful to have this pull request split in half:
By doing so we could merge Would it be OK for you? |
No problem, I'll do this today. |
I'll wait until this is merged before submitting a new PR for |
Guess what? :-) |
Thanks @jmalloc! |
I fully expect that this PR will be rejected in its current form, but I needed a place to start a discussion about it.
This pull-request attempts to provide a reliable way to register callbacks on the event-loop that are executed with the following guarantees:
On each tick of the engine:
As best I can tell this matches the current behaviour of
process.nextTick()
in nodejs (0.10.x), and as such I have dared to call the feature 'nextTick'. Please correct any misconceptions I have about this :) (see #92)There is one new interface:
NextTickLoopInterface
- adds thenextTick()
method to the existingLoopInterface
And two implementations:
ExtEventLoop
- uses ext-event (OOP bindings for libevent, see Implement new event pecl package #161)StreamSelectNextTickLoop
- a stream_select() based loopBoth implementations are tested against the existing abstract test cases for loops and timers.
I haven't had a chance to try
libev
orlibuv
yet, but I will attempt to provide implementations based on both of those libraries if this PR is well received.I chose not to completely replace the existing
stream_select()
based loop at this stage, but there is no reason it would need to remain if the implementation proposed in this PR is accepted in the future.Finally, I also added some doc-blocks to
LoopInterface
to clarify the guarantees made in regards to timer behaviour (in contrast to nextTick() behaviour) and return values.Edit:
Notably absent is any concept of minimum resolution for timers, I was speaking with @nrk about this on IRC and we talked about the possibility of allowing users to configure the resolution, perhaps that is now unnecessary.