blob: e402e8c84d7f2534c758a03950c5c688ed3fce80 [file] [log] [blame]
/* ------------------------------------------------------------------
* Copyright (C) 1998-2009 PacketVideo
*
* 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.
* -------------------------------------------------------------------
*/
#include "threadsafe_callback_ao.h"
#include "pvlogger.h"
#include "oscl_procstatus.h"
#include "oscl_dll.h"
OSCL_DLL_ENTRY_POINT_DEFAULT()
/////////////////////////////////////////////////////////////////////////////
// Add AO to the scheduler
/////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF void ThreadSafeCallbackAO::ThreadLogon()
{
if (!IsAdded())
{
AddToScheduler();
}
iLogger = PVLogger::GetLoggerObject(iLoggerString);
}
////////////////////////////////////////////////////////////////////////////
// Remove AO from the scheduler
////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF void ThreadSafeCallbackAO::ThreadLogoff()
{
//thread logoff
if (IsAdded())
{
RemoveFromScheduler();
}
iLogger = NULL;
}
///////////////////////////////////////////////////////////////////////////
// CONSTRUCTOR
///////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF ThreadSafeCallbackAO::ThreadSafeCallbackAO(void *aObserver, uint32 aDepth, const char *aAOname, int32 aPriority)
: OsclActiveObject(aPriority, aAOname),
iLogger(NULL)
{
OsclReturnCode queue_init_status = OsclSuccess;
OsclProcStatus::eOsclProcError mutex_init_status = OsclProcStatus::SUCCESS_ERROR;
OsclProcStatus::eOsclProcError sema_init_status = OsclProcStatus::SUCCESS_ERROR;
int32 err = 0;
iLoggerString = aAOname;
iObserver = aObserver;
Q = NULL;
OSCL_TRY(err,
queue_init_status = QueueInit(aDepth); //create the Q
mutex_init_status = Mutex.Create(); // Create Mutex
sema_init_status = RemoteThreadCtrlSema.Create(aDepth);
ThreadLogon(); // add to scheduler
);
if ((err != 0) ||
(queue_init_status != OsclSuccess) ||
(mutex_init_status != OsclProcStatus::SUCCESS_ERROR) ||
(sema_init_status != OsclProcStatus::SUCCESS_ERROR)
)
{
OSCL_LEAVE(OsclFailure);
}
PendForExec(); // make sure to "prime" the callback AO for the first event that arrives
}
/////////////////////////////////////////////////////////////////////////////
// Destructor
/////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF ThreadSafeCallbackAO::~ThreadSafeCallbackAO()
{
OsclReturnCode queue_deinit_status = OsclSuccess;
OsclProcStatus::eOsclProcError mutex_close_status = OsclProcStatus::SUCCESS_ERROR;
OsclProcStatus::eOsclProcError sema_close_status = OsclProcStatus::SUCCESS_ERROR;
int32 err = 0;
OSCL_TRY(err,
queue_deinit_status = QueueDeInit(); // Destroy the queue
mutex_close_status = Mutex.Close(); // close the mutex
sema_close_status = RemoteThreadCtrlSema.Close(); // close the semaphore
ThreadLogoff();
);
if ((err != 0) ||
(queue_deinit_status != OsclSuccess) ||
(mutex_close_status != OsclProcStatus::SUCCESS_ERROR) ||
(sema_close_status != OsclProcStatus::SUCCESS_ERROR))
{
OSCL_LEAVE(OsclFailure);
}
iObserver = NULL;
}
/////////////////////////////////////////////////////////////////////////////
// Run Routine
/////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF void ThreadSafeCallbackAO::Run()
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "ThreadSafeCallbackAO::Run() In"));
OsclAny *P; // parameter to dequeue
OsclReturnCode status;
// get the event parameters and status of de-queuing
P = DeQueue(status);
if (status == OsclSuccess)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "ThreadSafeCallbackAO::Run() - Calling Process Event"));
ProcessEvent(P);
}
else
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "ThreadSafeCallbackAO::Run() - could not dequeue event data"));
}
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "ThreadSafeCallbackAO::Run() Out"));
}
/////////////////////////////////////////////////////////////////////////////////////
//////// ReceiveEvent
/////////////////////////////////////////////////////////////////////////////////////
/////// NOTE: THIS METHOD IS EXECUTED IN THE REMOTE THREAD CONTEXT
//////////////AS PART OF THE CALLBACK. IT QUEUES THE EVENT PARAMETERS !!!!!!!!!!!!
/////////////////////////////////////////////////////////////////////////////////////
///////////// GENERIC API requires only one parameter (osclany pointer to "event data")
///////////////
////////////// BEFORE CALLING THE GENERIC API, YOU MUST SETUP (COPY IF NECESSARY)
///////////////// CALLBACK EVENT PARAMETERS AND PROVIDE An OSCLANY POINTER TO THEM
/////////////////////////////////////////////////////////////////////////////////////
///////////////YOU MUST NOT USE LOGGER IN THE CALLBACK (IN THE REMOTE THREAD CONTEXT)
///////////////
//////////////// EXAMPLE CODE (For CALLING THE GENERIC API):
// typedef struct event_data{
// int d1;
// char *d2;
// uint32 d3;
// } event_data;
//int ActualCallbackAPI(int p1, char * p2, uint32 p3)
//{
// // pack all parameters
// event_data *pED = malloc(sizeof(event_data));
// pED->d1 = p1;
// pED->d2 = p2;
// pED->d3 = p3;
// OsclAny *P = (OsclAny *) pED:
// // call the generic callback API:
// PVMFStatus stat = ReceiveEvent(P);
// // check the status
// if(stat == PVMFSuccess)
// return OK;
// else
// return ERROR;
//}
//
OSCL_EXPORT_REF OsclReturnCode ThreadSafeCallbackAO::ReceiveEvent(OsclAny *EventData)
{
OsclReturnCode status;
status = Queue(EventData);
return status;
}
OSCL_EXPORT_REF OsclReturnCode ThreadSafeCallbackAO::ProcessEvent(OsclAny *EventData)
{
OSCL_UNUSED_ARG(EventData);
// DO NOTHING. OVERLOAD THIS METHOD IN THE DERIVED CLASS TO DO SOMETHING MEANINGFUL
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "ThreadSafeCallbackAO::ProcessEvent() In and Out"));
return OsclSuccess;
}
///////////////////////////////////////////////////////////////////////////////
//// QUEUE OPERATIONS
///////////////////////////////////////////////////////////////////////////////
/////CREATE QUEUE AND INIT ///////////////////
OSCL_EXPORT_REF OsclReturnCode ThreadSafeCallbackAO::QueueInit(uint32 aMaxQueueDepth)
{
if (aMaxQueueDepth < 1)
{
Q = NULL;
return OsclErrArgument;
}
// create Q as simple array
Q = (QueueT *) oscl_malloc(sizeof(QueueT));
if (Q == NULL)
{
return OsclErrNoMemory;
}
Q->MaxNumElements = aMaxQueueDepth;
Q->NumElem = 0;
Q->index_in = 0;
Q->index_out = 0;
Q->pFirst = NULL;
// create the Q elements
Q->pFirst = (QElement *) oscl_malloc((Q->MaxNumElements) * oscl_mem_aligned_size(sizeof(QElement)));
if (Q->pFirst == NULL)
{
Q = NULL;
return OsclErrNoMemory;
}
// zero out the Q
oscl_memset(Q->pFirst, 0, (Q->MaxNumElements) * oscl_mem_aligned_size(sizeof(QElement)));
return OsclSuccess;
}
///////////// DESTROY QUEUE ///////////////////////
OSCL_EXPORT_REF OsclReturnCode ThreadSafeCallbackAO::QueueDeInit()
{
OsclReturnCode status = OsclSuccess;
// free the Q elements
if (Q->pFirst != NULL)
oscl_free(Q->pFirst);
Q->pFirst = NULL;
// free the Q structure
if (Q != NULL)
oscl_free(Q);
Q = NULL;
return status;
}
/////////////QUEUE ONE ELEMENT //////////////////////
OSCL_EXPORT_REF OsclReturnCode ThreadSafeCallbackAO::Queue(OsclAny *pData)
{
OsclProcStatus::eOsclProcError sema_status;
// Wait on the remote thread control semaphore. If the queue is full, must block and wait
// for the AO to dequeue some previous event. If the queue is not full, proceed
sema_status = RemoteThreadCtrlSema.Wait();
if (sema_status != OsclProcStatus::SUCCESS_ERROR)
return OsclFailure;
// protect queue access
Mutex.Lock();
if (Q->NumElem >= Q->MaxNumElements)
{
Mutex.Unlock();
RemoteThreadCtrlSema.Signal(); // signal the sema to maintain sema count consistency in case
// of error (inability to queue)
return OsclFailure;
}
(Q->pFirst[Q->index_in]).pData = pData;
Q->index_in++;
// roll-over the index to 0 if it reaches the end.
if (Q->index_in == Q->MaxNumElements)
Q->index_in = 0;
Q->NumElem++;
// check if AO needs to be scheduled (i.e. check if this is the first event the queue after the queue was empty)
if (GetQueueNumElem() == 1)
{
PendComplete(OSCL_REQUEST_ERR_NONE);
}
Mutex.Unlock();
return OsclSuccess;
}
///////////DE-QUEUE ONE ELEMENT /////////////////////
OSCL_EXPORT_REF OsclAny* ThreadSafeCallbackAO::DeQueue(OsclReturnCode &stat)
{
OsclAny *pData;
OsclProcStatus::eOsclProcError sema_status;
stat = OsclSuccess;
// Protect the queue while accessing it:
Mutex.Lock();
if (Q->NumElem == 0)
{
// nothing to de-queue
stat = OsclFailure;
Mutex.Unlock();
return NULL;
}
pData = (Q->pFirst[Q->index_out]).pData;
Q->index_out++;
// roll-over the index
if (Q->index_out == Q->MaxNumElements)
Q->index_out = 0;
Q->NumElem--;
// check if there is need to reschedule
if ((Q->NumElem) > 0)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "ThreadSafeCallbackAO::Run() - More events, call RunIfNotReady()"));
RunIfNotReady();
}
else
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "ThreadSafeCallbackAO::Run() - No more events, call PendForExec()"));
PendForExec();
}
//release queue access
Mutex.Unlock();
// Signal the semaphore that controls the remote thread.
// The remote thread might be blocked and waiting for an event to be processed in case the event queue is full
sema_status = RemoteThreadCtrlSema.Signal();
if (sema_status != OsclProcStatus::SUCCESS_ERROR)
{
stat = OsclFailure;
return NULL;
}
return pData;
}
////////// GET CURRENT QUEUE SIZE//////////////
OSCL_EXPORT_REF uint32 ThreadSafeCallbackAO::GetQueueNumElem()
{
return Q->NumElem;
}