EPICS Controls Argonne National Laboratory

Experimental Physics and
Industrial Control System

2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  <20122013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024  Index 2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  <20122013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024 
<== Date ==> <== Thread ==>

Subject: [Merge] lp:~epics-core/epics-base/spinlocks into lp:epics-base
From: Ralph Lange <[email protected]>
To: [email protected]
Date: Mon, 12 Nov 2012 09:28:21 -0000
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  <20122013  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  <20122013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024 
ANJ, 26 Nov 2012 Valid HTML 4.01! · Home · News · About · Base · Modules · Extensions · Distributions · Download ·
· Search · EPICS V4 · IRMIS · Talk · Bugs · Documents · Links · Licensing ·