Jeyhun Karimov Jeyhun Karimov - 1 month ago 19
C Question

C concurrent array allocation and free

I am using

array
with 2 threads. One is writing to it and another reading. You can assume that reading is slower than writing. The reader and writer are separate
pthreads
. As far as I know sharing an
array
as a global variable between those threads is safe.
So overall picture is like:

char ** array;

void writer(void){
for (unsigned long i = 0; i < maxArraySize; i++){
array[i] = malloc(someSize);
array[i] = someCharArray;
}

void reader(void){
for (unsigned long i = 0; i < maxArraySize; i++){
if(array[i] == NULL){ // in case reader is faster
i--;
continue;
}
useData(array[i]);
free(array[i]); // HERE IS MY QUESTION..
}


main(){
array = malloc(maxArraySize);
pthread_t reader, writer;
pthread_create( &reader, NULL, reader, NULL);
pthread_create( &writer, NULL, writer, NULL);
}


My question is related with line where I free i'th element of array. Is it safe to do it? Because when I free i'th element, at the same time, write is writing to array. So can there be a case that writer gets wrong address as it can lose the head pointer?

Answer

No it is not safe if you read during a write without a special instruction the result is undefined. You could get any value, though it is unlikely that you will see any other than NULL or the one you had assigned.

As others have mentioned in the comments the un-initialized array may contain anything (it is undefined) though it is likely zeroed before the kernel gave it to you.

If you want safety you need a locking mechanism such as a semaphore (http://man7.org/linux/man-pages/man3/sem_init.3.html).

char ** array;
// Allows access while non zero
sem_t sem;

void writer(void){
    for (unsigned long i = 0; i < maxArraySize; i++){
     array[i] = malloc(someSize);
     array[i] = someCharArray;
     // Increment semaphore.
     sem_post(&sem);
}

void reader(void){
    for (unsigned long i = 0; i < maxArraySize; i++){
     // Will return -1 if the semaphore is not at zero
     // Will return 0 if semaphore is greater than zero and decrement it.
     if(sem_trywait(&sem)){ // in case reader is faster
      i--;
      continue;
    }
    useData(array[i]);
    free(array[i]);      // HERE IS MY QUESTION..
}


main(){
    // Initialize semaphore to zero
    sem_init(&sem, 0 , 0);
    // Initialize array to have maxArraySize elements.
    array  = malloc(maxArraySize * sizeof(*array));   
    pthread_t reader, writer;
    pthread_create( &reader, NULL, reader, NULL);
    pthread_create( &writer, NULL, writer, NULL);
}

This should be fast but will spin your cpu doing a lot of nothing at the sem_trywait. Use sem_wait if you can wait a little longer and do not need the spinning.

I also corrected the bug in your malloc statement because it was not allocating enough space for maxArraySize char * items.

Comments