@@ -447,6 +447,92 @@ object AsyncCallback {
447447 /** Creates a new (non-reentrant) read/write mutex. */
448448 def readWriteMutex : CallbackTo [ReadWriteMutex ] =
449449 CallbackTo (new ReadWriteMutex )
450+
451+ // ===================================================================================================================
452+
453+ final class Ref [A ] private [AsyncCallback ](atomicReads : Boolean , atomicWrites : Boolean ) {
454+
455+ private val mutex = readWriteMutex.runNow()
456+ private val initialised = barrier.runNow()
457+ private var _value : A = _
458+
459+ private val markInitialised =
460+ initialised.complete.asAsyncCallback
461+
462+ /** If this hasn't been set yet, it will block until it is set. */
463+ val get : AsyncCallback [A ] = {
464+ var readValue : AsyncCallback [A ] =
465+ AsyncCallback .delay(_value)
466+
467+ if (atomicReads)
468+ readValue = mutex.read(readValue)
469+
470+ initialised.await >> readValue
471+ }
472+
473+ /** Synchronously return whatever value is currently stored. (Ignores atomicity). */
474+ lazy val getIfAvailable : CallbackTo [Option [A ]] =
475+ initialised.isComplete.map {
476+ case true => Some (_value)
477+ case false => None
478+ }
479+
480+ private def inWriteMutex [B ](a : AsyncCallback [B ]): AsyncCallback [B ] =
481+ if (atomicWrites)
482+ mutex.write(a)
483+ else
484+ a
485+
486+ // This must only be called within the write mutex
487+ private def setWithinMutex (c : AsyncCallback [A ]): AsyncCallback [Unit ] =
488+ for {
489+ a <- c
490+ _ <- AsyncCallback .delay { _value = a }
491+ _ <- markInitialised
492+ } yield ()
493+
494+ def set (a : => A ): AsyncCallback [Unit ] =
495+ setAsync(AsyncCallback .delay(a))
496+
497+ def setSync (c : CallbackTo [A ]): AsyncCallback [Unit ] =
498+ setAsync(c.asAsyncCallback)
499+
500+ def setAsync (c : AsyncCallback [A ]): AsyncCallback [Unit ] =
501+ inWriteMutex(setWithinMutex(c))
502+
503+ /** Returns whether or not the value was set. */
504+ def setIfUnset (a : => A ): AsyncCallback [Boolean ] =
505+ setIfUnsetAsync(AsyncCallback .delay(a))
506+
507+ /** Returns whether or not the value was set. */
508+ def setIfUnsetSync (c : CallbackTo [A ]): AsyncCallback [Boolean ] =
509+ setIfUnsetAsync(c.asAsyncCallback)
510+
511+ /** Returns whether or not the value was set. */
512+ def setIfUnsetAsync (c : AsyncCallback [A ]): AsyncCallback [Boolean ] =
513+ initialised.isComplete.asAsyncCallback.flatMap {
514+ case true => AsyncCallback .pure(false )
515+ case false =>
516+ inWriteMutex {
517+ AsyncCallback .byName {
518+ if (initialised.isComplete.runNow())
519+ AsyncCallback .pure(false )
520+ else
521+ setWithinMutex(c).ret(true )
522+ }
523+ }
524+ }
525+ }
526+
527+ @ inline def ref [A ]: CallbackTo [Ref [A ]] =
528+ ref()
529+
530+ def ref [A ](allowStaleReads : Boolean = false ,
531+ atomicWrites : Boolean = true ): CallbackTo [Ref [A ]] =
532+ CallbackTo (new Ref (
533+ atomicReads = atomicWrites && ! allowStaleReads,
534+ atomicWrites = atomicWrites,
535+ ))
450536}
451537
452538// █████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
0 commit comments