Update of /cvsroot/pure-data/externals/grh/threadlib/src In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv27788
Added Files: callbacks.c detach.c fifo.c join.c Makefile Makefile_darwin Makefile_mingw sleep.c threadedsf.c threadlib.c threadlib.h Log Message: initial commit
--- NEW FILE: threadlib.h --- /* * * threadlib * library for threaded patching in PureData * Copyright (C) 2005 Georg Holzmann grh@mur.at * heavily based on code by Tim Blechmann * (detach, join, pd_devel) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */
#ifndef __PD_THREADLIB_H_ #define __PD_THREADLIB_H_
#include "m_pd.h" #include "pthread.h"
// threadlib version string #define VERSION "0.1pre"
// define it to use the lockfree fifo #define THREADLIB_LOCKFREE
// for debuging //#define DEBUG
/* --------- lockfree FIFO of pd devel ----------- */ // (implemted in fifo.c)
/* used data structures */ EXTERN_STRUCT _fifo; #define t_fifo struct _fifo
/* function prototypes */ t_fifo * fifo_init(void); void fifo_destroy(t_fifo*);
/* fifo_put() and fifo_get are the only threadsafe functions!!! */ void fifo_put(t_fifo*, void*); void* fifo_get(t_fifo*);
/* --------- callback FIFO of pd devel ----------- */ // (implemted in callbacks.c)
/* NOTE: difference to pd_devel * in pd_devel the callbacks are called in idle time * (idle callbacks), because this is not possible * in current pd, they are called here by the * clock callbacks */
/* set up callback fifo and start clock callback */ void h_init_callbacks();
/* free fifo and clock callback */ void h_free_callbacks();
/* tb: to be called at idle time */ /* Holzmann: idle callbacks of current PD are not reliable, so it will be called by the clock-callbacks for now */ /* register a new callback in FIFO */ void h_set_callback(t_int (*callback) (t_int* argv), t_int* argv, t_int argc);
#endif // __PD_THREADLIB_H_
--- NEW FILE: detach.c --- /* * * detach * Copyright (C) 2005 Georg Holzmann, grh@mur.at * Copyright (C) 2005 Tim Blechmann * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */
#include "threadlib.h"
static t_class *detach_class;
typedef t_fifo fifo_t; /* for emacs syntax highlighting */
typedef struct _detach { t_object x_obj;
t_outlet * x_outlet;
pthread_t x_thread; pthread_mutex_t x_mutex; pthread_cond_t x_cond;
fifo_t * x_fifo;
} detach_t;
typedef struct _detach_content { struct _detach_content_t * next; enum { BANG, POINTER, FLOAT, SYMBOL, LIST, ANYTHING, CANCEL} type; int argc; t_atom * argv; t_symbol * symbol; } detach_content_t;
static void detach_thread(detach_t* x) { detach_content_t * me; while(1) { pthread_cond_wait(&x->x_cond, &x->x_mutex);
me = (detach_content_t*) fifo_get(x->x_fifo);
while (me != NULL) { /* run function */ switch (me->type) { case BANG: outlet_bang(x->x_outlet); break; case FLOAT: outlet_float(x->x_outlet, atom_getfloat(me->argv)); break; case SYMBOL: outlet_symbol(x->x_outlet, atom_getsymbol(me->argv)); break; case LIST: outlet_list(x->x_outlet, 0, me->argc, me->argv); freebytes(me->argv, me->argc * sizeof(t_atom)); break; case POINTER: outlet_pointer(x->x_outlet, me->argv->a_w.w_gpointer); break; case ANYTHING: outlet_anything(x->x_outlet, me->symbol, me->argc, me->argv); freebytes(me->argv, me->argc * sizeof(t_atom)); break; case CANCEL: goto done; }
/* free */ if (me->argc) freebytes(me->argv, me->argc * sizeof (t_atom)); freebytes (me, sizeof(detach_content_t)); me = (detach_content_t*) fifo_get(x->x_fifo); } }
done: /* free the fifo and exit */
do { if (me->argc) freebytes(me->argv, me->argc * sizeof (t_atom)); freebytes (me, sizeof(detach_content_t)); } while ( (me = (detach_content_t*) fifo_get(x->x_fifo)) );
fifo_destroy(x->x_fifo); pthread_mutex_destroy(&x->x_mutex); pthread_cond_destroy(&x->x_cond); return; }
/* todo: take argument for thread priority */ static detach_t * detach_new() { detach_t *x = (detach_t*) pd_new(detach_class); int status;
x->x_outlet = outlet_new(&x->x_obj, NULL); x->x_fifo = fifo_init();
/* thread initialisation */ pthread_mutex_init (&x->x_mutex,NULL); pthread_mutex_unlock (&x->x_mutex); pthread_cond_init (&x->x_cond,NULL);
status = pthread_create(&x->x_thread, NULL, (void*)detach_thread, x);
if (status == 0) post("detaching thread");
return x; }
static void detach_free(detach_t * x) { detach_content_t * me = getbytes(sizeof(detach_content_t));
me->type = CANCEL; me->argc = 0; fifo_put(x->x_fifo, me); pthread_cond_broadcast(&x->x_cond); }
static void detach_bang(detach_t * x) { detach_content_t * me = getbytes(sizeof(detach_content_t));
me->type = BANG; me->argc = 0; fifo_put(x->x_fifo, me);
pthread_cond_broadcast(&x->x_cond); }
static void detach_float(detach_t * x, t_float f) { detach_content_t * me = getbytes(sizeof(detach_content_t));
me->type = FLOAT; me->argc = 1; me->argv = getbytes(sizeof(t_atom)); SETFLOAT(me->argv, f); fifo_put(x->x_fifo, me);
pthread_cond_broadcast(&x->x_cond); }
static void detach_pointer(detach_t * x, t_gpointer* gp) { detach_content_t * me = getbytes(sizeof(detach_content_t));
me->type = POINTER; me->argc = 1; me->argv = getbytes(sizeof(t_atom)); SETPOINTER(me->argv, gp); fifo_put(x->x_fifo, me);
pthread_cond_broadcast(&x->x_cond); }
static void detach_symbol(detach_t * x, t_symbol * s) { detach_content_t * me = getbytes(sizeof(detach_content_t));
me->type = SYMBOL; me->argc = 1; me->argv = getbytes(sizeof(t_atom)); SETSYMBOL(me->argv, s); fifo_put(x->x_fifo, me);
pthread_cond_broadcast(&x->x_cond); }
static void detach_list(detach_t * x, t_symbol * s, int argc, t_atom* argv) { detach_content_t * me = getbytes(sizeof(detach_content_t));
me->type = LIST; me->argc = argc; me->argv = copybytes(argv, argc * sizeof(t_atom)); fifo_put(x->x_fifo, me);
pthread_cond_broadcast(&x->x_cond); }
static void detach_anything(detach_t * x, t_symbol * s, int argc, t_atom* argv) { detach_content_t * me = getbytes(sizeof(detach_content_t));
me->type = ANYTHING; me->argc = argc; me->argv = copybytes(argv, argc * sizeof(t_atom)); me->symbol = s; fifo_put(x->x_fifo, me);
pthread_cond_broadcast(&x->x_cond); }
void detach_setup(void) { detach_class = class_new(gensym("detach"), (t_newmethod)detach_new, (t_method)detach_free, sizeof(detach_t), CLASS_DEFAULT, 0);
class_addbang(detach_class, detach_bang); class_addfloat(detach_class, detach_float); class_addpointer(detach_class, detach_pointer); class_addsymbol(detach_class, detach_symbol); class_addlist(detach_class, detach_list); class_addanything(detach_class, detach_anything); }
--- NEW FILE: Makefile_mingw --- # ------------------------------------------------- # adjust the next 2 pathes to your system:
# this should point to the directory which contains # m_pd.h and g_canvas.h PDSCR=c:/pd/src
# this is the pd directory, here the files will be # installed PDPATH=c:/pd
# --------------------------------------------------
TARGET=threadlib.pd_linux
OBJ=fifo.o callbacks.o threadlib.o sleep.o detach.o \ join.o threadedsf.o
CC = gcc LD = gcc INCLUDE=-I$(PDSCR) -I. LIB=$(PD-PATH)/bin/pd.dll CC_FLAGS = -DPD -DWINDOWS -c -mms-bitfields \ -Wall -Wno-parentheses -Wno-switch -O3 \ -funroll-loops -fomit-frame-pointer -pthread LD_FLAGS = --export-dynamic -shared -o
# --------------------------------------------------
all: pd_linux
pd_linux: $(TARGET)
$(TARGET): $(OBJ) $(LD) $(LD_FLAGS) $(TARGET) $(OBJ) $(LIB) strip --strip-unneeded $(TARGET) chmod 755 $(TARGET)
threadlib.o: threadlib.h threadlib.c $(CC) $(CC_FLAGS) $(INCLUDE) threadlib.c
fifo.o: threadlib.o fifo.c $(CC) $(CC_FLAGS) $(INCLUDE) fifo.c
callbacks.o: fifo.o threadlib.o callbacks.c $(CC) $(CC_FLAGS) $(INCLUDE) callbacks.c
sleep.o: threadlib.o sleep.c $(CC) $(CC_FLAGS) $(INCLUDE) sleep.c
detach.o: threadlib.o fifo.o detach.c $(CC) $(CC_FLAGS) $(INCLUDE) detach.c
join.o: threadlib.o callbacks.o join.c $(CC) $(CC_FLAGS) $(INCLUDE) join.c
threadedsf.o: threadlib.o callbacks.o threadedsf.c $(CC) $(CC_FLAGS) $(INCLUDE) threadedsf.c
# --------------------------------------------------
clean: rm -f $(OBJ) $(TARGET)
install: @test -d $(PDPATH)/extra || mkdir -p $(PDPATH)/extra install $(TARGET) $(PDPATH)/extra install ../doc/*.pd $(PDPATH)/doc/5.reference
--- NEW FILE: threadlib.c --- /* * * threadlib * library for threaded patching in PureData * Copyright (C) 2005 Georg Holzmann grh@mur.at * heavily based on code by Tim Blechmann * (detach, join, pd_devel) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */
#include "threadlib.h"
typedef struct threadlib { t_object x_obj; } t_threadlib;
t_class *threadlib_class;
static void threadlib_help(void) { post("\nthreadlib vers."VERSION", library for threaded patching\n" "2005, by Georg Holzmann grh@mur.at\n" "heavily based on pd_devel code by Tim Blechmann\n"
// help text: "\tdetach run part of the patch in a helper thread\n" "\tjoin synchronize messages to pd's main thread\n" "\tsleep block system for specific time\n" "\tthreadedsf threaded soundfiler from pd_devel_0.38\n"
"WARNING: this is very experimental and can crash your patches !\n"); }
void *threadlib_new(void) { t_threadlib *x = (t_threadlib *)pd_new(threadlib_class); return (void *)x; }
void sleep_setup(); void detach_setup(); void join_setup(); void threadedsf_setup(); void sys_start_sfthread();
void threadlib_setup(void) { // call all the setup functions: sleep_setup(); detach_setup(); join_setup(); threadedsf_setup();
// init callback system h_init_callbacks();
// start global soundfiler helper thread sys_start_sfthread();
post("\nthreadlib vers."VERSION", library for threaded patching\n" "2005, by Georg Holzmann grh@mur.at\n" "heavily based on pd_devel code by Tim Blechmann\n" "WARNING: this is very experimental and may crash your patches !\n");
threadlib_class = class_new(gensym("threadlib"), threadlib_new, 0, sizeof(t_threadlib), 0, 0); class_addmethod(threadlib_class, (t_method)threadlib_help, gensym("help"), 0); }
--- NEW FILE: Makefile --- # ------------------------------------------------- # adjust the next 2 pathes to your system:
# this should point to the directory which contains # m_pd.h and g_canvas.h PDSCR=/home/holzi/pd-0.39-1test1/src
# this is the pd directory, here the files will be # installed PDPATH=/usr/lib/pd
# --------------------------------------------------
TARGET=threadlib.pd_linux
OBJ=fifo.o callbacks.o threadlib.o sleep.o detach.o \ join.o threadedsf.o
CC = gcc LD = gcc INCLUDE=-I$(PDSCR) -I. LIB=-lc -lm CC_FLAGS = -DPD -DUNIX -c -fPIC \ -Wall -Wno-parentheses -Wno-switch -O3 \ -funroll-loops -fomit-frame-pointer -pthread LD_FLAGS = --export-dynamic -shared -o
# --------------------------------------------------
all: pd_linux
pd_linux: $(TARGET)
$(TARGET): $(OBJ) $(LD) $(LD_FLAGS) $(TARGET) $(OBJ) $(LIB) strip --strip-unneeded $(TARGET) chmod 755 $(TARGET)
threadlib.o: threadlib.h threadlib.c $(CC) $(CC_FLAGS) $(INCLUDE) threadlib.c
fifo.o: threadlib.o fifo.c $(CC) $(CC_FLAGS) $(INCLUDE) fifo.c
callbacks.o: fifo.o threadlib.o callbacks.c $(CC) $(CC_FLAGS) $(INCLUDE) callbacks.c
sleep.o: threadlib.o sleep.c $(CC) $(CC_FLAGS) $(INCLUDE) sleep.c
detach.o: threadlib.o fifo.o detach.c $(CC) $(CC_FLAGS) $(INCLUDE) detach.c
join.o: threadlib.o callbacks.o join.c $(CC) $(CC_FLAGS) $(INCLUDE) join.c
threadedsf.o: threadlib.o callbacks.o threadedsf.c $(CC) $(CC_FLAGS) $(INCLUDE) threadedsf.c
# --------------------------------------------------
clean: rm -f $(OBJ) $(TARGET)
install: @test -d $(PDPATH)/extra || mkdir -p $(PDPATH)/extra install $(TARGET) $(PDPATH)/extra install ../doc/*.pd $(PDPATH)/doc/5.reference
--- NEW FILE: join.c --- /* * * join * Copyright (C) 2005 Georg Holzmann, grh@mur.at * Copyright (C) 2005 Tim Blechmann * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */
#include "threadlib.h" #include <string.h>
static t_class *join_class;
typedef struct _join { t_object x_obj; t_outlet * x_outlet; } join_t;
static join_t * join_new(void) { join_t *x = (join_t*) pd_new(join_class); x->x_outlet = outlet_new(&x->x_obj, NULL); return x; }
static t_int join_bang_callback(t_int * argv) { outlet_bang((t_outlet*)argv[0]); return 0; }
static void join_bang(join_t * x) { t_int* argv = getbytes(sizeof(t_int*)); argv[0] = (t_int)x->x_outlet;
h_set_callback(join_bang_callback, argv, 1); }
static t_int join_pointer_callback(t_int * argv) { outlet_pointer((t_outlet*)argv[0], (t_gpointer*)argv[1]); return 0; }
static void join_pointer(join_t * x, t_gpointer * gp) { t_int* argv = getbytes(2*sizeof(t_int*)); argv[0] = (t_int)x->x_outlet; argv[1] = (t_int)gp;
h_set_callback(join_pointer_callback, argv, 2); }
static t_int join_float_callback(t_int * argv) { outlet_float((t_outlet*)argv[0], (t_float)argv[1]); return 0; }
static void join_float(join_t * x, t_float f) { t_int* argv = getbytes(2*sizeof(t_int*)); argv[0] = (t_int)x->x_outlet; argv[1] = (t_int)f;
h_set_callback(join_float_callback, argv, 2); }
static t_int join_symbol_callback(t_int * argv) { outlet_symbol((t_outlet*)argv[0], (t_symbol*)argv[1]); return 0; }
static void join_symbol(join_t * x, t_symbol * s) { t_int* argv = getbytes(2*sizeof(t_int*)); argv[0] = (t_int)x->x_outlet; argv[1] = (t_int)s;
h_set_callback(join_symbol_callback, argv, 2); }
static t_int join_list_callback(t_int * argv) { outlet_list((t_outlet*)argv[0], 0, (int)argv[1], (t_atom*)argv[2]); freebytes ((t_atom*)argv[2], (int)argv[1] * sizeof(t_atom)); return 0; }
static void join_list(join_t * x, t_symbol * s, int argc, t_atom* largv) { t_int* argv = getbytes(3*sizeof(t_int*)); t_atom* copied_argv = copybytes(largv, argc * sizeof(t_atom));
argv[0] = (t_int)x->x_outlet; argv[1] = (t_int)argc; argv[2] = (t_int)copied_argv;
h_set_callback(join_list_callback, argv, 3); }
static t_int join_anything_callback(t_int * argv) { outlet_anything((t_outlet*)argv[0], &s_list, (int)argv[1], (t_atom*)argv[2]); freebytes ((t_atom*)argv[2], (int)argv[1] * sizeof(t_atom)); return 0; }
static void join_anything(join_t * x, t_symbol * s, int argc, t_atom* largv) { t_int* argv = getbytes(3*sizeof(t_int*));
// also copy selector symbol int copied_argc = argc+1; t_atom *copied_argv; copied_argv = (t_atom*)getbytes(copied_argc * sizeof(t_atom));
if(copied_argc) { memcpy(copied_argv, s, sizeof(t_atom)); SETSYMBOL(copied_argv, s); memcpy(copied_argv+1, largv, argc * sizeof(t_atom)); }
argv[0] = (t_int)x->x_outlet; argv[1] = (t_int)copied_argc; argv[2] = (t_int)copied_argv;
h_set_callback(join_anything_callback, argv, 3); }
void join_setup(void) { join_class = class_new(gensym("join"), (t_newmethod)join_new, 0, sizeof(join_t), CLASS_DEFAULT, 0);
class_addbang(join_class, join_bang); class_addfloat(join_class, join_float); class_addpointer(join_class, join_pointer); class_addsymbol(join_class, join_symbol); class_addlist(join_class, join_list); class_addanything(join_class, join_anything); }
--- NEW FILE: sleep.c --- /* * * sleep * like the c function sleep - blocks the system for a specific time * Copyright (C) 2005 Georg Holzmann grh@mur.at * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */
#include "threadlib.h" #include <unistd.h>
static t_class *sleep_class;
typedef struct _sleep { t_object x_obj; t_outlet * x_outlet; } t_sleep;
static void sleep_float(t_sleep * x, t_float f) { int time = (int)(f<0?0:f); sleep(time); outlet_bang(x->x_outlet); }
static void *sleep_new(void) { t_sleep *x = (t_sleep *)pd_new(sleep_class);
x->x_outlet = outlet_new(&x->x_obj,&s_float);
return (void *)x; }
void sleep_setup(void) { sleep_class = class_new(gensym("sleep"), (t_newmethod)sleep_new, 0, sizeof(t_sleep), CLASS_DEFAULT, 0);
class_addfloat(sleep_class, sleep_float); }
--- NEW FILE: fifo.c --- /* * fifo.c * this is the lockfree fifo implementation of pd_devel_0.39 * * Copyright (c) 2004, Tim Blechmann * supported by vibrez.net * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt" in this distribution. */
#include "threadlib.h" #include "stddef.h"
#ifndef THREADLIB_LOCKFREE
/* we always have the implementation for posix systems with threadlocks */
#include "errno.h"
typedef struct _fifocell { struct _fifocell* next; void* data; /* pointer to our data */ } t_fifocell;
struct _fifo { t_fifocell * head; t_fifocell * tail; pthread_mutex_t mutex; };
t_fifo * fifo_init() { t_fifo* ret = (t_fifo*) getbytes(sizeof (t_fifo)); t_fifocell * fifo_begin = (t_fifocell*) getbytes (sizeof (t_fifocell) ); fifo_begin->data = NULL; fifo_begin->next = NULL;
ret->head = fifo_begin; ret->tail = fifo_begin; pthread_mutex_init(&ret->mutex, NULL); pthread_mutex_unlock(&ret->mutex);
return ret; }
void fifo_destroy(t_fifo* fifo) { void * data; do { data = fifo_get(fifo); } while (data != NULL);
pthread_mutex_lock(&fifo->mutex); pthread_mutex_destroy(&fifo->mutex); freebytes(fifo, sizeof(t_fifo)); return; }
/* fifo_put and fifo_get are the only threadsafe functions!!! */ void fifo_put(t_fifo* fifo, void* data) { if (data != NULL) { t_fifocell * cell = (t_fifocell*) getbytes(sizeof(t_fifocell)); cell->data = data; cell->next = NULL; pthread_mutex_lock(&fifo->mutex); fifo->tail->next = cell; fifo->tail = cell; pthread_mutex_unlock(&fifo->mutex); } return; }
/* this fifo_get returns NULL if the fifo is empty * or locked by another thread */ void* fifo_get(t_fifo* fifo) { t_fifocell * cell; void* data; if(pthread_mutex_trylock(&fifo->mutex) != EBUSY) { cell = fifo->head->next;
if (cell != NULL) { fifo->head->next = cell->next; if(cell == fifo->tail) fifo->tail = fifo->head; data = cell->data; freebytes (cell, sizeof(t_fifocell)); } else data = NULL;
pthread_mutex_unlock(&fifo->mutex); } else data = NULL; return data; }
#else /* THREADLIB_LOCKFREE */
/* lockfree fifo adapted from the midishare: Copyright © Grame 1999 Grame Research Laboratory, 9, rue du Garet 69001 Lyon - France grame@rd.grame.fr */
typedef struct _fifocell { struct _fifocell* next; void* data; /* pointer to our data */ } t_fifocell;
typedef struct _lifo { unsigned long ic; /* operation counter */ t_fifocell* top; /* stack pointer */ unsigned long oc; /* operation counter */ #ifdef __POWERPC__ long unused [5]; /* lifo size must be at least 32 bytes */ /* to avoid livelock in multiprocessor */ #endif } t_lifo;
struct _fifo { t_lifo in; t_lifo out; };
/* platform dependent code */
#ifdef __SMP__ #define LOCK lock ; #else #define LOCK #endif
#if defined(__GNUC__) && defined(__POWERPC__)
static void* lifo_pop(t_lifo* lifo) { register void * data; register volatile long a, b; register long c=0; asm volatile ( "# LFPOP \n" "0: \n" " lwarx %4, %1, %2 \n" /* creates a reservation on lf */ " cmpwi %4, 0 \n" /* test if the lifo is empty */ " beq- 1f \n" " lwz %5, 0(%4) \n" /* next cell in b */ " sync \n" /* synchronize instructions */ " stwcx. %5, %1, %2 \n" /* if the reservation is not altered */ /* modify lifo top */ " bne- 0b \n" /* otherwise: loop and try again */ "0: \n" " lwarx %5, %1, %3 \n" /* creates a reservation on lf->count */ " addi %5, %5, -1 \n" /* dec count */ " sync \n" /* synchronize instructions */ " stwcx. %5, %1, %3 \n" /* conditionnal store */ " bne- 0b \n" "1: \n" " mr %0, %4 \n" :"=r" (data), "=r" (c) : "r" (&lifo->top), "r" (&lifo->oc), "r" (a), "r" (b), "1" (c) : "r0" /* prevents using r0 because of the ambiguity of 'addi' coding: */ /* gcc version 2.95.3 20010315 (release - Linux-Mandrake 8.0 for PPC) */ /* compiles the instruction "addi 0, 0, n" as li 0, n */ ); return data; }
static void* lifo_push(register t_lifo* lifo, register void* data) { register volatile long t1; register long t2=0; asm volatile ( "# LFPUSH \n" "0: \n" " lwarx %0, %3, %1 \n" " stw %0, 0(%2) \n" " sync \n" " stwcx. %2, %3, %1 \n" " bne- 0b \n" "0: \n" " lwarx %0, %3, %4 \n" " addi %0, %0, 1 \n" " sync \n" " stwcx. %0, %3, %4 \n" " bne- 0b \n" : "=r" (t1) : "r" (&lifo->top), "r" (data), "r" (t2), "r" (&lifo->oc), "0" (t1) : "r0" /* prevents using r0 because of the ambiguity of 'addi' coding: */ /* gcc version 2.95.3 20010315 (release - Linux-Mandrake 8.0 for PPC) */ /* compiles the instruction "addi 0, 0, n" as li 0, n */ ); }
#elif defined(__Macintosh__) || defined(__MacOSX__)
static void* lifo_pop(t_lifo* lifo) { register cell * data; register long a, b; asm { addi lifo, lifo, 4 loop: lwarx a, 0, lifo /* creates a reservation on lifo */ cmpwi a, 0 /* test if the lifo is empty */ beq- empty lwz b, 0(a) /* next cell in b */ sync /* synchronize instructions */ stwcx. b, 0, lifo /* if the reservation is not altered */ /* modify lifo top */ bne- loop /* otherwise: loop and try again */
addi lifo, lifo, 4 dec: lwarx b, 0, lifo /* creates a reservation on lifo->count */ addi b, b, -1 /* dec count */ sync /* synchronize instructions */ stwcx. b, 0, lifo /* conditionnal store */ bne- dec
empty: mr data, a } return data; }
static void lifo_push (register t_lifo * lifo, register void * data) { register long tmp; asm { addi lifo, lifo, 4 loop: lwarx tmp, 0, lifo /* creates a reservation on lifo */ stw tmp, 0(data) /* link the new cell to the lifo */ sync /* synchronize instructions */ stwcx. data, 0, lifo /* if the reservation is not altered */ /* modify lifo top */ bne- loop /* otherwise: loop and try again */
addi lifo, lifo, 4 inc: lwarx tmp, 0, lifo /* creates a reservation on lifo->count */ addi tmp, tmp, 1 /* inc count */ sync /* synchronize instructions */ stwcx. tmp, 0, lifo /* conditionnal store */ bne- inc } }
#elif defined(__GNUC__) && (defined(_X86_) || defined(__i386__) || defined(__i586__) || defined(__i686__))
static void* lifo_pop(t_lifo* lifo) { void * data = 0; __asm__ __volatile__ ( "# LFPOP \n\t" "pushl %%ebx \n\t" "pushl %%ecx \n\t" "movl 4(%%esi), %%edx \n\t" "movl (%%esi), %%eax \n\t" "testl %%eax, %%eax \n\t" "jz 20f \n" "10:\t" "movl (%%eax), %%ebx \n\t" "movl %%edx, %%ecx \n\t" "incl %%ecx \n\t" LOCK "cmpxchg8b (%%esi) \n\t" "jz 20f \n\t" "testl %%eax, %%eax \n\t" "jnz 10b \n" "20:\t" "popl %%ecx \n\t" "popl %%ebx \n\t" :"=a" (data) :"S" (&lifo->top) :"memory", "edx"); return data; }
static void lifo_push(t_lifo * lifo, void * data) { __asm__ __volatile__ ( "# LFPUSH \n\t" "pushl %%ebx \n\t" "pushl %%ecx \n\t" "movl 0(%%esi), %%eax \n\t" "movl 4(%%esi), %%edx \n" "1:\t" "movl %%eax, %%ebx \n\t" "incl %%ebx \n\t" "movl %%edx, (%%ecx) \n\t" LOCK "cmpxchg8b (%%esi) \n\t" "jnz 1b \n\t" "popl %%ecx \n\t" "popl %%ebx \n\t" :/* no output */ :"S" (lifo), "c" (data) :"memory", "eax", "edx"); }
#elif defined(__GNUC__) && defined(__x86_64__)
/* this will not work for all revisions of the amd64 architecture ... */
static void* lifo_pop(t_lifo* lifo) { void * data = 0; __asm__ __volatile__ ( "# LFPOP \n\t" "push %%rbx \n\t" "push %%rcx \n\t" "mov 8(%%rdi), %%rdx \n\t" "mov (%%rdi), %%rax \n\t" "test %%rax, %%rax \n\t" "jz 20f \n" "10:\t" "mov (%%rax), %%rbx \n\t" "mov %%rdx, %%rcx \n\t" "inc %%rcx \n\t" LOCK "cmpxchg16b (%%rdi) \n\t" "jz 20f \n\t" "test %%rax, %%rax \n\t" "jnz 10b \n" "20:\t" "pop %%rcx \n\t" "pop %%rbx \n\t" :"=a" (data) :"D" (&lifo->top) :"memory", "rdx"); return data; }
static void lifo_push(t_lifo * lifo, void * data) { __asm__ __volatile__ ( "# LFPUSH \n\t" "push %%rbx \n\t" "push %%rcx \n\t" "mov 0(%%rdi), %%rax \n\t" "mov 8(%%rdi), %%rdx \n" "1:\t" "mov %%rax, %%rbx \n\t" "inc %%rbx \n\t" "mov %%rdx, (%%rcx) \n\t" LOCK "cmpxchg16b (%%rdi) \n\t" "jnz 1b \n\t" "pop %%rcx \n\t" "pop %%rbx \n\t" :/* no output */ :"D" (lifo), "c" (data) :"memory", "rax", "rdx"); }
#elif defined(_WIN32) && defined(_MSC_VER)
static void* lifo_pop(t_lifo* lifo) { __asm { push ebx push ecx push edx push esi mov esi, lifo add esi, 4 mov edx, dword ptr [esi+4] mov eax, dword ptr [esi] test eax, eax jz _end _loop: mov ebx, dword ptr [eax] mov ecx, edx inc ecx LOCK cmpxchg8b qword ptr [esi] jz _end test eax, eax jnz _loop _end: pop esi pop edx pop ecx pop ebx } }
static void lifo_push(t_lifo * lifo, void * data) { __asm { push eax push ebx push ecx push edx push esi mov esi, lifo mov eax, dword ptr [esi] mov ecx, data mov edx, dword ptr 4[esi] _loop: mov ebx, eax inc ebx mov [ecx], edx LOCK cmpxchg8b qword ptr [esi] jnz _loop pop esi pop edx pop ecx pop ebx pop eax } }
#else #error lockfree fifos not available on this platform #endif
static void lifo_init(t_lifo* lifo) { lifo->ic = 0; lifo->top = NULL; lifo->oc = 0; }
t_fifo* fifo_init(void) { t_fifo* ret = (t_fifo*) getbytes(sizeof(t_fifo)); lifo_init(&ret->in); lifo_init(&ret->out); return ret; }
void fifo_destroy(t_fifo* fifo) { void * data; do { data = fifo_get(fifo); } while (data != NULL);
freebytes(fifo, sizeof(t_fifo)); return; }
void fifo_put(t_fifo* fifo, void* data) { lifo_push(&fifo->in, data); }
void* fifo_get(t_fifo* fifo) { void * data; t_lifo *out = &fifo->out; data = lifo_pop(out); if (!data) { void * tmp; t_lifo *in = &fifo->in; data = lifo_pop(in);
if (data) { while((tmp = lifo_pop(in))) { lifo_push(out, data); data = tmp; } } } return data; }
#endif
--- NEW FILE: Makefile_darwin --- # ------------------------------------------------- # adjust the next 2 pathes to your system:
# this should point to the directory which contains # m_pd.h and g_canvas.h PDSCR=/home/holzi/pd-0.39-1test1/src
# this is the pd directory, here the files will be # installed PDPATH=/usr/lib/pd
# --------------------------------------------------
TARGET=threadlib.pd_linux
OBJ=fifo.o callbacks.o threadlib.o sleep.o detach.o \ join.o threadedsf.o
CC = gcc LD = gcc INCLUDE=-I$(PDSCR) -I. LIB=-lc -lm CC_FLAGS = -DPD -c \ -Wall -Wno-parentheses -Wno-switch -O3 \ -funroll-loops -fomit-frame-pointer -pthread LD_FLAGS = -bundle -bundle_loader $(PDPATH)/bin/pd \ --export-dynamic -o
# --------------------------------------------------
all: pd_linux
pd_linux: $(TARGET)
$(TARGET): $(OBJ) $(LD) $(LD_FLAGS) $(TARGET) $(OBJ) $(LIB) #strip --strip-unneeded $(TARGET) chmod 755 $(TARGET)
threadlib.o: threadlib.h threadlib.c $(CC) $(CC_FLAGS) $(INCLUDE) threadlib.c
fifo.o: threadlib.o fifo.c $(CC) $(CC_FLAGS) $(INCLUDE) fifo.c
callbacks.o: fifo.o threadlib.o callbacks.c $(CC) $(CC_FLAGS) $(INCLUDE) callbacks.c
sleep.o: threadlib.o sleep.c $(CC) $(CC_FLAGS) $(INCLUDE) sleep.c
detach.o: threadlib.o fifo.o detach.c $(CC) $(CC_FLAGS) $(INCLUDE) detach.c
join.o: threadlib.o callbacks.o join.c $(CC) $(CC_FLAGS) $(INCLUDE) join.c
threadedsf.o: threadlib.o callbacks.o threadedsf.c $(CC) $(CC_FLAGS) $(INCLUDE) threadedsf.c
# --------------------------------------------------
clean: rm -f $(OBJ) $(TARGET)
install: @test -d $(PDPATH)/extra || mkdir -p $(PDPATH)/extra install $(TARGET) $(PDPATH)/extra install ../doc/*.pd $(PDPATH)/doc/5.reference
--- NEW FILE: callbacks.c --- /* * * callbacks.c * implementation of the callback FIFO * * this is the (modified) FIFO of the idle callbacks from pd_devel_0.38 * (implemented in m_sched.c) */
#include "threadlib.h"
// global callback fifo t_fifo * h_callback_fifo = NULL;
// global clock callback to trigger // the callback fifo t_clock *h_callback_clock = NULL;
/* linked list of callbacks * callback will be freed after returning 0 */ typedef struct _sched_callback { struct _sched_callback* next; /* next callback in ringbuffer / in fifo */ t_int (*function) (t_int* argv); t_int* argv; t_int argc; } t_sched_callback;
// forward declaration static void h_run_callbacks();
void h_init_callbacks() { h_callback_fifo = fifo_init(); h_callback_clock = clock_new(NULL, (t_method)h_run_callbacks); }
void h_free_callbacks() { clock_free(h_callback_clock); fifo_destroy(h_callback_fifo); }
void h_set_callback(t_int (*callback) (t_int* argv), t_int* argv, t_int argc) { t_sched_callback* new = (t_sched_callback*) getbytes (sizeof(t_sched_callback));
new->function = callback; new->argv = (t_int*) copybytes (argv, argc * sizeof (t_int)); new->argc = argc; new->next = NULL; fifo_put(h_callback_fifo, new);
// TODO find solution without lock sys_lock(); clock_delay(h_callback_clock, 0); sys_unlock(); }
static t_sched_callback *ringbuffer_head;
void h_run_callbacks() { t_sched_callback * new_callback;
sys_unlock();
/* append idle callback to ringbuffer */
while ( (new_callback = (t_sched_callback*) fifo_get(h_callback_fifo)) ) { t_sched_callback * next;
/* set the next field to NULL ... it might be set in the fifo */ new_callback->next = NULL; if (ringbuffer_head == NULL) { ringbuffer_head = new_callback; } else { next = ringbuffer_head; while (next->next != 0) next = next->next; next->next = new_callback; } }
if (ringbuffer_head != NULL) { t_sched_callback * idle_callback = ringbuffer_head; t_sched_callback * last = NULL; t_sched_callback * next;
do { int status;
sys_lock(); status = (idle_callback->function)(idle_callback->argv); sys_unlock();
switch (status) { /* callbacks returning 0 will be deleted */ case 0: next = idle_callback->next; freebytes (idle_callback->argv, idle_callback->argc); freebytes ((void*)idle_callback, sizeof(t_sched_callback));
if (last == NULL) ringbuffer_head = next; else last->next = next;
idle_callback = next;
/* callbacks returning 1 will be run again */ case 1: break;
/* callbacks returning 2 will be run during the next idle callback */ case 2: last = idle_callback; idle_callback = idle_callback->next; } } while (idle_callback != NULL); }
sys_lock(); }
--- NEW FILE: threadedsf.c --- /* * * threadedsf * * this is a little bit hacked version of the * threaded soundfiler of pd_devel_0.38 by Tim Blechmann * * (c) 2005, Georg Holzmann, grh@mur.at */
/* Copyright (c) 1997-1999 Miller Puckette. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
/* this file contains, first, a collection of soundfile access routines, a sort of soundfile library. Second, the "soundfiler" object is defined which uses the routines to read or write soundfiles, synchronously, from garrays. These operations are not to be done in "real time" as they may have to wait for disk accesses (even the write routine.) Finally, the realtime objects [...1880 lines suppressed...] x->x_canvas = canvas_getcurrent(); outlet_new(&x->x_obj, &s_float); return (x); }
void threadedsf_setup(void) { threadedsf_class = class_new(gensym("threadedsf"), (t_newmethod)threadedsf_new, 0, sizeof(t_threadedsf), 0, 0);
class_addmethod(threadedsf_class, (t_method)threadedsf_t_read_addq, gensym("read"), A_GIMME, 0); class_addmethod(threadedsf_class, (t_method)threadedsf_t_write_addq, gensym("write"), A_GIMME, 0); class_addmethod(threadedsf_class, (t_method)threadedsf_t_resize_addq, gensym("resize"), A_GIMME, 0); // class_addmethod(threadedsf_class, (t_method)threadedsf_t_const_addq, // gensym("const"), A_GIMME, 0); }