\documentclass{article}
\usepackage{lineno}
\usepackage{moreverb}
\author{Benjamin Karas}
\title{POSIX Threads	\\
Version 1.0	\\
{\small
	\code{http://vorlon.ces.cwru.edu/\~{}bjk4/week10/week10.pdf}}}
\pagestyle{headings}

\newcommand*{\code}[1]{\texttt{#1}}
\newcommand*{\com}[1]{\emph{#1\/}}
\newcommand*{\carg}[1]{{\itshape #1 \/}}
\newcommand*{\ctrl}[1]{\framebox{\code{CTRL-#1}}}
\newcommand*{\lcode}[1]{\begin{quote}\code{#1}\end{quote}}

\newcommand*{\vocab}[1]{{\bfseries #1}}

\begin{document}
\maketitle
\tableofcontents

\section{What are threads?}

Threads are flows of execution that can coexist within a single process.
When you start a new thread, a new flow of execution is created.  This is
conceptually similar to forking a new process, yet the details differ greatly.

Each process has at least one thread of execution.  If there is more than one
thread in a process, then each thread will receive a time-slice of the
execution time given to the process by the operating system.  The scheduling
of threads can be controlled to some extent by the user by choosing different
scheduling models, but this is beyond scope of this document.

All threads within a process have access to all dynamic, static, and global
variables and memory.  They also share file descriptors and user and group
ids.  Although they are executing the same program, different threads are
allowed to execute separate threads may be executing different parts of the
code at any given point in time.  Each thread owns its own set of registers
and program counter.

\begin{quote}
From W. Richard Stevens', {\itshape UNIX Network Programming Vol 2:
Interprocess Communications\/}:

All threads within a process share:
	\begin{itemize}
	\item process instructions,
	\item most data,
	\item open files (e.g., descriptors),
	\item signal handlers and signal dispositions,
	\item current working directory, and
	\item user and group IDs.
	\end{itemize}
But each thread has its own
	\begin{itemize}
	\item thread ID,
	\item set of registers, including program counter and stack pointer,
	\item stack (for local variables and return addresses),
	\item \code{errno}
	\item signal mask, and
	\item priority.
	\end{itemize}
\end{quote}

Threads are powerful for a two main reasons.  First, they are faster than
processes because the context switch is less severe.  Second, they can share
data quickly and easily.  However, this ability also causes concurrency
problems.

POSIX\footnote{POSIX is the current standard for threads.  Other versions
exist, but POSIX is the nice one} threads provide several tools to solve concurrency problems:
\begin{description}
\item [mutexes]	These operate just like binary semaphore mutexes.  They can be
used to protect critical sections of code.  Unlike semaphores, they are
treated like keys; they are either held by a thread or not.  A thread may only
lock or unlock a mutex.
\item [condition variables] Unrelated to monitors or conditional critical
regions, condition variables are used to wake a thread when a condition has
changed.
\item [read-write locks] Since the readers-writer problem is so common, POSIX
threads provide a read-write lock.  This lock solves the readers-writer
problem.
\end{description}

\section{Programming}
\subsection{Overview}

In programming threads, we will deal with the following opaque data types:
\begin{quote}
\begin{description}
\item [\code{pthread\_t}] This is a pthread ID.  It uniquely references a single
thread within a process.
\item [\code{pthread\_attr\_t}] This is a pthread attribute data
type.  It is used to set options for a thread when you create it.  If you
do not use this data type, then the system defaults will be used when you
create threads.
\item [\code{pthread\_mutex\_t}] This is a pthread mutex data type.  It is
used to track information for locking and unlocking critical sections.
\item [\code{pthread\_mutexattr\_t}] This data type is used to setup options
for mutexes.  
\item [\code{pthread\_cond\_t}] This is a pthread condition variable data
type.  It tracks information concerning condition variables for you.
\item [\code{pthread\_condattr\_t}] Use this data type to setup options for
condition variables.
\item [\code{pthread\_rwlock\_t}] This is a read write lock data type.  It
tracks information used to implement read-write locks.
\item [\code{pthread\_rwlockattr\_t}] Use this data type to setup options for
read-write locks.
\end{description}
\end{quote}

Each of the above data types is opaque.  This means we don't care about what
is in them, we just use them in functions designed to operate on the
variables.

Each data type must be initialized before it can be operated on.  In
initializing mutexes, condition variables, or read-write locks, you can
optionally use an attribute variable to customize options associated with the
variable.

All pthread function calls return zero (0) on success and a positive error
value otherwise.

\subsubsection{Getting help}

The UNIX machines may or may not be setup to access the \code{pthread} manual
pages by default.  First, make sure \code{/usr/man} or \code{/usr/share/man}
is in your \code{MANPATH} by typing:
\begin{quote}\code{echo \$MANPATH}\end{quote}
Next, to access them, try typing something like the following:
\begin{quote}\code{man -s 3t pthread\_create}\end{quote}
To get a list of topics, type the following:
\begin{quote}\code{ls /usr/man/sman3t/pthread*}\end{quote}
{\bfseries Warning:} In some cases the manual pages are wrong (e.g. for
\code{pthread\_cond\_wait}.  If you are
unsure about the syntax for a function, either look it up in one of the books
(The Monkey Book, or \emph{UNIX Network Programming Vol 2: Interprocess
Communications} by W. Richard Stevens), or check the function prototype in
\code{/usr/include/pthread.h}.

\subsubsection{Compiling}

To compile programs with threads, you need to include the \code{libpthread}
library using the \code{-lpthread} option for \code{gcc}.  If you continue to
get errors, make sure you include the necessary headers and \code{\#define}s.

You should \code{\#define \_REENTRANT} before you include any header files.
Some functions do not work well in multi-threaded environments.  Those that
are safe say so in the manual page, such as that for \code{rand\_r(3C)}.

\subsubsection{Multi-thread unsafe functions}

Some system calls do not support multiple threads.  This is a short list (from
the man page \code{attributes(5)}:

\begin{tabular}[h]{ll}
{\bfseries Unsafe}	&	{\bfseries Safe}	\\
\code{ctime}                   &     \code{ctime\_r}			\\
\code{localtime}               &     \code{localtime\_r}		\\
\code{asctime}                 &     \code{asctime\_r}			\\
\code{gmtime}                  &     \code{gmtime\_r}			\\
\code{ctermid}                 &     \code{ctermid\_r}			\\
\code{getlogin}                &     \code{getlogin\_r}		\\
\code{rand}                    &     \code{rand\_r}			\\
\code{readdir}                 &     \code{readdir\_r}			\\
\code{strtok}                  &     \code{strtok\_r}			\\
\code{tmpnam}                  &     \code{tmpnam\_r}			
\end{tabular}

\subsection{Threads}
\subsubsection{\code{pthread\_create(3t)}}

\begin{quote}
\begin{verbatim}
#include <pthread.h>
int pthread_create(pthread_t *thread,  const  pthread_attr_t
        *attr, void *(*start_routine, void*),void *arg);
\end{verbatim}
\end{quote}

This function spawns a new thread within the process.  

The first parameter is used to pass back the thread ID of the new thread to
the caller.

The options for the thread are obtained either from the attribute variable
referenced in the second parameter, or from the system defaults if the second
parameter is \code{NULL}.

You specify where the new thread will begin executing using the third
parameter.  The function \emph{must} return a \code{void *} and take a
\code{void *} argument.  See the code example for the syntax regarding how to
do this.

The last parameter is the argument that will be passed to the function given
in he third parameter.  To be valid, the pointer must point to static, global,
or dynamic memory.  This is required because each thread has its own stack,
and thus its own automatic (local) variables.

\subsubsection{\code{pthread\_attr\_init(3t)}}

\begin{quote}
\begin{verbatim}
#include <pthread.h>
int pthread_attr_init(pthread_attr_t *attr);
\end{verbatim}
\end{quote}

This function initializes an attribute object.  \code{pthread\_attr\_init(3t)}
will modify the variable you reference in the first parameter.  Be sure to
call this function before using a \code{pthread\_attr\_t} in any other
function.

Once initialized, you can modify a number of attributes using the following
functions.  Look in the manual pages for descriptions about what these
functions do.

\begin{quote}
{\small
\begin{verbatim}
int pthread_attr_setdetachstate
    (pthread_attr_t *attr, int detachstate);
int pthread_attr_getdetachstate
    (const pthread_attr_t *attr, int *detachstate);

int pthread_attr_getguardsize
    (const pthread_attr_t *attr, size_t *guardsize);
int pthread_attr_setguardsize
    (pthread_attr_t *attr, size_t guardsize);

int pthread_attr_setinheritsched
    (pthread_attr_t *attr, int inheritsched);
int pthread_attr_getinheritsched
    (const pthread_attr_t *attr, int *inheritsched);

int pthread_attr_setschedparam
    (pthread_attr_t *attr, const struct sched_param *param);
int pthread_attr_getschedparam
    (const pthread_attr_t *attr, struct sched_param *param);

int pthread_attr_setschedpolicy
    (pthread_attr_t *attr, int policy);
int pthread_attr_getschedpolicy
    (const pthread_attr_t *attr, int *policy);

int pthread_attr_setscope
    (pthread_attr_t *attr, int contentionscope);
int pthread_attr_getscope
    (const pthread_attr_t *attr, int *contentionscope);

int pthread_attr_setstackaddr
    (pthread_attr_t *attr, void *stackaddr);
int pthread_attr_getstackaddr
    (const pthread_attr_t *attr, void **stackaddr); 

int pthread_attr_setstacksize
    (pthread_attr_t *attr, size_t stacksize);
int pthread_attr_getstacksize
    (const pthread_attr_t *attr, size_t *stacksize);
\end{verbatim}
} \end{quote}

All the above functions return zero (0) on success and a positive error value
otherwise.

\subsection{\code{pthread\_attr\_destroy(3t)}}

\begin{quote}
\begin{verbatim}
#include <pthread.h>
int pthread_attr_destroy(pthread_attr_t *attr);
\end{verbatim}
\end{quote}

This function releases any memory associated with an attribute and
uninitializes it.  If you want to reuse an attribute, reinitialize it after
calling this function.

\section{Thread management}

\subsection{\code{pthread\_self(3t)}}
\begin{quote}
\begin{verbatim}
#include <pthread.h>
pthread_t pthread_self(void);
\end{verbatim}
\end{quote}

Use this function to return the thread ID of the current thread.  

\subsection{\code{pthread\_equal(3t)}}
\begin{quote}
\begin{verbatim}
#include <pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2);
\end{verbatim}
\end{quote}

If you store thread IDs globally and you have to figure out which thread you
are\ldots{} use this function to compare thread IDs safely.

\subsection{\code{pthread\_join(3t)}}
\begin{quote}
\begin{verbatim}
#include <pthread.h>
int pthread_join(pthread_t thread, void **value_ptr);
\end{verbatim}
\end{quote}

This function is conceptually equivalent to \code{wait(2)}.  The caller will
wait until the specified non-detached thread returns, is canceled, or calls
\code{pthread\_exit(3t)}.  If you want, you can also retrieve the return
value.

You \emph{must} give a specific thread ID to \code{pthread\_join(3t)} as the
first parameter.  This is annoying, but unavoidable given the calling
semantics.

The function will return the return value of the exiting thread using the
second parameter.  To be valid, the exiting thread must return a \code{void *}
to a static, global, or dynamic variable.  

\subsection{\code{pthread\_detach(3t)}}
\begin{quote}
\begin{verbatim}
#include <pthread.h>
int pthread_detach(pthread_t thread);
\end{verbatim}
\end{quote}

Detaching a thread prevents its return value from being returned to any other
threads that call \code{pthread\_join(3t)}.  If you do not detach a thread,
its resources will no be freed until another thread calls
\code{pthread\_join(3t)}.  You can avoid this hassle using
\code{pthread\_detach(3t)}.

Give \code{pthread\_detach(3t)} the thread ID of the thread you wish to
detach.  This does not have to be the current thread's ID.

\subsection{\code{pthread\_cancel(3t)}}
\begin{quote}
\begin{verbatim}
#include <pthread.h>
int pthread_cancel(pthread_t target_thread);
\end{verbatim}
\end{quote}

Canceling a thread causes it to stop executing.  Specify the thread ID in the
first parameter.

\subsection{\code{pthread\_exit(3t)}}
\begin{quote}
\begin{verbatim}
#include <pthread.h>
void pthread_exit(void *value_ptr);
\end{verbatim}
\end{quote}

A thread may safely exit in any one of three ways:
\begin{enumerate}
\item using \code{return}
\item using \code{pthread\_exit(3t)}
\item using \code{pthread\_cancel(3t)}
\end{enumerate}

Use one of the first two methods if you want to exit the current thread.  Use
the third method to cancel a separate thread.  

The parameter to \code{pthread\_exit(3t)} is the return value for the
function.  It should be a pointer to a static, global, or dynamic variable so
that another thread can access the data it points to.  

\section{Mutexes}

Mutexes are used to protect critical sections.  They do this by only allowing
one thread to lock a mutex at any point in time.  If another thread attempts
to lock an already locked mutex, it will block until it becomes unlocked.  You
can use this property to bracket accesses to shared data to prevent
concurrency problems.

Mutexes must be initialized before use, either using a mutex attribute
variable or using the system defaults.  When you will no longer use a mutex,
you should destroy it.

\subsection{\code{pthread\_mutex\_init(3t)}}
\begin{quote}
\begin{verbatim}
#include <pthread.h>
int pthread_mutex_init
    (pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
pthread_mutex_t mutex= PTHREAD_MUTEX_INITIALIZER
\end{verbatim}
\end{quote}

Mutexes must be initialized.  To do so, you may either use the function,
\code{pthread\_mutex\_init(3t)}, or you may assign the variable to the initializer, \code{PTHREAD\_MUTEX\_INITIALIZER}.

The first argument is the address of your mutex variable of type
\code{pthread\_mutex\_t}.

The second parameter may be \code{NULL}, or point to a previously initialized attribute object.

\subsection{\code{pthread\_mutex\_destroy(3t)}}
\begin{quote}
\begin{verbatim}
#include <pthread.h>
int pthread_mutex_destroy(pthread_mutex_t *mutex);
\end{verbatim}
\end{quote}

Destroying a mutex frees the resources associated with the mutex.  This will
also uninitialize the mutex, so if you want to reuse it, remember to
reinitialize it after calling this function.

\subsection{\code{pthread\_mutexattr\_init(3t)}}
\begin{quote}
\begin{verbatim}
#include <pthread.h>

int pthread_mutexattr_init(pthread_mutexattr_t *attr);
\end{verbatim}
\end{quote}

Before you can change options in a mutex attribute object, you must initialize
it.  Give the function the address of a variable of type \code{pthread\_mutexattr\_t}.

You can change attributes using the following functions:
\begin{quote}
\begin{verbatim}
int pthread_mutexattr_getpshared
    (const pthread_mutexattr_t *attr, int *pshared);
int pthread_mutexattr_setpshared
    (pthread_mutexattr_t *attr, int pshared);
\end{verbatim}
\end{quote}

The above function return zero (0) on success, and a positive error value
otherwise.

\subsection{\code{pthread\_mutexattr\_destroy(3t)}}
\begin{quote}
\begin{verbatim}
#include <pthread.h>
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
\end{verbatim}
\end{quote}

This function released resources associated with the mutex attribute.  If you
want to reuse the attribute variable, remember to reinitialize it after
calling this function. 

\section{Mutex operations}

\subsection{\code{pthread\_mutex\_lock(3t)}}
\begin{quote}
\begin{verbatim}
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
\end{verbatim}
\end{quote}

Locking a resource is similar to waiting on a binary semaphore.  Only one
thread may hold mutex at any one point in time.  If the mutex is not
available, then the caller will block until it is.  The parameter is the
address of the initialized mutex.

We will see later that mutexes and condition variables work together in some
very nice ways, but that might be confusing at first, especially toward how
things maintain mutexes.

\subsection{\code{pthread\_mutex\_trylock(3t)}}
\begin{quote}
\begin{verbatim}
#include <pthread.h>
int pthread_mutex_trylock(pthread_mutex_t *mutex);
\end{verbatim}
\end{quote}

This function is similar to \code{pthread\_mutex\_lock(3t)}, except that it
will not block.  Instead, if the mutex is not available, it will return
\code{EBUSY} as an error.  The parameter is the address of the initialized
mutex.

\subsection{\code{pthread\_mutex\_unlock(3t)}}
\begin{quote}
\begin{verbatim}
#include <pthread.h>
int pthread_mutex_unlock(pthread_mutex_t *mutex);
\end{verbatim}
\end{quote}

This is equivalent to signalling a binary semaphore.  This releases the mutex,
which lets other threads grab it.  The scheduling attribute is used to choose
the next thread to wake up, if any are blocked.  The parameter is the address
of the initialized mutex.

\section{Condition variables}
\subsection{Overview}

A condition variable lets a process block on it until another process signals
the condition variable.  It is assumed that if you are waiting for a condition
to occur, such as data arriving for processing, you will repeatedly block
until it turns true.

Each condition variable is tied to a mutex.  This mutex is used to protect the
condition variable.  The mutex should be locked prior to waiting on the
condition variable and unlocked after waking.  In reality, the wait function
will unlock the mutex prior to blocking and re-lock it upon waking.

The normal use of condition variables is to wait for a variable to reach a
certain value.  This is why the condition variable is tied to a mutex.  

\subsection{\code{pthread\_cond\_init(3t)}}
\begin{quote}
\begin{verbatim}
#include <pthread.h>
int pthread_cond_init
	(pthread_cond_t *cond, const pthread_condattr_t *attr);
pthread_cond_t cond= PTHREAD_COND_INITIALIZER;
\end{verbatim}
\end{quote}

Condition variables must be initialized.  To do so, you may either use the
function, \code{pthread\_cond\_t(3t)}, or you may assign the variable to the
initializer, \code{PTHREAD\_COND\_INITIALIZER}.  Make sure the condition
variable is either in dynamic memory, is static, or is global.  Automatic
variables, which are kept on the stack, are not shared between threads.

The first parameter is the condition variable to initialize.

You can set the options for the condition variable either using the system
defaults by using \code{NULL} for the second parameter, or using a previously
initialized condition variable attribute variable.

\subsection{\code{pthread\_cond\_destroy(3t)}}
\begin{quote}
\begin{verbatim}
#include <pthread.h>
int pthread_cond_destroy(pthread_cond_t *cond);
\end{verbatim}
\end{quote}

Destroying a condition variable releases the resources associated with the
condition variable.  If you plan on reusing the condition variable after
calling this function, remember to reinitialize it.

\subsection{\code{pthread\_condattr\_init(3t)}}
\begin{quote}
\begin{verbatim}
#include <pthread.h>
int pthread_condattr_init(pthread_condattr_t *attr);
\end{verbatim}
\end{quote}

Before you can change options in a condition variable attribute object, you
must initialize it.  Give the function the address of a condition variable
attribute.

Once initialized, you can use the following functions to change options:

\begin{quote}
{\small
\begin{verbatim}
#include <pthread.h>
int pthread_condattr_getpshared
        (const pthread_condattr_t *attr, int *pshared);
int pthread_condattr_setpshared
        (pthread_condattr_t *attr, int pshared); 
\end{verbatim}
} \end{quote}

The above functions return zero (0) on success and a positive error value
otherwise.

\subsection{\code{pthread\_condattr\_destroy(3t)}}
\begin{quote}
\begin{verbatim}
#include <pthread.h>
int pthread_condattr_destroy(pthread_condattr_t *attr);
\end{verbatim}
\end{quote}

This function releases resources associated with the condition variable
attribute variable.  If you plan on reusing the attribute after calling this
function, remember to reinitialize it.

\section{Condition variable operations}

\subsection{\code{pthread\_cond\_wait(3t)}}
\begin{quote}
\begin{verbatim}
#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *cond
       , pthread_mutex_t *mutex);
\end{verbatim}
\end{quote}

You use this function to block on a condition variable.  Before you can wait
on the condition variable, however, you must lock the mutex.  This mutex is
used to protect the condition variable data structures.  Do not worry about
the associated mutex causing a deadlock, for the function call will unlock it
just prior to blocking and will re-lock it when it unblocks.  The manual page
for this function is especially well written, even though its function
definition is incorrect.  I highly recommend you read it for details on how
\code{pthread\_cond\_wait(3t)} works.

According to the manual page:

\begin{quote}
pthread\_cond\_wait atomically unlocks  the  mutex  (as  per
pthread\_unlock\_mutex) and waits for the condition variable
cond to be signaled. The thread execution is suspended and
does not consume any CPU time until the condition variable
is signaled. The mutex  must  be  locked  by  the  calling
thread on entrance to pthread\_cond\_wait.  Before returning
to the calling thread, pthread\_cond\_wait re-acquires mutex
(as per pthread\_lock\_mutex)
\end{quote}

If you are using this function to wait until a variable reaches a certain
state, you should place this function in a loop to avoid problems with
spurious awakening.

\subsection{\code{pthread\_cond\_timedwait(3t)}}
\begin{quote}
\begin{verbatim}
#include <pthread.h>
int pthread_cond_timedwait
       (pthread_cond_t *cond, pthread_mutex_t *mutex,
        const struct timespec *abstime);
\end{verbatim}
\end{quote}

This function is very similar to \code{pthread\_cond\_wait(3t)}, except that
you can define a time limit to the waiting.  The time limit is contained in a
\code{struct timespec}, which refers to a specific point in time, as opposed
to an offset.  The structure looks like:

\begin{quote}
\begin{verbatim}
struct timespec {
    time_t  tv_sec;      /* seconds */
    long    tv_nsec;     /* nanoseconds */
}
\end{verbatim}
\end{quote}

\code{tv\_sec} is the number of seconds since January 1, 1970, UTC.

\subsection{\code{pthread\_cond\_signal(3t)}}
\begin{quote}
\begin{verbatim}
#include <pthread.h>
int pthread_cond_signal(pthread_cond_t *cond);
\end{verbatim}
\end{quote}

This function wakes one thread that waiting on a condition variable.

\subsection{\code{pthread\_cond\_broadcast(3t)}}
\begin{quote}
\begin{verbatim}
#include <pthread.h>
int pthread_cond_broadcast(pthread_cond_t *cond);
\end{verbatim}
\end{quote}

This function wakes all threads waiting on a condition variable.

\section{Read-write locks}

Read-write locks help solve readers-writer type problems.  In these problems,
no threads may enter a critical section if a single writer is writing.
However, any number of readers may enter if no writer is writing, and no
writer may enter if any readers are reading.  

These locks work very similarly to mutexes, except that the functions specify
the type of action intended by the thread.  

\subsection{\code{pthread\_rwlock\_init(3t)}}
\begin{quote}
\begin{verbatim}
#include <pthread.h>
int pthread_rwlock_init
        (pthread_rwlock_t *rwlock,
        const pthread_rwlockattr_t *attr);
pthread_rwlock_t rwlock=PTHREAD_RWLOCK_INITIALIZER;
\end{verbatim}
\end{quote}

Read-write locks must be initialized before use.  To do so, you may either use
the function, \code{pthread\_rwlock\_init(3t)}, or you may assign the lock to
the initializer, \code{PTHREAD\_RWLOCK\_INITIALIZER}.  Make sure the
read-write lock is located in either dynamic, static, or global memory, as
automatic variables will not be shared between threads.

The first parameter is the read-write lock to initialize.

If the second parameter is \code{NULL}, then the system defaults are used to
initialize the read-write lock.  Otherwise, the read-write lock attribute
variable is used to setup the options of the read-write lock.

\subsection{\code{pthread\_rwlock\_destroy(3t)}}
\begin{quote}
\begin{verbatim}
#include <pthread.h>
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
\end{verbatim}
\end{quote}

This function releases the resources associated with the read-write lock.  If
you plan on reusing the read-write lock variable after calling this function,
remember to reinitialize it.

\subsection{\code{pthread\_rwlockattr\_init(3t)}}
\begin{quote}
\begin{verbatim}
#include <pthread.h>
int pthread_rwlockattr_init(pthread_rwlockattr_t *attr);
\end{verbatim}
\end{quote}

Before you can change options in a read-write lock attribute variable, you
must initialize it.  Give the function the address of your read-write lock
attribute variable.

Once initialized, you can use the following functions to change options:

\begin{quote}
{\small
\begin{verbatim}
int pthread_rwlockattr_getpshared
        (const pthread_rwlockattr_t *attr, int *pshared);
int pthread_rwlockattr_setpshared
        (pthread_rwlockattr_t *attr, int pshared);
\end{verbatim}
} \end{quote}

\subsection{\code{pthread\_rwlockattr\_destroy(3t)}}
\begin{quote}
\begin{verbatim}
#include <pthread.h>
int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr);
\end{verbatim}
\end{quote}

Destroying a read-write lock attribute variable releases resources associate
with it.  If you plan on reusing the read-write lock attribute variable after
calling this function, remember to reinitialize it.

\subsection{\code{pthread\_rwlock\_rdlock(3t)}}
\begin{quote}
\begin{verbatim}
#include <pthread.h>
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
\end{verbatim}
\end{quote}

These functions are equivalent to the functions \code{pthread\_cond\_lock(3t)} and
\code{pthread\_cond\_trylock(3t)}, except they follow the rules imposed on
readers by the readers-writer problem.

\subsection{\code{pthread\_rwlock\_wrlock(3t)}}
\begin{quote}
\begin{verbatim}
#include <pthread.h>
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
\end{verbatim}
\end{quote}

These functions are equivalent to the functions \code{pthread\_cond\_lock(3t)} and
\code{pthread\_cond\_trylock(3t)}, except they follow the rules imposed on
writers by the readers-writer problem.

\subsection{\code{pthread\_rwlock\_unlock(3t)}}
\begin{quote}
\begin{verbatim}
#include <pthread.h>
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
\end{verbatim}
\end{quote}

This function is equivalent to \code{pthread\_cond\_unlock(3t)}, except that
is will not violate the conditions imposed by the readers-writer problem.

\medskip\hrule

\begin{flushright}
	 Benjamin Karas	\\
	\code{bjk4@po.cwru.edu}
\end{flushright}
\end{document}
