{
#endif /* __cplusplus */
+/**
+ * Value used for the owner field of an oc_mutex that doesn't have an owner.
+ */
+#define OC_INVALID_THREAD_ID 0
+
typedef struct oc_mutex_internal *oc_mutex;
typedef struct oc_cond_internal *oc_cond;
typedef struct oc_thread_internal *oc_thread;
bool oc_mutex_free(oc_mutex mutex);
/**
+ * On Debug builds, assert that the current thread owns or does not own a mutex.
+ *
+ * This function has no effect on Release builds.
+ *
+ * @param[in] mutex The mutex to assert on.
+ * @param[in] currentThreadIsOwner true if the current thread is expected to
+ * be the mutex owner, false otherwise.
+ *
+ */
+void oc_mutex_assert_owner(const oc_mutex mutex, bool currentThreadIsOwner);
+
+/**
* Creates new condition.
*
* @return Reference to newly created oc_cond, otherwise NULL.
typedef struct _tagMutexInfo_t
{
pthread_mutex_t mutex;
+
+ /**
+ * Catch some of the incorrect mutex usage, by tracking the mutex owner,
+ * on Debug builds.
+ */
+#ifndef NDEBUG
+ pthread_t owner;
+#endif
} oc_mutex_internal;
typedef struct _tagEventInfo_t
pthread_attr_t threadattr;
} oc_thread_internal;
+static pthread_t oc_get_current_thread_id()
+{
+ pthread_t id = pthread_self();
+ assert(OC_INVALID_THREAD_ID != id);
+ return id;
+}
+
OCThreadResult_t oc_thread_new(oc_thread *t, void *(*start_routine)(void *), void *arg)
{
OCThreadResult_t res = OC_THREAD_SUCCESS;
int ret=pthread_mutex_init(&(mutexInfo->mutex), PTHREAD_MUTEX_DEFAULT);
if (0 == ret)
{
+#ifndef NDEBUG
+ mutexInfo->owner = OC_INVALID_THREAD_ID;
+#endif
retVal = (oc_mutex) mutexInfo;
}
else
OIC_LOG_V(ERROR, TAG, "Pthread Mutex lock failed: %d", ret);
exit(ret);
}
+
+#ifndef NDEBUG
+ /**
+ * Updating the owner field must be performed while owning the lock,
+ * to solve race conditions with other threads using the same lock.
+ */
+ mutexInfo->owner = oc_get_current_thread_id();
+#endif
}
else
{
oc_mutex_internal *mutexInfo = (oc_mutex_internal*) mutex;
if (mutexInfo)
{
+#ifndef NDEBUG
+ /**
+ * Updating the owner field must be performed while owning the lock,
+ * to solve race conditions with other threads using the same lock.
+ */
+ mutexInfo->owner = OC_INVALID_THREAD_ID;
+#endif
+
int ret = pthread_mutex_unlock(&mutexInfo->mutex);
if(ret != 0)
{
}
}
+void oc_mutex_assert_owner(const oc_mutex mutex, bool currentThreadIsOwner)
+{
+#ifdef NDEBUG
+ (void)mutex;
+ (void)currentThreadIsOwner;
+#else
+ assert(NULL != mutex);
+ const oc_mutex_internal *mutexInfo = (const oc_mutex_internal*) mutex;
+
+ pthread_t currentThreadID = oc_get_current_thread_id();
+ if (currentThreadIsOwner)
+ {
+ assert(pthread_equal(mutexInfo->owner, currentThreadID));
+ }
+ else
+ {
+ assert(!pthread_equal(mutexInfo->owner, currentThreadID));
+ }
+#endif
+}
+
oc_cond oc_cond_new(void)
{
oc_cond retVal = NULL;
typedef struct _tagMutexInfo_t
{
CRITICAL_SECTION mutex;
+
+ /**
+ * Catch some of the incorrect mutex usage, by tracking the mutex owner,
+ * on Debug builds.
+ */
+#ifndef NDEBUG
+ DWORD owner;
+#endif
} oc_mutex_internal;
+static DWORD oc_get_current_thread_id()
+{
+ DWORD id = GetCurrentThreadId();
+ assert(OC_INVALID_THREAD_ID != id);
+ return id;
+}
+
typedef struct _tagEventInfo_t
{
CONDITION_VARIABLE cond;
oc_mutex_internal *mutexInfo = (oc_mutex_internal*) OICMalloc(sizeof(oc_mutex_internal));
if (NULL != mutexInfo)
{
+#ifndef NDEBUG
+ mutexInfo->owner = OC_INVALID_THREAD_ID;
+#endif
InitializeCriticalSection(&mutexInfo->mutex);
retVal = (oc_mutex)mutexInfo;
}
if (mutexInfo)
{
EnterCriticalSection(&mutexInfo->mutex);
+
+#ifndef NDEBUG
+ /**
+ * Updating the owner field must be performed while owning the lock,
+ * to solve race conditions with other threads using the same lock.
+ */
+ mutexInfo->owner = oc_get_current_thread_id();
+#endif
}
else
{
oc_mutex_internal *mutexInfo = (oc_mutex_internal*) mutex;
if (mutexInfo)
{
+#ifndef NDEBUG
+ /**
+ * Updating the owner field must be performed while owning the lock,
+ * to solve race conditions with other threads using the same lock.
+ */
+ mutexInfo->owner = OC_INVALID_THREAD_ID;
+#endif
+
LeaveCriticalSection(&mutexInfo->mutex);
}
else
}
}
+void oc_mutex_assert_owner(const oc_mutex mutex, bool currentThreadIsOwner)
+{
+#ifdef NDEBUG
+ OC_UNUSED(mutex);
+ OC_UNUSED(currentThreadIsOwner);
+#else
+ assert(NULL != mutex);
+ const oc_mutex_internal *mutexInfo = (const oc_mutex_internal*) mutex;
+
+ DWORD currentThreadID = oc_get_current_thread_id();
+ if (currentThreadIsOwner)
+ {
+ assert(mutexInfo->owner == currentThreadID);
+ }
+ else
+ {
+ assert(mutexInfo->owner != currentThreadID);
+ }
+#endif
+}
+
oc_cond oc_cond_new(void)
{
oc_cond retVal = NULL;
OIC_LOG_V(DEBUG, NET_SSL_TAG, "In %s(peer = %s:%u, attribute = %#x)", __func__,
peer->addr, (uint32_t)peer->port, newAttribute);
- oc_mutex_lock(g_sslContextMutex);
+ // Acquiring g_sslContextMutex recursively here is not supported, so assert
+ // that the caller already owns this mutex. IOT-1876 tracks a possible
+ // refactoring of the code that is using g_sslContextMutex, to address these
+ // API quirks.
+ oc_mutex_assert_owner(g_sslContextMutex, true);
if (NULL == g_caSslContext)
{
}
}
- oc_mutex_unlock(g_sslContextMutex);
-
OIC_LOG_V(DEBUG, NET_SSL_TAG, "Out %s -> %s", __func__, result ? "success" : "failed");
return result;
}