Merged Ralph's spinlocks branch.

Added an RTEMS implementation which locks interrupts,
and some Release Notes mentioning the new API.
This commit is contained in:
Andrew Johnson
2013-02-12 12:13:01 -06:00
12 changed files with 572 additions and 3 deletions

View File

@@ -14,7 +14,7 @@ OS_CLASS = Linux
CODE_CPPFLAGS = -D_REENTRANT
POSIX_CPPFLAGS = -D_POSIX_C_SOURCE=199506L -D_POSIX_THREADS -D_XOPEN_SOURCE=500
POSIX_CPPFLAGS = -D_POSIX_C_SOURCE=200112L -D_POSIX_THREADS -D_XOPEN_SOURCE=500
POSIX_LDLIBS = -lpthread
# -D_BSD_SOURCE for gethostname() in unistd.h as needed by cacChannelIO.cpp.

View File

@@ -20,7 +20,7 @@ COMPILER_LDFLAGS += -mt
SOLARIS_VERSION = $(subst 5.,,$(shell uname -r))
POSIX_CPPFLAGS += -D_POSIX_C_SOURCE=199506L $(POSIX_CPPFLAGS_$(SOLARIS_VERSION))
POSIX_CPPFLAGS += -D_POSIX_C_SOURCE=200112L $(POSIX_CPPFLAGS_$(SOLARIS_VERSION))
POSIX_CPPFLAGS += -D_XOPEN_SOURCE=500
POSIX_LDLIBS += -lposix4 -lpthread $(POSIX_LDLIBS_$(SOLARIS_VERSION))

View File

@@ -20,7 +20,7 @@ COMPILER_LDFLAGS += -mt
SOLARIS_VERSION = $(subst 5.,,$(shell uname -r))
POSIX_CPPFLAGS += -D_POSIX_C_SOURCE=199506L $(POSIX_CPPFLAGS_$(SOLARIS_VERSION))
POSIX_CPPFLAGS += -D_POSIX_C_SOURCE=200112L $(POSIX_CPPFLAGS_$(SOLARIS_VERSION))
POSIX_CPPFLAGS += -D_XOPEN_SOURCE=500
POSIX_LDLIBS += -lposix4 -lpthread $(POSIX_LDLIBS_$(SOLARIS_VERSION))

View File

@@ -15,6 +15,18 @@ EPICS Base 3.15.0.x releases are not intended for use in production systems.</p>
<h2 align="center">Changes between 3.15.0.1 and 3.15.0.2</h2>
<!-- Insert new items immediately below here ... -->
<h3>Spin-locks API added</h3>
<p>The new header file epicsSpin.h adds a portable spin-locks API which is
intended for locking very short sections of code (typically one or two lines of
C or C++) to provide a critical section that protects against race conditions.
On Posix platforms this uses the pthread_spinlock_t type if it's available but
falls back to a pthread_mutex_t if not; on the UP VxWorks and RTEMS platforms
the implementations lock out the CPU interrupts; the default implementation
(used where no better implementation is available for the platform) uses an
epicsMutex. Spin-locks may not be taken recursively, and the code inside the
critical section should always be short and deterministic.</p>
<h3>Improvements to aToIPAddr()</h3>
<p>The libCom routine aToIPAddr() and the vxWorks implementation of the

View File

@@ -20,6 +20,7 @@ INC += osdInterrupt.h
INC += epicsMutex.h
INC += osdMutex.h
INC += epicsSpin.h
INC += epicsEvent.h
INC += osdEvent.h
INC += epicsMath.h
@@ -107,6 +108,7 @@ Com_SRCS += osdThread.c
Com_SRCS += osdThreadExtra.c
Com_SRCS += osdThreadHooks.c
Com_SRCS += osdMutex.c
Com_SRCS += osdSpin.c
Com_SRCS += osdEvent.c
Com_SRCS += osdTime.cpp
Com_SRCS += osdProcess.c

View File

@@ -0,0 +1,31 @@
/*************************************************************************\
* 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();
epicsShareFunc void epicsSpinDestroy(epicsSpinId);
epicsShareFunc void epicsSpinLock(epicsSpinId);
epicsShareFunc int epicsSpinTryLock(epicsSpinId);
epicsShareFunc void epicsSpinUnlock(epicsSpinId);
#ifdef __cplusplus
}
#endif
#endif /* epicsSpinh */

View File

@@ -0,0 +1,54 @@
/*************************************************************************\
* 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.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/*
* Authors: Ralph Lange <Ralph.Lange@gmx.de>
* Andrew Johnson <anj@aps.anl.gov>
*
* Based on epicsInterrupt.c (RTEMS implementation) by Eric Norum
*/
/*
* RTEMS (single CPU): LOCK INTERRUPT
*
* CAVEAT:
* This implementation is for UP architectures only.
*
*/
#include <stdlib.h>
#include <rtems.h>
#include "epicsSpin.h"
typedef struct epicsSpin {
rtems_interrupt_level level;
} epicsSpin;
epicsSpinId epicsSpinCreate() {
return calloc(1, sizeof(epicsSpin));
}
void epicsSpinDestroy(epicsSpinId spin) {
free(spin);
}
void epicsSpinLock(epicsSpinId spin) {
rtems_interrupt_disable(spin->level);
}
int epicsSpinTryLock(epicsSpinId spin) {
epicsSpinLock(spin);
return 0;
}
void epicsSpinUnlock(epicsSpinId spin) {
rtems_interrupt_enable(spin->level);
}

View File

@@ -0,0 +1,74 @@
/*************************************************************************\
* 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 <Ralph.Lange@gmx.de>
*/
#include <stdlib.h>
#define epicsExportSharedSymbols
#include "errlog.h"
#include "epicsMutex.h"
#include "epicsSpin.h"
/*
* Default: EPICS MUTEX IMPLEMENTATION
*/
typedef struct epicsSpin {
epicsMutexId lock;
} epicsSpin;
epicsSpinId epicsSpinCreate() {
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;
}
void epicsSpinDestroy(epicsSpinId spin) {
epicsMutexDestroy(spin->lock);
free(spin);
}
void epicsSpinLock(epicsSpinId spin) {
epicsMutexLockStatus status;
status = epicsMutexLock(spin->lock);
if (status != epicsMutexLockOK) {
errlogPrintf("epicsSpin epicsMutexLock failed: error %s\n",
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("epicsSpin epicsMutexTryLock failed: error epicsMutexLockError\n");
return 2;
}
void epicsSpinUnlock(epicsSpinId spin) {
epicsMutexUnlock(spin->lock);
}

View File

@@ -0,0 +1,154 @@
/*************************************************************************\
* 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 <Ralph.Lange@gmx.de>
*/
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include "errlog.h"
#include "epicsSpin.h"
#define checkStatus(status,message) \
if ((status)) { \
errlogPrintf("epicsSpin %s failed: error %s\n", \
(message), strerror((status))); \
}
#if defined(_POSIX_SPIN_LOCKS) && (_POSIX_SPIN_LOCKS > 1)
/*
* POSIX SPIN LOCKS IMPLEMENTATION
*/
typedef struct epicsSpin {
pthread_spinlock_t lock;
} epicsSpin;
epicsSpinId epicsSpinCreate() {
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");
}
int epicsSpinTryLock(epicsSpinId spin) {
int status;
status = pthread_spin_trylock(&spin->lock);
if (status == EBUSY)
return 1;
checkStatus(status, "pthread_spin_trylock");
return 0;
}
void epicsSpinUnlock(epicsSpinId spin) {
int status;
status = pthread_spin_unlock(&spin->lock);
checkStatus(status, "pthread_spin_unlock");
}
#else /* defined(_POSIX_SPIN_LOCKS) && (_POSIX_SPIN_LOCKS > 1) */
/*
* POSIX MUTEX IMPLEMENTATION
*/
typedef struct epicsSpin {
pthread_mutex_t lock;
} epicsSpin;
epicsSpinId epicsSpinCreate() {
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");
}
int epicsSpinTryLock(epicsSpinId spin) {
int status;
status = pthread_mutex_trylock(&spin->lock);
if (status == EBUSY)
return 1;
checkStatus(status, "pthread_mutex_trylock");
return 0;
}
void epicsSpinUnlock(epicsSpinId spin) {
int status;
status = pthread_mutex_unlock(&spin->lock);
checkStatus(status, "pthread_mutex_unlock");
}
#endif /* defined(_POSIX_SPIN_LOCKS) && (_POSIX_SPIN_LOCKS > 1) */

View File

@@ -0,0 +1,52 @@
/*************************************************************************\
* 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 <Ralph.Lange@gmx.de>
*
* based on epicsInterrupt.c (vxWorks implementation) by Marty Kraimer
*/
/*
* vxWorks (single CPU): LOCK INTERRUPT
*
* CAVEAT:
* This implementation will not compile on vxWorks SMP architectures.
* These architectures provide spinlocks, which must be used instead.
*
*/
#include <stdlib.h>
#include <intLib.h>
#include "epicsSpin.h"
typedef struct epicsSpin {
int key;
} epicsSpin;
epicsSpinId epicsSpinCreate() {
return calloc(1, sizeof(epicsSpin));
}
void epicsSpinDestroy(epicsSpinId spin) {
free(spin);
}
void epicsSpinLock(epicsSpinId spin) {
spin->key = intLock();
}
int epicsSpinTryLock(epicsSpinId spin) {
epicsSpinLock(spin);
return 0;
}
void epicsSpinUnlock(epicsSpinId spin) {
intUnlock(spin->key);
}

View File

@@ -131,6 +131,11 @@ epicsMutexTest_SRCS += epicsMutexTest.cpp
testHarness_SRCS += epicsMutexTest.cpp
TESTS += epicsMutexTest
TESTPROD_HOST += epicsSpinTest
epicsSpinTest_SRCS += epicsSpinTest.c
testHarness_SRCS += epicsSpinTest.c
TESTS += epicsSpinTest
TESTPROD_HOST += epicsAtomicTest
epicsAtomicTest_SRCS += epicsAtomicTest.cpp
testHarness_SRCS += epicsAtomicTest.cpp

View File

@@ -0,0 +1,185 @@
/*************************************************************************\
* 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 <Ralph.Lange@gmx.de>
*
* based on epicsMutexTest by Marty Kraimer and Jeff Hill
*
*/
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <time.h>
#include "epicsTime.h"
#include "epicsThread.h"
#include "epicsSpin.h"
#include "epicsEvent.h"
#include "errlog.h"
#include "epicsUnitTest.h"
#include "testMain.h"
typedef struct info {
int threadnum;
epicsSpinId spin;
int quit;
} info;
void spinThread(void *arg)
{
info *pinfo = (info *) arg;
testDiag("spinThread %d starting", pinfo->threadnum);
while (pinfo->quit--) {
epicsSpinLock(pinfo->spin);
testPass("spinThread %d epicsSpinLock taken", pinfo->threadnum);
epicsThreadSleep(.1);
epicsSpinUnlock(pinfo->spin);
epicsThreadSleep(.9);
}
testDiag("spinThread %d exiting", pinfo->threadnum);
return;
}
static void lockPair(struct epicsSpin *spin)
{
epicsSpinLock(spin);
epicsSpinUnlock(spin);
}
static void tenLockPairs(struct epicsSpin *spin)
{
lockPair(spin);
lockPair(spin);
lockPair(spin);
lockPair(spin);
lockPair(spin);
lockPair(spin);
lockPair(spin);
lockPair(spin);
lockPair(spin);
lockPair(spin);
}
static void tenLockPairsSquared(struct epicsSpin *spin)
{
tenLockPairs(spin);
tenLockPairs(spin);
tenLockPairs(spin);
tenLockPairs(spin);
tenLockPairs(spin);
tenLockPairs(spin);
tenLockPairs(spin);
tenLockPairs(spin);
tenLockPairs(spin);
tenLockPairs(spin);
}
void epicsSpinPerformance ()
{
static const unsigned N = 10000;
unsigned i;
epicsSpinId spin;
epicsTimeStamp begin;
epicsTimeStamp end;
double delay;
/* Initialize spinlock */
spin = epicsSpinCreate();
/* test a single lock pair */
epicsTimeGetCurrent(&begin);
for ( i = 0; i < N; i++ ) {
tenLockPairsSquared(spin);
}
epicsTimeGetCurrent(&end);
delay = epicsTimeDiffInSeconds(&end, &begin);
delay /= N * 100u; /* convert to delay per lock pair */
delay *= 1e6; /* convert to micro seconds */
testDiag("lock()*1/unlock()*1 takes %f microseconds", delay);
}
struct verifyTryLock {
epicsSpinId spin;
epicsEventId done;
};
static void verifyTryLockThread(void *pArg)
{
struct verifyTryLock *pVerify =
(struct verifyTryLock *) pArg;
testOk1(epicsSpinTryLock(pVerify->spin));
epicsEventSignal(pVerify->done);
}
static void verifyTryLock()
{
struct verifyTryLock verify;
verify.spin = epicsSpinCreate();
verify.done = epicsEventMustCreate(epicsEventEmpty);
testOk1(epicsSpinTryLock(verify.spin) == 0);
epicsThreadCreate("verifyTryLockThread", 40,
epicsThreadGetStackSize(epicsThreadStackSmall),
verifyTryLockThread, &verify);
testOk1(epicsEventWait(verify.done) == epicsEventWaitOK);
epicsSpinUnlock(verify.spin);
epicsSpinDestroy(verify.spin);
epicsEventDestroy(verify.done);
}
MAIN(epicsSpinTest)
{
const int nthreads = 3;
const int nrounds = 5;
unsigned int stackSize;
epicsThreadId *id;
int i;
char **name;
void **arg;
info **pinfo;
epicsSpinId spin;
testPlan(3 + nthreads * nrounds);
verifyTryLock();
spin = epicsSpinCreate();
id = (epicsThreadId *) calloc(nthreads, sizeof(epicsThreadId));
name = (char **) calloc(nthreads, sizeof(char *));
arg = (void **) calloc(nthreads, sizeof(void *));
pinfo = (info **) calloc(nthreads, sizeof(info *));
stackSize = epicsThreadGetStackSize(epicsThreadStackSmall);
for (i = 0; i < nthreads; i++) {
name[i] = (char *) calloc(10, sizeof(char));
sprintf(name[i],"task%d",i);
pinfo[i] = (info *) calloc(1, sizeof(info));
pinfo[i]->threadnum = i;
pinfo[i]->spin = spin;
pinfo[i]->quit = nrounds;
arg[i] = pinfo[i];
id[i] = epicsThreadCreate(name[i], 40, stackSize,
spinThread,
arg[i]);
}
epicsThreadSleep(2.0 + nrounds);
epicsSpinPerformance();
return testDone();
}