pthread void pointer casting - c

I am working on an assignment which requires me to use threads to process and synchronize fetching data from a file. My professor told me that I can change my data to a void pointer to pass it to my function and then cast it back. I am trying to do this with file IO.
pthread_create(&th1, NULL, processing, (void *)&fp);
In my processing function I am trying to cast it back to a FILE pointer with this:
FILE driveOne = (FILE *)file;
This clearly doesn't work, so can someone explain this to me?

Here's a more complete example.
Let's say your worker function needs a file handle. For simplicity, let's say it reads each char from it, and returns the number of chars read, cast to a pointer:
void *worker(void *data)
{
FILE *handle = (FILE *)data;
uintptr_t count = 0;
if (handle && !ferror(handle)) {
/* handle is a valid file handle */
while (getc(handle) != EOF)
count++;
}
return (void *)count;
}
If count were of some other type than intptr_t or uintptr_t (declared in <stdint.h>, which is typically included by including <inttypes.h>), you'd need to cast it first to that type, and then to void pointer, i.e. (void *)(uintptr_t)count.
Because such worker threads don't need much stack (almost none, to be precise), and default thread stack sizes are huge (megabytes), we can save some memory (and allow much more threads if needed, especially on 32-bit architectures) by creating a pthread attribute that instructs pthread_create() to use a smaller stack. This attribute is not "consumed" by the call; it is more like an configuration block.
Let's say you have three streams, FILE *in[3];, and you wish to use three threads to check their lengths. Using a pthread attribute to use a smaller stack (2*PTHREAD_STACK_MIN, as defined in <limits.h>, is a good, safe value for worker threads that don't use alloca() or local arrays.):
pthread_t worker_id[3];
uintptr_t length[3];
pthread_attr_t attrs;
void *retptr;
int i, result;
/* Create a pthread attribute set, defining smaller stack size. */
pthread_attr_init(&attrs);
pthread_attr_setstacksize(&attrs, 2*PTHREAD_STACK_MIN);
/* Create the three worker threads. */
for (i = 0; i < 3; i++) {
result = pthread_create(&(worker_id[i]), &attrs, worker, (void *)in[i]);
if (result) {
fprintf(stderr, "Cannot create thread: %s.\n", strerror(result));
exit(EXIT_FAILURE);
}
}
/* pthread attributes are no longer needed. */
pthread_attr_destroy(&attrs);
/*
... This thread can do something else here ...
*/
/* Reap the threads, and collect their return values. */
for (i = 0; i < 3; i++) {
result = pthread_join(worker_id[i], &retptr);
if (result) {
fprintf(stderr, "Cannot reap thread: %s.\n", strerror(result));
exit(EXIT_FAILURE);
}
length[i] = (uintptr_t)retptr;
}
for (i = 0; i < 3; i++)
printf("in[%d] contained %llu chars.\n", i, (unsigned long long)length[i]);
The same pattern can be used when you want to pass multiple parameters to the thread function. You first construct a structure to hold those parameters, and create them. You can allocate them dynamically, declare them as global variables, or declare them as local variables in main() -- any scope that exists for the full duration when the worker thread exists, works.
For example, let's say your worker function calculates a histogram of each unsigned char value it reads from the stream:
struct work {
pthread_t id; /* Thread identifier */
FILE *in; /* File handle to read from */
size_t count[UCHAR_MAX + 1]; /* Histogram */
};
void *worker(void *data) {
struct work *const work = (struct worker_data *)data;
int c;
if (!work || !work->in) {
/* Invalid data, or invalid file handle. */
return (void *)(intptr_t)(EINVAL);
}
if (ferror(work->in)) {
/* Stream is in error state. */
return (void *)(intptr_t)(EIO);
}
/* Read the stream. */
while ((c = getc(work->in)) != EOF) {
/* Update histogram. */
work->count[(unsigned char)c]++;
}
/* Did the reading stop due to an I/O error? */
if (ferror(work->in))
return (void *)(intptr_t)(EIO);
/* No errors, all done. */
return (void *)0;
}
Note that struct work *const work = ... initializes a constant pointer work, not a pointer to constant. The const there is just an optimization that tells the C compiler that we won't try to modify work pointer itself. The data it points to, is modifiable.
(To read pointer declarations, read them from right to left, replacing each * with "is a pointer to", to get the proper sense of it.)
The code to create these workers is very similar, except that we allocate the work dynamically:
struct work *work[3];
pthread_attr_t attrs;
void *retptr;
int i, result;
/* Create and initialize the three pointers. */
for (i = 0; i < 3; i++) {
/* Allocate a work structure. */
work[i] = malloc(sizeof *(work[i]));
if (!work[i]) {
fprintf(stderr, "Out of memory.\n");
exit(EXIT_FAILURE);
}
/* Copy the handle to read from, */
work[i]->in = in[i];
/* and clear the histogram part. */
memset(work[i]->count, 0, sizeof work[i]->count);
}
/* Create a pthread attribute set, defining smaller stack size. */
pthread_attr_init(&attrs);
pthread_attr_setstacksize(&attrs, 2*PTHREAD_STACK_MIN);
/* Create the three worker threads. */
for (i = 0; i < 3; i++) {
result = pthread_create(&(work[i]->id), &attrs, worker, (void *)work[i]);
if (result) {
fprintf(stderr, "Cannot create thread: %s.\n", strerror(result));
exit(EXIT_FAILURE);
}
}
/* pthread attributes are no longer needed. */
pthread_attr_destroy(&attrs);
/*
... This thread can do something else here ...
*/
/* Reap the threads, and collect their return values. */
for (i = 0; i < 3; i++) {
result = pthread_join(work[i]->id, &retptr);
if (result) {
fprintf(stderr, "Cannot reap thread: %s.\n", strerror(result));
exit(EXIT_FAILURE);
}
/* If the thread reported a failure, print the corresponding
error message (but do not exit). */
if (retptr)
fprintf(stderr, "Thread %d of 3: %s.\n", i+1, strerror((intptr_t)retptr));
/* ... print the histogram here? ... */
}
/* Free the work structures. */
for (i = 0; i < 3; i++)
free(work[i]);
If you don't want to abort the program when an error occurs, it is useful to note that free(NULL) is safe and does nothing; and that struct work *pointerarray[SIZE] = {0}; declares an array of SIZE pointers to struct work, and initializes them all to zero. For example, if an allocation or thread creation fails at some point, you can just free() each pointer, whether or not its allocation was successful.
That is, if you want to allocate three different types of structures (struct atype *a;, struct btype *b;, and struct ctype *c;), you can do
a = malloc(sizeof *a);
b = malloc(sizeof *b);
c = malloc(sizeof *c);
if (!a || !b || !c) {
free(c);
free(b);
free(a);
return ALLOCATION_FAILED;
}
/* Allocation was successful */
instead of allocating each one and testing for failure separately.

You need to declare driveOne to be FILE *, not FILE.
FILE *driveOne = (FILE *)file;
In addition, assuming that fp was initially declared as FILE *, your call to pthread_create should not have & before fp, like so:
pthread_create(&th1, NULL, processing, (void *)fp);

Related

Condition variable blocks on main thread

I want to use condition variables to launch at most N thread to process all files one one huge directory (1M files).
The code seems to work but after some times, it blocks in main thread. Below the frustrating code:
void* run(void* ctx)
{
clientCtx* client = (clientCtx*)ctx;
printf("New file from thread %d: %s\n", client->num, client->filename);
free(client->filename);
pthread_mutex_lock(&clientFreeMutex);
client->state = IDLE_STATE;
pthread_cond_signal(&clientFreeCond);
printf("Thread %d is free\n", client->num);
pthread_mutex_unlock(&clientFreeMutex);
return NULL;
}
int main(int argc, char** argv)
{
pthread_t client[MAX_CLIENT] = {0};
clientCtx ctx[MAX_CLIENT] = {0};
DIR* directory = NULL;
struct dirent* element = NULL;
/* Initialize condition variable for max clients */
pthread_mutex_init(&clientFreeMutex, NULL);
pthread_cond_init(&clientFreeCond, NULL);
/* Initialize contexts for clients */
for (int cnt = 0; cnt < MAX_CLIENT; cnt ++)
{
ctx[cnt].state = IDLE_STATE;
ctx[cnt].num = cnt;
}
directory = opendir(argv[1]);
while((element = readdir(directory)) != NULL)
{
pthread_mutex_lock(&clientFreeMutex);
int cnt;
for (cnt = 0; cnt < MAX_CLIENT; cnt++)
{
if(ctx[cnt].state == IDLE_STATE)
{
ctx[cnt].filename = strdup(element->d_name);
ctx[cnt].state = BUSY_STATE;
pthread_create(&client[cnt], NULL, run, &(ctx[cnt]));
break;
}
}
/* No free client */
if (cnt == MAX_CLIENT)
{
printf("No free thread. Waiting.\n");
pthread_cond_wait(&clientFreeCond, &clientFreeMutex);
}
pthread_mutex_unlock(&clientFreeMutex);
}
closedir(directory);
exit(EXIT_SUCCESS);
}
What is the problem? thanks for your help :)
Warning you use the value of readdir in separate threads without any protection against the multi-threading, so when you (try to) printf client->file->d_name may be you are doing at the same time readdir in the main thread modifying the saved result, this has an undefined behavior.
You need for example to save a strdup of element->file->d_name in main and save that string in the clientCtx rather than the struct dirent *, and of course to free it in run
Note also a closedir is missing at the end of main even in this case it is not a real problem (just do to remember for your other programs).
I finally found the problem: launched threads were not joined and pthread_create finally returned an error code with errno message set to "Could not allocate memory". The signal was never sent and the main thread was then blocking.
I fixed this creating a new state for already launched threads and adding a join in main loop.

glibc corrupted double linked list error while running a C code on 64 bit machine

I have a large C code that works fine on 32 bit machine but fails on 64 bit machines with
*** glibc detected *** ./XXX.exe: corrupted double-linked list: error.
I have run Valgrind and have used print statements to exactly pinpoint where the error is coming from but am clueless about the fix.
The actual code is very big but I am giving the relevant portions here. Hoping someone can help me troubleshoot it.
The actual error is coming from the Buf_Close() module where it tries to free Buf_elm_p p as if (p != NULL) free(p);
These are functions that the main code calls and I am only giving this here as the error is somewhere here. The calling sequence from the main code is:
1. Buf_Init
2. Buf_New_p
3. Buf_Close
Buf_bf_p
Buf_Init( size_t elm_size,
int nelm_buf,
long nbuf )
/*******************************************************************************
!C
!Description: Buf_Init (buffer initialize) initializes buffer data structures
and buffers.
!Input Parameters:
elm_size element size (bytes)
nelm_buf number of elements per buffer
nbuf number of buffers
!Output Parameters:
(returns) pointer to the buffer data structure
or NULL for an error return
*****************************************************************************/
{
Buf_bf_p bf;
long buf_size;
long ibuf;
/* Calculate buffer size and check */
buf_size = ((long) elm_size) * nelm_buf;
/*Allocate the buffer data structure */
if ((bf = (Buf_bf_p) malloc(sizeof(Buf_bf_t))) == NULL)
{Buf_Error(&BUF_NOMEMORY, "Init"); return NULL;}
bf->key = BUF_KEY;
bf->elm_size = elm_size;
bf->nelm_buf = nelm_buf;
bf->nbuf = nbuf;
bf->buf_size = buf_size;
bf->fp = NULL;
bf->access = NO_FILE;
bf->nbuf_alloc = 1;
bf->ibuf_end = 0;
bf->ibuf_newest = 0;
bf->ibuf_oldest = 0;
bf->nelm = 0;
/* Allocate the buffer status data structure */
bf->nbstat = max(NBSTAT_START, bf->nbuf + 1);
if ((bf->bstat = (Buf_bstat_t *)
malloc(bf->nbstat * sizeof(Buf_bstat_t))) == NULL)
{Buf_Error(&BUF_NOMEMORY, "Init"); return NULL;}
/* Allocate the first buffer */
bf->bstat[0].loc = MEM_ONLY;
if( (bf->bstat[0].buf_p = (Buf_elm_p) malloc(bf->buf_size)) == NULL)
{ Buf_Error(&BUF_NOMEMORY, "Init");
return NULL;
}
else
{
/* initialize */
memset( bf->bstat[0].buf_p, '\0', bf->buf_size );
}
bf->bstat[0].newer = -1;
bf->bstat[0].older = -1;
/* Initialize the rest of the buffer status array */
printf("bf->nbstat %d\n", bf->nbstat);
for (ibuf = 1; ibuf < bf->nbstat; ibuf++) {
bf->bstat[ibuf].loc = NOT_ALLOC;
bf->bstat[ibuf].buf_p = NULL;
bf->bstat[ibuf].newer = -1;
bf->bstat[ibuf].older = -1;
bf->bstat[ibuf].initialized = 1;
}
return bf;
}
Buf_elm_p
Buf_New_p( Buf_bf_p bf,
long *ielm )
/*******************************************************************************
!C
!Description: Buf_New_p (new buffer element pointer) returns a memory
location and element number of a new element; elements are number
sequentially as they are allocated.
!Input Parameters:
bf pointer to the buffer data structure
!Output Parameters:
ielm new element number
(returns) pointer to memory location of new element
or NULL for error
!Notes:
1. 'Buf_Init' must be called before this routine to initialize
the buffer data structure.
2. If there is no more space in memory and disk write access is allowed,
the oldest buffer is written to disk and the memory is re-used.
3. If the file is opened with 'read only' access this routine will return
an error.
!END
******************************************************************************/
{
long ibuf, jelm, jbuf, kbuf;
long nbuf_cmplt;
Buf_elm_p p;
long dsk_loc, eof_loc;
/* New element number/location */
*ielm = bf->nelm++;
ibuf = *ielm / bf->nelm_buf;
jelm = *ielm % bf->nelm_buf;
/* Are we at the past the end of the last buffer? */
if (ibuf > bf->ibuf_end) {
if (ibuf != (bf->ibuf_end + 1))
{Buf_Error(&BUF_BADBUF, "New_p"); return NULL;}
/* Re-allocate buffer status data structure if not large enough */
if( ibuf >= bf->nbstat )
{
bf->nbstat += min(bf->nbstat, MAX_NEW_NBSTAT);
if( (bf->bstat = realloc(bf->bstat, bf->nbstat * sizeof(Buf_bstat_t)))
== NULL)
{ Buf_Error(&BUF_NOMEMORY, "New_p");
return NULL;
}
}
if (bf->nbuf_alloc < bf->nbuf || bf->access == NO_FILE) {
/* Allocate a new buffer */
if( (p = (Buf_elm_p) malloc(bf->buf_size)) == NULL)
{ Buf_Error(&BUF_NOMEMORY, "New_p");
return NULL;
}
else
{
/* initialize */
memset( p, '\0', bf->buf_size );
}
bf->nbuf_alloc++;
if (bf->nbuf < bf->nbuf_alloc) bf->nbuf = bf->nbuf_alloc;
} else {
/* Re-use an existing buffer */
/* Get the oldest buffer */
jbuf = bf->ibuf_oldest;
/* Delete oldest buffer from old/new pointer list */
p = bf->bstat[jbuf].buf_p;
bf->ibuf_oldest = bf->bstat[jbuf].newer;
bf->bstat[bf->ibuf_oldest].older = -1;
bf->bstat[jbuf].buf_p = NULL;
bf->bstat[jbuf].older = -1;
bf->bstat[jbuf].newer = -1;
bf->bstat[jbuf].initialized = 1;
}
/* Put current buffer in old/new pointer list */
bf->bstat[ibuf].loc = MEM_ONLY;
bf->bstat[ibuf].buf_p = p;
bf->bstat[ibuf].older = bf->ibuf_newest;
bf->bstat[ibuf].newer = -1;
bf->bstat[ibuf].initialized = 1;
bf->ibuf_end = ibuf;
bf->bstat[bf->ibuf_newest].newer = ibuf;
bf->ibuf_newest = ibuf;
}
/* Calculate pointer to memory location of element */
p = (unsigned char *) bf->bstat[ibuf].buf_p + (jelm * bf->elm_size);
return p;
}
int
Buf_Close( Buf_bf_p bf )
/*******************************************************************************
!C
!Description: Buf_Close (buffer cache file close) writes the remainder of the
cache to the disk cache file and closes the file and frees memory of the
buffer data structure and buffers.
!Input Parameters:
bf pointer to the buffer data structure
Notes:
1. 'Buf_Create' or 'Buf_Open' must be called before this routine to open
the file.
!END
*****************************************************************************/
{
int i;
long dsk_loc;
logical_t cmplt_flag;
/* int b; */
Buf_elm_p p;
long ibuf, nelm_wrt;
int nb;
unsigned char header[HEADER_SIZE];
/* Write remaining buffers which are still only in memory */
for (ibuf = 0; ibuf < (bf->ibuf_end + 1); ibuf++)
/* for (ibuf = 0; ibuf < (bf->ibuf_end); ibuf++)*/{
p = bf->bstat[ibuf].buf_p;
/* Free the buffer memory */
**THIS FOLLOWING LINE IS WHERE THE ERROR IS COMING FROM**
**VALGRIND SHOWS `Conditional jump or move depends on uninitialised value(s)` ERROR**
**BUT AM NOT SURE HOW `p` is coming out to be uninitialized`**
if (p != NULL) free(p);
}
/* Free the buffer status memory */
free(bf->bstat);
/* Free the buffer cache data structure */
bf->fp = (FILE *)NULL;
bf->key = 0;
free(bf);
printf("buf here 5\n");
return BUF_NORMAL;
}
I work on a project that has a lot of questionable practices (currently working toward cleaning them up). I ran into this error and went through everything I could imagine to ferret out where the problem was including clang sanitizers, a variety of valgrind tools, and a variety of other tricks.
The problem: exit() was being called in one thread about the same time as main() returned, so all the global/static constructors were being kicked off in two separate threads simultaneously. I'm actually kind of annoyed I didn't make the connection sooner.
This error also manifests as:
double free or corruption
... which may also be caused by another problem
segfault/sig11 inside exit()
Crashes inside malloc_consolidate with a call stack that looks like:
I guess you can't add code examples after a bullet item
#0 0xabcdabcd in malloc_consolidate () from /lib/libc.so.6
#1 0xabcdabcd in _int_free () from /lib/libc.so.6
#2 0xabcdabcd in operator delete (...)
#3 0xabcdabcd in operator delete[] (...)
(...)
Furthermore, I couldn't get it to exhibit this problem while running under valgrind -- whether it was a timing issue, or some artifact of how valgrind works that hid the problem I may never know.
It bit difficult to understand your program logic by static analysis. However Valgrind print "Conditional jump or move depends on uninitialized value" under the scenario
where program attempts to make use of uninitialized data in a way that might affect your program's externally-visible behaviour.
Sources of uninitialised data tend to be:
Local variables in procedures which have not been initialized.
The contents of heap blocks (allocated with malloc, new, or a similar function) before you (or a constructor) write something there.
To see information on the sources of uninitialised data in your program, you may use
the option --track-origins=yes. So You can run the program(./a.out) as follows:
valgrind --tool=memcheck --track-origins=yes ./a.out
This might be helpful and provide more useful information closer to your actual source of your problem.You can find more detailed information about it from the location:
http://valgrind.org/docs/manual/mc-manual.html

pthread_create int instead of void

I have the following code:
for(i = 0 ; i < max_thread; i++)
{
struct arg_struct args;
args.arg1 = file;
args.arg2 = word;
args.arg3 = repl;
if(pthread_create(&thread_id[i],NULL,&do_process,&args) != 0)
{
i--;
fprintf(stderr,RED "\nError in creating thread\n" NONE);
}
}
for(i = 0 ; i < max_thread; i++)
if(pthread_join(thread_id[i],NULL) != 0)
{
fprintf(stderr,RED "\nError in joining thread\n" NONE);
}
int do_process(void *arguments)
{
//code missing
}
*How can I transform (void *)do_process into (int) do_process ?*
That function returns very important info and without those returns I don't know how to read the replies
I get the following error: warning: passing arg 3 of `pthread_create' makes pointer from integer without a cast
The thread function returns a pointer. At minimum, you can allocate an integer dynamically and return it.
void * do_process (void *arg) {
/* ... */
int *result = malloc(sizeof(int));
*result = the_result_code;
return result;
}
Then, you can recover this pointer from the thread_join() call;
void *join_result;
if(pthread_join(thread_id[i],&join_result) != 0)
{
fprintf(stderr,RED "\nError in joining thread\n" NONE);
} else {
int result = *(int *)join_result;
free(join_result);
/* ... */
}
Just write a helper function that is of the correct type, but all it does is take the void * input parameter, get all the right parameters out of it, call your function, take the return of that, and package it up as a void * for pthread_join to get.
To your specific question, you can't/shouldn't. Just do what I outlined above and you'll be golden.
The pthread_join() is a simple way to communicate between the two threads. It has two limitations. First, it can pass only one value from the pointer (you can make it a pointer and store multiple values). Second, you can return it only when the thread is all done -- after returning this value, the thread goes in terminated state. So, if you want the threads to communicate in a more granular fashion, you will be better served in using a common shared data. Of course, at teh very least, you would to use Pthread mutex to synchronize access to the common data. And, if you want the threads to communicate with each other, then you would also need to use Pthread condvars.

I'm having a hard time having a struct work using multiple files. Please help?

I am trying to solve a consumer/producer problem, and I have to create three different classes.
The main class includes the creation of threads, and consumer/producer logic
the other two classes are
A header file for a ring buffer
A file containing the implementation of the ring buffer
I'm getting the following errors when trying to compile:
ringbuf.c: In function ‘rb_init’:
ringbuf.c:10: warning: incompatible implicit declaration of built-in function ‘malloc’
ringbuf.c:10: error: invalid application of ‘sizeof’ to incomplete type ‘struct ringbuf_t’
ringbuf.c:12: error: dereferencing pointer to incomplete type
I many other errors, but I can handle them myself once I get through this one.
this is the header file for the buffer:
struct ringbuf_t
{
pthread_mutex_t mutex; /* lock to protect from simutaneous access to the buffer */
pthread_cond_t cond_full; /* producer needs to wait when the buffer is full */
pthread_cond_t cond_empty; /* consumer needs to wait when the buffer is empty */
int bufsiz; /* size of the buffer; you may use an empty slot to differentiate the situation the buffer is full or empty */
int front; /* index of the first element */
int back; /* index next to the last element (or index to the first empty slot) */
int count; //keeps track of the number of elements in the buffer
char* buf; /* buffer itself */
};
/* return the pointer to the newl created and initialized ring buffer of the given size */
extern struct ringbuf_t* rb_init(int bufsiz);
/* reclaim the ring buffer */
extern void rb_finalize(struct ringbuf_t* rb);
/* return the current number of elements in the buffer */
extern int rb_size(struct ringbuf_t* rb);
/* return non-zero (true) if the buffer is currently full */
extern int rb_is_full(struct ringbuf_t* rb);
/* return non-zero (true) if the buffer is currently empty */
extern int rb_is_empty(struct ringbuf_t* rb);
/* insert (i.e., append) a character into the buffer; if the buffer is full, the caller thread will be blocked */
extern void rb_insert(struct ringbuf_t* rb, int c);
/* retrieve a character at the front of the ring buffer; if the buffer is empty, the caller thread will be blocked */
extern int rb_remove(struct ringbuf_t* rb);
and this is the implementation of the buffer:
#include <malloc.h>
#include <stdio.h>
struct ringbuf_t
{
pthread_mutex_t mutex; /* lock to protect from simutaneous access to the buffer */
pthread_cond_t cond_full; /* producer needs to wait when the buffer is full */
pthread_cond_t cond_empty; /* consumer needs to wait when the buffer is empty */
int bufsiz; /* size of the buffer; you may use an empty slot to differentiate the situation the buffer is full or empty */
int front; /* index of the first element */
int back; /* index next to the last element (or index to the first empty slot) */
int count; //keeps track of the number of elements in the buffer
char* buf; /* buffer itself */
};
struct ringbuf_t* rb_init(int bufsiz)
{
struct ringbuf_t* buffer = (struct ringbuf_t*)malloc(sizeof(struct ringbuf_t));
buffer->bufsiz = bufsiz;
buffer->front = 0;
buffer->back = 0;
buffer->count = 0;
}
/* reclaim the ring buffer */
void rb_finalize(struct ringbuf_t* rb)
{
free(rb);
pthread_mutex_destroy(&rb->mutex);
printf("\nnotice - ring buffer finalized");
}
/* return the current number of elements in the buffer */
int rb_size(struct ringbuf_t* rb)
{
return (rb->count);
}
/* return non-zero (true) if the buffer is currently full */
int rb_is_full(struct ringbuf_t* rb)
{
return ((rb->count) == rb->bufsiz);
}
/* return non-zero (true) if the buffer is currently empty */
int rb_is_empty(struct ringbuf_t* rb)
{
return ((rb->count) == 0);
}
/* insert (i.e., append) a character into the buffer; if the buffer is full, the caller thread will be blocked */
void rb_insert(struct ringbuf_t* rb, int c)
{
char* temp;
if(rb->count < rb->bufsiz)
{
if(rb->count == 0)
{
rb->front = 0;
rb->back = 0;
rb->buf = c;
rb->count++;
}
else
{
if(rb->front < (rb->bufsiz - 1))
{
temp = rb->buf;
temp = temp + rb->front + 1;
temp = c;
rb->front++;
}
else if(rb->front == (rb->bufsiz -1))
{
rb->front = 0;
rb->buf = c;
}
}
}
else
{
printf("\nerror - trying to insert into full buffer");
}
}
/* retrieve a character at the tail (back) of the ring buffer; if the buffer is empty, the caller thread will be blocked */
int rb_remove(struct ringbuf_t* rb)
{
if(rb->count != 0)
{
count--;
if(rb->back < (rb->bufsiz-1)
{
rb->back++;
}
else if(rb->back == (rb->bufsiz -1))
{
rb->back = 0;
}
}
else
{
printf("\nerror - trying to remove from empty buffer");
}
}
this is the main class:
#include <stdio.h>
#include <pthread.h>
#include <ringbuf.h>
//creating a static ring buffer
struct ringbuf* mybuffer = rb_init(10);
int thisChar;
/*
consumer thread, reads one character at a time, sets the lock when addinga character to the ring buffer
while locked it checks if the buffer is full, waits for a slot, and then continues.
*/
void* consumer(void* arg)
{
printf("consumer started");
while(thisChar != EOF)
{
pthread_mutex_lock(&(mybuffer->mutex));
while(rb_is_empty(mybuffer))
pthread_cond_wait(&(mybuffer->cond_full), &(mybuffer->mutex));
printf("%s", (char)rb_remove(mybuffer));
pthread_cond_signal(&(mybuffer->cond_empty));
pthread_mutex_unlock(&(mybuffer->mutex));
}
}
/*
producer thread, takes one character at a time from the buffer, (while the buffer is not empty)
and prints it to the screen.
*/
void* producer(void* arg)
{
printf("producer started");
while ((thisChar = getchar()) != EOF)
{
pthread_mutex_lock(&(mybuffer->mutex));
while(rb_is_full(mybuffer))
pthread_cond_wait(&(mybuffer->cond_empty), &(mybuffer->mutex));
rb_insert(mybuffer, thisChar);
pthread_cond_signal(&(mybuffer->cond_full));
pthread_mutex_unlock(&(mybuffer->mutex));
}
}
int main()
{
//declaring threads
pthread_t t0, t1;
//creating threads as condumer, producer
p_thread_create(&t0, NULL, consumer, (void*)mybuffer);
p_thread_create(&t1, NULL, producer, (void*)mybuffer);
pthread_join(t0, NULL);
pthread_join(t1, NULL);
rb_finalize(mybuffer);
return 0;
}
I'm missing some stuff, but I need to get through this first! please help!
Replace your #include <malloc.h> lines with #include <stdlib.h>. That will fix the errors you have pasted here (and probably many many more). When you do that, go back through your code and remove all the casts in your calls to malloc(3):
struct ringbuf_t* buffer = (struct ringbuf_t*)malloc(sizeof(struct ringbuf_t));
That (struct ringbuf_t*) hasn't been necessary since roughly 1989, when function prototypes were pushed into the language.
See also:
Should I use #include in headers?
What are extern variables in C?
ringbuf.h
Your ringbuf.h header should be self-contained and idempotent. It should, therefore, include <pthread.h>.
#ifndef RINGBUF_H_INCLUDED
#define RINGBUF_H_INCLUDED
#include <pthread.h>
struct ringbuf_t
{
pthread_mutex_t mutex; /* lock to protect from simutaneous access to the buffer */
pthread_cond_t cond_full; /* producer needs to wait when the buffer is full */
pthread_cond_t cond_empty; /* consumer needs to wait when the buffer is empty */
int bufsiz; /* size of the buffer; you may use an empty slot to differentiate the situation the buffer is full or empty */
int front; /* index of the first element */
int back; /* index next to the last element (or index to the first empty slot) */
int count; //keeps track of the number of elements in the buffer
char* buf; /* buffer itself */
};
/* return the pointer to the newl created and initialized ring buffer of the given size */
extern struct ringbuf_t* rb_init(int bufsiz);
/* reclaim the ring buffer */
extern void rb_finalize(struct ringbuf_t* rb);
/* return the current number of elements in the buffer */
extern int rb_size(struct ringbuf_t* rb);
/* return non-zero (true) if the buffer is currently full */
extern int rb_is_full(struct ringbuf_t* rb);
/* return non-zero (true) if the buffer is currently empty */
extern int rb_is_empty(struct ringbuf_t* rb);
/* insert (i.e., append) a character into the buffer; if the buffer is full, the caller thread will be blocked */
extern void rb_insert(struct ringbuf_t* rb, int c);
/* retrieve a character at the front of the ring buffer; if the buffer is empty, the caller thread will be blocked */
extern int rb_remove(struct ringbuf_t* rb);
#endif /* RINGBUF_H_INCLUDED */
Were it my header, I'd have an extra line:
typedef struct ringbuf_t ringbuf_t;
and I'd edit the function prototypes to lose the struct keyword.
The advantage of this is that anyone can include ringbuf.h and it will simply work for them.
ringbuf.c
It is crucial that the implementation file uses its own header; that gives you the necessary cross-checking that the header accurately reflects what is implemented. It should also be the first header included; this gives a simple but effective check that the header is self-contained.
You should not use <malloc.h> unless you are using its extended features. The <stdlib.h> declares malloc() et al and should be used unless you know which extra functions are available in <malloc.h> and you actually use them.
This leads to:
#include "ringbuf.h"
#include <stdio.h>
#include <stdlib.h>
struct ringbuf_t* rb_init(int bufsiz)
{
struct ringbuf_t* buffer = (struct ringbuf_t*)malloc(sizeof(struct ringbuf_t));
buffer->bufsiz = bufsiz;
buffer->front = 0;
buffer->back = 0;
buffer->count = 0;
}
/* reclaim the ring buffer */
void rb_finalize(struct ringbuf_t* rb)
{
free(rb);
pthread_mutex_destroy(&rb->mutex);
printf("\nnotice - ring buffer finalized");
}
/* return the current number of elements in the buffer */
int rb_size(struct ringbuf_t* rb)
{
return (rb->count);
}
/* return non-zero (true) if the buffer is currently full */
int rb_is_full(struct ringbuf_t* rb)
{
return ((rb->count) == rb->bufsiz);
}
/* return non-zero (true) if the buffer is currently empty */
int rb_is_empty(struct ringbuf_t* rb)
{
return ((rb->count) == 0);
}
/* insert (i.e., append) a character into the buffer; if the buffer is full, the caller thread will be blocked */
void rb_insert(struct ringbuf_t* rb, int c)
{
char* temp;
if(rb->count < rb->bufsiz)
{
if(rb->count == 0)
{
rb->front = 0;
rb->back = 0;
rb->buf = c;
rb->count++;
}
else
{
if(rb->front < (rb->bufsiz - 1))
{
temp = rb->buf;
temp = temp + rb->front + 1;
temp = c;
rb->front++;
}
else if(rb->front == (rb->bufsiz -1))
{
rb->front = 0;
rb->buf = c;
}
}
}
else
{
printf("\nerror - trying to insert into full buffer");
}
}
/* retrieve a character at the tail (back) of the ring buffer; if the buffer is empty, the caller thread will be blocked */
int rb_remove(struct ringbuf_t* rb)
{
if(rb->count != 0)
{
count--;
if(rb->back < (rb->bufsiz-1)
{
rb->back++;
}
else if(rb->back == (rb->bufsiz -1))
{
rb->back = 0;
}
}
else
{
printf("\nerror - trying to remove from empty buffer");
}
}
You should probably use fprintf(stderr, ...) rather than printf() for diagnostics, and you should consider how to turn them off at run-time (or, more likely, how to turn them on).
Note that it is conventional to put system-provided headers in angle brackets (hence <stdio.h>) and user-provided headers in double quotes (hence "ringbuf.h").
Your rb_init() function should initialize the structure completely. That means that the mutex and the two condition variables should both be initialized properly. It also needs to either initialize (zero) the buf member or allocate the appropriate amount of space - more likely the latter. Your code should check that the allocations succeed, and only use the allocated space if it does.
You should also review whether it is appropriate to make the producer and consumer threads manipulate the mutex and condition variables. If they are bundled with the structure, the functions bundled with the structure should do what is necessary with the mutexes and conditions. This would allow you to simplify the producer and consumer to just call the ring buffer functions. Clearly, the main() will still launch the two threads, but if you get your abstraction right, the threads themselves won't need to dink with mutexes and conditions directly; the ring buffer library code will do that correctly for the threads. One of the advantages of this is that your library can get the operations right, once, and all consumers benefit. The alternative is to have every producer and consumer handle the mutexes and conditions - which magnifies the opportunities to get it wrong. In a classroom situation where you won't use the abstraction again after this exercise, the proper separation and encapsulation is not so critical, but in professional code, it is crucial that the library make it easy for people to use the code correctly and hard for them to make mistakes.
main.c
In C, you cannot initialize a global variable with a function call - in C++, you can.
Hence, this won't compile in C:
//creating a static ring buffer
struct ringbuf_t *mybuffer = rb_init(10);
You should use:
struct ringbuf_t *mybuffer = 0;
and then, in main() or a function called from main() - directly or indirectly - do the function call:
mybuffer = rb_init(10);
This would be before you do any work creating the threads. When your rb_init() code initializes the mutex and condition variables, your main() will be able to go ahead as written.
Until then, you have a good deal of cleanup to do.
Disclaimer I have not compiled the code to see what the compiler witters about.
Note If you use GCC but don't compile with at least -Wall and preferably -Wextra too (and clean up any (all) the warnings), you are missing out on a very important assistant. I work with retrograde code bases where I have to worry about -Wmissing-prototypes -Wold-style-definition -Wstrict-prototypes too. Using -Werror can be helpful; it forces you to clean up the warnings.

Passing Data to Multi Threads

I study this code from some book:
#include <pthread.h>
#include <stdio.h>
/* Parameters to print_function. */
struct char_print_parms {
/* The character to print. */
char character;
/* The number of times to print it. */
int count;
};
/* Prints a number of characters to stderr, as given by PARAMETERS,
which is a pointer to a struct char_print_parms. */
void* char_print(void* parameters) {
/* Cast the cookie pointer to the right type. */
struct char_print_parms* p = (struct char_print_parms*) parameters;
int i;
for (i = 0; i < p->count; ++i)
fputc(p->character, stderr);
return NULL;
}
/* The main program. */
int main() {
pthread_t thread1_id;
pthread_t thread2_id;
struct char_print_parms thread1_args;
struct char_print_parms thread2_args;
/* Create a new thread to print 30,000 ’x’s. */
thread1_args.character = 'x';
thread1_args.count = 30000;
pthread_create(&thread1_id, NULL, &char_print, &thread1_args);
/* Create a new thread to print 20,000 o’s. */
thread2_args.character = 'o';
thread2_args.count = 20000;
pthread_create(&thread2_id, NULL, &char_print, &thread2_args);
usleep(20);
return 0;
}
after running this code, I see different result each time. and some time corrupted result. what is wrong and what the correct way to do that?
Add:
pthread_join( thread1_id, NULL);
pthread_join( thread2_id, NULL);
to the bottom of your code, before the return in main. Your prosess ends before your threads can complete. A sleep of 20 micro seconds is not enough to let your threads complete executing. Safer to wait for the threads to return.

Resources