Concurrency - A monitor that implements semaphores - c

I need help constructing a monitor that implements a semaphore, and simple C example will do.
This is to demonstrate that a monitor can be used any place a semaphore can be used.

If you say mutex/condvars are allowed, then check this:
#include <pthread.h>
typedef struct
{
unsigned int count;
pthread_mutex_t lock;
pthread_cond_t cond;
} semaph_t;
int
semaph_init (semaph_t *s, unsigned int n)
{
s->count = n;
pthread_mutex_init (&s->lock, 0);
pthread_cond_init (&s->cond, 0);
return 0;
}
int
semaph_post (semaph_t *s)
{
pthread_mutex_lock (&s->lock); // enter monitor
if (s->count == 0)
pthread_cond_signal (&s->cond); // signal condition
++s->count;
pthread_mutex_unlock (&s->lock); // exit monitor
return 0;
}
int
semaph_wait (semaph_t *s)
{
pthread_mutex_lock (&s->lock); // enter monitor
while (s->count == 0)
pthread_cond_wait (&s->cond, &s->lock); // wait for condition
--s->count;
pthread_mutex_unlock (&s->lock); // exit monitor
return 0;
}

This is the main answer on the Wikipedia article regarding monitors.
monitor class Semaphore
{
private int s := 0
invariant s >= 0
private Condition sIsPositive /* associated with s > 0 */
public method P()
{
if s = 0 then wait sIsPositive
assert s > 0
s := s - 1
}
public method V()
{
s := s + 1
assert s > 0
signal sIsPositive
}
}

Related

Multi threading Raytracer

I am making a raytracer, im trying to use pthread to divide the rendering. i noticed that isnt helping with the speed because the function pthread_join is to slow, if i use a loop to make the 'await' is way faster and works almost every time fine. But i cant use that because the time of rendering changes with the scene.
Is there a way to check if a thread is finished, on a more efficient way. This is the code.
`
int threats(t_file *c) //this function creates the threads
{
int i;
int err;
pthread_t th[THREADS];
i = 0;
printf("1\n");
c->thread = -1;
mlx_clear_window(c->mlx_ptr, c->win_ptr);
while (i < THREADS)
{
err = pthread_create(&th[i], 0, (void *)paint_scene, (void *)c);
if (err)
return parse_error("Thread Error: CAN NOT CREATE THREAD");
i++;
}
// while (i-- >= 0)
// pthread_join(th[i], 0);
//my await function xd
while (i < 200000000)
i++;
mlx_put_image_to_window(c->mlx_ptr, c->win_ptr, c->img.mlx_img, 0, 0);
c->thread = 0;
return 1;
}
void paint_scene(void *a)
{
int y;
int x;
t_ray ray;
int color;
t_file *c;
c = (t_file *)a;
color = 0;
c->thread++;
y = (c->thread * (c->win_heigth / THREADS));
printf("y:%d,hilo%d\n", y, c->thread);
while (y < (c->thread + 1) * (c->win_heigth / THREADS))
{
x = 0;
while (x < c->win_width)
{
ray = generate_ray(x, y, *c);
color = get_intersections(&ray, c);
if (c->ligth)
color = shading(&ray, color, c);
my_mlx_pixel_put(&c->img, x, y, color);
x++;
}
//ft_printf("\rLoading%d: %d%%", c->thread, y / (c->win_heigth / 100));
y++;
}
pthread_exit(0);
}
`
You have a concurrency problem here in your thread function:
c->thread++;
y = (c->thread * (c->win_heigth / THREADS));
printf("y:%d,hilo%d\n", y, c->thread);
while (y < (c->thread + 1) * (c->win_heigth / THREADS))
{
....
}
c->thread is shared between all threads, and based on likely thread timings and current face of the moon, I can make an educated guess and say that the first thread is calculating the whole image. When starting up, the first thread might see c->thread == -1, but later (if thread startup is faster than the while loop) other thread increase the value until the first thread sees c->thread == THREADS-1
To fix this, each call to create_thread must pass a pointer to a unique parameter object that holds that threads id. So remove the thread member from t_file. It probably serves no purpose there. And create a type of struct that holds the parameters to the thread function:
struct thread_param
{
unsigned int thread;
file_t *c;
}
You use it like this when starting threads:
struct thread_param params[THREADS];
while (i < THREADS)
{
params[i].thread = i;
params[i].c = c;
err = pthread_create(&th[i], 0, (void *)paint_scene, (void *)&(params[i]));
if (err)
return parse_error("Thread Error: CAN NOT CREATE THREAD");
i++;
}
And you access the data in your thread function:
void paint_scene(void *a)
{
struct thread_param *param = (struct thread_param *)a;
unsigned int thread = param->thread;
t_file *c = param->c;
/*
in the rest of the code you remove `c->thread++`
and replace `c->thread` with `thread`
*/
....
}
If you have atomic data types (C11, #ifndef __STDC_NO_ATOMICS__) then implement a global counter and wait until it hits zero (if decreasing) or the amount of threads (if increasing).
e.g.
#include <stdatomic.h>
atomic_int num_jobs;
void* thread_func(void*)
{
//the work to do in the thread function
//before exit decrease counter
--num_jobs;
pthread_exit(0);
}
int main()
{
num_jobs = THREADS; // same as your number of threads
create_threads(THREADS); // number of threads = THREADS
while (num_jobs) { // loop while threads running
//sleep for a while
}
join_threads(); // join threads for cleanup
return 0;
}
Otherwise classic lock mechanics,
e.g.
#include <pthread.h>
pthread_spinlock_t lock;
int num_jobs;
// called by main
int numJobs()
{
pthread_spin_lock(&lock);
int res = num_jobs;
pthread_spin_unlock(&lock);
return res;
}
// called by thread_func
void decNumJobs()
{
pthread_spin_lock(&lock);
--num_jobs;
pthread_spin_unlock(&lock);
}
int main()
{
pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE);
// the other stuff as before
pthread_spin_destroy(&lock);
return 0;
}
Another alternative would be with pthread_cond_wait and pthread_cond_signal (mainly to avoid the sleep in the while loop, continue after receiving the signal and not based on a fixed amount of time).
e.g.
#include <pthread.h>
int num_jobs;
pthread_cond_t cond;
pthread_mutex_t lock;
void decNumJobs()
{
pthread_mutex_lock(&lock);
if (--num_jobs == 0)
pthread_cond_signal(&cond);
pthread_mutex_unlock(&lock);
}
void* thread_func(void*)
{
//the work to do in the thread function
//before exit decrease counter
decNumJobs();
pthread_exit(0);
}
int main()
{
num_jobs = THREADS;
pthread_cond_init(&cond, NULL);
pthread_mutex_init(&lock, NULL);
pthread_mutex_lock(&lock);
create_threads(THREADS);
pthread_cond_wait(&cond, &lock);
pthread_mutex_unlock(&lock);
join_threads();
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&lock);
return 0;
}
Note: For the sake of simplicity, there is no error checking nor handling. Reading the documentation of the pthread_* functions (return values, interrupted wait, etc) is strongly advised.

Multithreaded Fibonacci Pair Program

I'm trying to write a program which creates two threads: a "front-end" and "back-end" thread. I want to create a "back-end" thread to iterate and compute pairs of terms from the fibonacci sequence and put them in an array, and a "front-end" thread that will print out the pairs of the array at each iteration.
"Front-End" Thread - For displaying result of "Back-End" thread operations in each iterations
"Back-End" Thread - For calculating and setting an array
ie. [5, 8], and after an iteration it will contain [13, 21]
I'm struggling to implement the Fibonacci sequence part in a thread and I've made the following progress:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>
int fib;
void *front_end(void *ptr);
void *back_end(void *ptr);
int main() {
pthread_t thread1, thread2;
int arr[2] = {5,8};
const int *ptrtoarr;
ptrtoarr=arr;
int create1, create2;
int *s=(int *)(ptrtoarr);
printf("%d \n", *s);
ptrtoarr++;
s = (int *)(ptrtoarr);
printf("%d \n", *s);
ptrtoarr--;
create1 = pthread_create(&thread1, NULL, back_end, &arr);
if(create1) {
fprintf(stderr,"Error - pthread_create() return code: %d\n",create1);
exit(EXIT_FAILURE);
}
pthread_join(thread1, NULL);
//pthread_join(thread2, NULL);
}
// front-end thread to be callback for each back-end iteration
void *front_end(void *ptr) {
int *sum = ptr;
int i, upper = atoi(ptr);
if (upper > 0) {
for (i=0; i<upper; i++){
//Print the fib pairs
}
}
pthread_exit(0);
}
void *back_end(void *ptr) {
int i, upper = atoi(ptr);
fib=1;
if(upper > 0) {
int pre1 = 0;
int current;
//calc fib numbers.....
if(fib == 1){
printf("")
}
}
}
Can someone guide me through how I might approach this?
Your skeleton needs work.
Assuming the following:
unsigned n = ...; // How many to generate.
unsigned n_ready = 2; // How many are ready to print.
unsigned *fibs = malloc(sizeof(unsigned)*n);
fibs[0] = 0;
fibs[1] = 1;
At the core of your back end worker, you will have
for (unsigned i=2; i<n; ++i) {
fibs[i] = fibs[i-2] + fibs[i-1];
n_ready = i+1;
}
At the core of your frontend worker, you will have
for (unsigned i=0; i<n; ++i) {
while (i >= n_ready)
/* Nothing */;
printf("%u\n", fibs[i]);
}
Problem #1
You get into problems if a thread tries to read a variable when another is writing to it. Two or more threads reading the same variable at the same time is ok.
The variables used by both threads are n, the elements of fib[] and n_ready.
n:Not changed by either thread, so we don't need to control access to it.
fib[i] for i >= n_ready:Only accessed by the back end worker, so we don't need to control access to these.
fib[i] for i < n_ready:Only accessed by the frontend worker, so we don't need to control access to these.
n_ready:The back end worker could set n_ready at any time, and the frontend work could try to read n_ready at any time, so we do need to control access to n_ready.
Mutex are usually used to ensure that only one thread is accessing a resource (e.g. a variable, group of variables, file handle, etc) at a time.
Our back end worker becomes
for (unsigned i=2; i<n; ++i) {
// The mutex only protects n_ready
// --nothing else is going to touch fib[i-2] or fib[i-1] or fib[i]--
// so we don't need to obtain a lock yet.
fibs[i] = fibs[i-2] + fibs[i-1];
// We need to access n_ready.
pthread_mutex_lock(&mutex);
n_ready = i+1;
pthread_mutex_unlock(&mutex);
}
Our frontend worker becomes
for (unsigned i=0; i<n; ++i) {
// We need to access n_ready.
pthread_mutex_lock(&mutex);
while (i >= n_ready) {
// Allow other thread to gain the lock.
pthread_mutex_unlock(&mutex);
// We need to access n_ready.
pthread_mutex_lock(&mutex);
}
// The mutex only protects n_ready
// --nothing is going to change fib[i]--
// so we can release it now rather than later.
pthread_mutex_unlock(&mutex);
printf("%u\n", fibs[i]);
}
Problem #2
You have a busy loop. In general, this is bad because it means your thread is using 100% doing nothing by waiting. (In this particular case, since i >= n_ready is probably already true, this would actually be a good strategy. But let's ignore that.) A thread can sleep until signaled by another thread using condition vars.
Our back end worker becomes
for (unsigned i=2; i<n; ++i) {
// The mutex only protects n_ready
// --nothing else is going to touch fib[i-2] or fib[i-1] or fib[i]--
// so we don't need to obtain a lock yet.
fibs[i] = fibs[i-2] + fibs[i-1];
// We need to access n_ready.
pthread_mutex_lock(&mutex);
n_ready = i+1;
// Wake up the other thread if it's blocked.
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
Our frontend worker becomes
for (unsigned i=0; i<n; ++i) {
// We need to access n_ready.
pthread_mutex_lock(&mutex);
while (i >= n_ready)
pthread_cond_wait(&cond, &mutex);
// The mutex only protects n_ready
// --nothing is going to change fib[i]--
// so we can release it now rather than later.
pthread_mutex_unlock(&mutex);
printf("%u\n", fibs[i]);
}
Always call pthread_cond_wait on a locked mutex. It will unlock the mutex when it's called, and it will lock it before returning. This allows the other thread to obtain the mutex in order to change n_ready.
Complete code:
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define UNUSED(x) (void)(x)
// To control access to n_ready.
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
static unsigned n_ready = 0; // How many are ready to print.
static unsigned n; // How many to generate.
static unsigned *fibs = NULL;
static void *back_worker(void *unused) {
UNUSED(unused);
fibs[0] = 0;
fibs[1] = 1;
// We need to access n_ready.
pthread_mutex_lock(&mutex);
n_ready = 2;
// Wake up the other thread if it's blocked.
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
for (unsigned i=2; i<n; ++i) {
// The mutex only protects n_ready
// --nothing is going to touch fib[i]--
// so we don't need to obtain a lock yet.
fibs[i] = fibs[i-2] + fibs[i-1];
// We need to access n_ready.
pthread_mutex_lock(&mutex);
n_ready = i+1;
// Wake up the other thread if it's blocked.
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
return NULL;
}
static void *front_worker(void *unused) {
UNUSED(unused);
for (unsigned i=0; i<n; ++i) {
// We need to access n_ready.
pthread_mutex_lock(&mutex);
while (i >= n_ready)
pthread_cond_wait(&cond, &mutex);
// The mutex only protects n_ready
// --nothing is going to change fib[i]--
// so we can release it now rather than later.
pthread_mutex_unlock(&mutex);
printf("%u\n", fibs[i]);
}
return NULL;
}
int main(void) {
n = 20; // How many to generate.
fibs = malloc(sizeof(unsigned) * n);
pthread_t back_thread;
if (errno = pthread_create(&back_thread, NULL, back_worker, NULL)) {
perror(NULL);
exit(1);
}
pthread_t front_thread;
if (errno = pthread_create(&front_thread, NULL, front_worker, NULL)) {
perror(NULL);
exit(1);
}
pthread_join(back_thread, NULL);
pthread_join(front_thread, NULL);
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mutex);
free(fibs);
return 0;
}
Output:
$ gcc -Wall -Wextra -pedantic a.c -o a -lpthread && a
0
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
Suggestion for an exercise to apply the above
Create a pool of workers that print out the numbers placed into a queue. The output doesn't need to be in order.
The worker function is already written for you. You may not change the main or worker functions. I've even created the queue for you. You simply have to make it thread safe by modifying Queue_enqueue, Queue_dequeue and Queue_done functions. These are the only functions you may change.
#include <errno.h>
#include <inttypes.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define NUM_WORKERS 4
#define QUEUE_SIZE 10
#define NUM_ITEMS 40
typedef struct {
pthread_mutex_t mutex;
pthread_cond_t cond;
int done;
int empty;
int full;
size_t max;
size_t next_insert;
size_t next_read;
unsigned *buf;
} Queue;
static void Queue_init(Queue* q, size_t max) {
pthread_mutex_init(&(q->mutex), NULL);
pthread_cond_init(&(q->cond), NULL);
q->done = 0;
q->empty = 1;
q->full = 0;
q->max = max;
q->next_insert = 0;
q->next_read = 0;
q->buf = malloc(sizeof(unsigned)*max);
}
static void Queue_destroy(Queue *q) {
free(q->buf);
pthread_cond_destroy(&(q->cond));
pthread_mutex_destroy(&(q->mutex));
}
static void Queue_done(Queue *q) {
q->done = 1;
}
// Returns the oldest item from the queue (via a parameter) and returns 1.
// If the queue is empty and done, returns 0.
// If the queue is empty and not done, waits until that changes.
static int Queue_dequeue(Queue *q, unsigned *i) {
while (q->empty && !q->done) {
}
if (q->empty) {
// We are completely done.
return 0;
} else {
*i = q->buf[ q->next_read ];
q->next_read = ( q->next_read + 1 ) % q->max;
q->empty = q->next_read == q->next_insert;
q->full = 0;
return 1;
}
}
// Adds the argument to the queue.
// If the queue is full, waits until that changes.
static void Queue_enqueue(Queue *q, unsigned i) {
while (q->full && !q->done) {
}
if (q->done) {
fprintf(stderr, "Error: Attempted to add item to \"done\" queue.\n");
return;
}
q->buf[q->next_insert] = i;
q->next_insert = ( q->next_insert + 1 ) % q->max;
q->empty = 0;
q->full = q->next_insert == q->next_read;
}
static int msleep(long msec) {
struct timespec ts;
int res;
if (msec < 0) {
errno = EINVAL;
return -1;
}
ts.tv_sec = msec / 1000;
ts.tv_nsec = (msec % 1000) * 1000000;
do {
res = nanosleep(&ts, &ts);
} while (res && errno == EINTR);
return res;
}
// Protects access to stdout.
static pthread_mutex_t stdout_mutex;
static Queue q;
static void *worker(void *worker_id_) {
uintptr_t worker_id = (uintptr_t)worker_id_;
unsigned int seed = worker_id; // Whatever.
unsigned i;
while (Queue_dequeue(&q, &i)) {
pthread_mutex_lock(&stdout_mutex);
printf("[%" PRIuPTR "] Dequeued %u\n", worker_id, i);
pthread_mutex_unlock(&stdout_mutex);
// msleep( rand_r(&seed) % 1000 + 1000 ); // Simulate a 1 to 2s load.
pthread_mutex_lock(&stdout_mutex);
printf("[%" PRIuPTR "] Finished processing %u\n", worker_id, i);
pthread_mutex_unlock(&stdout_mutex);
}
return NULL;
}
int main(void) {
Queue_init(&q, QUEUE_SIZE);
pthread_t workers[NUM_WORKERS];
for (uintptr_t i=0; i<NUM_WORKERS; ++i) {
if (errno = pthread_create(&(workers[i]), NULL, worker, (void*)i)) {
perror(NULL);
exit(1);
}
}
for (unsigned i=0; i<NUM_ITEMS; ++i) {
pthread_mutex_lock(&stdout_mutex);
printf("[x] Enqueuing %u...\n", i);
pthread_mutex_unlock(&stdout_mutex);
Queue_enqueue(&q, i);
pthread_mutex_lock(&stdout_mutex);
printf("[x] Enqueued %u.\n", i);
pthread_mutex_unlock(&stdout_mutex);
}
Queue_done(&q);
pthread_mutex_lock(&stdout_mutex);
printf("[x] Called done.\n");
pthread_mutex_unlock(&stdout_mutex);
for (unsigned i=0; i<NUM_WORKERS; ++i)
pthread_join(workers[i], NULL);
Queue_destroy(&q);
pthread_mutex_destroy(&stdout_mutex);
return 0;
}
If you have questions about this, feel free to post a link to the question as a comment to this answer.
Solution to suggested excercise:
static void Queue_done(Queue *q) {
pthread_mutex_lock(&(q->mutex));
q->done = 1;
pthread_cond_signal(&(q->cond));
pthread_mutex_unlock(&(q->mutex));
}
// Returns the oldest item from the queue (via a parameter) and returns 1.
// If the queue is empty and done, returns 0.
// If the queue is empty and not done, waits until that changes.
static int Queue_dequeue(Queue *q, unsigned *i) {
pthread_mutex_lock(&(q->mutex));
while (q->empty && !q->done)
pthread_cond_wait(&(q->cond), &(q->mutex));
int dequeued;
if (q->empty) {
// We are completely done.
dequeued = 0;
} else {
*i = q->buf[ q->next_read ];
q->next_read = ( q->next_read + 1 ) % q->max;
q->empty = q->next_read == q->next_insert;
q->full = 0;
dequeued = 1;
}
pthread_cond_signal(&(q->cond));
pthread_mutex_unlock(&(q->mutex));
return dequeued;
}
// Adds the argument to the queue.
// If the queue is full, waits until that changes.
static void Queue_enqueue(Queue *q, unsigned i) {
pthread_mutex_lock(&(q->mutex));
while (q->full && !q->done)
pthread_cond_wait(&(q->cond), &(q->mutex));
if (q->done) {
fprintf(stderr, "Error: Attempted to add item to \"done\" queue.\n");
} else {
q->buf[q->next_insert] = i;
q->next_insert = ( q->next_insert + 1 ) % q->max;
q->empty = 0;
q->full = q->next_insert == q->next_read;
}
pthread_cond_signal(&(q->cond));
pthread_mutex_unlock(&(q->mutex));
}

unable to reacquire mutex and pass values correctly between threads

I am trying to implement a code to practice synchronization, so might not be best design or approach but goal is as below
Main thread
Creates a payload of 100 integers and waits for any thread to be available
When it gets signal from a thread its available - it unlocks the payload for copying and proceeds to create another payload
Worker thread
on creation of it makes itself available for data processing and sends signal that its available
Tries to lock the data payload from main thread and copy it to local array
( observing bug here - not able to access data properly)
Turn off the sign of available
( unable to turn off available state to off)
Keep processing data through local copy
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#define WORKERS 2
#define ARRAY_ELEMENTS 100
#define MAX 1000
pthread_mutex_t mutex_bucket1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex_signal = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond_go = PTHREAD_COND_INITIALIZER;
pthread_cond_t cond_busy = PTHREAD_COND_INITIALIZER;
static int value = 0;
bool available = false;
void *worker_thread(void *pbucket)
{
sleep(5);
while(1)
{
unsigned int count = 0;
int local_array[ARRAY_ELEMENTS];
int *ptbucket = (int*)pbucket;
setbuf(stdout, NULL);
pthread_mutex_lock(&mutex_signal);
printf(" -------------- \n chainging state to available \n --------- ");
available = true;
printf(" -------------- \n from thread sending go signal \n --------- ");
pthread_cond_signal(&cond_go);
pthread_mutex_unlock(&mutex_signal);
pthread_mutex_lock(&mutex_bucket1);
printf(" -------------- \n data part locked in thread for copying \n --------- ");
while(count < ARRAY_ELEMENTS)
{
printf(" %d - \n", ptbucket[count]); /***incorrect values***/
local_array[count] = ptbucket[count];
count++;
}
pthread_mutex_unlock(&mutex_bucket1);
/*Never able to acquire mutex_signal and change state to not available*/ **BUG**
pthread_mutex_lock(&mutex_signal);
printf(" -------------- \n chainging state to not available \n --------- ");
available = false;
pthread_mutex_unlock(&mutex_signal);
count = 0;
while(count < ARRAY_ELEMENTS)
{
printf(" %d - \n", local_array[count]);
count++;
}
printf(" -------------- \n about to sleep for 5secs \n --------- ");
sleep(5);
}
}
int main(void)
{
pthread_t thread_id[WORKERS];
unsigned int* pbucket1 = (int*) malloc(sizeof(int) * ARRAY_ELEMENTS);
unsigned int* pbucket;
for(int i = 0; i < WORKERS - 1; i++)
{
pthread_create(&thread_id[i], NULL, worker_thread, (void *) pbucket);
}
for(int i = 0; i < MAX; i++)
{
unsigned int count = 0;
pbucket = pbucket1;
// Make the payload ready
pthread_mutex_lock(&mutex_bucket1);
printf(" -------------- creating data payload --------- \n");
while(count < ARRAY_ELEMENTS)
{
pbucket1[count] = i;
i++;
count++;
}
printf(" -------------- \n waiting for go signal \n --------- ");
while(!available)
{
pthread_cond_wait(&cond_go, &mutex_signal);
}
pthread_mutex_unlock(&mutex_bucket1);
/*I believe after we unlock variable "available" can be mutexed
again by other thread but seems thinking is flawed */
printf(" -------------- \n Main thread sleep for 3 seconds \n --------- ");
sleep(3);
}
for(int i = 0; i < WORKERS; i++)
{
pthread_join(thread_id[i], NULL);
}
return 0;
}
I think some of your idea is backwards; It shouldn't be the main context that is waiting, it should be the worker threads waiting for data ...
The job of the main thread should be to keep populating the payload and waking one thread at a time to process it.
So here's some scribbled code that is a little more sensible, I think:
/**
file: answer.c
compile: gcc -o answer answer.c -pthread
usage: answer [numThreads] [numElements]
**/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#define STATE_WAIT 1
#define STATE_READY 2
void *routine(void*);
typedef struct _shared_t {
pthread_mutex_t m;
pthread_cond_t c;
unsigned char state;
int *payload;
size_t numElements;
pthread_t *threads;
size_t numThreads;
} shared_t;
static inline void shared_init(shared_t *shared, size_t numThreads, size_t numElements) {
memset(shared, 0, sizeof(shared_t));
pthread_mutex_init(&shared->m, NULL);
pthread_cond_init(&shared->c, NULL);
shared->state = STATE_WAIT;
shared->numThreads = numThreads;
shared->numElements = numElements;
{
int it = 0;
shared->threads = (pthread_t*) calloc(shared->numThreads, sizeof(pthread_t));
while (it < shared->numThreads) {
if (pthread_create(&shared->threads[it], NULL, routine, shared) != 0) {
break;
}
it++;
}
}
}
static inline void shared_populate(shared_t *shared) {
if (pthread_mutex_lock(&shared->m) != 0) {
return;
}
shared->payload = (int*) calloc(shared->numElements, sizeof(int));
{
int it = 0,
end = shared->numElements;
while (it < end) {
shared->payload[it] = rand();
it++;
}
}
shared->state = STATE_READY;
pthread_cond_signal(&shared->c);
pthread_mutex_unlock(&shared->m);
}
static inline void shared_cleanup(shared_t *shared) {
int it = 0,
end = shared->numThreads;
while (it < end) {
pthread_join(shared->threads[it], NULL);
}
pthread_mutex_destroy(&shared->m);
pthread_cond_destroy(&shared->c);
free(shared->threads);
}
void* routine(void *arg) {
shared_t *shared = (shared_t*) arg;
int *payload;
do {
if (pthread_mutex_lock(&shared->m) != 0) {
break;
}
while (shared->state == STATE_WAIT) {
pthread_cond_wait(&shared->c, &shared->m);
}
payload = shared->payload;
shared->state = STATE_WAIT;
pthread_mutex_unlock(&shared->m);
if (payload) {
int it = 0,
end = shared->numElements;
while (it < end) {
printf("Thread #%ld got payload %p(%d)=%d\n",
pthread_self(), payload, it, payload[it]);
it++;
}
free(payload);
}
} while(1);
pthread_exit(NULL);
}
int main(int argc, char *argv[]) {
shared_t shared;
int numThreads = argc > 1 ? atoi(argv[1]) : 1;
int numElements = argc > 2 ? atoi(argv[2]) : 100;
shared_init(&shared, numThreads, numElements);
do {
shared_populate(&shared);
} while (1);
shared_cleanup(&shared);
return 0;
}
Obviously, the code above is not very tolerant of errors, and is not easy to shutdown cleanly ... it's illustration only.
Let's first look at main so that we know what the flow of the main program is going to be:
int main(int argc, char *argv[]) {
shared_t shared;
int numThreads = argc > 1 ? atoi(argv[1]) : 1;
int numElements = argc > 2 ? atoi(argv[2]) : 100;
shared_init(&shared, numThreads, numElements);
do {
shared_populate(&shared);
} while (1);
shared_cleanup(&shared);
return 0;
}
It keeps a shared_t on the stack:
typedef struct _shared_t {
pthread_mutex_t m;
pthread_cond_t c;
unsigned char state;
int *payload;
size_t numElements;
pthread_t *threads;
size_t numThreads;
} shared_t;
Mostly self explanatory, mutex, condition and state are required for synchronization.
First of all the shared_t must be initialized with mutex, condition, state and threads using the provided options:
static inline void shared_init(shared_t *shared, size_t numThreads, size_t numElements) {
memset(shared, 0, sizeof(shared_t));
pthread_mutex_init(&shared->m, NULL);
pthread_cond_init(&shared->c, NULL);
shared->state = STATE_WAIT;
shared->numThreads = numThreads;
shared->numElements = numElements;
{
int it = 0;
shared->threads = (pthread_t*) calloc(shared->numThreads, sizeof(pthread_t));
while (it < shared->numThreads) {
if (pthread_create(&shared->threads[it], NULL, routine, shared) != 0) {
break;
}
it++;
}
}
}
When the worker threads are created by this routine, they are forced into a waiting state.
The first call to shared_populate in the loop awakens the first thread after setting the payload to some random numbers:
static inline void shared_populate(shared_t *shared) {
if (pthread_mutex_lock(&shared->m) != 0) {
return;
}
shared->payload = (int*) calloc(shared->numElements, sizeof(int));
{
int it = 0,
end = shared->numElements;
while (it < end) {
shared->payload[it] = rand();
it++;
}
}
shared->state = STATE_READY;
pthread_cond_signal(&shared->c);
pthread_mutex_unlock(&shared->m);
}
Note the use of pthread_cond_signal over pthread_cond_broadcast, because we only want to wake the first thread.
void* routine(void *arg) {
shared_t *shared = (shared_t*) arg;
int *payload;
do {
if (pthread_mutex_lock(&shared->m) != 0) {
break;
}
while (shared->state == STATE_WAIT) {
pthread_cond_wait(&shared->c, &shared->m);
}
payload = shared->payload;
shared->state = STATE_WAIT;
pthread_mutex_unlock(&shared->m);
if (payload) {
int it = 0,
end = shared->numElements;
while (it < end) {
printf("Thread #%ld got payload %p(%d)=%d\n",
pthread_self(), payload, it, payload[it]);
it++;
}
free(payload);
}
} while(1);
pthread_exit(NULL);
}
So we wake up in routine at the call to pthread_cond_wait, the state has changed, so we break out of the loop, we save the pointer to the payload, reset the state to WAIT, and release the mutex.
At this point main can repopulate the payload and awaken the next thread, meanwhile the current worker thread can process, and then free the payload.
Some advice:
Always use as few mutex and condition variables as possible (KISS)
Research the atomic nature of condition variables
Always follow the basic rules regarding acquisition and release of mutex and signaling of condition variables:
If you locked it, unlock it.
Only ever wait for something: predicated wait loops are absolutely required, all the time.
If you can't reproduce what I done, then take the code and try to expand upon it; The first thing you need to do is be able to shutdown the process gracefully (enter shared_cleanup), maybe you need a variable sized payload, or some other requirement not mentioned in the original question.
Note about printf ... appending to a stream is not guaranteed to be atomic, it so happens that most of the time on *nix it is ... since we are just doing show and tell, we don't need to care about that ... ordinarily, do not rely on atomicity for any stream operations ...

Destroying pthread mutex/rwlock in signal handler

How to correctly destroy pthread mutex or rwlock in signal handler? For example - in the code below i has main thread and 3 another threads. All threads do some tasks in infinity loop on some array, using mutexes and locks. Because main thread is also doing some task, the only way to exit from program - using signal handler. But in this way i can't destroy my mutex/rwlock object, because there's no guarantee that object is unlocked. And if I'll try to unlock it, of course one of thread will lock it again. And when i'm trying to launch my program again, that print corrupted result. So how can i solve this problem? There is example of my code with rwlocks:
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <signal.h>
#include <stdlib.h>
#include "thread_data.h"
#include "exchange_types.h"
pthread_rwlock_t rwlock;
unsigned int main_time = 500000;
unsigned int shift_time = 1000000;
unsigned int mirror_time = 2000000;
unsigned int count_time = 4000000;
void signal_handler(int signo) {
// Destroying locked lock or mutex is UB
pthread_rwlock_destroy(&rwlock);
exit(1);
}
void* shift_letter_case_async( void* array ) {
while(1) {
if( pthread_rwlock_rdlock(&rwlock) < 0 )
handle_error("rdlock[shift]");
carray_t* arr = (carray_t*) array;
shift_letter_case( arr->array, arr->size );
if( pthread_rwlock_unlock(&rwlock) < 0 )
handle_error("unlock[shift]");
usleep(shift_time);
}
return NULL;
}
void* mirror_array_async( void* array ) {
while(1) {
if( pthread_rwlock_rdlock(&rwlock) < 0 )
handle_error("rdlock[mirror]");
carray_t* arr = (carray_t*) array;
mirror_array( arr->array, arr->size );
if( pthread_rwlock_unlock(&rwlock) < 0 )
handle_error("unlock[mirror]");
usleep(mirror_time);
}
return NULL;
}
void* count_async( void* array ) {
while(1) {
if( pthread_rwlock_wrlock(&rwlock) < 0 )
handle_error("wrlock[count]");
carray_t* arr = (carray_t*) array;
count_upper_letters( arr->array, arr->size );
if( pthread_rwlock_unlock(&rwlock) < 0 )
handle_error("unlock[count]");
usleep(count_time);
}
return NULL;
}
int main( int argc, char** argv ) {
/* Common data */
char letters[ 'z' - 'a' + 1 ];
size_t letter_len;
carray_t transferer;
/* pthread data */
pthread_t threads[3];
/* Initializing array */
letter_len = sizeof(letters);
for( int i = 0; i < letter_len; i++ )
letters[i] = 'a' + i;
transferer.array = letters;
transferer.size = letter_len;
/* Initializing signal handlers */
if ( signal(SIGINT, signal_handler) == SIG_ERR )
handle_error("signal[SIGINT]");
if ( signal(SIGTERM, signal_handler) == SIG_ERR )
handle_error("signal[SIGTERM]");
/* Initializing locks */
if( pthread_rwlock_init(&rwlock, NULL) < 0 )
handle_error("pthread_rwlock_init");
/* Initializing threads */
if( pthread_create( &threads[0], NULL, shift_letter_case_async, &transferer ) != 0 )
handle_error("phtread_create[shift_letter_case]");
if( pthread_create( &threads[1], NULL, mirror_array_async, &transferer ) != 0 )
handle_error("phtread_create[mirror_array]");
if( pthread_create( &threads[2], NULL, count_async, &transferer ) != 0 )
handle_error("phtread_create[count]");
while(1) {
if( pthread_rwlock_wrlock(&rwlock) < 0 )
handle_error("wrlock[main]");
print_array(letters, letter_len);
if( pthread_rwlock_unlock(&rwlock) < 0 )
handle_error("unlock[main]");
usleep(main_time);
}
return 0;
}
Instead of the very risky-sounding approach you propose, consider shutting down by arranging to signal every thread, to which each one responds by performing an orderly shutdown. Have the main thread join the others in that case so that it knows when they have finished; then it can cleanly tear down any persistent synchronization structures, temporary files, etc..
Alternatively, signal the main thread only, and have it -- in a properly synchronized way -- raise a flag that each other thread will recognize as an instruction to shut down, then proceed as above (join the worker threads, then tear down).
I think my first naive attempt at this would be to change the while(1) of the threads and main to while(running) with "running" defined by:
volatile int running = 1;
Change the signal_handler to:
void signal_handler(int signo) {
running = 0;
}
You can join the threads as normal before the return in main. I didn't run this so I may be completely wrong. Also, you may wish to have "if (running)" predicates for the usleep functions.

A variation on the seqlock algorithm

Can someone provide some examples/tips/indications of how to solve the following assignment: a resource may be used by 2 types of processes: black and white. When the resource is used by the white processes, it can not be used by the black processes and vice-versa. Implement the access to the resource avoiding starvation. In an older post I was advised to use a variation on the seqlock algorithm, but I can't figure how to adjust that algorithm for this assignment.
EDIT: this is the code I've written so far
#include <stdio.h>
#include <pthread.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
struct RW;
struct RW
{
volatile int num_reads_in_progress;
volatile int num_writes;
pthread_cond_t reader_cv;
pthread_cond_t writer_cv;
pthread_mutex_t lock;
};
char *buf;
void signal_next(struct RW *b);
extern char *xx_read(struct RW *);
extern void xx_write(struct RW *, char *);
// Precondition: b->lock must be locked before this function is called
void signal_next(struct RW *b)
{
if (b->num_writes > 0)
{
// if any writes are waiting wake one up
pthread_cond_signal(&b->writer_cv);
}
else
{
// if are no writes pending, wake up all the readers
pthread_cond_broadcast(&b->reader_cv);
}
}
void *ts_read(void *vb);
void *ts_read(void *vb)
{
struct RW *b = vb;
pthread_mutex_lock(&b->lock);
while (b->num_writes > 0)
{
// cond_wait unlocks the mutex, waits to be signaled, then re-acquires the mutex
pthread_cond_wait(&b->reader_cv, &b->lock);
}
// By there b->num_writes must be 0
b->num_reads_in_progress++;
pthread_mutex_unlock(&b->lock);
buf = xx_read(b);
pthread_mutex_lock(&b->lock);
b->num_reads_in_progress--;
signal_next(b);
pthread_mutex_unlock(&b->lock);
return 0;
}
void *ts_write(void *vb);
void *ts_write(void *vb)
{
struct RW *b = vb;
pthread_mutex_lock(&b->lock);
b->num_writes++;
if (b->num_writes > 1 || b->num_reads_in_progress > 0)
{
// cond_wait unlocks the mutex, waits to be signaled,
// then re-acquires the mutex
pthread_cond_wait(&b->writer_cv, &b->lock);
}
pthread_mutex_unlock(&b->lock);
xx_write(b, buf);
pthread_mutex_lock(&b->lock);
b->num_writes--;
signal_next(b);
pthread_mutex_unlock(&b->lock);
return 0;
}
int main(void)
{
pthread_t white[3];
pthread_t black[3];
struct RW *rw;
rw = malloc(sizeof(struct RW));
int i;
for (i = 0; i < 3; i++)
{
pthread_create(&white[i], NULL, &ts_read, &rw);
}
for (i = 0; i < 3; i++)
{
pthread_create(&black[i], NULL, ts_write, &rw);
}
for (i = 0; i < 3; i++)
{
pthread_join(white[i], NULL);
}
for (i = 0; i < 3; i++)
{
pthread_join(black[i], NULL);
}
return 0;
}
You need a Mutex that locks and unlocks. Basically you can think of a mutex as a boolean value that is either true or false(locked or unlocked if you prefer).
When black process accesses the resource, the mutex should be locked. And, on the other hand when white tries to access it, it should first check for the mutex's status. If the status of mutex is locked, then it will have to wait until the mutex is unlocked.
Pseudocode:
unsigned char mutex = 0;
//processBlack tries to access resource
if(mutex == 1)
while(mutex != 0);
mutex = 1;
//now the mutex is unlocked, do whatever you want
mutex = 0; //do not forget to unlock it.
//processWhite tries to access resource
if(mutex == 1)
while(mutex != 0);
mutex = 1;
//now the mutex is unlocked, do whatever you want
mutex = 0; //do not forget to unlock it.
If you want to use the seqlock mechanism take a look here, section 5.7.4.:
http://www.makelinux.net/ldd3/chp-5-sect-7
and here for writers example:
http://www.sao.ru/hq/sts/linux/doc/porting_to_26/22818.html

Resources