IObject.c revision cf3a6383a9bc9a94ca5b424ea97313293ee0dcb0
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/* Object implementation */
18
19#include "sles_allinclusive.h"
20
21
22// Called by a worker thread to handle an asynchronous Object.Realize.
23// Parameter self is the Object.
24
25static void HandleRealize(void *self, void *ignored, int unused)
26{
27
28    // validate input parameters
29    IObject *thiz = (IObject *) self;
30    assert(NULL != thiz);
31    const ClassTable *clazz = thiz->mClass;
32    assert(NULL != clazz);
33    AsyncHook realize = clazz->mRealize;
34    SLresult result;
35    SLuint8 state;
36
37    // check object state
38    object_lock_exclusive(thiz);
39    state = thiz->mState;
40    switch (state) {
41
42    case SL_OBJECT_STATE_REALIZING_1:   // normal case
43        if (NULL != realize) {
44            thiz->mState = SL_OBJECT_STATE_REALIZING_2;
45            object_unlock_exclusive(thiz);
46            // Note that the mutex is unlocked during the realize hook
47            result = (*realize)(thiz, SL_BOOLEAN_TRUE);
48            object_lock_exclusive(thiz);
49            assert(SL_OBJECT_STATE_REALIZING_2 == thiz->mState);
50            state = SL_RESULT_SUCCESS == result ? SL_OBJECT_STATE_REALIZED :
51                SL_OBJECT_STATE_UNREALIZED;
52        } else {
53            result = SL_RESULT_SUCCESS;
54            state = SL_OBJECT_STATE_REALIZED;
55        }
56        break;
57
58    case SL_OBJECT_STATE_REALIZING_1A:  // operation was aborted while on work queue
59        result = SL_RESULT_OPERATION_ABORTED;
60        state = SL_OBJECT_STATE_UNREALIZED;
61        break;
62
63    default:                            // impossible
64        assert(SL_BOOLEAN_FALSE);
65        result = SL_RESULT_INTERNAL_ERROR;
66        break;
67
68    }
69
70    // mutex is locked, update state
71    thiz->mState = state;
72
73    // Make a copy of these, so we can call the callback with mutex unlocked
74    slObjectCallback callback = thiz->mCallback;
75    void *context = thiz->mContext;
76    object_unlock_exclusive(thiz);
77
78    // Note that the mutex is unlocked during the callback
79    if (NULL != callback) {
80        (*callback)(&thiz->mItf, context, SL_OBJECT_EVENT_ASYNC_TERMINATION, result, state, NULL);
81    }
82}
83
84
85static SLresult IObject_Realize(SLObjectItf self, SLboolean async)
86{
87    SL_ENTER_INTERFACE
88
89    IObject *thiz = (IObject *) self;
90    SLuint8 state;
91    const ClassTable *clazz = thiz->mClass;
92    bool isSharedEngine = false;
93    object_lock_exclusive(thiz);
94    // note that SL_OBJECTID_ENGINE and XA_OBJECTID_ENGINE map to same class
95    if (clazz == objectIDtoClass(SL_OBJECTID_ENGINE)) {
96        // important: the lock order is engine followed by theOneTrueMutex
97        int ok = pthread_mutex_lock(&theOneTrueMutex);
98        assert(0 == ok);
99        isSharedEngine = 1 < theOneTrueRefCount;
100        ok = pthread_mutex_unlock(&theOneTrueMutex);
101        assert(0 == ok);
102    }
103    state = thiz->mState;
104    // Reject redundant calls to Realize, except on a shared engine
105    if (SL_OBJECT_STATE_UNREALIZED != state) {
106        object_unlock_exclusive(thiz);
107        // redundant realize on the shared engine is permitted
108        if (isSharedEngine && (SL_OBJECT_STATE_REALIZED == state)) {
109            result = SL_RESULT_SUCCESS;
110        } else {
111            result = SL_RESULT_PRECONDITIONS_VIOLATED;
112        }
113    } else {
114        // Asynchronous: mark operation pending and cancellable
115        if (async && (SL_OBJECTID_ENGINE != clazz->mSLObjectID)) {
116            state = SL_OBJECT_STATE_REALIZING_1;
117        // Synchronous: mark operation pending and non-cancellable
118        } else {
119            state = SL_OBJECT_STATE_REALIZING_2;
120        }
121        thiz->mState = state;
122        object_unlock_exclusive(thiz);
123        switch (state) {
124        case SL_OBJECT_STATE_REALIZING_1: // asynchronous on non-Engine
125            assert(async);
126            result = ThreadPool_add_ppi(&thiz->mEngine->mThreadPool, HandleRealize, thiz, NULL, 0);
127            if (SL_RESULT_SUCCESS != result) {
128                // Engine was destroyed during realize, or insufficient memory
129                object_lock_exclusive(thiz);
130                thiz->mState = SL_OBJECT_STATE_UNREALIZED;
131                object_unlock_exclusive(thiz);
132            }
133            break;
134        case SL_OBJECT_STATE_REALIZING_2: // synchronous, or asynchronous on Engine
135            {
136            AsyncHook realize = clazz->mRealize;
137            // Note that the mutex is unlocked during the realize hook
138            result = (NULL != realize) ? (*realize)(thiz, async) : SL_RESULT_SUCCESS;
139            object_lock_exclusive(thiz);
140            assert(SL_OBJECT_STATE_REALIZING_2 == thiz->mState);
141            state = (SL_RESULT_SUCCESS == result) ? SL_OBJECT_STATE_REALIZED :
142                SL_OBJECT_STATE_UNREALIZED;
143            thiz->mState = state;
144            slObjectCallback callback = thiz->mCallback;
145            void *context = thiz->mContext;
146            object_unlock_exclusive(thiz);
147            // asynchronous Realize on an Engine is actually done synchronously, but still has
148            // callback because there is no thread pool yet to do it asynchronously.
149            if (async && (NULL != callback)) {
150                (*callback)(&thiz->mItf, context, SL_OBJECT_EVENT_ASYNC_TERMINATION, result, state,
151                    NULL);
152            }
153            }
154            break;
155        default:                          // impossible
156            assert(SL_BOOLEAN_FALSE);
157            break;
158        }
159    }
160
161    SL_LEAVE_INTERFACE
162}
163
164
165// Called by a worker thread to handle an asynchronous Object.Resume.
166// Parameter self is the Object.
167
168static void HandleResume(void *self, void *ignored, int unused)
169{
170
171    // valid input parameters
172    IObject *thiz = (IObject *) self;
173    assert(NULL != thiz);
174    const ClassTable *clazz = thiz->mClass;
175    assert(NULL != clazz);
176    AsyncHook resume = clazz->mResume;
177    SLresult result;
178    SLuint8 state;
179
180    // check object state
181    object_lock_exclusive(thiz);
182    state = thiz->mState;
183    switch (state) {
184
185    case SL_OBJECT_STATE_RESUMING_1:    // normal case
186        if (NULL != resume) {
187            thiz->mState = SL_OBJECT_STATE_RESUMING_2;
188            object_unlock_exclusive(thiz);
189            // Note that the mutex is unlocked during the resume hook
190            result = (*resume)(thiz, SL_BOOLEAN_TRUE);
191            object_lock_exclusive(thiz);
192            assert(SL_OBJECT_STATE_RESUMING_2 == thiz->mState);
193            state = SL_RESULT_SUCCESS == result ? SL_OBJECT_STATE_REALIZED :
194                SL_OBJECT_STATE_SUSPENDED;
195        } else {
196            result = SL_RESULT_SUCCESS;
197            state = SL_OBJECT_STATE_REALIZED;
198        }
199        break;
200
201    case SL_OBJECT_STATE_RESUMING_1A:   // operation was aborted while on work queue
202        result = SL_RESULT_OPERATION_ABORTED;
203        state = SL_OBJECT_STATE_SUSPENDED;
204        break;
205
206    default:                            // impossible
207        assert(SL_BOOLEAN_FALSE);
208        result = SL_RESULT_INTERNAL_ERROR;
209        break;
210
211    }
212
213    // mutex is unlocked, update state
214    thiz->mState = state;
215
216    // Make a copy of these, so we can call the callback with mutex unlocked
217    slObjectCallback callback = thiz->mCallback;
218    void *context = thiz->mContext;
219    object_unlock_exclusive(thiz);
220
221    // Note that the mutex is unlocked during the callback
222    if (NULL != callback) {
223        (*callback)(&thiz->mItf, context, SL_OBJECT_EVENT_ASYNC_TERMINATION, result, state, NULL);
224    }
225}
226
227
228static SLresult IObject_Resume(SLObjectItf self, SLboolean async)
229{
230    SL_ENTER_INTERFACE
231
232    IObject *thiz = (IObject *) self;
233    const ClassTable *clazz = thiz->mClass;
234    SLuint8 state;
235    object_lock_exclusive(thiz);
236    state = thiz->mState;
237    // Reject redundant calls to Resume
238    if (SL_OBJECT_STATE_SUSPENDED != state) {
239        object_unlock_exclusive(thiz);
240        result = SL_RESULT_PRECONDITIONS_VIOLATED;
241    } else {
242        // Asynchronous: mark operation pending and cancellable
243        if (async) {
244            state = SL_OBJECT_STATE_RESUMING_1;
245        // Synchronous: mark operatio pending and non-cancellable
246        } else {
247            state = SL_OBJECT_STATE_RESUMING_2;
248        }
249        thiz->mState = state;
250        object_unlock_exclusive(thiz);
251        switch (state) {
252        case SL_OBJECT_STATE_RESUMING_1: // asynchronous
253            assert(async);
254            result = ThreadPool_add_ppi(&thiz->mEngine->mThreadPool, HandleResume, thiz, NULL, 0);
255            if (SL_RESULT_SUCCESS != result) {
256                // Engine was destroyed during resume, or insufficient memory
257                object_lock_exclusive(thiz);
258                thiz->mState = SL_OBJECT_STATE_SUSPENDED;
259                object_unlock_exclusive(thiz);
260            }
261            break;
262        case SL_OBJECT_STATE_RESUMING_2: // synchronous
263            {
264            AsyncHook resume = clazz->mResume;
265            // Note that the mutex is unlocked during the resume hook
266            result = (NULL != resume) ? (*resume)(thiz, SL_BOOLEAN_FALSE) : SL_RESULT_SUCCESS;
267            object_lock_exclusive(thiz);
268            assert(SL_OBJECT_STATE_RESUMING_2 == thiz->mState);
269            thiz->mState = (SL_RESULT_SUCCESS == result) ? SL_OBJECT_STATE_REALIZED :
270                SL_OBJECT_STATE_SUSPENDED;
271            object_unlock_exclusive(thiz);
272            }
273            break;
274        default:                        // impossible
275            assert(SL_BOOLEAN_FALSE);
276            break;
277        }
278    }
279
280    SL_LEAVE_INTERFACE
281}
282
283
284static SLresult IObject_GetState(SLObjectItf self, SLuint32 *pState)
285{
286    SL_ENTER_INTERFACE
287
288    if (NULL == pState) {
289        result = SL_RESULT_PARAMETER_INVALID;
290    } else {
291        IObject *thiz = (IObject *) self;
292        // Note that the state is immediately obsolete, so a peek lock is safe
293        object_lock_peek(thiz);
294        SLuint8 state = thiz->mState;
295        object_unlock_peek(thiz);
296        // Re-map the realizing, resuming, and suspending states
297        switch (state) {
298        case SL_OBJECT_STATE_REALIZING_1:
299        case SL_OBJECT_STATE_REALIZING_1A:
300        case SL_OBJECT_STATE_REALIZING_2:
301        case SL_OBJECT_STATE_DESTROYING:    // application shouldn't call GetState after Destroy
302            state = SL_OBJECT_STATE_UNREALIZED;
303            break;
304        case SL_OBJECT_STATE_RESUMING_1:
305        case SL_OBJECT_STATE_RESUMING_1A:
306        case SL_OBJECT_STATE_RESUMING_2:
307        case SL_OBJECT_STATE_SUSPENDING:
308            state = SL_OBJECT_STATE_SUSPENDED;
309            break;
310        case SL_OBJECT_STATE_UNREALIZED:
311        case SL_OBJECT_STATE_REALIZED:
312        case SL_OBJECT_STATE_SUSPENDED:
313            // These are the "official" object states, return them as is
314            break;
315        default:
316            assert(SL_BOOLEAN_FALSE);
317            break;
318        }
319        *pState = state;
320        result = SL_RESULT_SUCCESS;
321    }
322
323    SL_LEAVE_INTERFACE
324}
325
326static SLresult IObject_GetInterface(SLObjectItf self, const SLInterfaceID iid, void *pInterface)
327{
328    SL_ENTER_INTERFACE
329
330    if (NULL == pInterface) {
331        result = SL_RESULT_PARAMETER_INVALID;
332    } else {
333        void *interface = NULL;
334        if (NULL == iid) {
335            result = SL_RESULT_PARAMETER_INVALID;
336        } else {
337            IObject *thiz = (IObject *) self;
338            const ClassTable *clazz = thiz->mClass;
339            int MPH, index;
340            if ((0 > (MPH = IID_to_MPH(iid))) ||
341                    // no need to check for an initialization hook
342                    // (NULL == MPH_init_table[MPH].mInit) ||
343                    (0 > (index = clazz->mMPH_to_index[MPH]))) {
344                result = SL_RESULT_FEATURE_UNSUPPORTED;
345            } else {
346                unsigned mask = 1 << index;
347                object_lock_exclusive(thiz);
348                if ((SL_OBJECT_STATE_REALIZED != thiz->mState) &&
349                        !(INTERFACE_PREREALIZE & clazz->mInterfaces[index].mInterface)) {
350                    // Can't get interface on an unrealized object unless pre-realize is ok
351                    result = SL_RESULT_PRECONDITIONS_VIOLATED;
352                } else if ((MPH_MUTESOLO == MPH) && (SL_OBJECTID_AUDIOPLAYER ==
353                        clazz->mSLObjectID) && (1 == ((CAudioPlayer *) thiz)->mNumChannels)) {
354                    // Can't get the MuteSolo interface of an audio player if the channel count is
355                    // mono, but _can_ get the MuteSolo interface if the channel count is unknown
356                    result = SL_RESULT_FEATURE_UNSUPPORTED;
357                } else {
358                    switch (thiz->mInterfaceStates[index]) {
359                    case INTERFACE_EXPOSED:
360                    case INTERFACE_ADDED:
361                        interface = (char *) thiz + clazz->mInterfaces[index].mOffset;
362                        // Note that interface has been gotten,
363                        // for debugger and to detect incorrect use of interfaces
364                        if (!(thiz->mGottenMask & mask)) {
365                            thiz->mGottenMask |= mask;
366                            // This trickery validates the v-table
367                            ((size_t *) interface)[0] ^= ~0;
368                        }
369                        result = SL_RESULT_SUCCESS;
370                        break;
371                    // Can't get interface if uninitialized, initialized, suspended,
372                    // suspending, resuming, adding, or removing
373                    default:
374                        result = SL_RESULT_FEATURE_UNSUPPORTED;
375                        break;
376                    }
377                }
378                object_unlock_exclusive(thiz);
379            }
380        }
381        *(void **)pInterface = interface;
382    }
383
384    SL_LEAVE_INTERFACE
385}
386
387
388static SLresult IObject_RegisterCallback(SLObjectItf self,
389    slObjectCallback callback, void *pContext)
390{
391    SL_ENTER_INTERFACE
392
393    IObject *thiz = (IObject *) self;
394    object_lock_exclusive(thiz);
395    thiz->mCallback = callback;
396    thiz->mContext = pContext;
397    object_unlock_exclusive(thiz);
398    result = SL_RESULT_SUCCESS;
399
400    SL_LEAVE_INTERFACE
401}
402
403
404/** \brief This is internal common code for Abort and Destroy.
405 *  Note: called with mutex unlocked, and returns with mutex locked.
406 */
407
408static void Abort_internal(IObject *thiz)
409{
410    const ClassTable *clazz = thiz->mClass;
411    bool anyAsync = false;
412    object_lock_exclusive(thiz);
413
414    // Abort asynchronous operations on the object
415    switch (thiz->mState) {
416    case SL_OBJECT_STATE_REALIZING_1:   // Realize
417        thiz->mState = SL_OBJECT_STATE_REALIZING_1A;
418        anyAsync = true;
419        break;
420    case SL_OBJECT_STATE_RESUMING_1:    // Resume
421        thiz->mState = SL_OBJECT_STATE_RESUMING_1A;
422        anyAsync = true;
423        break;
424    case SL_OBJECT_STATE_REALIZING_1A:  // Realize
425    case SL_OBJECT_STATE_REALIZING_2:
426    case SL_OBJECT_STATE_RESUMING_1A:   // Resume
427    case SL_OBJECT_STATE_RESUMING_2:
428        anyAsync = true;
429        break;
430    case SL_OBJECT_STATE_DESTROYING:
431        assert(false);
432        break;
433    default:
434        break;
435    }
436
437    // Abort asynchronous operations on interfaces
438    SLuint8 *interfaceStateP = thiz->mInterfaceStates;
439    unsigned index;
440    for (index = 0; index < clazz->mInterfaceCount; ++index, ++interfaceStateP) {
441        switch (*interfaceStateP) {
442        case INTERFACE_ADDING_1:    // AddInterface
443            *interfaceStateP = INTERFACE_ADDING_1A;
444            anyAsync = true;
445            break;
446        case INTERFACE_RESUMING_1:  // ResumeInterface
447            *interfaceStateP = INTERFACE_RESUMING_1A;
448            anyAsync = true;
449            break;
450        case INTERFACE_ADDING_1A:   // AddInterface
451        case INTERFACE_ADDING_2:
452        case INTERFACE_RESUMING_1A: // ResumeInterface
453        case INTERFACE_RESUMING_2:
454        case INTERFACE_REMOVING:    // not observable: RemoveInterface is synchronous & mutex locked
455            anyAsync = true;
456            break;
457        default:
458            break;
459        }
460    }
461
462    // Wait until all asynchronous operations either complete normally or recognize the abort
463    while (anyAsync) {
464        object_unlock_exclusive(thiz);
465        // FIXME should use condition variable instead of polling
466        usleep(20000);
467        anyAsync = false;
468        object_lock_exclusive(thiz);
469        switch (thiz->mState) {
470        case SL_OBJECT_STATE_REALIZING_1:   // state 1 means it cycled during the usleep window
471        case SL_OBJECT_STATE_RESUMING_1:
472        case SL_OBJECT_STATE_REALIZING_1A:
473        case SL_OBJECT_STATE_REALIZING_2:
474        case SL_OBJECT_STATE_RESUMING_1A:
475        case SL_OBJECT_STATE_RESUMING_2:
476            anyAsync = true;
477            break;
478        case SL_OBJECT_STATE_DESTROYING:
479            assert(false);
480            break;
481        default:
482            break;
483        }
484        interfaceStateP = thiz->mInterfaceStates;
485        for (index = 0; index < clazz->mInterfaceCount; ++index, ++interfaceStateP) {
486            switch (*interfaceStateP) {
487            case INTERFACE_ADDING_1:    // state 1 means it cycled during the usleep window
488            case INTERFACE_RESUMING_1:
489            case INTERFACE_ADDING_1A:
490            case INTERFACE_ADDING_2:
491            case INTERFACE_RESUMING_1A:
492            case INTERFACE_RESUMING_2:
493            case INTERFACE_REMOVING:
494                anyAsync = true;
495                break;
496            default:
497                break;
498            }
499        }
500    }
501
502    // At this point there are no pending asynchronous operations
503}
504
505
506static void IObject_AbortAsyncOperation(SLObjectItf self)
507{
508    SL_ENTER_INTERFACE_VOID
509
510    IObject *thiz = (IObject *) self;
511    Abort_internal(thiz);
512    object_unlock_exclusive(thiz);
513
514    SL_LEAVE_INTERFACE_VOID
515}
516
517
518void IObject_Destroy(SLObjectItf self)
519{
520    SL_ENTER_INTERFACE_VOID
521
522    IObject *thiz = (IObject *) self;
523    // mutex is unlocked
524    Abort_internal(thiz);
525    // mutex is locked
526    const ClassTable *clazz = thiz->mClass;
527    PreDestroyHook preDestroy = clazz->mPreDestroy;
528    // The pre-destroy hook is called with mutex locked, and should block until it is safe to
529    // destroy.  It is OK to unlock the mutex temporarily, as it long as it re-locks the mutex
530    // before returning.
531    if (NULL != preDestroy) {
532        predestroy_t okToDestroy = (*preDestroy)(thiz);
533        switch (okToDestroy) {
534        case predestroy_ok:
535            break;
536        case predestroy_error:
537            SL_LOGE("Object::Destroy(%p) not allowed", thiz);
538            // fall through
539        case predestroy_again:
540            object_unlock_exclusive(thiz);
541            // unfortunately Destroy doesn't return a result
542            SL_LEAVE_INTERFACE_VOID
543            // unreachable
544        default:
545            assert(false);
546            break;
547        }
548    }
549    thiz->mState = SL_OBJECT_STATE_DESTROYING;
550    VoidHook destroy = clazz->mDestroy;
551    // const, no lock needed
552    IEngine *thisEngine = &thiz->mEngine->mEngine;
553    unsigned i = thiz->mInstanceID;
554    assert(MAX_INSTANCE >= i);
555    // avoid a recursive lock on the engine when destroying the engine itself
556    if (thisEngine->mThis != thiz) {
557        interface_lock_exclusive(thisEngine);
558    }
559    // An unpublished object has a slot reserved, but the ID hasn't been chosen yet
560    assert(0 < thisEngine->mInstanceCount);
561    --thisEngine->mInstanceCount;
562    // If object is published, then remove it from exposure to sync thread and debugger
563    if (0 != i) {
564        --i;
565        unsigned mask = 1 << i;
566        assert(thisEngine->mInstanceMask & mask);
567        thisEngine->mInstanceMask &= ~mask;
568        assert(thisEngine->mInstances[i] == thiz);
569        thisEngine->mInstances[i] = NULL;
570    }
571    // avoid a recursive unlock on the engine when destroying the engine itself
572    if (thisEngine->mThis != thiz) {
573        interface_unlock_exclusive(thisEngine);
574    }
575    // The destroy hook is called with mutex locked
576    if (NULL != destroy) {
577        (*destroy)(thiz);
578    }
579    // Call the deinitializer for each currently initialized interface,
580    // whether it is implicit, explicit, optional, or dynamically added.
581    // The deinitializers are called in the reverse order that the
582    // initializers were called, so that IObject_deinit is called last.
583    unsigned index = clazz->mInterfaceCount;
584    const struct iid_vtable *x = &clazz->mInterfaces[index];
585    SLuint8 *interfaceStateP = &thiz->mInterfaceStates[index];
586    for ( ; index > 0; --index) {
587        --x;
588        size_t offset = x->mOffset;
589        void *thisItf = (char *) thiz + offset;
590        SLuint32 state = *--interfaceStateP;
591        switch (state) {
592        case INTERFACE_UNINITIALIZED:
593            break;
594        case INTERFACE_EXPOSED:     // quiescent states
595        case INTERFACE_ADDED:
596        case INTERFACE_SUSPENDED:
597            // The remove hook is called with mutex locked
598            {
599            VoidHook remove = MPH_init_table[x->mMPH].mRemove;
600            if (NULL != remove) {
601                (*remove)(thisItf);
602            }
603            *interfaceStateP = INTERFACE_INITIALIZED;
604            }
605            // fall through
606        case INTERFACE_INITIALIZED:
607            {
608            VoidHook deinit = MPH_init_table[x->mMPH].mDeinit;
609            if (NULL != deinit) {
610                (*deinit)(thisItf);
611            }
612            *interfaceStateP = INTERFACE_UNINITIALIZED;
613            }
614            break;
615        case INTERFACE_ADDING_1:    // active states indicate incorrect use of API
616        case INTERFACE_ADDING_1A:
617        case INTERFACE_ADDING_2:
618        case INTERFACE_RESUMING_1:
619        case INTERFACE_RESUMING_1A:
620        case INTERFACE_RESUMING_2:
621        case INTERFACE_REMOVING:
622        case INTERFACE_SUSPENDING:
623            SL_LOGE("Object::Destroy(%p) while interface %u active", thiz, index);
624            break;
625        default:
626            assert(SL_BOOLEAN_FALSE);
627            break;
628        }
629    }
630    // The mutex is unlocked and destroyed by IObject_deinit, which is the last deinitializer
631    memset(thiz, 0x55, clazz->mSize); // catch broken applications that continue using interfaces
632                                        // was ifdef USE_DEBUG but safer to do this unconditionally
633    free(thiz);
634
635    if (SL_OBJECTID_ENGINE == clazz->mSLObjectID) {
636        CEngine_Destroyed((CEngine *) thiz);
637    }
638
639    SL_LEAVE_INTERFACE_VOID
640}
641
642
643static SLresult IObject_SetPriority(SLObjectItf self, SLint32 priority, SLboolean preemptable)
644{
645    SL_ENTER_INTERFACE
646
647#if USE_PROFILES & USE_PROFILES_BASE
648    IObject *thiz = (IObject *) self;
649    object_lock_exclusive(thiz);
650    thiz->mPriority = priority;
651    thiz->mPreemptable = SL_BOOLEAN_FALSE != preemptable; // normalize
652    object_unlock_exclusive(thiz);
653    result = SL_RESULT_SUCCESS;
654#else
655    result = SL_RESULT_FEATURE_UNSUPPORTED;
656#endif
657
658    SL_LEAVE_INTERFACE
659}
660
661
662static SLresult IObject_GetPriority(SLObjectItf self, SLint32 *pPriority, SLboolean *pPreemptable)
663{
664    SL_ENTER_INTERFACE
665
666#if USE_PROFILES & USE_PROFILES_BASE
667    if (NULL == pPriority || NULL == pPreemptable) {
668        result = SL_RESULT_PARAMETER_INVALID;
669    } else {
670        IObject *thiz = (IObject *) self;
671        object_lock_shared(thiz);
672        SLint32 priority = thiz->mPriority;
673        SLboolean preemptable = thiz->mPreemptable;
674        object_unlock_shared(thiz);
675        *pPriority = priority;
676        *pPreemptable = preemptable;
677        result = SL_RESULT_SUCCESS;
678    }
679#else
680    result = SL_RESULT_FEATURE_UNSUPPORTED;
681#endif
682
683    SL_LEAVE_INTERFACE
684}
685
686
687static SLresult IObject_SetLossOfControlInterfaces(SLObjectItf self,
688    SLint16 numInterfaces, SLInterfaceID *pInterfaceIDs, SLboolean enabled)
689{
690    SL_ENTER_INTERFACE
691
692#if USE_PROFILES & USE_PROFILES_BASE
693    result = SL_RESULT_SUCCESS;
694    if (0 < numInterfaces) {
695        SLuint32 i;
696        if (NULL == pInterfaceIDs) {
697            result = SL_RESULT_PARAMETER_INVALID;
698        } else {
699            IObject *thiz = (IObject *) self;
700            const ClassTable *clazz = thiz->mClass;
701            unsigned lossOfControlMask = 0;
702            // The cast is due to a typo in the spec, bug 6482
703            for (i = 0; i < (SLuint32) numInterfaces; ++i) {
704                SLInterfaceID iid = pInterfaceIDs[i];
705                if (NULL == iid) {
706                    result = SL_RESULT_PARAMETER_INVALID;
707                    goto out;
708                }
709                int MPH, index;
710                // We ignore without error any invalid MPH or index, but spec is unclear
711                if ((0 <= (MPH = IID_to_MPH(iid))) &&
712                        // no need to check for an initialization hook
713                        // (NULL == MPH_init_table[MPH].mInit) ||
714                        (0 <= (index = clazz->mMPH_to_index[MPH]))) {
715                    lossOfControlMask |= (1 << index);
716                }
717            }
718            object_lock_exclusive(thiz);
719            if (enabled) {
720                thiz->mLossOfControlMask |= lossOfControlMask;
721            } else {
722                thiz->mLossOfControlMask &= ~lossOfControlMask;
723            }
724            object_unlock_exclusive(thiz);
725        }
726    }
727out:
728#else
729    result = SL_RESULT_FEATURE_UNSUPPORTED;
730#endif
731
732    SL_LEAVE_INTERFACE
733}
734
735
736static const struct SLObjectItf_ IObject_Itf = {
737    IObject_Realize,
738    IObject_Resume,
739    IObject_GetState,
740    IObject_GetInterface,
741    IObject_RegisterCallback,
742    IObject_AbortAsyncOperation,
743    IObject_Destroy,
744    IObject_SetPriority,
745    IObject_GetPriority,
746    IObject_SetLossOfControlInterfaces
747};
748
749
750/** \brief This must be the first initializer called for an object */
751
752void IObject_init(void *self)
753{
754    IObject *thiz = (IObject *) self;
755    thiz->mItf = &IObject_Itf;
756    // initialized in construct:
757    // mClass
758    // mInstanceID
759    // mLossOfControlMask
760    // mEngine
761    // mInterfaceStates
762    thiz->mState = SL_OBJECT_STATE_UNREALIZED;
763    thiz->mGottenMask = 1;  // IObject
764    thiz->mAttributesMask = 0;
765    thiz->mCallback = NULL;
766    thiz->mContext = NULL;
767#if USE_PROFILES & USE_PROFILES_BASE
768    thiz->mPriority = SL_PRIORITY_NORMAL;
769    thiz->mPreemptable = SL_BOOLEAN_FALSE;
770#endif
771    thiz->mStrongRefCount = 0;
772    int ok;
773    ok = pthread_mutex_init(&thiz->mMutex, (const pthread_mutexattr_t *) NULL);
774    assert(0 == ok);
775#ifdef USE_DEBUG
776    memset(&thiz->mOwner, 0, sizeof(pthread_t));
777    thiz->mFile = NULL;
778    thiz->mLine = 0;
779#endif
780    ok = pthread_cond_init(&thiz->mCond, (const pthread_condattr_t *) NULL);
781    assert(0 == ok);
782}
783
784
785/** \brief This must be the last deinitializer called for an object */
786
787void IObject_deinit(void *self)
788{
789    IObject *thiz = (IObject *) self;
790#ifdef USE_DEBUG
791    assert(pthread_equal(pthread_self(), thiz->mOwner));
792#endif
793    int ok;
794    ok = pthread_cond_destroy(&thiz->mCond);
795    assert(0 == ok);
796    // equivalent to object_unlock_exclusive, but without the rigmarole
797    ok = pthread_mutex_unlock(&thiz->mMutex);
798    assert(0 == ok);
799    ok = pthread_mutex_destroy(&thiz->mMutex);
800    assert(0 == ok);
801    // redundant: thiz->mState = SL_OBJECT_STATE_UNREALIZED;
802}
803
804
805/** \brief Publish a new object after it is fully initialized.
806 *  Publishing will expose the object to sync thread and debugger,
807 *  and make it safe to return the SLObjectItf to the application.
808 */
809
810void IObject_Publish(IObject *thiz)
811{
812    IEngine *thisEngine = &thiz->mEngine->mEngine;
813    interface_lock_exclusive(thisEngine);
814    // construct earlier reserved a pending slot, but did not choose the actual slot number
815    unsigned availMask = ~thisEngine->mInstanceMask;
816    assert(availMask);
817    unsigned i = ctz(availMask);
818    assert(MAX_INSTANCE > i);
819    assert(NULL == thisEngine->mInstances[i]);
820    thisEngine->mInstances[i] = thiz;
821    thisEngine->mInstanceMask |= 1 << i;
822    // avoid zero as a valid instance ID
823    thiz->mInstanceID = i + 1;
824    interface_unlock_exclusive(thisEngine);
825}
826