The offer side instead is quite simple:
I'm not able to understand why the version that just use the memory barriers provided by volatile set/get and queue.isEmpty/poll/offer seems not correct (at least in Java)...any idea?
Obviously the right side version could be "enhanced" by adding a !task.isEmpty check BEFORE the lock to avoid entering the lock, without creating any deadlock.
Consider that if I replace the volatile boolean with an atomic reference holding a potentially parked thread and the condition usage with LockSupport::unpark/park, the version on the left is working as expected...
One important note on the q: JCtools MpscArrayQueue has a full barrier on offer side just while moving the producer sequence and not while populating the slot, but q.isEmpty/q.poll is consistent with it (@nitsanw am I correct? ),
so the full memory barrier required while sending messages is embedded into the q offer.
Cheers,
Franz
Hi guys!
I've recently read Adventures with Memory Barriers and Seastar on Linux of Avi K. and I was trying to use its pseudo-code to implement a batch drain method with optional wait/sleep when no items are detected.
Sadly, I wasn't able to use it without getting a deadlock, hence probably there is something I'm missing on it :(On the left side there is the version that is deadlocking and is pretending to use the same pattern on the article, while on the right side there is a "fixed" version (based on take blocking strategy of JCtools) that is not deadlocking:
The offer side instead is quite simple:
I'm not able to understand why the version that just use the memory barriers provided by volatile set/get and queue.isEmpty/poll/offer seems not correct (at least in Java)...any idea?
One important note: JCtools MpscArrayQueue has a full barrier on offer side just while moving the producer sequence and not while populating the slot, but q.isEmpty/q.poll is consistent with it (@nitsanw am I correct? ),
so the full memory barrier required while sending messages is embedded into the q offer.
Cheers,
Franz
--
You received this message because you are subscribed to the Google Groups "mechanical-sympathy" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mechanical-symp...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
It's good to see someone putting it into practice! The technique is somewhat esoteric.
I don't understand the if (tasks.isEmpty()) { continue; } block. Shouldn't it be inverted? You want to sleep if there is nothing to do, not go back and poll again.
To unsubscribe from this group and stop receiving emails from it, send an email to mechanical-sympathy+unsub...@googlegroups.com.
Thanks Avi for the quick response and the article!!
It's good to see someone putting it into practice! The technique is somewhat esoteric.
The final objective would be to provide barrier "injection" into the producer from the consumer, but I have to think carefully if in Java it is somehow possible (maybe with mprotect or a file lock? not sure how to handle the signal or if Java will raise an exeception)...
I don't understand the if (tasks.isEmpty()) { continue; } block. Shouldn't it be inverted? You want to sleep if there is nothing to do, not go back and poll again.
You're right, I have already updated the post, but via email has been received only the first "stale" version...
--Il giorno mercoledì 20 giugno 2018 14:52:59 UTC+2, Avi Kivity ha scritto:
On 2018-06-20 11:34, Francesco Nigro wrote:
Hi guys!
I've recently read Adventures with Memory Barriers and Seastar on Linux of Avi K. and I was trying to use its pseudo-code to implement a batch drain method with optional wait/sleep when no items are detected.
It's good to see someone putting it into practice! The technique is somewhat esoteric.
Sadly, I wasn't able to use it without getting a deadlock, hence probably there is something I'm missing on it :(On the left side there is the version that is deadlocking and is pretending to use the same pattern on the article, while on the right side there is a "fixed" version (based on take blocking strategy of JCtools) that is not deadlocking:
I don't understand the if (tasks.isEmpty()) { continue; } block. Shouldn't it be inverted? You want to sleep if there is nothing to do, not go back and poll again.
--The offer side instead is quite simple:
I'm not able to understand why the version that just use the memory barriers provided by volatile set/get and queue.isEmpty/poll/offer seems not correct (at least in Java)...any idea?
One important note: JCtools MpscArrayQueue has a full barrier on offer side just while moving the producer sequence and not while populating the slot, but q.isEmpty/q.poll is consistent with it (@nitsanw am I correct? ),
so the full memory barrier required while sending messages is embedded into the q offer.
Cheers,
Franz
You received this message because you are subscribed to the Google Groups "mechanical-sympathy" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mechanical-sympathy+unsub...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
You received this message because you are subscribed to the Google Groups "mechanical-sympathy" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mechanical-symp...@googlegroups.com.
Are you sure that volatile reads insert a full barrier? The offer-side if (!running) should have an MFENCE before it. Look at the generated assembly if you can.
Are you sure that volatile reads insert a full barrier? The offer-side if (!running) should have an MFENCE before it. Look at the generated assembly if you can.
On offer side MpscArrayQueue::offer *should* have a full barrier after it, that result in (pseudo-code):
q.offer//StoreLoadboolean run = running;
//LoadStore + LoadLoadIf(!run){//...}
To be sure that it wasn't the reason of the deadlock I have put manually a full barrier
between offer and if(!running) with no success: the only way
I have found to avoid deadlocks is checking the queue emptyness on cosumer side while holding the lock, before waiting.
Well, that looks correct, so I don't know why you deadlock.
boolean x = !tasks.isEmpty();
running = false;
if(x){
continue;
}running is declared as volatile and that means that while storing it is enforced a sequential consistent memory ordering with the subsequent statements ie isEmpty evaluation can't happen before it.
I would say instead that the drain seems blocked.
It doesn't. Please look at https://shipilev.net/blog/2014/jmmpragmatics/#_jmm_interpretation_roach_motel (it is a great presentation)
What do you mean by 'blocked' exactly?
I think that it explains in JMM term that such optimization isn't possible AFAIK.
I mean that the poller thread is calling condition::wait without never being wake up.
Why do you think that poller should be waken up, do you call offer side?
--
You received this message because you are subscribed to the Google Groups "mechanical-sympathy" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mechanical-symp...@googlegroups.com.