User Tools

Site Tools


,

Go-like waitgroup

Note: w/o check of return values.

waitgroup.c
/*
 * Copyright 2022 Oleg Borodin  <borodin@unix7.org>
 */
 
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
#include <pthread.h>
#include <stdatomic.h>
#include <stdio.h>
 
 
typedef struct {
    sem_t       sem;
    atomic_int  num;
} wg_t;
 
wg_t* new_gw() {
    wg_t* wg = malloc(sizeof(wg_t));
    wg->num = 0;
    sem_init(&(wg->sem), 1, 0);
    return wg;
}
 
void wg_add(wg_t* wg) {
    wg->num++;
}
 
void wg_done(wg_t* wg) {
    if ((--wg->num) == 0) sem_post(&(wg->sem));
}
 
void wg_wait(wg_t* wg) {
    if ((wg->num) < 1) sem_post(&(wg->sem));
    sem_wait(&(wg->sem));
}
 
void wg_init(wg_t* wg) {
    wg->num = 0;
    sem_init(&(wg->sem), 1, 0);
}
 
void wg_destroy(wg_t* wg) {
    sem_destroy(&(wg->sem));
}
 
void wg_free(wg_t* wg) {
    sem_destroy(&(wg->sem));
    free(wg);
}
 
typedef struct {
    int         ident;
    int         loops;
    wg_t*       wg;
    pthread_t   thread;
} worker_t;
 
 
worker_t* new_worker(wg_t* wg, int ident, int loops) {
    worker_t* worker = malloc(sizeof(worker_t));
    worker->ident   = ident;
    worker->wg      = wg;
    worker->loops   = loops;
    return worker;
}
 
void worker_init(worker_t* worker, wg_t* wg, int ident, int loops) {
    worker->ident   = ident;
    worker->wg      = wg;
    worker->loops   = loops;
}
 
void* worker_start(void* argp) {
    worker_t* worker = (worker_t*)argp;
    printf("worker %d start\n", worker->ident);
 
    for (int i = 0; i < worker->loops; i++) {
        sleep(1);
    }
    printf("worker %d done\n", worker->ident);
    wg_done(worker->wg);
    pthread_exit(NULL);
}
 
void worker_run(worker_t* worker) {
    pthread_create(&(worker->thread), NULL, worker_start, (void*)worker);
    pthread_detach(worker->thread);
}
 
void worker_destroy(worker_t* worker) {
    // NOP
}
 
void worker_free(worker_t* worker) {
    free(worker);
}
 
#define WORKERS 10
 
int main(int argc, char **argv) {
    srand(1);
    printf("main started\n");
 
    wg_t wg;
    wg_init(&wg);
 
    worker_t workers[WORKERS];
    for (int i = 0; i < WORKERS; i++) {
        worker_t* worker = &(workers[i]);
        int loops = rand() % 5 + 1;
        int ident = i + 1;
        worker_init(worker, &wg, ident, loops);
        wg_add(&wg);
        worker_run(worker);
    }
    printf("wg num = %d\n", wg.num);
    wg_wait(&wg);
    wg_destroy(&wg);
 
    for (int i = 0; i < WORKERS; i++) {
        worker_destroy(&(workers[i]));
    }
    printf("wg num = %d\n", wg.num);
    printf("main done\n");
    return 0;
}

Out

$ ./wg
main started
worker 1 start
worker 3 start
worker 2 start
worker 4 start
worker 5 start
worker 6 start
worker 7 start
worker 8 start
worker 9 start
worker 10 start
wg num = 10
worker 10 done
worker 4 done
worker 5 done
worker 2 done
worker 7 done
worker 1 done
worker 3 done
worker 8 done
worker 6 done
worker 9 done
wg num = 0
main done