Merge lp:~epics-core/epics-base/spinlocks into lp:~epics-core/epics-base/3.15

Proposed by Ralph Lange
Status: Merged
Merged at revision: 12404
Proposed branch: lp:~epics-core/epics-base/spinlocks
Merge into: lp:~epics-core/epics-base/3.15
Diff against target: 595 lines (+506/-3)
10 files modified
configure/os/CONFIG.Common.linuxCommon (+1/-1)
configure/os/CONFIG.Common.solaris-sparc (+1/-1)
configure/os/CONFIG.Common.solaris-x86 (+1/-1)
src/libCom/osi/Makefile (+2/-0)
src/libCom/osi/epicsSpin.h (+31/-0)
src/libCom/osi/os/default/osdSpin.c (+74/-0)
src/libCom/osi/os/posix/osdSpin.c (+154/-0)
src/libCom/osi/os/vxWorks/osdSpin.c (+52/-0)
src/libCom/test/Makefile (+5/-0)
src/libCom/test/epicsSpinTest.c (+185/-0)
To merge this branch: bzr merge lp:~epics-core/epics-base/spinlocks
Reviewer Review Type Date Requested Status
Andrew Johnson Approve
Review via email: mp+133880@code.launchpad.net

Description of the change

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.

To post a comment you must log in.
lp:~epics-core/epics-base/spinlocks updated
12388. By Andrew Johnson

Fix vxWorks implementation.

12389. By Andrew Johnson

Solaris & vxWorks 5.5.x fixes

Remove C++-isms.
Make internal routines static when duplicate names appear
in the epicsMutexTest.cpp code; on vxWorks and RTEMS all
test programs get linked into a single binary.

12390. By Andrew Johnson

Fix for win32 builds.

Revision history for this message
Andrew Johnson (anj) wrote :

Tested on Linux, Darwin, Solaris and Windows (MS and MinGW).

review: Approve
Revision history for this message
Andrew Johnson (anj) wrote :

I'm adding an RTEMS implementation (UP, locking interrupts) and some text for the Release Notes while merging this branch. It could use a Windows implementation, but I'm not the best person to write that so for now Windows will fall back to using an epicsMutex.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'configure/os/CONFIG.Common.linuxCommon'
2--- configure/os/CONFIG.Common.linuxCommon 2010-10-05 19:27:37 +0000
3+++ configure/os/CONFIG.Common.linuxCommon 2012-11-20 23:30:26 +0000
4@@ -14,7 +14,7 @@
5
6 CODE_CPPFLAGS = -D_REENTRANT
7
8-POSIX_CPPFLAGS = -D_POSIX_C_SOURCE=199506L -D_POSIX_THREADS -D_XOPEN_SOURCE=500
9+POSIX_CPPFLAGS = -D_POSIX_C_SOURCE=200112L -D_POSIX_THREADS -D_XOPEN_SOURCE=500
10 POSIX_LDLIBS = -lpthread
11
12 # -D_BSD_SOURCE for gethostname() in unistd.h as needed by cacChannelIO.cpp.
13
14=== modified file 'configure/os/CONFIG.Common.solaris-sparc'
15--- configure/os/CONFIG.Common.solaris-sparc 2010-11-19 22:38:46 +0000
16+++ configure/os/CONFIG.Common.solaris-sparc 2012-11-20 23:30:26 +0000
17@@ -20,7 +20,7 @@
18
19 SOLARIS_VERSION = $(subst 5.,,$(shell uname -r))
20
21-POSIX_CPPFLAGS += -D_POSIX_C_SOURCE=199506L $(POSIX_CPPFLAGS_$(SOLARIS_VERSION))
22+POSIX_CPPFLAGS += -D_POSIX_C_SOURCE=200112L $(POSIX_CPPFLAGS_$(SOLARIS_VERSION))
23 POSIX_CPPFLAGS += -D_XOPEN_SOURCE=500
24 POSIX_LDLIBS += -lposix4 -lpthread $(POSIX_LDLIBS_$(SOLARIS_VERSION))
25
26
27=== modified file 'configure/os/CONFIG.Common.solaris-x86'
28--- configure/os/CONFIG.Common.solaris-x86 2010-11-19 22:38:46 +0000
29+++ configure/os/CONFIG.Common.solaris-x86 2012-11-20 23:30:26 +0000
30@@ -20,7 +20,7 @@
31
32 SOLARIS_VERSION = $(subst 5.,,$(shell uname -r))
33
34-POSIX_CPPFLAGS += -D_POSIX_C_SOURCE=199506L $(POSIX_CPPFLAGS_$(SOLARIS_VERSION))
35+POSIX_CPPFLAGS += -D_POSIX_C_SOURCE=200112L $(POSIX_CPPFLAGS_$(SOLARIS_VERSION))
36 POSIX_CPPFLAGS += -D_XOPEN_SOURCE=500
37 POSIX_LDLIBS += -lposix4 -lpthread $(POSIX_LDLIBS_$(SOLARIS_VERSION))
38
39
40=== modified file 'src/libCom/osi/Makefile'
41--- src/libCom/osi/Makefile 2012-06-28 16:12:26 +0000
42+++ src/libCom/osi/Makefile 2012-11-20 23:30:26 +0000
43@@ -20,6 +20,7 @@
44
45 INC += epicsMutex.h
46 INC += osdMutex.h
47+INC += epicsSpin.h
48 INC += epicsEvent.h
49 INC += osdEvent.h
50 INC += epicsMath.h
51@@ -107,6 +108,7 @@
52 Com_SRCS += osdThreadExtra.c
53 Com_SRCS += osdThreadHooks.c
54 Com_SRCS += osdMutex.c
55+Com_SRCS += osdSpin.c
56 Com_SRCS += osdEvent.c
57 Com_SRCS += osdTime.cpp
58 Com_SRCS += osdProcess.c
59
60=== added file 'src/libCom/osi/epicsSpin.h'
61--- src/libCom/osi/epicsSpin.h 1970-01-01 00:00:00 +0000
62+++ src/libCom/osi/epicsSpin.h 2012-11-20 23:30:26 +0000
63@@ -0,0 +1,31 @@
64+/*************************************************************************\
65+* Copyright (c) 2012 Helmholtz-Zentrum Berlin
66+* fuer Materialien und Energie GmbH.
67+* Copyright (c) 2012 ITER Organization.
68+* EPICS BASE is distributed subject to a Software License Agreement found
69+* in file LICENSE that is included with this distribution.
70+\*************************************************************************/
71+
72+#ifndef epicsSpinh
73+#define epicsSpinh
74+
75+#include "shareLib.h"
76+
77+#ifdef __cplusplus
78+extern "C" {
79+#endif
80+
81+typedef struct epicsSpin *epicsSpinId;
82+
83+epicsShareFunc epicsSpinId epicsSpinCreate();
84+epicsShareFunc void epicsSpinDestroy(epicsSpinId);
85+
86+epicsShareFunc void epicsSpinLock(epicsSpinId);
87+epicsShareFunc int epicsSpinTryLock(epicsSpinId);
88+epicsShareFunc void epicsSpinUnlock(epicsSpinId);
89+
90+#ifdef __cplusplus
91+}
92+#endif
93+
94+#endif /* epicsSpinh */
95
96=== added file 'src/libCom/osi/os/default/osdSpin.c'
97--- src/libCom/osi/os/default/osdSpin.c 1970-01-01 00:00:00 +0000
98+++ src/libCom/osi/os/default/osdSpin.c 2012-11-20 23:30:26 +0000
99@@ -0,0 +1,74 @@
100+/*************************************************************************\
101+* Copyright (c) 2012 Helmholtz-Zentrum Berlin
102+* fuer Materialien und Energie GmbH.
103+* Copyright (c) 2012 ITER Organization.
104+* EPICS BASE is distributed subject to a Software License Agreement found
105+* in file LICENSE that is included with this distribution.
106+\*************************************************************************/
107+
108+/*
109+ * Author: Ralph Lange <Ralph.Lange@gmx.de>
110+ */
111+
112+#include <stdlib.h>
113+
114+#define epicsExportSharedSymbols
115+#include "errlog.h"
116+#include "epicsMutex.h"
117+#include "epicsSpin.h"
118+
119+/*
120+ * Default: EPICS MUTEX IMPLEMENTATION
121+ */
122+
123+typedef struct epicsSpin {
124+ epicsMutexId lock;
125+} epicsSpin;
126+
127+epicsSpinId epicsSpinCreate() {
128+ epicsSpin *spin;
129+
130+ spin = calloc(1, sizeof(*spin));
131+ if (!spin)
132+ goto fail;
133+
134+ spin->lock = epicsMutexCreate();
135+ if (!spin->lock)
136+ goto fail;
137+
138+ return spin;
139+
140+fail:
141+ free(spin);
142+ return NULL;
143+}
144+
145+void epicsSpinDestroy(epicsSpinId spin) {
146+ epicsMutexDestroy(spin->lock);
147+ free(spin);
148+}
149+
150+void epicsSpinLock(epicsSpinId spin) {
151+ epicsMutexLockStatus status;
152+
153+ status = epicsMutexLock(spin->lock);
154+ if (status != epicsMutexLockOK) {
155+ errlogPrintf("epicsSpin epicsMutexLock failed: error %s\n",
156+ status == epicsMutexLockTimeout ? "epicsMutexLockTimeout" : "epicsMutexLockError");
157+ }
158+}
159+
160+int epicsSpinTryLock(epicsSpinId spin) {
161+ epicsMutexLockStatus status;
162+
163+ status = epicsMutexTryLock(spin->lock);
164+ if (status == epicsMutexLockOK) return 0;
165+ if (status == epicsMutexLockTimeout) return 1;
166+
167+ errlogPrintf("epicsSpin epicsMutexTryLock failed: error epicsMutexLockError\n");
168+ return 2;
169+}
170+
171+void epicsSpinUnlock(epicsSpinId spin) {
172+ epicsMutexUnlock(spin->lock);
173+}
174
175=== added file 'src/libCom/osi/os/posix/osdSpin.c'
176--- src/libCom/osi/os/posix/osdSpin.c 1970-01-01 00:00:00 +0000
177+++ src/libCom/osi/os/posix/osdSpin.c 2012-11-20 23:30:26 +0000
178@@ -0,0 +1,154 @@
179+/*************************************************************************\
180+* Copyright (c) 2012 Helmholtz-Zentrum Berlin
181+* fuer Materialien und Energie GmbH.
182+* Copyright (c) 2012 ITER Organization.
183+* EPICS BASE is distributed subject to a Software License Agreement found
184+* in file LICENSE that is included with this distribution.
185+\*************************************************************************/
186+
187+/*
188+ * Author: Ralph Lange <Ralph.Lange@gmx.de>
189+ */
190+
191+#include <unistd.h>
192+#include <errno.h>
193+#include <stdlib.h>
194+#include <string.h>
195+#include <pthread.h>
196+
197+#include "errlog.h"
198+#include "epicsSpin.h"
199+
200+#define checkStatus(status,message) \
201+ if ((status)) { \
202+ errlogPrintf("epicsSpin %s failed: error %s\n", \
203+ (message), strerror((status))); \
204+ }
205+
206+#if defined(_POSIX_SPIN_LOCKS) && (_POSIX_SPIN_LOCKS > 1)
207+
208+/*
209+ * POSIX SPIN LOCKS IMPLEMENTATION
210+ */
211+
212+typedef struct epicsSpin {
213+ pthread_spinlock_t lock;
214+} epicsSpin;
215+
216+epicsSpinId epicsSpinCreate() {
217+ epicsSpin *spin;
218+ int status;
219+
220+ spin = calloc(1, sizeof(*spin));
221+ if (!spin)
222+ goto fail;
223+
224+ status = pthread_spin_init(&spin->lock, PTHREAD_PROCESS_PRIVATE);
225+ checkStatus(status, "pthread_spin_init");
226+ if (status)
227+ goto fail;
228+
229+ return spin;
230+
231+fail:
232+ free(spin);
233+ return NULL;
234+}
235+
236+void epicsSpinDestroy(epicsSpinId spin) {
237+ int status;
238+
239+ status = pthread_spin_destroy(&spin->lock);
240+ checkStatus(status, "pthread_spin_destroy");
241+
242+ free(spin);
243+}
244+
245+void epicsSpinLock(epicsSpinId spin) {
246+ int status;
247+
248+ status = pthread_spin_lock(&spin->lock);
249+ checkStatus(status, "pthread_spin_lock");
250+}
251+
252+int epicsSpinTryLock(epicsSpinId spin) {
253+ int status;
254+
255+ status = pthread_spin_trylock(&spin->lock);
256+ if (status == EBUSY)
257+ return 1;
258+ checkStatus(status, "pthread_spin_trylock");
259+ return 0;
260+}
261+
262+void epicsSpinUnlock(epicsSpinId spin) {
263+ int status;
264+
265+ status = pthread_spin_unlock(&spin->lock);
266+ checkStatus(status, "pthread_spin_unlock");
267+}
268+
269+#else /* defined(_POSIX_SPIN_LOCKS) && (_POSIX_SPIN_LOCKS > 1) */
270+
271+/*
272+ * POSIX MUTEX IMPLEMENTATION
273+ */
274+
275+typedef struct epicsSpin {
276+ pthread_mutex_t lock;
277+} epicsSpin;
278+
279+epicsSpinId epicsSpinCreate() {
280+ epicsSpin *spin;
281+ int status;
282+
283+ spin = calloc(1, sizeof(*spin));
284+ if (!spin)
285+ goto fail;
286+
287+ status = pthread_mutex_init(&spin->lock, NULL);
288+ checkStatus(status, "pthread_mutex_init");
289+ if (status)
290+ goto fail;
291+
292+ return spin;
293+
294+fail:
295+ free(spin);
296+ return NULL;
297+}
298+
299+void epicsSpinDestroy(epicsSpinId spin) {
300+ int status;
301+
302+ status = pthread_mutex_destroy(&spin->lock);
303+ checkStatus(status, "pthread_mutex_destroy");
304+
305+ free(spin);
306+}
307+
308+void epicsSpinLock(epicsSpinId spin) {
309+ int status;
310+
311+ status = pthread_mutex_lock(&spin->lock);
312+ checkStatus(status, "pthread_mutex_lock");
313+}
314+
315+int epicsSpinTryLock(epicsSpinId spin) {
316+ int status;
317+
318+ status = pthread_mutex_trylock(&spin->lock);
319+ if (status == EBUSY)
320+ return 1;
321+ checkStatus(status, "pthread_mutex_trylock");
322+ return 0;
323+}
324+
325+void epicsSpinUnlock(epicsSpinId spin) {
326+ int status;
327+
328+ status = pthread_mutex_unlock(&spin->lock);
329+ checkStatus(status, "pthread_mutex_unlock");
330+}
331+
332+#endif /* defined(_POSIX_SPIN_LOCKS) && (_POSIX_SPIN_LOCKS > 1) */
333
334=== added file 'src/libCom/osi/os/vxWorks/osdSpin.c'
335--- src/libCom/osi/os/vxWorks/osdSpin.c 1970-01-01 00:00:00 +0000
336+++ src/libCom/osi/os/vxWorks/osdSpin.c 2012-11-20 23:30:26 +0000
337@@ -0,0 +1,52 @@
338+/*************************************************************************\
339+* Copyright (c) 2012 Helmholtz-Zentrum Berlin
340+* fuer Materialien und Energie GmbH.
341+* Copyright (c) 2012 ITER Organization.
342+* EPICS BASE is distributed subject to a Software License Agreement found
343+* in file LICENSE that is included with this distribution.
344+\*************************************************************************/
345+
346+/*
347+ * Author: Ralph Lange <Ralph.Lange@gmx.de>
348+ *
349+ * based on epicsInterrupt.c (vxWorks implementation) by Marty Kraimer
350+ */
351+
352+/*
353+ * vxWorks (single CPU): LOCK INTERRUPT
354+ *
355+ * CAVEAT:
356+ * This implementation will not compile on vxWorks SMP architectures.
357+ * These architectures provide spinlocks, which must be used instead.
358+ *
359+ */
360+
361+#include <stdlib.h>
362+#include <intLib.h>
363+
364+#include "epicsSpin.h"
365+
366+typedef struct epicsSpin {
367+ int key;
368+} epicsSpin;
369+
370+epicsSpinId epicsSpinCreate() {
371+ return calloc(1, sizeof(epicsSpin));
372+}
373+
374+void epicsSpinDestroy(epicsSpinId spin) {
375+ free(spin);
376+}
377+
378+void epicsSpinLock(epicsSpinId spin) {
379+ spin->key = intLock();
380+}
381+
382+int epicsSpinTryLock(epicsSpinId spin) {
383+ epicsSpinLock(spin);
384+ return 0;
385+}
386+
387+void epicsSpinUnlock(epicsSpinId spin) {
388+ intUnlock(spin->key);
389+}
390
391=== modified file 'src/libCom/test/Makefile'
392--- src/libCom/test/Makefile 2012-07-06 19:23:03 +0000
393+++ src/libCom/test/Makefile 2012-11-20 23:30:26 +0000
394@@ -124,6 +124,11 @@
395 testHarness_SRCS += epicsMutexTest.cpp
396 TESTS += epicsMutexTest
397
398+TESTPROD_HOST += epicsSpinTest
399+epicsSpinTest_SRCS += epicsSpinTest.c
400+testHarness_SRCS += epicsSpinTest.c
401+TESTS += epicsSpinTest
402+
403 TESTPROD_HOST += epicsAtomicTest
404 epicsAtomicTest_SRCS += epicsAtomicTest.cpp
405 testHarness_SRCS += epicsAtomicTest.cpp
406
407=== added file 'src/libCom/test/epicsSpinTest.c'
408--- src/libCom/test/epicsSpinTest.c 1970-01-01 00:00:00 +0000
409+++ src/libCom/test/epicsSpinTest.c 2012-11-20 23:30:26 +0000
410@@ -0,0 +1,185 @@
411+/*************************************************************************\
412+* Copyright (c) 2012 Helmholtz-Zentrum Berlin
413+* fuer Materialien und Energie GmbH.
414+* Copyright (c) 2012 ITER Organization.
415+* EPICS BASE is distributed subject to a Software License Agreement found
416+* in file LICENSE that is included with this distribution.
417+\*************************************************************************/
418+
419+/*
420+ * Author: Ralph Lange <Ralph.Lange@gmx.de>
421+ *
422+ * based on epicsMutexTest by Marty Kraimer and Jeff Hill
423+ *
424+ */
425+
426+#include <stdlib.h>
427+#include <stddef.h>
428+#include <string.h>
429+#include <stdio.h>
430+#include <errno.h>
431+#include <time.h>
432+
433+#include "epicsTime.h"
434+#include "epicsThread.h"
435+#include "epicsSpin.h"
436+#include "epicsEvent.h"
437+#include "errlog.h"
438+#include "epicsUnitTest.h"
439+#include "testMain.h"
440+
441+typedef struct info {
442+ int threadnum;
443+ epicsSpinId spin;
444+ int quit;
445+} info;
446+
447+void spinThread(void *arg)
448+{
449+ info *pinfo = (info *) arg;
450+ testDiag("spinThread %d starting", pinfo->threadnum);
451+ while (pinfo->quit--) {
452+ epicsSpinLock(pinfo->spin);
453+ testPass("spinThread %d epicsSpinLock taken", pinfo->threadnum);
454+ epicsThreadSleep(.1);
455+ epicsSpinUnlock(pinfo->spin);
456+ epicsThreadSleep(.9);
457+ }
458+ testDiag("spinThread %d exiting", pinfo->threadnum);
459+ return;
460+}
461+
462+static void lockPair(struct epicsSpin *spin)
463+{
464+ epicsSpinLock(spin);
465+ epicsSpinUnlock(spin);
466+}
467+
468+static void tenLockPairs(struct epicsSpin *spin)
469+{
470+ lockPair(spin);
471+ lockPair(spin);
472+ lockPair(spin);
473+ lockPair(spin);
474+ lockPair(spin);
475+ lockPair(spin);
476+ lockPair(spin);
477+ lockPair(spin);
478+ lockPair(spin);
479+ lockPair(spin);
480+}
481+
482+static void tenLockPairsSquared(struct epicsSpin *spin)
483+{
484+ tenLockPairs(spin);
485+ tenLockPairs(spin);
486+ tenLockPairs(spin);
487+ tenLockPairs(spin);
488+ tenLockPairs(spin);
489+ tenLockPairs(spin);
490+ tenLockPairs(spin);
491+ tenLockPairs(spin);
492+ tenLockPairs(spin);
493+ tenLockPairs(spin);
494+}
495+
496+void epicsSpinPerformance ()
497+{
498+ static const unsigned N = 10000;
499+ unsigned i;
500+ epicsSpinId spin;
501+ epicsTimeStamp begin;
502+ epicsTimeStamp end;
503+ double delay;
504+
505+ /* Initialize spinlock */
506+ spin = epicsSpinCreate();
507+
508+ /* test a single lock pair */
509+ epicsTimeGetCurrent(&begin);
510+ for ( i = 0; i < N; i++ ) {
511+ tenLockPairsSquared(spin);
512+ }
513+ epicsTimeGetCurrent(&end);
514+
515+ delay = epicsTimeDiffInSeconds(&end, &begin);
516+ delay /= N * 100u; /* convert to delay per lock pair */
517+ delay *= 1e6; /* convert to micro seconds */
518+ testDiag("lock()*1/unlock()*1 takes %f microseconds", delay);
519+}
520+
521+struct verifyTryLock {
522+ epicsSpinId spin;
523+ epicsEventId done;
524+};
525+
526+static void verifyTryLockThread(void *pArg)
527+{
528+ struct verifyTryLock *pVerify =
529+ (struct verifyTryLock *) pArg;
530+
531+ testOk1(epicsSpinTryLock(pVerify->spin));
532+ epicsEventSignal(pVerify->done);
533+}
534+
535+static void verifyTryLock()
536+{
537+ struct verifyTryLock verify;
538+
539+ verify.spin = epicsSpinCreate();
540+ verify.done = epicsEventMustCreate(epicsEventEmpty);
541+
542+ testOk1(epicsSpinTryLock(verify.spin) == 0);
543+
544+ epicsThreadCreate("verifyTryLockThread", 40,
545+ epicsThreadGetStackSize(epicsThreadStackSmall),
546+ verifyTryLockThread, &verify);
547+
548+ testOk1(epicsEventWait(verify.done) == epicsEventWaitOK);
549+
550+ epicsSpinUnlock(verify.spin);
551+ epicsSpinDestroy(verify.spin);
552+ epicsEventDestroy(verify.done);
553+}
554+
555+MAIN(epicsSpinTest)
556+{
557+ const int nthreads = 3;
558+ const int nrounds = 5;
559+ unsigned int stackSize;
560+ epicsThreadId *id;
561+ int i;
562+ char **name;
563+ void **arg;
564+ info **pinfo;
565+ epicsSpinId spin;
566+
567+ testPlan(3 + nthreads * nrounds);
568+
569+ verifyTryLock();
570+
571+ spin = epicsSpinCreate();
572+
573+ id = (epicsThreadId *) calloc(nthreads, sizeof(epicsThreadId));
574+ name = (char **) calloc(nthreads, sizeof(char *));
575+ arg = (void **) calloc(nthreads, sizeof(void *));
576+ pinfo = (info **) calloc(nthreads, sizeof(info *));
577+ stackSize = epicsThreadGetStackSize(epicsThreadStackSmall);
578+ for (i = 0; i < nthreads; i++) {
579+ name[i] = (char *) calloc(10, sizeof(char));
580+ sprintf(name[i],"task%d",i);
581+ pinfo[i] = (info *) calloc(1, sizeof(info));
582+ pinfo[i]->threadnum = i;
583+ pinfo[i]->spin = spin;
584+ pinfo[i]->quit = nrounds;
585+ arg[i] = pinfo[i];
586+ id[i] = epicsThreadCreate(name[i], 40, stackSize,
587+ spinThread,
588+ arg[i]);
589+ }
590+ epicsThreadSleep(2.0 + nrounds);
591+
592+ epicsSpinPerformance();
593+
594+ return testDone();
595+}

Subscribers

People subscribed via source and target branches

to all changes: