Motr  M0
FOM long lock DLD

Overview

Long lock is a non-blocking synchronization construct which has to be used in FOM state handlers m0_fom_ops::fo_tick(). Long lock provides support for a reader-writer lock for FOMs.

Functional specification

According to reasons, described in Logical specification, application of struct m0_long_lock requires to introduce special link structures struct m0_long_lock_link: one structure per one acquiring long lock in the FOM. It's required that individual FOMs allocate and initialize link structures in their derived FOM data structures, and use this structures with long lock interfaces. The following code example demonstrates the usage example of the lock:

// Object encompassing FOM for some FOP type which
// typically contains necessary context data
struct fom_object_type {
// Generic m0_fom object.
struct m0_fom fp_gen;
// ...
// Long lock link structure, embedded into derived FOM object.
struct m0_long_lock_link fp_link;
// ...
};
static int fom_tick(struct m0_fom *fom)
{
//...
struct fom_object_type *fom_obj;
struct m0_long_lock_link *link;
// Retreive derived FOM object and long lock link.
fom_obj = container_of(fom, struct fom_object_type, fp_gen);
link = &fom_obj->fp_link;
//...
if (m0_fom_phase(fom) == PH_CURRENT) {
// try to obtain a lock, when it's obtained FOM is
// transitted into PH_GOT_LOCK
} else if (m0_fom_phase(fom) == PH_GOT_LOCK) {
// ...
// ...
}
//...
}
See also
FOM long lock API

Logical specification

The lock is considered to be obtained when FOM transitions to the phase specified by the next_phase parameter to the m0_long_{read|write}_lock subroutine - the PH_GOT_LOCK value in the example above. Eventually, when FOMs state handler does not need to read or write shared data structures, access to which must be synchronized, the lock can be released with m0_long_read_unlock() and m0_long_write_unlock() calls.

The long lock uses special link structures struct m0_long_lock_link to track the FOMs that actively own the lock (m0_long_lock::l_owners), and the FOMs that are queued waiting to acquire the lock (m0_long_lock::l_waiters). This is dictated by the requirement that one FOM can obtain multiple long locks. Holding multiple locks assumes that the same FOM can be presented in an arbitary number of queues in different long_locks. Derived FOM objects should contain a distinct link structure for each long lock used. The link must be initialized with m0_long_lock_link_init() before use.

To avoid starvation of writers in the face of a stream of incoming readers, a queue of waiting for the long lock FOMs is maintained:

  • Long lock links store read/write flags.
  • m0_long_read_lock() adds new links into the m0_long_lock::l_owners list if the lock is obtained for reading or unlocked, otherwise into the m0_long_lock::l_waiters.
  • When a lock is released and there are no FOMs which own the lock, FOMs waiting for processing (if any) are processed in FIFO order. This avoid starvation and provides a degree of fairness.

When some lock is being unlocked the work of selecting the next lock owner(s) and waking them up is done in the unlock path in m0_long_read_unlock() and m0_long_write_unlock() with respect to m0_long_lock::l_owners and m0_long_lock::l_waiters lists. m0_fom_ready_remote() call is used to wake FOMs up.