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
Support pluggable EventLoop task queue #9105
Comments
@nitsanw FYI... |
AFAIK the only misbehaviours could happen by using offer of a single-producer JCTools queue or just relaxedPoll instead of poll. |
@franz1981 I'm afraid I don't quite follow what you're saying, but we currently do use However, I have just taken another look at |
@belliottsmith when you say we should guard you think of doing something like this:
? |
The poll() always return null if there is no pending offering and the only (internal) spinning I'm aware of in JCTools is while awaiting the message slot to be filled by the producer (because JCTools's offer happen in 2 stages: producer sequence increment AND message slot fill).
I think that the awake logic could use a different strategy too, that would avoid using compareAndSwap on offer side: https://github.com/JCTools/JCTools/blob/master/jctools-experimental/src/main/java/org/jctools/queues/blocking/ScParkTakeStrategy.java#L19 |
@normanmaurer right |
@franz1981 the two stages are not atomic, and a thread can be suspended by the operating system in between these two steps also, the EventLoop should definitely not be parking; it should only be voluntarily suspending its execution when entering |
@belliottsmith Correct: that means that you would likely to see a spin into the queue::poll call: that's the issue you're seeing? Because I cannot see how using a putVolatile while setting the message slot could make any difference...@nitsanw can confirm/reject it |
@belliottsmith I think I would even argue that we may not need the CAS at all and could just use a volatile. At worse we would call |
@normanmaurer I think the get + CAS is probably safest for now, the problem being the negotiation between the consumer resetting it to |
@belliottsmith @normanmaurer I think this can be closed now that #9247 is merged? |
@njhill yes! |
@belliottsmith "Specifically, it is not non-blocking, and can lead to the EventLoop busy-spinning waiting for a task that may not be provided promptly because the offering thread's execution has been suspended by the operating system." - I assume you are referring to the following:
If this happens the consumer thread will spin-wait for the element to appear in
This makes the queue blocking. What is worse, successful If this is the issue under discussion, I'm not sure what the following suggestion means: "A queue identical to the JCTools one but using a putVolatile to set the item in the queue's backing array would suffice to avoid the busy-spin blocking of the EventLoop" The problem is that the index is visible before the element. Making the element "more visible" (by using a stronger barrier) will not make a difference. The index and the element are not atomically published (as @belliottsmith points out later). Using CLQ will provide better progress guarantees here AFAIK (IIRC the node is only published when fully initialized, so the "bubble" in the queue cannot happen) and I think the feature request is sensible. |
@belliottsmith @franz1981 reading through the discussion and the java doc I realize JCTools should do a better job documenting the progress guarantees on this and other queues. I've filed a bug: JCTools/JCTools#259 |
It means the Obviously a preferable solution is to use a truly non-blocking queue, but blocking the |
I was referring in particular to: "using a putVolatile to set the item in the queue's backing array would suffice to avoid the busy-spin blocking of the EventLoop". |
Well, in combination with #9109 you might appreciate my reasoning. Long story short, it depends on the behaviour of the code that wraps it to provide blocking behaviour. (It's been a while since I had the context to think about this, so I may bow out of further discussion for now, as I don't really have time to re-research my answers/thoughts) |
Reading through the discussion, I understand you have some reservations regarding the semantics/guarantees of JCTools queues, but I can't see how they add up to anything actionable. If you have a concern with a particular detail please file a bug with JCTools and I'll be happy to address it. Making the element write a volatile write makes no difference to either ticket AFAICT. It imposes further ordering constraints on the offer, which are irrelevant to the offer and do not help the |
I have no concerns about the semantics, that would imply I thought they were overall problematic. Every concurrent structure's semantics matter only in the context they're used. IMO, this project should be using a truly non-blocking queue, but that doesn't mean the algorithm employed by JCTools is of general concern. (I will concede that I consider the default spin-lock blocking behaviour to be a bug, for any real-time application, but that's a personal stance on the matter of unbounded user-space spin locks and priority inversion) |
"IMO, this project should be using a truly non-blocking queue" - The trade offs are between higher per-element costs (allocation + barriers) and the JCTools option which is lock-less but still risks blocking. The configurable queue impl solves this issue and offers choice, so I think this is solved. "I consider the default spin-lock blocking behaviour to be a bug" - Would you prefer to put a yield in the loop instead? If you have suggestion I'm happy to hear it. |
Given that a relaxed poll can return NULL either due to emptiness and a "not yet" published element I believe that it can be improved by make these 2 cases explicit and let a user to choose what to do while awaiting element publication, but it would mean an additional xxxPoll method: it could be useful, but most JCtools users love and uses it because qs are "kinda" replacement for j.u.c. queues and I'm not sure that Netty would love to be bounded to use MessagePassingQueue API instead of the more general java Queue one. |
I've been down enough of these rabbit holes to know any answer will only lead to another, so I hope you don't mind if I bow out for real for now. I only provided that qualifier as a post-script because I felt I had been dishonest by disclaiming any concerns about JCTools in response to your claim I had expressed general concerns (which I do not feel I had, I was only discussing their semantics in the context of this project). JCTools is a great library, that you should be proud of; it would be surprising if nobody had any concerns or criticisms. Perhaps we will have some time to discuss them in person one day. |
"JCTools is a great library, that you should be proud of" - Thanks :-) "it would be surprising if nobody had any concerns or criticisms." - Indeed, and I am keen to address these concerns where I can. So please, if possible, make time to submit issues if appropriate. Even if they remain unresolved they will be informing other users of the risks/rewards/tradeoffs/decision making etc. Thanks for your time, I hope we have an opportunity to discuss in person in the near future. |
The default task queue used by EPoll and NIO EventLoop implementations is provided by JCTools, and while it is efficient it has some undesirable properties. Specifically, it is not non-blocking, and can lead to the EventLoop busy-spinning waiting for a task that may not be provided promptly because the offering thread's execution has been suspended by the operating system. This is a rare scenario, but it will happen and some application maintainers might like to avoid it.
Unfortunately, there is no simple fix in the use of the queue; the
relaxedPoll()
method appears to be a candidate, but does not provide volatile visibility guarantees to a precedingoffer()
, meaning a wakeup could be missed (although we could write to a dummy volatile variable after offering to provide this). A queue identical to the JCTools one but using aputVolatile
to set the item in the queue's backing array would suffice to avoid the busy-spin blocking of the EventLoop, but the issue of future tasks being unreachable until the thread completes would remain.Ideally, the queue used by these event loops would be pluggable, so that users can choose their ideal point on the spectrum of tradeoffs.
A simple replacement that guarantees progress is the JDK
ConcurrentLinkedQueue
, but at the cost of slightly more expensiveoffer()
andpoll()
; the above mentioned modification to the JCToolsoffer()
could be mixed withrelaxedPoll()
to simply avoid busy-spin blocking, and a custom Mpsc queue implementation could guarantee forward progress with only marginally higher cost. Permitting the application maintainer to pick their poison would be tremendously helpful.The text was updated successfully, but these errors were encountered: