[PD-cvs] externals/grh/threadlib/src callbacks.c, NONE, 1.1 detach.c, NONE, 1.1 fifo.c, NONE, 1.1 join.c, NONE, 1.1 Makefile, NONE, 1.1 Makefile_darwin, NONE, 1.1 Makefile_mingw, NONE, 1.1 sleep.c, NONE, 1.1 threadedsf.c, NONE, 1.1 threadlib.c, NONE, 1.1 threadlib.h, NONE, 1.1

Georg Holzmann grholzi at users.sourceforge.net
Mon Nov 14 22:05:20 CET 2005


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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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);
}





More information about the Pd-cvs mailing list