/* * 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, int unused) { // validate input parameters IObject *this = (IObject *) self; assert(NULL != this); const ClassTable *class__ = this->mClass; assert(NULL != class__); AsyncHook realize = class__->mRealize; SLresult result; SLuint8 state; // check object state object_lock_exclusive(this); state = this->mState; switch (state) { case SL_OBJECT_STATE_REALIZING_1: // normal case if (NULL != realize) { this->mState = SL_OBJECT_STATE_REALIZING_2; object_unlock_exclusive(this); // Note that the mutex is unlocked during the realize hook result = (*realize)(this, SL_BOOLEAN_TRUE); object_lock_exclusive(this); assert(SL_OBJECT_STATE_REALIZING_2 == this->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 this->mState = state; // Make a copy of these, so we can call the callback with mutex unlocked slObjectCallback callback = this->mCallback; void *context = this->mContext; object_unlock_exclusive(this); // Note that the mutex is unlocked during the callback if (NULL != callback) { (*callback)(&this->mItf, context, SL_OBJECT_EVENT_ASYNC_TERMINATION, result, state, NULL); } } static SLresult IObject_Realize(SLObjectItf self, SLboolean async) { SL_ENTER_INTERFACE IObject *this = (IObject *) self; SLuint8 state; const ClassTable *class__ = this->mClass; object_lock_exclusive(this); state = this->mState; // Reject redundant calls to Realize if (SL_OBJECT_STATE_UNREALIZED != state) { object_unlock_exclusive(this); result = SL_RESULT_PRECONDITIONS_VIOLATED; } else { // Asynchronous: mark operation pending and cancellable if (async && (SL_OBJECTID_ENGINE != class__->mObjectID)) { state = SL_OBJECT_STATE_REALIZING_1; // Synchronous: mark operation pending and non-cancellable } else { state = SL_OBJECT_STATE_REALIZING_2; } this->mState = state; object_unlock_exclusive(this); switch (state) { case SL_OBJECT_STATE_REALIZING_1: // asynchronous on non-Engine assert(async); result = ThreadPool_add(&this->mEngine->mThreadPool, HandleRealize, this, 0); if (SL_RESULT_SUCCESS != result) { // Engine was destroyed during realize, or insufficient memory object_lock_exclusive(this); this->mState = SL_OBJECT_STATE_UNREALIZED; object_unlock_exclusive(this); } break; case SL_OBJECT_STATE_REALIZING_2: // synchronous, or asynchronous on Engine { AsyncHook realize = class__->mRealize; // Note that the mutex is unlocked during the realize hook result = (NULL != realize) ? (*realize)(this, async) : SL_RESULT_SUCCESS; object_lock_exclusive(this); assert(SL_OBJECT_STATE_REALIZING_2 == this->mState); state = (SL_RESULT_SUCCESS == result) ? SL_OBJECT_STATE_REALIZED : SL_OBJECT_STATE_UNREALIZED; this->mState = state; slObjectCallback callback = this->mCallback; void *context = this->mContext; object_unlock_exclusive(this); // 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)(&this->mItf, context, SL_OBJECT_EVENT_ASYNC_TERMINATION, result, state, NULL); } } break; default: // impossible 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, int unused) { // valid input parameters IObject *this = (IObject *) self; assert(NULL != this); const ClassTable *class__ = this->mClass; assert(NULL != class__); AsyncHook resume = class__->mResume; SLresult result; SLuint8 state; // check object state object_lock_exclusive(this); state = this->mState; switch (state) { case SL_OBJECT_STATE_RESUMING_1: // normal case if (NULL != resume) { this->mState = SL_OBJECT_STATE_RESUMING_2; object_unlock_exclusive(this); // Note that the mutex is unlocked during the resume hook result = (*resume)(this, SL_BOOLEAN_TRUE); object_lock_exclusive(this); assert(SL_OBJECT_STATE_RESUMING_2 == this->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 this->mState = state; // Make a copy of these, so we can call the callback with mutex unlocked slObjectCallback callback = this->mCallback; void *context = this->mContext; object_unlock_exclusive(this); // Note that the mutex is unlocked during the callback if (NULL != callback) { (*callback)(&this->mItf, context, SL_OBJECT_EVENT_ASYNC_TERMINATION, result, state, NULL); } } static SLresult IObject_Resume(SLObjectItf self, SLboolean async) { SL_ENTER_INTERFACE IObject *this = (IObject *) self; const ClassTable *class__ = this->mClass; SLuint8 state; object_lock_exclusive(this); state = this->mState; // Reject redundant calls to Resume if (SL_OBJECT_STATE_SUSPENDED != state) { object_unlock_exclusive(this); 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; } this->mState = state; object_unlock_exclusive(this); switch (state) { case SL_OBJECT_STATE_RESUMING_1: // asynchronous assert(async); result = ThreadPool_add(&this->mEngine->mThreadPool, HandleResume, this, 0); if (SL_RESULT_SUCCESS != result) { // Engine was destroyed during resume, or insufficient memory object_lock_exclusive(this); this->mState = SL_OBJECT_STATE_SUSPENDED; object_unlock_exclusive(this); } break; case SL_OBJECT_STATE_RESUMING_2: // synchronous { AsyncHook resume = class__->mResume; // Note that the mutex is unlocked during the resume hook result = (NULL != resume) ? (*resume)(this, SL_BOOLEAN_FALSE) : SL_RESULT_SUCCESS; object_lock_exclusive(this); assert(SL_OBJECT_STATE_RESUMING_2 == this->mState); this->mState = (SL_RESULT_SUCCESS == result) ? SL_OBJECT_STATE_REALIZED : SL_OBJECT_STATE_SUSPENDED; object_unlock_exclusive(this); } break; default: // impossible 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 *this = (IObject *) self; // Note that the state is immediately obsolete, so a peek lock is safe object_lock_peek(this); SLuint8 state = this->mState; object_unlock_peek(this); // 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 *this = (IObject *) self; const ClassTable *class__ = this->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 = class__->mMPH_to_index[MPH]))) { result = SL_RESULT_FEATURE_UNSUPPORTED; } else { unsigned mask = 1 << index; object_lock_exclusive(this); if ((SL_OBJECT_STATE_REALIZED != this->mState) && !(INTERFACE_PREREALIZE & class__->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 == class__->mObjectID) && (1 == ((CAudioPlayer *) this)->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 (this->mInterfaceStates[index]) { case INTERFACE_EXPOSED: case INTERFACE_ADDED: interface = (char *) this + class__->mInterfaces[index].mOffset; // Note that interface has been gotten, // for debugger and to detect incorrect use of interfaces if (!(this->mGottenMask & mask)) { this->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(this); } } *(void **)pInterface = interface; } SL_LEAVE_INTERFACE } static SLresult IObject_RegisterCallback(SLObjectItf self, slObjectCallback callback, void *pContext) { SL_ENTER_INTERFACE IObject *this = (IObject *) self; object_lock_exclusive(this); this->mCallback = callback; this->mContext = pContext; object_unlock_exclusive(this); 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 *this) { const ClassTable *class__ = this->mClass; bool anyAsync = false; object_lock_exclusive(this); // Abort asynchronous operations on the object switch (this->mState) { case SL_OBJECT_STATE_REALIZING_1: // Realize this->mState = SL_OBJECT_STATE_REALIZING_1A; anyAsync = true; break; case SL_OBJECT_STATE_RESUMING_1: // Resume this->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 = this->mInterfaceStates; unsigned index; for (index = 0; index < class__->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(this); // FIXME should use condition variable instead of polling usleep(20000); anyAsync = false; object_lock_exclusive(this); switch (this->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 = this->mInterfaceStates; for (index = 0; index < class__->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 *this = (IObject *) self; Abort_internal(this); object_unlock_exclusive(this); SL_LEAVE_INTERFACE_VOID } void IObject_Destroy(SLObjectItf self) { SL_ENTER_INTERFACE_VOID IObject *this = (IObject *) self; // mutex is unlocked Abort_internal(this); // mutex is locked const ClassTable *class__ = this->mClass; BoolHook preDestroy = class__->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) { bool okToDestroy = (*preDestroy)(this); if (!okToDestroy) { object_unlock_exclusive(this); // unfortunately Destroy doesn't return a result SL_LOGE("Object::Destroy(%p) not allowed", this); SL_LEAVE_INTERFACE_VOID } } this->mState = SL_OBJECT_STATE_DESTROYING; VoidHook destroy = class__->mDestroy; // const, no lock needed IEngine *thisEngine = this->mEngine; unsigned i = this->mInstanceID; assert(MAX_INSTANCE >= i); // avoid a recursive lock on the engine when destroying the engine itself if (thisEngine->mThis != this) { 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] == this); thisEngine->mInstances[i] = NULL; } // avoid a recursive unlock on the engine when destroying the engine itself if (thisEngine->mThis != this) { interface_unlock_exclusive(thisEngine); } // The destroy hook is called with mutex locked if (NULL != destroy) { (*destroy)(this); } // 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 = class__->mInterfaceCount; const struct iid_vtable *x = &class__->mInterfaces[index]; SLuint8 *interfaceStateP = &this->mInterfaceStates[index]; for ( ; index > 0; --index) { --x; size_t offset = x->mOffset; void *thisItf = (char *) this + 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", this, index); break; default: assert(SL_BOOLEAN_FALSE); break; } } // The mutex is unlocked and destroyed by IObject_deinit, which is the last deinitializer memset(this, 0x55, class__->mSize); // catch broken applications that continue using interfaces // was ifdef USE_DEBUG but safer to do this unconditionally free(this); if (SL_OBJECTID_ENGINE == class__->mObjectID) { CEngine_Destroyed((CEngine *) this); } SL_LEAVE_INTERFACE_VOID } static SLresult IObject_SetPriority(SLObjectItf self, SLint32 priority, SLboolean preemptable) { SL_ENTER_INTERFACE #if USE_PROFILES & USE_PROFILES_BASE IObject *this = (IObject *) self; object_lock_exclusive(this); this->mPriority = priority; this->mPreemptable = SL_BOOLEAN_FALSE != preemptable; // normalize object_unlock_exclusive(this); 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 *this = (IObject *) self; object_lock_shared(this); SLint32 priority = this->mPriority; SLboolean preemptable = this->mPreemptable; object_unlock_shared(this); *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 *this = (IObject *) self; const ClassTable *class__ = this->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 = class__->mMPH_to_index[MPH]))) { lossOfControlMask |= (1 << index); } } object_lock_exclusive(this); if (enabled) { this->mLossOfControlMask |= lossOfControlMask; } else { this->mLossOfControlMask &= ~lossOfControlMask; } object_unlock_exclusive(this); } } 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 *this = (IObject *) self; this->mItf = &IObject_Itf; // initialized in construct: // mClass // mInstanceID // mLossOfControlMask // mEngine // mInterfaceStates this->mState = SL_OBJECT_STATE_UNREALIZED; this->mGottenMask = 1; // IObject this->mAttributesMask = 0; this->mCallback = NULL; this->mContext = NULL; #if USE_PROFILES & USE_PROFILES_BASE this->mPriority = SL_PRIORITY_NORMAL; this->mPreemptable = SL_BOOLEAN_FALSE; #endif this->mStrongRefCount = 0; int ok; ok = pthread_mutex_init(&this->mMutex, (const pthread_mutexattr_t *) NULL); assert(0 == ok); #ifdef USE_DEBUG memset(&this->mOwner, 0, sizeof(pthread_t)); this->mFile = NULL; this->mLine = 0; #endif ok = pthread_cond_init(&this->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 *this = (IObject *) self; #ifdef USE_DEBUG assert(pthread_equal(pthread_self(), this->mOwner)); #endif int ok; ok = pthread_cond_destroy(&this->mCond); assert(0 == ok); // equivalent to object_unlock_exclusive, but without the rigmarole ok = pthread_mutex_unlock(&this->mMutex); assert(0 == ok); ok = pthread_mutex_destroy(&this->mMutex); assert(0 == ok); // redundant: this->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 *this) { IEngine *thisEngine = this->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] = this; thisEngine->mInstanceMask |= 1 << i; // avoid zero as a valid instance ID this->mInstanceID = i + 1; interface_unlock_exclusive(thisEngine); }