Some Issues With Synchronized

Somedude lovelydear at mailmetrash.com
Fri Dec 23 01:25:56 PST 2011


Le 22/12/2011 10:47, Andrew Wiley a écrit :
> I'm having some issues getting the synchronized modifier to work like
> a synchronized block. Sorry this code example is long, but here's
> three dummy implementations of a Condition-based work queue:
> 
> ---import std.stdio;
> import core.sync.mutex;
> import core.sync.condition;
> import core.thread;
> 
> synchronized class Queue1 {
> private:
>     bool _work;
>     Condition _cond;
> public:
>     this() {
>         auto lock = new Mutex(this); // initialize the monitor for
> this object so we can use the same lock in the Condition
>         _cond = new Condition(lock);
>         _work = false;
>     }
>     void doWork() {
>         while(!_work) (cast()_cond).wait();
>         _work = false;
>         writeln("did work");
>         return;
>     }
>     void addWork() {
>         _work = true;
>         (cast()_cond).notify();
>         writeln("added work");
>     }
> }
> 
> class Queue2 {
> private:
>     bool _work;
>     Condition _cond;
> public:
>     this() {
>         auto lock = new Mutex(this); // again, initialize the monitor
> explicitly so we can reuse the lock
>         _cond = new Condition(lock);
>         _work = false;
>     }
>     synchronized void doWork() {
>         while(!_work) (cast()_cond).wait();
>         _work = false;
>         writeln("did work");
>         return;
>     }
>     synchronized void addWork() {
>         _work = true;
>         (cast()_cond).notify();
>         writeln("added work");
>     }
> }
> 
> class Queue3 {
> private:
>     bool _work;
>     Condition _cond;
>     Mutex _lock;
> public:
>     this() {
>         _lock = new Mutex(this); // make our own lock and ignore the monitor
>         _cond = new Condition(_lock);
>         _work = false;
>     }
>     void doWork() shared {
>         synchronized(_lock) {
>             while(!_work) (cast()_cond).wait();
>             _work = false;
>             writeln("did work");
>             return;
>         }
>     }
>     void addWork() shared {
>         synchronized(_lock) {
>             _work = true;
>             (cast()_cond).notify();
>             writeln("added work");
>        }
>     }
> }
> 
> void main() {
>     version(Q1) auto queue = new shared(Queue1)();
>     version(Q2) auto queue = new shared(Queue2)();
>     version(Q3) auto queue = new shared(Queue3)();
>     auto thread = new Thread({
>         while(true) queue.doWork();
>         });
>     thread.start();
>     while(true) queue.addWork();
> }
> ---
> 
> As you can see, this program starts a producer thread and a consumer
> thread, and it should endlessly print "did work" and "added work".
> Because the consumer has to wake up from the condition variable
> periodically, the producer tends to print far more often.
> 
> So first, according to the D spec, I believe these three queues should
> behave identically. Is that true?
> 
> Here's what happens:
> Queue1:
>   DMD: crash
>   GDC: crash
>     Both print errors that look like this:
> core.sync.exception.SyncException at src/core/sync/mutex.d(159): Unable
> to unlock mutex
>     From GDC's stack trace, it looks like the constructor call is
> being synchronized, so it crashes when I swap out the lock halfway
> through the constructor. Do we really need to synchronize constructor
> calls? I've never heard of another language that does this.
> 
> Queue2:
>   DMD: works
>   GDC: deadlock
>     Unless someone can see an issue here, I guess this is a GDC bug
> and should be handled separately.
> 
> Queue3:
>   DMD: works
>   GDC: works
>     This is about the most problematic way to do locking, but it does
> seem to work reliably.

On windows XP with DMD 2.057, I get
Queue1: deadlock
Queue2: works
Queue3: works


More information about the Digitalmars-d mailing list