Multithreaded Programming Guide

Unique Solaris Synchronization Functions-Readers/Writer Locks

Readers/Writer locks allow simultaneous read access by many threads while restricting write access to only one thread at a time.

When any thread holds the lock for reading, other threads can also acquire the lock for reading but must wait to acquire the lock for writing. If one thread holds the lock for writing, or is waiting to acquire the lock for writing, other threads must wait to acquire the lock for either reading or writing.

Readers/writer locks are slower than mutexes, but can improve performance when they protect data that are not frequently written but that are read by many concurrent threads.

Use readers/writer locks to synchronize threads in this process and other processes by allocating them in memory that is writable and shared among the cooperating processes (see mmap(2)) and by initializing them for this behavior.

By default, the acquisition order is not defined when multiple threads are waiting for a readers/writer lock. However, to avoid writer starvation, the Solaris threads package tends to favor writers over readers.

Readers/writer locks must be initialized before use.

Initialize a Readers/Writer Lock

rwlock_init(3T)

#include <synch.h>  (or #include <thread.h>)

int rwlock_init(rwlock_t *rwlp, int type, void * arg);

Use rwlock_init() to initialize the readers/writer lock pointed to by rwlp and to set the lock state to unlocked. type can be one of the following (note that arg is currently ignored).

Multiple threads must not initialize the same readers/writer lock simultaneously. Readers/writer locks can also be initialized by allocation in zeroed memory, in which case a type of USYNC_THREAD is assumed. A readers/writer lock must not be reinitialized while other threads might be using it.

Initializing Readers/Writer Locks with Intraprocess Scope

#include <thread.h>

rwlock_t rwlp;
int ret;

/* to be used within this process only */
ret = rwlock_init(&rwlp, USYNC_THREAD, 0); 

Initializing Readers/Writer Locks with Interprocess Scope

#include <thread.h>

rwlock_t rwlp;
int ret;

/* to be used among all processes */
ret = rwlock_init(&rwlp, USYNC_PROCESS, 0); 

Return Values

rwlock_init() returns zero after completing successfully. Any other returned value indicates that an error occurred. When any of the following conditions occur, the function fails and returns the corresponding value.


EINVAL

Invalid argument.


EFAULT

rwlp or arg points to an illegal address.

Acquire a Read Lock

rw_rdlock(3T)

#include <synch.h> (or #include <thread.h>)

int rw_rdlock(rwlock_t *rwlp);

Use rw_rdlock() to acquire a read lock on the readers/writer lock pointed to by rwlp. When the readers/writer lock is already locked for writing, the calling thread blocks until the write lock is released. Otherwise, the read lock is acquired.

Return Values

rw_rdlock() returns zero after completing successfully. Any other returned value indicates that an error occurred. When any of the following conditions occur, the function fails and returns the corresponding value.


EINVAL

Invalid argument.


EFAULT

rwlp points to an illegal address.

Try to Acquire a Read Lock

rw_tryrdlock(3T)

#include <synch.h>  (or #include <thread.h>)

int rw_tryrdlock(rwlock_t *rwlp);

Use rw_tryrdlock() to attempt to acquire a read lock on the readers/writer lock pointed to by rwlp. When the readers/writer lock is already locked for writing, it returns an error. Otherwise, the read lock is acquired.

Return Values

rw_tryrdlock() returns zero after completing successfully. Any other returned value indicates that an error occurred. When any of the following conditions occur, the function fails and returns the corresponding value.


EINVAL

Invalid argument.


EFAULT

rwlp points to an illegal address.


EBUSY

The readers/writer lock pointed to by rwlp was already locked.

Acquire a Write Lock

rw_wrlock(3T)

#include <synch.h>  (or #include <thread.h>)

int rw_wrlock(rwlock_t *rwlp);

Use rw_wrlock() to acquire a write lock on the readers/writer lock pointed to by rwlp. When the readers/writer lock is already locked for reading or writing, the calling thread blocks until all the read locks and write locks are released. Only one thread at a time can hold a write lock on a readers/writer lock.

Return Values

rw_wrlock() returns zero after completing successfully. Any other returned value indicates that an error occurred. When any of the following conditions occur, the function fails and returns the corresponding value.


EINVAL

Invalid argument.


EFAULT

rwlp points to an illegal address.

Try to Acquire a Write Lock

rw_trywrlock(3T)

#include <synch.h>  (or #include <thread.h>)

int rw_trywrlock(rwlock_t *rwlp);

Use rw_trywrlock() to attempt to acquire a write lock on the readers/writer lock pointed to by rwlp. When the readers/writer lock is already locked for reading or writing, it returns an error.

Return Values

rw_trywrlock() returns zero after completing successfully. Any other returned value indicates that an error occurred. When any of the following conditions occur, the function fails and returns the corresponding value.


EINVAL

Invalid argument.


EFAULT

rwlp points to an illegal address.


EBUSY

The readers/writer lock pointed to by rwlp was already locked.

Unlock a Readers/Writer Lock

rw_unlock(3T)

#include <synch.h>  (or #include <thread.h>)

int rw_unlock(rwlock_t *rwlp);

Use rw_unlock() to unlock a readers/writer lock pointed to by rwlp. The readers/writer lock must be locked and the calling thread must hold the lock either for reading or writing. When any other threads are waiting for the readers/writer lock to become available, one of them is unblocked.

Return Values

rw_unlock() returns zero after completing successfully. Any other returned value indicates that an error occurred. When any of the following conditions occur, the function fails and returns the corresponding value.


EINVAL

Invalid argument.


EFAULT

rwlp points to an illegal address.

Destroy Readers/Writer Lock State

rwlock_destroy(3T)

#include <synch.h>  (or #include <thread.h>)

int rwlock_destroy(rwlock_t *rwlp);

Use rwlock_destroy() to destroy any state associated with the readers/writer lock pointed to by rlwp. The space for storing the readers/writer lock is not freed.

Return Values

rwlock_destroy() returns zero after completing successfully. Any other returned value indicates that an error occurred. When any of the following conditions occur, the function fails and returns the corresponding value.


EINVAL

Invalid argument.


EFAULT

rwlp points to an illegal address.

Readers/Writer Lock Example

Example 9-1 uses a bank account to demonstrate readers/writer locks. While the program could allow multiple threads to have concurrent read-only access to the account balance, only a single writer is allowed. Note that the get_balance() function needs the lock to ensure that the addition of the checking and saving balances occurs atomically.


Example 9-1 Read/Write Bank Account

rwlock_t account_lock;
float checking_balance = 100.0;
float saving_balance = 100.0;
...
rwlock_init(&account_lock, 0, NULL);
...

float
get_balance() {
    float bal;

    rw_rdlock(&account_lock);
    bal = checking_balance + saving_balance;									
    rw_unlock(&account_lock);
    return(bal);
}

void
transfer_checking_to_savings(float amount) {
    rw_wrlock(&account_lock);
    checking_balance = checking_balance - amount;
    saving_balance = saving_balance + amount;
    rw_unlock(&account_lock);
}