File tree Expand file tree Collapse file tree 3 files changed +61
-0
lines changed
core/src/main/scala/japgolly/scalajs/react
test/src/test/scala/japgolly/scalajs/react/core Expand file tree Collapse file tree 3 files changed +61
-0
lines changed Original file line number Diff line number Diff line change @@ -327,6 +327,44 @@ object AsyncCallback {
327327 _ <- b.complete.when_(count <= 0 )
328328 } yield new CountDownLatch (count, b)
329329
330+ // ===================================================================================================================
331+
332+ final class Mutex private [AsyncCallback ]() {
333+
334+ private var mutex : Option [Barrier ] =
335+ None
336+
337+ private val release : Callback =
338+ CallbackTo {
339+ val old = mutex
340+ mutex = None
341+ old
342+ }.flatMap(Callback .traverseOption(_)(_.complete))
343+
344+ /** Wrap a [[AsyncCallback ]] so that it executes in the mutex.
345+ *
346+ * Note: THIS IS NOT RE-ENTRANT. Calling this from within the mutex will block.
347+ */
348+ def apply [A ](ac : AsyncCallback [A ]): AsyncCallback [A ] =
349+ byName {
350+
351+ mutex match {
352+ case None =>
353+ // Mutex empty
354+ val b = barrier.runNow()
355+ mutex = Some (b)
356+ ac.finallyRunSync(release)
357+
358+ case Some (b) =>
359+ // Mutex in use
360+ b.await >> apply(ac)
361+ }
362+ }
363+ }
364+
365+ /** Creates a new (non-reentrant) mutex. */
366+ def mutex : CallbackTo [Mutex ] =
367+ CallbackTo (new Mutex )
330368}
331369
332370// █████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
Original file line number Diff line number Diff line change @@ -36,6 +36,18 @@ This entire release is focused on `AsyncCallback`.
3636 }
3737 ```
3838
39+ * Added `mutex` which returns a `AsyncCallback.Mutex` :
40+
41+ ```scala
42+ AsyncCallback .Mutex {
43+ /** Wrap a AsyncCallback so that it executes in the mutex.
44+ *
45+ * Note: THIS IS NOT RE-ENTRANT. Calling this from within the mutex will block.
46+ */
47+ def apply [A ](ac : AsyncCallback [A ]): AsyncCallback [A ]
48+ }
49+ ```
50+
3951 * You can now add a `_` suffix to the following to return an `AsyncCallback[Unit]` and be more efficient under- the- hood :
4052
4153 * `traverse`
Original file line number Diff line number Diff line change @@ -236,5 +236,16 @@ object AsyncCallbackTest extends TestSuite {
236236 }
237237 }
238238
239+ " mutex" - asyncTest {
240+ for {
241+ mutex <- AsyncCallback .mutex.asAsyncCallback
242+ b <- AsyncCallback .barrier.asAsyncCallback
243+ _ <- mutex(b.await).fork_.asAsyncCallback // mutex start
244+ t <- mutex(AsyncCallback .unit).timeoutMs(500 ).delayMs(1 )
245+ _ <- b.complete.asAsyncCallback // mutex end
246+ _ <- mutex(AsyncCallback .unit)
247+ } yield assert(t.isEmpty)
248+ }
249+
239250 }
240251}
You can’t perform that action at this time.
0 commit comments