client_block(2) — CX/UX
NAME
client_block, client_wake1, client_wakechan − client-server thread coordination
SYNOPSIS
#include <sys/types.h>
#include <sys/time.h>
#include <sys/thread_synch.h>
int client_block (server, chan, options, m, r, timeout)
tid_t server;
int chan, options;
struct spin_mutex ∗m;
struct resched_var ∗r;
struct timeval ∗timeout;
int client_wake1 (server, client, r)
tid_t server, client;
struct resched_var ∗r;
int client_wakechan (server, chan, r)
tid_t server;
int chan;
struct resched_var ∗r;
DESCRIPTION
When one thread requests service from another, the former thread is called a "client", and the latter thread is called a "server." To control priority inversion, a server should have a priority at least as high as any of its clients. The routines described here provide a priority inheritance mechanism.
client_block blocks the calling thread (a client) and establishes a formal client-server relationship with another thread (the server). Client_wake1 and client_wakechan wake threads that are blocked in client_block.
client_block releases spinlock m, decrements the number of rescheduling locks in r, and blocks the caller. The unlock and block operations are atomic to guarantee that the caller does not miss a wakeup. Server’s priority will be at least as high as the caller’s while the caller is blocked. m and r are optional and may be specified as NULL.
If the options flag is 1 the server will be woken as in server_wake1(2).
The chan parameter groups related clients of a server. It is used by client_wakechan.
If timeout is not NULL, it specifies the maximum length of time the caller will be blocked. If timeout is NULL, the caller is blocked indefinitely.
The real or effective user ID of the caller must match the real or effective user ID of server, unless the effective user ID of the caller is super-user.
The caller should be prepared for premature returns; that is, it should re-test the condition that originally caused it to block. Upon return there is no guarantee that the condition blocking the caller has changed.
Client_wake1 wakes client, if it is blocked in client_block requesting service from server, and decrements the number of rescheduling locks in r. If client is not blocked under these conditions, client_wake1 has no effect on it. A value of 0 for server refers to the calling thread. (In this implementation, server must be 0.)
The real or effective user ID of the caller must match the real or effective user ID of client, unless the effective user ID of the caller is super-user.
Client_wakechan wakes all of the clients grouped under chan requesting service from server, and decrements the number of rescheduling locks in r. A value of 0 for server refers to the calling thread. (In this implementation, server must be 0.) A value of 0 for chan wakes ALL of the server’s clients.
The real or effective user ID of the caller must match the real or effective user ID of the clients, unless the effective user ID of the caller is super-user.
EXAMPLES
Priority inversion is a condition wherein one or more low priority threads prevent the progress of a high priority thread. The most often cited example is that of a low priority thread L being preempted in a critical region. A high priority thread H attempting to enter the critical region will block until L leaves the critical region. However, threads in the priority gap between H and L prevent L from running. If L were to inherit H’s priority, the gap would close and L would run until it was preempted by H upon exit from the critical region.
The code fragments below implement priority inheritance in sleepy-wait mutual exclusion. Let
01 struct sleep_mutex {
02struct spin_mutex mx;
03tid_t owner;
04int waiters;
05 };
represent a sleepy-wait mutex. The owner field identifies the thread that owns the mutex, the waiters field indicates whether threads are blocked on the mutex, and the mx field serializes access to the mutex. Let rv represent the running thread’s rescheduling variable, and spin_acquire and spin_release represent primitives that lock and unlock spinlocks and rescheduling variables (see spin_trylock(2)).
A mutex may be locked as follows.
11 void
12 sleep_lock (s)
13struct sleep_mutex ∗s;
14 {
15spin_acquire (&s->mx, &rv);
16while (s->owner) {
17s->waiters = 1;
18client_block (s->owner, s, 0, &s->mx, &rv, 0);
19spin_acquire (&s->mx, &rv);
20}
21s->owner = rv.rv_tid;
22spin_release (&s->mx, &rv);
23 }
Although perhaps an unusual interpretation of the client-server relationship, the owner of the mutex is considered to be a server, and the waiting threads are considered to be its clients. client_block guarantees that the owner’s priority is at least as high as any of the waiting threads. This example assumes that a mutex appears at the same location in every thread’s address space, so the address of the mutex is used as the chan argument to client_block to distinguish one mutex’s waiters from another’s.
A mutex may be unlocked as follows.
31 void
32 sleep_unlock (s)
33struct sleep_mutex ∗s;
34 {
35int were_waiters;
36
37spin_acquire (&s->mx, &rv);
38if (s->owner != rv.rv_tid) {
39Indicate error condition.
40}
41s->owner = 0;
42were_waiters = s->waiters;
43s->waiters = 0;
44spin_unlock (&s->mx);
45
46if (were_waiters)
47client_wakechan (0, s, &rv);
48else
49resched_unlock (&rv);
50 }
When an owner releases a mutex all of the waiters are woken. The newly awakened waiters re-contend for the mutex when they execute. One will become the new owner of the mutex, while the others block and establish new client-server relationships.
RETURN VALUE
Upon successful completion 0 is returned. Otherwise, −1 is returned and errno is set to indicate the error.
ERRORS
These routines will fail if any of the following are true:
[EFAULT] A bad address was specified for one of the arguments.
[EINVAL] The spinlock specified in m was not in the locked state.
[EINVAL] The timeout was invalid.
[EINVAL] The rescheduling variable specified in r was not the caller’s rescheduling variable.
[EINVAL] The number of rescheduling locks in r was <= 0.
[ESRCH] No thread could be found with the specified ID.
[EPERM] The user ID of the calling thread was not super-user, and its real or effective user ID did not match the real or effective user ID of the target thread.
[EINTR] The system call was interrupted by a signal.
[ETIME] The system call timed out.
SEE ALSO
CX/UX Programmer’s Guide.
gettid(2), resched_cntl(2), spin_trylock(2), server_block(2).
CX/UX Programmer’s Reference Manual