summaryrefslogtreecommitdiffstats
path: root/flow/gsl/gslcommon.c
diff options
context:
space:
mode:
Diffstat (limited to 'flow/gsl/gslcommon.c')
-rw-r--r--flow/gsl/gslcommon.c1651
1 files changed, 1651 insertions, 0 deletions
diff --git a/flow/gsl/gslcommon.c b/flow/gsl/gslcommon.c
new file mode 100644
index 0000000..cb16b05
--- /dev/null
+++ b/flow/gsl/gslcommon.c
@@ -0,0 +1,1651 @@
+/* GSL - Generic Sound Layer
+ * Copyright (C) 2001 Tim Janik
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/utsname.h>
+#include <string.h>
+#include <sched.h>
+#include <errno.h>
+#include <sys/poll.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include "gslcommon.h"
+#include "gsldatacache.h"
+
+/* some systems don't have ERESTART (which is what linux returns for system
+ * calls on pipes which are being interrupted). most propably just use EINTR,
+ * and maybe some can return both. so we check for both in the below code,
+ * and alias ERESTART to EINTR if it's not present. compilers are supposed
+ * to catch and optimize the doubled check arising from this.
+ */
+#ifndef ERESTART
+#define ERESTART EINTR
+#endif
+
+
+#define PREALLOC (8)
+#define SIMPLE_CACHE_SIZE (64)
+#define TS8_SIZE (MAX (sizeof (GTrashStack), 8))
+#define DBG8_SIZE (MAX (sizeof (gsize), 8))
+
+
+/* --- variables --- */
+volatile guint64 gsl_externvar_tick_stamp = 0;
+static guint64 tick_stamp_system_time = 0;
+static guint global_tick_stamp_leaps = 0;
+static GslDebugFlags gsl_debug_flags = 0;
+
+
+/* --- memory allocation --- */
+static GslMutex global_memory = { 0, };
+static GTrashStack *simple_cache[SIMPLE_CACHE_SIZE] = { 0, 0, 0, /* ... */ };
+static gulong memory_allocated = 0;
+
+const guint
+gsl_alloc_upper_power2 (const gulong number)
+{
+ return number ? 1 << g_bit_storage (number - 1) : 0;
+}
+
+static inline gpointer
+low_alloc (gsize mem_size)
+{
+ gpointer mem;
+
+ if (mem_size >= TS8_SIZE && mem_size / 8 < SIMPLE_CACHE_SIZE)
+ {
+ guint cell;
+
+ mem_size = (mem_size + 7) & ~0x7;
+ cell = (mem_size >> 3) - 1;
+ GSL_SPIN_LOCK (&global_memory);
+ mem = g_trash_stack_pop (simple_cache + cell);
+ GSL_SPIN_UNLOCK (&global_memory);
+ if (!mem)
+ {
+ guint8 *cache_mem = g_malloc (mem_size * PREALLOC);
+ guint i;
+
+ GSL_SPIN_LOCK (&global_memory);
+ memory_allocated += mem_size * PREALLOC;
+ for (i = 0; i < PREALLOC - 1; i++)
+ {
+ g_trash_stack_push (simple_cache + cell, cache_mem);
+ cache_mem += mem_size;
+ }
+ GSL_SPIN_UNLOCK (&global_memory);
+ mem = cache_mem;
+ }
+ }
+ else
+ {
+ mem = g_malloc (mem_size);
+ GSL_SPIN_LOCK (&global_memory);
+ memory_allocated += mem_size;
+ GSL_SPIN_UNLOCK (&global_memory);
+ }
+ return mem;
+}
+
+static inline void
+low_free (gsize mem_size,
+ gpointer mem)
+{
+ if (mem_size >= TS8_SIZE && mem_size / 8 < SIMPLE_CACHE_SIZE)
+ {
+ guint cell;
+
+ mem_size = (mem_size + 7) & ~0x7;
+ cell = (mem_size >> 3) - 1;
+ GSL_SPIN_LOCK (&global_memory);
+ g_trash_stack_push (simple_cache + cell, mem);
+ GSL_SPIN_UNLOCK (&global_memory);
+ }
+ else
+ {
+ g_free (mem);
+ GSL_SPIN_LOCK (&global_memory);
+ memory_allocated -= mem_size;
+ GSL_SPIN_UNLOCK (&global_memory);
+ }
+}
+
+gpointer
+gsl_alloc_memblock (gsize block_size)
+{
+ guint8 *cmem;
+ gsize *debug_size;
+
+ g_return_val_if_fail (block_size >= sizeof (gpointer), NULL); /* cache-link size */
+
+ cmem = low_alloc (block_size + DBG8_SIZE);
+ debug_size = (gsize*) cmem;
+ *debug_size = block_size;
+ cmem += DBG8_SIZE;
+
+ return cmem;
+}
+
+void
+gsl_free_memblock (gsize block_size,
+ gpointer mem)
+{
+ gsize *debug_size;
+ guint8 *cmem;
+
+ g_return_if_fail (mem != NULL);
+
+ cmem = mem;
+ cmem -= DBG8_SIZE;
+ debug_size = (gsize*) cmem;
+ g_return_if_fail (block_size == *debug_size);
+
+ low_free (block_size + DBG8_SIZE, cmem);
+}
+
+void
+gsl_alloc_report (void)
+{
+ guint cell, cached = 0;
+
+ GSL_SPIN_LOCK (&global_memory);
+ for (cell = 0; cell < SIMPLE_CACHE_SIZE; cell++)
+ {
+ GTrashStack *trash = simple_cache[cell];
+ guint memsize, n = 0;
+
+ while (trash)
+ {
+ n++;
+ trash = trash->next;
+ }
+
+ if (n)
+ {
+ memsize = (cell + 1) << 3;
+ g_message ("cell %4u): %u bytes in %u nodes", memsize, memsize * n, n);
+ cached += memsize * n;
+ }
+ }
+ g_message ("%lu bytes allocated from system, %u bytes unused in cache", memory_allocated, cached);
+ GSL_SPIN_UNLOCK (&global_memory);
+}
+
+gpointer
+gsl_alloc_memblock0 (gsize block_size)
+{
+ gpointer mem = gsl_alloc_memblock (block_size);
+
+ memset (mem, 0, block_size);
+
+ return mem;
+}
+
+static void
+gsl_free_node_list (gpointer mem,
+ gsize node_size)
+{
+ struct { gpointer next, data; } *tmp, *node = mem;
+
+ g_return_if_fail (node != NULL);
+ g_return_if_fail (node_size >= 2 * sizeof (gpointer));
+
+ /* FIXME: this can be optimized to an O(1) operation with T-style links in mem-caches */
+ do
+ {
+ tmp = node->next;
+
+ gsl_free_memblock (node_size, node);
+ node = tmp;
+ }
+ while (node);
+}
+
+
+/* --- ring (circular-list) --- */
+static inline GslRing*
+gsl_ring_prepend_i (GslRing *head,
+ gpointer data)
+{
+ GslRing *ring = gsl_new_struct (GslRing, 1);
+
+ ring->data = data;
+ if (!head)
+ {
+ ring->prev = ring;
+ ring->next = ring;
+ }
+ else
+ {
+ ring->prev = head->prev;
+ ring->next = head;
+ head->prev->next = ring;
+ head->prev = ring;
+ }
+ return ring;
+}
+
+GslRing*
+gsl_ring_prepend (GslRing *head,
+ gpointer data)
+{
+ return gsl_ring_prepend_i (head, data);
+}
+
+GslRing*
+gsl_ring_prepend_uniq (GslRing *head,
+ gpointer data)
+{
+ GslRing *walk;
+
+ for (walk = head; walk; walk = gsl_ring_walk (head, walk))
+ if (walk->data == data)
+ return head;
+ return gsl_ring_prepend_i (head, data);
+}
+
+GslRing*
+gsl_ring_append (GslRing *head,
+ gpointer data)
+{
+ GslRing *ring;
+
+ ring = gsl_ring_prepend_i (head, data);
+
+ return head ? head : ring;
+}
+
+GslRing*
+gsl_ring_concat (GslRing *head1,
+ GslRing *head2)
+{
+ GslRing *tail1, *tail2;
+
+ if (!head1)
+ return head2;
+ if (!head2)
+ return head1;
+ tail1 = head1->prev;
+ tail2 = head2->prev;
+ head1->prev = tail2;
+ tail2->next = head1;
+ head2->prev = tail1;
+ tail1->next = head2;
+
+ return head1;
+}
+
+GslRing*
+gsl_ring_remove_node (GslRing *head,
+ GslRing *node)
+{
+ if (!head)
+ g_return_val_if_fail (head == NULL && node == NULL, NULL);
+ if (!head || !node)
+ return NULL;
+
+ /* special case one item ring */
+ if (head->prev == head)
+ {
+ g_return_val_if_fail (node == head, head);
+
+ gsl_delete_struct (GslRing, node);
+ return NULL;
+ }
+ g_return_val_if_fail (node != node->next, head); /* node can't be a one item ring here */
+
+ node->next->prev = node->prev;
+ node->prev->next = node->next;
+ if (head == node)
+ head = node->next;
+ gsl_delete_struct (GslRing, node);
+
+ return head;
+}
+
+GslRing*
+gsl_ring_remove (GslRing *head,
+ gpointer data)
+{
+ GslRing *walk;
+
+ if (!head)
+ return NULL;
+
+ /* make tail data removal an O(1) operation */
+ if (head->prev->data == data)
+ return gsl_ring_remove_node (head, head->prev);
+
+ for (walk = head; walk; walk = gsl_ring_walk (head, walk))
+ if (walk->data == data)
+ return gsl_ring_remove_node (head, walk);
+
+ g_warning (G_STRLOC ": couldn't find data item (%p) to remove from ring (%p)", data, head);
+
+ return head;
+}
+
+guint
+gsl_ring_length (GslRing *head)
+{
+ GslRing *ring;
+ guint i = 0;
+
+ for (ring = head; ring; ring = gsl_ring_walk (head, ring))
+ i++;
+
+ return i;
+}
+
+GslRing*
+gsl_ring_find (GslRing *head,
+ gconstpointer data)
+{
+ GslRing *ring;
+
+ for (ring = head; ring; ring = gsl_ring_walk (head, ring))
+ if (ring->data == (gpointer) data)
+ return ring;
+
+ return NULL;
+}
+
+GslRing*
+gsl_ring_nth (GslRing *head,
+ guint n)
+{
+ GslRing *ring = head;
+
+ while (n-- && ring)
+ ring = gsl_ring_walk (head, ring);
+
+ return ring;
+}
+
+gpointer
+gsl_ring_nth_data (GslRing *head,
+ guint n)
+{
+ GslRing *ring = head;
+
+ while (n-- && ring)
+ ring = gsl_ring_walk (head, ring);
+
+ return ring ? ring->data : ring;
+}
+
+void
+gsl_ring_free (GslRing *head)
+{
+ if (head)
+ {
+ head->prev->next = NULL;
+ gsl_free_node_list (head, sizeof (*head));
+ }
+}
+
+gpointer
+gsl_ring_pop_head (GslRing **head_p)
+{
+ gpointer data;
+
+ g_return_val_if_fail (head_p != NULL, NULL);
+
+ if (!*head_p)
+ return NULL;
+ data = (*head_p)->data;
+ *head_p = gsl_ring_remove_node (*head_p, *head_p);
+
+ return data;
+}
+
+gpointer
+gsl_ring_pop_tail (GslRing **head_p)
+{
+ gpointer data;
+
+ g_return_val_if_fail (head_p != NULL, NULL);
+
+ if (!*head_p)
+ return NULL;
+ data = (*head_p)->prev->data;
+ *head_p = gsl_ring_remove_node (*head_p, (*head_p)->prev);
+
+ return data;
+}
+
+GslRing*
+gsl_ring_insert_sorted (GslRing *head,
+ gpointer data,
+ GCompareFunc func)
+{
+ gint cmp;
+
+ g_return_val_if_fail (func != NULL, head);
+
+ if (!head)
+ return gsl_ring_prepend (head, data);
+
+ /* typedef gint (*GCompareFunc) (gconstpointer a,
+ * gconstpointer b);
+ */
+ cmp = func (data, head->data);
+
+ if (cmp >= 0) /* insert after head */
+ {
+ GslRing *tmp, *tail = head->prev;
+
+ /* make appending an O(1) operation */
+ if (head == tail || func (data, tail->data) >= 0)
+ return gsl_ring_append (head, data);
+
+ /* walk forward while data >= tmp (skipping equal nodes) */
+ for (tmp = head->next; tmp != tail; tmp = tmp->next)
+ if (func (data, tmp->data) < 0)
+ break;
+
+ /* insert before sibling which is greater than data */
+ gsl_ring_prepend (tmp, data); /* keep current head */
+ return head;
+ }
+ else /* cmp < 0 */
+ return gsl_ring_prepend (head, data);
+}
+
+
+/* --- GslThread --- */
+typedef struct
+{
+ GslThreadFunc func;
+ gpointer data;
+ gint wpipe[2];
+ volatile gint abort;
+ guint64 awake_stamp;
+ GslDebugFlags auxlog_reporter;
+ const gchar *auxlog_section;
+} ThreadData;
+static GslMutex global_thread = { 0, };
+static GslRing *global_thread_list = NULL;
+static GslCond global_thread_cond = { 0, };
+static GslRing *awake_tdata_list = NULL;
+static ThreadData *main_thread_tdata = NULL;
+static GslThread *main_thread = NULL;
+
+static inline ThreadData*
+thread_data_from_gsl_thread (GslThread *thread)
+{
+ GThread *gthread = (GThread*) thread;
+
+ /* if gthread->data==NULL, we assume this is the main thread */
+
+ return gthread->data ? gthread->data : main_thread_tdata;
+}
+
+static gpointer
+thread_wrapper (gpointer arg)
+{
+ GslThread *self = gsl_thread_self ();
+ ThreadData *tdata = arg;
+
+ g_assert (tdata == thread_data_from_gsl_thread (gsl_thread_self ()));
+
+ GSL_SYNC_LOCK (&global_thread);
+ global_thread_list = gsl_ring_prepend (global_thread_list, self);
+ gsl_cond_broadcast (&global_thread_cond);
+ GSL_SYNC_UNLOCK (&global_thread);
+
+ tdata->func (tdata->data);
+
+ GSL_SYNC_LOCK (&global_thread);
+ global_thread_list = gsl_ring_remove (global_thread_list, self);
+ if (tdata->awake_stamp)
+ awake_tdata_list = gsl_ring_remove (awake_tdata_list, tdata);
+ gsl_cond_broadcast (&global_thread_cond);
+ GSL_SYNC_UNLOCK (&global_thread);
+
+ close (tdata->wpipe[0]);
+ tdata->wpipe[0] = -1;
+ close (tdata->wpipe[1]);
+ tdata->wpipe[1] = -1;
+ gsl_delete_struct (ThreadData, tdata);
+
+ return NULL;
+}
+
+static ThreadData*
+create_tdata (void)
+{
+ ThreadData *tdata;
+ glong d_long;
+ gint error;
+
+ tdata = gsl_new_struct0 (ThreadData, 1);
+ tdata->func = NULL;
+ tdata->data = NULL;
+ tdata->wpipe[0] = -1;
+ tdata->wpipe[1] = -1;
+ tdata->abort = FALSE;
+ tdata->auxlog_reporter = 0;
+ tdata->auxlog_section = NULL;
+ error = pipe (tdata->wpipe);
+ if (error == 0)
+ {
+ d_long = fcntl (tdata->wpipe[0], F_GETFL, 0);
+ /* g_printerr ("pipe-readfd, blocking=%ld\n", d_long & O_NONBLOCK); */
+ d_long |= O_NONBLOCK;
+ error = fcntl (tdata->wpipe[0], F_SETFL, d_long);
+ }
+ if (error == 0)
+ {
+ d_long = fcntl (tdata->wpipe[1], F_GETFL, 0);
+ /* g_printerr ("pipe-writefd, blocking=%ld\n", d_long & O_NONBLOCK); */
+ d_long |= O_NONBLOCK;
+ error = fcntl (tdata->wpipe[1], F_SETFL, d_long);
+ }
+ if (error)
+ {
+ close (tdata->wpipe[0]);
+ close (tdata->wpipe[1]);
+ gsl_delete_struct (ThreadData, tdata);
+ tdata = NULL;
+ }
+ return tdata;
+}
+
+GslThread*
+gsl_thread_new (GslThreadFunc func,
+ gpointer user_data)
+{
+ gpointer gthread = NULL;
+ ThreadData *tdata;
+ GError *gerror = NULL;
+
+ g_return_val_if_fail (func != NULL, FALSE);
+
+ tdata = create_tdata ();
+
+ if (tdata)
+ {
+ const gboolean joinable = FALSE;
+
+ /* don't dare setting joinable to TRUE, that prevents the thread's
+ * resources from being freed, since we don't offer pthread_join().
+ * so we'd just rn out of stack at some point.
+ */
+ tdata->func = func;
+ tdata->data = user_data;
+ gthread = g_thread_create_full (thread_wrapper, tdata, 0, joinable, FALSE,
+ G_THREAD_PRIORITY_NORMAL, &gerror);
+ }
+
+ if (gthread)
+ {
+ GSL_SYNC_LOCK (&global_thread);
+ while (!gsl_ring_find (global_thread_list, gthread))
+ gsl_cond_wait (&global_thread_cond, &global_thread);
+ GSL_SYNC_UNLOCK (&global_thread);
+ }
+ else
+ {
+ if (tdata)
+ {
+ close (tdata->wpipe[0]);
+ close (tdata->wpipe[1]);
+ gsl_delete_struct (ThreadData, tdata);
+ }
+ g_warning ("Failed to create thread: %s", gerror->message);
+ g_error_free (gerror);
+ }
+
+ return gthread;
+}
+
+GslThread*
+gsl_thread_self (void)
+{
+ gpointer gthread = g_thread_self ();
+
+ if (!gthread)
+ g_error ("gsl_thread_self() failed");
+
+ return gthread;
+}
+
+GslThread*
+gsl_thread_main (void)
+{
+ return main_thread;
+}
+
+guint
+gsl_threads_get_count (void)
+{
+ guint count;
+
+ GSL_SYNC_LOCK (&global_thread);
+ count = gsl_ring_length (global_thread_list);
+ GSL_SYNC_UNLOCK (&global_thread);
+
+ return count;
+}
+
+static void
+thread_wakeup_I (ThreadData *tdata)
+{
+ guint8 data = 'W';
+ gint r;
+
+ do
+ r = write (tdata->wpipe[1], &data, 1);
+ while (r < 0 && (errno == EINTR || errno == ERESTART));
+}
+
+/**
+ * gsl_thread_wakeup
+ * @thread: thread to wake up
+ * Wake up a currently sleeping thread. In practice, this
+ * function simply causes the next call to gsl_thread_sleep()
+ * within @thread to last for 0 seconds.
+ */
+void
+gsl_thread_wakeup (GslThread *thread)
+{
+ ThreadData *tdata;
+
+ g_return_if_fail (thread != NULL);
+
+ GSL_SYNC_LOCK (&global_thread);
+ g_assert (gsl_ring_find (global_thread_list, thread));
+ GSL_SYNC_UNLOCK (&global_thread);
+
+ tdata = thread_data_from_gsl_thread (thread);
+ thread_wakeup_I (tdata);
+}
+
+/**
+ * gsl_thread_abort
+ * @thread: thread to abort
+ * Abort a currently running thread. This function does not
+ * return until the thread in question terminated execution.
+ * Note that the thread handle gets invalidated with invocation
+ * of gsl_thread_abort() or gsl_thread_queue_abort().
+ */
+void
+gsl_thread_abort (GslThread *thread)
+{
+ ThreadData *tdata;
+
+ g_return_if_fail (thread != NULL);
+ g_return_if_fail (thread != main_thread);
+
+ GSL_SYNC_LOCK (&global_thread);
+ g_assert (gsl_ring_find (global_thread_list, thread));
+ GSL_SYNC_UNLOCK (&global_thread);
+
+ tdata = thread_data_from_gsl_thread (thread);
+
+ GSL_SYNC_LOCK (&global_thread);
+ tdata->abort = TRUE;
+ thread_wakeup_I (tdata);
+
+ while (gsl_ring_find (global_thread_list, thread))
+ gsl_cond_wait (&global_thread_cond, &global_thread);
+ GSL_SYNC_UNLOCK (&global_thread);
+}
+
+/**
+ * gsl_thread_queue_abort
+ * @thread: thread to abort
+ * Same as gsl_thread_abort(), but returns as soon as possible,
+ * even if thread hasn't stopped execution yet.
+ * Note that the thread handle gets invalidated with invocation
+ * of gsl_thread_abort() or gsl_thread_queue_abort().
+ */
+void
+gsl_thread_queue_abort (GslThread *thread)
+{
+ ThreadData *tdata;
+
+ g_return_if_fail (thread != NULL);
+ g_return_if_fail (thread != main_thread);
+
+ GSL_SYNC_LOCK (&global_thread);
+ g_assert (gsl_ring_find (global_thread_list, thread));
+ GSL_SYNC_UNLOCK (&global_thread);
+
+ tdata = thread_data_from_gsl_thread (thread);
+
+ GSL_SYNC_LOCK (&global_thread);
+ tdata->abort = TRUE;
+ thread_wakeup_I (tdata);
+ GSL_SYNC_UNLOCK (&global_thread);
+}
+
+/**
+ * gsl_thread_aborted
+ * @returns: %TRUE if the thread should abort execution
+ * Find out if the currently running thread should be aborted (the thread is
+ * supposed to return from its main thread function).
+ */
+gboolean
+gsl_thread_aborted (void)
+{
+ ThreadData *tdata = thread_data_from_gsl_thread (gsl_thread_self ());
+ gboolean aborted;
+
+ GSL_SYNC_LOCK (&global_thread);
+ aborted = tdata->abort != FALSE;
+ GSL_SYNC_UNLOCK (&global_thread);
+
+ return aborted;
+}
+
+/**
+ * gsl_thread_sleep
+ * @max_msec: maximum amount of milli seconds to sleep (-1 for infinite time)
+ * @returns: %TRUE if the thread should continue execution
+ * Sleep for the amount of time given. This function may get interrupted
+ * by wakeup or abort requests, it returns whether the thread is supposed
+ * to continue execution after waking up. This function also processes
+ * remaining data from the thread's poll fd.
+ */
+gboolean
+gsl_thread_sleep (glong max_msec)
+{
+ ThreadData *tdata = thread_data_from_gsl_thread (gsl_thread_self ());
+ struct pollfd pfd;
+ gint r, aborted;
+
+ pfd.fd = tdata->wpipe[0];
+ pfd.events = G_IO_IN;
+ pfd.revents = 0;
+
+ r = poll (&pfd, 1, max_msec);
+
+ if (r < 0 && errno != EINTR)
+ g_message (G_STRLOC ": poll() error: %s\n", g_strerror (errno));
+ else if (pfd.revents & G_IO_IN)
+ {
+ guint8 data[64];
+
+ do
+ r = read (tdata->wpipe[0], data, sizeof (data));
+ while ((r < 0 && (errno == EINTR || errno == ERESTART)) || r == sizeof (data));
+ }
+
+ GSL_SYNC_LOCK (&global_thread);
+ aborted = tdata->abort != FALSE;
+ GSL_SYNC_UNLOCK (&global_thread);
+
+ return !aborted;
+}
+
+/**
+ * gsl_thread_awake_after
+ * RETURNS: GPollFD for the current thread
+ * Get the GPollfd for the current thread which is used
+ * to signal thread wakeups (e.g. due to
+ * gsl_thread_abort() or gsl_thread_wakeup()).
+ */
+void
+gsl_thread_get_pollfd (GPollFD *pfd)
+{
+ ThreadData *tdata = thread_data_from_gsl_thread (gsl_thread_self ());
+
+ pfd->fd = tdata->wpipe[0];
+ pfd->events = G_IO_IN;
+ pfd->revents = 0;
+}
+
+/**
+ * gsl_thread_awake_after
+ * @tick_stamp: tick stamp update to trigger wakeup
+ * Wakeup the currently running thread after the global tick stamp
+ * (see gsl_tick_stamp()) has been updated to @tick_stamp.
+ * (If the moment of wakeup has already passed by, the thread is
+ * woken up at the next global tick stamp update.)
+ */
+void
+gsl_thread_awake_after (guint64 tick_stamp)
+{
+ ThreadData *tdata = thread_data_from_gsl_thread (gsl_thread_self ());
+
+ g_return_if_fail (tick_stamp > 0);
+
+ GSL_SYNC_LOCK (&global_thread);
+ if (!tdata->awake_stamp)
+ {
+ awake_tdata_list = gsl_ring_prepend (awake_tdata_list, tdata);
+ tdata->awake_stamp = tick_stamp;
+ }
+ else
+ tdata->awake_stamp = MIN (tdata->awake_stamp, tick_stamp);
+ GSL_SYNC_UNLOCK (&global_thread);
+}
+
+/**
+ * gsl_thread_awake_before
+ * @tick_stamp: tick stamp update to trigger wakeup
+ * Wakeup the currently running thread upon the last global tick stamp
+ * update (see gsl_tick_stamp()) that happens prior to updating the
+ * global tick stamp to @tick_stamp.
+ * (If the moment of wakeup has already passed by, the thread is
+ * woken up at the next global tick stamp update.)
+ */
+void
+gsl_thread_awake_before (guint64 tick_stamp)
+{
+ g_return_if_fail (tick_stamp > 0);
+
+ if (tick_stamp > global_tick_stamp_leaps)
+ gsl_thread_awake_after (tick_stamp - global_tick_stamp_leaps);
+ else
+ gsl_thread_awake_after (tick_stamp);
+}
+
+/**
+ * gsl_tick_stamp
+ * @RETURNS: GSL's execution tick stamp as unsigned 64bit integer
+ *
+ * Retrive the GSL global tick stamp.
+ * GSL increments its global tick stamp at certain intervals,
+ * by specific amounts (refer to gsl_engine_init() for further
+ * details). The tick stamp is a non-wrapping, unsigned 64bit
+ * integer greater than 0. Threads can schedule sleep interruptions
+ * at certain tick stamps with gsl_thread_awake_after() and
+ * gsl_thread_awake_before(). Tick stamp updating occours at
+ * GSL engine block processing boundaries, so code that can
+ * guarantee to not run across those boundaries (for instance
+ * GslProcessFunc() functions) may use the macro %GSL_TICK_STAMP
+ * to retrive the current tick in a faster manner (not involving
+ * mutex locking). See also gsl_module_tick_stamp().
+ * This function is MT-safe and may be called from any thread.
+ */
+guint64
+gsl_tick_stamp (void)
+{
+ guint64 stamp;
+
+ GSL_SYNC_LOCK (&global_thread);
+ stamp = gsl_externvar_tick_stamp;
+ GSL_SYNC_UNLOCK (&global_thread);
+
+ return stamp;
+}
+
+void
+_gsl_tick_stamp_set_leap (guint ticks)
+{
+ GSL_SYNC_LOCK (&global_thread);
+ global_tick_stamp_leaps = ticks;
+ GSL_SYNC_UNLOCK (&global_thread);
+}
+
+/**
+ * gsl_time_system
+ * @RETURNS: Current system time in micro seconds
+ *
+ * Get the current system time in micro seconds.
+ * Subsequent calls to this function do not necessarily
+ * return growing values. In fact, a second call may return
+ * a value smaller than the first call under certainsystem
+ * conditions.
+ * This function is MT-safe and may be called from any thread.
+ */
+guint64
+gsl_time_system (void)
+{
+ struct timeval tv;
+ guint64 csys_time;
+ gint error;
+
+ error = gettimeofday (&tv, NULL);
+ if (error)
+ g_error ("gettimeofday() failed: %s", g_strerror (errno));
+ csys_time = tv.tv_sec;
+ csys_time = csys_time * 1000000 + tv.tv_usec;
+
+ return csys_time;
+}
+
+/**
+ * gsl_tick_stamp_last
+ * @RETURNS: Current tick stamp and system time in micro seconds
+ *
+ * Get the system time of the last GSL global tick stamp update.
+ * This function is MT-safe and may be called from any thread.
+ */
+GslTickStampUpdate
+gsl_tick_stamp_last (void)
+{
+ GslTickStampUpdate ustamp;
+
+ GSL_SYNC_LOCK (&global_thread);
+ ustamp.tick_stamp = gsl_externvar_tick_stamp;
+ ustamp.system_time = tick_stamp_system_time;
+ GSL_SYNC_UNLOCK (&global_thread);
+
+ return ustamp;
+}
+
+void
+_gsl_tick_stamp_inc (void)
+{
+ volatile guint64 newstamp;
+ GslRing *ring;
+ guint64 systime;
+
+ g_return_if_fail (global_tick_stamp_leaps > 0);
+
+ systime = gsl_time_system ();
+ newstamp = gsl_externvar_tick_stamp + global_tick_stamp_leaps;
+
+ GSL_SYNC_LOCK (&global_thread);
+ gsl_externvar_tick_stamp = newstamp;
+ tick_stamp_system_time = systime;
+ for (ring = awake_tdata_list; ring; )
+ {
+ ThreadData *tdata = ring->data;
+
+ if (tdata->awake_stamp <= GSL_TICK_STAMP)
+ {
+ GslRing *next = gsl_ring_walk (awake_tdata_list, ring);
+
+ tdata->awake_stamp = 0;
+ awake_tdata_list = gsl_ring_remove (awake_tdata_list, tdata);
+
+ thread_wakeup_I (tdata);
+ ring = next;
+ }
+ else
+ ring = gsl_ring_walk (awake_tdata_list, ring);
+ }
+ GSL_SYNC_UNLOCK (&global_thread);
+}
+
+
+/* --- GslMutex --- */
+static gboolean is_smp_system = FALSE;
+
+static void
+default_mutex_init (GslMutex *mutex)
+{
+ g_return_if_fail (mutex != NULL);
+
+ mutex->mutex_pointer = g_mutex_new ();
+}
+
+static int
+default_mutex_trylock (GslMutex *mutex)
+{
+ return g_mutex_trylock (mutex->mutex_pointer) ? 0 : -1;
+}
+
+static void
+default_mutex_lock (GslMutex *mutex)
+{
+ /* spin locks should be held only very short times,
+ * so frequently we should succeed here
+ */
+ if (g_mutex_trylock (mutex->mutex_pointer))
+ return;
+
+ if (!is_smp_system)
+ {
+ /* on uni processor systems, there's no point in busy spinning */
+ do
+ {
+#if defined(_POSIX_PRIORITY_SCHEDULING)
+ sched_yield ();
+#endif
+ if (g_mutex_trylock (mutex->mutex_pointer))
+ return;
+ }
+ while (TRUE);
+ }
+ else
+ {
+ /* for multi processor systems, mutex_lock() is hopefully implemented
+ * via spinning. note that we can't implement spinning ourselves with
+ * mutex_trylock(), since on some architectures that'd block memory
+ * bandwith due to constant bus locks
+ */
+ g_mutex_lock (mutex->mutex_pointer);
+ }
+}
+
+static void
+default_mutex_unlock (GslMutex *mutex)
+{
+ g_mutex_unlock (mutex->mutex_pointer);
+}
+
+static void
+default_mutex_destroy (GslMutex *mutex)
+{
+ g_mutex_free (mutex->mutex_pointer);
+ memset (mutex, 0, sizeof (*mutex));
+}
+
+static void
+default_rec_mutex_init (GslRecMutex *rec_mutex)
+{
+ rec_mutex->depth = 0;
+ rec_mutex->owner = NULL;
+ gsl_mutex_init (&rec_mutex->sync_mutex);
+}
+
+static int
+default_rec_mutex_trylock (GslRecMutex *rec_mutex)
+{
+ gpointer self = gsl_thread_self ();
+
+ if (rec_mutex->owner == self)
+ {
+ g_assert (rec_mutex->depth > 0); /* paranoid */
+ rec_mutex->depth += 1;
+ return 0;
+ }
+ else
+ {
+ if (gsl_mutex_trylock (&rec_mutex->sync_mutex))
+ {
+ g_assert (rec_mutex->owner == NULL && rec_mutex->depth == 0); /* paranoid */
+ rec_mutex->owner = self;
+ rec_mutex->depth = 1;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static void
+default_rec_mutex_lock (GslRecMutex *rec_mutex)
+{
+ gpointer self = gsl_thread_self ();
+
+ if (rec_mutex->owner == self)
+ {
+ g_assert (rec_mutex->depth > 0); /* paranoid */
+ rec_mutex->depth += 1;
+ }
+ else
+ {
+ GSL_SYNC_LOCK (&rec_mutex->sync_mutex);
+ g_assert (rec_mutex->owner == NULL && rec_mutex->depth == 0); /* paranoid */
+ rec_mutex->owner = self;
+ rec_mutex->depth = 1;
+ }
+}
+
+static void
+default_rec_mutex_unlock (GslRecMutex *rec_mutex)
+{
+ gpointer self = gsl_thread_self ();
+
+ if (rec_mutex->owner == self && rec_mutex->depth > 0)
+ {
+ rec_mutex->depth -= 1;
+ if (!rec_mutex->depth)
+ {
+ rec_mutex->owner = NULL;
+ GSL_SYNC_UNLOCK (&rec_mutex->sync_mutex);
+ }
+ }
+ else
+ g_warning ("unable to unlock recursive mutex with self %p != %p or depth %u < 1",
+ rec_mutex->owner, self, rec_mutex->depth);
+}
+
+static void
+default_rec_mutex_destroy (GslRecMutex *rec_mutex)
+{
+ if (rec_mutex->owner || rec_mutex->depth)
+ {
+ g_warning (G_STRLOC ": recursive mutex still locked during destruction");
+ return;
+ }
+ gsl_mutex_destroy (&rec_mutex->sync_mutex);
+ g_assert (rec_mutex->owner == NULL && rec_mutex->depth == 0);
+}
+
+static void
+default_cond_init (GslCond *cond)
+{
+ cond->cond_pointer = g_cond_new ();
+}
+
+static void
+default_cond_wait (GslCond *cond,
+ GslMutex *mutex)
+{
+ /* infinite wait */
+ g_cond_wait (cond->cond_pointer, mutex->mutex_pointer);
+}
+
+static void
+default_cond_signal (GslCond *cond)
+{
+ g_cond_signal (cond->cond_pointer);
+}
+
+static void
+default_cond_broadcast (GslCond *cond)
+{
+ g_cond_broadcast (cond->cond_pointer);
+}
+
+static void
+default_cond_destroy (GslCond *cond)
+{
+ g_cond_free (cond->cond_pointer);
+}
+
+static void
+default_cond_wait_timed (GslCond *cond,
+ GslMutex *mutex,
+ gulong abs_secs,
+ gulong abs_usecs)
+{
+ GTimeVal gtime;
+
+ gtime.tv_sec = abs_secs;
+ gtime.tv_usec = abs_usecs;
+ g_cond_timed_wait (cond->cond_pointer, mutex->mutex_pointer, &gtime);
+}
+
+GslMutexTable gsl_mutex_table = {
+ default_mutex_init,
+ default_mutex_lock,
+ default_mutex_trylock,
+ default_mutex_unlock,
+ default_mutex_destroy,
+ default_rec_mutex_init,
+ default_rec_mutex_lock,
+ default_rec_mutex_trylock,
+ default_rec_mutex_unlock,
+ default_rec_mutex_destroy,
+ default_cond_init,
+ default_cond_signal,
+ default_cond_broadcast,
+ default_cond_wait,
+ default_cond_wait_timed,
+ default_cond_destroy,
+};
+
+void
+gsl_cond_wait_timed (GslCond *cond,
+ GslMutex *mutex,
+ glong max_useconds)
+{
+ if (max_useconds < 0)
+ gsl_cond_wait (cond, mutex);
+ else
+ {
+ struct timeval now;
+ glong secs;
+
+ gettimeofday (&now, NULL);
+ secs = max_useconds / 1000000;
+ now.tv_sec += secs;
+ max_useconds -= secs * 1000000;
+ now.tv_usec += max_useconds;
+ if (now.tv_usec >= 1000000)
+ {
+ now.tv_usec -= 1000000;
+ now.tv_sec += 1;
+ }
+
+ /* linux on x86 with pthread has actually 10ms resolution */
+ gsl_mutex_table.cond_wait_timed (cond, mutex, now.tv_sec, now.tv_usec);
+ }
+}
+
+
+/* --- GslMessage --- */
+const gchar*
+gsl_strerror (GslErrorType error)
+{
+ switch (error)
+ {
+ case GSL_ERROR_NONE: return "Everything went well";
+ case GSL_ERROR_INTERNAL: return "Internal error (please report)";
+ case GSL_ERROR_UNKNOWN: return "Unknown error";
+ case GSL_ERROR_IO: return "I/O error";
+ case GSL_ERROR_PERMS: return "Insufficient permission";
+ case GSL_ERROR_BUSY: return "Resource currently busy";
+ case GSL_ERROR_EXISTS: return "Resource exists already";
+ case GSL_ERROR_TEMP: return "Temporary error";
+ case GSL_ERROR_EOF: return "File empty or premature EOF";
+ case GSL_ERROR_NOT_FOUND: return "Resource not found";
+ case GSL_ERROR_OPEN_FAILED: return "Open failed";
+ case GSL_ERROR_SEEK_FAILED: return "Seek failed";
+ case GSL_ERROR_READ_FAILED: return "Read failed";
+ case GSL_ERROR_WRITE_FAILED: return "Write failed";
+ case GSL_ERROR_FORMAT_INVALID: return "Invalid format";
+ case GSL_ERROR_FORMAT_UNKNOWN: return "Unknown format";
+ case GSL_ERROR_DATA_CORRUPT: return "Data corrupt";
+ case GSL_ERROR_CONTENT_GLITCH: return "Data glitch (junk) detected";
+ case GSL_ERROR_NO_RESOURCE: return "Out of memory, disk space or similar resource";
+ case GSL_ERROR_CODEC_FAILURE: return "CODEC failure";
+ default: return NULL;
+ }
+}
+
+static const GDebugKey gsl_static_debug_keys[] = {
+ { "notify", GSL_MSG_NOTIFY },
+ { "dcache", GSL_MSG_DATA_CACHE },
+ { "dhandle", GSL_MSG_DATA_HANDLE },
+ { "loader", GSL_MSG_LOADER },
+ { "osc", GSL_MSG_OSC },
+ { "engine", GSL_MSG_ENGINE },
+ { "jobs", GSL_MSG_JOBS },
+ { "fjobs", GSL_MSG_FJOBS },
+ { "sched", GSL_MSG_SCHED },
+ { "master", GSL_MSG_MASTER },
+ { "slave", GSL_MSG_SLAVE },
+};
+
+static const gchar*
+reporter_name (GslDebugFlags reporter)
+{
+ switch (reporter)
+ {
+ case GSL_MSG_NOTIFY: return "Notify";
+ case GSL_MSG_DATA_CACHE: return "DataCache";
+ case GSL_MSG_DATA_HANDLE: return "DataHandle";
+ case GSL_MSG_LOADER: return "Loader";
+ case GSL_MSG_OSC: return "Oscillator";
+ case GSL_MSG_ENGINE: return "Engine"; /* Engine */
+ case GSL_MSG_JOBS: return "Jobs"; /* Engine */
+ case GSL_MSG_FJOBS: return "FlowJobs"; /* Engine */
+ case GSL_MSG_SCHED: return "Sched"; /* Engine */
+ case GSL_MSG_MASTER: return "Master"; /* Engine */
+ case GSL_MSG_SLAVE: return "Slave"; /* Engine */
+ default: return "Custom";
+ }
+}
+
+const GDebugKey *gsl_debug_keys = gsl_static_debug_keys;
+const guint gsl_n_debug_keys = G_N_ELEMENTS (gsl_static_debug_keys);
+
+void
+gsl_message_send (GslDebugFlags reporter,
+ const gchar *section,
+ GslErrorType error,
+ const gchar *messagef,
+ ...)
+{
+ struct {
+ GslDebugFlags reporter;
+ gchar reporter_name[64];
+ gchar section[64]; /* auxillary information about reporter code portion */
+ GslErrorType error;
+ const gchar *error_str; /* gsl_strerror() of error */
+ gchar message[1024];
+ } tmsg, *msg = &tmsg;
+ gchar *string;
+ va_list args;
+
+ g_return_if_fail (messagef != NULL);
+
+ /* create message */
+ memset (msg, 0, sizeof (*msg));
+ msg->reporter = reporter;
+ strncpy (msg->reporter_name, reporter_name (msg->reporter), 63);
+ if (section)
+ strncpy (msg->section, section, 63);
+ msg->error = error;
+ msg->error_str = error ? gsl_strerror (msg->error) : NULL;
+
+ /* vsnprintf() replacement */
+ va_start (args, messagef);
+ string = g_strdup_vprintf (messagef, args);
+ va_end (args);
+ strncpy (msg->message, string, 1023);
+ g_free (string);
+
+ /* in current lack of a decent message queue, puke the message to stderr */
+ g_printerr ("GSL-%s%s%s: %s%s%s\n",
+ msg->reporter_name,
+ msg->section ? ":" : "",
+ msg->section ? msg->section : "",
+ msg->message,
+ msg->error_str ? ": " : "",
+ msg->error_str ? msg->error_str : "");
+}
+
+void
+gsl_debug_enable (GslDebugFlags dbg_flags)
+{
+ gsl_debug_flags |= dbg_flags;
+}
+
+void
+gsl_debug_disable (GslDebugFlags dbg_flags)
+{
+ gsl_debug_flags &= dbg_flags;
+}
+
+gboolean
+gsl_debug_check (GslDebugFlags dbg_flags)
+{
+ return (gsl_debug_flags & dbg_flags) != 0;
+}
+
+void
+gsl_debug (GslDebugFlags reporter,
+ const gchar *section,
+ const gchar *format,
+ ...)
+{
+ g_return_if_fail (format != NULL);
+
+ if (reporter & gsl_debug_flags)
+ {
+ va_list args;
+ gchar *string;
+
+ va_start (args, format);
+ string = g_strdup_vprintf (format, args);
+ va_end (args);
+ g_printerr ("DEBUG:GSL-%s%s%s: %s\n",
+ reporter_name (reporter),
+ section ? ":" : "",
+ section ? section : "",
+ string);
+ g_free (string);
+ }
+}
+
+void
+gsl_auxlog_push (GslDebugFlags reporter,
+ const gchar *section)
+{
+ ThreadData *tdata = thread_data_from_gsl_thread (gsl_thread_self ());
+
+ if (tdata)
+ {
+ tdata->auxlog_reporter = reporter;
+ tdata->auxlog_section = section;
+ }
+}
+
+void
+gsl_auxlog_debug (const gchar *format,
+ ...)
+{
+ ThreadData *tdata = thread_data_from_gsl_thread (gsl_thread_self ());
+ GslDebugFlags reporter = GSL_MSG_NOTIFY;
+ const gchar *section = NULL;
+ va_list args;
+ gchar *string;
+
+ if (tdata)
+ {
+ reporter = tdata->auxlog_reporter;
+ section = tdata->auxlog_section;
+ tdata->auxlog_reporter = 0;
+ tdata->auxlog_section = NULL;
+ }
+
+ g_return_if_fail (format != NULL);
+
+ va_start (args, format);
+ string = g_strdup_vprintf (format, args);
+ va_end (args);
+ gsl_debug (reporter, section, "%s", string);
+ g_free (string);
+}
+
+void
+gsl_auxlog_message (GslErrorType error,
+ const gchar *format,
+ ...)
+{
+ ThreadData *tdata = thread_data_from_gsl_thread (gsl_thread_self ());
+ GslDebugFlags reporter = GSL_MSG_NOTIFY;
+ const gchar *section = NULL;
+ va_list args;
+ gchar *string;
+
+ if (tdata)
+ {
+ reporter = tdata->auxlog_reporter;
+ section = tdata->auxlog_section;
+ tdata->auxlog_reporter = 0;
+ tdata->auxlog_section = NULL;
+ }
+
+ g_return_if_fail (format != NULL);
+
+ va_start (args, format);
+ string = g_strdup_vprintf (format, args);
+ va_end (args);
+ gsl_message_send (reporter, section, error, "%s", string);
+ g_free (string);
+}
+
+
+/* --- misc --- */
+const gchar*
+gsl_byte_order_to_string (guint byte_order)
+{
+ g_return_val_if_fail (byte_order == G_LITTLE_ENDIAN || byte_order == G_BIG_ENDIAN, NULL);
+
+ if (byte_order == G_LITTLE_ENDIAN)
+ return "little_endian";
+ if (byte_order == G_BIG_ENDIAN)
+ return "big_endian";
+
+ return NULL;
+}
+
+guint
+gsl_byte_order_from_string (const gchar *string)
+{
+ g_return_val_if_fail (string != NULL, 0);
+
+ while (*string == ' ')
+ string++;
+ if (strncasecmp (string, "little", 6) == 0)
+ return G_LITTLE_ENDIAN;
+ if (strncasecmp (string, "big", 3) == 0)
+ return G_BIG_ENDIAN;
+ return 0;
+}
+
+GslErrorType
+gsl_check_file (const gchar *file_name,
+ const gchar *mode)
+{
+ guint access_mask = 0;
+ guint check_file, check_dir, check_link;
+
+ if (strchr (mode, 'r')) /* readable */
+ access_mask |= R_OK;
+ if (strchr (mode, 'w')) /* writable */
+ access_mask |= W_OK;
+ if (strchr (mode, 'x')) /* executable */
+ access_mask |= X_OK;
+
+ if (access_mask && access (file_name, access_mask) < 0)
+ goto have_errno;
+
+ check_file = strchr (mode, 'f') != NULL; /* open as file */
+ check_dir = strchr (mode, 'd') != NULL; /* open as directory */
+ check_link = strchr (mode, 'l') != NULL; /* open as link */
+
+ if (check_file || check_dir || check_link)
+ {
+ struct stat st;
+
+ if (check_link)
+ {
+ if (lstat (file_name, &st) < 0)
+ goto have_errno;
+ }
+ else if (stat (file_name, &st) < 0)
+ goto have_errno;
+
+ if ((check_file && !S_ISREG (st.st_mode)) ||
+ (check_dir && !S_ISDIR (st.st_mode)) ||
+ (check_link && !S_ISLNK (st.st_mode)))
+ return GSL_ERROR_OPEN_FAILED;
+ }
+
+ return GSL_ERROR_NONE;
+
+ have_errno:
+ return gsl_error_from_errno (errno, GSL_ERROR_OPEN_FAILED);
+}
+
+GslErrorType
+gsl_error_from_errno (gint sys_errno,
+ GslErrorType fallback)
+{
+ switch (sys_errno)
+ {
+ case ELOOP:
+ case ENAMETOOLONG:
+ case ENOTDIR:
+ case ENOENT: return GSL_ERROR_NOT_FOUND;
+ case EROFS:
+ case EPERM:
+ case EACCES: return GSL_ERROR_PERMS;
+ case ENOMEM:
+ case ENOSPC:
+ case EFBIG:
+ case ENFILE:
+ case EMFILE: return GSL_ERROR_NO_RESOURCE;
+ case EISDIR:
+ case ESPIPE:
+ case EIO: return GSL_ERROR_IO;
+ case EEXIST: return GSL_ERROR_EXISTS;
+ case ETXTBSY:
+ case EBUSY: return GSL_ERROR_BUSY;
+ case EAGAIN:
+ case EINTR: return GSL_ERROR_TEMP;
+ case EINVAL:
+ case EFAULT:
+ case EBADF: return GSL_ERROR_INTERNAL;
+ default: return fallback;
+ }
+}
+
+
+/* --- global initialization --- */
+static guint
+get_n_processors (void)
+{
+#ifdef _SC_NPROCESSORS_ONLN
+ {
+ gint n = sysconf (_SC_NPROCESSORS_ONLN);
+
+ if (n > 0)
+ return n;
+ }
+#endif
+ return 1;
+}
+
+static const GslConfig *gsl_config = NULL;
+
+const GslConfig*
+gsl_get_config (void)
+{
+ return gsl_config;
+}
+
+#define ROUND(dblval) ((GslLong) ((dblval) + .5))
+
+void
+gsl_init (const GslConfigValue values[],
+ GslMutexTable *mtable)
+{
+ const GslConfigValue *config = values;
+ static GslConfig pconfig = { /* DEFAULTS */
+ 1, /* n_processors */
+ 2, /* wave_chunk_padding */
+ 4, /* wave_chunk_big_pad */
+ 512, /* dcache_block_size */
+ 1024 * 1024, /* dcache_cache_memory */
+ 69, /* midi_kammer_note */
+ 440, /* kammer_freq */
+ };
+
+ g_return_if_fail (gsl_config == NULL); /* assert single initialization */
+
+ /* get mutexes going first */
+ if (mtable)
+ gsl_mutex_table = *mtable;
+
+ gsl_externvar_tick_stamp = 1;
+
+ /* configure permanent config record */
+ if (config)
+ while (config->value_name)
+ {
+ if (strcmp ("wave_chunk_padding", config->value_name) == 0)
+ pconfig.wave_chunk_padding = ROUND (config->value);
+ else if (strcmp ("wave_chunk_big_pad", config->value_name) == 0)
+ pconfig.wave_chunk_big_pad = ROUND (config->value);
+ else if (strcmp ("dcache_cache_memory", config->value_name) == 0)
+ pconfig.dcache_cache_memory = ROUND (config->value);
+ else if (strcmp ("dcache_block_size", config->value_name) == 0)
+ pconfig.dcache_block_size = ROUND (config->value);
+ else if (strcmp ("midi_kammer_note", config->value_name) == 0)
+ pconfig.midi_kammer_note = ROUND (config->value);
+ else if (strcmp ("kammer_freq", config->value_name) == 0)
+ pconfig.kammer_freq = config->value;
+ config++;
+ }
+
+ /* constrain (user) config */
+ pconfig.wave_chunk_padding = MAX (1, pconfig.wave_chunk_padding);
+ pconfig.wave_chunk_big_pad = MAX (2 * pconfig.wave_chunk_padding, pconfig.wave_chunk_big_pad);
+ pconfig.dcache_block_size = MAX (2 * pconfig.wave_chunk_big_pad + sizeof (GslDataType), pconfig.dcache_block_size);
+ pconfig.dcache_block_size = gsl_alloc_upper_power2 (pconfig.dcache_block_size - 1);
+ /* pconfig.dcache_cache_memory = gsl_alloc_upper_power2 (pconfig.dcache_cache_memory); */
+
+ /* non-configurable config updates */
+ pconfig.n_processors = get_n_processors ();
+
+ /* export GSL configuration */
+ gsl_config = &pconfig;
+
+ /* initialize subsystems */
+ is_smp_system = GSL_CONFIG (n_processors) > 1;
+ gsl_mutex_init (&global_memory);
+ gsl_mutex_init (&global_thread);
+ gsl_cond_init (&global_thread_cond);
+ main_thread_tdata = create_tdata ();
+ g_assert (main_thread_tdata != NULL);
+ main_thread = gsl_thread_self ();
+ global_thread_list = gsl_ring_prepend (global_thread_list, main_thread);
+ _gsl_init_signal ();
+ _gsl_init_fd_pool ();
+ _gsl_init_data_caches ();
+ _gsl_init_engine_utils ();
+ _gsl_init_loader_gslwave ();
+ _gsl_init_loader_wav ();
+ _gsl_init_loader_oggvorbis ();
+ _gsl_init_loader_mad ();
+}