Experimental Physics and Industrial Control System
Ralph Lange has proposed merging lp:~epics-core/epics-base/spinlocks into lp:epics-base.
Requested reviews:
EPICS Core Developers (epics-core)
For more details, see:
https://code.launchpad.net/~epics-core/epics-base/spinlocks/+merge/133880
libCom/osi: add epicsSpin with default, posix, and vxWorks implementations
- posix uses pthread_spin_ interface when supported, pthread_mutex_ otherwise
- default uses epicsMutex
- vxWorks (single core) uses intLock()
Tests are based on tests for epicsMutex.
--
https://code.launchpad.net/~epics-core/epics-base/spinlocks/+merge/133880
Your team EPICS Core Developers is requested to review the proposed merge of lp:~epics-core/epics-base/spinlocks into lp:epics-base.
=== modified file 'configure/os/CONFIG.Common.linuxCommon'
--- configure/os/CONFIG.Common.linuxCommon 2010-10-05 19:27:37 +0000
+++ configure/os/CONFIG.Common.linuxCommon 2012-11-12 09:27:03 +0000
@@ -14,7 +14,7 @@
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.
=== modified file 'configure/os/CONFIG.Common.solaris-sparc'
--- configure/os/CONFIG.Common.solaris-sparc 2010-11-19 22:38:46 +0000
+++ configure/os/CONFIG.Common.solaris-sparc 2012-11-12 09:27:03 +0000
@@ -20,7 +20,7 @@
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))
=== modified file 'configure/os/CONFIG.Common.solaris-x86'
--- configure/os/CONFIG.Common.solaris-x86 2010-11-19 22:38:46 +0000
+++ configure/os/CONFIG.Common.solaris-x86 2012-11-12 09:27:03 +0000
@@ -20,7 +20,7 @@
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))
=== modified file 'src/libCom/osi/Makefile'
--- src/libCom/osi/Makefile 2012-06-28 16:12:26 +0000
+++ src/libCom/osi/Makefile 2012-11-12 09:27:03 +0000
@@ -20,6 +20,7 @@
INC += epicsMutex.h
INC += osdMutex.h
+INC += epicsSpin.h
INC += epicsEvent.h
INC += osdEvent.h
INC += epicsMath.h
@@ -107,6 +108,7 @@
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
=== added file 'src/libCom/osi/epicsSpin.h'
--- src/libCom/osi/epicsSpin.h 1970-01-01 00:00:00 +0000
+++ src/libCom/osi/epicsSpin.h 2012-11-12 09:27:03 +0000
@@ -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 */
=== added file 'src/libCom/osi/os/default/osdSpin.c'
--- src/libCom/osi/os/default/osdSpin.c 1970-01-01 00:00:00 +0000
+++ src/libCom/osi/os/default/osdSpin.c 2012-11-12 09:27:03 +0000
@@ -0,0 +1,71 @@
+/*************************************************************************\
+* 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 <[email protected]>
+ */
+
+#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);
+}
=== added file 'src/libCom/osi/os/posix/osdSpin.c'
--- src/libCom/osi/os/posix/osdSpin.c 1970-01-01 00:00:00 +0000
+++ src/libCom/osi/os/posix/osdSpin.c 2012-11-12 09:27:03 +0000
@@ -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 <[email protected]>
+ */
+
+#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) */
=== added file 'src/libCom/osi/os/vxWorks/osdSpin.c'
--- src/libCom/osi/os/vxWorks/osdSpin.c 1970-01-01 00:00:00 +0000
+++ src/libCom/osi/os/vxWorks/osdSpin.c 2012-11-12 09:27:03 +0000
@@ -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 <[email protected]>
+ *
+ * based on epicsInterrupt.c (vxWorks implementation) by Marty Kraimer
+ */
+
+/*
+ * vxWorks (single CPU): LOCK INTERRUPT
+ *
+ * CAVEAT:
+ * This implementation will break on vxWorks SMP architectures.
+ * These architectures provide spinlocks, which will have to be used.
+ *
+ */
+
+#include <vxWorks.h>
+#include <intLib.h>
+
+#include "epicsSpin.h"
+
+typedef struct epicsSpin {
+ int key;
+} epicsSpin;
+
+epicsSpinId epicsSpinCreate() {
+ return calloc(1, sizeof(*spin));
+}
+
+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);
+}
=== modified file 'src/libCom/test/Makefile'
--- src/libCom/test/Makefile 2012-07-06 19:23:03 +0000
+++ src/libCom/test/Makefile 2012-11-12 09:27:03 +0000
@@ -124,6 +124,11 @@
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
=== added file 'src/libCom/test/epicsSpinTest.c'
--- src/libCom/test/epicsSpinTest.c 1970-01-01 00:00:00 +0000
+++ src/libCom/test/epicsSpinTest.c 2012-11-12 09:27:03 +0000
@@ -0,0 +1,183 @@
+/*************************************************************************\
+* 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 <[email protected]>
+ *
+ * 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;
+}
+
+inline void lockPair(struct epicsSpin *spin)
+{
+ epicsSpinLock(spin);
+ epicsSpinUnlock(spin);
+}
+
+inline 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);
+}
+
+inline 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 ()
+{
+ unsigned i;
+ epicsSpinId spin;
+ epicsTimeStamp begin;
+ epicsTimeStamp end;
+
+ /* Initialize spinlock */
+ spin = epicsSpinCreate();
+
+ /* test a single lock pair */
+ epicsTimeGetCurrent(&begin);
+ static const unsigned N = 10000;
+ for ( i = 0; i < N; i++ ) {
+ tenLockPairsSquared(spin);
+ }
+ epicsTimeGetCurrent(&end);
+ double 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;
+};
+
+void verifyTryLockThread(void *pArg)
+{
+ struct verifyTryLock *pVerify =
+ (struct verifyTryLock *) pArg;
+
+ testOk1(epicsSpinTryLock(pVerify->spin));
+ epicsEventSignal(pVerify->done);
+}
+
+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();
+}
- Navigate by Date:
- Prev:
Re: RFC: libCom/osi - new spinlock API Ralph Lange
- Next:
Release timetable for R3.14.12.3 Andrew Johnson
- Index:
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
<2012>
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
- Navigate by Thread:
- Prev:
Re: RFC: libCom/osi - new spinlock API Andrew Johnson
- Next:
Release timetable for R3.14.12.3 Andrew Johnson
- Index:
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
<2012>
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024