diff --git a/src/libCom/Makefile b/src/libCom/Makefile index e14808371..ed23af63c 100644 --- a/src/libCom/Makefile +++ b/src/libCom/Makefile @@ -18,6 +18,7 @@ epicsReadline_INCLUDES += $(INCLUDES_$(COMMANDLINE_LIBRARY)) #POSIX thread priority scheduling flag THREAD_CPPFLAGS_NO += -DDONT_USE_POSIX_THREAD_PRIORITY_SCHEDULING osdThread_CPPFLAGS += $(THREAD_CPPFLAGS_$(USE_POSIX_THREAD_PRIORITY_SCHEDULING)) +osdSpin_CPPFLAGS += $(THREAD_CPPFLAGS_$(USE_POSIX_THREAD_PRIORITY_SCHEDULING)) #epicsVersion is created by this Makefile INC += epicsVersion.h @@ -160,6 +161,7 @@ INC += osdInterrupt.h INC += epicsMutex.h INC += osdMutex.h +INC += epicsSpin.h INC += epicsEvent.h INC += osdEvent.h INC += epicsMath.h @@ -224,6 +226,7 @@ SRCS += osdStdio.c SRCS += osdThread.c SRCS += osdMutex.c +SRCS += osdSpin.c SRCS += osdEvent.c SRCS += osdTime.cpp SRCS += osdProcess.c diff --git a/src/libCom/osi/epicsSpin.h b/src/libCom/osi/epicsSpin.h new file mode 100644 index 000000000..22ce8ec8f --- /dev/null +++ b/src/libCom/osi/epicsSpin.h @@ -0,0 +1,32 @@ +/*************************************************************************\ +* Copyright (c) 2012 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* Copyright (c) 2012 ITER Organization. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef epicsSpinh +#define epicsSpinh + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct epicsSpin *epicsSpinId; + +epicsShareFunc epicsSpinId epicsSpinCreate(void); +epicsShareFunc epicsSpinId epicsSpinMustCreate(void); +epicsShareFunc void epicsSpinDestroy(epicsSpinId); + +epicsShareFunc void epicsSpinLock(epicsSpinId); +epicsShareFunc int epicsSpinTryLock(epicsSpinId); +epicsShareFunc void epicsSpinUnlock(epicsSpinId); + +#ifdef __cplusplus +} +#endif + +#endif /* epicsSpinh */ diff --git a/src/libCom/osi/os/RTEMS/osdSpin.c b/src/libCom/osi/os/RTEMS/osdSpin.c new file mode 100644 index 000000000..7a4546812 --- /dev/null +++ b/src/libCom/osi/os/RTEMS/osdSpin.c @@ -0,0 +1,106 @@ +/*************************************************************************\ +* Copyright (c) 2012 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* Copyright (c) 2012 ITER Organization. +* Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2013 Brookhaven Science Assoc. as Operator of Brookhaven +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Authors: Ralph Lange + * Andrew Johnson + * Michael Davidsaver + * + * Inspired by Linux UP spinlocks implemention + * include/linux/spinlock_api_up.h + */ + +/* + * RTEMS (single CPU): LOCK INTERRUPT and DISABLE PREEMPTION + * + * CAVEAT: + * This implementation is intended for UP architectures only. + */ + +#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ 1 + +#include +#include + +#include "cantProceed.h" +#include "epicsSpin.h" + +typedef struct epicsSpin { + rtems_interrupt_level level; + unsigned int locked; +} epicsSpin; + +epicsSpinId epicsSpinCreate(void) { + return calloc(1, sizeof(epicsSpin)); +} + +epicsSpinId epicsSpinMustCreate(void) +{ + epicsSpinId ret = epicsSpinCreate(); + if (!ret) + cantProceed("epicsSpinMustCreate: epicsSpinCreate failed."); + return ret; +} + +void epicsSpinDestroy(epicsSpinId spin) { + free(spin); +} + +void epicsSpinLock(epicsSpinId spin) { + rtems_interrupt_level level; + + rtems_interrupt_disable(level); + _Thread_Disable_dispatch(); + if (spin->locked) { + rtems_interrupt_enable(level); + _Thread_Enable_dispatch(); + if (!rtems_interrupt_is_in_progress()) { + printk("epicsSpinLock(%p): Deadlock.\n", spin); + cantProceed("Recursive lock, missed unlock or block when locked."); + } + else { + printk("epicsSpinLock(%p): Deadlock in ISR.\n" + "Recursive lock, missed unlock or block when locked.\n", + spin); + } + return; + } + spin->level = level; + spin->locked = 1; +} + +int epicsSpinTryLock(epicsSpinId spin) { + rtems_interrupt_level level; + + rtems_interrupt_disable(level); + _Thread_Disable_dispatch(); + if (spin->locked) { + rtems_interrupt_enable(level); + _Thread_Enable_dispatch(); + return 1; + } + spin->level = level; + spin->locked = 1; + return 0; +} + +void epicsSpinUnlock(epicsSpinId spin) { + rtems_interrupt_level level = spin->level; + + if (!spin->locked) { + printk("epicsSpinUnlock(%p): not locked\n", spin); + return; + } + spin->level = spin->locked = 0; + rtems_interrupt_enable (level); + _Thread_Enable_dispatch(); +} diff --git a/src/libCom/osi/os/default/osdSpin.c b/src/libCom/osi/os/default/osdSpin.c new file mode 100644 index 000000000..6e9acb158 --- /dev/null +++ b/src/libCom/osi/os/default/osdSpin.c @@ -0,0 +1,84 @@ +/*************************************************************************\ +* Copyright (c) 2012 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* Copyright (c) 2012 ITER Organization. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + */ + +#include + +#define epicsExportSharedSymbols +#include "cantProceed.h" +#include "errlog.h" +#include "epicsMutex.h" +#include "epicsSpin.h" + +/* + * Default: EPICS MUTEX IMPLEMENTATION + */ + +typedef struct epicsSpin { + epicsMutexId lock; +} epicsSpin; + +epicsSpinId epicsSpinCreate(void) { + epicsSpin *spin; + + spin = calloc(1, sizeof(*spin)); + if (!spin) + goto fail; + + spin->lock = epicsMutexCreate(); + if (!spin->lock) + goto fail; + + return spin; + +fail: + free(spin); + return NULL; +} + +epicsSpinId epicsSpinMustCreate(void) +{ + epicsSpinId ret = epicsSpinCreate(); + if(!ret) + cantProceed("epicsSpinMustCreate: epicsSpinCreate failed."); + return ret; +} + +void epicsSpinDestroy(epicsSpinId spin) { + epicsMutexDestroy(spin->lock); + free(spin); +} + +void epicsSpinLock(epicsSpinId spin) { + epicsMutexLockStatus status; + + status = epicsMutexLock(spin->lock); + if (status != epicsMutexLockOK) { + errlogPrintf("epicsSpinLock(%p): epicsMutexLock returned %s\n", spin, + status == epicsMutexLockTimeout ? + "epicsMutexLockTimeout" : "epicsMutexLockError"); + } +} + +int epicsSpinTryLock(epicsSpinId spin) { + epicsMutexLockStatus status; + + status = epicsMutexTryLock(spin->lock); + if (status == epicsMutexLockOK) return 0; + if (status == epicsMutexLockTimeout) return 1; + + errlogPrintf("epicsSpinTryLock(%p): epicsMutexTryLock returned epicsMutexLockError\n", spin); + return 2; +} + +void epicsSpinUnlock(epicsSpinId spin) { + epicsMutexUnlock(spin->lock); +} diff --git a/src/libCom/osi/os/posix/osdSpin.c b/src/libCom/osi/os/posix/osdSpin.c new file mode 100644 index 000000000..aa61a6c90 --- /dev/null +++ b/src/libCom/osi/os/posix/osdSpin.c @@ -0,0 +1,180 @@ +/*************************************************************************\ +* Copyright (c) 2012 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* Copyright (c) 2012 ITER Organization. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + */ + +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "errlog.h" +#include "cantProceed.h" +#include "epicsSpin.h" + +/* POSIX spinlocks may be subject to priority inversion + * and so can't be guaranteed safe in situations where + * threads have different priorities, and thread + * preemption can't be disabled. + */ +#if defined(DONT_USE_POSIX_THREAD_PRIORITY_SCHEDULING) +#if defined(_POSIX_SPIN_LOCKS) && (_POSIX_SPIN_LOCKS > 0) +# define USE_PSPIN +#endif +#endif + +#define checkStatus(status,message) \ + if ((status)) { \ + errlogPrintf("epicsSpin %s failed: error %s\n", \ + (message), strerror((status))); \ + } + +#ifdef USE_PSPIN + +/* + * POSIX SPIN LOCKS IMPLEMENTATION + */ + +typedef struct epicsSpin { + pthread_spinlock_t lock; +} epicsSpin; + +epicsSpinId epicsSpinCreate(void) { + epicsSpin *spin; + int status; + + spin = calloc(1, sizeof(*spin)); + if (!spin) + goto fail; + + status = pthread_spin_init(&spin->lock, PTHREAD_PROCESS_PRIVATE); + checkStatus(status, "pthread_spin_init"); + if (status) + goto fail; + + return spin; + +fail: + free(spin); + return NULL; +} + +void epicsSpinDestroy(epicsSpinId spin) { + int status; + + status = pthread_spin_destroy(&spin->lock); + checkStatus(status, "pthread_spin_destroy"); + + free(spin); +} + +void epicsSpinLock(epicsSpinId spin) { + int status; + + status = pthread_spin_lock(&spin->lock); + checkStatus(status, "pthread_spin_lock"); + if (status) + cantProceed(NULL); +} + +int epicsSpinTryLock(epicsSpinId spin) { + int status; + + status = pthread_spin_trylock(&spin->lock); + if (status == EBUSY) + return 1; + checkStatus(status, "pthread_spin_trylock"); + return status; +} + +void epicsSpinUnlock(epicsSpinId spin) { + int status; + + status = pthread_spin_unlock(&spin->lock); + checkStatus(status, "pthread_spin_unlock"); +} + +#else /* USE_PSPIN */ + +/* + * POSIX MUTEX IMPLEMENTATION + */ + +typedef struct epicsSpin { + pthread_mutex_t lock; +} epicsSpin; + +epicsSpinId epicsSpinCreate(void) { + epicsSpin *spin; + int status; + + spin = calloc(1, sizeof(*spin)); + if (!spin) + goto fail; + + status = pthread_mutex_init(&spin->lock, NULL); + checkStatus(status, "pthread_mutex_init"); + if (status) + goto fail; + + return spin; + +fail: + free(spin); + return NULL; +} + +void epicsSpinDestroy(epicsSpinId spin) { + int status; + + status = pthread_mutex_destroy(&spin->lock); + checkStatus(status, "pthread_mutex_destroy"); + + free(spin); +} + +void epicsSpinLock(epicsSpinId spin) { + int status; + + status = pthread_mutex_lock(&spin->lock); + checkStatus(status, "pthread_mutex_lock"); + if (status) + cantProceed(NULL); +} + +int epicsSpinTryLock(epicsSpinId spin) { + int status; + + status = pthread_mutex_trylock(&spin->lock); + if (status == EBUSY) + return 1; + checkStatus(status, "pthread_mutex_trylock"); + return status; +} + +void epicsSpinUnlock(epicsSpinId spin) { + int status; + + status = pthread_mutex_unlock(&spin->lock); + checkStatus(status, "pthread_mutex_unlock"); +} + +#endif /* USE_PSPIN */ + + +epicsSpinId epicsSpinMustCreate(void) +{ + epicsSpinId ret = epicsSpinCreate(); + if(!ret) + cantProceed("epicsSpinMustCreate: epicsSpinCreate failed."); + return ret; +} diff --git a/src/libCom/osi/os/vxWorks/osdSpin.c b/src/libCom/osi/os/vxWorks/osdSpin.c new file mode 100644 index 000000000..a68cdd374 --- /dev/null +++ b/src/libCom/osi/os/vxWorks/osdSpin.c @@ -0,0 +1,115 @@ +/*************************************************************************\ +* Copyright (c) 2012 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* Copyright (c) 2012 ITER Organization. +* Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2013 Brookhaven Science Assoc. as Operator of Brookhaven +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Authors: Ralph Lange + * Andrew Johnson + * Michael Davidsaver + * + * Inspired by Linux UP spinlocks implemention + * include/linux/spinlock_api_up.h + */ + +/* + * vxWorks (single CPU): LOCK INTERRUPT and DISABLE PREEMPTION + * + * CAVEAT: + * This implementation will not compile on vxWorks SMP architectures. + * These architectures provide spinlocks, which must be used instead. + * + */ + +/* This is needed for vxWorks 6.x to prevent an obnoxious compiler warning */ +#define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h> + +#include +#include +#include +#include + +#include "cantProceed.h" +#include "epicsSpin.h" + +typedef struct epicsSpin { + int key; + unsigned int locked; +} epicsSpin; + +epicsSpinId epicsSpinCreate(void) { + return calloc(1, sizeof(epicsSpin)); +} + +epicsSpinId epicsSpinMustCreate(void) +{ + epicsSpinId ret = epicsSpinCreate(); + if (!ret) + cantProceed("epicsSpinMustCreate: epicsSpinCreate failed."); + return ret; +} + +void epicsSpinDestroy(epicsSpinId spin) { + free(spin); +} + +void epicsSpinLock(epicsSpinId spin) { + int key = intLock(); + + if (!intContext()) + taskLock(); + if (spin->locked) { + intUnlock(key); + if (!intContext()) { + taskUnlock(); + logMsg("epicsSpinLock(%p): Deadlock.\n", + (int) spin, 0, 0, 0, 0, 0); + cantProceed("Recursive lock, missed unlock or block when locked."); + } + else { + logMsg("epicsSpinLock(%p): Deadlock in ISR.\n" + "Recursive lock, missed unlock or block when locked.\n", + (int) spin, 0, 0, 0, 0, 0); + } + return; + } + spin->key = key; + spin->locked = 1; +} + +int epicsSpinTryLock(epicsSpinId spin) { + int key = intLock(); + + if (!intContext()) + taskLock(); + if (spin->locked) { + intUnlock(key); + if (!intContext()) + taskUnlock(); + return 1; + } + spin->key = key; + spin->locked = 1; + return 0; +} + +void epicsSpinUnlock(epicsSpinId spin) { + int key = spin->key; + + if (!spin->locked) { + logMsg("epicsSpinUnlock(%p): not locked\n", + (int) spin, 0, 0, 0, 0, 0); + return; + } + spin->key = spin->locked = 0; + intUnlock(key); + if (!intContext()) + taskUnlock(); +}