| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * 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. |
| */ |
| /** |
| ************************************************************************ |
| * @brief Mutex for Android |
| * @note This file implements functions to manipulate mutex |
| ************************************************************************ |
| */ |
| |
| #include "M4OSA_Debug.h" |
| #include "M4OSA_Types.h" |
| #include "M4OSA_Error.h" |
| #include "M4OSA_Memory.h" |
| #include "M4OSA_Mutex.h" |
| |
| #include <pthread.h> |
| #include <errno.h> |
| |
| |
| /* Context for the mutex */ |
| typedef struct |
| { |
| M4OSA_UInt32 coreID; /* mutex context identifiant */ |
| pthread_mutex_t mutex; /* mutex */ |
| pthread_t threadOwnerID; /* thread owner identifiant */ |
| } M4OSA_MutexContext; |
| |
| |
| |
| /** |
| ************************************************************************ |
| * @brief This method creates a new mutex. |
| * @note This function creates and allocates a unique context. It's the |
| * OSAL real time responsibility for managing its context. It must |
| * be freed by the M4OSA_mutexClose function. The context parameter |
| * will be sent back to any OSAL core mutex functions to allow |
| * retrieving data associated to the opened mutex. |
| * @param pContext:(OUT) Context of the created mutex |
| * @return M4NO_ERROR: there is no error |
| * @return M4ERR_ALLOC: there is no more available memory |
| * @return M4ERR_CONTEXT_FAILED: the context creation failed |
| ************************************************************************ |
| */ |
| M4OSA_ERR M4OSA_mutexOpen(M4OSA_Context* pContext) |
| { |
| M4OSA_MutexContext* pMutexContext = (M4OSA_MutexContext*)M4OSA_NULL; |
| pthread_mutexattr_t attribute = { 0 }; |
| M4OSA_Bool opened = M4OSA_FALSE; |
| |
| M4OSA_TRACE1_1("M4OSA_mutexOpen\t\tM4OSA_Context* 0x%x", pContext); |
| M4OSA_DEBUG_IF2(M4OSA_NULL == pContext, M4ERR_PARAMETER, |
| "M4OSA_mutexOpen: pContext is M4OSA_NULL"); |
| |
| *pContext = M4OSA_NULL; |
| |
| pMutexContext = (M4OSA_MutexContext*)M4OSA_32bitAlignedMalloc(sizeof(M4OSA_MutexContext), |
| M4OSA_MUTEX, (M4OSA_Char*)"M4OSA_mutexOpen: mutex context"); |
| |
| if(M4OSA_NULL == pMutexContext) |
| { |
| M4OSA_DEBUG(M4ERR_ALLOC, "M4OSA_mutexOpen"); |
| return M4ERR_ALLOC; |
| } |
| |
| /* Initialize the mutex attribute. */ |
| if ( 0 == pthread_mutexattr_init( &attribute ) ) |
| { |
| /* Initialize the mutex type. */ |
| if ( 0 == pthread_mutexattr_settype( &attribute, PTHREAD_MUTEX_RECURSIVE ) ) |
| { |
| /* Initialize the mutex. */ |
| if (0 == pthread_mutex_init( &pMutexContext->mutex, &attribute ) ) |
| { |
| opened = M4OSA_TRUE; |
| } |
| } |
| |
| /* Destroy the mutex attribute. */ |
| pthread_mutexattr_destroy( &attribute ); |
| } |
| |
| if(!opened) |
| { |
| M4OSA_DEBUG(M4ERR_CONTEXT_FAILED, "M4OSA_mutexOpen: OS mutex creation failed"); |
| free(pMutexContext); |
| return M4ERR_CONTEXT_FAILED ; |
| } |
| |
| pMutexContext->coreID = M4OSA_MUTEX; |
| |
| pMutexContext->threadOwnerID = 0; |
| |
| *pContext = (M4OSA_Context) pMutexContext; |
| |
| return M4NO_ERROR; |
| } |
| |
| |
| |
| |
| /** |
| ************************************************************************ |
| * @brief This method locks the mutex. "Context" identifies the mutex. |
| * @note If the mutex is already locked, the calling thread blocks until |
| * the mutex becomes available (by calling M4OSA_mutexUnlock) or |
| * "timeout" is reached. This is a blocking call. |
| * @param context:(IN/OUT) Context of the mutex |
| * @param timeout:(IN) Time out in milliseconds |
| * @return M4NO_ERROR: there is no error |
| * @return M4ERR_PARAMETER: at least one parameter is NULL |
| * @return M4WAR_TIME_OUT: time out is elapsed before mutex has been |
| * available |
| * @return M4ERR_BAD_CONTEXT: provided context is not a valid one |
| ************************************************************************ |
| */ |
| M4OSA_ERR M4OSA_mutexLock(M4OSA_Context context, M4OSA_UInt32 timeout) |
| { |
| M4OSA_MutexContext* pMutexContext = (M4OSA_MutexContext*)context; |
| pthread_t currentThread; |
| int result; |
| struct timespec ts; |
| struct timespec left; |
| |
| M4OSA_TRACE1_2("M4OSA_mutexLock\t\tM4OSA_Context 0x%x\tM4OSA_UInt32 %d", |
| context, timeout); |
| |
| M4OSA_DEBUG_IF2(M4OSA_NULL == context, M4ERR_PARAMETER, |
| "M4OSA_mutexLock: context is M4OSA_NULL"); |
| M4OSA_DEBUG_IF2(pMutexContext->coreID != M4OSA_MUTEX, |
| M4ERR_BAD_CONTEXT, "M4OSA_mutexLock"); |
| |
| currentThread = pthread_self(); |
| |
| if(pMutexContext ->threadOwnerID == currentThread) |
| { |
| M4OSA_DEBUG(M4ERR_BAD_CONTEXT, "M4OSA_mutexLock: Thread tried to lock a mutex it already owns"); |
| return M4ERR_BAD_CONTEXT ; |
| } |
| |
| /* Lock the mutex. */ |
| if ( M4OSA_WAIT_FOREVER == timeout) |
| { |
| if ( 0 != pthread_mutex_lock(&pMutexContext->mutex) ) |
| { |
| M4OSA_DEBUG(M4ERR_BAD_CONTEXT, "M4OSA_mutexLock: OS mutex wait failed"); |
| return M4ERR_BAD_CONTEXT; |
| } |
| } |
| else |
| { |
| result = pthread_mutex_trylock(&pMutexContext->mutex); |
| while ( ( EBUSY == result ) && ( 0 < timeout ) ) |
| { |
| ts.tv_sec = 0; |
| if (1 <= timeout) |
| { |
| ts.tv_nsec = 1000000; |
| timeout -= 1; |
| } |
| else |
| { |
| ts.tv_nsec = timeout * 1000000; |
| timeout = 0; |
| } |
| nanosleep(&ts, &left); |
| result = pthread_mutex_trylock(&pMutexContext->mutex); |
| } |
| if (0 != result) |
| { |
| if (EBUSY == result) |
| { |
| return M4WAR_TIME_OUT; |
| } |
| else |
| { |
| M4OSA_DEBUG(M4ERR_BAD_CONTEXT, "M4OSA_mutexLock: OS mutex wait failed"); |
| return M4ERR_BAD_CONTEXT; |
| } |
| } |
| } |
| |
| pMutexContext->threadOwnerID = currentThread; |
| |
| return M4NO_ERROR; |
| } |
| |
| |
| |
| /** |
| ************************************************************************ |
| * @brief This method unlocks the mutex. The mutex is identified by |
| * its context |
| * @note The M4OSA_mutexLock unblocks the thread with the highest |
| * priority and made it ready to run. |
| * @note No hypotheses can be made on which thread will be un-blocked |
| * between threads with the same priority. |
| * @param context:(IN/OUT) Context of the mutex |
| * @return M4NO_ERROR: there is no error |
| * @return M4ERR_PARAMETER: at least one parameter is NULL |
| * @return M4ERR_BAD_CONTEXT: provided context is not a valid one |
| ************************************************************************ |
| */ |
| M4OSA_ERR M4OSA_mutexUnlock(M4OSA_Context context) |
| { |
| M4OSA_MutexContext* pMutexContext = (M4OSA_MutexContext*)context; |
| pthread_t currentThread; |
| |
| M4OSA_TRACE1_1("M4OSA_mutexUnlock\t\tM4OSA_Context 0x%x", context); |
| M4OSA_DEBUG_IF2(M4OSA_NULL == context, M4ERR_PARAMETER, |
| "M4OSA_mutexUnlock: context is M4OSA_NULL"); |
| M4OSA_DEBUG_IF2(M4OSA_MUTEX != pMutexContext->coreID, |
| M4ERR_BAD_CONTEXT, "M4OSA_mutexUnlock"); |
| |
| currentThread = pthread_self(); |
| |
| if(pMutexContext->threadOwnerID != currentThread) |
| { |
| M4OSA_DEBUG(M4ERR_BAD_CONTEXT, "M4OSA_mutexUnlock: Thread tried to unlock a mutex it doesn't own"); |
| return M4ERR_BAD_CONTEXT; |
| } |
| |
| pMutexContext->threadOwnerID = 0 ; |
| |
| pthread_mutex_unlock(&pMutexContext->mutex); |
| |
| return M4NO_ERROR; |
| } |
| |
| |
| |
| |
| /** |
| ************************************************************************ |
| * @brief This method deletes a mutex (identify by its context). After |
| * this call, the mutex and its context is no more useable. This |
| * function frees all the memory related to this mutex. |
| * @note It is an application issue to warrant no more threads are locked |
| * on the deleted mutex. |
| * @param context:(IN/OUT) Context of the mutex |
| * @return M4NO_ERROR: there is no error |
| * @return M4ERR_PARAMETER: at least one parameter is NULL |
| * @return M4ERR_BAD_CONTEXT: provided context is not a valid one |
| ************************************************************************ |
| */ |
| M4OSA_ERR M4OSA_mutexClose(M4OSA_Context context) |
| { |
| M4OSA_MutexContext* pMutexContext = (M4OSA_MutexContext*)context; |
| |
| M4OSA_TRACE1_1("M4OSA_mutexClose\t\tM4OSA_Context 0x%x", context); |
| |
| M4OSA_DEBUG_IF2(M4OSA_NULL == context, M4ERR_PARAMETER, |
| "M4OSA_mutexClose: context is M4OSA_NULL"); |
| M4OSA_DEBUG_IF2(pMutexContext->coreID != M4OSA_MUTEX, |
| M4ERR_BAD_CONTEXT, "M4OSA_mutexUnlock"); |
| |
| pthread_mutex_destroy(&pMutexContext->mutex); |
| |
| free( pMutexContext); |
| |
| return M4NO_ERROR; |
| } |
| |