 |
Changeset 2010
- Timestamp:
- 04/09/07 20:12:58
(2 years ago)
- Author:
- jcomellas
- Message:
* Changed tango.util.locks.Condition interface to make it compatible with the
integrated locks proposal.
* Cleaned up comments for the modules in the tango.util.locks package.
* Removed unnecessary modules from the integrated locks proposal.
* Fixed ticket #363.
-
Files:
-
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
| r1901 |
r2010 |
|
| 6 | 6 | |
|---|
| 7 | 7 | private import tango.util.locks.Condition; |
|---|
| 8 | | private import tango.util.locks.Barrier; |
|---|
| 9 | 8 | private import tango.util.locks.LockException; |
|---|
| 10 | 9 | private import tango.core.Thread; |
|---|
| … | … | |
| 36 | 35 | |
|---|
| 37 | 36 | /** |
|---|
| 38 | | * Test for Condition.notifyOne(). |
|---|
| | 37 | * Test for Condition.notify(). |
|---|
| 39 | 38 | */ |
|---|
| 40 | 39 | void testNotifyOne() |
|---|
| … | … | |
| 46 | 45 | |
|---|
| 47 | 46 | scope Mutex mutex = new Mutex(); |
|---|
| 48 | | scope Condition cond = new Condition(); |
|---|
| | 47 | scope Condition cond = new Condition(mutex); |
|---|
| 49 | 48 | int waiting = 0; |
|---|
| 50 | 49 | Thread thread; |
|---|
| … | … | |
| 65 | 64 | log.trace("Acquired mutex"); |
|---|
| 66 | 65 | |
|---|
| | 66 | scope(exit) |
|---|
| | 67 | { |
|---|
| | 68 | debug (condition) |
|---|
| | 69 | log.trace("Releasing mutex"); |
|---|
| | 70 | mutex.release(); |
|---|
| | 71 | } |
|---|
| | 72 | |
|---|
| 67 | 73 | waiting++; |
|---|
| 68 | 74 | |
|---|
| … | … | |
| 71 | 77 | debug (condition) |
|---|
| 72 | 78 | log.trace("Waiting on condition variable"); |
|---|
| 73 | | cond.wait(mutex); |
|---|
| | 79 | cond.wait(); |
|---|
| 74 | 80 | } |
|---|
| 75 | 81 | |
|---|
| 76 | 82 | debug (condition) |
|---|
| 77 | 83 | log.trace("Condition variable was signaled"); |
|---|
| 78 | | |
|---|
| 79 | | debug (condition) |
|---|
| 80 | | log.trace("Releasing mutex"); |
|---|
| 81 | | mutex.release(); |
|---|
| 82 | 84 | } |
|---|
| 83 | 85 | catch (LockException e) |
|---|
| … | … | |
| 129 | 131 | debug (condition) |
|---|
| 130 | 132 | log.trace("Notifying test thread"); |
|---|
| 131 | | cond.notifyOne(); |
|---|
| | 133 | cond.notify(); |
|---|
| 132 | 134 | |
|---|
| 133 | 135 | debug (condition) |
|---|
| … | … | |
| 175 | 177 | |
|---|
| 176 | 178 | scope Mutex mutex = new Mutex(); |
|---|
| 177 | | scope Condition cond = new Condition(); |
|---|
| | 179 | scope Condition cond = new Condition(mutex); |
|---|
| 178 | 180 | int waiting = 0; |
|---|
| 179 | 181 | |
|---|
| … | … | |
| 202 | 204 | debug (condition) |
|---|
| 203 | 205 | log.trace("Waiting on condition variable"); |
|---|
| 204 | | cond.wait(mutex); |
|---|
| | 206 | cond.wait(); |
|---|
| 205 | 207 | } |
|---|
| 206 | 208 | |
|---|
| r1901 |
r2010 |
|
| 30 | 30 | debug (readwritemutex) |
|---|
| 31 | 31 | { |
|---|
| 32 | | scope Logger log = Log.getLogger("rwmutex"); |
|---|
| | 32 | scope Logger log = Log.getLogger("readwritemutex"); |
|---|
| 33 | 33 | |
|---|
| 34 | 34 | log.addAppender(new ConsoleAppender(new DateLayout())); |
|---|
| … | … | |
| 47 | 47 | debug (readwritemutex) |
|---|
| 48 | 48 | { |
|---|
| 49 | | Logger log = Log.getLogger("rwmutex." ~ Thread.getThis().name()); |
|---|
| | 49 | Logger log = Log.getLogger("readwritemutex." ~ Thread.getThis().name()); |
|---|
| 50 | 50 | |
|---|
| 51 | 51 | log.trace("Starting reader thread"); |
|---|
| … | … | |
| 73 | 73 | debug (readwritemutex) |
|---|
| 74 | 74 | { |
|---|
| 75 | | Logger log = Log.getLogger("rwmutex." ~ Thread.getThis().name()); |
|---|
| | 75 | Logger log = Log.getLogger("readwritemutex." ~ Thread.getThis().name()); |
|---|
| 76 | 76 | |
|---|
| 77 | 77 | log.trace("Starting writer thread"); |
|---|
| r1901 |
r2010 |
|
| 4 | 4 | author: Juan Jose Comellas <juanjo@comellas.com.ar> |
|---|
| 5 | 5 | *******************************************************************************/ |
|---|
| | 6 | |
|---|
| | 7 | module semaphore; |
|---|
| 6 | 8 | |
|---|
| 7 | 9 | private import tango.util.locks.Semaphore; |
|---|
| r1901 |
r2010 |
|
| 28 | 28 | |
|---|
| 29 | 29 | /** |
|---|
| 30 | | * Mutex wrapper that maps to a <CRITICAL_SECTION> on Windows and to a |
|---|
| 31 | | * <pthread_mutex_t> on UNIX. This implementation is optimized for locking |
|---|
| 32 | | * threads that are in the same process. |
|---|
| | 30 | * Mutex wrapper that's only valid for threads in the same process. |
|---|
| | 31 | * This implementation is optimized for locking threads that are in the |
|---|
| | 32 | * same process. It maps to a $(D_CODE CRITICAL_SECTION) on Windows and to |
|---|
| | 33 | * a $(D_CODE pthread_mutex_t) on UNIX. Mutexes on Windows are always |
|---|
| | 34 | * recursive, even if the $(D_CODE NonRecursive) mutex type is used. |
|---|
| 33 | 35 | */ |
|---|
| 34 | 36 | public class Mutex |
|---|
| … | … | |
| 37 | 39 | * Accessor for the underlying mutex implementation. |
|---|
| 38 | 40 | */ |
|---|
| 39 | | package pthread_mutex_t* mutex() |
|---|
| | 41 | package override pthread_mutex_t* mutex() |
|---|
| 40 | 42 | { |
|---|
| 41 | 43 | // DMD's intrinsic function gives us access to the Object's mutex |
|---|
| … | … | |
| 83 | 85 | |
|---|
| 84 | 86 | /** |
|---|
| 85 | | * Check the 'errorCode' argument against possible errno values and |
|---|
| 86 | | * throw an exception with the description of the error. |
|---|
| | 87 | * Check the $(D_PARAM errorCode) argument against possible values |
|---|
| | 88 | * of $(D_CODE SysError.lastCode()) and throw an exception with the |
|---|
| | 89 | * description of the error. |
|---|
| 87 | 90 | * |
|---|
| 88 | 91 | * Params: |
|---|
| 89 | | * errorCode = errno value; must not be 0. |
|---|
| | 92 | * errorCode = SysError.lastCode() value; must not be 0. |
|---|
| 90 | 93 | * file = name of the source file where the check is being |
|---|
| 91 | 94 | * made; you would normally use __FILE__ for this |
|---|
| … | … | |
| 97 | 100 | * Throws: |
|---|
| 98 | 101 | * AlreadyLockedException when the mutex has already been locked by |
|---|
| 99 | | * another thread (EBUSY); DeadlockException when the mutex has already |
|---|
| 100 | | * been locked by the calling thread (EDEADLK); InvalidMutexException |
|---|
| 101 | | * when the mutex has not been properly initialized (EINVAL); |
|---|
| 102 | | * MutexOwnerException when the calling thread does not own the mutex |
|---|
| 103 | | * (EPERM); LockException for any of the other cases in which errno is |
|---|
| 104 | | * not 0. |
|---|
| 105 | | */ |
|---|
| 106 | | protected final void checkError(int errorCode, char[] file, uint line) |
|---|
| | 102 | * another thread; DeadlockException when the mutex has already |
|---|
| | 103 | * been locked by the calling thread; InvalidMutexException when the |
|---|
| | 104 | * mutex has not been properly initialized; MutexOwnerException when |
|---|
| | 105 | * the calling thread does not own the mutex; LockException for any |
|---|
| | 106 | * of the other cases in which $(D_PARAM errorCode) is not 0. |
|---|
| | 107 | */ |
|---|
| | 108 | protected final void checkError(uint errorCode, char[] file, uint line) |
|---|
| 107 | 109 | in |
|---|
| 108 | 110 | { |
|---|
| … | … | |
| 229 | 231 | * Mutex wrapper that's only valid for threads in the same process. |
|---|
| 230 | 232 | * This implementation is optimized for locking threads that are in the |
|---|
| 231 | | * same process. It maps to a <CRITICAL_SECTION> on Windows and to a |
|---|
| 232 | | * <pthread_mutex_t> on UNIX. Mutexes on Windows are always recursive, |
|---|
| 233 | | * even if the <NonRecursive> mutex type is used. |
|---|
| | 233 | * same process. It maps to a $(D_CODE CRITICAL_SECTION) on Windows and to |
|---|
| | 234 | * a $(D_CODE pthread_mutex_t) on UNIX. Mutexes on Windows are always |
|---|
| | 235 | * recursive, even if the $(D_CODE NonRecursive) mutex type is used. |
|---|
| 234 | 236 | */ |
|---|
| 235 | 237 | public class Mutex |
|---|
| … | … | |
| 279 | 281 | |
|---|
| 280 | 282 | /** |
|---|
| 281 | | * |
|---|
| | 283 | * Initialize the mutex. |
|---|
| 282 | 284 | */ |
|---|
| 283 | 285 | public this() |
|---|
| … | … | |
| 286 | 288 | if (_mutex == cast(HANDLE) NULL) |
|---|
| 287 | 289 | { |
|---|
| 288 | | checkError(GetLastError(), __FILE__, __LINE__); |
|---|
| | 290 | checkError(SysError.lastCode(), __FILE__, __LINE__); |
|---|
| 289 | 291 | } |
|---|
| 290 | 292 | } |
|---|
| … | … | |
| 307 | 309 | if (result != WAIT_OBJECT_0) |
|---|
| 308 | 310 | { |
|---|
| 309 | | checkError(GetLastError(), __FILE__, __LINE__); |
|---|
| | 311 | checkError(SysError.lastCode(), __FILE__, __LINE__); |
|---|
| 310 | 312 | } |
|---|
| 311 | 313 | } |
|---|
| … | … | |
| 344 | 346 | else |
|---|
| 345 | 347 | { |
|---|
| 346 | | checkError(GetLastError(), __FILE__, __LINE__); |
|---|
| | 348 | checkError(SysError.lastCode(), __FILE__, __LINE__); |
|---|
| 347 | 349 | return false; |
|---|
| 348 | 350 | } |
|---|
| … | … | |
| 356 | 358 | if (ReleaseMutex(_mutex) != 0) |
|---|
| 357 | 359 | { |
|---|
| 358 | | checkError(GetLastError(), __FILE__, __LINE__); |
|---|
| 359 | | } |
|---|
| 360 | | } |
|---|
| 361 | | |
|---|
| 362 | | /** |
|---|
| 363 | | * Check the result from the GetLastError() Windows function and |
|---|
| 364 | | * throw an exception with the description of the error. |
|---|
| | 360 | checkError(SysError.lastCode(), __FILE__, __LINE__); |
|---|
| | 361 | } |
|---|
| | 362 | } |
|---|
| | 363 | |
|---|
| | 364 | /** |
|---|
| | 365 | * Check the $(D_PARAM errorCode) argument against possible values |
|---|
| | 366 | * of $(D_CODE SysError.lastCode()) and throw an exception with the |
|---|
| | 367 | * description of the error. |
|---|
| 365 | 368 | * |
|---|
| 366 | 369 | * Params: |
|---|
| 367 | | * file = name of the source file where the check is being made; you |
|---|
| 368 | | * would normally use __FILE__ for this parameter. |
|---|
| 369 | | * line = line number of the source file where this method was called; |
|---|
| 370 | | * you would normally use __LINE__ for this parameter. |
|---|
| | 370 | * errorCode = SysError.lastCode() value; must not be 0. |
|---|
| | 371 | * file = name of the source file where the check is being |
|---|
| | 372 | * made; you would normally use __FILE__ for this |
|---|
| | 373 | * parameter. |
|---|
| | 374 | * line = line number of the source file where this method |
|---|
| | 375 | * was called; you would normally use __LINE__ for |
|---|
| | 376 | * this parameter. |
|---|
| 371 | 377 | * |
|---|
| 372 | 378 | * Throws: |
|---|
| 373 | 379 | * AccessDeniedException when the caller does not have permissions to |
|---|
| 374 | 380 | * use the mutex; LockException for any of the other cases in which |
|---|
| 375 | | * GetLastError() is not 0. |
|---|
| 376 | | */ |
|---|
| 377 | | protected void checkError(DWORD errorCode, char[] file, uint line) |
|---|
| | 381 | * $(D_PARAM errorCode) is not 0. |
|---|
| | 382 | */ |
|---|
| | 383 | protected void checkError(uint errorCode, char[] file, uint line) |
|---|
| 378 | 384 | in |
|---|
| 379 | 385 | { |
|---|
| 380 | | assert(errorCode != 0); |
|---|
| | 386 | char[10] tmp; |
|---|
| | 387 | |
|---|
| | 388 | assert(errorCode != 0, "checkError() was called with SysError.lastCode() == 0 on file " ~ |
|---|
| | 389 | file ~ ":" ~ format(tmp, line)); |
|---|
| 381 | 390 | } |
|---|
| 382 | 391 | body |
|---|
| r1901 |
r2010 |
|
| 20 | 20 | * Implements "barrier synchronization". |
|---|
| 21 | 21 | * |
|---|
| 22 | | * This class allows <count> number of threads to synchronize their |
|---|
| 23 | | * completion of (one round of) a task, which is known as "barrier |
|---|
| 24 | | * synchronization". After all the threads call <wait()> on the barrier |
|---|
| 25 | | * they are all atomically released and can begin a new round. |
|---|
| | 22 | * This class allows $(D_PARAM count) number of threads to synchronize |
|---|
| | 23 | * their completion of (one round of) a task, which is known as "barrier |
|---|
| | 24 | * synchronization". After all the threads call $(D_CODE wait()) on the |
|---|
| | 25 | * barrier they are all atomically released and can begin a new round. |
|---|
| 26 | 26 | */ |
|---|
| 27 | 27 | public class Barrier |
|---|
| … | … | |
| 30 | 30 | |
|---|
| 31 | 31 | /** |
|---|
| 32 | | * Initialize the barrier to synchronize <count> threads. |
|---|
| | 32 | * Initialize the barrier to synchronize $(D_PARAM count) threads. |
|---|
| 33 | 33 | */ |
|---|
| 34 | 34 | public this(uint count) |
|---|
| … | … | |
| 61 | 61 | |
|---|
| 62 | 62 | /** |
|---|
| 63 | | * Block the caller until all <count> threads have called |
|---|
| 64 | | * Barrier.wait() and then allow all the caller threads to continue |
|---|
| 65 | | * in parallel. |
|---|
| | 63 | * Block the caller until all $(D_PARAM count) threads have called |
|---|
| | 64 | * $(D_CODE Barrier.wait()) and then allow all the caller threads to |
|---|
| | 65 | * continue in parallel. |
|---|
| 66 | 66 | */ |
|---|
| 67 | 67 | public void wait() |
|---|
| … | … | |
| 76 | 76 | |
|---|
| 77 | 77 | /** |
|---|
| 78 | | * Check the 'errorCode' argument against possible errno values and |
|---|
| 79 | | * throw an exception with the description of the error. |
|---|
| | 78 | * Check the $(D_PARAM errorCode) argument against possible values |
|---|
| | 79 | * of $(D_CODE SysError.lastCode()) and throw an exception with the |
|---|
| | 80 | * description of the error. |
|---|
| 80 | 81 | * |
|---|
| 81 | 82 | * Params: |
|---|
| 82 | | * errorCode = errno value; must not be 0. |
|---|
| | 83 | * errorCode = SysError.lastCode() value; must not be 0. |
|---|
| 83 | 84 | * file = name of the source file where the check is being |
|---|
| 84 | 85 | * made; you would normally use __FILE__ for this |
|---|
| … | … | |
| 90 | 91 | * Throws: |
|---|
| 91 | 92 | * AlreadyLockedException when the barrier cannot be destroyed because |
|---|
| 92 | | * it is already locked by another thread (EBUSY); |
|---|
| 93 | | * InvalidBarrierException when the barrier has not been properly |
|---|
| 94 | | * initialized (EINVAL); LockException for any of the other cases in |
|---|
| 95 | | * which errno is not 0. |
|---|
| 96 | | */ |
|---|
| 97 | | protected void checkError(int errorCode, char[] file, uint line) |
|---|
| | 93 | * it is already locked by another thread; InvalidBarrierException |
|---|
| | 94 | * when the barrier has not been properly initialized; LockException |
|---|
| | 95 | * for any of the other cases in which $(D_PARAM errorCode) is not 0. |
|---|
| | 96 | */ |
|---|
| | 97 | protected void checkError(uint errorCode, char[] file, uint line) |
|---|
| 98 | 98 | in |
|---|
| 99 | 99 | { |
|---|
| … | … | |
| 127 | 127 | /** |
|---|
| 128 | 128 | * Implements "barrier synchronization". |
|---|
| 129 | | * This class allows <count> number of threads to synchronize their |
|---|
| 130 | | * completion of (one round of) a task, which is known as "barrier |
|---|
| 131 | | * synchronization". After all the threads call <wait()> on the barrier |
|---|
| 132 | | * they are all atomically released and can begin a new round. |
|---|
| | 129 | * This class allows $(D_PARAM count) number of threads to synchronize |
|---|
| | 130 | * their completion of (one round of) a task, which is known as "barrier |
|---|
| | 131 | * synchronization". After all the threads call $(D_CODE wait()) on the |
|---|
| | 132 | * barrier they are all atomically released and can begin a new round. |
|---|
| 133 | 133 | * |
|---|
| 134 | 134 | * Remarks: |
|---|
| … | … | |
| 148 | 148 | Condition _finished; |
|---|
| 149 | 149 | |
|---|
| 150 | | public void init(uint count) |
|---|
| | 150 | public void init(uint count, Mutex mutex) |
|---|
| 151 | 151 | { |
|---|
| 152 | 152 | _runningThreads = count; |
|---|
| 153 | | _finished = new Condition(); |
|---|
| | 153 | _finished = new Condition(mutex); |
|---|
| 154 | 154 | } |
|---|
| 155 | 155 | } |
|---|
| … | … | |
| 169 | 169 | * efficiently solves the problem of what to do if all the first |
|---|
| 170 | 170 | * generation waiters don't leave the barrier before one of the |
|---|
| 171 | | * threads calls wait() again (i.e., starts up the next generation |
|---|
| 172 | | * barrier). |
|---|
| | 171 | * threads calls $(D_CODE wait()) again (i.e., starts up the next |
|---|
| | 172 | * generation barrier). |
|---|
| 173 | 173 | */ |
|---|
| 174 | 174 | SubBarrier[2] _subBarrier; |
|---|
| 175 | 175 | |
|---|
| 176 | 176 | /** |
|---|
| 177 | | * Initialize the barrier to synchronize <count> threads. |
|---|
| | 177 | * Initialize the barrier to synchronize $(D_PARAM count) threads. |
|---|
| 178 | 178 | */ |
|---|
| 179 | 179 | public this(uint count) |
|---|
| … | … | |
| 186 | 186 | _count = count; |
|---|
| 187 | 187 | _mutex = new Mutex(); |
|---|
| 188 | | _subBarrier[0].init(_count); |
|---|
| 189 | | _subBarrier[1].init(_count); |
|---|
| | 188 | _subBarrier[0].init(_count, _mutex); |
|---|
| | 189 | _subBarrier[1].init(_count, _mutex); |
|---|
| 190 | 190 | } |
|---|
| 191 | 191 | |
|---|
| … | … | |
| 202 | 202 | |
|---|
| 203 | 203 | /** |
|---|
| 204 | | * Block the caller until all <count> threads have called |
|---|
| 205 | | * Barrier.wait() and then allow all the caller threads to continue |
|---|
| 206 | | * in parallel. |
|---|
| | 204 | * Block the caller until all $(D_PARAM count) threads have called |
|---|
| | 205 | * $(D_CODE Barrier.wait()) and then allow all the caller threads to |
|---|
| | 206 | * continue in parallel. |
|---|
| 207 | 207 | */ |
|---|
| 208 | 208 | public void wait() |
|---|
| … | … | |
| 231 | 231 | while (current._runningThreads != _count) |
|---|
| 232 | 232 | { |
|---|
| 233 | | current._finished.wait(_mutex); |
|---|
| | 233 | current._finished.wait(); |
|---|
| 234 | 234 | } |
|---|
| 235 | 235 | } |
|---|
| … | … | |
| 246 | 246 | { |
|---|
| 247 | 247 | private import tango.util.locks.Mutex; |
|---|
| | 248 | private import tango.util.locks.LockException; |
|---|
| 248 | 249 | private import tango.core.Thread; |
|---|
| 249 | 250 | private import tango.io.Stdout; |
|---|
| 250 | | private import tango.util.locks.LockException; |
|---|
| | 251 | private import tango.text.convert.Integer; |
|---|
| | 252 | debug (barrier) |
|---|
| | 253 | { |
|---|
| | 254 | private import tango.util.log.Log; |
|---|
| | 255 | private import tango.util.log.ConsoleAppender; |
|---|
| | 256 | private import tango.util.log.DateLayout; |
|---|
| | 257 | } |
|---|
| 251 | 258 | |
|---|
| 252 | 259 | unittest |
|---|
| 253 | 260 | { |
|---|
| 254 | 261 | const uint MaxThreadCount = 100; |
|---|
| 255 | | const uint LoopsPerThread = 100000; |
|---|
| 256 | | |
|---|
| 257 | | Barrier allDone = new Barrier(MaxThreadCount); |
|---|
| | 262 | const uint LoopsPerThread = 10000; |
|---|
| | 263 | |
|---|
| | 264 | debug (barrier) |
|---|
| | 265 | { |
|---|
| | 266 | scope Logger log = Log.getLogger("barrier"); |
|---|
| | 267 | |
|---|
| | 268 | log.addAppender(new ConsoleAppender(new DateLayout())); |
|---|
| | 269 | |
|---|
| | 270 | log.info("Barrier test"); |
|---|
| | 271 | } |
|---|
| | 272 | |
|---|
| | 273 | Barrier barrier = new Barrier(MaxThreadCount); |
|---|
| 258 | 274 | Mutex mutex = new Mutex(); |
|---|
| 259 | 275 | uint count = 0; |
|---|
| … | … | |
| 262 | 278 | void barrierTestThread() |
|---|
| 263 | 279 | { |
|---|
| | 280 | debug (barrier) |
|---|
| | 281 | { |
|---|
| | 282 | Logger log = Log.getLogger("barrier." ~ Thread.getThis().name()); |
|---|
| | 283 | |
|---|
| | 284 | log.trace("Starting thread"); |
|---|
| | 285 | } |
|---|
| | 286 | |
|---|
| 264 | 287 | try |
|---|
| 265 | 288 | { |
|---|
| … | … | |
| 269 | 292 | // acquire the mutex before modifying it. |
|---|
| 270 | 293 | mutex.acquire(); |
|---|
| | 294 | // debug (barrier) |
|---|
| | 295 | // log.trace("Acquired mutex"); |
|---|
| 271 | 296 | count++; |
|---|
| | 297 | // debug (barrier) |
|---|
| | 298 | // log.trace("Releasing mutex"); |
|---|
| 272 | 299 | mutex.release(); |
|---|
| 273 | 300 | } |
|---|
| 274 | 301 | |
|---|
| 275 | 302 | // We wait for all the threads to finish counting. |
|---|
| 276 | | allDone.wait(); |
|---|
| | 303 | debug (barrier) |
|---|
| | 304 | log.trace("Waiting on barrier"); |
|---|
| | 305 | barrier.wait(); |
|---|
| | 306 | debug (barrier) |
|---|
| | 307 | log.trace("Barrier was opened"); |
|---|
| 277 | 308 | |
|---|
| 278 | 309 | // We make sure that all the threads exited the barrier after |
|---|
| 279 | 310 | // *all* of them had finished counting. |
|---|
| 280 | 311 | mutex.acquire(); |
|---|
| | 312 | // debug (barrier) |
|---|
| | 313 | // log.trace("Acquired mutex"); |
|---|
| 281 | 314 | if (count == MaxThreadCount * LoopsPerThread) |
|---|
| 282 | 315 | { |
|---|
| 283 | 316 | ++correctCount; |
|---|
| 284 | 317 | } |
|---|
| | 318 | // debug (barrier) |
|---|
| | 319 | // log.trace("Releasing mutex"); |
|---|
| 285 | 320 | mutex.release(); |
|---|
| 286 | 321 | } |
|---|
| 287 | 322 | catch (LockException e) |
|---|
| 288 | 323 | { |
|---|
| 289 | | Stderr.formatln("Lock exception caught inside Barrier test thread:\n{0}\n", |
|---|
| 290 | | e.toUtf8()); |
|---|
| | 324 | Stderr.formatln("Lock exception caught in Barrier test thread {0}:\n{1}\n", |
|---|
| | 325 | Thread.getThis().name, e.toUtf8()); |
|---|
| 291 | 326 | } |
|---|
| 292 | 327 | catch (Exception e) |
|---|
| 293 | 328 | { |
|---|
| 294 | | Stderr.formatln("Unexpected exception caught inside Barrier test thread:\n{0}\n", |
|---|
| 295 | | e.toUtf8()); |
|---|
| 296 | | } |
|---|
| 297 | | } |
|---|
| 298 | | |
|---|
| 299 | | auto group = new ThreadGroup(); |
|---|
| | 329 | Stderr.formatln("Unexpected exception caught in Barrier test thread {0}:\n{1}\n", |
|---|
| | 330 | Thread.getThis().name, e.toUtf8()); |
|---|
| | 331 | } |
|---|
| | 332 | } |
|---|
| | 333 | |
|---|
| | 334 | ThreadGroup group = new ThreadGroup(); |
|---|
| | 335 | Thread thread; |
|---|
| | 336 | char[10] tmp; |
|---|
| 300 | 337 | |
|---|
| 301 | 338 | for (uint i = 0; i < MaxThreadCount; ++i) |
|---|
| 302 | 339 | { |
|---|
| 303 | | group.create(&barrierTestThread); |
|---|
| 304 | | } |
|---|
| 305 | | |
|---|
| | 340 | thread = new Thread(&barrierTestThread); |
|---|
| | 341 | thread.name = "thread-" ~ format(tmp, i); |
|---|
| | 342 | |
|---|
| | 343 | group.add(thread); |
|---|
| | 344 | debug (barrier) |
|---|
| | 345 | log.trace("Created thread " ~ thread.name); |
|---|
| | 346 | thread.start(); |
|---|
| | 347 | } |
|---|
| | 348 | |
|---|
| | 349 | debug (barrier) |
|---|
| | 350 | log.trace("Waiting for threads to finish"); |
|---|
| 306 | 351 | group.joinAll(); |
|---|
| 307 | 352 | |
|---|
| 308 | | if (count != MaxThreadCount * LoopsPerThread) |
|---|
| 309 | | { |
|---|
| 310 | | Stderr.formatln("The Barrier is not working properly: the counter has an incorrect value"); |
|---|
| 311 | | assert(false); |
|---|
| | 353 | if (count == MaxThreadCount * LoopsPerThread) |
|---|
| | 354 | { |
|---|
| | 355 | debug (barrier) |
|---|
| | 356 | log.info("The Barrier test was successful"); |
|---|
| | 357 | } |
|---|
| | 358 | else |
|---|
| | 359 | { |
|---|
| | 360 | debug (barrier) |
|---|
| | 361 | { |
|---|
| | 362 | log.error("The Barrier is not working properly: the counter has an incorrect value"); |
|---|
| | 363 | assert(false); |
|---|
| | 364 | } |
|---|
| | 365 | else |
|---|
| | 366 | { |
|---|
| | 367 | assert(false, "The Barrier is not working properly: the counter has an incorrect value"); |
|---|
| | 368 | } |
|---|
| 312 | 369 | } |
|---|
| 313 | 370 | } |
|---|
| r1901 |
r2010 |
|
| 22 | 22 | * condition under the protection of a mutual exclusion lock (mutex) until |
|---|
| 23 | 23 | * the condition is satisfied. That is, the mutex must have been held by |
|---|
| 24 | | * the thread before calling wait or notifyOne/notifyAll on the condition. |
|---|
| 25 | | * If the condition is false, a thread blocks on a condition variable and |
|---|
| 26 | | * atomically releases the mutex that is waiting for the condition to |
|---|
| 27 | | * change. If another thread changes the condition, it may wake up waiting |
|---|
| 28 | | * threads by signaling the associated condition variable. The waiting |
|---|
| 29 | | * threads, upon awakening, reacquire the mutex and re-evaluate the |
|---|
| 30 | | * condition. |
|---|
| | 24 | * the thread before calling $(D_CODE wait()) or $(D_CODE notify()) / |
|---|
| | 25 | * $(D_CODE notifyAll()) on the condition. If the condition is false, a |
|---|
| | 26 | * thread blocks on a condition variable and atomically releases the mutex |
|---|
| | 27 | * that is waiting for the condition to change. If another thread changes |
|---|
| | 28 | * the condition, it may wake up waiting threads by signaling the associated |
|---|
| | 29 | * condition variable. The waiting threads, upon awakening, reacquire the |
|---|
| | 30 | * mutex and re-evaluate the condition. |
|---|
| 31 | 31 | * |
|---|
| 32 | 32 | * Remarks: |
|---|
| 33 | | * On POSIX-compatible platforms the Condition is implemented using a |
|---|
| 34 | | * pthread_cond_t from the pthread API.The Windows API (before Windows |
|---|
| 35 | | * Vista) does not provide a native condition variable, so it is emulated |
|---|
| 36 | | * with a mutex, a semaphore and an event. The Windows condition variable |
|---|
| 37 | | * emulation is based on the ACE_Condition template class from the |
|---|
| 38 | | * $LINK2(http://www.cs.wustl.edu/~schmidt/ACE.html ACE framework). |
|---|
| | 33 | * On POSIX-compatible platforms the $(D_CODE Condition) is implemented using a |
|---|
| | 34 | * $(D_CODE pthread_cond_t) from the pthread API. The Windows API (before |
|---|
| | 35 | * Windows Vista) does not provide a native condition variable, so it is |
|---|
| | 36 | * emulated with a mutex, a semaphore and an event. The Windows condition |
|---|
| | 37 | * variable emulation is based on the ACE_Condition template class from the |
|---|
| | 38 | * $(LINK2 http://www.cs.wustl.edu/~schmidt/ACE.html ACE framework). |
|---|
| 39 | 39 | * |
|---|
| 40 | 40 | * Examples: |
|---|
| … | … | |
| 51 | 51 | * while (!conditionBeingWaitedFor) |
|---|
| 52 | 52 | * { |
|---|
| 53 | | * success = cond.wait(lock, timeout); |
|---|
| | 53 | * success = cond.wait(timeout); |
|---|
| 54 | 54 | * } |
|---|
| 55 | 55 | * return success; |
|---|
| … | … | |
| 65 | 65 | * |
|---|
| 66 | 66 | * conditionBeingWaitedFor = true; |
|---|
| 67 | | * cond.notifyOne(); |
|---|
| | 67 | * cond.notify(); |
|---|
| 68 | 68 | * } |
|---|
| 69 | 69 | * --- |
|---|
| … | … | |
| 77 | 77 | class Condition |
|---|
| 78 | 78 | { |
|---|
| 79 | | pthread_cond_t _cond; |
|---|
| | 79 | private pthread_cond_t _cond; |
|---|
| | 80 | private Mutex _externalMutex; |
|---|
| 80 | 81 | |
|---|
| 81 | 82 | /** |
|---|
| 82 | 83 | * Initialize the condition variable. |
|---|
| 83 | 84 | */ |
|---|
| 84 | | public this() |
|---|
| 85 | | { |
|---|
| | 85 | public this(Mutex mutex) |
|---|
| | 86 | in |
|---|
| | 87 | { |
|---|
| | 88 | assert(mutex !is null); |
|---|
| | 89 | } |
|---|
| | 90 | body |
|---|
| | 91 | { |
|---|
| | 92 | _externalMutex = mutex; |
|---|
| 86 | 93 | // pthread_cond_init() will never return an error on Linux. |
|---|
| 87 | 94 | pthread_cond_init(&_cond, null); |
|---|
| 88 | 95 | } |
|---|
| 89 | 96 | |
|---|
| | 97 | /+ IMPORTANT: |
|---|
| | 98 | This method must remain commented out until the Mutex module that |
|---|
| | 99 | uses each object's implicit monitor is integrated into Tango. |
|---|
| | 100 | |
|---|
| | 101 | /** |
|---|
| | 102 | * Initialize the condition variable with a generic object. |
|---|
| | 103 | */ |
|---|
| | 104 | public this(Object object) |
|---|
| | 105 | in |
|---|
| | 106 | { |
|---|
| | 107 | assert(object !is null); |
|---|
| | 108 | } |
|---|
| | 109 | body |
|---|
| | 110 | { |
|---|
| | 111 | this(cast(Mutex) new MutexProxy(object)); |
|---|
| | 112 | } |
|---|
| | 113 | +/ |
|---|
| | 114 | |
|---|
| 90 | 115 | /** |
|---|
| 91 | 116 | * Implicitly destroy the condition variable. |
|---|
| … | … | |
| 102 | 127 | |
|---|
| 103 | 128 | /** |
|---|
| 104 | | * Notify only $B(one) waiting thread that the condition is true. |
|---|
| | 129 | * Returns a reference to the underlying mutex; |
|---|
| | 130 | */ |
|---|
| | 131 | public Mutex mutex() |
|---|
| | 132 | { |
|---|
| | 133 | return _externalMutex; |
|---|
| | 134 | } |
|---|
| | 135 | |
|---|
| | 136 | /** |
|---|
| | 137 | * Notify only $(B one) waiting thread that the condition is true. |
|---|
| 105 | 138 | * |
|---|
| 106 | 139 | * Remarks: |
|---|
| 107 | 140 | * The external mutex must be locked before calling this method. |
|---|
| 108 | 141 | */ |
|---|
| 109 | | void notifyOne() |
|---|
| | 142 | public void notify() |
|---|
| 110 | 143 | { |
|---|
| 111 | 144 | // pthread_cond_signal() will never return an error on Linux, but |
|---|
| … | … | |
| 120 | 153 | |
|---|
| 121 | 154 | /** |
|---|
| 122 | | * Notify $B(all) waiting threads that the condition is true. |
|---|
| | 155 | * Notify $(B all) waiting threads that the condition is true. |
|---|
| 123 | 156 | * |
|---|
| 124 | 157 | * Remarks: |
|---|
| 125 | 158 | * The external mutex must be locked before calling this method. |
|---|
| 126 | 159 | */ |
|---|
| 127 | | void notifyAll() |
|---|
| | 160 | public void notifyAll() |
|---|
| 128 | 161 | { |
|---|
| 129 | 162 | // pthread_cond_broadcast() will never return an error on Linux, |
|---|
| … | … | |
| 138 | 171 | |
|---|
| 139 | 172 | /** |
|---|
| 140 | | * Block on the condition. |
|---|
| | 173 | * Block until the condition is notified from another thread. |
|---|
| 141 | 174 | * |
|---|
| 142 | 175 | * Remarks: |
|---|
| 143 | 176 | * The external mutex must be locked before calling this method. |
|---|
| 144 | 177 | */ |
|---|
| 145 | | void wait(Mutex externalMutex) |
|---|
| 146 | | in |
|---|
| 147 | | { |
|---|
| 148 | | assert(externalMutex !is null); |
|---|
| 149 | | } |
|---|
| 150 | | body |
|---|
| | 178 | public void wait() |
|---|
| 151 | 179 | { |
|---|
| 152 | 180 | // pthread_cond_wait() will never return an error on Linux, |
|---|
| 153 | 181 | // but it may on other platforms. |
|---|
| 154 | | int rc = pthread_cond_wait(&_cond, &externalMutex._mutex); |
|---|
| | 182 | int rc = pthread_cond_wait(&_cond, _externalMutex.mutex()); |
|---|
| 155 | 183 | |
|---|
| 156 | 184 | if (rc != 0) |
|---|
| … | … | |
| 162 | 190 | /** |
|---|
| 163 | 191 | * Block on the condition, or until the specified (relative) amount |
|---|
| 164 | | * of time has passed. If $D_PARAM(timeout) == $D_CODE(Interval.max) |
|---|
| | 192 | * of time has passed. If ($D_PARAM timeout) == $(D_CODE Interval.max) |
|---|
| 165 | 193 | * there is no timeout. |
|---|
| 166 | 194 | * |
|---|
| … | … | |
| 171 | 199 | * The external mutex must be locked before calling this method. |
|---|
| 172 | 200 | */ |
|---|
| 173 | | bool wait(Mutex externalMutex, Interval timeout) |
|---|
| 174 | | in |
|---|
| 175 | | { |
|---|
| 176 | | assert(externalMutex !is null); |
|---|
| 177 | | } |
|---|
| 178 | | body |
|---|
| | 201 | public bool wait(Interval timeout) |
|---|
| 179 | 202 | { |
|---|
| 180 | 203 | if (timeout == Interval.max) |
|---|
| 181 | 204 | { |
|---|
| 182 | | wait(externalMutex); |
|---|
| | 205 | wait(); |
|---|
| 183 | 206 | return true; |
|---|
| 184 | 207 | } |
|---|
| … | … | |
| 188 | 211 | timespec ts; |
|---|
| 189 | 212 | |
|---|
| 190 | | rc = pthread_cond_timedwait(&_cond, &externalMutex._mutex, |
|---|
| | 213 | rc = pthread_cond_timedwait(&_cond, _externalMutex.mutex(), |
|---|
| 191 | 214 | toTimespec(&ts, toAbsoluteTime(timeout))); |
|---|
| 192 | 215 | |
|---|
| … | … | |
| 209 | 232 | |
|---|
| 210 | 233 | /** |
|---|
| 211 | | * Check the 'errorCode' argument against possible errno values and |
|---|
| 212 | | * throw an exception with the description of the error. |
|---|
| | 234 | * Check the $(D_PARAM errorCode) argument against possible values |
|---|
| | 235 | * of $(D_CODE SysError.lastCode()) and throw an exception with the |
|---|
| | 236 | * description of the error. |
|---|
| 213 | 237 | * |
|---|
| 214 | 238 | * Params: |
|---|
| 215 | | * errorCode = errno value; must not be 0. |
|---|
| | 239 | * errorCode = SysError.lastCode() value; must not be 0. |
|---|
| 216 | 240 | * file = name of the source file where the check is being |
|---|
| 217 | 241 | * made; you would normally use __FILE__ for this |
|---|
| … | … | |
| 223 | 247 | * Throws: |
|---|
| 224 | 248 | * AlreadyLockedException when the mutex has already been locked by |
|---|
| 225 | | * another thread (EBUSY); DeadlockException when the mutex has already |
|---|
| 226 | | * been locked by the calling thread (EDEADLK); InvalidMutexException |
|---|
| 227 | | * when the mutex has not been properly initialized (EINVAL); |
|---|
| 228 | | * MutexOwnerException when the calling thread does not own the mutex |
|---|
| 229 | | * (EPERM); LockException for any of the other cases in which errno is |
|---|
| 230 | | * not 0. |
|---|
| 231 | | */ |
|---|
| 232 | | protected void checkError(int errorCode, char[] file, uint line) |
|---|
| | 249 | * another thread; DeadlockException when the mutex has already |
|---|
| | 250 | * been locked by the calling thread; InvalidMutexException |
|---|
| | 251 | * when the mutex has not been properly initialized; |
|---|
| | 252 | * MutexOwnerException when the calling thread does not own the mutex; |
|---|
| | 253 | * LockException for any of the other cases in which |
|---|
| | 254 | * $(D_PARAM errorCode) is not 0. |
|---|
| | 255 | */ |
|---|
| | 256 | protected void checkError(uint errorCode, char[] file, uint line) |
|---|
| 233 | 257 | in |
|---|
| 234 | 258 | { |
|---|
| … | … | |
| 271 | 295 | private Event _waitersDone; |
|---|
| 272 | 296 | private bool _wasBroadcast = false; |
|---|
| | 297 | private Mutex _externalMutex; |
|---|
| 273 | 298 | |
|---|
| 274 | 299 | /** |
|---|
| 275 | 300 | * Initialize the condition variable. |
|---|
| 276 | 301 | */ |
|---|
| 277 | | public this() |
|---|
| | 302 | public this(Mutex mutex) |
|---|
| | 303 | in |
|---|
| | 304 | { |
|---|
| | 305 | assert(mutex !is null); |
|---|
| | 306 | } |
|---|
| | 307 | body |
|---|
| 278 | 308 | { |
|---|
| 279 | 309 | _wasBroadcast = 0; |
|---|
| … | … | |
| 288 | 318 | |
|---|
| 289 | 319 | _waitersDone = new Event(); |
|---|
| 290 | | } |
|---|
| | 320 | |
|---|
| | 321 | _externalMutex = mutex; |
|---|
| | 322 | } |
|---|
| | 323 | |
|---|
| | 324 | /+ IMPORTANT: |
|---|
| | 325 | This method must remain commented out until the Mutex module that |
|---|
| | 326 | uses each object's implicit monitor is integrated into Tango. |
|---|
| | 327 | |
|---|
| | 328 | /** |
|---|
| | 329 | * Initialize the condition variable with a generic Object to be used |
|---|
| | 330 | * as a mutex. |
|---|
| | 331 | */ |
|---|
| | 332 | public this(Object object) |
|---|
| | 333 | in |
|---|
| | 334 | { |
|---|
| | 335 | assert(object !is null); |
|---|
| | 336 | } |
|---|
| | 337 | body |
|---|
| | 338 | { |
|---|
| | 339 | this(cast(Mutex) new MutexProxy(object)); |
|---|
| | 340 | } |
|---|
| | 341 | +/ |
|---|
| 291 | 342 | |
|---|
| 292 | 343 | /** |
|---|
| … | … | |
| 295 | 346 | public ~this() |
|---|
| 296 | 347 | { |
|---|
| | 348 | _externalMutex = null; |
|---|
| 297 | 349 | delete _waitersDone; |
|---|
| 298 | 350 | delete _waitersLock; |
|---|
| … | … | |
| 301 | 353 | |
|---|
| 302 | 354 | /** |
|---|
| 303 | | * Notify only $B(one) waiting thread that the condition is true. |
|---|
| | 355 | * Notify only $(B one) waiting thread that the condition is true. |
|---|
| 304 | 356 | * |
|---|
| 305 | 357 | * Remarks: |
|---|
| 306 | 358 | * The external mutex must be locked before calling this method. |
|---|
| 307 | 359 | */ |
|---|
| 308 | | void notifyOne() |
|---|
| | 360 | public void notify() |
|---|
| 309 | 361 | { |
|---|
| 310 | 362 | // If there aren't any waiters, then this is a no-op. Note that |
|---|
| 311 | | // this function *must* be called with the <externalMutex> held |
|---|
| | 363 | // this function *must* be called with the 'externalMutex' held |
|---|
| 312 | 364 | // since otherwise there is a race condition that can lead to the |
|---|
| 313 | | // lost wakeup bug... This is needed to ensure that the <_waitersCount> |
|---|
| | 365 | // lost wakeup bug... This is needed to ensure that the '_waitersCount' |
|---|
| 314 | 366 | // value is not in an inconsistent internal state while being |
|---|
| 315 | 367 | // updated by another thread. |
|---|
| … | … | |
| 325 | 377 | |
|---|
| 326 | 378 | /** |
|---|
| 327 | | * Notify $B(all) waiting threads that the condition is true. |
|---|
| | 379 | * Notify $(B all) waiting threads that the condition is true. |
|---|
| 328 | 380 | * |
|---|
| 329 | 381 | * Remarks: |
|---|
| 330 | 382 | * The external mutex must be locked before calling this method. |
|---|
| 331 | 383 | */ |
|---|
| 332 | | void notifyAll() |
|---|
| | 384 | public void notifyAll() |
|---|
| 333 | 385 | { |
|---|
| 334 | 386 | bool hasWaiters = false; |
|---|
| … | … | |
| 336 | 388 | // The <externalMutex> must be locked before this call is made. |
|---|
| 337 | 389 | |
|---|
| 338 | | // This is needed to ensure that <_waitersCount> and <_wasBroadcast> are |
|---|
| | 390 | // This is needed to ensure that '_waitersCount' and '_wasBroadcast' are |
|---|
| 339 | 391 | // consistent relative to each other. |
|---|
| 340 | 392 | _waitersLock.acquire(); |
|---|
| … | … | |
| 345 | 397 | // Record the fact that we are broadcasting. This helps the |
|---|
| 346 | 398 | // Condition.wait() method know how to optimize itself. Be |
|---|
| 347 | | // sure to set this with the <_waitersLock> held. |
|---|
| | 399 | // sure to set this with the '_waitersLock' held. |
|---|
| 348 | 400 | _wasBroadcast = true; |
|---|
| 349 | 401 | hasWaiters = true; |
|---|
| … | … | |
| 363 | 415 | _waitersDone.wait(); |
|---|
| 364 | 416 | |
|---|
| 365 | | // This is okay, even without the <_waitersLock> held, because |
|---|
| | 417 | // This is okay, even without the '_waitersLock' held, because |
|---|
| 366 | 418 | // no other waiter threads can wake up to access it. |
|---|
| 367 | 419 | _wasBroadcast = false; |
|---|
|