/* * 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); // fall through 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; } // fall through 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 free(thiz); if (SL_OBJECTID_ENGINE == clazz->mSLObjectID) { CEngine_Destroyed((CEngine *) 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); }