Motr  M0
Waiting channels

Data Structures

struct  m0_chan
 
struct  m0_clink
 
struct  m0_chan_addb2
 

Typedefs

typedef bool(* m0_chan_cb_t) (struct m0_clink *link)
 

Functions

 M0_TL_DESCR_DEFINE (clink, "chan clinks", static, struct m0_clink, cl_linkage, cl_magic, M0_LIB_CHAN_MAGIC, M0_LIB_CHAN_HEAD_MAGIC)
 
 M0_TL_DEFINE (clink, static, struct m0_clink)
 
static bool clink_is_head (const struct m0_clink *clink)
 
M0_INTERNAL void m0_chan_lock (struct m0_chan *ch)
 
M0_INTERNAL void m0_chan_unlock (struct m0_chan *ch)
 
M0_INTERNAL bool m0_chan_is_locked (const struct m0_chan *ch)
 
static bool m0_chan_invariant (struct m0_chan *chan)
 
M0_INTERNAL void m0_chan_init (struct m0_chan *chan, struct m0_mutex *ch_guard)
 
M0_INTERNAL void m0_chan_fini (struct m0_chan *chan)
 
M0_INTERNAL void m0_chan_fini_lock (struct m0_chan *chan)
 
static void clink_signal (struct m0_clink *clink)
 
static void chan_signal_nr (struct m0_chan *chan, uint32_t nr)
 
M0_INTERNAL void m0_chan_signal (struct m0_chan *chan)
 
M0_INTERNAL void m0_chan_signal_lock (struct m0_chan *chan)
 
M0_INTERNAL void m0_chan_broadcast (struct m0_chan *chan)
 
M0_INTERNAL void m0_chan_broadcast_lock (struct m0_chan *chan)
 
M0_INTERNAL bool m0_chan_has_waiters (struct m0_chan *chan)
 
static void clink_init (struct m0_clink *link, struct m0_clink *group, m0_chan_cb_t cb)
 
M0_INTERNAL void m0_clink_init (struct m0_clink *link, m0_chan_cb_t cb)
 
M0_INTERNAL void m0_clink_fini (struct m0_clink *link)
 
M0_INTERNAL void m0_clink_attach (struct m0_clink *link, struct m0_clink *group, m0_chan_cb_t cb)
 
M0_INTERNAL void m0_clink_add (struct m0_chan *chan, struct m0_clink *link)
 
void m0_clink_add_lock (struct m0_chan *chan, struct m0_clink *link)
 
M0_INTERNAL void m0_clink_del (struct m0_clink *link)
 
M0_INTERNAL void m0_clink_del_lock (struct m0_clink *link)
 
M0_INTERNAL bool m0_clink_is_armed (const struct m0_clink *link)
 
M0_INTERNAL void m0_clink_cleanup (struct m0_clink *link)
 
M0_INTERNAL void m0_clink_cleanup_locked (struct m0_clink *link)
 
M0_INTERNAL void m0_clink_signal (struct m0_clink *clink)
 
M0_INTERNAL bool m0_chan_trywait (struct m0_clink *link)
 
M0_INTERNAL void m0_chan_wait (struct m0_clink *link)
 
M0_INTERNAL bool m0_chan_timedwait (struct m0_clink *link, const m0_time_t abs_timeout)
 

Variables

struct m0_chan M0_XCA_DOMAIN
 

Detailed Description

Waiting channel.

A channel (m0_chan) is a stream of asynchronous events that a channel user can wait or register a call-back for.

A clink (m0_clink) is a record of interest in events on a particular channel. A user adds a clink to a channel and appearance of new events in the stream is recorded in the clink.

There are two interfaces related to channels:

- producer interface. It consists of m0_chan_signal(), m0_clink_signal()
  and m0_chan_broadcast() functions. These functions are called to
  declare that new asynchronous event happened in the stream.

- consumer interface. It consists of m0_clink_add(), m0_clink_del(),
  m0_chan_wait() and m0_chan_trywait() functions.

When a producer declares an event on a channel, this event is delivered. If event is a broadcast (m0_chan_broadcast()) it is delivered to all clinks registered with the channel. If event is a signal (m0_chan_signal()) it is delivered to a single clink (if any) registered with the channel. Clinks for delivery of consecutive signals are selected in a round-robin manner.

A special m0_clink_signal() function is provided to signal a particular clink. m0_clink_signal() delivers a signal to its argument clink. This function does not take any locks and is designed to be used in "awkward" contexts, like interrupt handler or timer call-backs, where blocking on a lock is not allowed. Use sparingly.

The method of delivery depends on the clink interface used (m0_clink). If clink has a call-back, the delivery starts with calling this call-back. If a clink has no call-back or the call-back returns false, the delivered event becomes pending on the clink. Pending events can be consumed by calls to m0_chan_wait(), m0_chan_timedwait() and m0_chan_trywait().

Filtered wake-ups.

By returning true from a call-back, it is possible to "filter" some events out and avoid potentially expensive thread wake-up. A typical use case for this is the following:

struct wait_state {
struct m0_clink f_clink;
...
};
static bool callback(struct m0_clink *clink)
{
struct wait_state *f =
container_of(clink, struct wait_state, f_clink);
return !condition_is_right(f);
}
{
struct wait_state g;
m0_clink_init(&g.f_clink, &callback);
m0_clink_add(chan, &g.f_clink);
...
while (!condition_is_right(&g)) {
m0_chan_wait(&g.f_clink);
}
}

The idea behind this idiom is that the call-back is called in the same context where the event is declared and it is much cheaper to test whether a condition is right than to wake up a waiting thread that would check this and go back to sleep if it is not.

Multiple channels.

It is possible to wait for an event to be announced on a channel from a set. To this end, first a clink is created as usual. Then, additional (unintialised) clinks are attached to the first by a call to m0_clink_attach(), forming a "clink group" consisting of the original clink and all clinks attached. Clinks from the group can be registered with multiple (or the same) channels. Events announced on any channel are delivered to all clinks in the group.

Groups are used as following:

- initialise a "group head" clink;

- attach other clinks to the group, without initialising them;

- register the group clinks with their channels, starting with the head;

- to wait for an event on any channel, wait on the group head.

- call-backs can be used for event filtering on any channel as usual;

- de-register the clinks, head last.
struct m0_clink cl0;
struct m0_clink cl1;
m0_clink_init(&cl0, call_back0);
m0_clink_attach(&cl1, &cl0, call_back1);
m0_clink_add(chan0, &cl0);
m0_clink_add(chan1, &cl1);
// wait for an event on chan0 or chan1
// de-register clinks, head last
// finalise in any order
Note
An interface similar to m0_chan was a part of historical UNIX kernel implementations. It is where "CHAN" field in ps(1) output comes from.
Todo:
The next scalability improvement is to allow m0_chan to use an externally specified mutex instead of a built-in one. This would allow larger state machines with multiple channels to operate under fewer locks, reducing coherency bus traffic.

A simplistic user space implementation of m0_chan and m0_clink interfaces based on POSIX semaphores.

A list of registered clinks is maintained for each channel. Each clink has a semaphore, used to wait for pending events. When an event is declared on the channel, a number (depending on whether event is signalled or broadcast) of clinks on the channel list is scanned and for each of them either call-back is called or semaphore is upped.

To wait for an event, a user downs clink semaphore.

Semaphore is initialized every time when the clink is registered with a channel (m0_clink_add()) and destroyed every time the clink is deleted from a channel (m0_clink_del()). This guarantees that semaphore counter is exactly equal to the number of pending events declared on the channel.

Note
that a version of m0_chan_wait() with a timeout would induce some changes to the design, because in this case it is waiter who has to unlink a clink from a channel.

Typedef Documentation

◆ m0_chan_cb_t

typedef bool(* m0_chan_cb_t) (struct m0_clink *link)

Clink call-back called when event is delivered to the clink. The call-back returns true iff the event has been "consumed". Otherwise, the event will remain pending on the clink for future consumption by the waiting interfaces.

Definition at line 176 of file chan.h.

Function Documentation

◆ chan_signal_nr()

static void chan_signal_nr ( struct m0_chan chan,
uint32_t  nr 
)
static

Definition at line 146 of file chan.c.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ clink_init()

static void clink_init ( struct m0_clink link,
struct m0_clink group,
m0_chan_cb_t  cb 
)
static

Definition at line 190 of file chan.c.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ clink_is_head()

static bool clink_is_head ( const struct m0_clink clink)
static

Definition at line 63 of file chan.c.

Here is the caller graph for this function:

◆ clink_signal()

static void clink_signal ( struct m0_clink clink)
static

Definition at line 124 of file chan.c.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ m0_chan_broadcast()

M0_INTERNAL void m0_chan_broadcast ( struct m0_chan chan)

Notifies all clinks currently registered with the channel that a new event happened.

No guarantees about behaviour in the case when clinks are added or removed while m0_chan_broadcast() is running.

If clinks with call-backs (m0_clink::cl_cb) are registered with the channel at the time of this call, the call-backs are run to completion as part of broadcast.

Precondition
m0_chan_is_locked(chan)
See also
m0_chan_signal()

Definition at line 172 of file chan.c.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ m0_chan_broadcast_lock()

M0_INTERNAL void m0_chan_broadcast_lock ( struct m0_chan chan)

Calls m0_chan_broadcast() with ch_guard locked.

Definition at line 178 of file chan.c.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ m0_chan_fini()

M0_INTERNAL void m0_chan_fini ( struct m0_chan chan)

Definition at line 104 of file chan.c.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ m0_chan_fini_lock()

M0_INTERNAL void m0_chan_fini_lock ( struct m0_chan chan)

Definition at line 112 of file chan.c.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ m0_chan_has_waiters()

M0_INTERNAL bool m0_chan_has_waiters ( struct m0_chan chan)

True iff there are clinks registered with the chan.

Note
the return value of this function can, in general, be obsolete by the time it returns. It is up to the user to provide concurrency control mechanisms that would make this function useful.

Definition at line 185 of file chan.c.

Here is the caller graph for this function:

◆ m0_chan_init()

M0_INTERNAL void m0_chan_init ( struct m0_chan chan,
struct m0_mutex ch_guard 
)

Definition at line 96 of file chan.c.

◆ m0_chan_invariant()

static bool m0_chan_invariant ( struct m0_chan chan)
static

Channel invariant: all clinks on the list are clinks for this channel and number of waiters matches list length.

Definition at line 87 of file chan.c.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ m0_chan_is_locked()

M0_INTERNAL bool m0_chan_is_locked ( const struct m0_chan ch)

Tests ch_guard for being locked.

Definition at line 78 of file chan.c.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ m0_chan_lock()

M0_INTERNAL void m0_chan_lock ( struct m0_chan ch)

Locks ch_guard.

Definition at line 68 of file chan.c.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ m0_chan_signal()

M0_INTERNAL void m0_chan_signal ( struct m0_chan chan)

Notifies a clink currently registered with the channel that a new event happened.

Precondition
m0_chan_is_locked(chan)
See also
m0_chan_broadcast()

Definition at line 159 of file chan.c.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ m0_chan_signal_lock()

M0_INTERNAL void m0_chan_signal_lock ( struct m0_chan chan)

Calls m0_chan_signal() with ch_guard locked.

Definition at line 165 of file chan.c.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ m0_chan_timedwait()

M0_INTERNAL bool m0_chan_timedwait ( struct m0_clink link,
const m0_time_t  abs_timeout 
)

This is the same as m0_chan_wait, except that it has an expire time. If the time expires before event is pending, this function will return false.

Parameters
abs_timeoutabsolute time since Epoch (00:00:00, 1 January 1970)
Returns
true if the there is an event pending before timeout;
false if there is no events pending and timeout expires;

Definition at line 349 of file chan.c.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ m0_chan_trywait()

M0_INTERNAL bool m0_chan_trywait ( struct m0_clink link)

True there is an event pending in the clink. When this function returns true, the event is consumed, exactly like if m0_chan_wait() were called instead.

Definition at line 331 of file chan.c.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ m0_chan_unlock()

M0_INTERNAL void m0_chan_unlock ( struct m0_chan ch)

Unlocks ch_guard.

Definition at line 73 of file chan.c.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ m0_chan_wait()

M0_INTERNAL void m0_chan_wait ( struct m0_clink link)

Returns when there is an event pending in the clink. The event is consumed before the call returns.

Note that this implies that if an event happened after the clink has been registered (by a call to m0_clink_add()) and before call to m0_chan_wait(), the latter returns immediately.

User must guarantee that no more than one thread waits on the clink.

Definition at line 336 of file chan.c.

Here is the call graph for this function:

◆ m0_clink_add()

M0_INTERNAL void m0_clink_add ( struct m0_chan chan,
struct m0_clink link 
)
Precondition
!m0_clink_is_armed(link)
Postcondition
m0_clink_is_armed(link)

Registers the clink with the channel.

Precondition
m0_chan_is_locked(chan)
!m0_clink_is_armed(link)
Postcondition
m0_clink_is_armed(link)

Definition at line 228 of file chan.c.

Here is the call graph for this function:

◆ m0_clink_add_lock()

M0_INTERNAL void m0_clink_add_lock ( struct m0_chan chan,
struct m0_clink link 
)

Calls m0_clink_add() with ch_guard locked.

Definition at line 255 of file chan.c.

Here is the call graph for this function:

◆ m0_clink_attach()

M0_INTERNAL void m0_clink_attach ( struct m0_clink link,
struct m0_clink group,
m0_chan_cb_t  cb 
)

Attaches a clink group. is the original clink in the group.

Definition at line 215 of file chan.c.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ m0_clink_cleanup()

M0_INTERNAL void m0_clink_cleanup ( struct m0_clink link)

If clink armed, deletes the one from its channel. Otherwise, does nothing.

Precondition
!m0_chan_is_locked(link->cl_chan)

Definition at line 310 of file chan.c.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ m0_clink_cleanup_locked()

M0_INTERNAL void m0_clink_cleanup_locked ( struct m0_clink link)

If clink armed, deletes the one from its channel. Otherwise, does nothing.

Precondition
m0_chan_is_locked(link->cl_chan)

Definition at line 319 of file chan.c.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ m0_clink_del()

M0_INTERNAL void m0_clink_del ( struct m0_clink link)
Precondition
m0_clink_is_armed(link)
Postcondition
!m0_clink_is_armed(link)

Un-registers the clink from the channel.

Precondition
m0_chan_is_locked(chan)
m0_clink_is_armed(link)
Postcondition
!m0_clink_is_armed(link)

Definition at line 267 of file chan.c.

Here is the call graph for this function:

◆ m0_clink_del_lock()

M0_INTERNAL void m0_clink_del_lock ( struct m0_clink link)

Calls m0_clink_del() with ch_guard locked.

Definition at line 293 of file chan.c.

Here is the call graph for this function:

◆ m0_clink_fini()

M0_INTERNAL void m0_clink_fini ( struct m0_clink link)

Definition at line 208 of file chan.c.

◆ m0_clink_init()

M0_INTERNAL void m0_clink_init ( struct m0_clink link,
m0_chan_cb_t  cb 
)

Definition at line 201 of file chan.c.

Here is the call graph for this function:

◆ m0_clink_is_armed()

M0_INTERNAL bool m0_clink_is_armed ( const struct m0_clink link)

True iff the clink is registered with a channel.

Definition at line 303 of file chan.c.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ m0_clink_signal()

M0_INTERNAL void m0_clink_signal ( struct m0_clink clink)

Notifies a given clink that a new event happened.

This function takes no locks.

m0_chan_signal() should be used instead, unless the event is announced in a context where blocking is not allowed.

See also
m0_chan_signal()
m0_chan_broadcast()

Definition at line 326 of file chan.c.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ M0_TL_DEFINE()

M0_TL_DEFINE ( clink  ,
static  ,
struct m0_clink   
)

◆ M0_TL_DESCR_DEFINE()

M0_TL_DESCR_DEFINE ( clink  ,
"chan clinks"  ,
static  ,
struct m0_clink  ,
cl_linkage  ,
cl_magic  ,
M0_LIB_CHAN_MAGIC  ,
M0_LIB_CHAN_HEAD_MAGIC   
)

Variable Documentation

◆ M0_XCA_DOMAIN

struct m0_chan M0_XCA_DOMAIN