diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md index ca30e7c8f..617049dfa 100644 --- a/documentation/RELEASE_NOTES.md +++ b/documentation/RELEASE_NOTES.md @@ -19,6 +19,13 @@ should also be read to understand what has changed since earlier releases. +### Priority inversion safe posix mutexes + +On Posix systems, epicsMutex now support priority inheritance if available. +The IOC needs to run with SCHED_FIFO engaged. +Support for Posix implementations before POSIX.1-2001 (_XOPEN_SOURCE < 500, +glibc version < 2.3.3) has been dropped. + ### Add epicsStrSimilarity() Add epicsStrSimilarity() to epicsString.h which uses edit distance as an approximate comparison. diff --git a/modules/libcom/src/osi/Makefile b/modules/libcom/src/osi/Makefile index f0108e662..ce9dcc692 100644 --- a/modules/libcom/src/osi/Makefile +++ b/modules/libcom/src/osi/Makefile @@ -112,6 +112,7 @@ Com_SRCS += osdStdio.c 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)) +osdMutex_CPPFLAGS += $(THREAD_CPPFLAGS_$(USE_POSIX_THREAD_PRIORITY_SCHEDULING)) Com_SRCS += osdThread.c Com_SRCS += osdThreadExtra.c diff --git a/modules/libcom/src/osi/os/posix/osdEvent.c b/modules/libcom/src/osi/os/posix/osdEvent.c index 251f35d0b..db61c240b 100644 --- a/modules/libcom/src/osi/os/posix/osdEvent.c +++ b/modules/libcom/src/osi/os/posix/osdEvent.c @@ -23,6 +23,7 @@ #include "epicsEvent.h" #include "epicsTime.h" #include "errlog.h" +#include "osdPosixMutexPriv.h" struct epicsEventOSD { pthread_mutex_t mutex; @@ -50,11 +51,11 @@ LIBCOM_API epicsEventId epicsEventCreate(epicsEventInitialState init) epicsEventId pevent = malloc(sizeof(*pevent)); if (pevent) { - int status = pthread_mutex_init(&pevent->mutex, 0); + int status = osdPosixMutexInit(&pevent->mutex, 0); pevent->isFull = (init == epicsEventFull); if (status) { - printStatus(status, "pthread_mutex_init", "epicsEventCreate"); + printStatus(status, "osdPosixMutexInit", "epicsEventCreate"); } else { status = pthread_cond_init(&pevent->cond, 0); if (!status) diff --git a/modules/libcom/src/osi/os/posix/osdMutex.c b/modules/libcom/src/osi/os/posix/osdMutex.c index 707ae6304..6279c57f3 100644 --- a/modules/libcom/src/osi/os/posix/osdMutex.c +++ b/modules/libcom/src/osi/os/posix/osdMutex.c @@ -11,6 +11,10 @@ /* Author: Marty Kraimer Date: 13AUG1999 */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + #include #include #include @@ -21,6 +25,7 @@ #include #include "epicsMutex.h" +#include "osdPosixMutexPriv.h" #include "cantProceed.h" #include "epicsTime.h" #include "errlog.h" @@ -38,6 +43,69 @@ cantProceed((method)); \ } +#if defined(DONT_USE_POSIX_THREAD_PRIORITY_SCHEDULING) +#undef _POSIX_THREAD_PRIO_INHERIT +#endif + +/* Global var - pthread_once does not support passing args but it is more efficient + * than epicsThreadOnce which always acquires a mutex. + */ +static pthread_mutexattr_t globalAttrDefault; +static pthread_mutexattr_t globalAttrRecursive; +static pthread_once_t globalAttrInitOnce = PTHREAD_ONCE_INIT; + +static void globalAttrInit() +{ + int status; + + status = pthread_mutexattr_init(&globalAttrDefault); + checkStatusQuit(status,"pthread_mutexattr_init(&globalAttrDefault)","globalAttrInit"); + status = pthread_mutexattr_init(&globalAttrRecursive); + checkStatusQuit(status,"pthread_mutexattr_init(&globalAttrRecursive)","globalAttrInit"); + status = pthread_mutexattr_settype(&globalAttrRecursive, PTHREAD_MUTEX_RECURSIVE); + checkStatusQuit(status, "pthread_mutexattr_settype(&globalAttrRecursive, PTHREAD_MUTEX_RECURSIVE)", "globalAttrInit"); +#if defined _POSIX_THREAD_PRIO_INHERIT + status = pthread_mutexattr_setprotocol(&globalAttrDefault, PTHREAD_PRIO_INHERIT); + if (errVerbose) checkStatus(status, "pthread_mutexattr_setprotocol(&globalAttrDefault, PTHREAD_PRIO_INHERIT)"); + status = pthread_mutexattr_setprotocol(&globalAttrRecursive, PTHREAD_PRIO_INHERIT); + if (errVerbose) checkStatus(status, "pthread_mutexattr_setprotocol(&globalAttrRecursive, PTHREAD_PRIO_INHERIT)"); + if (status == 0) { + /* Can we really use PTHREAD_PRIO_INHERIT? */ + pthread_mutex_t temp; + status = pthread_mutex_init(&temp, &globalAttrRecursive); + if (errVerbose) checkStatus(status, "pthread_mutex_init(&temp, &globalAttrRecursive)"); + if (status != 0) { + /* No, PTHREAD_PRIO_INHERIT does not work, fall back to PTHREAD_PRIO_NONE */; + pthread_mutexattr_setprotocol(&globalAttrDefault, PTHREAD_PRIO_NONE); + pthread_mutexattr_setprotocol(&globalAttrRecursive, PTHREAD_PRIO_NONE); + } else { + pthread_mutex_destroy(&temp); + } + } +#endif +} + +int osdPosixMutexInit (pthread_mutex_t *m, int mutextype) +{ + pthread_mutexattr_t *atts; + int status; + + status = pthread_once( &globalAttrInitOnce, globalAttrInit ); + checkStatusQuit(status,"pthread_once","epicsPosixMutexAttrGet"); + + switch (mutextype) { + case PTHREAD_MUTEX_DEFAULT: + atts = &globalAttrDefault; + break; + case PTHREAD_MUTEX_RECURSIVE: + atts = &globalAttrRecursive; + break; + default: + return ENOTSUP; + } + return pthread_mutex_init(m, atts); +} + static int mutexLock(pthread_mutex_t *id) { int status; @@ -48,23 +116,8 @@ static int mutexLock(pthread_mutex_t *id) return status; } -/* Until these can be demonstrated to work leave them undefined*/ -/* On solaris 8 _POSIX_THREAD_PRIO_INHERIT fails*/ -#undef _POSIX_THREAD_PROCESS_SHARED -#undef _POSIX_THREAD_PRIO_INHERIT - -/* Two completely different implementations are provided below - * If support is available for PTHREAD_MUTEX_RECURSIVE then - * only pthread_mutex is used. - * If support is not available for PTHREAD_MUTEX_RECURSIVE then - * a much more complicated solution is required - */ - - -#if defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE)>=500 typedef struct epicsMutexOSD { pthread_mutex_t lock; - pthread_mutexattr_t mutexAttr; } epicsMutexOSD; epicsMutexOSD * epicsMutexOsdCreate(void) { @@ -73,32 +126,12 @@ epicsMutexOSD * epicsMutexOsdCreate(void) { pmutex = calloc(1, sizeof(*pmutex)); if(!pmutex) - goto fail; + return NULL; - status = pthread_mutexattr_init(&pmutex->mutexAttr); - if (status) - goto fail; + status = osdPosixMutexInit(&pmutex->lock, PTHREAD_MUTEX_RECURSIVE); + if (!status) + return pmutex; -#if defined(_POSIX_THREAD_PRIO_INHERIT) && _POSIX_THREAD_PRIO_INHERIT > 0 - status = pthread_mutexattr_setprotocol(&pmutex->mutexAttr, - PTHREAD_PRIO_INHERIT); - if (errVerbose) checkStatus(status, "pthread_mutexattr_setprotocal"); -#endif /*_POSIX_THREAD_PRIO_INHERIT*/ - - status = pthread_mutexattr_settype(&pmutex->mutexAttr, - PTHREAD_MUTEX_RECURSIVE); - checkStatus(status, "pthread_mutexattr_settype"); - if (status) - goto fail; - - status = pthread_mutex_init(&pmutex->lock, &pmutex->mutexAttr); - if (status) - goto dattr; - return pmutex; - -dattr: - pthread_mutexattr_destroy(&pmutex->mutexAttr); -fail: free(pmutex); return NULL; } @@ -109,11 +142,9 @@ void epicsMutexOsdDestroy(struct epicsMutexOSD * pmutex) status = pthread_mutex_destroy(&pmutex->lock); checkStatus(status, "pthread_mutex_destroy"); - status = pthread_mutexattr_destroy(&pmutex->mutexAttr); - checkStatus(status, "pthread_mutexattr_destroy"); free(pmutex); } - + void epicsMutexOsdUnlock(struct epicsMutexOSD * pmutex) { int status; @@ -157,178 +188,3 @@ void epicsMutexOsdShow(struct epicsMutexOSD * pmutex, unsigned int level) */ printf(" pthread_mutex_t* uaddr=%p\n", &pmutex->lock); } - -#else /*defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE)>=500 */ - -typedef struct epicsMutexOSD { - pthread_mutex_t lock; - pthread_mutexattr_t mutexAttr; - pthread_cond_t waitToBeOwner; -#if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED > 0 - pthread_condattr_t condAttr; -#endif /*_POSIX_THREAD_PROCESS_SHARED*/ - int count; - int owned; /* TRUE | FALSE */ - pthread_t ownerTid; -} epicsMutexOSD; - -epicsMutexOSD * epicsMutexOsdCreate(void) { - epicsMutexOSD *pmutex; - int status; - - pmutex = calloc(1, sizeof(*pmutex)); - if(!pmutex) - return NULL; - - status = pthread_mutexattr_init(&pmutex->mutexAttr); - if(status) - goto fail; - -#if defined(_POSIX_THREAD_PRIO_INHERIT) && _POSIX_THREAD_PRIO_INHERIT > 0 - status = pthread_mutexattr_setprotocol( - &pmutex->mutexAttr,PTHREAD_PRIO_INHERIT); - if (errVerbose) checkStatus(status, "pthread_mutexattr_setprotocal"); -#endif /*_POSIX_THREAD_PRIO_INHERIT*/ - - status = pthread_mutex_init(&pmutex->lock, &pmutex->mutexAttr); - if(status) - goto dattr; - -#if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED > 0 - status = pthread_condattr_init(&pmutex->condAttr); - checkStatus(status, "pthread_condattr_init"); - status = pthread_condattr_setpshared(&pmutex->condAttr, - PTHREAD_PROCESS_PRIVATE); - checkStatus(status, "pthread_condattr_setpshared"); - status = pthread_cond_init(&pmutex->waitToBeOwner, &pmutex->condAttr); -#else - status = pthread_cond_init(&pmutex->waitToBeOwner, 0); -#endif /*_POSIX_THREAD_PROCESS_SHARED*/ - if(status) - goto dmutex; - - return pmutex; - -dmutex: - pthread_mutex_destroy(&pmutex->lock); -dattr: - pthread_mutexattr_destroy(&pmutex->mutexAttr); -fail: - free(pmutex); - return NULL; -} - -void epicsMutexOsdDestroy(struct epicsMutexOSD * pmutex) -{ - int status; - - status = pthread_cond_destroy(&pmutex->waitToBeOwner); - checkStatus(status, "pthread_cond_destroy"); -#if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED > 0 - status = pthread_condattr_destroy(&pmutex->condAttr); -#endif /*_POSIX_THREAD_PROCESS_SHARED*/ - status = pthread_mutex_destroy(&pmutex->lock); - checkStatus(status, "pthread_mutex_destroy"); - status = pthread_mutexattr_destroy(&pmutex->mutexAttr); - checkStatus(status, "pthread_mutexattr_destroy"); - free(pmutex); -} - -void epicsMutexOsdUnlock(struct epicsMutexOSD * pmutex) -{ - int status; - - status = mutexLock(&pmutex->lock); - checkStatus(status, "pthread_mutex_lock epicsMutexOsdUnlock"); - if(status) - return; - - if ((pmutex->count <= 0) || (pmutex->ownerTid != pthread_self())) { - pthread_mutex_unlock(&pmutex->lock); - checkStatus(status, "pthread_mutex_unlock epicsMutexOsdUnlock"); - errlogPrintf("epicsMutexOsdUnlock but caller is not owner\n"); - cantProceed("epicsMutexOsdUnlock but caller is not owner"); - return; - } - - pmutex->count--; - if (pmutex->count == 0) { - pmutex->owned = 0; - pmutex->ownerTid = 0; - status = pthread_cond_signal(&pmutex->waitToBeOwner); - checkStatusQuit(status, "pthread_cond_signal epicsMutexOsdUnlock", "epicsMutexOsdUnlock"); - } - - status = pthread_mutex_unlock(&pmutex->lock); - checkStatus(status, "pthread_mutex_unlock epicsMutexOsdUnlock"); -} - -static int condWait(pthread_cond_t *condId, pthread_mutex_t *mutexId) -{ - int status; - - while ((status = pthread_cond_wait(condId, mutexId)) == EINTR) { - errlogPrintf("pthread_cond_wait returned EINTR. Violates SUSv3\n"); - } - return status; -} - -epicsMutexLockStatus epicsMutexOsdLock(struct epicsMutexOSD * pmutex) -{ - pthread_t tid = pthread_self(); - int status; - - if (!pmutex || !tid) return epicsMutexLockError; - status = mutexLock(&pmutex->lock); - if (status == EINVAL) return epicsMutexLockError; - checkStatus(status, "pthread_mutex_lock epicsMutexOsdLock"); - if(status) - return epicsMutexLockError; - - while (pmutex->owned && !pthread_equal(pmutex->ownerTid, tid)) - condWait(&pmutex->waitToBeOwner, &pmutex->lock); - pmutex->ownerTid = tid; - pmutex->owned = 1; - pmutex->count++; - - status = pthread_mutex_unlock(&pmutex->lock); - checkStatus(status, "pthread_mutex_unlock epicsMutexOsdLock"); - if(status) - return epicsMutexLockError; - return epicsMutexLockOK; -} - -epicsMutexLockStatus epicsMutexOsdTryLock(struct epicsMutexOSD * pmutex) -{ - pthread_t tid = pthread_self(); - epicsMutexLockStatus result; - int status; - - status = mutexLock(&pmutex->lock); - if (status == EINVAL) return epicsMutexLockError; - checkStatus(status, "pthread_mutex_lock epicsMutexOsdTryLock"); - if(status) - return epicsMutexLockError; - - if (!pmutex->owned || pthread_equal(pmutex->ownerTid, tid)) { - pmutex->ownerTid = tid; - pmutex->owned = 1; - pmutex->count++; - result = epicsMutexLockOK; - } else { - result = epicsMutexLockTimeout; - } - - status = pthread_mutex_unlock(&pmutex->lock); - checkStatus(status, "pthread_mutex_unlock epicsMutexOsdTryLock"); - if(status) - return epicsMutexLockError; - return result; -} - -void epicsMutexOsdShow(struct epicsMutexOSD *pmutex,unsigned int level) -{ - printf("ownerTid %p count %d owned %d\n", - (void *)pmutex->ownerTid, pmutex->count, pmutex->owned); -} -#endif /*defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE)>=500 */ diff --git a/modules/libcom/src/osi/os/posix/osdPosixMutexPriv.h b/modules/libcom/src/osi/os/posix/osdPosixMutexPriv.h new file mode 100644 index 000000000..2b6846c91 --- /dev/null +++ b/modules/libcom/src/osi/os/posix/osdPosixMutexPriv.h @@ -0,0 +1,28 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* SPDX-License-Identifier: EPICS +* EPICS Base is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef osdPosixMutexPrivh +#define osdPosixMutexPrivh + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Returns ENOTSUP if requested mutextype is not supported */ +/* At the moment, only 0 (default non recursive mutex) and PTHREAD_MUTEX_RECURSIVE are supported */ +int osdPosixMutexInit(pthread_mutex_t *,int mutextype); + +#ifdef __cplusplus +} +#endif + +#endif /* osdPosixMutexPrivh */ diff --git a/modules/libcom/src/osi/os/posix/osdSpin.c b/modules/libcom/src/osi/os/posix/osdSpin.c index 1f8434b8c..f039e7c34 100644 --- a/modules/libcom/src/osi/os/posix/osdSpin.c +++ b/modules/libcom/src/osi/os/posix/osdSpin.c @@ -109,6 +109,8 @@ void epicsSpinUnlock(epicsSpinId spin) { * POSIX MUTEX IMPLEMENTATION */ +#include + typedef struct epicsSpin { pthread_mutex_t lock; } epicsSpin; @@ -121,8 +123,8 @@ epicsSpinId epicsSpinCreate(void) { if (!spin) goto fail; - status = pthread_mutex_init(&spin->lock, NULL); - checkStatus(status, "pthread_mutex_init"); + status = osdPosixMutexInit(&spin->lock, 0); + checkStatus(status, "osdPosixMutexInit"); if (status) goto fail; diff --git a/modules/libcom/src/osi/os/posix/osdThread.c b/modules/libcom/src/osi/os/posix/osdThread.c index fe375fd16..45255881f 100644 --- a/modules/libcom/src/osi/os/posix/osdThread.c +++ b/modules/libcom/src/osi/os/posix/osdThread.c @@ -32,6 +32,7 @@ #include "ellLib.h" #include "epicsEvent.h" #include "epicsMutex.h" +#include "osdPosixMutexPriv.h" #include "epicsString.h" #include "epicsThread.h" #include "cantProceed.h" @@ -327,10 +328,10 @@ static void once(void) int status; pthread_key_create(&getpthreadInfo,0); - status = pthread_mutex_init(&onceLock,0); - checkStatusOnceQuit(status,"pthread_mutex_init","epicsThreadInit"); - status = pthread_mutex_init(&listLock,0); - checkStatusOnceQuit(status,"pthread_mutex_init","epicsThreadInit"); + status = osdPosixMutexInit(&onceLock,0); + checkStatusOnceQuit(status,"osdPosixMutexInit","epicsThreadInit"); + status = osdPosixMutexInit(&listLock,0); + checkStatusOnceQuit(status,"osdPosixMutexInit","epicsThreadInit"); pcommonAttr = calloc(1,sizeof(commonAttr)); if(!pcommonAttr) checkStatusOnceQuit(errno,"calloc","epicsThreadInit"); status = pthread_attr_init(&pcommonAttr->attr);