data
parent
80e7e15895
commit
8330023089
|
|
@ -0,0 +1,292 @@
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
#ifndef _STHREAD_H_
|
||||||
|
#define _STHREAD_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"{
|
||||||
|
#endif
|
||||||
|
#ifndef _POSIX_PTHREAD_SEMANTICS
|
||||||
|
#define _POSIX_PTHREAD_SEMANTICS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
Note: this library requires you to link with the posix threads
|
||||||
|
library (-lpthread) and the real time library (-lrt) {for
|
||||||
|
nanosleep}.
|
||||||
|
|
||||||
|
c++ -D_POSIX_PTHREAD_SEMANTICS main.cc sthread.c -lpthread -lrt
|
||||||
|
or
|
||||||
|
g++ -Wall -D_POSIX_PTHREAD_SEMANTICS main.cc sthread.c -lpthread -lrt
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
typedef pthread_mutex_t smutex_t;
|
||||||
|
typedef pthread_cond_t scond_t;
|
||||||
|
typedef pthread_t sthread_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* API for managing threads
|
||||||
|
*
|
||||||
|
* First the simplified version where the called
|
||||||
|
* function takes an int as an argument and returns
|
||||||
|
* an int when it's done.
|
||||||
|
*
|
||||||
|
* Then the more flexible version where the argument
|
||||||
|
* and return are arbitrary pointers.
|
||||||
|
*/
|
||||||
|
void sthread_create(sthread_t *thrd,
|
||||||
|
void (*start_routine)(int),
|
||||||
|
int arg_to_start_routine);
|
||||||
|
void sthread_yield();
|
||||||
|
long sthread_join(sthread_t thrd);
|
||||||
|
void sthread_exit(int ret);
|
||||||
|
|
||||||
|
void sthread_create_p(sthread_t *thrd,
|
||||||
|
void *(*start_routine)(void*),
|
||||||
|
void *arg_to_start_routine);
|
||||||
|
void *sthread_join_p(sthread_t thrd);
|
||||||
|
void sthread_exit_p(void *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);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* API for mutex locks
|
||||||
|
*/
|
||||||
|
void smutex_init(smutex_t *mutex);
|
||||||
|
void smutex_destroy(smutex_t *mutex);
|
||||||
|
void smutex_lock(smutex_t *mutex);
|
||||||
|
void smutex_unlock(smutex_t *mutex);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* API for condition variables
|
||||||
|
*/
|
||||||
|
void scond_init(scond_t *cond);
|
||||||
|
void scond_destroy(scond_t *cond);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Condition variables are always associated with state
|
||||||
|
* variables that you access before signalling, broadcasting,
|
||||||
|
* or waiting. To access the state variable, you must hold
|
||||||
|
* the associated mutex. To help enforce this, you
|
||||||
|
* are required to hold the mutex and pass it in as an
|
||||||
|
* argument to these functions.
|
||||||
|
*/
|
||||||
|
void scond_signal(scond_t *cond, smutex_t *mutex);
|
||||||
|
void scond_broadcast(scond_t *cond, smutex_t *mutex);
|
||||||
|
void scond_wait(scond_t *cond, smutex_t *mutex);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern C */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
Loading…
Reference in New Issue