7
votes

this is my first attempt at threading in C. I am creating a circularly bounded buffer. I know how to create the thread, but all examples I have seen only have threaded functions that accept one void parameter, but unfortunately my worker's specification requires me to use three, as shown here:

void bufferRead(BoundedBuffer* buffer, char* data, int count) {
    pthread_mutex_lock(&buffer->mutexBuffer);
    <snip>
    pthread_mutex_unlock(&buffer->mutexBuffer);
}

Here is my pthread_create statement

pthread_create(&buffer.readThread, NULL, (void *)bufferRead, &readParams)

And my readParams struct/assignments

struct readThreadParams {                                                   
    BoundedBuffer b;                                                        
    char* data;                                                             
    int count;                                                              
};                                                                          

struct readThreadParams readParams;                                         
readParams.b = buffer2;                                                     
readParams.data = out_array;                                                
readParams.count = in_size;

Any advice on how to assign each of the struct's parameters after passing to the bufferRead function would be greatly appreciated.

4

4 Answers

9
votes

That's because you only really need one parameter. When we have more than one value, as is typically the case, we encapsulate that into a struct. The function type that pthread_create will call is non-negotiable. This is an area where type-casting your function pointer can get you into serious trouble.

#include <pthread.h>
#include <stdlib.h>

struct BoundedBuffer {
    pthread_t readThread;
    pthread_mutex_t mutexBuffer;
} buffer2;

struct readThreadParams {
    struct BoundedBuffer b;
    char* data;
    int count;
};

void *bufferRead (void *context) {
    struct readThreadParams *readParams = context;

    pthread_mutex_lock(&readParams->b.mutexBuffer);
    //<snip>
    pthread_mutex_unlock(&readParams->b.mutexBuffer);

    return NULL;
}

int main(void) {
    int ret;
    char *out_array = malloc(42);
    size_t in_size = 42;

    struct readThreadParams readParams;
    readParams.b = buffer2;
    readParams.data = out_array;
    readParams.count = in_size;

    /* I presume that by "buffer", you really meant the .b member of
     * struct readThreadParams.  Further, this must have a member
     * named readThread of type pthread_t, etc.
     */
    ret = pthread_create(&readParams.b.readThread, NULL, bufferRead, &readParams);

    if (!ret) {
        pthread_join(&readParams.b.readThread, NULL);
    }

    free(out_array);

    return ret;
}
4
votes

Start function has to take argument. So your direction is right:

struct readThreadParams {                                                   
    BoundedBuffer *b; 
    char *data;                                                             
    int count;                                                              
};     

Then, you need to allocate the variable on heap, not on stack:

struct readThreadParams *readParams;

readParams = malloc(sizeof(*readParams));
readParams->b = buffer2;                                                     
readParams->data = out_array;                                                
readParams->count = in_size;

After which you can give it createThread:

pthread_create(&buffer.readThread, NULL, bufferRead, readParams);

Thread function shall take only 1 argument (void*):

void *bufferRead(void *arg)
{
    struct readThreadParams *params = arg;
    BoundedBuffer *buffer = params->b;
    char* data = params->data;
    int count = params->count;

    pthread_mutex_lock(&buffer->mutexBuffer);
    <snip>
    pthread_mutex_unlock(&buffer->mutexBuffer);

    return NULL;
}
2
votes

You got in the right way.

The function prototype should be like

void*  bufferRead(void *arg)
{
   ....
}

And typecast the argument to required type in thread function. Here, it should be

void*  bufferRead(void *arg)
{
     struct readThreadParams *input = (struct readThreadParams*)arg;
}

Passing more than one arguments to pthread function is not possible directly. so mostly formed as structure and passed to the function.

Refer this tutorial for more details on pthreads.

0
votes

This example borders on preprocessor abuse, but I like it because it demonstrates mimicking default argument values.

#include <assert.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef char *BoundedBuffer;

struct read_thread_param {  
    pthread_t thread;                                                 
    BoundedBuffer buffer;                                                        
    char* data;                                                             
    int count;                                                              
}; 

void buffer_read(BoundedBuffer* buffer, char* data, int count) {
    pthread_mutex_lock(&buffer->mutexBuffer);
    /*snip*/
    pthread_mutex_unlock(&buffer->mutexBuffer);
}

void *buffer_read_entrance(void *object) {
    struct read_thread_param *param = object;
    if (param->thread != 0) {
        buffer_read(&param->buffer, param->data, param->count);
        free(param);
        return NULL;
    }

    param = malloc(sizeof *param);

    /* TODO: Handle allocation error */
    assert(param != NULL);

    memcpy(param, object, sizeof *param);

    /* TODO: Handle thread creation error */
    assert(pthread_create(&param->thread, NULL, buffer_read_entrance, param) == 0);
    return NULL;
}

#define buffer_read_entrance(...) buffer_read_entrance(&(struct read_thread_param) { .thread = 0, __VA_ARGS__ })
void buffer_read(BoundedBuffer* buffer, char* data, int count);

int main(void) {
    buffer_read_entrance(.buffer = "hello world", .count = 42);
}