ROSE  0.9.6a
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
threadSupport.h File Reference
#include "rosePublicConfig.h"
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <pthread.h>
Include dependency graph for threadSupport.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Classes

struct  RTS_mutex_t
 Mutual exclusion lock. More...
 
struct  RTS_rwlock_t
 A read-write lock for ROSE Thread Support. More...
 
class  RTS_Message
 Support for messages in a multi-threaded program. More...
 
class  RTS_Message::Prefix
 The Prefix class is used to generate prefix text for every line of output. More...
 

Macros

#define ROSE_THREADS_ENABLED
 
#define ROSE_THREADS_POSIX
 
#define __attribute__(x)   /*NOTHING*/
 
#define __attribute(x)   /*NOTHING*/
 
#define RTS_MUTEX(MUTEX)
 Protect a critical section with a mutual exclusion lock. More...
 
#define RTS_MUTEX_END
 End an RTS_MUTEX construct. More...
 
#define RTS_MUTEX_MAGIC   0x1a95a713
 
#define RTS_MUTEX_INITIALIZER(LAYER)   { RTS_MUTEX_MAGIC, (LAYER), PTHREAD_MUTEX_INITIALIZER }
 
#define RTS_RWLOCK(RWLOCK, HOW)
 Protect a critical section with a read-write lock. More...
 
#define RTS_RWLOCK_END
 
#define RTS_READ(RWLOCK)   RTS_RWLOCK(RWLOCK, rdlock)
 See RTS_RWLOCK. More...
 
#define RTS_READ_END   RTS_RWLOCK_END
 See RTS_RWLOCK. More...
 
#define RTS_WRITE(RWLOCK)   RTS_RWLOCK(RWLOCK, wrlock)
 See RTS_RWLOCK. More...
 
#define RTS_WRITE_END   RTS_RWLOCK_END
 See RTS_RWLOCK. More...
 
#define RTS_RWLOCK_MAGIC   0x20e7f3f4
 
#define RTS_RWLOCK_INITIALIZER(LAYER)
 Static initializer for an RTS_rwlock_t instance, similar in nature to PTHREAD_RWLOCK_INITIALIZER. More...
 
#define RTS_INIT(MUTEX, ALLOW_RECURSION)
 Initializer synchronization. More...
 
#define RTS_INIT_END
 End an RTS_INIT construct. More...
 
#define RTS_INIT_RECURSIVE(MUTEX)   RTS_INIT(MUTEX, true)
 See RTS_INIT. More...
 
#define RTS_INIT_NONRECURSIVE(MUTEX)   RTS_INIT(MUTEX, false)
 See RTS_INIT. More...
 
#define RTS_MESSAGE(MESG)
 Provides a locked context for messaging. More...
 
#define RTS_MESSAGE_END(SOL)
 Provides a locked context for messaging. More...
 

Enumerations

enum  RTS_Layer {
  RTS_LAYER_DONTCARE = 0,
  RTS_LAYER_ROSE_CALLBACKS_LIST_OBJ = 100,
  RTS_LAYER_RTS_MESSAGE_CLASS = 105,
  RTS_LAYER_DISASSEMBLER_CLASS = 110,
  RTS_LAYER_ROSE_SMT_SOLVERS = 115,
  RTS_LAYER_RSIM_SIGNALHANDLING_OBJ = 200,
  RTS_LAYER_RSIM_PROCESS_OBJ = 201,
  RTS_LAYER_RSIM_PROCESS_CLONE_OBJ = 202,
  RTS_LAYER_RSIM_THREAD_OBJ = 203,
  RTS_LAYER_RSIM_THREAD_CLASS = 204,
  RTS_LAYER_RSIM_SYSCALLDISABLER_OBJ = 205,
  RTS_LAYER_RSIM_TRACEIO_OBJ = 206,
  RTS_LAYER_RSIM_SIMULATOR_CLASS = 207,
  RTS_LAYER_RSIM_SIMULATOR_OBJ = 208,
  RTS_LAYER_USER_MIN = 250,
  RTS_LAYER_USER_MAX = 299,
  RTS_LAYER_NLAYERS = 300
}
 Layers where syncrhonization primitives are defined. More...
 

Functions

bool RTS_acquiring (RTS_Layer)
 Check for layering violations. More...
 
void RTS_releasing (RTS_Layer)
 Notes the release of a lock. More...
 
int RTS_mutex_init (RTS_mutex_t *, RTS_Layer, pthread_mutexattr_t *)
 Initialize a mutual exclusion lock. More...
 
int RTS_mutex_lock (RTS_mutex_t *)
 Obtain an exclusive lock. More...
 
int RTS_mutex_unlock (RTS_mutex_t *)
 Release an exclusive lock. More...
 
int RTS_rwlock_init (RTS_rwlock_t *rwlock, RTS_Layer, pthread_rwlockattr_t *wrlock_attrs)
 Intializes an RTS_rwlock_t in a manner similar to pthread_rwlock_init(). More...
 
int RTS_rwlock_rdlock (RTS_rwlock_t *rwlock)
 Obtain a read lock. More...
 
int RTS_rwlock_wrlock (RTS_rwlock_t *rwlock)
 Obtain a write lock. More...
 
int RTS_rwlock_unlock (RTS_rwlock_t *rwlock)
 Release a read or write lock. More...
 

Macro Definition Documentation

#define ROSE_THREADS_ENABLED

Definition at line 36 of file threadSupport.h.

#define ROSE_THREADS_POSIX

Definition at line 37 of file threadSupport.h.

#define __attribute__ (   x)    /*NOTHING*/

Definition at line 62 of file threadSupport.h.

#define __attribute (   x)    /*NOTHING*/

Definition at line 63 of file threadSupport.h.

#define RTS_MUTEX (   MUTEX)
Value:
do { /* standard CPP macro protection */ \
RTS_mutex_t *RTS_Mp_mutex = &(MUTEX); /* saved for when we need to unlock it */ \
int RTS_Mp_err = RTS_mutex_lock(RTS_Mp_mutex); \
assert(0==RTS_Mp_err); \
do { /* so we can catch "break" statements */ \
try {

Protect a critical section with a mutual exclusion lock.

This macro should be used within ROSE whenever we need to obtain a lock for a critical section. The critical section should end with a matching RTS_MUTEX_END macro. The suggested code style is to use curly braces and indentation to help visually line up the RTS_MUTEX with the RTS_MUTEX_END, such as:

RTS_MUTEX(class_mutex) {
critical_section_goes_here;

The critical section should not exit the construct except through the RTS_MUTEX_END macro. In other words, the critical section should not have "return" statements, longjmps, or any "goto" that branches outside the critical section. However, "break" statements and exceptions are supported.

If the mutex is an error checking mutex then ROSE will assert that the lock is not already held by this thread. If the mutex is recursive then the lock will be obtained recursively if necessary.

Definition at line 162 of file threadSupport.h.

Referenced by ROSE_Callbacks::List< T >::after(), ROSE_Callbacks::List< T >::append(), ROSE_Callbacks::List< T >::before(), ROSE_Callbacks::List< T >::callbacks(), ROSE_Callbacks::List< T >::clear(), ROSE_Callbacks::List< T >::empty(), ROSE_Callbacks::List< T >::erase(), Disassembler::lookup(), ROSE_Callbacks::List< T >::prepend(), Disassembler::progress(), Disassembler::register_subclass(), ROSE_Callbacks::List< T >::replace(), Disassembler::set_progress_reporting(), and ROSE_Callbacks::List< T >::size().

#define RTS_MUTEX_MAGIC   0x1a95a713

Definition at line 192 of file threadSupport.h.

#define RTS_MUTEX_INITIALIZER (   LAYER)    { RTS_MUTEX_MAGIC, (LAYER), PTHREAD_MUTEX_INITIALIZER }

Definition at line 206 of file threadSupport.h.

#define RTS_RWLOCK (   RWLOCK,
  HOW 
)
Value:
do { /* standard CPP macro protection */ \
RTS_rwlock_t *RTS_Wp_rwlock = &(RWLOCK); /* saved for when we need to unlock it */ \
int RTS_Wp_err = RTS_rwlock_##HOW(RTS_Wp_rwlock); \
assert(0==RTS_Wp_err); \
do { /* so we can catch "break" statements */ \
try {

Protect a critical section with a read-write lock.

These macros should be used within ROSE whenever we need to protect a critical section among two kinds of access: reading and writing.

This construct allows at most one thread to hold a write lock, or multiple threads to hold read locks. Write locks are granted only when no other thread holds a read or write lock, and a request for a write lock blocks (becomes pending) if the lock cannot be granted. Read locks are granted only when no write lock is already granted to another thread, and no write lock is pending.

Like POSIX read-write locks, RTS_rwlock_t allows a single thread to obtain multiple read locks recursively. Unlike POSIX read-write locks, RTS_rwlock_t also allows the following:

  • A lock (read or write) is granted to a thread which already holds a write lock. The POSIX implementation deadlocks in this situation. This feature is useful in ROSE when a read-write lock guards access to data members of an object, and the object methods can be invoked recursively.
  • The RTS_rwlock_unlock() function releases locks in the reverse order they were granted and should only be called by the thread to which the lock was granted.

In particular, this implementation does not allow a thread which holds only read locks to be granted a write lock (i.e., no lock upgrading). Like POSIX read-write locks, this situation will lead to deadlock.

The RTS_READ macro should be paired with an RTS_READ_END macro; the RTS_WRITE macro should be paired with an RTS_WRITE_END macro. The RTS_RWLOCK macro is a generalization of RTS_READ and RTS_WRITE where its second argument is either the word "rdlock" or "wrlock", respectively. It should be paired with an RTS_RWLOCK_END macro.

The critical section may exit only via "break" statement, throwing an exception, or falling through the end. Exceptions thrown by the critical section will release the lock before rethrowing the exception.

A simple example demonstrating how locks can be obtained recursively. Any number of threads can be operating on a single, common object concurrently and each of the four defined operations remains atomic.

class Stack {
public:
Stack() {
RTS_rwlock_init(&rwlock, NULL);
}
MD5sum sum() const {
MD5sum retval;
RTS_READ(rwlock) {
for (size_t i=0; i<stack.size(); i++)
retval.composite(stack[i]);
return retval;
}
void push(const std::string &s) {
RTS_WRITE(rwlock) {
stack.push_back(s);
}
// swap top and third from top; toss second from top
// throw exception when stack becomes too small
void adjust() {
RTS_WRITE(rwlock) {
std::string s1 = pop(); // may throw
(void) pop(); // may throw
std::string s2 = pop(); // may throw
push(s1);
push(s2);
}
// adjust() until sum specified termination condition
// throw exception when stack becomes too small
void adjust_until(const MD5sum &term) {
RTS_WRITE(rwlock) {
while (sum()!=term) // recursive read lock
adjust(); // recursive write lock; may throw
}
private:
std::vector<std::string> stack;
mutable RTS_rwlock_t rwlock; // mutable so sum() can be const as user would expect
};

Definition at line 320 of file threadSupport.h.

#define RTS_RWLOCK_END
Value:
} catch (...) { \
RTS_Wp_err = RTS_rwlock_unlock(RTS_Wp_rwlock); \
assert(0==RTS_Wp_err); \
throw; \
} \
} while (0); \
RTS_Wp_err = RTS_rwlock_unlock(RTS_Wp_rwlock); \
assert(0==RTS_Wp_err); \
} while (0)

Definition at line 328 of file threadSupport.h.

#define RTS_READ (   RWLOCK)    RTS_RWLOCK(RWLOCK, rdlock)

See RTS_RWLOCK.

Definition at line 346 of file threadSupport.h.

#define RTS_READ_END   RTS_RWLOCK_END

See RTS_RWLOCK.

Definition at line 347 of file threadSupport.h.

#define RTS_WRITE (   RWLOCK)    RTS_RWLOCK(RWLOCK, wrlock)

See RTS_RWLOCK.

Definition at line 348 of file threadSupport.h.

#define RTS_WRITE_END   RTS_RWLOCK_END

See RTS_RWLOCK.

Definition at line 349 of file threadSupport.h.

#define RTS_RWLOCK_MAGIC   0x20e7f3f4

Definition at line 377 of file threadSupport.h.

#define RTS_RWLOCK_INITIALIZER (   LAYER)
Value:
(LAYER), \
PTHREAD_RWLOCK_INITIALIZER, \
0/*...*/ \
}

Static initializer for an RTS_rwlock_t instance, similar in nature to PTHREAD_RWLOCK_INITIALIZER.

Definition at line 381 of file threadSupport.h.

#define RTS_INIT (   MUTEX,
  ALLOW_RECURSION 
)
Value:
do { \
static bool RTS_Is_initialized=false, RTS_Is_initializing=false; /* "s"==shared; "p"=private */ \
static pthread_t RTS_Is_initializer; \
static pthread_cond_t RTS_Is_condition=PTHREAD_COND_INITIALIZER; \
RTS_mutex_t *RTS_Ip_mutex = &(MUTEX); \
bool RTS_Ip_initialized, RTS_Ip_initializing; \
bool RTS_Ip_allow_recursion = (ALLOW_RECURSION); \
\
/* First critical section is only to obtain the initialization status and update it to "initializing" if necessary. We \
* must release the lock before the RTS_I_LOCK body is executed in case we need to handle recursive calls to the \
* RTS_I_LOCK construct. */ \
RTS_MUTEX(MUTEX) { \
if (!(RTS_Ip_initialized=RTS_Is_initialized) && !(RTS_Ip_initializing=RTS_Is_initializing)) { \
RTS_Is_initializing = true; /* but leave private copy false so we can detect changed state */ \
RTS_Is_initializer = pthread_self(); \
} \
\
if (!RTS_Ip_initialized) { \
if (!RTS_Ip_initializing) { \
do { /* so we catch "break" statements in user-supplied code. */ \
try {

Initializer synchronization.

Sometimes we want a critical section to be executed only by the first thread to call the function and all other threads that might call the same function should block until the first caller completes. These macros can be used for that purpose.

The MUTEX is briefly locked to inspect the state of initilization, and then unlocked before the user-supplied body is executed. The user is permitted to obtain the mutex lock again in the body if desired, although this is only necessary if other code paths (outside the RTS_INIT construct) might interfere with the body.

If ALLOW_RECURSION is true, then a recursive call from the body will jump over the RTS_INIT construct without doing anything (other than briefly obtaining the mutex lock to inspect the state of initialization). Otherwise a recursive call from the body is considered a logic error and the process will be aborted. For convenience, we define two additional macros, RTS_INIT_RECURSIVE and RTS_INIT_NONRECURSIVE, which may be used in place of the two-argument RTS_INIT macro.

The user-supplied body may exit prematurely either by a "break" statement or by throwing an exception. In either case, the initialization is assumed to have completed and the body will not be executed by any other future call. Any other kind of premature exit from the body (return, goto, longjmp, etc) results in undefined behavior.

Example code. Consider a class method which is responsible for one-time initialization of certain class data structures. This initialization function is called by nearly every other method in the class, and therefore anything that the initialization function does might result in a recursive call to the initializer.

static void
SomeClass::initclass()
{
RTS_INIT_RECURSIVE(class_mutex) {
register_subclass(new Subclass1);
register_subclass(new Subclass2);
register_subclass(new Subclass3);
}

Definition at line 487 of file threadSupport.h.

#define RTS_INIT_END

End an RTS_INIT construct.

Definition at line 513 of file threadSupport.h.

Referenced by Disassembler::initclass().

#define RTS_INIT_RECURSIVE (   MUTEX)    RTS_INIT(MUTEX, true)

See RTS_INIT.

Definition at line 561 of file threadSupport.h.

Referenced by Disassembler::initclass().

#define RTS_INIT_NONRECURSIVE (   MUTEX)    RTS_INIT(MUTEX, false)

See RTS_INIT.

Definition at line 562 of file threadSupport.h.

#define RTS_MESSAGE (   MESG)
Value:
do { \
RTS_Message *RTS_Mp_mesg = &(MESG); \
int RTS_Mp_err = RTS_Mp_mesg->lock(); \
assert(0==RTS_Mp_err); \
do { \
try {

Provides a locked context for messaging.

Normal message output methods do not require locking since they perform the locking implicitly. However, one needs to provide some kind of customized output not otherwise possible, a lock needs to be obtained while that output is produced. See RTS_Messsage::lock() for details.

The RTS_MESSAGE macro takes an RTS_Message object as an argument, and locks it until the corresponding RTS_MESSAGE_END macro. The RTS_MESSAGE_END macro takes one argument: a Boolean expression which evaluates to true if no output was produced or the output ended with a line-feed.

The body between the RTS_MESSAGE and RTS_MESSAGE_END macro should not exit non-locally except by "break" or throwing an exception, both of which release the lock.

Example:

RTS_Message mesg(stderr, NULL);
RTS_MESSAGE(mesg) {
for (size_t i=0; i<n; i++) {
fprintf(stderr, " arg(%d) = %d\n", i, arg[i]);
}

Note that the previous example could have more easily been obtained with the following, except that in a multi-threaded application another thread might interject its own output between these lines.

RTS_Message mesg(stderr, NULL);
for (size_t i=0; i<n; i++) {
mesg.mesg(" arg(%d) = %d\n", i, arg[i]);
}

Definition at line 778 of file threadSupport.h.

#define RTS_MESSAGE_END (   SOL)
Value:
} catch (...) { \
RTS_Mp_err = RTS_Mp_mesg->unlock((SOL)); \
assert(0==RTS_Mp_err); \
throw; \
} \
} while (0); \
RTS_Mp_err = RTS_Mp_mesg->unlock((SOL)); \
assert(0==RTS_Mp_err); \
} while (0)

Provides a locked context for messaging.

Normal message output methods do not require locking since they perform the locking implicitly. However, one needs to provide some kind of customized output not otherwise possible, a lock needs to be obtained while that output is produced. See RTS_Messsage::lock() for details.

The RTS_MESSAGE macro takes an RTS_Message object as an argument, and locks it until the corresponding RTS_MESSAGE_END macro. The RTS_MESSAGE_END macro takes one argument: a Boolean expression which evaluates to true if no output was produced or the output ended with a line-feed.

The body between the RTS_MESSAGE and RTS_MESSAGE_END macro should not exit non-locally except by "break" or throwing an exception, both of which release the lock.

Example:

RTS_Message mesg(stderr, NULL);
RTS_MESSAGE(mesg) {
for (size_t i=0; i<n; i++) {
fprintf(stderr, " arg(%d) = %d\n", i, arg[i]);
}

Note that the previous example could have more easily been obtained with the following, except that in a multi-threaded application another thread might interject its own output between these lines.

RTS_Message mesg(stderr, NULL);
for (size_t i=0; i<n; i++) {
mesg.mesg(" arg(%d) = %d\n", i, arg[i]);
}

Definition at line 786 of file threadSupport.h.

Enumeration Type Documentation

enum RTS_Layer

Layers where syncrhonization primitives are defined.

When a thread intends to acquire multiple locks at a time, it must acquire those locks in a particular order to prevent deadlock. Deadlock can occur when thread 1 attempts to acquire lock A and then B, while thread 2 attempts to acquire lock B and then A. By defining every lock to belong to a particular software layer, we can impose a partial ordering on the locks and enforce the requirement that a thread obtain locks in that order. To use the previous example, if lock A belongs to layer X and lock B to layer Y, then a rule that says "locks of layer X must be acquired before locks of layer Y when attempting to acquire both at once" would be sufficient to prevent deadlock. This mechanism makes no attempt to define an acquisition order for locks of the same layer (at least not at this time).

When a thread acquires locks from more than one layer at a time, they must be acquired in descending order by layer (they can be released in any order). If a thread attempts to aquire a lock whose layer is greater than the minimum layer for which it already holds a lock, then an error message is emitted and the process aborts.

New layers can be added to this enum and the RTS_LAYER_NLAYERS constant can be increased if necessary. When a layer's number is changed, all of ROSE must be recompiled. The constant name is used in error messages. Names ending with "_CLASS" refer to synchronization primities that are class data members (or global), while those ending with "_OBJ" belong to a particular object.

Layer zero is special and is the default layer for all syncronization primitives not explicitly associated with any layer. Locks in layer zero can be acquired in any order without generating an error message (so silent deadlock is a distinct possibility).

Enumerator
RTS_LAYER_DONTCARE 
RTS_LAYER_ROSE_CALLBACKS_LIST_OBJ 

ROSE_Callbacks::List class.

RTS_LAYER_RTS_MESSAGE_CLASS 

RTS_Message class.

RTS_LAYER_DISASSEMBLER_CLASS 

Disassembler class.

RTS_LAYER_ROSE_SMT_SOLVERS 

SMTSolver class.

RTS_LAYER_RSIM_SIGNALHANDLING_OBJ 

RSIM_SignalHandling.

RTS_LAYER_RSIM_PROCESS_OBJ 

RSIM_Process.

RTS_LAYER_RSIM_PROCESS_CLONE_OBJ 

RSIM_Process::Clone.

RTS_LAYER_RSIM_THREAD_OBJ 

RSIM_Thread.

RTS_LAYER_RSIM_THREAD_CLASS 

RSIM_Thread.

RTS_LAYER_RSIM_SYSCALLDISABLER_OBJ 

RSIM_Adapter::SyscallDisabler.

RTS_LAYER_RSIM_TRACEIO_OBJ 

RSIM_Adapter::TraceIO.

RTS_LAYER_RSIM_SIMULATOR_CLASS 

RSIM_Simulator.

RTS_LAYER_RSIM_SIMULATOR_OBJ 

RSIM_Simulator.

RTS_LAYER_USER_MIN 

Minimum layer for end-user usage.

RTS_LAYER_USER_MAX 

Maximum layer for end-user usage.

RTS_LAYER_NLAYERS 

Definition at line 92 of file threadSupport.h.

Function Documentation

bool RTS_acquiring ( RTS_Layer  )

Check for layering violations.

This should be called just before any attempt to acquire a lock. The specified layer should be the layer of the lock being acquired. Returns true if it is OK to acquire the lock, false if doing so could result in deadlock. Before returning false, an error message is printed to stderr.

Note that this function is a no-op when the compiler does not support the "__thread" type qualifier, nor any other qualifier as detected by the ROSE configure script. Currently, this is a no-op on Mac OS X. [RPM 2011-05-04]

void RTS_releasing ( RTS_Layer  )

Notes the release of a lock.

This function should be called before or after each release of a lock. The layer number is that of the lock which is release.

int RTS_mutex_init ( RTS_mutex_t ,
RTS_Layer  ,
pthread_mutexattr_t *   
)

Initialize a mutual exclusion lock.

Referenced by ROSE_Callbacks::List< T >::List().

int RTS_mutex_lock ( RTS_mutex_t )

Obtain an exclusive lock.

Behavior is similar to pthread_mutex_lock(). Returns zero on success, errno on failure.

int RTS_mutex_unlock ( RTS_mutex_t )

Release an exclusive lock.

Behavior is similar to pthread_mutex_unlock(). Returns zero on success, errno on failure.

int RTS_rwlock_init ( RTS_rwlock_t rwlock,
RTS_Layer  ,
pthread_rwlockattr_t *  wrlock_attrs 
)

Intializes an RTS_rwlock_t in a manner similar to pthread_rwlock_init().

int RTS_rwlock_rdlock ( RTS_rwlock_t rwlock)

Obtain a read lock.

The semantics are identical to pthread_rwlock_rdlock() documented in "The Open Group Base Specifications Issue 6: IEEE Std 1003.1, 2004 Edition" [1] with the following changes:

  • If the calling thread holds a write lock, then the read lock is automatically granted. A single process can be both reading and writing, and may hold multiple kinds locks.
  • Calls to RTS_rwlock_unlock() release the recursive locks in the opposite order they were obtained.

[1] http://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_rwlock_rdlock.html

int RTS_rwlock_wrlock ( RTS_rwlock_t rwlock)

Obtain a write lock.

The semantics are identical to pthread_rwlock_wrlock() documented in "The Open Group Base Specifications Issue 6: IEEE Std 1003.1, 2004 Edition" [1] with the following changes:

  • If the calling thread already holds a write lock, then a recursive write lock is automatically granted. A single process can hold multiple write locks.
  • Calls to RTS_rwlock_unlock() release the recursive locks in the opposite order they were obtained.

Note that a write lock will not be granted if a thread already holds only read locks. Attempting to obtain a write lock in this situation will result in deadlock.

[1] http://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_rwlock_wrlock.html

int RTS_rwlock_unlock ( RTS_rwlock_t rwlock)

Release a read or write lock.

The semantics are identical to pthread_rwlock_unlock() documented in "The Open Group Base Specification Issue 6: IEEE Std 1003.1, 2004 Edition" [1] with the following additions:

  • Recursive locks are released in the oposite order they were acquired.

[1] http://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_rwlock_unlock.html