Skip to content

Commit

Permalink
Normalize the C style of yarn.c.
Browse files Browse the repository at this point in the history
  • Loading branch information
madler committed May 9, 2018
1 parent 43752cc commit 5b86ed0
Showing 1 changed file with 85 additions and 99 deletions.
184 changes: 85 additions & 99 deletions yarn.c
Expand Up @@ -20,84 +20,81 @@
1.4 19 Jan 2015 Allow yarn_abort() to avoid error message to stderr
Accept and do nothing for NULL argument to free_lock()
1.5 8 May 2018 Remove destruct() to avoid use of pthread_cancel()
Normalize the code style
*/

/* for thread portability */
// For thread portability.
#define _XOPEN_SOURCE 700
#define _POSIX_C_SOURCE 200809L
#define _THREAD_SAFE

/* use large file functions if available */
// Use large file functions if available.
#define _FILE_OFFSET_BITS 64

/* external libraries and entities referenced */
#include <stdio.h> /* fprintf(), stderr */
#include <stdlib.h> /* exit(), malloc(), free(), NULL */
#include <pthread.h> /* pthread_t, pthread_create(), pthread_join(), */
/* pthread_attr_t, pthread_attr_init(), pthread_attr_destroy(),
PTHREAD_CREATE_JOINABLE, pthread_attr_setdetachstate(),
pthread_self(), pthread_equal(),
pthread_mutex_t, PTHREAD_MUTEX_INITIALIZER, pthread_mutex_init(),
pthread_mutex_lock(), pthread_mutex_unlock(), pthread_mutex_destroy(),
pthread_cond_t, PTHREAD_COND_INITIALIZER, pthread_cond_init(),
pthread_cond_broadcast(), pthread_cond_wait(), pthread_cond_destroy() */
#include <errno.h> /* ENOMEM, EAGAIN, EINVAL */

/* interface definition */
// External libraries and entities referenced.
#include <stdio.h> // fprintf(), stderr
#include <stdlib.h> // exit(), malloc(), free(), NULL
#include <pthread.h> // pthread_t, pthread_create(), pthread_join(),
// pthread_attr_t, pthread_attr_init(), pthread_attr_destroy(),
// PTHREAD_CREATE_JOINABLE, pthread_attr_setdetachstate(),
// pthread_self(), pthread_equal(),
// pthread_mutex_t, PTHREAD_MUTEX_INITIALIZER, pthread_mutex_init(),
// pthread_mutex_lock(), pthread_mutex_unlock(), pthread_mutex_destroy(),
// pthread_cond_t, PTHREAD_COND_INITIALIZER, pthread_cond_init(),
// pthread_cond_broadcast(), pthread_cond_wait(), pthread_cond_destroy()
#include <errno.h> // ENOMEM, EAGAIN, EINVAL

// Interface definition.
#include "yarn.h"

/* constants */
#define local static /* for non-exported functions and globals */
// Constants.
#define local static // for non-exported functions and globals

/* error handling external globals, resettable by application */
// Error handling external globals, resettable by application.
char *yarn_prefix = "yarn";
void (*yarn_abort)(int) = NULL;


/* immediately exit -- use for errors that shouldn't ever happen */
local void fail(int err)
{
// Immediately exit -- use for errors that shouldn't ever happen.
local void fail(int err) {
if (yarn_abort != NULL)
yarn_abort(err);
fprintf(stderr, "%s: %s (%d) -- aborting\n", yarn_prefix,
err == ENOMEM ? "out of memory" : "internal pthread error", err);
exit(err == ENOMEM || err == EAGAIN ? err : EINVAL);
}

/* memory handling routines provided by user -- if none are provided, malloc()
and free() are used, which are therefore assumed to be thread-safe */
// Memory handling routines provided by user. If none are provided, malloc()
// and free() are used, which are therefore assumed to be thread-safe.
typedef void *(*malloc_t)(size_t);
typedef void (*free_t)(void *);
local malloc_t my_malloc_f = malloc;
local free_t my_free = free;

/* use user-supplied allocation routines instead of malloc() and free() */
void yarn_mem(malloc_t lease, free_t vacate)
{
// Use user-supplied allocation routines instead of malloc() and free().
void yarn_mem(malloc_t lease, free_t vacate) {
my_malloc_f = lease;
my_free = vacate;
}

/* memory allocation that cannot fail (from the point of view of the caller) */
local void *my_malloc(size_t size)
{
// Memory allocation that cannot fail (from the point of view of the caller).
local void *my_malloc(size_t size) {
void *block;

if ((block = my_malloc_f(size)) == NULL)
fail(ENOMEM);
return block;
}

/* -- lock functions -- */
// -- Lock functions --

struct lock_s {
pthread_mutex_t mutex;
pthread_cond_t cond;
long value;
};

lock *new_lock(long initial)
{
lock *new_lock(long initial) {
int ret;
lock *bolt;

Expand All @@ -109,24 +106,21 @@ lock *new_lock(long initial)
return bolt;
}

void possess(lock *bolt)
{
void possess(lock *bolt) {
int ret;

if ((ret = pthread_mutex_lock(&(bolt->mutex))) != 0)
fail(ret);
}

void release(lock *bolt)
{
void release(lock *bolt) {
int ret;

if ((ret = pthread_mutex_unlock(&(bolt->mutex))) != 0)
fail(ret);
}

void twist(lock *bolt, enum twist_op op, long val)
{
void twist(lock *bolt, enum twist_op op, long val) {
int ret;

if (op == TO)
Expand All @@ -140,8 +134,7 @@ void twist(lock *bolt, enum twist_op op, long val)

#define until(a) while(!(a))

void wait_for(lock *bolt, enum wait_op op, long val)
{
void wait_for(lock *bolt, enum wait_op op, long val) {
int ret;

switch (op) {
Expand All @@ -167,13 +160,11 @@ void wait_for(lock *bolt, enum wait_op op, long val)
}
}

long peek_lock(lock *bolt)
{
long peek_lock(lock *bolt) {
return bolt->value;
}

void free_lock(lock *bolt)
{
void free_lock(lock *bolt) {
int ret;

if (bolt == NULL)
Expand All @@ -184,38 +175,37 @@ void free_lock(lock *bolt)
my_free(bolt);
}

/* -- thread functions (uses lock functions above) -- */
// -- Thread functions (uses the lock functions above) --

struct thread_s {
pthread_t id;
int done; /* true if this thread has exited */
thread *next; /* for list of all launched threads */
int done; // true if this thread has exited
thread *next; // for list of all launched threads
};

/* list of threads launched but not joined, count of threads exited but not
joined (incremented by ignition() just before exiting) */
// List of threads launched but not joined, count of threads exited but not
// joined (incremented by ignition() just before exiting).
local lock threads_lock = {
PTHREAD_MUTEX_INITIALIZER,
PTHREAD_COND_INITIALIZER,
0 /* number of threads exited but not joined */
0 // number of threads exited but not joined
};
local thread *threads = NULL; /* list of extant threads */
local thread *threads = NULL; // list of extant threads

/* structure in which to pass the probe and its payload to ignition() */
// Structure in which to pass the probe and its payload to ignition().
struct capsule {
void (*probe)(void *);
void *payload;
};

/* mark the calling thread as done and alert join_all() */
local void reenter(void *dummy)
{
// Mark the calling thread as done and alert join_all().
local void reenter(void *dummy) {
thread *match, **prior;
pthread_t me;

(void)dummy;

/* find this thread in the threads list by matching the thread id */
// find this thread in the threads list by matching the thread id
me = pthread_self();
possess(&(threads_lock));
prior = &(threads);
Expand All @@ -227,87 +217,84 @@ local void reenter(void *dummy)
if (match == NULL)
fail(EINVAL);

/* mark this thread as done and move it to the head of the list */
// mark this thread as done and move it to the head of the list
match->done = 1;
if (threads != match) {
*prior = match->next;
match->next = threads;
threads = match;
}

/* update the count of threads to be joined and alert join_all() */
// update the count of threads to be joined and alert join_all()
twist(&(threads_lock), BY, +1);
}

/* all threads go through this routine so that just before the thread exits,
it marks itself as done in the threads list and alerts join_all() so that
the thread resources can be released -- use cleanup stack so that the
marking occurs even if the thread is cancelled */
local void *ignition(void *arg)
{
// All threads go through this routine. Just before a thread exits, it marks
// itself as done in the threads list and alerts join_all() so that the thread
// resources can be released. Use a cleanup stack so that the marking occurs
// even if the thread is cancelled.
local void *ignition(void *arg) {
struct capsule *capsule = arg;

/* run reenter() before leaving */
// run reenter() before leaving
pthread_cleanup_push(reenter, NULL);

/* execute the requested function with argument */
// execute the requested function with argument
capsule->probe(capsule->payload);
my_free(capsule);

/* mark this thread as done and let join_all() know */
// mark this thread as done and let join_all() know
pthread_cleanup_pop(1);

/* exit thread */
// exit thread
return NULL;
}

/* not all POSIX implementations create threads as joinable by default, so that
is made explicit here */
thread *launch(void (*probe)(void *), void *payload)
{
// Not all POSIX implementations create threads as joinable by default, so that
// is made explicit here.
thread *launch(void (*probe)(void *), void *payload) {
int ret;
thread *th;
struct capsule *capsule;
pthread_attr_t attr;

/* construct the requested call and argument for the ignition() routine
(allocated instead of automatic so that we're sure this will still be
there when ignition() actually starts up -- ignition() will free this
allocation) */
// construct the requested call and argument for the ignition() routine
// (allocated instead of automatic so that we're sure this will still be
// there when ignition() actually starts up -- ignition() will free this
// allocation)
capsule = my_malloc(sizeof(struct capsule));
capsule->probe = probe;
capsule->payload = payload;

/* assure this thread is in the list before join_all() or ignition() looks
for it */
// assure this thread is in the list before join_all() or ignition() looks
// for it
possess(&(threads_lock));

/* create the thread and call ignition() from that thread */
// create the thread and call ignition() from that thread
th = my_malloc(sizeof(struct thread_s));
if ((ret = pthread_attr_init(&attr)) ||
(ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE)) ||
(ret = pthread_create(&(th->id), &attr, ignition, capsule)) ||
(ret = pthread_attr_destroy(&attr)))
fail(ret);

/* put the thread in the threads list for join_all() */
// put the thread in the threads list for join_all()
th->done = 0;
th->next = threads;
threads = th;
release(&(threads_lock));
return th;
}

void join(thread *ally)
{
void join(thread *ally) {
int ret;
thread *match, **prior;

/* wait for thread to exit and return its resources */
// wait for thread to exit and return its resources
if ((ret = pthread_join(ally->id, NULL)) != 0)
fail(ret);

/* find the thread in the threads list */
// find the thread in the threads list
possess(&(threads_lock));
prior = &(threads);
while ((match = *prior) != NULL) {
Expand All @@ -318,33 +305,32 @@ void join(thread *ally)
if (match == NULL)
fail(EINVAL);

/* remove thread from list and update exited count, free thread */
// remove thread from list and update exited count, free thread
if (match->done)
threads_lock.value--;
*prior = match->next;
release(&(threads_lock));
my_free(ally);
}

/* This implementation of join_all() only attempts to join threads that have
announced that they have exited (see ignition()). When there are many
threads, this is faster than waiting for some random thread to exit while a
bunch of other threads have already exited. */
int join_all(void)
{
// This implementation of join_all() only attempts to join threads that have
// announced that they have exited (see ignition()). When there are many
// threads, this is faster than waiting for some random thread to exit while a
// bunch of other threads have already exited.
int join_all(void) {
int ret, count;
thread *match, **prior;

/* grab the threads list and initialize the joined count */
// grab the threads list and initialize the joined count
count = 0;
possess(&(threads_lock));

/* do until threads list is empty */
// do until threads list is empty
while (threads != NULL) {
/* wait until at least one thread has reentered */
// wait until at least one thread has reentered
wait_for(&(threads_lock), NOT_TO_BE, 0);

/* find the first thread marked done (should be at or near the top) */
// find the first thread marked done (should be at or near the top)
prior = &(threads);
while ((match = *prior) != NULL) {
if (match->done)
Expand All @@ -354,8 +340,8 @@ int join_all(void)
if (match == NULL)
fail(EINVAL);

/* join the thread (will be almost immediate), remove from the threads
list, update the reenter count, and free the thread */
// join the thread (will be almost immediate), remove from the threads
// list, update the reenter count, and free the thread
if ((ret = pthread_join(match->id, NULL)) != 0)
fail(ret);
threads_lock.value--;
Expand All @@ -364,7 +350,7 @@ int join_all(void)
count++;
}

/* let go of the threads list and return the number of threads joined */
// let go of the threads list and return the number of threads joined
release(&(threads_lock));
return count;
}

0 comments on commit 5b86ed0

Please sign in to comment.