digitalmars.D - [std.concurrency] Critical bug
- osa (104/104) Sep 30 2010 I've struggled with occasional hangs in my code using std.concurrency
- Sean Kelly (2/10) Sep 30 2010 Thanks for the repro. The bug is fixed in SVN rev 2078.
I've struggled with occasional hangs in my code using std.concurrency for a while. Initially I thought it was my fault but it seems that std.concurrency has a critical bug which makes it completely unusable, at least for me. The problem is that in some situations message sent by send() is never delivered to receive(). This happens when the thread on receive() side has other linked threads (started by spawnLinked) and those threads terminate causing LinkTerminated exception. And this exception screws receiving thread queue completely. Here is the smallest example I could come up with: let's suppose we have two threads, "main" and "service", exchanging messages in a simple loop. The "main" thread sends message A to the "service" and waits for message B. The "service" thread waits for message A and sends message B to the main thread: main thread: for( ;; ) { send( service, A() ); receive( ( B ){} ); } service thread: for( ;; ) { receive( ( A ){} ); send( main, B() ); } This works like a charm. But if we have another linked thread spawned from the service, and that thread terminates causing LinkTerminated exception raised by call to receive() in service thread, next receive() calls never succeed. Below is the actual test program: ------- // compile with -version=hang to see the problem import std.concurrency; import std.stdio; struct A { int c; } struct B {} void main() { auto service = spawn( &service_proc, thisTid ); int count; for( count = 0; count < 200; ++count ) { send( service, A( count ) ); writeln( "main\t: waiting for B" ); receive( ( B ){} ); } writeln( "done: ", count, " iterations" ); } void service_proc( Tid main_tid ) { Tid child; for( ;; ) { version(hang) if( child == Tid.init ) child = spawnLinked( &child_proc ); try { if( child != Tid.init ) send( child, 42 ); writeln( "service\t: waiting for A" ); received, sending B to main" ); } ); send( main_tid, B() ); } catch( LinkTerminated e ) { assert( e.tid == child ); writeln( "service\t: link terminated" ); child = Tid.init; } catch( OwnerTerminated ) { return; } } } void child_proc() { for( int i = 0; i < 2; ++i ) receive( ( int ){} ); } ------- Without -version=hang, no child thread is started from the service and everything works fine, output is like service : waiting for A main : waiting for B service : waiting for A ....................... service : waiting for A done: 200 iterations If compiled with -version=hang (dmd v2.049, tried both Windows and Linux, makes no difference), the service starts a child thread and the output is this: main : waiting for B service : waiting for A service : waiting for A main : waiting for B service : waiting for A main : waiting for B service : link terminated service : waiting for A and the program hangs forever. Sometimes it takes more messages, and sometimes it even works fine, but in most cases the last A sent to the service before LinkTerminated exception is lost and never received. I did not file the bug in Bugzilla yet but if anyone confirms that the problem is true, I'd file it.
Sep 30 2010
osa Wrote:I've struggled with occasional hangs in my code using std.concurrency for a while. Initially I thought it was my fault but it seems that std.concurrency has a critical bug which makes it completely unusable, at least for me. The problem is that in some situations message sent by send() is never delivered to receive(). This happens when the thread on receive() side has other linked threads (started by spawnLinked) and those threads terminate causing LinkTerminated exception. And this exception screws receiving thread queue completely.Thanks for the repro. The bug is fixed in SVN rev 2078.
Sep 30 2010