How do I protect shared data with MutexLock in EK9?

← Design Patterns and Idioms · Ref: Q213

MutexLock of T wraps a value and controls access through MutexKey callback functions. The canonical EK9 pattern is to hold the lock as a FIELD on a wrapping class and expose the protected operations as class methods. The lock never escapes the wrapping class.

FIELD-ON-CLASS PATTERN

Declare the MutexLock as a field with an inline initialiser:

  Inventory
    lockedStock as MutexLock of List of String: MutexLock(List() of String)

The field initialiser is the only legal MutexLock construction site (E08254/E08256 reject locals and collections of locks).

MUTEXKEY CALLBACK

Inside each method on the wrapping class, declare a dynamic MutexKey function. The function body is the critical section; lockedItem is the protected value:

  add()
    -> item as String
    accessKey <- (item) is MutexKey of List of String as function
      lockedItem += item
    require lockedStock.enter(accessKey)

ENTER THE LOCK

Call enter() (blocking) or tryEnter() (non-blocking) inside the class's own method. The lock releases automatically when the callback returns.

COPY DATA OUT

To expose a snapshot of the protected value, copy it out with :=: into a return-target captured in the callback:

  read()
    <- rtn as List of String: List() of String
    accessKey <- (rtn) is MutexKey of List of String as function
      rtn :=: lockedItem
    require lockedStock.enter(accessKey)

WHY FIELD-ON-CLASS

EK9's compile-time data-race detection needs every lock to have a stable field-rooted home. Locks declared as locals or held inside collections defeat the static analysis. Wrapping the lock in a class also keeps the API surface small — callers see only the protected operations.

See Q158 for MutexLock basics. See Q159 for parallel processing. See Q52 for dynamic functions. See Q267 for anti-patterns including mutable shared state.

Example

defines module qa.patterns.mutex

  defines class

    //Canonical pattern: MutexLock as a field on a wrapping class.
    //The class methods are the only API to the protected stock list;
    //the lock itself never escapes.
    Inventory
      lockedStock as MutexLock of List of String: MutexLock(List() of String)

      add()
        -> item as String

        accessKey <- (item) is MutexKey of List of String as function
          lockedItem += item

        require lockedStock.enter(accessKey)

      read()
        <- rtn as List of String: List() of String

        accessKey <- (rtn) is MutexKey of List of String as function
          rtn :=: lockedItem

        require lockedStock.enter(accessKey)

      default operator ?

  defines program

    MutexPatternDemo()
      stdout <- Stdout()

      //Construct the wrapper — the MutexLock is created by the
      //class's field initialiser, not here in user code.
      inventory <- Inventory()

      inventory.add("widgets")
      inventory.add("sprockets")

      snapshot <- inventory.read()
      stdout.println(`Inventory snapshot: ${$snapshot}`)

Common mistakes

E08256 — MutexLock must be declared as a field on a class with an inline initialiser. The field-rooted home is required for compile-time lock-identity tracking. See ek9 -h E08256 for details.

Incorrect:

lock <- MutexLock(List() of String)   //local — rejected

Correct:

Inventory
      lockedStock as MutexLock of List of String: MutexLock(List() of String)
Other ways to ask this
  • How do I use MutexLock with a callback in EK9?
  • What is the MutexKey pattern in EK9?
  • How do I safely update locked data in EK9?

Coming from another language?

Java: synchronized blocks or ReentrantLock with try/finally. Python: threading.Lock with context manager. Rust: Mutex<T>::lock() returns MutexGuard. Go: sync.Mutex with Lock()/Unlock(). EK9: MutexLock as a class field, accessed through MutexKey callbacks inside class methods. The field-rooted home enables compile-time data-race and deadlock detection.

Keywords: enter, pattern, shared, lock, class, protect, safety, field, thread, idiom, key, mutex, concurrent, design