/*
* Copyright (C) 2010 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 CEngine.c Engine class */
#include "sles_allinclusive.h"
/* This implementation supports at most one engine, identical for both OpenSL ES and OpenMAX AL */
CEngine *theOneTrueEngine = NULL;
pthread_mutex_t theOneTrueMutex = PTHREAD_MUTEX_INITIALIZER;
unsigned theOneTrueRefCount = 0;
// incremented by slCreateEngine or xaCreateEngine, decremented by Object::Destroy on engine
/** \brief Called by dlopen when .so is loaded */
__attribute__((constructor)) static void onDlOpen(void)
{
}
/** \brief Called by dlclose when .so is unloaded */
__attribute__((destructor)) static void onDlClose(void)
{
// a memory barrier would be sufficient, but the mutex is easier
(void) pthread_mutex_lock(&theOneTrueMutex);
if ((NULL != theOneTrueEngine) || (0 < theOneTrueRefCount)) {
SL_LOGE("Object::Destroy omitted for engine %p", theOneTrueEngine);
}
(void) pthread_mutex_unlock(&theOneTrueMutex);
}
/** \brief Hook called by Object::Realize when an engine is realized */
SLresult CEngine_Realize(void *self, SLboolean async)
{
CEngine *thiz = (CEngine *) self;
SLresult result;
#ifndef ANDROID
// create the sync thread
int err = pthread_create(&thiz->mSyncThread, (const pthread_attr_t *) NULL, sync_start, thiz);
result = err_to_result(err);
if (SL_RESULT_SUCCESS != result)
return result;
#endif
// initialize the thread pool for asynchronous operations
result = ThreadPool_init(&thiz->mThreadPool, 0, 0);
if (SL_RESULT_SUCCESS != result) {
thiz->mEngine.mShutdown = SL_BOOLEAN_TRUE;
(void) pthread_join(thiz->mSyncThread, (void **) NULL);
return result;
}
#ifdef USE_SDL
SDL_open(&thiz->mEngine);
#endif
return SL_RESULT_SUCCESS;
}
/** \brief Hook called by Object::Resume when an engine is resumed */
SLresult CEngine_Resume(void *self, SLboolean async)
{
return SL_RESULT_SUCCESS;
}
/** \brief Hook called by Object::Destroy when an engine is destroyed */
void CEngine_Destroy(void *self)
{
CEngine *thiz = (CEngine *) self;
// Verify that there are no extant objects
unsigned instanceCount = thiz->mEngine.mInstanceCount;
unsigned instanceMask = thiz->mEngine.mInstanceMask;
if ((0 < instanceCount) || (0 != instanceMask)) {
SL_LOGE("Object::Destroy(%p) for engine ignored; %u total active objects",
thiz, instanceCount);
while (0 != instanceMask) {
unsigned i = ctz(instanceMask);
assert(MAX_INSTANCE > i);
SL_LOGE("Object::Destroy(%p) for engine ignored; active object ID %u at %p",
thiz, i + 1, thiz->mEngine.mInstances[i]);
instanceMask &= ~(1 << i);
}
}
// If engine was created but not realized, there will be no sync thread yet
pthread_t zero;
memset(&zero, 0, sizeof(pthread_t));
if (0 != memcmp(&zero, &thiz->mSyncThread, sizeof(pthread_t))) {
// Announce to the sync thread that engine is shutting down; it polls so should see it soon
thiz->mEngine.mShutdown = SL_BOOLEAN_TRUE;
// Wait for the sync thread to acknowledge the shutdown
while (!thiz->mEngine.mShutdownAck) {
object_cond_wait(&thiz->mObject);
}
// The sync thread should have exited by now, so collect it by joining
(void) pthread_join(thiz->mSyncThread, (void **) NULL);
}
// Shutdown the thread pool used for asynchronous operations (there should not be any)
ThreadPool_deinit(&thiz->mThreadPool);
#if defined(ANDROID)
// free equalizer preset names
if (NULL != thiz->mEqPresetNames) {
for (unsigned i = 0; i < thiz->mEqNumPresets; ++i) {
if (NULL != thiz->mEqPresetNames[i]) {
delete[] thiz->mEqPresetNames[i];
thiz->mEqPresetNames[i] = NULL;
}
}
delete[] thiz->mEqPresetNames;
thiz->mEqPresetNames = NULL;
}
thiz->mEqNumPresets = 0;
#endif
#ifdef USE_SDL
SDL_close();
#endif
}
/** \brief Hook called by Object::Destroy before an engine is about to be destroyed */
predestroy_t CEngine_PreDestroy(void *self)
{
predestroy_t ret;
(void) pthread_mutex_lock(&theOneTrueMutex);
assert(self == theOneTrueEngine);
switch (theOneTrueRefCount) {
case 0:
assert(false);
ret = predestroy_error;
break;
case 1:
ret = predestroy_ok;
break;
default:
--theOneTrueRefCount;
ret = predestroy_again;
break;
}
(void) pthread_mutex_unlock(&theOneTrueMutex);
return ret;
}
/** \brief Called by IObject::Destroy after engine is destroyed. The parameter refers to the
* previous engine, which is now undefined memory.
*/
void CEngine_Destroyed(CEngine *self)
{
int ok;
ok = pthread_mutex_lock(&theOneTrueMutex);
assert(0 == ok);
assert(self == theOneTrueEngine);
theOneTrueEngine = NULL;
assert(1 == theOneTrueRefCount);
theOneTrueRefCount = 0;
ok = pthread_mutex_unlock(&theOneTrueMutex);
assert(0 == ok);
}