root/OpenSceneGraph/trunk/src/OpenThreads/win32/Win32ConditionPrivateData.h @ 10457

Revision 10457, 3.9 kB (checked in by robert, 5 years ago)

From David Fries, "Here is a fix for a deadlock seen under Windows using OpenThreads?
Barrier operations. The error is with atomic operations in the
win32 condition implementation. The attached sample program will
reliably trigger with as few as three threads and a dual core system,
though sometimes it will take 65,000 iterations.

2.8.1 was the base for these changes

Win32ConditionPrivateData.h
Win32ConditionPrivateData::wait does two operations to decrement
waiters_ then read, when InterlockedDecrement? decrements and returns
the value in one operation. The two operations allows another thread
to also decrement with both getting 0 for an answer.

Win32ConditionPrivateData::broadcast is using waiters_ directly
instead of using the w value read earlier, if it was safe to use
waiters_ directly there would be no need for InterlockedGet? or w.

overview of deadlock in barrier with three threads
one thread in broadcast, 2 threads in wait,
release semaphore 2, waits on waiters_done_
both threads wake, decrement waiters_, get 0 for w,

<logic error here>

one calls set waiters_done_,
broadcast thread comes out of waiters_done_,
other thread calls waiters_done_, (which leaves waiters_done_ in the
signaled state)

<sets the trap>

broadcast thread returns releases mutex, other threads get
mutex and also return,
next barrier, first two threads enter wait, one goes to broadcast, release
semaphore 2, skips waiters_done_ as it had been released last time
returns, processes, enters the barrier for the next barrier operation
and waits,
three threads are now in wait, two have the previous barrier phase,
one the current phase, there's one count left in the semaphore which a
thread gets, returns, enters the barrier as a waiter, sleeps, and the
deadlock is completed"

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/* -*-c++-*- OpenThreads library, Copyright (C) 2002 - 2007  The Open Thread Group
2 *
3 * This library is open source and may be redistributed and/or modified under 
4 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
5 * (at your option) any later version.  The full license is in LICENSE file
6 * included with this distribution, and on the openscenegraph.org website.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 * OpenSceneGraph Public License for more details.
12*/
13
14//
15//
16// WIN32ConditionPrivateData.h - Private data structure for Condition
17// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
18//
19#ifndef _WIN32CONDITIONPRIVATEDATA_H_
20#define _WIN32CONDITIONPRIVATEDATA_H_
21
22#ifndef _WINDOWS_
23#define WIN32_LEAN_AND_MEAN
24#define _WIN32_WINNT 0x0400
25#include <windows.h>
26#endif
27
28#include <OpenThreads/ScopedLock>
29
30#include "Win32ThreadPrivateData.h"
31#include "HandleHolder.h"
32
33#define InterlockedGet(x) InterlockedExchangeAdd(x,0)
34
35namespace OpenThreads {
36
37class Condition;
38
39class Win32ConditionPrivateData {
40public:
41    friend class Condition;
42    /// number of waiters.
43    long waiters_;
44
45    Win32ConditionPrivateData ()
46        :waiters_(0),
47         sema_(CreateSemaphore(NULL,0,0x7fffffff,NULL)),
48         waiters_done_(CreateEvent(NULL,FALSE,FALSE,NULL)),
49         was_broadcast_(0)
50    {
51    }
52
53    ~Win32ConditionPrivateData ();
54
55    inline int broadcast ()
56    {
57        int have_waiters = 0;
58        long w = InterlockedGet(&waiters_);
59
60        if (w > 0)
61        {
62          // we are broadcasting. 
63          was_broadcast_ = 1;
64          have_waiters = 1;
65        }
66
67        int result = 0;
68        if (have_waiters)
69        {
70            // Wake up all the waiters.
71            ReleaseSemaphore(sema_.get(), w, NULL);
72
73            cooperativeWait(waiters_done_.get(), INFINITE);
74
75            //end of broadcasting
76            was_broadcast_ = 0;
77        }
78        return result;
79    }
80
81    inline int signal()
82    {
83        long w = InterlockedGet(&waiters_);
84        int have_waiters = w > 0;
85 
86        int result = 0;
87
88        if (have_waiters)
89        {
90            if( !ReleaseSemaphore(sema_.get(),1,NULL) )
91                result = -1;
92        }
93        return result;
94    }
95
96    inline int wait (Mutex& external_mutex, long timeout_ms)
97    {
98   
99        // Prevent race conditions on the <waiters_> count.
100        InterlockedIncrement(&waiters_);
101
102        int result = 0;
103
104        ReverseScopedLock<Mutex> lock(external_mutex);
105
106        // wait in timeslices, giving testCancel() a change to
107        // exit the thread if requested.
108        try {
109              DWORD dwResult = cooperativeWait(sema_.get(), timeout_ms);
110            if(dwResult != WAIT_OBJECT_0)
111                result = (int)dwResult;
112        }
113        catch(...){
114            // thread is canceled in cooperative wait , do cleanup
115            long w = InterlockedDecrement(&waiters_);
116            int last_waiter = was_broadcast_ && w == 0;
117
118            if (last_waiter)  SetEvent(waiters_done_.get());
119            // rethrow
120            throw;
121        }
122
123       
124        // We're ready to return, so there's one less waiter.
125        long w = InterlockedDecrement(&waiters_);
126        int last_waiter = was_broadcast_ && w == 0;
127
128        if (result != -1 && last_waiter)
129            SetEvent(waiters_done_.get());
130
131        return result;
132    }
133
134protected:
135
136  /// Serialize access to the waiters count.
137  /// Mutex waiters_lock_;
138  /// Queue up threads waiting for the condition to become signaled.
139  HandleHolder sema_;
140  /**
141   * An auto reset event used by the broadcast/signal thread to wait
142   * for the waiting thread(s) to wake up and get a chance at the
143   * semaphore.
144   */
145  HandleHolder waiters_done_;
146  /// Keeps track of whether we were broadcasting or just signaling.
147  size_t was_broadcast_;
148};
149
150#undef InterlockedGet
151
152}
153
154
155
156
157
158
159
160#endif // !_WIN32CONDITIONPRIVATEDATA_H_
161
162
Note: See TracBrowser for help on using the browser.