IObject.c revision 7a79f519d89eb0e1a5b3f4005484b16d6854d7e2
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
84static SLresult IObject_Realize(SLObjectItf self, SLboolean async)
85{
86    SL_ENTER_INTERFACE
87
88    IObject *this = (IObject *) self;
89    SLuint8 state;
90    const ClassTable *class__ = this->mClass;
91    object_lock_exclusive(this);
92    state = this->mState;
93    // Reject redundant calls to Realize
94    if (SL_OBJECT_STATE_UNREALIZED != state) {
95        object_unlock_exclusive(this);
96        result = SL_RESULT_PRECONDITIONS_VIOLATED;
97    } else {
98        // Asynchronous: mark operation pending and cancellable
99        if (async && (SL_OBJECTID_ENGINE != class__->mObjectID)) {
100            state = SL_OBJECT_STATE_REALIZING_1;
101        // Synchronous: mark operation pending and non-cancellable
102        } else {
103            state = SL_OBJECT_STATE_REALIZING_2;
104        }
105        this->mState = state;
106        object_unlock_exclusive(this);
107        switch (state) {
108        case SL_OBJECT_STATE_REALIZING_1: // asynchronous on non-Engine
109            assert(async);
110            result = ThreadPool_add(&this->mEngine->mThreadPool, HandleRealize, this, 0);
111            if (SL_RESULT_SUCCESS != result) {
112                // Engine was destroyed during realize, or insufficient memory
113                object_lock_exclusive(this);
114                this->mState = SL_OBJECT_STATE_UNREALIZED;
115                object_unlock_exclusive(this);
116            }
117            break;
118        case SL_OBJECT_STATE_REALIZING_2: // synchronous, or asynchronous on Engine
119            {
120            AsyncHook realize = class__->mRealize;
121            // Note that the mutex is unlocked during the realize hook
122            result = (NULL != realize) ? (*realize)(this, async) : SL_RESULT_SUCCESS;
123            object_lock_exclusive(this);
124            assert(SL_OBJECT_STATE_REALIZING_2 == this->mState);
125            state = (SL_RESULT_SUCCESS == result) ? SL_OBJECT_STATE_REALIZED :
126                SL_OBJECT_STATE_UNREALIZED;
127            this->mState = state;
128            slObjectCallback callback = this->mCallback;
129            void *context = this->mContext;
130            object_unlock_exclusive(this);
131            // asynchronous Realize on an Engine is actually done synchronously, but still has
132            // callback because there is no thread pool yet to do it asynchronously.
133            if (async && (NULL != callback))
134                (*callback)(&this->mItf, context, SL_OBJECT_EVENT_ASYNC_TERMINATION, result, state,
135                    NULL);
136            }
137            break;
138        default:                          // impossible
139            assert(SL_BOOLEAN_FALSE);
140            break;
141        }
142    }
143
144    SL_LEAVE_INTERFACE
145}
146
147
148// Called by a worker thread to handle an asynchronous Object.Resume.
149// Parameter self is the Object.
150
151static void HandleResume(void *self, int unused)
152{
153
154    // valid input parameters
155    IObject *this = (IObject *) self;
156    assert(NULL != this);
157    const ClassTable *class__ = this->mClass;
158    assert(NULL != class__);
159    AsyncHook resume = class__->mResume;
160    SLresult result;
161    SLuint8 state;
162
163    // check object state
164    object_lock_exclusive(this);
165    state = this->mState;
166    switch (state) {
167
168    case SL_OBJECT_STATE_RESUMING_1:    // normal case
169        if (NULL != resume) {
170            this->mState = SL_OBJECT_STATE_RESUMING_2;
171            object_unlock_exclusive(this);
172            // Note that the mutex is unlocked during the resume hook
173            result = (*resume)(this, SL_BOOLEAN_TRUE);
174            object_lock_exclusive(this);
175            assert(SL_OBJECT_STATE_RESUMING_2 == this->mState);
176            state = SL_RESULT_SUCCESS == result ? SL_OBJECT_STATE_REALIZED :
177                SL_OBJECT_STATE_SUSPENDED;
178        } else {
179            result = SL_RESULT_SUCCESS;
180            state = SL_OBJECT_STATE_REALIZED;
181        }
182        break;
183
184    case SL_OBJECT_STATE_RESUMING_1A:   // operation was aborted while on work queue
185        result = SL_RESULT_OPERATION_ABORTED;
186        state = SL_OBJECT_STATE_SUSPENDED;
187        break;
188
189    default:                            // impossible
190        assert(SL_BOOLEAN_FALSE);
191        result = SL_RESULT_INTERNAL_ERROR;
192        break;
193
194    }
195
196    // mutex is unlocked, update state
197    this->mState = state;
198
199    // Make a copy of these, so we can call the callback with mutex unlocked
200    slObjectCallback callback = this->mCallback;
201    void *context = this->mContext;
202    object_unlock_exclusive(this);
203
204    // Note that the mutex is unlocked during the callback
205    if (NULL != callback)
206        (*callback)(&this->mItf, context, SL_OBJECT_EVENT_ASYNC_TERMINATION, result, state, NULL);
207}
208
209
210static SLresult IObject_Resume(SLObjectItf self, SLboolean async)
211{
212    SL_ENTER_INTERFACE
213
214    IObject *this = (IObject *) self;
215    const ClassTable *class__ = this->mClass;
216    SLuint8 state;
217    object_lock_exclusive(this);
218    state = this->mState;
219    // Reject redundant calls to Resume
220    if (SL_OBJECT_STATE_SUSPENDED != state) {
221        object_unlock_exclusive(this);
222        result = SL_RESULT_PRECONDITIONS_VIOLATED;
223    } else {
224        // Asynchronous: mark operation pending and cancellable
225        if (async) {
226            state = SL_OBJECT_STATE_RESUMING_1;
227        // Synchronous: mark operatio pending and non-cancellable
228        } else {
229            state = SL_OBJECT_STATE_RESUMING_2;
230        }
231        this->mState = state;
232        object_unlock_exclusive(this);
233        switch (state) {
234        case SL_OBJECT_STATE_RESUMING_1: // asynchronous
235            assert(async);
236            result = ThreadPool_add(&this->mEngine->mThreadPool, HandleResume, this, 0);
237            if (SL_RESULT_SUCCESS != result) {
238                // Engine was destroyed during resume, or insufficient memory
239                object_lock_exclusive(this);
240                this->mState = SL_OBJECT_STATE_SUSPENDED;
241                object_unlock_exclusive(this);
242            }
243            break;
244        case SL_OBJECT_STATE_RESUMING_2: // synchronous
245            {
246            AsyncHook resume = class__->mResume;
247            // Note that the mutex is unlocked during the resume hook
248            result = (NULL != resume) ? (*resume)(this, SL_BOOLEAN_FALSE) : SL_RESULT_SUCCESS;
249            object_lock_exclusive(this);
250            assert(SL_OBJECT_STATE_RESUMING_2 == this->mState);
251            this->mState = (SL_RESULT_SUCCESS == result) ? SL_OBJECT_STATE_REALIZED :
252                SL_OBJECT_STATE_SUSPENDED;
253            object_unlock_exclusive(this);
254            }
255            break;
256        default:                        // impossible
257            assert(SL_BOOLEAN_FALSE);
258            break;
259        }
260    }
261
262    SL_LEAVE_INTERFACE
263}
264
265
266static SLresult IObject_GetState(SLObjectItf self, SLuint32 *pState)
267{
268    SL_ENTER_INTERFACE
269
270    if (NULL == pState) {
271        result = SL_RESULT_PARAMETER_INVALID;
272    } else {
273        IObject *this = (IObject *) self;
274        // Note that the state is immediately obsolete, so a peek lock is safe
275        object_lock_peek(this);
276        SLuint8 state = this->mState;
277        object_unlock_peek(this);
278        // Re-map the realizing, resuming, and suspending states
279        switch (state) {
280        case SL_OBJECT_STATE_REALIZING_1:
281        case SL_OBJECT_STATE_REALIZING_1A:
282        case SL_OBJECT_STATE_REALIZING_2:
283            state = SL_OBJECT_STATE_UNREALIZED;
284            break;
285        case SL_OBJECT_STATE_RESUMING_1:
286        case SL_OBJECT_STATE_RESUMING_1A:
287        case SL_OBJECT_STATE_RESUMING_2:
288        case SL_OBJECT_STATE_SUSPENDING:
289            state = SL_OBJECT_STATE_SUSPENDED;
290            break;
291        case SL_OBJECT_STATE_UNREALIZED:
292        case SL_OBJECT_STATE_REALIZED:
293        case SL_OBJECT_STATE_SUSPENDED:
294            // These are the "official" object states, return them as is
295            break;
296        default:
297            assert(SL_BOOLEAN_FALSE);
298            break;
299        }
300        *pState = state;
301        result = SL_RESULT_SUCCESS;
302    }
303
304    SL_LEAVE_INTERFACE
305}
306
307
308static SLresult IObject_GetInterface(SLObjectItf self, const SLInterfaceID iid, void *pInterface)
309{
310    SL_ENTER_INTERFACE
311
312    if (NULL == pInterface) {
313        result = SL_RESULT_PARAMETER_INVALID;
314    } else {
315        void *interface = NULL;
316        if (NULL == iid) {
317            result = SL_RESULT_PARAMETER_INVALID;
318        } else {
319            IObject *this = (IObject *) self;
320            const ClassTable *class__ = this->mClass;
321            int MPH, index;
322            if ((0 > (MPH = IID_to_MPH(iid))) || (0 > (index = class__->mMPH_to_index[MPH]))) {
323                result = SL_RESULT_FEATURE_UNSUPPORTED;
324            } else {
325                unsigned mask = 1 << index;
326                object_lock_exclusive(this);
327                // Can't get interface on a suspended/suspending/resuming object
328                if (SL_OBJECT_STATE_REALIZED != this->mState) {
329                    result = SL_RESULT_PRECONDITIONS_VIOLATED;
330                } else {
331                    switch (this->mInterfaceStates[index]) {
332                    case INTERFACE_EXPOSED:
333                    case INTERFACE_ADDED:
334                        // Note that interface has been gotten,
335                        // for debugger and to detect incorrect use of interfaces
336                        this->mGottenMask |= mask;
337                        interface = (char *) this + class__->mInterfaces[index].mOffset;
338                        result = SL_RESULT_SUCCESS;
339                        break;
340                    // Can't get interface if uninitialized/suspend(ed,ing)/resuming/adding/removing
341                    default:
342                        result = SL_RESULT_FEATURE_UNSUPPORTED;
343                        break;
344                    }
345                }
346                object_unlock_exclusive(this);
347            }
348        }
349        *(void **)pInterface = interface;
350    }
351
352    SL_LEAVE_INTERFACE
353}
354
355
356static SLresult IObject_RegisterCallback(SLObjectItf self,
357    slObjectCallback callback, void *pContext)
358{
359    SL_ENTER_INTERFACE
360
361    IObject *this = (IObject *) self;
362    object_lock_exclusive(this);
363    this->mCallback = callback;
364    this->mContext = pContext;
365    object_unlock_exclusive(this);
366    result = SL_RESULT_SUCCESS;
367
368    SL_LEAVE_INTERFACE
369}
370
371
372// internal common code for Abort and Destroy
373
374static void Abort_internal(IObject *this, SLboolean shutdown)
375{
376    const ClassTable *class__ = this->mClass;
377    object_lock_exclusive(this);
378    // Abort asynchronous operations on the object
379    switch (this->mState) {
380    case SL_OBJECT_STATE_REALIZING_1:   // Realize
381        this->mState = SL_OBJECT_STATE_REALIZING_1A;
382        break;
383    case SL_OBJECT_STATE_RESUMING_1:    // Resume
384        this->mState = SL_OBJECT_STATE_RESUMING_1A;
385        break;
386    default:
387        break;
388    }
389    // Abort asynchronous operations on interfaces
390    SLuint8 *interfaceStateP = this->mInterfaceStates;
391    unsigned index;
392    for (index = 0; index < class__->mInterfaceCount; ++index, ++interfaceStateP) {
393        switch (*interfaceStateP) {
394        case INTERFACE_ADDING_1:    // AddInterface
395            *interfaceStateP = INTERFACE_ADDING_1A;
396            break;
397        case INTERFACE_RESUMING_1:  // ResumeInterface
398            *interfaceStateP = INTERFACE_RESUMING_1A;
399            break;
400        default:
401            break;
402        }
403    }
404    object_unlock_exclusive(this);
405}
406
407
408static void IObject_AbortAsyncOperation(SLObjectItf self)
409{
410    SL_ENTER_INTERFACE_VOID
411
412    IObject *this = (IObject *) self;
413    Abort_internal(this, SL_BOOLEAN_FALSE);
414
415    SL_LEAVE_INTERFACE_VOID
416}
417
418
419static void IObject_Destroy(SLObjectItf self)
420{
421    SL_ENTER_INTERFACE_VOID
422
423    IObject *this = (IObject *) self;
424    Abort_internal(this, SL_BOOLEAN_TRUE);
425    const ClassTable *class__ = this->mClass;
426    VoidHook destroy = class__->mDestroy;
427    const struct iid_vtable *x = class__->mInterfaces;
428    // const, no lock needed
429    IEngine *thisEngine = this->mEngine;
430    unsigned i = this->mInstanceID;
431    assert(0 < i && i <= MAX_INSTANCE);
432    --i;
433    // remove object from exposure to sync thread and debugger
434    interface_lock_exclusive(thisEngine);
435    assert(0 < thisEngine->mInstanceCount);
436    --thisEngine->mInstanceCount;
437    assert(0 != thisEngine->mInstanceMask);
438    thisEngine->mInstanceMask &= ~(1 << i);
439    assert(thisEngine->mInstances[i] == this);
440    thisEngine->mInstances[i] = NULL;
441#ifdef USE_SDL
442    if ((SL_OBJECTID_OUTPUTMIX == class__->mObjectID) &&
443        (((COutputMix *) this == thisEngine->mOutputMix))) {
444        SDL_PauseAudio(1);
445        thisEngine->mOutputMix = NULL;
446        // Note we don't attempt to connect another output mix to SDL
447    }
448#endif
449    interface_unlock_exclusive(thisEngine);
450    object_lock_exclusive(this);
451    if (NULL != destroy)
452        (*destroy)(this);
453    // Call the deinitializer for each currently exposed interface,
454    // whether it is implicit, explicit, optional, or dynamically added.
455    unsigned incorrect = 0;
456    SLuint8 *interfaceStateP = this->mInterfaceStates;
457    unsigned index;
458    for (index = 0; index < class__->mInterfaceCount; ++index, ++x, ++interfaceStateP) {
459        SLuint32 state = *interfaceStateP;
460        switch (state) {
461        case INTERFACE_UNINITIALIZED:
462            break;
463        case INTERFACE_EXPOSED:     // quiescent states
464        case INTERFACE_ADDED:
465        case INTERFACE_SUSPENDED:
466            {
467            VoidHook deinit = MPH_init_table[x->mMPH].mDeinit;
468            if (NULL != deinit)
469                (*deinit)((char *) this + x->mOffset);
470            }
471            break;
472        case INTERFACE_ADDING_1:    // active states indicate incorrect use of API
473        case INTERFACE_ADDING_1A:
474        case INTERFACE_ADDING_2:
475        case INTERFACE_RESUMING_1:
476        case INTERFACE_RESUMING_1A:
477        case INTERFACE_RESUMING_2:
478        case INTERFACE_REMOVING:
479        case INTERFACE_SUSPENDING:
480            ++incorrect;
481            break;
482        default:
483            assert(SL_BOOLEAN_FALSE);
484            break;
485        }
486    }
487    // redundant: this->mState = SL_OBJECT_STATE_UNREALIZED;
488    object_unlock_exclusive(this);
489#ifndef NDEBUG
490    memset(this, 0x55, class__->mSize);
491#endif
492    free(this);
493    // one or more interfaces was actively changing at time of Destroy
494    assert(incorrect == 0);
495
496    SL_LEAVE_INTERFACE_VOID
497}
498
499
500static SLresult IObject_SetPriority(SLObjectItf self, SLint32 priority, SLboolean preemptable)
501{
502    SL_ENTER_INTERFACE
503
504#ifdef USE_CONFORMANCE
505    IObject *this = (IObject *) self;
506    object_lock_exclusive(this);
507    this->mPriority = priority;
508    this->mPreemptable = SL_BOOLEAN_FALSE != preemptable; // normalize
509    object_unlock_exclusive(this);
510    result = SL_RESULT_SUCCESS;
511#else
512    result = SL_RESULT_FEATURE_UNSUPPORTED;
513#endif
514
515    SL_LEAVE_INTERFACE
516}
517
518
519static SLresult IObject_GetPriority(SLObjectItf self, SLint32 *pPriority, SLboolean *pPreemptable)
520{
521    SL_ENTER_INTERFACE
522
523#ifdef USE_CONFORMANCE
524    if (NULL == pPriority || NULL == pPreemptable) {
525        result = SL_RESULT_PARAMETER_INVALID;
526    } else {
527        IObject *this = (IObject *) self;
528        object_lock_shared(this);
529        SLint32 priority = this->mPriority;
530        SLboolean preemptable = this->mPreemptable;
531        object_unlock_shared(this);
532        *pPriority = priority;
533        *pPreemptable = preemptable;
534        result = SL_RESULT_SUCCESS;
535    }
536#else
537    result = SL_RESULT_FEATURE_UNSUPPORTED;
538#endif
539
540    SL_LEAVE_INTERFACE
541}
542
543
544static SLresult IObject_SetLossOfControlInterfaces(SLObjectItf self,
545    SLint16 numInterfaces, SLInterfaceID *pInterfaceIDs, SLboolean enabled)
546{
547    SL_ENTER_INTERFACE
548
549#ifdef USE_CONFORMANCE
550    result = SL_RESULT_SUCCESS;
551    if (0 < numInterfaces) {
552        SLuint32 i;
553        if (NULL == pInterfaceIDs) {
554            result = SL_RESULT_PARAMETER_INVALID;
555        } else {
556            IObject *this = (IObject *) self;
557            const ClassTable *class__ = this->mClass;
558            unsigned lossOfControlMask = 0;
559            // The cast is due to a typo in the spec, bug 6482
560            for (i = 0; i < (SLuint32) numInterfaces; ++i) {
561                SLInterfaceID iid = pInterfaceIDs[i];
562                if (NULL == iid) {
563                    result = SL_RESULT_PARAMETER_INVALID;
564                    goto out;
565                }
566                int MPH, index;
567                // We ignore without error any invalid MPH or index, but spec is unclear
568                if ((0 <= (MPH = IID_to_MPH(iid))) && (0 <= (index = class__->mMPH_to_index[MPH])))
569                    lossOfControlMask |= (1 << index);
570            }
571            object_lock_exclusive(this);
572            if (enabled)
573                this->mLossOfControlMask |= lossOfControlMask;
574            else
575                this->mLossOfControlMask &= ~lossOfControlMask;
576            object_unlock_exclusive(this);
577        }
578    }
579out:
580#else
581    result = SL_RESULT_FEATURE_UNSUPPORTED;
582#endif
583
584    SL_LEAVE_INTERFACE
585}
586
587
588static const struct SLObjectItf_ IObject_Itf = {
589    IObject_Realize,
590    IObject_Resume,
591    IObject_GetState,
592    IObject_GetInterface,
593    IObject_RegisterCallback,
594    IObject_AbortAsyncOperation,
595    IObject_Destroy,
596    IObject_SetPriority,
597    IObject_GetPriority,
598    IObject_SetLossOfControlInterfaces,
599};
600
601void IObject_init(void *self)
602{
603    IObject *this = (IObject *) self;
604    this->mItf = &IObject_Itf;
605    // initialized in construct:
606    // mClass
607    // mInstanceID
608    // mLossOfControlMask
609    // mEngine
610    // mInstanceStates
611    this->mState = SL_OBJECT_STATE_UNREALIZED;
612    this->mGottenMask = 1;  // IObject
613    this->mAttributesMask = 0;
614    this->mCallback = NULL;
615    this->mContext = NULL;
616#ifdef USE_CONFORMANCE
617    this->mPriority = SL_PRIORITY_NORMAL;
618    this->mPreemptable = SL_BOOLEAN_FALSE;
619#endif
620    int ok;
621    ok = pthread_mutex_init(&this->mMutex, (const pthread_mutexattr_t *) NULL);
622    assert(0 == ok);
623    ok = pthread_cond_init(&this->mCond, (const pthread_condattr_t *) NULL);
624    assert(0 == ok);
625}
626