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