/* * Copyright (c) 1993-1994 by the University of Southern California * * For copying and distribution information, please see the file * . */ #include /* This is the Prospero interface to threads. This threads package is currently used exclusively by the Prospero server when it is operating in multi-threaded mode. At the moment, this stuff is experimental. It will only be tested on the server side, not on the client. */ /* * The interface to the Prospero server's use of threads is fully defined in * this file. That means that as long as you provide the external interface * herein defined, you're OK. */ /* This interface does not currently attempt to handle issues of cancelling * threads, although that will be a later extension. */ /* Every thread (on the server) has an active RREQ associated with it. Every * active RREQ has a live thread associated with it. RREQs get moved off of * the queue of items to be processed to the list of items in progress when an * available thread is there for them. When a thread is available, * ardp_accept() is called. */ #ifndef PFS_THREADS_H #define PFS_THREADS_H #include /* needed for FILE */ /* #define P_MAX_NUM_THREADS here if you want any threads, and PFS_THREADS. */ /*#define PFS_THREADS */ /* Part of the external interface. */ #ifdef PFS_THREADS #define P_MAX_NUM_THREADS 61 /* This will define threads 0 through 60, quite a lot. */ /* You can easily set a lower value for the number of threads the server * will actually use, by modifying DIRSRV_SUB_THREAD_COUNT in dirsrv.c. */ #define PFS_THREADS_HAVE_SAFE_MALLOC /* Version */ /* Define which PFS_THREADS package you have, options supported currently include PFS_THREADS_SOLARIS or PFS_THREADS_FLORIDA - note NONE of these must be defined if PFS_THREADS is undefined */ /* #define PFS_THREADS_SOLARIS */ #endif /* PFS_THREADS */ #include /* assert */ /* To port to a new threads package - pick an identifer (e.g. PFS_THREADS_DCE). -- Note PFS_THREADS_POSIX is reservered for post-balloting implementation -- Tell prospero-developers@isi.edu which you are using. - Grep through all of the source for instances of PFS_THREADS_ -- Editing these is documented where they apply -- This will include lib/ardp/p_th_self_num server/dirsrv */ #ifdef PFS_THREADS_FLORIDA /* pthread.h is the include file from the Florida State University PART project PTHREADS distribution. It prototypes all of the functions and interfaces whose names start with pthread_. */ #include #endif /* PFS_THREADS_FLORIDA */ #ifdef PFS_THREADS_SOLARIS /* Make sure we get our signal.h with _POSIX_C_SOURCE before thread.h includes sys/signal.h */ #include /* Thread.h prototypes most functions starting thr_ */ #include #endif /* PFS_THREADS_SOLARIS */ #ifdef PFS_THREADS /* Yield a unique integer between 0 and P_MAX_NUM_THREADS - 1 for the current thread. Hopefully this function will go away eventually as the server's internal structure changes in a way that a reference to the current thread is passed around to all subfunctions. But maybe not, too. This is implemented using the per-thread key mechanisms. */ extern void p_th_allocate_self_num(void); /* external; must be called by thread after startup. */ extern void p_th_deallocate_self_num(void); /* external; must be called by thread before exit. */ #endif /*PFS_THREADS*/ #ifdef PFS_THREADS extern int p__th_self_num(void); /* not external interface. */ #else #define p__th_self_num() 0 #endif /* PFS_THREADS */ /* We don't include a trailing ; at the end of our declarations and definitions because a ; by itself in a declarations list is treated by C as a statement, not as an empty declaration. This means that the AUTOSTAT declaration has to be the last one. */ /* This declares an automatic variable name VARNAME of type char **. *VARNAME will be a reference to a persistent per-thread static variable of type char *, with function scope. Note that *varname will be initially NULL, but may expand with time. Part of the external interface.*/ #ifdef PFS_THREADS #define AUTOSTAT_CHARPP(VARNAME) \ static char *(VARNAME##_target[P_MAX_NUM_THREADS]); /* NULL by default. */\ char **VARNAME = (VARNAME##_target + p__th_self_num()) #else #define AUTOSTAT_CHARPP(VARNAME) \ static char *VARNAME##_target = NULL; \ char **VARNAME = &VARNAME##_target #endif /* PFS_THREADS */ /* ditto, for int* . Part of the external interface. */ #ifdef PFS_THREADS #define AUTOSTAT_INTP(VARNAME) \ static int (VARNAME##_target[P_MAX_NUM_THREADS]); /* automatically 0 */\ int *VARNAME = (VARNAME##_target + p__th_self_num()) #else #define AUTOSTAT_INTP(VARNAME) \ static int VARNAME##_target = 0; \ int *VARNAME = &VARNAME##_target #endif /* PFS_THREADS */ /* ditto, for an arbitrary type* . Part of the external interface. */ #ifdef PFS_THREADS #define AUTOSTAT_TYPEP(TYPE,VARNAME) \ static (TYPE) (VARNAME##_target[P_MAX_NUM_THREADS]); /* automatically 0 */\ (TYPE) *VARNAME = (VARNAME##_target + p__th_self_num()) #else #define AUTOSTAT_TYPEP(TYPE,VARNAME) \ static (TYPE) VARNAME##_target = 0; \ (TYPE) *VARNAME = &VARNAME##_target #endif /* PFS_THREADS */ #ifdef PFS_THREADS #define AUTOSTATIC_VAR_INITIALIZED(TYPE,VARNAME,INITIALIZER) \ static TYPE (VARNAME##_target[P_MAX_NUM_THREADS]); /* automatically 0 */\ TYPE *VARNAME = (VARNAME##_target + p__th_self_num()) #else #define AUTOSTATIC_VAR_INITIALIZED(VARNAME) \ static TYPE VARNAME##_target = 0; \ TYPE *VARNAME = &VARNAME##_target #endif /* PFS_THREADS */ /* Possible alternative interface for a common case. I'm experimenting with it. Experimental part of external interface. */ #if 1 #define SCRATCHBUF_CHARPP_DEF(VARNAME) AUTOSTAT_CHARPP(VARNAME) /* Set using *VARNAME = or passing VARNAME as a pointer to the target. */ #define SCRATCHBUF_CHARPP_CLEANUP(VARNAME) #else /* This version of the interface will always allocate a new buffer upon entry. It has the sole advantage that it does not leave static buffers around. It has disadvantages too: presumably the many calls to malloc() will not be an efficient way of using the processor. */ /* A good optimizing compiler should clean up the following (I hope). */ #define SCRATCHBUF_CHARPP_DEF(VARNAME) \ char * VARNAME##_target = NULL; \ char **VARNAME = &VARNAME##_target /* Set using *VARNAME = or passing VARNAME as a pointer to the target. */ #define SCRATCHBUF_CHARPP_CLEANUP(VARNAME) do { \ stfree(*VARNAME); \ *VARNAME = NULL; \ } while(0) #endif /* The following abstraction is used for statics (per-file externs) and real externs. */ /* Externs need separate declarations and data definitions. */ /* Each extern will need one of these after the declaration, to keep the old stuff working. */ #ifdef PFS_THREADS #define EXTERN_LONG_DECL(VARNAME) \ extern long p_th_ar##VARNAME[P_MAX_NUM_THREADS] #define EXTERN_LONG_DEF(VARNAME) \ long p_th_ar##VARNAME[P_MAX_NUM_THREADS] #define PERFILE_STATIC_LONG_DEF(VARNAME) \ static long p_th_ar##VARNAME[P_MAX_NUM_THREADS] #define EXTERN_INT_DECL(VARNAME) \ extern int p_th_ar##VARNAME[P_MAX_NUM_THREADS] #define EXTERN_INT_DEF(VARNAME) \ int p_th_ar##VARNAME[P_MAX_NUM_THREADS] #define PERFILE_STATIC_INT_DEF(VARNAME) \ static int p_th_ar##VARNAME[P_MAX_NUM_THREADS] #define EXTERN_CHARP_DECL(VARNAME) \ extern char * p_th_ar##VARNAME[P_MAX_NUM_THREADS] #define EXTERN_CHARP_DEF(VARNAME) \ char * p_th_ar##VARNAME[P_MAX_NUM_THREADS] #define PERFILE_STATIC_CHARP_DEF(VARNAME) \ static char * p_th_ar##VARNAME[P_MAX_NUM_THREADS] #define EXTERN_TYPEP_DECL(TYPE,VARNAME) \ extern TYPE p_th_ar##VARNAME[P_MAX_NUM_THREADS] #define EXTERN_TYPEP_DEF(TYPE,VARNAME) \ TYPE p_th_ar##VARNAME[P_MAX_NUM_THREADS] #else #define EXTERN_LONG_DECL(VARNAME) \ extern long p_th_ar##VARNAME[1] #define EXTERN_LONG_DEF(VARNAME) \ long p_th_ar##VARNAME[1] #define PERFILE_STATIC_LONG_DEF(VARNAME) \ static long p_th_ar##VARNAME[1] #define EXTERN_INT_DECL(VARNAME) \ extern int p_th_ar##VARNAME[1] #define EXTERN_INT_DEF(VARNAME) \ int p_th_ar##VARNAME[1] #define PERFILE_STATIC_INT_DEF(VARNAME) \ static int p_th_ar##VARNAME[1] #define EXTERN_CHARP_DECL(VARNAME) \ extern char * p_th_ar##VARNAME[1] #define EXTERN_CHARP_DEF(VARNAME) \ char * p_th_ar##VARNAME[1] #define PERFILE_STATIC_CHARP_DEF(VARNAME) \ static char * p_th_ar##VARNAME[1] #define EXTERN_TYPEP_DECL(VARNAME) \ extern TYPE p_th_ar##VARNAME[1] #define EXTERN_TYPEP_DEF(VARNAME) \ TYPE p_th_ar##VARNAME[1] #endif /* PFS_THREADS */ /* In any case, follow definitions with a: #define VARNAME p_th_arVARNAME[p__th_self_num()] */ #if 0 /* first attempt */ /* Externs need separate declarations and data definitions. */ #define EXTERN_INTP_DECL(VARNAMEP) extern int *VARNAMEP /* Each extern will need one of these after the declaration, to keep the old stuff working. */ /* #define VARNAME (*VARNAMEP) */ /* Definition. */ #define EXTERN_INTP_DEF(VARNAMEP) int *VARNAMEP #endif /* #if 0 */ /* Mutexes,implemented on top of the pthreads package. */ /* p_th_mutex is an exported data type, used when we mutex functions or collections of functions. See lib/ardp/ptalloc.c for an example of this. */ /* Do so in init code, to keep from depending on ONCE implementation. Again, this interface is only called directly by Prospero and ARDP code when a set of functions need to be mutexed; otherwise, the *_MUTEXED_* variable declaration and definition interfaces below are used. */ #ifndef NDEBUG #define zz(a) assert((a) == 0) #else #define zz(a) a #endif #if defined(PFS_THREADS) && !defined(NDEBUG) #define DIAGMUTEX(MX1,MX2) if (p_th_mutex_islocked(&p_th_mutex##MX1 )) { printf(mutex_locked_msg,MX2); } extern char mutex_locked_msg[]; #else #define DIAGMUTEX(MX1,MX2) do { } while(0) #endif #ifdef PFS_THREADS_FLORIDA typedef pthread_mutex_t p_th_mutex; #define p_th_mutex_init(MUTEX) \ pthread_mutex_init(&(MUTEX), (pthread_mutexattr_t *) NULL) #define p_th_mutex_lock(MUTEX) { pthread_mutex_lock(&(MUTEX)) } #define p_th_mutex_unlock(MUTEX) pthread_mutex_unlock(&(MUTEX)) #define p_th_mutex_islocked(MUTEX) (pthread_mutex_trylock(&(MUTEX)) == -1) #define p_th_mutex_trylock(MUTEX) \ ( (p_th_mutex_trylock(&(MUTEX)) != -1) \ ? p_th_mutex_lock(MUTEX) \ : TRUE ) #endif /*PFS_THREADS_FLORIDA*/ #ifdef PFS_THREADS_SOLARIS typedef mutex_t p_th_mutex; #define p_th_mutex_init(MUTEX) zz(mutex_init(&(MUTEX), USYNC_THREAD, NULL)) #define p_th_mutex_lock(MUTEX) zz(mutex_lock(&(MUTEX))) #define p_th_mutex_unlock(MUTEX) zz(mutex_unlock(&(MUTEX))) extern int p_th_mutex_islocked(mutex_t *mp); #define p_th_mutex_trylock(MUTEX) (mutex_trylock(&(MUTEX))) #endif /*PFS_THREADS_SOLARIS*/ #ifndef PFS_THREADS #define p_th_mutex_init(MUTEX) do { } while(0) #define p_th_mutex_lock(MUTEX) do { } while(0) #define p_th_mutex_unlock(MUTEX) do { } while(0) /* Used only in ardp_accept() */ #define p_th_mutex_islocked(MUTEX) 0 #define p_th_mutex_trylock(MUTEX) 0 #endif /*!PFS_THREADS*/ /* Handling of global shared variables (e.g., ardp_runQ) */ /* Mutex initialized in ardp_mutexes.c (ardp_init_mutexes()). */ /* Variable defined and declared as mutexed. */ #ifdef PFS_THREADS #define EXTERN_MUTEX_DECL(VARNAME) \ extern p_th_mutex p_th_mutex##VARNAME #define EXTERN_MUTEX_DEF(VARNAME) \ p_th_mutex p_th_mutex##VARNAME /* Initialize the mutex (in ardp_init_mutexes(), pfs_init_mutexes(), dirsrv_init_mutexes()) */ #define EXTERN_MUTEXED_INIT_MUTEX(VARNAME) \ p_th_mutex_init(p_th_mutex##VARNAME) #else /* No-ops if not threaded. */ #define EXTERN_MUTEX_DECL(VARNAME) #define EXTERN_MUTEX_DEF(VARNAME) #define EXTERN_MUTEXED_INIT_MUTEX(VARNAME) do {} while(0) /* no-op */ #endif /* PFS_THREADS */ #define EXTERN_ALLOC_DECL(VARNAME) \ EXTERN_MUTEX_DECL(VARNAME) #define EXTERN_MUTEXED_DECL(TYPE,VARNAME) \ EXTERN_MUTEX_DECL(VARNAME); \ extern TYPE VARNAME #define EXTERN_MUTEXED_DEF(TYPE,VARNAME) \ EXTERN_MUTEX_DEF(VARNAME); \ TYPE VARNAME #define EXTERN_MUTEXED_DEF_INITIALIZER(TYPE,VARNAME,INITIALIZER) \ EXTERN_MUTEX_DEF(VARNAME); \ TYPE VARNAME = (INITIALIZER) /* Lock and unlock a mutexed external variable. */ /* In order to avoid potential deadlock situations, all variables must be locked in alphabetical order. */ #ifdef PFS_THREADS #define EXTERN_MUTEXED_LOCK(VARNAME) p_th_mutex_lock(p_th_mutex##VARNAME) #define EXTERN_MUTEXED_UNLOCK(VARNAME) p_th_mutex_unlock(p_th_mutex##VARNAME) #else #define EXTERN_MUTEXED_LOCK(VARNAME) do {} while (0) #define EXTERN_MUTEXED_UNLOCK(VARNAME) do {} while (0) #endif /* PFS_THREADS */ /* This is found in assertions inside functions that expect to be called only when they are running as the 'master' thread in Prospero. */ /* The master thread is the one that is started off. When we're single-threaded, that's the master. */ /* This is used entirely in assertions. */ #define P_IS_THIS_THREAD_MASTER() (p__th_self_num() == 0) #ifdef PFS_THREADS_FLORIDA /* Note that under the Draft 7 standard, pthread_detach takes a pthread_t, whereas under Draft 6, it takes a pointer to a pthread_t. This implementation uses Draft 6. */ #define p_th_t pthread_t #define p_th_create_detached(NEWTHREAD,FUNC,ARG) ( \ pthread_create(&NEWTHREAD, (pthread_attr_t *) NULL, \ (pthread_funct_t) FUNC, (any_t) ARG) \ : pthread_detach(&NEWTHREAD) \ ) #define p_th_self pthread_self #define p_th_equal(a,b) pthread_equal(a,b) #endif #ifdef PFS_THREADS_SOLARIS #define p_th_t thread_t /* caller needs to check this for errors */ #define p_th_create_detached(NEWTHREAD,FUNC,ARG) \ thr_create(NULL,0,(void *)FUNC,ARG,THR_DETACHED, &NEWTHREAD) #define p_th_self thr_self /* thread_t is an int so ...*/ #define p_th_equal(a,b) (a == b) #endif /* Mutexed versions of everything that might possibly call malloc() and free(). We can yank this out once we have thread-safe libraries, but that might be a few years off. */ /* Note that we have converted over all of the server code that might be called when multi-threaded to not call any of the printf() family of functions. With a thread_safe malloc() now available from FSU, that means we need none of these special definitions. Hooray! */ #if defined(PFS_THREADS) && !defined(PFS_THREADS_SOLARIS) && !defined(PFS_THREADS_FLORIDA) /* commented out for the implemented thread implementations.*/ #define free(P) p_th_free((P)) extern void p_th_free(void *P); #define malloc(P) p_th_malloc((P)) extern void *p_th_malloc(unsigned size); #define calloc(A,B) p_th_calloc((A),(B)) extern void *p_th_calloc(unsigned nelem, unsigned size); #define _filbuf(p) p_th__filbuf((p)) #define _flsbuf(p,q) p_th__flsbuf((p),(q)) #define fgets(a,b,c) p_th_fgets((a),(b), (c)) extern char *p_th_fgets(char *s, int n, FILE *stream); #define gets(a) p_th_gets((a)) extern char *p_th_gets(char *s); #define fputs(a,b) p_th_fputs((a),(b)) extern int p_th_fputs(const char *s, FILE *stream); #define puts(a) p_th_puts((a)) extern int p_th_puts(const char *s); #define sprintf p_th_sprintf extern char *p_th_sprintf(); #define fprintf p_th_fprintf #define printf p_th_printf #define fflush(a) p_th_fflush((a)) extern int p_th_fflush(FILE *); #define fgetc(a) p_th_fgetc((a)) extern int p_th_fgetc(FILE *); #define fputc(a,b) p_th_fputc((a),(b)) extern int p_th_fputc(); #define fread(a,b,c,d) p_th_fread((a),(b),(c), (d)) extern int p_th_fread(char *ptr, int size, int nitems, FILE*stream); #define fwrite(a,b,c,d) p_th_fwrite((a),(b),(c), (d)) extern int p_th_fwrite(const char *ptr, int size, int nitems, FILE *stream); #define fopen(a,b) p_th_fopen((a),(b)) extern FILE *p_th_fopen(const char *a, const char *b); #define fclose(a) p_th_fclose((a)) extern int p_th_fclose(FILE *a); #endif /* !defined(PFS_THREADS_FLORIDA) && !defined(PFS_THREADS_SOLARIS */ /* also in ardp.h */ extern FILE *locked_fopen(const char *a, const char *b); extern int locked_fclose_A(FILE *a, const char*filename, int readonly); extern void locked_clear(FILE *a); int locked_fclose_and_rename(FILE *afile, const char *tmpfilename, const char *filename, int retval); #if 0 #define locked_fopen(a,b) fopen((a),(b)) #define locked_fclose_A(a,b,c) fclose((a)) #define locked_clear(a) #endif /* 0 */ extern void p__th_set_self_master(); #endif /* PFS_THREADS_H */