blob: da88845c2a40325819b48a5b2c17b3116e5bb601 [file] [log] [blame]
/*
* 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.
*/
/* Object implementation */
#include "sles_allinclusive.h"
// Called by a worker thread to handle an asynchronous Object.Realize.
// Parameter self is the Object.
static void HandleRealize(void *self, void *ignored, int unused)
{
// validate input parameters
IObject *thiz = (IObject *) self;
assert(NULL != thiz);
const ClassTable *clazz = thiz->mClass;
assert(NULL != clazz);
AsyncHook realize = clazz->mRealize;
SLresult result;
SLuint8 state;
// check object state
object_lock_exclusive(thiz);
state = thiz->mState;
switch (state) {
case SL_OBJECT_STATE_REALIZING_1: // normal case
if (NULL != realize) {
thiz->mState = SL_OBJECT_STATE_REALIZING_2;
// Note that the mutex is locked on entry to and exit from the realize hook,
// but the hook is permitted to temporarily unlock the mutex (e.g. for async).
result = (*realize)(thiz, SL_BOOLEAN_TRUE);
assert(SL_OBJECT_STATE_REALIZING_2 == thiz->mState);
state = SL_RESULT_SUCCESS == result ? SL_OBJECT_STATE_REALIZED :
SL_OBJECT_STATE_UNREALIZED;
} else {
result = SL_RESULT_SUCCESS;
state = SL_OBJECT_STATE_REALIZED;
}
break;
case SL_OBJECT_STATE_REALIZING_1A: // operation was aborted while on work queue
result = SL_RESULT_OPERATION_ABORTED;
state = SL_OBJECT_STATE_UNREALIZED;
break;
default: // impossible
assert(SL_BOOLEAN_FALSE);
result = SL_RESULT_INTERNAL_ERROR;
break;
}
// mutex is locked, update state
thiz->mState = state;
// Make a copy of these, so we can call the callback with mutex unlocked
slObjectCallback callback = thiz->mCallback;
void *context = thiz->mContext;
object_unlock_exclusive(thiz);
// Note that the mutex is unlocked during the callback
if (NULL != callback) {
(*callback)(&thiz->mItf, context, SL_OBJECT_EVENT_ASYNC_TERMINATION, result, state, NULL);
}
}
static SLresult IObject_Realize(SLObjectItf self, SLboolean async)
{
SL_ENTER_INTERFACE
IObject *thiz = (IObject *) self;
SLuint8 state;
const ClassTable *clazz = thiz->mClass;
bool isSharedEngine = false;
object_lock_exclusive(thiz);
// note that SL_OBJECTID_ENGINE and XA_OBJECTID_ENGINE map to same class
if (clazz == objectIDtoClass(SL_OBJECTID_ENGINE)) {
// important: the lock order is engine followed by theOneTrueMutex
int ok = pthread_mutex_lock(&theOneTrueMutex);
assert(0 == ok);
isSharedEngine = 1 < theOneTrueRefCount;
ok = pthread_mutex_unlock(&theOneTrueMutex);
assert(0 == ok);
}
state = thiz->mState;
// Reject redundant calls to Realize, except on a shared engine
if (SL_OBJECT_STATE_UNREALIZED != state) {
object_unlock_exclusive(thiz);
// redundant realize on the shared engine is permitted
if (isSharedEngine && (SL_OBJECT_STATE_REALIZED == state)) {
result = SL_RESULT_SUCCESS;
} else {
result = SL_RESULT_PRECONDITIONS_VIOLATED;
}
} else {
// Asynchronous: mark operation pending and cancellable
if (async && (SL_OBJECTID_ENGINE != clazz->mSLObjectID)) {
state = SL_OBJECT_STATE_REALIZING_1;
// Synchronous: mark operation pending and non-cancellable
} else {
state = SL_OBJECT_STATE_REALIZING_2;
}
thiz->mState = state;
switch (state) {
case SL_OBJECT_STATE_REALIZING_1: // asynchronous on non-Engine
object_unlock_exclusive(thiz);
assert(async);
result = ThreadPool_add_ppi(&thiz->mEngine->mThreadPool, HandleRealize, thiz, NULL, 0);
if (SL_RESULT_SUCCESS != result) {
// Engine was destroyed during realize, or insufficient memory
object_lock_exclusive(thiz);
thiz->mState = SL_OBJECT_STATE_UNREALIZED;
object_unlock_exclusive(thiz);
}
break;
case SL_OBJECT_STATE_REALIZING_2: // synchronous, or asynchronous on Engine
{
AsyncHook realize = clazz->mRealize;
// Note that the mutex is locked on entry to and exit from the realize hook,
// but the hook is permitted to temporarily unlock the mutex (e.g. for async).
result = (NULL != realize) ? (*realize)(thiz, async) : SL_RESULT_SUCCESS;
assert(SL_OBJECT_STATE_REALIZING_2 == thiz->mState);
state = (SL_RESULT_SUCCESS == result) ? SL_OBJECT_STATE_REALIZED :
SL_OBJECT_STATE_UNREALIZED;
thiz->mState = state;
slObjectCallback callback = thiz->mCallback;
void *context = thiz->mContext;
object_unlock_exclusive(thiz);
// asynchronous Realize on an Engine is actually done synchronously, but still has
// callback because there is no thread pool yet to do it asynchronously.
if (async && (NULL != callback)) {
(*callback)(&thiz->mItf, context, SL_OBJECT_EVENT_ASYNC_TERMINATION, result, state,
NULL);
}
}
break;
default: // impossible
object_unlock_exclusive(thiz);
assert(SL_BOOLEAN_FALSE);
break;
}
}
SL_LEAVE_INTERFACE
}
// Called by a worker thread to handle an asynchronous Object.Resume.
// Parameter self is the Object.
static void HandleResume(void *self, void *ignored, int unused)
{
// valid input parameters
IObject *thiz = (IObject *) self;
assert(NULL != thiz);
const ClassTable *clazz = thiz->mClass;
assert(NULL != clazz);
AsyncHook resume = clazz->mResume;
SLresult result;
SLuint8 state;
// check object state
object_lock_exclusive(thiz);
state = thiz->mState;
switch (state) {
case SL_OBJECT_STATE_RESUMING_1: // normal case
if (NULL != resume) {
thiz->mState = SL_OBJECT_STATE_RESUMING_2;
// Note that the mutex is locked on entry to and exit from the resume hook,
// but the hook is permitted to temporarily unlock the mutex (e.g. for async).
result = (*resume)(thiz, SL_BOOLEAN_TRUE);
assert(SL_OBJECT_STATE_RESUMING_2 == thiz->mState);
state = SL_RESULT_SUCCESS == result ? SL_OBJECT_STATE_REALIZED :
SL_OBJECT_STATE_SUSPENDED;
} else {
result = SL_RESULT_SUCCESS;
state = SL_OBJECT_STATE_REALIZED;
}
break;
case SL_OBJECT_STATE_RESUMING_1A: // operation was aborted while on work queue
result = SL_RESULT_OPERATION_ABORTED;
state = SL_OBJECT_STATE_SUSPENDED;
break;
default: // impossible
assert(SL_BOOLEAN_FALSE);
result = SL_RESULT_INTERNAL_ERROR;
break;
}
// mutex is unlocked, update state
thiz->mState = state;
// Make a copy of these, so we can call the callback with mutex unlocked
slObjectCallback callback = thiz->mCallback;
void *context = thiz->mContext;
object_unlock_exclusive(thiz);
// Note that the mutex is unlocked during the callback
if (NULL != callback) {
(*callback)(&thiz->mItf, context, SL_OBJECT_EVENT_ASYNC_TERMINATION, result, state, NULL);
}
}
static SLresult IObject_Resume(SLObjectItf self, SLboolean async)
{
SL_ENTER_INTERFACE
IObject *thiz = (IObject *) self;
const ClassTable *clazz = thiz->mClass;
SLuint8 state;
object_lock_exclusive(thiz);
state = thiz->mState;
// Reject redundant calls to Resume
if (SL_OBJECT_STATE_SUSPENDED != state) {
object_unlock_exclusive(thiz);
result = SL_RESULT_PRECONDITIONS_VIOLATED;
} else {
// Asynchronous: mark operation pending and cancellable
if (async) {
state = SL_OBJECT_STATE_RESUMING_1;
// Synchronous: mark operatio pending and non-cancellable
} else {
state = SL_OBJECT_STATE_RESUMING_2;
}
thiz->mState = state;
switch (state) {
case SL_OBJECT_STATE_RESUMING_1: // asynchronous
object_unlock_exclusive(thiz);
assert(async);
result = ThreadPool_add_ppi(&thiz->mEngine->mThreadPool, HandleResume, thiz, NULL, 0);
if (SL_RESULT_SUCCESS != result) {
// Engine was destroyed during resume, or insufficient memory
object_lock_exclusive(thiz);
thiz->mState = SL_OBJECT_STATE_SUSPENDED;
object_unlock_exclusive(thiz);
}
break;
case SL_OBJECT_STATE_RESUMING_2: // synchronous
{
AsyncHook resume = clazz->mResume;
// Note that the mutex is locked on entry to and exit from the resume hook,
// but the hook is permitted to temporarily unlock the mutex (e.g. for async).
result = (NULL != resume) ? (*resume)(thiz, SL_BOOLEAN_FALSE) : SL_RESULT_SUCCESS;
assert(SL_OBJECT_STATE_RESUMING_2 == thiz->mState);
thiz->mState = (SL_RESULT_SUCCESS == result) ? SL_OBJECT_STATE_REALIZED :
SL_OBJECT_STATE_SUSPENDED;
object_unlock_exclusive(thiz);
}
break;
default: // impossible
object_unlock_exclusive(thiz);
assert(SL_BOOLEAN_FALSE);
break;
}
}
SL_LEAVE_INTERFACE
}
static SLresult IObject_GetState(SLObjectItf self, SLuint32 *pState)
{
SL_ENTER_INTERFACE
if (NULL == pState) {
result = SL_RESULT_PARAMETER_INVALID;
} else {
IObject *thiz = (IObject *) self;
object_lock_shared(thiz);
SLuint8 state = thiz->mState;
object_unlock_shared(thiz);
// Re-map the realizing, resuming, and suspending states
switch (state) {
case SL_OBJECT_STATE_REALIZING_1:
case SL_OBJECT_STATE_REALIZING_1A:
case SL_OBJECT_STATE_REALIZING_2:
case SL_OBJECT_STATE_DESTROYING: // application shouldn't call GetState after Destroy
state = SL_OBJECT_STATE_UNREALIZED;
break;
case SL_OBJECT_STATE_RESUMING_1:
case SL_OBJECT_STATE_RESUMING_1A:
case SL_OBJECT_STATE_RESUMING_2:
case SL_OBJECT_STATE_SUSPENDING:
state = SL_OBJECT_STATE_SUSPENDED;
break;
case SL_OBJECT_STATE_UNREALIZED:
case SL_OBJECT_STATE_REALIZED:
case SL_OBJECT_STATE_SUSPENDED:
// These are the "official" object states, return them as is
break;
default:
assert(SL_BOOLEAN_FALSE);
break;
}
*pState = state;
result = SL_RESULT_SUCCESS;
}
SL_LEAVE_INTERFACE
}
static SLresult IObject_GetInterface(SLObjectItf self, const SLInterfaceID iid, void *pInterface)
{
SL_ENTER_INTERFACE
if (NULL == pInterface) {
result = SL_RESULT_PARAMETER_INVALID;
} else {
void *interface = NULL;
if (NULL == iid) {
result = SL_RESULT_PARAMETER_INVALID;
} else {
IObject *thiz = (IObject *) self;
const ClassTable *clazz = thiz->mClass;
int MPH, index;
if ((0 > (MPH = IID_to_MPH(iid))) ||
// no need to check for an initialization hook
// (NULL == MPH_init_table[MPH].mInit) ||
(0 > (index = clazz->mMPH_to_index[MPH]))) {
result = SL_RESULT_FEATURE_UNSUPPORTED;
} else {
unsigned mask = 1 << index;
object_lock_exclusive(thiz);
if ((SL_OBJECT_STATE_REALIZED != thiz->mState) &&
!(INTERFACE_PREREALIZE & clazz->mInterfaces[index].mInterface)) {
// Can't get interface on an unrealized object unless pre-realize is ok
result = SL_RESULT_PRECONDITIONS_VIOLATED;
} else if ((MPH_MUTESOLO == MPH) && (SL_OBJECTID_AUDIOPLAYER ==
clazz->mSLObjectID) && (1 == ((CAudioPlayer *) thiz)->mNumChannels)) {
// Can't get the MuteSolo interface of an audio player if the channel count is
// mono, but _can_ get the MuteSolo interface if the channel count is unknown
result = SL_RESULT_FEATURE_UNSUPPORTED;
} else {
switch (thiz->mInterfaceStates[index]) {
case INTERFACE_EXPOSED:
case INTERFACE_ADDED:
interface = (char *) thiz + clazz->mInterfaces[index].mOffset;
// Note that interface has been gotten,
// for debugger and to detect incorrect use of interfaces
if (!(thiz->mGottenMask & mask)) {
thiz->mGottenMask |= mask;
// This trickery validates the v-table
((size_t *) interface)[0] ^= ~0;
}
result = SL_RESULT_SUCCESS;
break;
// Can't get interface if uninitialized, initialized, suspended,
// suspending, resuming, adding, or removing
default:
result = SL_RESULT_FEATURE_UNSUPPORTED;
break;
}
}
object_unlock_exclusive(thiz);
}
}
*(void **)pInterface = interface;
}
SL_LEAVE_INTERFACE
}
static SLresult IObject_RegisterCallback(SLObjectItf self,
slObjectCallback callback, void *pContext)
{
SL_ENTER_INTERFACE
IObject *thiz = (IObject *) self;
object_lock_exclusive(thiz);
thiz->mCallback = callback;
thiz->mContext = pContext;
object_unlock_exclusive(thiz);
result = SL_RESULT_SUCCESS;
SL_LEAVE_INTERFACE
}
/** \brief This is internal common code for Abort and Destroy.
* Note: called with mutex unlocked, and returns with mutex locked.
*/
static void Abort_internal(IObject *thiz)
{
const ClassTable *clazz = thiz->mClass;
bool anyAsync = false;
object_lock_exclusive(thiz);
// Abort asynchronous operations on the object
switch (thiz->mState) {
case SL_OBJECT_STATE_REALIZING_1: // Realize
thiz->mState = SL_OBJECT_STATE_REALIZING_1A;
anyAsync = true;
break;
case SL_OBJECT_STATE_RESUMING_1: // Resume
thiz->mState = SL_OBJECT_STATE_RESUMING_1A;
anyAsync = true;
break;
case SL_OBJECT_STATE_REALIZING_1A: // Realize
case SL_OBJECT_STATE_REALIZING_2:
case SL_OBJECT_STATE_RESUMING_1A: // Resume
case SL_OBJECT_STATE_RESUMING_2:
anyAsync = true;
break;
case SL_OBJECT_STATE_DESTROYING:
assert(false);
break;
default:
break;
}
// Abort asynchronous operations on interfaces
SLuint8 *interfaceStateP = thiz->mInterfaceStates;
unsigned index;
for (index = 0; index < clazz->mInterfaceCount; ++index, ++interfaceStateP) {
switch (*interfaceStateP) {
case INTERFACE_ADDING_1: // AddInterface
*interfaceStateP = INTERFACE_ADDING_1A;
anyAsync = true;
break;
case INTERFACE_RESUMING_1: // ResumeInterface
*interfaceStateP = INTERFACE_RESUMING_1A;
anyAsync = true;
break;
case INTERFACE_ADDING_1A: // AddInterface
case INTERFACE_ADDING_2:
case INTERFACE_RESUMING_1A: // ResumeInterface
case INTERFACE_RESUMING_2:
case INTERFACE_REMOVING: // not observable: RemoveInterface is synchronous & mutex locked
anyAsync = true;
break;
default:
break;
}
}
// Wait until all asynchronous operations either complete normally or recognize the abort
while (anyAsync) {
object_unlock_exclusive(thiz);
// FIXME should use condition variable instead of polling
usleep(20000);
anyAsync = false;
object_lock_exclusive(thiz);
switch (thiz->mState) {
case SL_OBJECT_STATE_REALIZING_1: // state 1 means it cycled during the usleep window
case SL_OBJECT_STATE_RESUMING_1:
case SL_OBJECT_STATE_REALIZING_1A:
case SL_OBJECT_STATE_REALIZING_2:
case SL_OBJECT_STATE_RESUMING_1A:
case SL_OBJECT_STATE_RESUMING_2:
anyAsync = true;
break;
case SL_OBJECT_STATE_DESTROYING:
assert(false);
break;
default:
break;
}
interfaceStateP = thiz->mInterfaceStates;
for (index = 0; index < clazz->mInterfaceCount; ++index, ++interfaceStateP) {
switch (*interfaceStateP) {
case INTERFACE_ADDING_1: // state 1 means it cycled during the usleep window
case INTERFACE_RESUMING_1:
case INTERFACE_ADDING_1A:
case INTERFACE_ADDING_2:
case INTERFACE_RESUMING_1A:
case INTERFACE_RESUMING_2:
case INTERFACE_REMOVING:
anyAsync = true;
break;
default:
break;
}
}
}
// At this point there are no pending asynchronous operations
}
static void IObject_AbortAsyncOperation(SLObjectItf self)
{
SL_ENTER_INTERFACE_VOID
IObject *thiz = (IObject *) self;
Abort_internal(thiz);
object_unlock_exclusive(thiz);
SL_LEAVE_INTERFACE_VOID
}
void IObject_Destroy(SLObjectItf self)
{
SL_ENTER_INTERFACE_VOID
IObject *thiz = (IObject *) self;
// mutex is unlocked
Abort_internal(thiz);
// mutex is locked
const ClassTable *clazz = thiz->mClass;
PreDestroyHook preDestroy = clazz->mPreDestroy;
// The pre-destroy hook is called with mutex locked, and should block until it is safe to
// destroy. It is OK to unlock the mutex temporarily, as it long as it re-locks the mutex
// before returning.
if (NULL != preDestroy) {
predestroy_t okToDestroy = (*preDestroy)(thiz);
switch (okToDestroy) {
case predestroy_ok:
break;
case predestroy_error:
SL_LOGE("Object::Destroy(%p) not allowed", thiz);
FALLTHROUGH_INTENDED;
case predestroy_again:
object_unlock_exclusive(thiz);
// unfortunately Destroy doesn't return a result
SL_LEAVE_INTERFACE_VOID
// unreachable
default:
assert(false);
break;
}
}
thiz->mState = SL_OBJECT_STATE_DESTROYING;
VoidHook destroy = clazz->mDestroy;
// const, no lock needed
IEngine *thisEngine = &thiz->mEngine->mEngine;
unsigned i = thiz->mInstanceID;
assert(MAX_INSTANCE >= i);
// avoid a recursive lock on the engine when destroying the engine itself
if (thisEngine->mThis != thiz) {
interface_lock_exclusive(thisEngine);
}
// An unpublished object has a slot reserved, but the ID hasn't been chosen yet
assert(0 < thisEngine->mInstanceCount);
--thisEngine->mInstanceCount;
// If object is published, then remove it from exposure to sync thread and debugger
if (0 != i) {
--i;
unsigned mask = 1 << i;
assert(thisEngine->mInstanceMask & mask);
thisEngine->mInstanceMask &= ~mask;
assert(thisEngine->mInstances[i] == thiz);
thisEngine->mInstances[i] = NULL;
}
// avoid a recursive unlock on the engine when destroying the engine itself
if (thisEngine->mThis != thiz) {
interface_unlock_exclusive(thisEngine);
}
// The destroy hook is called with mutex locked
if (NULL != destroy) {
(*destroy)(thiz);
}
// Call the deinitializer for each currently initialized interface,
// whether it is implicit, explicit, optional, or dynamically added.
// The deinitializers are called in the reverse order that the
// initializers were called, so that IObject_deinit is called last.
unsigned index = clazz->mInterfaceCount;
const struct iid_vtable *x = &clazz->mInterfaces[index];
SLuint8 *interfaceStateP = &thiz->mInterfaceStates[index];
for ( ; index > 0; --index) {
--x;
size_t offset = x->mOffset;
void *thisItf = (char *) thiz + offset;
SLuint32 state = *--interfaceStateP;
switch (state) {
case INTERFACE_UNINITIALIZED:
break;
case INTERFACE_EXPOSED: // quiescent states
case INTERFACE_ADDED:
case INTERFACE_SUSPENDED:
// The remove hook is called with mutex locked
{
VoidHook remove = MPH_init_table[x->mMPH].mRemove;
if (NULL != remove) {
(*remove)(thisItf);
}
*interfaceStateP = INTERFACE_INITIALIZED;
}
FALLTHROUGH_INTENDED;
case INTERFACE_INITIALIZED:
{
VoidHook deinit = MPH_init_table[x->mMPH].mDeinit;
if (NULL != deinit) {
(*deinit)(thisItf);
}
*interfaceStateP = INTERFACE_UNINITIALIZED;
}
break;
case INTERFACE_ADDING_1: // active states indicate incorrect use of API
case INTERFACE_ADDING_1A:
case INTERFACE_ADDING_2:
case INTERFACE_RESUMING_1:
case INTERFACE_RESUMING_1A:
case INTERFACE_RESUMING_2:
case INTERFACE_REMOVING:
case INTERFACE_SUSPENDING:
SL_LOGE("Object::Destroy(%p) while interface %u active", thiz, index);
break;
default:
assert(SL_BOOLEAN_FALSE);
break;
}
}
// The mutex is unlocked and destroyed by IObject_deinit, which is the last deinitializer
memset(thiz, 0x55, clazz->mSize); // catch broken applications that continue using interfaces
// was ifdef USE_DEBUG but safer to do this unconditionally
if (SL_OBJECTID_ENGINE == clazz->mSLObjectID) {
CEngine_Destroyed((CEngine *) thiz);
}
free(thiz);
SL_LEAVE_INTERFACE_VOID
}
static SLresult IObject_SetPriority(SLObjectItf self, SLint32 priority, SLboolean preemptable)
{
SL_ENTER_INTERFACE
#if USE_PROFILES & USE_PROFILES_BASE
IObject *thiz = (IObject *) self;
object_lock_exclusive(thiz);
thiz->mPriority = priority;
thiz->mPreemptable = SL_BOOLEAN_FALSE != preemptable; // normalize
object_unlock_exclusive(thiz);
result = SL_RESULT_SUCCESS;
#else
result = SL_RESULT_FEATURE_UNSUPPORTED;
#endif
SL_LEAVE_INTERFACE
}
static SLresult IObject_GetPriority(SLObjectItf self, SLint32 *pPriority, SLboolean *pPreemptable)
{
SL_ENTER_INTERFACE
#if USE_PROFILES & USE_PROFILES_BASE
if (NULL == pPriority || NULL == pPreemptable) {
result = SL_RESULT_PARAMETER_INVALID;
} else {
IObject *thiz = (IObject *) self;
object_lock_shared(thiz);
SLint32 priority = thiz->mPriority;
SLboolean preemptable = thiz->mPreemptable;
object_unlock_shared(thiz);
*pPriority = priority;
*pPreemptable = preemptable;
result = SL_RESULT_SUCCESS;
}
#else
result = SL_RESULT_FEATURE_UNSUPPORTED;
#endif
SL_LEAVE_INTERFACE
}
static SLresult IObject_SetLossOfControlInterfaces(SLObjectItf self,
SLint16 numInterfaces, SLInterfaceID *pInterfaceIDs, SLboolean enabled)
{
SL_ENTER_INTERFACE
#if USE_PROFILES & USE_PROFILES_BASE
result = SL_RESULT_SUCCESS;
if (0 < numInterfaces) {
SLuint32 i;
if (NULL == pInterfaceIDs) {
result = SL_RESULT_PARAMETER_INVALID;
} else {
IObject *thiz = (IObject *) self;
const ClassTable *clazz = thiz->mClass;
unsigned lossOfControlMask = 0;
// The cast is due to a typo in the spec, bug 6482
for (i = 0; i < (SLuint32) numInterfaces; ++i) {
SLInterfaceID iid = pInterfaceIDs[i];
if (NULL == iid) {
result = SL_RESULT_PARAMETER_INVALID;
goto out;
}
int MPH, index;
// We ignore without error any invalid MPH or index, but spec is unclear
if ((0 <= (MPH = IID_to_MPH(iid))) &&
// no need to check for an initialization hook
// (NULL == MPH_init_table[MPH].mInit) ||
(0 <= (index = clazz->mMPH_to_index[MPH]))) {
lossOfControlMask |= (1 << index);
}
}
object_lock_exclusive(thiz);
if (enabled) {
thiz->mLossOfControlMask |= lossOfControlMask;
} else {
thiz->mLossOfControlMask &= ~lossOfControlMask;
}
object_unlock_exclusive(thiz);
}
}
out:
#else
result = SL_RESULT_FEATURE_UNSUPPORTED;
#endif
SL_LEAVE_INTERFACE
}
static const struct SLObjectItf_ IObject_Itf = {
IObject_Realize,
IObject_Resume,
IObject_GetState,
IObject_GetInterface,
IObject_RegisterCallback,
IObject_AbortAsyncOperation,
IObject_Destroy,
IObject_SetPriority,
IObject_GetPriority,
IObject_SetLossOfControlInterfaces
};
/** \brief This must be the first initializer called for an object */
void IObject_init(void *self)
{
IObject *thiz = (IObject *) self;
thiz->mItf = &IObject_Itf;
// initialized in construct:
// mClass
// mInstanceID
// mLossOfControlMask
// mEngine
// mInterfaceStates
thiz->mState = SL_OBJECT_STATE_UNREALIZED;
thiz->mGottenMask = 1; // IObject
thiz->mAttributesMask = 0;
thiz->mCallback = NULL;
thiz->mContext = NULL;
#if USE_PROFILES & USE_PROFILES_BASE
thiz->mPriority = SL_PRIORITY_NORMAL;
thiz->mPreemptable = SL_BOOLEAN_FALSE;
#endif
thiz->mStrongRefCount = 0;
int ok;
ok = pthread_mutex_init(&thiz->mMutex, (const pthread_mutexattr_t *) NULL);
assert(0 == ok);
#ifdef USE_DEBUG
memset(&thiz->mOwner, 0, sizeof(pthread_t));
thiz->mFile = NULL;
thiz->mLine = 0;
thiz->mGeneration = 0;
#endif
ok = pthread_cond_init(&thiz->mCond, (const pthread_condattr_t *) NULL);
assert(0 == ok);
}
/** \brief This must be the last deinitializer called for an object */
void IObject_deinit(void *self)
{
IObject *thiz = (IObject *) self;
#ifdef USE_DEBUG
assert(pthread_equal(pthread_self(), thiz->mOwner));
#endif
int ok;
ok = pthread_cond_destroy(&thiz->mCond);
assert(0 == ok);
// equivalent to object_unlock_exclusive, but without the rigmarole
ok = pthread_mutex_unlock(&thiz->mMutex);
assert(0 == ok);
ok = pthread_mutex_destroy(&thiz->mMutex);
assert(0 == ok);
// redundant: thiz->mState = SL_OBJECT_STATE_UNREALIZED;
}
/** \brief Publish a new object after it is fully initialized.
* Publishing will expose the object to sync thread and debugger,
* and make it safe to return the SLObjectItf to the application.
*/
void IObject_Publish(IObject *thiz)
{
IEngine *thisEngine = &thiz->mEngine->mEngine;
interface_lock_exclusive(thisEngine);
// construct earlier reserved a pending slot, but did not choose the actual slot number
unsigned availMask = ~thisEngine->mInstanceMask;
assert(availMask);
unsigned i = ctz(availMask);
assert(MAX_INSTANCE > i);
assert(NULL == thisEngine->mInstances[i]);
thisEngine->mInstances[i] = thiz;
thisEngine->mInstanceMask |= 1 << i;
// avoid zero as a valid instance ID
thiz->mInstanceID = i + 1;
interface_unlock_exclusive(thisEngine);
}