/*
* 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.
*/
/**
************************************************************************
* @file M4OSA_Semaphore.c
* @brief Semaphore for Windows
* @note This file implements functions to manipulate semaphore
************************************************************************
*/
#include "M4OSA_Debug.h"
#include "M4OSA_Types.h"
#include "M4OSA_Error.h"
#include "M4OSA_Memory.h"
#include "M4OSA_Semaphore.h"
#include <semaphore.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <time.h>
/* Context for the semaphore */
typedef struct {
M4OSA_UInt32 coreID; /* semaphore context identifiant */
sem_t semaphore; /* semaphore */
} M4OSA_SemaphoreContext;
/**
************************************************************************
* @brief This method creates a new semaphore with the "initialCounter"
* value.
* @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_semaphoreClose function. The context
* parameter will be sent back to any OSAL core semaphore functions
* to allow retrieving data associated to the opened semaphore.
* @param context:(OUT) Context of the created semaphore
* @param initial_count:(IN) Initial counter of the semaphore
* @return M4NO_ERROR: there is no error
* @return M4ERR_PARAMETER: provided context is NULL
* @return M4ERR_ALLOC: there is no more available memory
* @return M4ERR_CONTEXT_FAILED: the context creation failed
************************************************************************
*/
M4OSA_ERR M4OSA_semaphoreOpen(M4OSA_Context* context,
M4OSA_UInt32 initial_count)
{
M4OSA_SemaphoreContext* semaphoreContext = M4OSA_NULL;
M4OSA_TRACE1_2("M4OSA_semaphoreOpen\t\tM4OSA_Context* 0x%x\tM4OSA_UInt32 "
"%d", context, initial_count);
M4OSA_DEBUG_IF2(context == M4OSA_NULL,
M4ERR_PARAMETER, "M4OSA_semaphoreOpen");
*context = M4OSA_NULL;
semaphoreContext = (M4OSA_SemaphoreContext*) M4OSA_32bitAlignedMalloc(
sizeof(M4OSA_SemaphoreContext), M4OSA_SEMAPHORE,
(M4OSA_Char*)"M4OSA_semaphoreOpen: semaphore context");
if(semaphoreContext == M4OSA_NULL)
{
M4OSA_DEBUG(M4ERR_ALLOC, "M4OSA_semaphoreOpen");
return M4ERR_ALLOC;
}
if (0 != sem_init(&semaphoreContext->semaphore, 0, initial_count))
{
free(semaphoreContext);
M4OSA_DEBUG(M4ERR_CONTEXT_FAILED,
"M4OSA_semaphoreOpen: OS semaphore creation failed");
return M4ERR_CONTEXT_FAILED;
}
semaphoreContext->coreID = M4OSA_SEMAPHORE ;
*context = (M4OSA_Context)semaphoreContext;
return M4NO_ERROR;
}
/**
************************************************************************
* @brief This method decrements (one by one) the semaphore counter. The
* semaphore is identified by its context This call is not blocking
* if the semaphore counter is positive or zero (after
* decrementation). This call is blocking if the semaphore counter
* is less than zero (after decrementation), until the semaphore is
* upper than zero (see M4OSA_semaphorePost) or time_out is
* reached.
* @note If "timeout" value is M4OSA_WAIT_FOREVER, the calling thread
* will block indefinitely until the semaphore is unlocked.
* @param context:(IN/OUT) Context of the semaphore
* @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 semaphore has been
* available.
* @return M4ERR_BAD_CONTEXT: provided context is not a valid one
************************************************************************
*/
M4OSA_ERR M4OSA_semaphoreWait(M4OSA_Context context, M4OSA_Int32 timeout)
{
M4OSA_SemaphoreContext* semaphoreContext = (M4OSA_SemaphoreContext*)context;
struct timespec ts;
struct timespec left;
int result;
M4OSA_TRACE1_2("M4OSA_semaphoreWait\t\tM4OSA_Context 0x%x\tM4OSA_UInt32 %d",
context, timeout);
M4OSA_DEBUG_IF2(context == M4OSA_NULL,
M4ERR_PARAMETER, "M4OSA_semaphoreWait");
M4OSA_DEBUG_IF2(semaphoreContext->coreID != M4OSA_SEMAPHORE,
M4ERR_BAD_CONTEXT, "M4OSA_semaphoreWait");
if ( (M4OSA_Int32)M4OSA_WAIT_FOREVER == timeout)
{
if ( 0 != sem_wait(&semaphoreContext->semaphore) )
{
M4OSA_DEBUG(M4ERR_BAD_CONTEXT,
"M4OSA_semaphoreWait: OS semaphore wait failed");
return M4ERR_BAD_CONTEXT ;
}
}
else
{
result = sem_trywait(&semaphoreContext->semaphore);
while ( ((EBUSY == result) || (EAGAIN == 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 = sem_trywait(&semaphoreContext->semaphore);
}
if (0 != result)
{
if ((EBUSY == result) || (EAGAIN == result))
{
return M4WAR_TIME_OUT;
}
else
{
M4OSA_DEBUG(M4ERR_BAD_CONTEXT, "M4OSA_semaphoreWait: OS semaphore wait failed");
return M4ERR_BAD_CONTEXT;
}
}
}
return M4NO_ERROR;
}
/**
************************************************************************
* @brief This method increments the semaphore counter. The semaphore is
* identified by its context
* @note If the semaphore counter is upper than zero (after addition),
* the M4OSA_semaphoreWait call of the thread with the highest
* priority is unblocked and made ready to run.
* @note No hypotheses can be made on which thread will be unblocked
* between threads with the same priority.
* @param context:(IN/OUT) Context of the semaphore
* @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_semaphorePost(M4OSA_Context context)
{
M4OSA_SemaphoreContext* semaphoreContext = (M4OSA_SemaphoreContext*)context;
M4OSA_TRACE1_1("M4OSA_semaphorePost\t\tM4OSA_Context 0x%x", context);
M4OSA_DEBUG_IF2(context == M4OSA_NULL,
M4ERR_PARAMETER, "M4OSA_semaphorePost");
M4OSA_DEBUG_IF2(semaphoreContext->coreID != M4OSA_SEMAPHORE,
M4ERR_BAD_CONTEXT, "M4OSA_semaphorePost");
sem_post(&semaphoreContext->semaphore);
return M4NO_ERROR;
}
/**
************************************************************************
* @brief This method deletes a semaphore (identify by its context).
* After this call the semaphore and its context is no more
* useable. This function frees all the memory related to this
* semaphore.
* @note It is an application issue to warrant no more threads are locked
* on the deleted semaphore.
* @param context:(IN/OUT) Context of the semaphore
* @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_semaphoreClose(M4OSA_Context context)
{
M4OSA_SemaphoreContext* semaphoreContext = (M4OSA_SemaphoreContext*)context;
M4OSA_TRACE1_1("M4OSA_semaphoreClose\t\tM4OSA_Context 0x%x", context);
M4OSA_DEBUG_IF2(context == M4OSA_NULL,
M4ERR_PARAMETER, "M4OSA_semaphoreClose");
M4OSA_DEBUG_IF2(semaphoreContext->coreID != M4OSA_SEMAPHORE,
M4ERR_BAD_CONTEXT, "M4OSA_semaphoreClose");
sem_destroy(&semaphoreContext->semaphore);
free(semaphoreContext);
return M4NO_ERROR;
}