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