From ef7e4d4e4958de8847381575b84cc0537ec2b661 Mon Sep 17 00:00:00 2001 From: M Joonas Pihlaja Date: Thu, 6 May 2010 22:47:36 +0300 Subject: Support mutex type attributes. This patch adds support for the pthread_mutex_settype() function. To have correct single-threaded semantics for PTHREAD_MUTEX_ERRORCHECK kind mutexes, the patch also implements the following functions: pthread_mutex_init, pthread_mutex_lock, pthread_mutex_unlock, pthread_mutexattr_gettype, pthread_mutexattr_init, pthread_mutexattr_destroy If any one of these functions is not implemented by the host system, then all of them are provided by pthread-stubs. This is to avoid mismatches between pthread-stubs and the host. --- list.m4 | 34 ++++++------ stubs.c.m4 | 171 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 190 insertions(+), 15 deletions(-) diff --git a/list.m4 b/list.m4 index 01f5e6f..9a97952 100644 --- a/list.m4 +++ b/list.m4 @@ -1,15 +1,19 @@ -alias(zero, pthread_t, pthread_self) -alias(zero, int, pthread_mutex_init) -alias(zero, int, pthread_mutex_destroy) -alias(zero, int, pthread_mutex_lock) -alias(zero, int, pthread_mutex_unlock) -alias(zero, int, pthread_cond_init) -alias(zero, int, pthread_cond_destroy) -alias(zero, int, pthread_condattr_init) -alias(zero, int, pthread_condattr_destroy) -alias(abort, int, pthread_cond_wait) -alias(abort, int, pthread_cond_timedwait) -alias(zero, int, pthread_cond_signal) -alias(zero, int, pthread_cond_broadcast) -alias(equal, int, pthread_equal) -alias(exit, void, pthread_exit) +alias(zero, pthread_t, pthread_self) +alias(mutex_init, int, pthread_mutex_init) +alias(zero, int, pthread_mutex_destroy) +alias(mutex_lock, int, pthread_mutex_lock) +alias(mutex_unlock, int, pthread_mutex_unlock) +alias(zero, int, pthread_cond_init) +alias(zero, int, pthread_cond_destroy) +alias(zero, int, pthread_condattr_init) +alias(zero, int, pthread_condattr_destroy) +alias(abort, int, pthread_cond_wait) +alias(abort, int, pthread_cond_timedwait) +alias(zero, int, pthread_cond_signal) +alias(zero, int, pthread_cond_broadcast) +alias(equal, int, pthread_equal) +alias(exit, void, pthread_exit) +alias(zero, int, pthread_mutexattr_destroy) +alias(mutexattr_init, int, pthread_mutexattr_init) +alias(mutexattr_settype, int, pthread_mutexattr_settype) +alias(mutexattr_gettype, int, pthread_mutexattr_gettype) diff --git a/stubs.c.m4 b/stubs.c.m4 index d2cb428..87fff4f 100644 --- a/stubs.c.m4 +++ b/stubs.c.m4 @@ -12,6 +12,19 @@ m4_define([alias], [dnl #endif ]) +# dependency_group(, [...]) +# Declare that if any function in the function list needs +# to be stubbed using our implementation, then all of them do. +# If our implementations are used, then additionally #define +# NEED_. +m4_define([dependency_group],[dnl +#if m4_foreach([x], [$2], [!defined(HAVE_[]upcase(x)) || ])0 +# define NEED_[]upcase($1) 1 +m4_foreach([x], [$2], [dnl +# undef HAVE_[]upcase(x) +])dnl +#endif]) + m4_divert(0)dnl /* Copyright (C) 2006 Diego Pettenò * Copyright (C) 2010 M Joonas Pihlaja @@ -44,8 +57,28 @@ m4_divert(0)dnl #include #include +#include #include "config.h" +/* Gah.. linux pthreads doesn't give us the posix mutex types by + * default. Map them manually. */ +#if defined(__linux__) +# define PTHREAD_MUTEX_NORMAL PTHREAD_MUTEX_TIMED_NP +# define PTHREAD_MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE_NP +# define PTHREAD_MUTEX_ERRORCHECK PTHREAD_MUTEX_ERRORCHECK_NP +# define PTHREAD_MUTEX_DEFAULT PTHREAD_MUTEX_NORMAL +#endif + +dependency_group(MUTEXATTR_GROUP, [ + pthread_mutex_init, + pthread_mutex_destroy, + pthread_mutex_lock, + pthread_mutex_unlock, + pthread_mutexattr_init, + pthread_mutexattr_destroy, + pthread_mutexattr_settype, + pthread_mutexattr_gettype]) + m4_include([list.m4]) #ifdef NEED_ZERO_STUB @@ -75,3 +108,141 @@ static void __pthread_exit_stub(void *ret) exit(EXIT_SUCCESS); } #endif + +#if NEED_MUTEXATTR_GROUP +typedef struct { + int mutex_type; +} __pthread_mutexattr_stub_t; + +typedef struct { + unsigned locked_count; + __pthread_mutexattr_stub_t attr; +} __pthread_mutex_stub_t; +#endif + +#ifdef NEED_MUTEXATTR_INIT_STUB +static int __pthread_mutexattr_init_stub(__pthread_mutexattr_stub_t *attr) +{ + if (!attr) return EINVAL; + attr->mutex_type = PTHREAD_MUTEX_DEFAULT; + return 0; +} +#endif + +#ifdef NEED_MUTEXATTR_SETTYPE_STUB +static int __pthread_mutexattr_settype_stub(__pthread_mutexattr_stub_t *attr, + int type) +{ + if (!attr) return EINVAL; + if (type == PTHREAD_MUTEX_NORMAL || + type == PTHREAD_MUTEX_ERRORCHECK || + type == PTHREAD_MUTEX_RECURSIVE || + type == PTHREAD_MUTEX_DEFAULT) + { + attr->mutex_type = type; + return 0; + } + return EINVAL; +} +#endif + +#ifdef NEED_MUTEXATTR_GETTYPE_STUB +static int __pthread_mutexattr_gettype_stub(__pthread_mutexattr_stub_t *attr, + int *type) +{ + if (!attr || !type) return EINVAL; + *type = attr->mutex_type; + return 0; +} +#endif + +#ifdef NEED_MUTEX_INIT_STUB +static int __pthread_mutex_init_stub(__pthread_mutex_stub_t *mutex, + __pthread_mutexattr_stub_t *attr) +{ + if (!mutex) return EINVAL; + mutex->locked_count = 0; + if (attr) { + mutex->attr = *attr; + } else { + __pthread_mutexattr_init_stub(&mutex->attr); + } + return 0; +} +#endif + +#ifdef NEED_MUTEX_LOCK_STUB +static int __pthread_mutex_lock_stub(__pthread_mutex_stub_t *mutex) +{ + int type; + if (!mutex) return EINVAL; + + if (mutex->locked_count == 0) { + mutex->locked_count = 1; + return 0; + } + + type = mutex->attr.mutex_type; + + if (type == PTHREAD_MUTEX_RECURSIVE) { + if (mutex->locked_count+1 == 0) + /* "The mutex could not be acquired because the maximum + * number of recursive locks for mutex has been + * exceeded." */ + return EAGAIN; + ++mutex->locked_count; + return 0; + } + + if (type == PTHREAD_MUTEX_NORMAL) { + /* "If the mutex type is PTHREAD_MUTEX_NORMAL, deadlock + * detection shall not be provided. Attempting to relock the + * mutex causes deadlock." Uh... okay. :| */ + while (1) {} + return EDEADLK; + } + + if (type == PTHREAD_MUTEX_DEFAULT || + type == PTHREAD_MUTEX_ERRORCHECK) + { + /* "If the mutex type is PTHREAD_MUTEX_DEFAULT, attempting to + * recursively lock the mutex results in undefined + * behavior." */ + /* "If a thread attempts to relock a + * [PTHREAD_MUTEX_ERRORCHECK] mutex that it has already + * locked, an error shall be returned." */ + return EDEADLK; + } + + /* "The value specified by mutex does not refer to an + * initialized mutex object." */ + return EINVAL; +} +#endif + +#ifdef NEED_MUTEX_UNLOCK_STUB +static int __pthread_mutex_unlock_stub(__pthread_mutex_stub_t *mutex) +{ + if (!mutex) return EINVAL; + + if (mutex->locked_count == 0) + /* "The current thread does not own the mutex." */ + return EPERM; + + if (mutex->locked_count == 1) { + mutex->locked_count = 0; + return 0; + } + + if (mutex->attr.mutex_type == PTHREAD_MUTEX_RECURSIVE) { + --mutex->locked_count; + return 0; + } + + /* "The value specified by mutex does not refer to an initialized + * mutex object." */ + /* or "undefined behaviour." */ + /* or "an error shall be returned." */ + return EINVAL; +} +#endif -- cgit v1.2.3