From c3ebe159bdafdb85dc1334afd4ebce8b17957537 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Tue, 11 Mar 2003 21:01:31 +0000 Subject: [PATCH] added (hopefully) compatible cothreads emulation by using GThreads. Original commit message from CVS: added (hopefully) compatible cothreads emulation by using GThreads. use '--gst-scheduler=basicgthread' to try it out includes bugfix for opt to call do_cothreads_init when using threads --- gst/schedulers/Makefile.am | 12 +- gst/schedulers/cothreads_compat.h | 12 +- gst/schedulers/gstoptimalscheduler.c | 4 + gst/schedulers/gthread-cothreads.h | 216 +++++++++++++++++++++++++++ 4 files changed, 240 insertions(+), 4 deletions(-) create mode 100644 gst/schedulers/gthread-cothreads.h diff --git a/gst/schedulers/Makefile.am b/gst/schedulers/Makefile.am index 11c5ff0dde..d4b94f6392 100644 --- a/gst/schedulers/Makefile.am +++ b/gst/schedulers/Makefile.am @@ -2,9 +2,11 @@ plugindir = $(libdir)/gstreamer-@GST_MAJORMINOR@ plugin_LTLIBRARIES = \ libgstbasicomegascheduler.la \ + libgstbasicgthreadscheduler.la \ libgstbasicwingoscheduler.la \ libgstoptscheduler.la \ libgstoptomegascheduler.la \ + libgstoptgthreadscheduler.la \ libgstoptwingoscheduler.la libgstbasicomegascheduler_la_SOURCES = gstbasicscheduler.c @@ -12,6 +14,10 @@ libgstbasicomegascheduler_la_CFLAGS = $(GST_CFLAGS) -D_COTHREADS_OMEGA libgstbasicomegascheduler_la_LIBADD = ../libcothreads.la libgstbasicomegascheduler_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstbasicgthreadscheduler_la_SOURCES = gstbasicscheduler.c +libgstbasicgthreadscheduler_la_CFLAGS = $(GST_CFLAGS) -D_COTHREADS_GTHREAD +libgstbasicgthreadscheduler_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) + # some silly bug prevents us from putting both $(top_builddir) and # $(src_dir) on the same line for CFLAGS (try it, reverse them, see the # strangeness on your gcc line) so we pull this dirty += trick on it, hah ! @@ -31,6 +37,10 @@ libgstoptomegascheduler_la_CFLAGS = $(GST_CFLAGS) -D_COTHREADS_OMEGA -DUSE_COTHR libgstoptomegascheduler_la_LIBADD = ../libcothreads.la libgstoptomegascheduler_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstoptgthreadscheduler_la_SOURCES = gstoptimalscheduler.c +libgstoptgthreadscheduler_la_CFLAGS = $(GST_CFLAGS) -D_COTHREADS_GTHREAD -DUSE_COTHREADS +libgstoptgthreadscheduler_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) + libgstoptwingoscheduler_la_SOURCES = gstoptimalscheduler.c libgstoptwingoscheduler_la_CFLAGS = $(GST_CFLAGS) -D_COTHREADS_WINGO -DUSE_COTHREADS libgstoptwingoscheduler_la_CFLAGS += -I$(top_builddir)/libs/ext/cothreads @@ -43,4 +53,4 @@ libgstoptwingoscheduler_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(top_builddir)/libs/ext/cothreads/cothreads/libcothreads-gthreads.la: cd $(top_builddir)/libs/ext/cothreads/cothreads && ${MAKE} -noinst_HEADERS = cothreads_compat.h +noinst_HEADERS = cothreads_compat.h gthread-cothreads.h diff --git a/gst/schedulers/cothreads_compat.h b/gst/schedulers/cothreads_compat.h index bbbd5d5522..94f364e7e3 100644 --- a/gst/schedulers/cothreads_compat.h +++ b/gst/schedulers/cothreads_compat.h @@ -63,7 +63,7 @@ typedef cothread_state cothread; #define do_cothread_lock(cothread) cothread_lock(cothread) #define do_cothread_unlock(cothread) cothread_unlock(cothread) -#define do_cothread_get_current() (cothread_current()) +#define do_cothread_get_current(context) (cothread_current()) #define do_cothread_get_main(context) (cothread_current_main()) @@ -81,7 +81,7 @@ typedef cothread_state cothread; /* unify the structs * - * "cothread" and "cothread_context" need to vbe defined + * "cothread" and "cothread_context" need to be defined */ typedef cothread cothread_context; @@ -129,12 +129,18 @@ static void do_cothread_switch(cothread *to) #define do_cothread_lock(cothread) /* FIXME */ #define do_cothread_unlock(cothread) /* FIXME */ -#define do_cothread_get_current() (cothread_self()) +#define do_cothread_get_current(context) (cothread_self()) #define do_cothread_get_main(context) (context) +/* use the new cothreads implementation in libs/ext/cothreads */ +#elif defined(_COTHREADS_GTHREAD) + +#include "gthread-cothreads.h" + + /* bail out with an error if no cothreads package is defined */ #else #error "No cothreads package defined" diff --git a/gst/schedulers/gstoptimalscheduler.c b/gst/schedulers/gstoptimalscheduler.c index c5cc2a91f4..b687ec4650 100644 --- a/gst/schedulers/gstoptimalscheduler.c +++ b/gst/schedulers/gstoptimalscheduler.c @@ -297,6 +297,10 @@ gst_opt_scheduler_class_init (GstOptSchedulerClass *klass) gstscheduler_class->clock_wait = GST_DEBUG_FUNCPTR (gst_opt_scheduler_clock_wait); gstscheduler_class->iterate = GST_DEBUG_FUNCPTR (gst_opt_scheduler_iterate); gstscheduler_class->show = GST_DEBUG_FUNCPTR (gst_opt_scheduler_show); + +#ifdef USE_COTHREADS + do_cothreads_init(NULL); +#endif } static void diff --git a/gst/schedulers/gthread-cothreads.h b/gst/schedulers/gthread-cothreads.h new file mode 100644 index 0000000000..ff952c2768 --- /dev/null +++ b/gst/schedulers/gthread-cothreads.h @@ -0,0 +1,216 @@ +/* GStreamer + * Copyright (C) 2003 Benjamin Otte + * + * gthread-cothreads.c: cothreads implemented via GThread for compatibility + * They're probably slooooooow + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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 + +/* the name of this cothreads */ +#define COTHREADS_NAME "gthread" +#define COTHREADS_NAME_CAPITAL "GThread" + +/** + * Theory of operation: + * Instead of using cothreads, GThreads and 1 mutex are used. + * Every thread may only run if it holds the mutex. Otherwise it holds its own + * cond which has to be signaled to wakeit up. + */ + +/* define "cothread", "cothread_context" and "cothread_func" */ +typedef void (*cothread_func) (int, char **); + +typedef struct _cothread cothread; +typedef struct _cothread_context cothread_context; + +struct _cothread_context { + GSList * cothreads; /* contains all threads but main */ + cothread * main; + cothread * current; + GMutex * mutex; +}; + +struct _cothread { + GThread * thread; + GCond * cond; + cothread_func run; + int argc; + char ** argv; + cothread * creator; + gboolean die; + cothread_context * context; +}; + +/* define functions + * Functions starting with "do_" are used by the scheduler. + */ +static void do_cothreads_init (void *unused); +static cothread_context *do_cothread_context_init (void); +static void do_cothread_context_destroy (cothread_context *context); +static cothread * cothread_create (cothread_context *context, + cothread_func func, + int argc, + char **argv); +#define do_cothread_create(new_cothread, context, func, argc, argv) \ + G_STMT_START{ \ + new_cothread = cothread_create ((context), (func), argc, (char**) (argv)); \ + }G_STMT_END +static void do_cothread_switch (cothread *to); +static void do_cothread_setfunc (cothread *thread, + cothread_context *context, + cothread_func func, + int argc, + char **argv); +static void do_cothread_destroy (cothread *thread); +#define do_cothread_lock(cothread) /* FIXME */ +#define do_cothread_unlock(cothread) /* FIXME */ +#define do_cothread_get_current(context) ((context)->current) +#define do_cothread_get_main(context) ((context)->main) + +static void +do_cothreads_init (void *unused) +{ + if (!g_thread_supported ()) g_thread_init (NULL); +} +static cothread_context * +do_cothread_context_init (void) +{ + cothread_context *ret = g_new0 (cothread_context, 1); + + ret->main = g_new0 (cothread, 1); + ret->main->thread = g_thread_self (); + ret->main->cond = g_cond_new (); + ret->main->die = FALSE; + ret->main->context = ret; + ret->mutex = g_mutex_new (); + ret->cothreads = NULL; + ret->current = ret->main; + g_mutex_lock (ret->mutex); + + return ret; +} +static void +do_cothread_context_destroy (cothread_context *context) +{ + g_assert (g_thread_self() != context->main->thread); + + while (context->cothreads) { + do_cothread_destroy (context->cothreads->data); + } + + g_free (context); +} +static void +die (cothread *to_die) { + g_cond_free (to_die->cond); + g_slist_remove (to_die->context->cothreads, to_die); + g_free (to_die); + g_thread_exit (to_die); +} +static gpointer +run_new_thread (gpointer data) +{ + cothread *self = (cothread *) data; + + g_mutex_lock (self->context->mutex); + g_cond_signal (self->creator->cond); + g_cond_wait (self->cond, self->context->mutex); + if (self->die) + die (self); + while (TRUE) { + self->run (self->argc, self->argv); + /* compatibility */ + do_cothread_switch (do_cothread_get_main (self->context)); + } + g_assert_not_reached (); + return NULL; +} +static cothread * +cothread_create (cothread_context *context, cothread_func func, int argc, char **argv) +{ + cothread *ret; + + if ((ret = g_new (cothread, 1)) == NULL) { + goto out1; + } + ret->cond = g_cond_new (); + ret->run = func; + ret->argc = argc; + ret->argv = argv; + ret->creator = do_cothread_get_current (context); + ret->die = FALSE; + ret->context = context; + context->cothreads = g_slist_prepend (context->cothreads, ret); + ret->thread = g_thread_create (run_new_thread, ret, TRUE, NULL); + if (ret->thread == NULL) goto out2; + g_cond_wait (do_cothread_get_current (context)->cond, context->mutex); + return ret; + +out2: + g_slist_remove (context->cothreads, ret); + g_free (ret); +out1: + return NULL; +} + +static void do_cothread_switch (cothread *to) +{ + cothread *self = do_cothread_get_current(to->context); + + if (self == to) { + g_warning ("trying to switch to the same cothread, not allowed"); + } else { + self->context->current = to; + g_cond_signal (to->cond); + g_cond_wait (self->cond, self->context->mutex); + if (self->die) + die (self); + } +} + +static void +do_cothread_setfunc (cothread *thread, cothread_context *context, + cothread_func func, int argc, char **argv) +{ + thread->run = func; + thread->argc = argc; + thread->argv = argv; +} +static void +do_cothread_destroy (cothread *thread) +{ + g_return_if_fail (thread != thread->context->main); + + thread->die = TRUE; + g_cond_signal (thread->cond); + g_mutex_unlock (thread->context->mutex); + if (thread != g_thread_join (thread->thread)) { + g_warning ("error destroying thread %p", thread); + } +} + +#define do_cothread_lock(cothread) /* FIXME */ +#define do_cothread_unlock(cothread) /* FIXME */ + +#define do_cothread_get_current(context) ((context)->current) +#define do_cothread_get_main(context) ((context)->main) + + + +