NAME

libslack(thread) - thread module


SYNOPSIS

    #include <slack/thread.h>

    typedef struct Locker Locker;
    typedef int lockf_t(void *lock);

    Locker *locker_create_mutex(pthread_mutex_t *mutex);
    Locker *locker_create_rwlock(pthread_rwlock_t *rwlock);
    Locker *locker_create_debug_mutex(pthread_mutex_t *mutex);
    Locker *locker_create_debug_rwlock(pthread_rwlock_t *rwlock);
    Locker *locker_create(void *lock, lockf_t *tryrdlock, lockf_t *rdlock, lockf_t *trywrlock, lockf_t *wrlock, lockf_t *unlock);
    void locker_release(Locker *locker);
    void *locker_destroy(Locker **locker);
    int locker_tryrdlock(Locker *locker);
    int locker_rdlock(Locker *locker);
    int locker_trywrlock(Locker *locker);
    int locker_wrlock(Locker *locker);
    int locker_unlock(Locker *locker);

    int thread_attr_init(pthread_attr_t *attr);
    int thread_attr_set(pthread_attr_t *attr, int scope, int detachstate, int inheritsched, int schedpolicy, struct sched_param *schedparam);
    int thread_init();
    int thread_setcancel(int type, int state, int *oldtype, int *oldstate);

    int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr);
    int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
    int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
    int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
    int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
    int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
    int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
    int pthread_rwlockattr_init(pthread_rwlockattr_t *attr);
    int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr);
    int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attr, int *pshared);
    int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared);

    int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count);
    int pthread_barrier_destroy(pthread_barrier_t *barrier);
    int pthread_barrier_wait(pthread_barrier_t *barrier);
    int pthread_barrierattr_init(pthread_barrierattr_t *attr);
    int pthread_barrierattr_destroy(pthread_barrierattr_t *attr);
    int pthread_barrierattr_getpshared(const pthread_barrierattr_t *attr, int *pshared);
    int pthread_barrierattr_setpshared(pthread_barrierattr_t *attr, int pshared);


DESCRIPTION

This module provides an abstraction of thread synchronisation that facilitates the implementation of MT-Disciplined libraries. I'll explain what this means.

Libraries need to be MT-Safe when used in a multi threaded program. However, most programs are single threaded and synchronisation doesn't come for free so libraries should be Unsafe when used in a single threaded program. Even in multi threaded programs, some functions or objects may only be accessed by a single thread and so they should not incur the expense of synchronisation.

When an object is shared between multiple threads which need to be synchronised, the method of synchronisation must be carefully selected by the client code. There are tradeoffs between concurrency and overhead. The greater the concurrency, the greater the overhead. More locks give greater concurrency but have greater overhead. Readers/Writer locks can give greater concurrency than Mutex locks but have greater overhead. One lock for each object may be required, or one lock for all (or a set of) objects may be more appropriate.

Generally, the best synchronisation strategy for a given application can only be determined by testing/benchmarking the written application. It is important to be able to experiment with the synchronisation strategy at this stage of development without pain.

The solution, of course, is to decouple the synchronisation strategy from the library code. To facilitate this, the Locker type and associated functions can be incorporated into library code to provide the necessary flexibility.

The Locker type specifies a lock and a set of functions for manipulating the lock. Arbitrary objects can include a pointer to a Locker object to use for thread synchronisation. Such objects may each have their own lock by having separate Locker objects or they may share the same lock by sharing the same Locker object. Only the application developer can determine what is appropriate for each application on an case by case basis.

MT-Disciplined means that the application developer has a mechanism for specifying the synchronisation requirements to be applied to library code.

This module also provides a few shorthand functions for using the pthread library as well as a few synchronisation variables that are defined in recent standards but may not be on your system yet (i.e. readers/writer locks and barriers).

Locker *locker_create_mutex(pthread_mutex_t *lock)

Creates a Locker object that will operate on the mutex lock, lock. locker_tryrdlock() and locker_trywrlock() will call pthread_mutex_trylock(). locker_rdlock() and locker_wrlock() will call pthread_mutex_lock(). locker_unlock() will call pthread_mutex_unlock(). It is the caller's responsibility to initialise lock if necessary before use and to destroy lock if necessary after use. On success, returns the new Locker object. On error, returns NULL.

Locker *locker_create_rwlock(pthread_mutex_t *lock)

Creates a Locker object that will operate on the readers/writer lock, lock. locker_tryrdlock() will call pthread_rwlock_tryrdlock(). locker_rdlock() will call pthread_rwlock_rdlock(). locker_trywrlock() will call pthread_rwlock_trywrlock(). locker_wrlock() will call pthread_rwlock_wrlock(). locker_unlock() will call pthread_rwlock_unlock(). It is the caller's responsibility to initialise lock if necessary before use and to destroy lock if necessary after use. On success, returns the new Locker object. On error, returns NULL.

Locker *locker_create_mutex_debug(pthread_mutex_t *mutex)

Just like locker_create_mutex() except that debug messages are printed to standard output before and after each locking function is called. The debug messages look like ``[thread id] funcname(mutex address) ...\n'' and ``[thread id] funcname(mutex address) done\n''. On success, returns the new Locker. On error, returns NULL.

Locker *locker_create_debug_rwlock(pthread_mutex_t *rwlock)

Just like locker_create_rwlock() except that debug messages are printed to standard output before and after each locking function is called. The debug messages look like ``[thread id] funcname(rwlock address) ...\n'' and ``[thread id] funcname(rwlock address) done\n''. On success, returns the new Locker. On error, returns NULL.

Locker *locker_create(void *lock, lockf_t *tryrdlock, lockf_t *rdlock, lockf_t *trywrlock, lockf_t *wrlock, lockf_t *unlock)

Creates a Locker object that will operate on the synchronisation variable, lock. locker_tryrdlock() will call tryrdlock. locker_rdlock() will call rdlock. locker_trywrlock() will call trywrlock. locker_wrlock() will call wrlock. locker_unlock() will call unlock. It is the caller's responsibility to initialise lock if necessary before use and to destroy lock if necessary after use. On success, returns the new Locker object. On error, returns NULL.

void locker_release(Locker *locker)

Releases (deallocates) locker. It is the caller's responsibility to destroy the synchronisation variable used by locker if necessary.

void *locker_destroy(Locker **locker)

Destroys (deallocates and sets to NULL) locker. Returns NULL. It is the caller's responsibility to destroy the synchronisation variable used by locker if necessary.

int locker_tryrdlock(Locker *locker)

Tries to claim a read lock on the synchronisation variable managed by locker. See pthread_mutex_trylock() and pthread_rwlock_tryrdlock() for details. On success, returns 0. On error, returns -1 with errno set appropriately.

int locker_rdlock(Locker *locker)

Claims a read lock on the synchronisation variable managed by locker. See pthread_mutex_lock() and pthread_rwlock_rdlock() for details. On success, returns 0. On error, returns -1 with errno set appropriately.

int locker_trywrlock(Locker *locker)

Tries to claim a write lock on the synchronisation variable managed by locker. See pthread_mutex_trylock() and pthread_rwlock_trywrlock() for details. On success, returns 0. On error, returns -1 with errno set appropriately.

int locker_wrlock(Locker *locker)

Claims a write lock on the synchronisation variable managed by locker. See pthread_mutex_lock() and pthread_rwlock_wrlock() for details. On success, returns 0. On error, returns -1 with errno set appropriately.

int locker_unlock(Locker *locker)

Unlocks the synchronisation variable managed by locker. See pthread_mutex_unlock() and pthread_rwlock_unlock() for details. On success, returns 0. On error, returns -1 with errno set appropriately.

int thread_attr_init(pthread_attr_t *attr)

Initialises attr for creating detached threads with system scope and explicit ``other'' scheduling. I prefer this to the POSIX default attributes (and Linux doesn't have process scope scheduling anyway). On success, returns 0. On error, returns the error code from the underlying pthread library call that failed.

int thread_attr_set(pthread_attr_t *attr, int scope, int detachstate, int inheritsched, int schedpolicy, struct sched_param *schedparam)

Initialises attr with the following arguments. Each argument is passed to the pthread_attr_set function whose name corresponds with its name. On success, returns 0. On error, returns the error code from the underlying pthread library call that failed.

int thread_init()

Enables deferred cancellation on the current thread. This is the same as the POSIX default and is therefore pointless on systems that implement POSIX threads accurately. It's just here for those who like to be explicit. On success, returns 0. On error, returns the error code from the underlying pthread library call that failed.

int thread_setcancel(int type, int state, int *oldtype, int *oldstate)

Sets the cancellation type and state for the current thread to type and state, respectively. If oldtype is not NULL, the previous cancellation type is stored there. If oldstate is not NULL, the previous cancellation state is stored there. On success, return 0. On error, returns the error code from the underlying pthread library call that failed.


MT-Level

MT-Safe


SEE ALSO

libslack(3), pthread_attr_init(3), pthread_attr_setscope(3), pthread_attr_setdetachstate(3), pthread_attr_setinheritsched(3), pthread_attr_setschedpolicy(3), pthread_attr_setschedparam(3), pthread_setcanceltype(3), pthread_setcancelstate(3)


AUTHOR

20010215 raf <raf@raf.org>