posix: PI for epicsMutex, epicsEvent etc.

Enable priority-inheritance for primitives based on pthread_mutex_t.

Based on work by Till Straumann <till.straumann@psi.ch>
This commit is contained in:
2020-11-23 14:59:28 +01:00
committed by Michael Davidsaver
parent c4348dc6e0
commit 5a8b6e4111
7 changed files with 121 additions and 225 deletions

View File

@@ -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.

View File

@@ -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

View File

@@ -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)

View File

@@ -11,6 +11,10 @@
/* Author: Marty Kraimer Date: 13AUG1999 */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
@@ -21,6 +25,7 @@
#include <pthread.h>
#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 */

View File

@@ -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 <pthread.h>
#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 */

View File

@@ -109,6 +109,8 @@ void epicsSpinUnlock(epicsSpinId spin) {
* POSIX MUTEX IMPLEMENTATION
*/
#include <osdPosixMutexPriv.h>
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;

View File

@@ -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);