1
votes

I was trying to build a system (in Linux) with a simulated time execution, coordinating threads with semaphores, the idea was to make them all work cycles of one second, to have a accurate simulation the threads must wait for every other thread to end its execution, on the current simulated second.

I had a really annoying memory leak, because the main thread finished the work without joining the other threads (newbie mistake), so to solve it I added pthread_join() instructions for every thread, supposing they were finishing its work, since every thread execution was inside a while loop with a global condition to check, which was changed by the main thread before finishing. Sadly even with that, now, most of the executions of the program, it gets frozen waiting for the thread to finish.

I need to find a way to end every thread execution, even ignoring its full work, but making sure the join is made (and so the memory is freed).

Now I post a compilable code with the error, you can compile it with gcc -o sample -pthread sample.c -lm

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <pthread.h>
#include <semaphore.h>
#include <math.h>

#define SIZE 100
#define READY 12345
#define END 99
#define TIME 3600
int *advicer;
int  *td;
pthread_t *phones;
sem_t *counter;
int ready;
int end;

void cycle(int cycles){
    int i,j,k;
    for(i = 0; i < cycles; i++){
        k = 1;
        while(k){
            k = 0;
            for(j = 0; j < SIZE; j++){
                if(advicer[j] == 0){
                    k = 1;
                    break;
                }
            }
        }
        for(j = 0; j < SIZE; j++){
            advicer[j] = 0;
            sem_post(&counter[j]);
        }
    }
}

void *do_something(void *td){   
    int t;
    t = *((int *) td);
    while(end != END){
        if(end == END)
            break;
        t += t;
        advicer[t] = 1;
        sem_wait(&counter[t]);      
    }
    pthread_exit(NULL);
}

void all_free(){
    int i,j;
    end = END;
    printf("reach %d\n",end);
    for(i = 0; i < SIZE; i++)
        sem_post(&counter[i]);
    printf("reach2\n");
    for(i = 0; i < SIZE; i++){
        pthread_join(phones[i],NULL);
    }
    free(phones);   
    printf("reach3\n");
    for(i = 0; i < SIZE; i++)
        sem_destroy(&counter[i]);
    free(counter);
    free(td);
    free(advicer);
}

void main(){
    int i,my_count;
    counter = (sem_t *)malloc(sizeof(sem_t)*SIZE);
    advicer = (int *)malloc(sizeof(int)*SIZE);
    td = (int *)malloc(sizeof(int)*SIZE);
    phones = (pthread_t *)malloc(sizeof(pthread_t)*SIZE);
    for(i = 0; i < SIZE; i++){
        sem_init(&counter[i], 0, 0);
        advicer[i] = 0;
    }
    ready = READY;
    my_count = 0;
    end = 0;
    for(i = 0; i < SIZE; i++){
        td[i] = i;
        pthread_create(&(phones[i]), NULL, do_something, (void *)(&td[i]));
    }   
    printf("starting simulation\n");
    while(my_count < TIME){
        cycle(60);
        printf("hello\n");
        my_count += 60;
    }
    printf("simulation ended\n");
    all_free();
}
1
Code (as close to your actual code) is much better to communicate what the code is doing than a description. Small details matter a lot in threading. - Michael Burr
'I had a really annoying memory leak' - how did you know, and on what OS were you running? With most OS/crt, exiting the main thread causes the process to terminate, so killing all threads and releasing all memory. - Martin James
@MartinJames because I used valgrind to look for memory leaks and I found some, moreover, the posix threads' docs, says that if I don't do any join is expected to happen because pthreads by default are joinable, so they need the join to make a good free. BTW the OS is Linux. - Mig
@Mig - would it be possible to post a compilable example that demonstrates the problem? It looks like if you comment out the do_something() call it wouldn't take much to have a complete, compilable set of C source. I don't see a problem in the posted code, so it might be something that's not shown yet (of course, I might just be missing the problem that is there...) - Michael Burr
@MichaelBurr finally I was able to post a compilable version of the problem. - Mig

1 Answers

2
votes

The following section could lead to the lock:

for(i = 0; i < m->pp; i++)
  sem_post(&counter[i]);
for(i = 0; i < m->pp; i++){
 if(m->ppl[i] != NULL){
  phone_free(m->ppl[i]);
 }
 ...
}

You call sem_post() for one thread only (which unlocks it) then call pthread_join() (via phone_free()) for all threads, which would block for all but the one you called sem_post() for, as the others do not terminate, because stuck in sem_wait().

Although you called sem_post() during main execution (cycle()) the value might have been fetched by the thread prior to your call to the end-execution code (map_free()) pthread_join() (via phone_free()).

To cancel a thread you could use pthread_cancel().

To avoid the memory leak when having left a thread and not calling pthread_join() on it, you could detach the thread using pthread_detach(). The OS then frees all of the thread's resouce when the thread terminates.