@@ -225,6 +225,41 @@ class ReadWriteMutex
225
225
}
226
226
}
227
227
228
+ /**
229
+ * Attempts to acquire a read lock on the enclosing mutex. If one can
230
+ * be obtained without blocking, the lock is acquired and true is
231
+ * returned. If not, the function blocks until either the lock can be
232
+ * obtained or the time elapsed exceeds $(D_PARAM timeout), returning
233
+ * true if the lock was acquired and false if the function timed out.
234
+ *
235
+ * Params:
236
+ * timeout = maximum amount of time to wait for the lock
237
+ * Returns:
238
+ * true if the lock was acquired and false if not.
239
+ */
240
+ bool tryLock (Duration timeout)
241
+ {
242
+ const initialTime = MonoTime.currTime;
243
+ synchronized ( m_commonMutex )
244
+ {
245
+ ++ m_numQueuedReaders;
246
+ scope (exit) -- m_numQueuedReaders;
247
+
248
+ while (shouldQueueReader)
249
+ {
250
+ const timeElapsed = MonoTime.currTime - initialTime;
251
+ if (timeElapsed >= timeout)
252
+ return false ;
253
+ auto nextWait = timeout - timeElapsed;
254
+ // Avoid problems calling wait(Duration) with huge arguments.
255
+ enum maxWaitPerCall = dur! " hours" (24 * 365 );
256
+ m_readerQueue.wait(nextWait < maxWaitPerCall ? nextWait : maxWaitPerCall);
257
+ }
258
+ ++ m_numActiveReaders;
259
+ return true ;
260
+ }
261
+ }
262
+
228
263
229
264
private :
230
265
@property bool shouldQueueReader()
@@ -341,6 +376,40 @@ class ReadWriteMutex
341
376
}
342
377
}
343
378
379
+ /**
380
+ * Attempts to acquire a write lock on the enclosing mutex. If one can
381
+ * be obtained without blocking, the lock is acquired and true is
382
+ * returned. If not, the function blocks until either the lock can be
383
+ * obtained or the time elapsed exceeds $(D_PARAM timeout), returning
384
+ * true if the lock was acquired and false if the function timed out.
385
+ *
386
+ * Params:
387
+ * timeout = maximum amount of time to wait for the lock
388
+ * Returns:
389
+ * true if the lock was acquired and false if not.
390
+ */
391
+ bool tryLock (Duration timeout)
392
+ {
393
+ const initialTime = MonoTime.currTime;
394
+ synchronized ( m_commonMutex )
395
+ {
396
+ ++ m_numQueuedWriters;
397
+ scope (exit) -- m_numQueuedWriters;
398
+
399
+ while (shouldQueueWriter)
400
+ {
401
+ const timeElapsed = MonoTime.currTime - initialTime;
402
+ if (timeElapsed >= timeout)
403
+ return false ;
404
+ auto nextWait = timeout - timeElapsed;
405
+ // Avoid problems calling wait(Duration) with huge arguments.
406
+ enum maxWaitPerCall = dur! " hours" (24 * 365 );
407
+ m_writerQueue.wait(nextWait < maxWaitPerCall ? nextWait : maxWaitPerCall);
408
+ }
409
+ ++ m_numActiveWriters;
410
+ return true ;
411
+ }
412
+ }
344
413
345
414
private :
346
415
@property bool shouldQueueWriter()
@@ -526,3 +595,79 @@ unittest
526
595
runTest(ReadWriteMutex .Policy.PREFER_READERS );
527
596
runTest(ReadWriteMutex .Policy.PREFER_WRITERS );
528
597
}
598
+
599
+ unittest
600
+ {
601
+ import core.atomic , core.thread ;
602
+ __gshared ReadWriteMutex rwmutex;
603
+ shared static bool threadTriedOnceToGetLock;
604
+ shared static bool threadFinallyGotLock;
605
+
606
+ rwmutex = new ReadWriteMutex ();
607
+ atomicFence;
608
+ const maxTimeAllowedForTest = dur! " seconds" (20 );
609
+ // Test ReadWriteMutex.Reader.tryLock(Duration).
610
+ {
611
+ static void testReaderTryLock ()
612
+ {
613
+ assert (! rwmutex.reader.tryLock(Duration.min));
614
+ threadTriedOnceToGetLock.atomicStore(true );
615
+ assert (rwmutex.reader.tryLock(Duration.max));
616
+ threadFinallyGotLock.atomicStore(true );
617
+ rwmutex.reader.unlock;
618
+ }
619
+ assert (rwmutex.writer.tryLock(Duration.zero), " should have been able to obtain lock without blocking" );
620
+ auto otherThread = new Thread (&testReaderTryLock).start;
621
+ const failIfThisTimeisReached = MonoTime.currTime + maxTimeAllowedForTest;
622
+ Thread .yield;
623
+ // We started otherThread with the writer lock held so otherThread's
624
+ // first rwlock.reader.tryLock with timeout Duration.min should fail.
625
+ while (! threadTriedOnceToGetLock.atomicLoad)
626
+ {
627
+ assert (MonoTime.currTime < failIfThisTimeisReached, " timed out" );
628
+ Thread .yield;
629
+ }
630
+ rwmutex.writer.unlock;
631
+ // Soon after we release the writer lock otherThread's second
632
+ // rwlock.reader.tryLock with timeout Duration.max should succeed.
633
+ while (! threadFinallyGotLock.atomicLoad)
634
+ {
635
+ assert (MonoTime.currTime < failIfThisTimeisReached, " timed out" );
636
+ Thread .yield;
637
+ }
638
+ otherThread.join;
639
+ }
640
+ threadTriedOnceToGetLock.atomicStore(false ); // Reset.
641
+ threadFinallyGotLock.atomicStore(false ); // Reset.
642
+ // Test ReadWriteMutex.Writer.tryLock(Duration).
643
+ {
644
+ static void testWriterTryLock ()
645
+ {
646
+ assert (! rwmutex.writer.tryLock(Duration.min));
647
+ threadTriedOnceToGetLock.atomicStore(true );
648
+ assert (rwmutex.writer.tryLock(Duration.max));
649
+ threadFinallyGotLock.atomicStore(true );
650
+ rwmutex.writer.unlock;
651
+ }
652
+ assert (rwmutex.reader.tryLock(Duration.zero), " should have been able to obtain lock without blocking" );
653
+ auto otherThread = new Thread (&testWriterTryLock).start;
654
+ const failIfThisTimeisReached = MonoTime.currTime + maxTimeAllowedForTest;
655
+ Thread .yield;
656
+ // We started otherThread with the reader lock held so otherThread's
657
+ // first rwlock.writer.tryLock with timeout Duration.min should fail.
658
+ while (! threadTriedOnceToGetLock.atomicLoad)
659
+ {
660
+ assert (MonoTime.currTime < failIfThisTimeisReached, " timed out" );
661
+ Thread .yield;
662
+ }
663
+ rwmutex.reader.unlock;
664
+ // Soon after we release the reader lock otherThread's second
665
+ // rwlock.writer.tryLock with timeout Duration.max should succeed.
666
+ while (! threadFinallyGotLock.atomicLoad)
667
+ {
668
+ assert (MonoTime.currTime < failIfThisTimeisReached, " timed out" );
669
+ Thread .yield;
670
+ }
671
+ otherThread.join;
672
+ }
673
+ }
0 commit comments