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:
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 */
|
||||
|
||||
28
modules/libcom/src/osi/os/posix/osdPosixMutexPriv.h
Normal file
28
modules/libcom/src/osi/os/posix/osdPosixMutexPriv.h
Normal 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 */
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user