1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16/**
17 ************************************************************************
18 * @brief        Mutex for Android
19 * @note         This file implements functions to manipulate mutex
20 ************************************************************************
21*/
22
23#include "M4OSA_Debug.h"
24#include "M4OSA_Types.h"
25#include "M4OSA_Error.h"
26#include "M4OSA_Memory.h"
27#include "M4OSA_Mutex.h"
28
29#include <pthread.h>
30#include <errno.h>
31
32
33/* Context for the mutex */
34typedef struct
35{
36   M4OSA_UInt32     coreID;               /* mutex context identifiant */
37   pthread_mutex_t  mutex;                /* mutex */
38   pthread_t        threadOwnerID;        /* thread owner identifiant */
39} M4OSA_MutexContext;
40
41
42
43/**
44 ************************************************************************
45 * @brief      This method creates a new mutex.
46 * @note       This function creates and allocates a unique context. It's the
47 *             OSAL real time responsibility for managing its context. It must
48 *             be freed by the M4OSA_mutexClose function. The context parameter
49 *             will be sent back to any OSAL core mutex functions to allow
50 *             retrieving data associated to the opened mutex.
51 * @param      pContext:(OUT) Context of the created mutex
52 * @return     M4NO_ERROR: there is no error
53 * @return     M4ERR_ALLOC: there is no more available memory
54 * @return     M4ERR_CONTEXT_FAILED: the context creation failed
55 ************************************************************************
56*/
57M4OSA_ERR M4OSA_mutexOpen(M4OSA_Context* pContext)
58{
59    M4OSA_MutexContext* pMutexContext = (M4OSA_MutexContext*)M4OSA_NULL;
60    pthread_mutexattr_t attribute = { 0 };
61    M4OSA_Bool opened = M4OSA_FALSE;
62
63    M4OSA_TRACE1_1("M4OSA_mutexOpen\t\tM4OSA_Context* 0x%x", pContext);
64    M4OSA_DEBUG_IF2(M4OSA_NULL == pContext, M4ERR_PARAMETER,
65                                     "M4OSA_mutexOpen: pContext is M4OSA_NULL");
66
67    *pContext = M4OSA_NULL;
68
69    pMutexContext = (M4OSA_MutexContext*)M4OSA_32bitAlignedMalloc(sizeof(M4OSA_MutexContext),
70                    M4OSA_MUTEX, (M4OSA_Char*)"M4OSA_mutexOpen: mutex context");
71
72    if(M4OSA_NULL == pMutexContext)
73    {
74        M4OSA_DEBUG(M4ERR_ALLOC, "M4OSA_mutexOpen");
75        return M4ERR_ALLOC;
76    }
77
78    /* Initialize the mutex attribute. */
79    if ( 0 == pthread_mutexattr_init( &attribute ) )
80    {
81        /* Initialize the mutex type. */
82        if ( 0 == pthread_mutexattr_settype( &attribute, PTHREAD_MUTEX_RECURSIVE ) )
83        {
84            /* Initialize the mutex. */
85            if (0 == pthread_mutex_init( &pMutexContext->mutex, &attribute ) )
86            {
87                opened = M4OSA_TRUE;
88            }
89        }
90
91        /* Destroy the mutex attribute. */
92        pthread_mutexattr_destroy( &attribute );
93    }
94
95    if(!opened)
96    {
97        M4OSA_DEBUG(M4ERR_CONTEXT_FAILED, "M4OSA_mutexOpen: OS mutex creation failed");
98        free(pMutexContext);
99        return M4ERR_CONTEXT_FAILED ;
100    }
101
102    pMutexContext->coreID = M4OSA_MUTEX;
103
104    pMutexContext->threadOwnerID = 0;
105
106    *pContext = (M4OSA_Context) pMutexContext;
107
108    return M4NO_ERROR;
109}
110
111
112
113
114/**
115 ************************************************************************
116 * @brief      This method locks the mutex. "Context" identifies the mutex.
117 * @note       If the mutex is already locked, the calling thread blocks until
118 *             the mutex becomes available (by calling M4OSA_mutexUnlock) or
119 *             "timeout" is reached. This is a blocking call.
120 * @param      context:(IN/OUT) Context of the mutex
121 * @param      timeout:(IN) Time out in milliseconds
122 * @return     M4NO_ERROR: there is no error
123 * @return     M4ERR_PARAMETER: at least one parameter is NULL
124 * @return     M4WAR_TIME_OUT: time out is elapsed before mutex has been
125 *             available
126 * @return     M4ERR_BAD_CONTEXT: provided context is not a valid one
127 ************************************************************************
128*/
129M4OSA_ERR M4OSA_mutexLock(M4OSA_Context context, M4OSA_UInt32 timeout)
130{
131    M4OSA_MutexContext* pMutexContext = (M4OSA_MutexContext*)context;
132    pthread_t           currentThread;
133    int                 result;
134    struct timespec     ts;
135    struct timespec     left;
136
137    M4OSA_TRACE1_2("M4OSA_mutexLock\t\tM4OSA_Context 0x%x\tM4OSA_UInt32 %d",
138        context, timeout);
139
140    M4OSA_DEBUG_IF2(M4OSA_NULL == context, M4ERR_PARAMETER,
141                                      "M4OSA_mutexLock: context is M4OSA_NULL");
142    M4OSA_DEBUG_IF2(pMutexContext->coreID != M4OSA_MUTEX,
143                                          M4ERR_BAD_CONTEXT, "M4OSA_mutexLock");
144
145    currentThread = pthread_self();
146
147    if(pMutexContext ->threadOwnerID == currentThread)
148    {
149        M4OSA_DEBUG(M4ERR_BAD_CONTEXT, "M4OSA_mutexLock: Thread tried to lock a mutex it already owns");
150        return M4ERR_BAD_CONTEXT ;
151    }
152
153    /* Lock the mutex. */
154    if ( M4OSA_WAIT_FOREVER == timeout)
155    {
156        if ( 0 != pthread_mutex_lock(&pMutexContext->mutex) )
157        {
158            M4OSA_DEBUG(M4ERR_BAD_CONTEXT, "M4OSA_mutexLock: OS mutex wait failed");
159            return M4ERR_BAD_CONTEXT;
160        }
161    }
162    else
163    {
164        result = pthread_mutex_trylock(&pMutexContext->mutex);
165        while ( ( EBUSY == result ) && ( 0 < timeout ) )
166        {
167            ts.tv_sec  = 0;
168            if (1 <= timeout)
169            {
170                ts.tv_nsec = 1000000;
171                timeout -= 1;
172            }
173            else
174            {
175                ts.tv_nsec = timeout * 1000000;
176                timeout = 0;
177            }
178            nanosleep(&ts, &left);
179            result = pthread_mutex_trylock(&pMutexContext->mutex);
180        }
181        if (0 != result)
182        {
183            if (EBUSY == result)
184            {
185                return M4WAR_TIME_OUT;
186            }
187            else
188            {
189                M4OSA_DEBUG(M4ERR_BAD_CONTEXT, "M4OSA_mutexLock: OS mutex wait failed");
190                return M4ERR_BAD_CONTEXT;
191            }
192        }
193    }
194
195    pMutexContext->threadOwnerID = currentThread;
196
197    return M4NO_ERROR;
198}
199
200
201
202/**
203 ************************************************************************
204 * @brief      This method unlocks the mutex. The mutex is identified by
205 *             its context
206 * @note       The M4OSA_mutexLock unblocks the thread with the highest
207 *             priority and made it ready to run.
208 * @note       No hypotheses can be made on which thread will be un-blocked
209 *             between threads with the same priority.
210 * @param      context:(IN/OUT) Context of the mutex
211 * @return     M4NO_ERROR: there is no error
212 * @return     M4ERR_PARAMETER: at least one parameter is NULL
213 * @return     M4ERR_BAD_CONTEXT: provided context is not a valid one
214************************************************************************
215*/
216M4OSA_ERR M4OSA_mutexUnlock(M4OSA_Context context)
217{
218    M4OSA_MutexContext* pMutexContext = (M4OSA_MutexContext*)context;
219    pthread_t currentThread;
220
221    M4OSA_TRACE1_1("M4OSA_mutexUnlock\t\tM4OSA_Context 0x%x", context);
222    M4OSA_DEBUG_IF2(M4OSA_NULL == context, M4ERR_PARAMETER,
223                                    "M4OSA_mutexUnlock: context is M4OSA_NULL");
224    M4OSA_DEBUG_IF2(M4OSA_MUTEX != pMutexContext->coreID,
225                                        M4ERR_BAD_CONTEXT, "M4OSA_mutexUnlock");
226
227    currentThread = pthread_self();
228
229    if(pMutexContext->threadOwnerID != currentThread)
230    {
231        M4OSA_DEBUG(M4ERR_BAD_CONTEXT, "M4OSA_mutexUnlock: Thread tried to unlock a mutex it doesn't own");
232        return M4ERR_BAD_CONTEXT;
233    }
234
235    pMutexContext->threadOwnerID = 0 ;
236
237    pthread_mutex_unlock(&pMutexContext->mutex);
238
239    return M4NO_ERROR;
240}
241
242
243
244
245/**
246 ************************************************************************
247 * @brief      This method deletes a mutex (identify by its context). After
248 *             this call, the mutex and its context is no more useable. This
249 *             function frees all the memory related to this mutex.
250 * @note       It is an application issue to warrant no more threads are locked
251 *             on the deleted mutex.
252 * @param      context:(IN/OUT) Context of the mutex
253 * @return     M4NO_ERROR: there is no error
254 * @return     M4ERR_PARAMETER: at least one parameter is NULL
255 * @return     M4ERR_BAD_CONTEXT: provided context is not a valid one
256 ************************************************************************
257*/
258M4OSA_ERR M4OSA_mutexClose(M4OSA_Context context)
259{
260    M4OSA_MutexContext* pMutexContext = (M4OSA_MutexContext*)context;
261
262    M4OSA_TRACE1_1("M4OSA_mutexClose\t\tM4OSA_Context 0x%x", context);
263
264    M4OSA_DEBUG_IF2(M4OSA_NULL == context, M4ERR_PARAMETER,
265                                     "M4OSA_mutexClose: context is M4OSA_NULL");
266    M4OSA_DEBUG_IF2(pMutexContext->coreID != M4OSA_MUTEX,
267                                        M4ERR_BAD_CONTEXT, "M4OSA_mutexUnlock");
268
269    pthread_mutex_destroy(&pMutexContext->mutex);
270
271    free( pMutexContext);
272
273    return M4NO_ERROR;
274}
275
276