MSVC memory barrier issue.

Nicolas Noble nicolas at grumpycoder.net
Sat Jan 4 10:35:44 CET 2014


Hi,

  Basically, I've discovered a problem in libev, when compiling it using
Visual Studio. Long story short, the libecb's MEMORY_FENCE macros are
incorrect for Visual Studio 2008 and up:

  Here is the fix I have locally in order to address the problem I have
been seeing:

  #elif _MSC_VER >= 1500 /* VC++ 2008 */
    #pragma intrinsic(_ReadBarrier,_WriteBarrier,_ReadWriteBarrier)
    #define ECB_MEMORY_FENCE         _ReadWriteBarrier (); MemoryBarrier()
    #define ECB_MEMORY_FENCE_ACQUIRE _ReadWriteBarrier (); MemoryBarrier()
/* according to msdn, _ReadBarrier is not a load fence */
    #define ECB_MEMORY_FENCE_RELEASE _WriteBarrier (); MemoryBarrier()
  #elif _MSC_VER >= 1400 /* VC++ 2005 */
    #pragma intrinsic(_ReadBarrier,_WriteBarrier,_ReadWriteBarrier)
    #define ECB_MEMORY_FENCE         _ReadWriteBarrier ();
    #define ECB_MEMORY_FENCE_ACQUIRE _ReadWriteBarrier (); /* according to
msdn, _ReadBarrier is not a load fence */
    #define ECB_MEMORY_FENCE_RELEASE _WriteBarrier ();


 Basically, if you refer to the MSDN, (from this article, more precisely:
http://msdn.microsoft.com/en-us/library/f20w0x5e(v=vs.90).aspx ) the
_ReadWriteBarrier() intrinsic is now only a compiler intrinsic that only
prevents compiler reordering. It doesn't generate anymore the appropriate
code to trigger a CPU-barrier. The MemoryBarrier() macro however is what
should be used instead to generate the appropriate code for a CPU barrier.

  For more details, if needed, I can provide a fully-featured reproduction
case tomorrow - it's 1:30am right now, and I'm only glad I finally found
this one - but here is some pseudo-code that exacerbates and demonstrates
the problem:

main thread:
  setup async event
  setup threaded condition X
  start SomeThread
  while forever:
    signal threaded condition X
    run libev's ev_run once

SomeThread:
  while forever:
    wait for threaded condition X
    signal async event

  What happens is while ev_run should returned almost immediately, since
the other thread signals the async event once right when the main thread
enters ev_run, it sometime just stays here for a very long time.
Until MAX_BLOCKTIME that is.

  Another workaround I first found, that confirms the problem, is that I've
changed pipe_write_wanted to always be 1. Same exact code using the old
barrier code, just writing 1 in pipe_write_wanted instead of 0, in order to
force writing into the pipe. That should demonstrate that pipe_write_wanted
is getting desynchronised between the thread that runs evpipe_write and the
main thread that runs ev_run.

  Let me know if you have further questions or concerns about this, or if
you really want a short reproduction code to demonstrate this - right now
my code is a full-blown application that wouldn't be very easy to use as a
test case.

  Thanks,

  -- Nicolas Noble
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.schmorp.de/pipermail/libev/attachments/20140104/5752f62c/attachment.html>


More information about the libev mailing list