diff options
Diffstat (limited to 'src/wg.c')
-rw-r--r-- | src/wg.c | 86 |
1 files changed, 62 insertions, 24 deletions
@@ -26,37 +26,75 @@ /* pthread_cond_t is overly complicated. Just use a self-pipe. */ +static +void *wg_gc(void *wg_anon) { + struct wg *wg = wg_anon; + + pthread_t thread; + while (1) { + ssize_t r = read(wg->fd_threads[0], &thread, sizeof(thread)); + if (r < 0) { + if (errno == EINTR) + continue; + error(EXIT_FAILURE, errno, "wg_gc: read"); + } else if ((size_t)r < sizeof(thread)) { + error(EXIT_FAILURE, 0, "wg_gc: read: only read %zd/%zu", r, sizeof(thread)); + } + + pthread_join(thread, NULL); + + int p; + if ((p = pthread_mutex_lock(&wg->lock)) != 0) + error(EXIT_FAILURE, p, "wg_gc: pthread_mutex_lock"); + wg->count--; + if (wg->count == 0) { + if ((p = pthread_mutex_unlock(&wg->lock)) != 0) + error(EXIT_FAILURE, p, "wg_gc: pthread_mutex_unlock"); + return NULL; + } + if ((p = pthread_mutex_unlock(&wg->lock)) != 0) + error(EXIT_FAILURE, p, "wg_gc: pthread_mutex_unlock"); + } +} + void wg_init(struct wg *wg) { wg->count = 0; - pthread_mutex_init(&wg->lock, NULL); - int fds[2]; - if (pipe(fds) != 0) - error(EXIT_FAILURE, errno, "pipe"); - wg->fd_wait = fds[0]; - wg->fd_signal = fds[1]; + int r; + if ((r = pthread_mutex_init(&wg->lock, NULL)) != 0) + error(EXIT_FAILURE, r, "wg_init: pthread_mutex_init"); + if (pipe(wg->fd_threads) != 0) + error(EXIT_FAILURE, errno, "wg_init: pipe"); + if ((r = pthread_create(&wg->gc, NULL, wg_gc, (void*)wg)) != 0) + error(EXIT_FAILURE, r, "wg_init: pthread_create"); } -void wg_add(struct wg *wg, unsigned int n) { - pthread_mutex_lock(&wg->lock); - wg->count += n; - pthread_mutex_unlock(&wg->lock); +void wg_add(struct wg *wg) { + int r; + if ((r = pthread_mutex_lock(&wg->lock)) != 0) + error(EXIT_FAILURE, r, "wg_add: pthread_mutex_lock"); + wg->count++; + if ((r = pthread_mutex_unlock(&wg->lock)) != 0) + error(EXIT_FAILURE, r, "wg_add: pthread_mutex_unlock"); } -void wg_sub(struct wg *wg, unsigned int n) { - pthread_mutex_lock(&wg->lock); - wg->count -= n; - if (wg->count == 0) - if (write(wg->fd_signal, " ", 1) < 1) - error(EXIT_FAILURE, errno, "write"); - pthread_mutex_unlock(&wg->lock); +void wg_sub(struct wg *wg) { + pthread_t thread = pthread_self(); + ssize_t r = write(wg->fd_threads[1], &thread, sizeof(thread)); + if (r < 0) { + error(EXIT_FAILURE, errno, "wg_sub: write"); + } else if ((size_t)r < sizeof(thread)) { + error(EXIT_FAILURE, 0, "wg_sub: only wrote %zd/%zu", r, sizeof(thread)); + } } void wg_wait(struct wg *wg) { - char b; - retry: - if (read(wg->fd_wait, &b, 1) == -1) { - if (errno == EINTR) - goto retry; - error(EXIT_FAILURE, errno, "wg_wait"); - } + int r; + if ((r = pthread_join(wg->gc, NULL)) != 0) + error(EXIT_FAILURE, r, "wg_wait: pthread_join"); + if (close(wg->fd_threads[1]) != 0) + error(EXIT_FAILURE, errno, "wg_wait: close(fd_threads[1])"); + if (close(wg->fd_threads[0]) != 0) + error(EXIT_FAILURE, errno, "wg_wait: close(fd_threads[0])"); + if ((r = pthread_mutex_destroy(&wg->lock)) != 0) + error(EXIT_FAILURE, r, "wg_wait: pthread_mutex_destroy"); } |