Project

General

Profile

Download (3.4 KB) Statistics
| Branch: | Tag: | Revision:
7e8045d8 David Sorber
/**
* Copyright © 2019, Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Universal Permissive License v 1.0 as shown at
* http://oss.oracle.com/licenses/upl
*
*/
#include "todo.h"
#include "malloc.h"
#include <assert.h>
#include <pthread.h>

struct todo_entry {
struct todo_item *item;
struct todo_entry *next;
};

struct todo_list{
pthread_mutex_t mutex;
pthread_cond_t cond;
struct todo_entry *entries;
size_t n_undone; // how many items haven't finished.
};

struct todo_list *
mk_todo_list(void) {
struct todo_list *MALLOC(tl);
pthread_mutex_init(&tl->mutex, NULL);
pthread_cond_init(&tl->cond, NULL);
tl->entries = NULL;
tl->n_undone = 0;
return tl;
}

struct todo_list *
todo_list_destroy(struct todo_list *tl) {
assert(tl->entries == NULL);
assert(tl->n_undone == 0);
pthread_mutex_destroy(&tl->mutex);
pthread_cond_destroy(&tl->cond);
FREE(tl);
return NULL;
}


void
todo_list_push(struct todo_list *tl,
struct todo_item *item) {
struct todo_entry *MALLOC(entry);
pthread_mutex_lock(&tl->mutex);
*entry = (struct todo_entry){item, tl->entries};
tl->entries = entry;
tl->n_undone++;
pthread_cond_signal(&tl->cond);
pthread_mutex_unlock(&tl->mutex);
}

static struct todo_item *
todo_list_pop(struct todo_list *tl) {
pthread_mutex_lock(&tl->mutex);
while (tl->n_undone > 0 && tl->entries == NULL) {
pthread_cond_wait(&tl->cond, &tl->mutex);
}
struct todo_item *result;
if (tl->entries) {
struct todo_entry *entry = tl->entries;
tl->entries = entry->next;
result = entry->item;
FREE(entry);
} else {
assert(tl->n_undone == 0);
result = NULL;
}
pthread_mutex_unlock(&tl->mutex);
return result;
}

static void
todo_list_decrement_undone(struct todo_list *tl) {
pthread_mutex_lock(&tl->mutex);
tl->n_undone--;
if (tl->n_undone == 0) {
pthread_cond_broadcast(&tl->cond);
}
pthread_mutex_unlock(&tl->mutex);
}

typedef const char *magic_t;
magic_t wpair_magic;

struct wpair {
magic_t *magic;
size_t threadnum;
struct todo_list *tl;
void (*work_on_item_function)(struct todo_list *, struct todo_item *);
};

static void* todo_worker(void *arg) {
struct wpair *p = arg;
assert(p->magic == &wpair_magic);
while (1) {
struct todo_item *item = todo_list_pop(p->tl);
if (item == NULL) break;
p->work_on_item_function(p->tl, item);
todo_list_decrement_undone(p->tl);
}
return arg;
}

void todo_list_run(struct todo_list *tl,
size_t n_threads,
struct todo_item *first_item,
void (*work_on_item_function)(struct todo_list *tl,
struct todo_item *)) {
todo_list_push(tl, first_item);
pthread_t *MALLOC_N(threads, n_threads);
struct wpair *MALLOC_N(wpairs, n_threads);
for (size_t i = 0; i < n_threads; i++) {
wpairs[i] = (struct wpair){&wpair_magic, i, tl, work_on_item_function};
pthread_create(&threads[i], NULL, todo_worker, &wpairs[i]);
}
for (size_t i = 0; i < n_threads; i++) {
void *retval;
pthread_join(threads[i], &retval);
assert(retval == &wpairs[i]);
}
assert(tl->entries == NULL);
assert(tl->n_undone == 0);
FREE(threads);
FREE(wpairs);
}