Destroying pthread mutex/rwlock in signal handler - c

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.

Related

program intermittently stuck with main reporting a different thread id as opposed to the thread itself

I am trying to figure out how multi-threading works, this is my code :
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <pthread.h>
static pthread_cond_t threadDied = PTHREAD_COND_INITIALIZER ; // cond var initialization
static pthread_mutex_t threadMutex = PTHREAD_MUTEX_INITIALIZER ; // mutex initialization
// this mutex will protect all of the below global vars
static int totThreads = 0 ; // total number of threads created
static int numLive = 0 ; // Total no. of threads still alive .. or terminated but not joined
static int numUnjoined = 0 ; // no. of threads that have not yet been joined
enum tstate { // enumeration of thread states
TS_ALIVE, // thread is alive
TS_TERMINATED, // thread terminated, not yet joined
TS_JOINED // thread terminated and joined
};
static struct { // info about each thread
pthread_t tid ; // thread ID
enum tstate state; // Thread state as per the above enum
int sleepTime ; // no. of seconds to live before terminating
} *thread ; // name of the struct .. well a pointer
static void *threadFunc (void *arg) { // default start function for each thread
int idx = *(int *)arg ; // since arg is of type void , we typecast it to * of type int and deref it
int s ; // for ret val
sleep(thread[idx].sleepTime) ; // pretending as though thread is doing some work :/
s = pthread_mutex_lock(&threadMutex);
if (s!=0) {
printf("whoops, couldn't acquire mutex\n") ;
fflush(stdout);
exit (-1) ;
}
numUnjoined ++ ;
thread[idx].state = TS_TERMINATED ;
s = pthread_mutex_unlock(&threadMutex) ;
if ( s!=0 ) {
printf("whoops, couldn't release mutex\n") ;
fflush(stdout);
exit (-2) ;
}
s = pthread_cond_signal(&threadDied) ; // signalling any listening thread to wake up !!
if (s != 0) {
printf("whoops, couldn't signal the main thread to reap\n");
fflush(stdout);
exit (-3) ;
}
printf("Thread %d has worked hard and is now terminating\n", idx);
fflush(stdout);
return NULL ;
}
int main(int argc, char *argv[]) {
int s, idx ;
if (argc < 2 || strcmp(argv[1], "--help") == 0) {
printf("Usage : %s nsecs...\n", argv[0]);
fflush(stdout);
exit(-4) ;
}
thread = calloc(argc -1, sizeof(*thread) );
if (thread == NULL) {
printf("whoops, couldn't allocate memory of size %lu\n", (argc -1) * sizeof(*thread) );
fflush(stdout);
exit(-5);
}
// Let's create all the threads now !!
for (idx =0 ; idx < argc -1 ; idx++ ) {
thread[idx].sleepTime = atoi(argv[idx + 1 ]) ; // thread sleeps for the duration entered in the cmd line
thread[idx].state = TS_ALIVE ;
s = pthread_create(&thread[idx].tid, NULL, threadFunc, &idx);
printf("Main created thread %d with tid : %lu \n", ( * (int *)&idx ), (unsigned long)thread[idx].tid);
fflush(stdout);
if (s != 0 ){
printf("whoops couldn't create thread %lu\n",(unsigned long) (&thread[idx].tid) );
fflush(stdout);
exit(-6) ;
}
//sleep(1); // << -- if I don't add this sleep, then it just deadlocks
}
totThreads = argc -1 ;
numLive = totThreads ;
// Join terminated threads
while (numLive > 0 ) {
s = pthread_mutex_lock(&threadMutex) ;
if (s!=0){
printf("whoops, couldn't lock mutex for joining\n") ;
fflush(stdout);
exit(-7) ;
}
while (numUnjoined == 0) {
s = pthread_cond_wait(&threadDied, &threadMutex) ;
if (s!=0) {
printf("whoops, couldn't wait for thread join\n") ;
fflush(stdout);
exit(-8) ;
}
}
for (idx = 0 ; idx < totThreads ; idx++ ) {
if (thread[idx].state == TS_TERMINATED) {
s = pthread_join(thread[idx].tid, NULL) ;
if (s!=0) {
printf("Failed thread join\n");
fflush(stdout);
exit(-9) ;
}
thread[idx].state = TS_JOINED ;
numLive-- ;
numUnjoined-- ;
printf("Reaped thread %d (numLive=%d)\n", idx, numLive);
fflush(stdout);
}
}
s = pthread_mutex_unlock(&threadMutex) ;
if (s!=0){
printf("whopps, couldn't unlock mutex after joining\n");
fflush(stdout);
exit(-10) ;
}
}
exit(EXIT_SUCCESS);
}
For a thread count of 1, this code works sometimes, at other times it just hangs :(
WORKING :
#./thread_multijoin 1
Main created thread 0 with tid : 139835063281408
Thread 0 has worked hard and is now terminating
Reaped thread 0 (numLive=0)
HANG :
#./thread_multijoin 1
Main created thread 0 with tid : 140301613573888
Thread 1 has worked hard and is now terminating
^C
NOTICE here that Main says "Thread 0 was created" ; whereas the thread itself says "Thread 1" ... why is there a mismatch ??
It definitely gets stuck when I have multiple threads :
#./thread_multijoin 1 2 2 1
Main created thread 0 with tid : 140259455936256
Main created thread 1 with tid : 140259447543552
Main created thread 2 with tid : 140259439150848
Main created thread 3 with tid : 140259430758144
Thread 4 has worked hard and is now terminating
Thread 0 has worked hard and is now terminating
Reaped thread 0 (numLive=3)
Reaped thread 3 (numLive=2)
Thread 3 has worked hard and is now terminating
Reaped thread 2 (numLive=1)
Thread 2 has worked hard and is now terminating
^C
the only thing I am understanding from this is that the thread ID's reported by main and the thread itself are different, so I am guessing due to parallel scheduling there is something going on with the thread counter ... can you guys help me narrow this down please?
Thanks in advance.
========================================
Thanks #mevets and #user3386109 for the answer :)
I tried doing what #mevets suggested : i,e
pthread_create(&thread[idx].tid, NULL, threadFunc, (void *)idx);
and
int idx = (int)arg ;
but got this error when compiling :
thread_multijoin.c: In function ‘threadFunc’:
thread_multijoin.c:32:15: error: cast from pointer to integer of different
size [-Werror=pointer-to-int-cast]
int idx = (int)arg ; // since arg is of type void , we typecast it to * of type int and deref it
thread_multijoin.c: In function ‘main’:
thread_multijoin.c:90:64: error: cast to pointer from integer of different
size [-Werror=int-to-pointer-cast]
s = pthread_create(&thread[idx].tid, NULL, threadFunc, (void *)idx );
Upon researching further, found this thread :
cast to pointer from integer of different size, pthread code
which suggested the use of intptr_t :
s = pthread_create(&thread[idx].tid, NULL, threadFunc, (void *)(intptr_t)idx );
and
int idx = (intptr_t)arg
That worked perfectly fine without errors . Thanks once again for your time, really appreciate it :)
PS : to use intptr_t , you need to use _GNU_SOURCE :
#define _GNU_SOURCE
[ the thread id ]:
You pass the address of idx into each thread, then dereference it to index the table. So each thread gets the same pointer argument.
You probably wanted to:
s = pthread_create(&thread[idx].tid, NULL, threadFunc, (void *)idx);
and
int idx = (int)arg ; // since arg is of type void , we typecast it to * of type int and deref it
ie; not deref it, just pass it in a “void *” container.

Pthreads and Mutexes for RaceCondition Control

I'm having an issue where my code is not acquiring the required mutex lock in order to manage Race Conditions. The code intentionally causes race conditions by using nanosleep to pause the threads and let them compete for the same data. My issue is that when adding the mutex lock to the code, not all of my threads are executing and thus giving a wrong value.
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <sys/time.h>
#define NUM_THREADS 10
//Global Value
int SHARED_VALUE = 0 ;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
void* add10(void*);
struct timespec ts = {0, 10};
int main() {
//Required for thread creation
pthread_t thread_id; //Thread Identifier
pthread_attr_t attributes; //Default Attributes
pthread_attr_init(&attributes); //Attributes Initialized to Default Values
//Initialize Mutex
if (pthread_mutex_init(&lock, NULL) != 0){
fprintf(stderr,"Error Creating Mutex\n");
return -100;
}
//Spawn 10 Threads
for(int i = 0; i < NUM_THREADS;i++){
thread_id = i+1;
if(pthread_create(&thread_id,&attributes,add10,thread_id) != 0) {
fprintf(stderr,"Error Creating Thread: %d", thread_id);
return -100;
}
}
for(int i = 0; i < NUM_THREADS; i++){
pthread_join (thread_id,NULL);
}
//pthread_mutex_destroy(&lock);
printf("Shared_Value: %d", SHARED_VALUE);
}
void* add10(void* arg) {
int thread_id = (int *) arg;
if ((pthread_mutex_lock(&lock)) == 0) {
fprintf(stderr, "Lock Acquired:%d\n", (int *) arg);
//Access Shared Value
int local_value = SHARED_VALUE;
fprintf(stderr, "\tInitial Local Value: %d\n", local_value);
local_value = local_value + 10;
fprintf(stderr, "\tPost Local Value: %d\n", local_value);
//Stall thread for race conditions
nanosleep(&ts, NULL);
SHARED_VALUE = local_value;
} else {
fprintf(stderr, "No Lock!\n");
pthread_exit;
}
if ((pthread_mutex_unlock(&lock)) < 0) {
fprintf(stderr, "Error on unlock\n");
}
}
Output Looks Like:
Lock Acquired:5
Initial Local Value: 0
Post Local Value: 10
Lock Acquired:6
Initial Local Value: 10
Post Local Value: 20
Lock Acquired:4
Initial Local Value: 20
Post Local Value: 30
Lock Acquired:7
Initial Local Value: 30
Post Local Value: 40
Lock Acquired:9
Initial Local Value: 40
Post Local Value: 50
Lock Acquired:10
Initial Local Value: 50
Post Local Value: 60
Shared_Value: 60Lock Acquired:8
Process finished with exit code 0
for(int i = 0; i < NUM_THREADS;i++){
thread_id = i+1;
if(pthread_create(&thread_id,&attributes,add10,thread_id) != 0)
You are using the same variable thread_id for all threads.You need an array here.
pthread_t thread_id[10];
for(int i = 0; i < NUM_THREADS;i++){
if(pthread_create(&thread_id[i],&attributes,add10,thread_id[i]) != 0)
Well, essentially you are waiting for only one thread to join and ignored remaining 9 threads
for(int i = 0; i < NUM_THREADS; i++){
pthread_join (thread_id,NULL);
}
because of this create statement
if(pthread_create(&thread_id,&attributes,add10,thread_id) != 0)

Trying to use semget() to get a semaphore set but I keep getting an EEXIST error, even after I make a new key

So I'm currently writing a server/client program that will take in three commands (HI, PID and GOODBYE) and process them accordingly. I seem to have run into some trouble with my use of semget() in my server.c code.
The error that I'm getting is "EEXIST", which according to the man pages, says that the key already exists (duh lol) - the thing is, I keep manually changing the key each time and it still gives me the error. Am I not understanding something here? It also seems to work fine for the client.c code. Do any of you guys have an idea as to why I'm experiencing this?
Sorry for the somewhat sloppy code, I've been smashing my head on my keyboard all night over this, I'll do my best to comment out my thought processes. Let me know if there's anything I can do to make it easier to read.
client.c:
#include "stdio.h"
#include "stdlib.h"
#include "sys/shm.h"
#include "sys/ipc.h"
#include "sys/types.h"
#include "sys/sem.h"
#include "signal.h"
#include "string.h"
#include <unistd.h>
#define MEM_KEY 99 // memory key
#define SEM_KEY 1337 // semaphore key
#define SEG_SIZE ( (size_t)100 ) // segment size
#define oops( m, x ) { perror(m); exit(x); } // for error checking
int seg_id, semset_id;
union semun{ int val; struct semid_ds* buf; ushort* array; }; // for wait and release functions
void wait_and_lock( int );
void release_lock( int );
int main()
{
char c;
key_t memKey = MEM_KEY, semKey = SEM_KEY; // not sure if this is necessary
char *memPtr;
if ((seg_id = shmget(memKey, SEG_SIZE, IPC_CREAT | 0777)) < 0) // get segment ID
oops("shmget", 1);
if ((memPtr = shmat(seg_id, NULL, 0)) == (char *) -1) // attach the memory segment
oops("shmat", 2);
semset_id = semget(semKey, 2, ( 0666 | IPC_CREAT | IPC_EXCL )); // for some reason I couldn't include this in an if-statement
// but it seems to work here in the server code
if (semset_id == -1)
oops("semset", 2.5);
wait_and_lock( semset_id ); // this function was something our teacher went over with us.
// I'm still a little confused with what it's doing.
while(1)
{
printf("enter: "); // get the commands, is there a better way of doing this?
scanf("%s", memPtr);
}
release_lock( semset_id );
while (*memPtr != '*') // not sure if this is necessary, left over from old code that I was experimenting with.
sleep(1);
shmdt( memPtr ); // detach the memory
exit(0);
}
void wait_and_lock( int semset_id )
{
union semun sem_info; // some properties
struct sembuf actions[2]; // action set, an array
actions[0].sem_num = 1; // sem[1] is n_writers
actions[0].sem_flg = SEM_UNDO; // auto cleanup
actions[0].sem_op = 0; // wait for 0
actions[1].sem_num = 0; // sem[0] is n_readers
actions[1].sem_flg = SEM_UNDO; // auto cleanup
actions[1].sem_op = 1; // incr n_readers
if ( semop( semset_id, actions, 2 ) == -1 )
oops( "semop: locking", 10 );
}
void release_lock( int semset_id )
{
union semun sem_info; // some properties
struct sembuf actions[1]; // action set
actions[0].sem_num = 0; // sem[0] is n_readers
actions[0].sem_flg = SEM_UNDO; // auto cleanup
actions[0].sem_op = -1; // decr reader country
if ( semop( semset_id, actions, 1 ) == -1 )
oops( "semop: unlocking", 10 );
}
server.c:
#include "stdio.h"
#include "stdlib.h"
#include "sys/shm.h"
#include "sys/ipc.h"
#include "sys/types.h"
#include "sys/sem.h"
#include "signal.h"
#include "string.h"
#include <unistd.h>
#define MEM_KEY 99
#define SEM_KEY 1337
#define SEG_SIZE ( (size_t)100 )
#define oops( m, x ) { perror(m); exit(x); }
union semun { int val; struct semid_ds* buf; unsigned short* array; };
int seg_id, semset_id;
void cleanup( int );
void set_sem_value( int, int, int );
void wait_and_lock( int );
void release_lock( int );
int main()
{
int id = 0;
char *memPtr;
key_t memKey = MEM_KEY, semKey = SEM_KEY;
signal( SIGINT, cleanup ); // to handle Ctrl-C
seg_id = shmget( memKey, SEG_SIZE, 0777 ); // get segment ID
if( seg_id == -1 )
oops( "shmget", 1 );
if ((memPtr = shmat(seg_id, NULL, 0)) == (char *) -1) // attach to memPtr
oops("shmat",2);
semset_id = semget( semKey, 2, ( 0666 | IPC_CREAT | IPC_EXCL ) );
// this is where there seems to be an issue? This is where the code stops.
if ( semset_id == -1 )
oops( "semget", 3 );
set_sem_value( semset_id, 0, 0 ); // set counters
set_sem_value( semset_id, 1, 0 ); // both to zero
//Now read what the client put in the memory (still not sure if this works
//because the program hasn't technically gotten that far. So this while loop is more
//of a prototype).
while(1)
{
wait_and_lock( semset_id );
printf( "\tshm_ts2 update memory\n" ); // will be removed at the end
sleep(1);
if(strcmp(memPtr, "HI")==0) // look for HI
{
printf("Greetings!\n"); // print this to the server screen
fflush(stdout);
memPtr[0] = '\0';
}
else if(strcmp(memPtr, "PID")==0) // look for PID and get server's PID
{
id = (int)getpid();
printf("Server pid: %i\n", id);
fflush(stdout);
memPtr[0] = '\0';
}
else if(strcmp(memPtr, "QUIT")==0){ // check for quit
shmctl(seg_id, IPC_RMID, NULL); // mark seg to be destroyed
shmdt(memPtr); // detach segment
printf("GOODBYE!\n");
exit(0);
}
release_lock( semset_id );
printf( "\tshm_ts2 released lock\n" ); // will be removed at the end
}
*memPtr = '*'; // still not sure if this is necessary, from old code
cleanup(0);
return 0;
}
void cleanup( int n )
{
shmctl( seg_id, IPC_RMID, NULL );
semctl( semset_id, 0, IPC_RMID, NULL );
}
void set_sem_value( int semset_id, int semnum, int val )
{
union semun initval;
initval.val = val;
if ( semctl( semset_id, semnum, SETVAL, initval ) == -1 )
oops( "semctl", 4 );
}
void wait_and_lock( int semset_id )
{
struct sembuf actions[2]; // action set, an array
actions[0].sem_num = 0; // sem[0] is n_readers
actions[0].sem_flg = SEM_UNDO; // auto cleanup
actions[0].sem_op = 0; // wait til no readers
actions[1].sem_num = 1; // sem[1] is n_writers
actions[1].sem_flg = SEM_UNDO; // auto cleanup
actions[1].sem_op = 1; // increment number of writers
if ( semop( semset_id, actions, 2 ) == -1 )
oops("semop: locking", 10 );
}
// Thing 4: build and execute a 1-element action set: decrement num_writers
void release_lock( int semset_id )
{
struct sembuf actions[1]; // action set, an array
actions[0].sem_num = 1; // sem[0] is n_writers
actions[0].sem_flg = SEM_UNDO; // auto cleanup
actions[0].sem_op = -1; // decrement number of writer count
if ( semop( semset_id, actions, 1 ) == -1 )
oops( "semop: unlocking", 10 );
}
You need to make a system V IPC key using the ftok function. Than, that key should be passed as the first parameter to the shmget system call. The same applies to semaphores. Make a System V IPC key using ftok and pass it as the first parameter to semget.

Pthread runtime errors

I'm having trouble debugging the following program I wrote. The idea is to have two seperate threads; one thread executes a 5 second countdown while the other waits for key input from the user. Whichever thread completes first should cancel the sibling thread and exit the program. However, the following code just hangs.
Any help would be appreciated, but I would be most grateful for an explanation as to the problem.
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> // For sleep()
#define NUM_THREADS 2
// The stuct to be passed as an argument to the countdown routine
typedef struct countdown_struct {
pthread_t *thread;
signed int num_secs;
} CountdownStruct;
// Struct for passing to the input wait routine
typedef struct wait_struct {
pthread_t *thread;
int *key;
} WaitStruct;
// Countdown routine; simply acts as a timer counting down
void * countdown(void *args)
{
CountdownStruct *cd_str = (CountdownStruct *)args;
signed int secs = cd_str->num_secs;
printf("Will use default setting in %d seconds...", secs);
while (secs >= 0)
{
sleep(1);
secs -= 1;
printf("Will use default setting in %d seconds...", secs);
}
// Cancel the other struct
pthread_cancel(*(cd_str->thread));
return NULL;
}
// Waits for the user to pass input through the tty
void * wait_for_input(void *args)
{
WaitStruct *wait_str = (WaitStruct *) args;
int c = 0;
do {
c = getchar();
} while (!(c == '1' || c == '2'));
*(wait_str->key) = c;
// Cancel the other thread
pthread_cancel(*(wait_str->thread));
return NULL;
}
int main(int argc, char **argv)
{
pthread_t wait_thread;
pthread_t countdown_thread;
pthread_attr_t attr;
int key=0;
long numMillis=5000;
int rc=0;
int status=0;
// Create the structs to be passe as paramaters to both routines
CountdownStruct *cd_str = (CountdownStruct *) malloc(sizeof(CountdownStruct));
if (cd_str == NULL)
{
printf("Couldn't create the countdown struct. Aborting...");
return -1;
}
cd_str->thread = &wait_thread;
cd_str->num_secs = 5;
WaitStruct *wait_str = (WaitStruct *) malloc(sizeof(WaitStruct));
if (wait_str == NULL)
{
printf("Couldn't create the iput wait struct. Aborting...");
return -1;
}
wait_str->thread = &countdown_thread;
wait_str->key = &key;
// Create the joinable attribute
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
// Create both threads
rc = pthread_create(&countdown_thread, &attr, countdown, (void *) cd_str);
if (rc) { printf("Error with the thread creation!"); exit(-1); }
rc = pthread_create(&wait_thread, &attr, wait_for_input, (void *) wait_str);
if (rc) { printf("Error with the thread creation!"); exit(-1); }
// Destroy the pthread_attribute
pthread_attr_destroy(&attr);
// now join on the threads and wait for main
pthread_join(wait_thread, NULL);
pthread_join(countdown_thread, NULL);
// Call pthread_exit
pthread_exit(NULL);
// Free the function structs
free(cd_str);
free(wait_str);
}
Getchar is not required to be a cancellation point. Select and pselect are. Even if you want to continue to use a countdown thread you could still provide a cancellation point in the opposing thread by use of select.
I had reasonable behavior with the following modified wait_for_input()
// Waits for the user to pass input through the tty
void * wait_for_input(void *args)
{
WaitStruct *wait_str = (WaitStruct *) args;
int c = 0;
fd_set readFds;
int numFds=0;
FD_ZERO(&readFds);
do {
struct timeval timeout={.tv_sec=8,.tv_usec=0};
/* select here is primarily to serve as a cancellation
* point. Though there is a possible race condition
* still between getchar() getting called right as the
* the timeout thread calls cancel.().
* Using the timeout option on select would at least
* cover that, but not done here while testing.
*******************************************************/
FD_ZERO(&readFds);
FD_SET(STDOUT_FILENO,&readFds);
numFds=select(STDOUT_FILENO+1,&readFds,NULL,NULL,&timeout);
if(numFds==0 )
{
/* must be timeout if no FD's selected */
break;
}
if(FD_ISSET(STDOUT_FILENO,&readFds))
{
printf("Only get here if key pressed\n");
c = getchar();
}
} while (!(c == '1' || c == '2'));
*(wait_str->key) = c;
// Cancel the other thread
pthread_cancel(*(wait_str->thread));
return NULL;
}

Consumer - Producer, assertion failure

I have the bounded buffer, producer consumer problem to deal with and can only modify the prod and cons functions. This code runs without issues with only one consumer and producer threads. But with multiple of each it always gives me the same problem, sooner or later:
p5p1: p5p2a.c:207: bb_remove: Assertion `bbp->cnt > 0' failed.
What I don't get is how can this error occur when I check the bbp->cnt variable before calling the bbp_remove() function.
EDIT: The problem has been solved. I was not checking the variable within the lock, in either of the functions.
#include <sys/times.h>
#include <unistd.h>
#include <assert.h>
#include <stdlib.h>
#include <pthread.h>
#include <time.h>
#include <errno.h>
#include <stdio.h>
#define DEBUG 0
#define BUF_SIZE 100000
#define ITER 10000000
#define PROD_THRD 3
#define CONS_THRD 3
#define USAGE_STRING "Usage: %s\n"
extern int errno;
/* This is the bounded buffer type */
typedef struct {
int cnt, in, out;
pthread_mutex_t lock; /* Mutex to avoid race conditions */
int buf[BUF_SIZE]; /* The data passed is the id of the
* producer */
} bbuf_t;
typedef struct {
bbuf_t *bbp;
int id;
} parg_t;
/*
* yield()
* Because there is no yield system call in Linux, what we
* do is to put the thread to sleep for 1 ms. Actually, it
* will sleep for at least 1/HZ, which is 10 ms in Linux/386.
*/
#define YIELD_s 0
#define YIELD_ns 1000000
void yield() {
struct timespec st = {YIELD_s, YIELD_ns};
if( (nanosleep(&st, NULL)) == -1 && (errno == EINVAL)) {
perror("nanosleep");
pthread_exit(NULL);
}
}
/* Initialize bounded buffer */
int bbuf_init(bbuf_t *bbp) {
if(bbp == NULL)
return 0;
bbp->in = 0;
bbp->out = 0;
bbp->cnt = 0;
pthread_mutex_init(&(bbp->lock), NULL); /* I do not understand, but the
* compiler complains when I use
* PTHREAD_MUTEX_INITIALIZER */
return 1;
}
/* Print the bounded buffer members that matter */
void print_bbuf(bbuf_t *bbp) {
printf("bbp->in = %d bbp->out = %d bbp_cnt = %d \n",
bbp->in, bbp->out, bbp->cnt);
}
/* To validate the value of the members in, out and cnt of bbuf_t */
int val(int n, int min, int max) {
return( (min <= n) && (n <= max));
}
/* Ensure that the values of the members in, out and cnt are consistent */
int consist(int cnt, int in, int out) {
return ( in == ((out + cnt) % BUF_SIZE) );
}
/* This is the code of the checker thread. It is used to ensure that
* the bounded buffer has not been corrupted.
* Every 100 ms it checks the values of: in, out and cnt.
* This thread exits either if it detects the buffer has been corrupted
* or if it does not detect any activity in 50 consecutive iterations,
* i.e. approximately 5s. */
/* These constants are used with nanosleep() and
* put a process to sleep for 100 ms */
#define SLEEP_s 0
#define SLEEP_ns 100000000
#define MAX_IDLE 50
void *check(void *arg) {
bbuf_t *bbp = arg;
int cnt[2], in[2], out[2]; /* values read */
int idle;
struct timespec st = {SLEEP_s, SLEEP_ns}; /* 100 ms */
while(1) {
pthread_mutex_lock( &(bbp->lock) );
in[1] = bbp->in;
out[1] = bbp->out;
cnt[1] = bbp->cnt;
pthread_mutex_unlock( &(bbp->lock) );
if(in[1] == in[0] && out[1] == out[0] && cnt[1] == cnt[0] ) {
idle++;
if( idle >= MAX_IDLE ) {
printf("Checking thread exiting:");
print_bbuf(bbp);
printf("\t no activity detected for some time.\n");
pthread_exit(NULL);
}
} else {
idle = 0;
}
if( !val(in[1], 0, BUF_SIZE - 1) ) {
printf("Invalid value in = %d \n", in[1]);
pthread_exit(NULL);
} else if ( !val(out[1], 0, BUF_SIZE - 1) ) {
printf("Invalid value out = %d \n", out[1]);
pthread_exit(NULL);
} else if ( !val(cnt[1], 0, BUF_SIZE) ) {
printf("Invalid value cnt = %d \n", cnt[1]);
pthread_exit(NULL);
} else if ( !consist(cnt[1], in[1], out[1]) ) {
printf("Inconsistent buffer: cnt = %d in = %d out = %d \n",
cnt[1], in[1], out[1]);
pthread_exit(NULL);
}
if( (nanosleep(&st, NULL) == -1) && (errno == EINVAL)) {
perror("nanosleep");
pthread_exit(NULL);
}
in[0] = in[1];
out[0] = out[1];
cnt[0] = cnt[1];
}
}
/* The producer threads may use this code to
* enter one item into the buffer */
void bb_enter(bbuf_t *bbp, int me) {
assert( bbp->cnt < BUF_SIZE);
(bbp->buf)[bbp->in] = me;
(bbp->in)++;
(bbp->in) %= BUF_SIZE;
(bbp->cnt)++;
//printf("%d\n",bbp->cnt);
}
/* This is the code for the producer threads.
*
* To avoid busy waiting (or at least too much busy waiting) the producers
* should yield, using the yield() defined above, if the buffer is
* full. In that case, they should print a debugging message as well.
*
* Each producer should produce ITER (10 M) items: an integer with
* the id it receives in prod()'s argument.
*/
void *prod(void *arg) {
parg_t *parg = (parg_t *)arg;
bbuf_t *bbp = parg->bbp;
int me = parg->id;
/* Add variables and code, if necessary */
printf("I am a producer and have started\n");
int gcnt = 0;
while( gcnt <= ITER ){
if(bbp->cnt < BUF_SIZE){
pthread_mutex_lock(&(bbp->lock));
bb_enter(bbp,me);
gcnt++;
pthread_mutex_unlock(&(bbp->lock));}
else if( bbp->cnt == (BUF_SIZE-1)) {printf("I shall produce yield()\n"); yield();}
}
printf("I am a producer and have ended\n");
return;
}
/* The consumer threads may use this function to
* remove an item */
int bb_remove(bbuf_t *bbp) {
int val;
assert(bbp->cnt > 0);
val = (bbp->buf)[bbp->out];
(bbp->out)++;
(bbp->out) %= BUF_SIZE;
(bbp->cnt)--;
return val;
}
/* This is the code for the consumer threads.
* To avoid busy waiting (or at least too much busy waiting) consumers
* should yield, using the yield() defined above, if the buffer is
* empty. In that case, they should print a debugging message as well.
*
* Each consumer should consume ITER (10 M) items, and keep track of the
* producers of the items it consumes: use the cnt[] below.
*/
void *cons(void *arg) {
bbuf_t *bbp = (bbuf_t *)arg;
int cnt[PROD_THRD];
/* Add variables and code, if necessary:
* do not forget to initialize cnt */
printf("I am a consumer and have started\n");
int i;
for(i = 0; i < PROD_THRD; i++){
cnt[i] = 0;}
int temp;
int gcnt = 0;
while( gcnt <= ITER ){
if(bbp->cnt > 0){
pthread_mutex_lock(&(bbp->lock));
temp = bb_remove(bbp);
gcnt++;
cnt[temp]++;
pthread_mutex_unlock(&(bbp->lock));}
else if( bbp->cnt == 0) {printf("I shall consume yield()\n"); yield();}
}
printf("I am a consumer and have ended\n");
return;
}
int main(int argc, char *argv[]) {
int i;
pthread_t *tid, ctid;
parg_t *parg;
bbuf_t *bbp;
/* This is to measure the time it takes to run the program */
struct tms t;
clock_t start, end;
long ticks = sysconf(_SC_CLK_TCK);
start = times(&t);
if( argc != 1 ) {
printf(USAGE_STRING, argv[0]);
exit(1);
}
/* Array for pthread_join() */
if((tid = (pthread_t *) malloc((PROD_THRD + CONS_THRD) * sizeof(pthread_t)))
== NULL ) {
printf("Out of memory.\n");
exit(2);
}
/* Allocate Bounded Buffer */
if((bbp = (bbuf_t *) malloc(sizeof(bbuf_t))) == NULL ) {
printf("Out of memory. \n");
exit(2);
}
/* Initialize Bounded Buffer */
if( bbuf_init(bbp) == 0 ) {
printf("Failed to initialize bounded buffer\n");
exit(3);
}
/* Arguments for producer threads */
if((parg = (parg_t *) malloc( PROD_THRD * sizeof (parg_t))) == NULL ) {
printf("Out of memory.\n");
exit(2);
}
/* Create checker thread */
if( pthread_create(&ctid, NULL, check, bbp) )
perror("pthread_create");
printf("Created checker thread %u\n", (unsigned)ctid);
/* Create consumer threads */
for( i = 0; i < CONS_THRD; i++ ) {
/* We pass the same data structure, the bounded buffer,
* to each consumer: they need to synchronize to access it */
if( pthread_create(tid+i, NULL, cons, bbp) )
perror("pthread_create");
printf("Created consumer thread %u\n", (unsigned)tid[i]);
}
/* Create producer threads */
for( i = 0; i < PROD_THRD; i++ ) {
/* Because we want each consumer to keep track of the
* producer of the items it consumes, we assign an
* id to each producer thread */
parg[i].bbp = bbp;
parg[i].id = i;
if( pthread_create(tid+(i+CONS_THRD), NULL, prod, parg+i) )
perror("pthread_create");
printf("Created producer thread %u (%d)\n", (unsigned)tid[i+CONS_THRD], i);
}
/* Join consumer and producer threads */
for( i = 0; i < CONS_THRD + PROD_THRD; i ++ ) {
if( pthread_join(tid[i], NULL) == 0 ) {
printf("Joined thread %u.\n", (unsigned)tid[i]);
} else {
printf("Failed to join thread %u\n", (unsigned)tid[i]);
}
}
/* Join checker thread */
if( pthread_join(ctid, NULL) == 0 ) {
printf("Joined checker thread %u.\n", (unsigned)ctid);
} else {
printf("Failed to join checker thread %u\n", (unsigned)ctid);
}
/* How long did it take to run this ? */
end = times(&t);
printf("Wall time %2.4f s \n", (float)(end - start) / ticks);
return 0;
}
You should enter the mutex lock before you check bbp->cnt. Since you are checking it before you enter the mutex, another thread can reduce the value before you acquire the mutex and try to reduce the value yourself.

Resources