CSC 552.201 - Advanced Unix Programming, Fall, 2008 Monday, November 24 POSIX threads (pthreads)
-
Upload
clara-bradley -
Category
Documents
-
view
231 -
download
4
Transcript of CSC 552.201 - Advanced Unix Programming, Fall, 2008 Monday, November 24 POSIX threads (pthreads)
CSC 552.201 - Advanced Unix Programming, Fall, 2008
Monday, November 24POSIX threads (pthreads)
Pthread system calls, Table 12.1
• pthread_create creates a thread• pthread_join waits for another thread• pthread_self gets this thread’s ID• pthread_equal tests 2 threads for equality• pthread_detach makes caller a daemon
thread; no pthread_join required for cleanup• pthread_exit exits just this thread
• Returning from thread startup function has same effect
Pthread system calls continued
• pthread_kill sends a signal to a thread• pthread_cancel terminates another thread• Signal delivery to threads after pthread_create
• The signal mask is inherited from the creating thread.• The set of signals pending for the new thread is empty.• A new signal can be delivered to any unmasked thread!
• pthread_sigmask sets a thread’s signal mask• Guideline: Use pthread_sigmask to keep child threads from
handling signals. See next slide for the main thread.
Keeping threads safe from signals
• 1. Temporarily block signals in initial thread if ((sigfillset(&maskblock)== -1) || (sigprocmask(SIG_SETMASK, &maskblock, &maskold) == -1))
• 2. pthread_create child thread(s)• The signal mask is inherited from the creating thread.
• 3. The main thread can restore its masksigprocmask(SIG_SETMASK, &maskold, NULL);
pthread_*() errors don’t set errno
• You cannot report these errors using perror()• strerror() can format a return error code, but
it is not thread safe – it uses a static buffer• ~parson/UnixSysProg/threadwait/strerror_r.c
expands the textbook’s thread-safe functions• They use a mutex and signal masks to protect the
invoking thread.• My enhancements report some additional errors.• See example usage in threadwait.cxx.
pthread_create parameters
• int pthread_create(pthread_t *restrict thread, const pthread_attr_t *restrict attr, void *(*start_routine)(void*), void *restrict arg);
• The start_routine takes a void * parameter that is passed as the pthread_create arg parameter.
• The start_routine return value or pthread_exit parameter gives the thread’s exit status as a pointer to an application object, or a (void *) int.
pthread attributes
• When pthread_create’s pthread_attr_t attr parameter is non-NULL, it sets thread attributes.
• Joinable versus detached (daemon) state. Former needs a pthread_join call to clean up its resources.
• Stack size and stack guard size.• Thread scheduling policy – FIFO (preemption by priority,
preempted go at queue front), round robin (additional periodic preemption, at queue back), sporadic, with a scheduling priority.
• Inter-process versus intra-process scheduling contention scope.• Attributes are set with dedicated POSIX functions.• There are O.S. specific extensions, e.g., hardware thread pinning.
~parson/UnixSysProg/threadwait/• if (((errcode = pthread_attr_init(&pattr)) != 0) || ((errcode = pthread_attr_setdetachstate(&pattr, PTHREAD_CREATE_DETACHED)) != 0) || ((errcode = pthread_attr_setschedpolicy(&pattr, SCHED_FIFO)) != 0)) { // by priority and THEN by FIFO order DUMPERR("producer attribute error"); exit(errcode);} if (((errcode = pthread_attr_init(&cattr)) != 0) || ((errcode = pthread_attr_setdetachstate(&cattr, PTHREAD_CREATE_JOINABLE)) != 0) || ((errcode = pthread_attr_setschedpolicy(&cattr, SCHED_RR)) != 0)) { // time-sliced round robin by priority DUMPERR("producer attribute error"); exit(errcode); }
Thread local data
• int pthread_key_create(pthread_key_t *key, void (*destructor, void*));
• This function creates a thread-specific data key visible to all threads in the process. Key values provided by pthread_key_create() are opaque objects used to locate thread-specific data. Although the same key value may be used by different threads, the values bound to the key by pthread_setspecific() are maintained on a per-thread basis and persist for the life of the calling thread. Destructor may be NULL. If not, it is called for the key’s value upon thread exit.
• The key acts like a hash index to access thread-local data.
Thread local data access
• int pthread_setspecific(pthread_key_t key, const void *value);
• Sets thread-local data for key to pointer value.• value points to a valid object that persists across calls.
• void *pthread_getspecific(pthread_key_t key);• Returns the key’s value in this thread, or NULL if they
key has not been set in this thread.
• int pthread_key_delete(pthread_key_t key);• Deletes the mapping in this thread.
• Used for identical functions in multiple threads.
A mutex encloses a critical section for mutual exclusion.
• A properly used mutex serializes access to inter-dependent data among multiple threads.
• pthread_mutex_init(&buffermutex, NULL);• pthread_mutex_lock() … pthread_mutex_unlock()
• Data access must occur in bounded time.• Second, pthread_mutexattr_t parameter is preset
using pthread_mutexattr_init.• pthread_mutex_trylock is a non-blocking locker.• Mutex is freed using pthread_mutex_destroy.
Mutex attributes (all return int)
• pthread_mutexattr_init(pthread_mutexattr_t *attr);• pthread_mutexattr_destroy(pthread_mutexattr_t
*attr)• pthread_mutexattr_gettype(pthread_mutexattr_t
*restrict attr, int *restrict type);• type PTHREAD_MUTEX_NORMAL – no deadlock
detection, non-recursive (granted once per thread)• PTHREAD_MUTEX_RECURSIVE allows one thread to
lock It multiple times. Unlock calls must balance locks.• PTHREAD_MUTEX_DEFAULT is a risky gamble.
Thread-unsafe static initialization
• The following code is not multithread safe. Why?
static int firsttime = 1 ;...if (firsttime) {
do some initialization stepsfirsttime = 0 ;
}
Thread-safe static initialization
• The following code is multithread safe. It runs at most one time.
static pthread_once_t firsttime = PTHREAD_ONCE_INIT ;
static void init(void) { initialization steps … }…int status = pthread_once(&firsttime, init);
Condition variables
• A condition variable allows a thread to release a mutex temporarily while waiting for a condition on data.
• The waiting thread is awakened and it reacquires the mutex in one atomic step.
• The awakened thread must still recheck the data condition after reacquiring the mutex, since another such thread may have already changed the data condition.
pthread_cond_init()
• pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr)
• The attr is for shared condvars in shared memory.• Use NULL in single process thread synchronization.
• pthread_cond_destroy destroys a condition var.• pthread_cond_wait and pthread_cond_timedwait
• These wait to be signaled.
• pthread_cond_signal and pthread_cond_broadcast• The signal one waiting thread or all waiting threads respectively.
Pthread read-write locks
• Read/write locks enable multiple readers or one writer to lock a critical section
• int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);• int pthread_rwlock_destroy(pthread_rwlock_t
**rwlock);
• pthread_rwlock_rdlock(), pthread_rwlock_tryrdlock• pthread_rwlock_wrlock()• pthread_rwlock_unlock()
pthread_rwlock_rdlock() pthread_rwlock_tryrdlock()
• Multiple readers can acquire the lock if no writer holds the lock.
• “POSIX states that it is up to the implementation whether to allow a reader to acquire a lock if writers are blocked on the lock.”
• Solaris man page says, “The calling thread does not acquire the lock if a writer holds the lock or if writers of higher or equal priority are blocked on the lock; otherwise, the calling thread acquires the lock. If the read lock is not acquired, the calling thread blocks until it can acquire the lock.
pthread_rwlock_wrlock pthread_rwlock_trywrlock
• Pthread_rwlock_wrlock• The calling thread acquires the write lock if no other
thread (reader or writer) holds the read-write lock rwlock. Otherwise, the thread blocks until it can acquire the lock.• Writers are favored over readers of the same priority
to avoid writer starvation.
• Pthread_rwlock_trywrlock• The function fails if any thread currently holds rwlock
(for reading or writing).
POSIX:SEM semaphores
• sem_t *sem_open(const char *name, int oflag, …);• optional permissions and initial value for O_CREATE
• int sem_init(sem_t *sem, int pshared, unsigned int value); ALSO int sem_destroy(sem_t *sem);
• pshared is 0 for single process, 1 for interprocess• value is 0 for a locked semaphore, > 0 for unlocked
• int sem_wait(sem_t *sem);• blocks on a 0 sem_t value, decrements if > 0• or int sem_trywait(sem_t *sem);
• int sem_post(sem_t *sem);• Adds 1 to sem_t if there are no blocked sem_wait callers, unblocks
one thread if 1 or more are blocked
Programming assignment 4(modify a copy of assignment 2 or 3)
• Each chess plugin starts one or more threads to monitor its incoming data streams, blocking on read(), and copying the data stream to a log file and, for gnuchess, to an output stream.
• Threads reading stdout from gnuchess or pchess must detect moves as in assignment 2.
• They invoke a callback function that passes the move back to the main thread via a condition variable and a queue of moves. The main thread blocks on this condition variable. There is no select() or poll() loop.
• Move injection from the main thread to the stdin on a child game (gnuchess or pchess) must use a mutex to protect writing into the child’s stdin stream, only if there are multiple writers (e.g., xboard to gnuchess).
• Callbacks signal end-of-file and errors in the child data connections.• The main thread must handle signals as in assignment 2. The child threads
must mask out all signals.