293 lines
6.9 KiB
C
293 lines
6.9 KiB
C
#ifndef _POSIX_PTHREAD_SEMANTICS
|
|
#define _POSIX_PTHREAD_SEMANTICS
|
|
#endif
|
|
#include <assert.h>
|
|
#include <pthread.h>
|
|
#include <errno.h>
|
|
#include <sched.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <time.h>
|
|
#include "sthread.h"
|
|
|
|
/*
|
|
* sthread_create()
|
|
*
|
|
* Create a new thread, that will run a specified routine
|
|
* as its initial function. That function gets a specified
|
|
* int as an argument.
|
|
*
|
|
* thread -- data structure to represent the thread
|
|
* start_routine -- pointer to the function the thread
|
|
* should run
|
|
* argToStartRoutine -- argument to pass to the start_routine
|
|
*/
|
|
void sthread_create(sthread_t *thrd,
|
|
void (*start_routine)(int),
|
|
int arg_to_start_routine)
|
|
{
|
|
//
|
|
// When a detached thread returns from
|
|
// its entry function, the thread will be destroyed. If we
|
|
// don't detach it, then the memory associated with the thread
|
|
// won't be cleaned up until somebody "joins" with the thread
|
|
// by calling pthread_wait().
|
|
//
|
|
pthread_attr_t attr;
|
|
pthread_attr_init(&attr);
|
|
// pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
|
|
|
|
|
// We are using pthreads which expects to pass a pointer
|
|
// as the argument to the start routine. To make some
|
|
// examples in the book simpler, we pass an int instead.
|
|
// For safety, we need to make sure that an int fits in this
|
|
// pointer argument.
|
|
assert(sizeof(int) <= sizeof(void *));
|
|
|
|
|
|
if(pthread_create(thrd,
|
|
&attr,
|
|
(void *(*)(void *))start_routine,
|
|
(void *)(intptr_t)arg_to_start_routine)){
|
|
perror("pthread_create failed");
|
|
exit(-1);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* sthread_create_p()
|
|
*
|
|
* Create a new thread, that will run a specified routine
|
|
* as its initial function. That function gets a specified
|
|
* pointer (to anything) as an argument.
|
|
*
|
|
* thread -- data structure to represent the thread
|
|
* start_routine -- pointer to the function the thread
|
|
* should run
|
|
* argToStartRoutine -- argument to pass to the start_routine
|
|
* void* means that it can point to anything.
|
|
* start_routine() must know what type
|
|
* it really is and cast it appropriately.
|
|
*/
|
|
void sthread_create_p(sthread_t *thread,
|
|
void *(*start_routine)(void*),
|
|
void *argToStartRoutine)
|
|
{
|
|
//
|
|
// When a detached thread returns from
|
|
// its entry function, the thread will be destroyed. If we
|
|
// don't detach it, then the memory associated with the thread
|
|
// won't be cleaned up until somebody "joins" with the thread
|
|
// by calling pthread_join().
|
|
//
|
|
pthread_attr_t attr;
|
|
pthread_attr_init(&attr);
|
|
// pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
|
|
|
if(pthread_create(thread, &attr, start_routine, argToStartRoutine)){
|
|
perror("pthread_create failed");
|
|
exit(-1);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* sthread_yield()
|
|
*
|
|
* Volunteer to give up the CPU so that some other thread can run
|
|
* At some future point, the scheduler will reschedule the calling
|
|
* thread and it will return as if it had just called a (slow)
|
|
* null procedure call.
|
|
*/
|
|
void sthread_yield()
|
|
{
|
|
int err = sched_yield();
|
|
assert(err == 0);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* sthread_join()
|
|
*
|
|
* Wait for the specified thread to call sthread_exit() (if
|
|
* it hasn't already.)
|
|
*
|
|
* The value passed to sthread_exit() by the terminating thread
|
|
* is returned so that the joining
|
|
* thread can receive the "return value" of the terminating
|
|
* thread.
|
|
*
|
|
* We use casting to convert the "pointer to a pointer to
|
|
* void" into a "pointer to int", and we assume that
|
|
* an int fits into a pointer to void. See sthread_exit().
|
|
*/
|
|
long sthread_join(sthread_t thrd)
|
|
{
|
|
long ret;
|
|
void *retv;
|
|
assert(sizeof(sthread_t) == sizeof(pthread_t));
|
|
int err = pthread_join((pthread_t)thrd, &retv);
|
|
assert(err == 0);
|
|
//assert(sizeof(int) >= sizeof(void *));
|
|
ret = (long)retv;
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* sthread_join_p()
|
|
*
|
|
* Wait for the specified thread to call sthread_exit_p() (if
|
|
* it hasn't already.)
|
|
*
|
|
* The value passed to sthread_exit() by the terminating thread
|
|
* is returned so that the joining
|
|
* thread can receive the "return value" of the terminating
|
|
* thread.
|
|
*
|
|
*/
|
|
void *sthread_join_p(sthread_t thrd)
|
|
{
|
|
void *ret;
|
|
int err = pthread_join((pthread_t)thrd, &ret);
|
|
assert(err == 0);
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
void sthread_exit(int ret)
|
|
{
|
|
/*
|
|
* The underlying pthread_exit returns a pointer
|
|
* so that the thread can hand back a pointer to
|
|
* an arbitrary structure. For simplicity, we're
|
|
* only going to allow thread to pass back an int.
|
|
* To avoid allocating new memory (and causing a
|
|
* leak), we assume that we can fit an int
|
|
* into a pointer and cast it.
|
|
*/
|
|
assert(sizeof(int) <= sizeof(void *));
|
|
pthread_exit((void *)(intptr_t)ret);
|
|
}
|
|
|
|
void sthread_exit_p(void *ret)
|
|
{
|
|
pthread_exit(ret);
|
|
}
|
|
|
|
|
|
/*
|
|
* WARNING:
|
|
* Do not use sleep for synchronizing threads that
|
|
* should be waiting for events (using condition variables)!
|
|
* Sleep should only be used to wait for a specified amount
|
|
* of time! (If you find yourself looping on a predicate
|
|
* and calling sleep in the loop, you probably are using
|
|
* it incorrectly! We will deduct points from your grade
|
|
* if you do this!
|
|
*/
|
|
void sthread_sleep(unsigned int seconds, unsigned int nanoseconds)
|
|
{
|
|
struct timespec rqt;
|
|
assert(nanoseconds < 1000000000);
|
|
rqt.tv_sec = seconds;
|
|
rqt.tv_nsec = nanoseconds;
|
|
if(nanosleep(&rqt, NULL) != 0){
|
|
perror("sleep failed. Woke up early");
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
|
|
void smutex_init(smutex_t *mutex)
|
|
{
|
|
if(pthread_mutex_init(mutex, NULL)){
|
|
perror("pthread_mutex_init failed");
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
void smutex_destroy(smutex_t *mutex)
|
|
{
|
|
if(pthread_mutex_destroy(mutex)){
|
|
perror("pthread_mutex_destroy failed");
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
void smutex_lock(smutex_t *mutex)
|
|
{
|
|
if(pthread_mutex_lock(mutex)){
|
|
perror("pthread_mutex_lock failed");
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
void smutex_unlock(smutex_t *mutex)
|
|
{
|
|
if(pthread_mutex_unlock(mutex)){
|
|
perror("pthread_mutex_unlock failed");
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
|
|
void scond_init(scond_t *cond)
|
|
{
|
|
if(pthread_cond_init(cond, NULL)){
|
|
perror("pthread_cond_init failed");
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
void scond_destroy(scond_t *cond)
|
|
{
|
|
if(pthread_cond_destroy(cond)){
|
|
perror("pthread_cond_destroy failed");
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
void scond_signal(scond_t *cond, smutex_t *muted /* NOTUSED */)
|
|
{
|
|
//
|
|
// assert(mutex is held by this thread);
|
|
//
|
|
|
|
if(pthread_cond_signal(cond)){
|
|
perror("pthread_cond_signal failed");
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
void scond_broadcast(scond_t *cond, smutex_t *mutex /* NOTUSED */)
|
|
{
|
|
//
|
|
// assert(mutex is held by this thread);
|
|
//
|
|
if(pthread_cond_broadcast(cond)){
|
|
perror("pthread_cond_broadcast failed");
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
void scond_wait(scond_t *cond, smutex_t *mutex)
|
|
{
|
|
//
|
|
// assert(mutex is held by this thread);
|
|
//
|
|
|
|
if(pthread_cond_wait(cond, mutex)){
|
|
perror("pthread_cond_wait failed");
|
|
exit(-1);
|
|
}
|
|
}
|
|
|