Commit 7beb9fb1 authored by Daniel Ferguson's avatar Daniel Ferguson Committed by Dave Thaler

Add generic thread & synchro facilities

* Split Windows and Posix threading and
  synchronization facilities into separate
  files.
* Add ca_thread_[new|free|wait]
* Moved facilities to a more generalized location
  within the iotivity directory hierarchy
* TODO: add ca_mutex_init

Change-Id: I7718a9f5bd07610f0f989ba4729cb89111331238
Signed-off-by: default avatarDaniel Ferguson <daniel.j.ferguson@intel.com>
Reviewed-on: https://gerrit.iotivity.org/gerrit/10743Reviewed-by: default avatarDavid Antler <david.a.antler@intel.com>
Tested-by: default avatarjenkins-iotivity <jenkins-iotivity@opendaylight.org>
Reviewed-by: default avatarDave Thaler <dthaler@microsoft.com>
parent 4c49d8fb
......@@ -138,7 +138,8 @@ env.AppendUnique(CPPPATH = [
os.path.join(Dir('.').abspath, 'oic_malloc', 'include'),
os.path.join(Dir('.').abspath, 'oic_string', 'include'),
os.path.join(Dir('.').abspath, 'oic_time', 'include'),
os.path.join(Dir('.').abspath, 'ocrandom', 'include')
os.path.join(Dir('.').abspath, 'ocrandom', 'include'),
os.path.join(Dir('.').abspath, 'octhread', 'include')
])
if target_os == 'tizen':
......@@ -166,8 +167,16 @@ common_src = [
'oic_string/src/oic_string.c',
'oic_malloc/src/oic_malloc.c',
'oic_time/src/oic_time.c',
'ocrandom/src/ocrandom.c',
'ocrandom/src/ocrandom.c'
]
if env['POSIX_SUPPORTED']:
common_src.append('octhread/src/posix/octhread.c')
elif target_os in ['windows']:
common_src.append('octhread/src/windows/octhread.c')
else:
common_src.append('octhread/src/noop/octhread.c')
commonlib = common_env.StaticLibrary('c_common', common_src)
common_env.InstallTarget(commonlib, 'c_common')
common_env.UserInstallTargetLib(commonlib, 'c_common')
......
......@@ -24,10 +24,13 @@
* This file provides APIs related to mutex and semaphores.
*/
#ifndef CA_MUTEX_H_
#define CA_MUTEX_H_
#ifndef OC_THREAD_H_
#define OC_THREAD_H_
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include "cacommon.h"
#ifdef __cplusplus
extern "C"
......@@ -36,6 +39,7 @@ extern "C"
typedef struct ca_mutex_internal *ca_mutex;
typedef struct ca_cond_internal *ca_cond;
typedef struct ca_thread_internal *ca_thread;
/**
* Enums for ca_cond_wait_for return values.
......@@ -47,6 +51,52 @@ typedef enum
CA_WAIT_TIMEDOUT = -2 /**< Condition Timed Out. */
} CAWaitResult_t;
typedef enum
{
CA_THREAD_SUCCESS = 0,
CA_THREAD_ALLOCATION_FAILURE = 1,
CA_THREAD_CREATE_FAILURE=2,
CA_THREAD_INVALID=3,
CA_THREAD_WAIT_FAILURE=4,
CA_THREAD_INVALID_PARAMETER=5
} CAThreadResult_t;
/**
* Allocates, and starts a new thread
*
* @param[out] t The thread that will refer to a newly allocated, and started thread
* @param[in] start_routine The function that will execute in a new thread
* @param[in] arg The information passed to the start_routine
* @return CAThreadResult_t An enumeration of possible outcomes
* @retval CA_THREAD_SUCCESS If a thread was successfully allocated and started.
* @retval CA_THREAD_ALLOCATION_FAILURE If a thread was unable to be allocated
* @retval CA_THREAD_CREATE_FAILURE If a thread was unable to be started
*
*/
CAThreadResult_t ca_thread_new(ca_thread *t, void *(*start_routine)(void *), void *arg);
/**
* Frees a thread previously allocated with ca_thread_new()
*
* @param[in] t The thread to be unallocated
* @return CAThreadResult_t An enumeration of possible outcomes
* @retval CA_THREAD_SUCCESS If a thread was successfully unallocated
* @retval CA_THREAD_INVALID_PARAMETER If param t is NULL
*
*/
CAThreadResult_t ca_thread_free(ca_thread t);
/**
* Block until a thread's execution has been completed
*
* @param[in] t The thread to be waited on
* @return CAThreadResult_t An enumeration of possible outcomes
* @retval CA_THREAD_SUCCESS If the thread successfully completed execution
* @retval CA_THREAD_WAIT_FAILURE If a problem occured while waiting for execution of the thread to complete
*
*/
CAThreadResult_t ca_thread_wait(ca_thread t);
/**
* Creates new mutex.
*
......@@ -75,6 +125,9 @@ void ca_mutex_unlock(ca_mutex mutex);
* Free the mutex.
*
* @param mutex The mutex to be freed.
* @return bool to indicate success or failure
* @retval true if mutex was freed successfully
* @retval false if mutex parameter is invalid
*
*/
bool ca_mutex_free(ca_mutex mutex);
......@@ -82,7 +135,7 @@ bool ca_mutex_free(ca_mutex mutex);
/**
* Creates new condition.
*
* @return Reference to newly created ::ca_cond, otherwise NULL.
* @return Reference to newly created ca_cond, otherwise NULL.
*
*/
ca_cond ca_cond_new(void);
......
......@@ -26,8 +26,7 @@
* This file provides APIs related to mutex with no operation
* for Singlethread implementation.
*/
#include "camutex.h"
#include "octhread.h"
/**
* TAG
......@@ -37,18 +36,17 @@
typedef struct _tagMutexInfo_t
{
#if defined(_MSC_VER)
uint8_t unused; //VS doesnt like empty structs
#endif
} ca_mutex_internal;
typedef struct _tagEventInfo_t
{
#if defined(_MSC_VER)
uint8_t unused; //VS doesnt like empty structs
#endif
} ca_cond_internal;
typedef struct _tagThreadInfo_t
{
} ca_thread_internal;
/**
* @var g_mutexInfo
* @brief This is used to return a non NULL value for ca_mutex_new().
......@@ -61,6 +59,21 @@ static ca_mutex_internal g_mutexInfo = { 0 };
*/
static ca_cond_internal g_condInfo = { 0 };
CAThreadResult_t ca_thread_new(ca_thread *t, void *(*start_routine)(void *), void *arg)
{
return CA_THREAD_CREATE_FAILURE;
}
CAThreadResult_t ca_thread_free(ca_thread t)
{
return CA_THREAD_INVALID;
}
CAThreadResult_t ca_thread_wait(ca_thread t)
{
return CA_THREAD_INVALID;
}
ca_mutex ca_mutex_new(void)
{
return (ca_mutex)&g_mutexInfo;
......
......@@ -37,6 +37,7 @@
#endif
#include "iotivity_config.h"
#include "octhread.h"
#ifdef HAVE_STRING_H
#include <string.h>
#endif
......@@ -52,14 +53,10 @@
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef HAVE_WINSOCK2_H
#include <winsock2.h>
#endif
#include <stdio.h>
#include <errno.h>
#include <assert.h>
#include <oic_malloc.h>
#include "camutex.h"
#include "logger.h"
/**
......@@ -90,33 +87,85 @@ static const uint64_t NANOSECS_PER_SEC = 1000000000L;
typedef struct _tagMutexInfo_t
{
#if defined(_WIN32)
CRITICAL_SECTION mutex;
#else
pthread_mutex_t mutex;
#endif
} ca_mutex_internal;
typedef struct _tagEventInfo_t
{
#if defined(_WIN32)
CONDITION_VARIABLE cond;
#else
pthread_cond_t cond;
pthread_condattr_t condattr;
#endif
} ca_cond_internal;
typedef struct _tagThreadInfo_t
{
pthread_t thread;
pthread_attr_t threadattr;
} ca_thread_internal;
CAThreadResult_t ca_thread_new(ca_thread *t, void *(*start_routine)(void *), void *arg)
{
CAThreadResult_t res = CA_THREAD_SUCCESS;
ca_thread_internal *threadInfo = (ca_thread_internal*)OICMalloc(sizeof(ca_thread_internal));
if (NULL != threadInfo)
{
int result = pthread_create(&threadInfo->thread, NULL, start_routine, arg);
if (result != 0)
{
res = CA_THREAD_CREATE_FAILURE;
*t = NULL;
OICFree(threadInfo);
OIC_LOG_V(ERROR, TAG, "%s: pthread_create failed", __func__);
}
else
{
*t = (ca_thread)threadInfo;
}
}
else
{
OIC_LOG_V(ERROR, TAG, "%s Failed to allocate thread!", __func__);
*t = NULL;
res = CA_THREAD_ALLOCATION_FAILURE;
}
return res;
}
CAThreadResult_t ca_thread_free(ca_thread t)
{
CAThreadResult_t res = CA_THREAD_SUCCESS;
ca_thread_internal *threadInfo = (ca_thread_internal*) t;
if (threadInfo)
{
OICFree(threadInfo);
}
else
{
OIC_LOG_V(ERROR, TAG, "%s Invalid thread !", __func__);
res = CA_THREAD_INVALID;
}
return res;
}
CAThreadResult_t ca_thread_wait(ca_thread t)
{
CAThreadResult_t res = CA_THREAD_SUCCESS;
ca_thread_internal *threadInfo = (ca_thread_internal*) t;
int joinres = pthread_join(threadInfo->thread, NULL);
if (0 != joinres)
{
OIC_LOG_V(ERROR, TAG, "Failed to join thread with error %d", joinres);
res = CA_THREAD_WAIT_FAILURE;
}
return res;
}
ca_mutex ca_mutex_new(void)
{
ca_mutex retVal = NULL;
ca_mutex_internal *mutexInfo = (ca_mutex_internal*) OICMalloc(sizeof(ca_mutex_internal));
if (NULL != mutexInfo)
{
#if defined(_WIN32)
InitializeCriticalSection(&mutexInfo->mutex);
retVal = (ca_mutex)mutexInfo;
#else
// create the mutex with the attributes set
int ret=pthread_mutex_init(&(mutexInfo->mutex), PTHREAD_MUTEX_DEFAULT);
if (0 == ret)
......@@ -128,7 +177,6 @@ ca_mutex ca_mutex_new(void)
OIC_LOG_V(ERROR, TAG, "%s Failed to initialize mutex !", __func__);
OICFree(mutexInfo);
}
#endif
}
else
{
......@@ -141,15 +189,9 @@ ca_mutex ca_mutex_new(void)
bool ca_mutex_free(ca_mutex mutex)
{
bool bRet=false;
ca_mutex_internal *mutexInfo = (ca_mutex_internal*) mutex;
if (mutexInfo)
{
#if defined(_WIN32)
DeleteCriticalSection(&mutexInfo->mutex);
OICFree(mutexInfo);
bRet=true;
#else
int ret = pthread_mutex_destroy(&mutexInfo->mutex);
if (0 == ret)
{
......@@ -160,7 +202,6 @@ bool ca_mutex_free(ca_mutex mutex)
{
OIC_LOG_V(ERROR, TAG, "%s Failed to free mutex !", __func__);
}
#endif
}
else
{
......@@ -175,21 +216,16 @@ void ca_mutex_lock(ca_mutex mutex)
ca_mutex_internal *mutexInfo = (ca_mutex_internal*) mutex;
if (mutexInfo)
{
#if defined(_WIN32)
EnterCriticalSection(&mutexInfo->mutex);
#else
int ret = pthread_mutex_lock(&mutexInfo->mutex);
if(ret != 0)
{
OIC_LOG_V(ERROR, TAG, "Pthread Mutex lock failed: %d", ret);
exit(ret);
}
#endif
}
else
{
OIC_LOG_V(ERROR, TAG, "%s Invalid mutex !", __func__);
return;
}
}
......@@ -198,9 +234,6 @@ void ca_mutex_unlock(ca_mutex mutex)
ca_mutex_internal *mutexInfo = (ca_mutex_internal*) mutex;
if (mutexInfo)
{
#if defined(_WIN32)
LeaveCriticalSection(&mutexInfo->mutex);
#else
int ret = pthread_mutex_unlock(&mutexInfo->mutex);
if(ret != 0)
{
......@@ -208,12 +241,10 @@ void ca_mutex_unlock(ca_mutex mutex)
exit(ret);
}
(void)ret;
#endif
}
else
{
OIC_LOG_V(ERROR, TAG, "%s: Invalid mutex !", __func__);
return;
}
}
......@@ -223,10 +254,6 @@ ca_cond ca_cond_new(void)
ca_cond_internal *eventInfo = (ca_cond_internal*) OICMalloc(sizeof(ca_cond_internal));
if (NULL != eventInfo)
{
#if defined(_WIN32)
InitializeConditionVariable(&eventInfo->cond);
retVal = (ca_cond) eventInfo;
#else
int ret = pthread_condattr_init(&(eventInfo->condattr));
if(0 != ret)
{
......@@ -266,7 +293,6 @@ ca_cond ca_cond_new(void)
pthread_condattr_destroy(&(eventInfo->condattr));
OICFree(eventInfo);
}
#endif
}
else
{
......@@ -281,9 +307,6 @@ void ca_cond_free(ca_cond cond)
ca_cond_internal *eventInfo = (ca_cond_internal*) cond;
if (eventInfo != NULL)
{
#if defined(_WIN32)
OICFree(cond);
#else
int ret = pthread_cond_destroy(&(eventInfo->cond));
int ret2 = pthread_condattr_destroy(&(eventInfo->condattr));
if (0 == ret && 0 == ret2)
......@@ -295,7 +318,6 @@ void ca_cond_free(ca_cond cond)
OIC_LOG_V(ERROR, TAG, "%s: Failed to destroy condition variable %d, %d",
__func__, ret, ret2);
}
#endif
}
else
{
......@@ -308,15 +330,11 @@ void ca_cond_signal(ca_cond cond)
ca_cond_internal *eventInfo = (ca_cond_internal*) cond;
if (eventInfo != NULL)
{
#if defined(_WIN32)
WakeConditionVariable(&eventInfo->cond);
#else
int ret = pthread_cond_signal(&(eventInfo->cond));
if (0 != ret)
{
OIC_LOG_V(ERROR, TAG, "%s: Failed to signal condition variable", __func__);
}
#endif
}
else
{
......@@ -329,15 +347,11 @@ void ca_cond_broadcast(ca_cond cond)
ca_cond_internal* eventInfo = (ca_cond_internal*) cond;
if (eventInfo != NULL)
{
#if defined(_WIN32)
WakeAllConditionVariable(&eventInfo->cond);
#else
int ret = pthread_cond_broadcast(&(eventInfo->cond));
if (0 != ret)
{
OIC_LOG_V(ERROR, TAG, "%s: failed to signal condition variable", __func__);
}
#endif
}
else
{
......@@ -357,7 +371,6 @@ void ca_cond_wait(ca_cond cond, ca_mutex mutex)
}
#endif
#if !defined(_WIN32)
struct timespec ca_get_current_time()
{
#if defined(__ANDROID__) || _POSIX_TIMERS > 0
......@@ -383,7 +396,6 @@ void ca_add_microseconds_to_timespec(struct timespec* ts, uint64_t microseconds)
ts->tv_nsec = (totalNs)% NANOSECS_PER_SEC;
ts->tv_sec += secPart + secOfNs;
}
#endif
CAWaitResult_t ca_cond_wait_for(ca_cond cond, ca_mutex mutex, uint64_t microseconds)
{
......@@ -406,25 +418,6 @@ CAWaitResult_t ca_cond_wait_for(ca_cond cond, ca_mutex mutex, uint64_t microseco
if (microseconds > 0)
{
#if defined(_WIN32)
// Wait for the given time
DWORD milli = (DWORD)(microseconds / 1000);
if (!SleepConditionVariableCS(&eventInfo->cond, &mutexInfo->mutex, milli))
{
if (GetLastError() == ERROR_TIMEOUT)
{
retVal = CA_WAIT_TIMEDOUT;
}
else
{
OIC_LOG_V(ERROR, TAG, "SleepConditionVariableCS() with Timeout failed %i", GetLastError());
retVal = CA_WAIT_INVAL;
}
}else
{
retVal = CA_WAIT_SUCCESS;
}
#else
int ret = 0;
struct timespec abstime = { .tv_sec = 0 };
......@@ -463,25 +456,12 @@ CAWaitResult_t ca_cond_wait_for(ca_cond cond, ca_mutex mutex, uint64_t microseco
retVal = CA_WAIT_INVAL;
break;
}
#endif
}
else
{
#if defined(_WIN32)
// Wait forever
if (!SleepConditionVariableCS(&eventInfo->cond, &mutexInfo->mutex, INFINITE))
{
OIC_LOG_V(ERROR, TAG, "SleepConditionVariableCS() w/o Timeout failed %i", GetLastError());
retVal = CA_WAIT_INVAL;
}else
{
retVal = CA_WAIT_SUCCESS;
}
#else
// Wait forever
int ret = pthread_cond_wait(&eventInfo->cond, &mutexInfo->mutex);
retVal = ret == 0 ? CA_WAIT_SUCCESS : CA_WAIT_INVAL;
#endif
}
return retVal;
}
......
/* *****************************************************************
*
* Copyright 2016 Intel Corporation
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************/
/**
* @file
* This file provides APIs related to mutex and semaphores.
*/
#include "iotivity_config.h"
#include "octhread.h"
#include <string.h>
#include <time.h>
#include <winsock2.h>
#include <stdio.h>
#include <errno.h>
#include <oic_malloc.h>
#include "logger.h"
static const uint64_t USECS_PER_MSEC = 1000;
typedef struct _tagMutexInfo_t
{
CRITICAL_SECTION mutex;
} ca_mutex_internal;
typedef struct _tagEventInfo_t
{
CONDITION_VARIABLE cond;
} ca_cond_internal;
typedef struct _tagThreadInfo_t
{
HANDLE handle;
} ca_thread_internal;
CAThreadResult_t ca_thread_new(ca_thread *t, void *(*start_routine)(void *), void *arg)
{
CAThreadResult_t res = CA_THREAD_SUCCESS;
ca_thread_internal *threadInfo = (ca_thread_internal*)OICMalloc(sizeof(ca_thread_internal));
if (NULL != threadInfo)
{
threadInfo->handle = CreateThread(NULL, 0, (PTHREAD_START_ROUTINE)start_routine, arg, 0, NULL);
if (threadInfo->handle == NULL)
{
res = CA_THREAD_CREATE_FAILURE;
*t = NULL;
OICFree(threadInfo);
OIC_LOG_V(ERROR, TAG, "%s: CreateThread failed: %i", __func__, GetLastError());
}
else
{
*t = (ca_thread)threadInfo;
}
}
else
{
OIC_LOG_V(ERROR, TAG, "%s Failed to allocate thread!", __func__);
*t = NULL;
res = CA_THREAD_ALLOCATION_FAILURE;
}
return res;
}
CAThreadResult_t ca_thread_free(ca_thread t)
{
CAThreadResult_t res = CA_THREAD_INVALID_PARAMETER;
ca_thread_internal *threadInfo = (ca_thread_internal*) t;
if (threadInfo)
{
CloseHandle(threadInfo->handle);
OICFree(threadInfo);
res = CA_THREAD_SUCCESS;
}
else
{
OIC_LOG_V(ERROR, TAG, "%s Invalid thread !", __func__);
}
return res;
}
CAThreadResult_t ca_thread_wait(ca_thread t)
{
CAThreadResult_t res = CA_THREAD_SUCCESS;
ca_thread_internal *threadInfo = (ca_thread_internal*) t;
DWORD joinres = WaitForSingleObject(threadInfo->handle, INFINITE);
if (WAIT_OBJECT_0 != joinres)
{
OIC_LOG(ERROR, TAG, "Failed to join thread");
res = CA_THREAD_WAIT_FAILURE;
}
else
{
CloseHandle(threadInfo->handle);
}
return res;
}
ca_mutex ca_mutex_new(void)
{
ca_mutex retVal = NULL;
ca_mutex_internal *mutexInfo = (ca_mutex_internal*) OICMalloc(sizeof(ca_mutex_internal));
if (NULL != mutexInfo)
{