static SLresult I3DGrouping_Set3DGroup(SL3DGroupingItf self, SLObjectItf group)
{
    SL_ENTER_INTERFACE

    // validate input parameters
    C3DGroup *newGroup = (C3DGroup *) group;
    result = SL_RESULT_SUCCESS;
    if (NULL != newGroup) {
        // check that new group has the correct object ID and is realized, and acquire a strong
        // reference to it. FYI note that a deadlock will occur if application incorrectly
        // specifies group as this audio player
        result = AcquireStrongRef(&newGroup->mObject, SL_OBJECTID_3DGROUP);
        // the new group is left unlocked, but it will be locked again below
    }
    if (SL_RESULT_SUCCESS == result) {
        I3DGrouping *thiz = (I3DGrouping *) self;
        IObject *thisObject = InterfaceToIObject(thiz);
        unsigned id = thisObject->mInstanceID;
        assert(0 != id);        // player object must be published by this point
        --id;
        assert(MAX_INSTANCE > id);
        unsigned mask = 1 << id;
        interface_lock_exclusive(thiz);
        C3DGroup *oldGroup = thiz->mGroup;
        if (newGroup != oldGroup) {
            // remove this object from the old group's set of objects
            if (NULL != oldGroup) {
                IObject *oldGroupObject = &oldGroup->mObject;
                // note that we already have a strong reference to the old group
                object_lock_exclusive(oldGroupObject);
                assert(oldGroup->mMemberMask & mask);
                oldGroup->mMemberMask &= ~mask;
                ReleaseStrongRefAndUnlockExclusive(oldGroupObject);
            }
            // add this object to the new group's set of objects
            if (NULL != newGroup) {
                IObject *newGroupObject = &newGroup->mObject;
                // we already have a strong reference to the new group, but we need to re-lock it
                // so that we always lock objects in the same nesting order to prevent a deadlock
                object_lock_exclusive(newGroupObject);
                assert(!(newGroup->mMemberMask & mask));
                newGroup->mMemberMask |= mask;
                object_unlock_exclusive(newGroupObject);
            }
            thiz->mGroup = newGroup;
        }
        interface_unlock_exclusive(thiz);
    }

    SL_LEAVE_INTERFACE
}
static void HandleAdd(void *self, int MPH)
{

    // validate input parameters
    IDynamicInterfaceManagement *this = (IDynamicInterfaceManagement *) self;
    assert(NULL != this);
    IObject *thisObject = InterfaceToIObject(this);
    assert(NULL != thisObject);
    assert(0 <= MPH && MPH < MPH_MAX);
    const ClassTable *class__ = thisObject->mClass;
    assert(NULL != class__);
    int index = class__->mMPH_to_index[MPH];
    assert(0 <= index && index < (int) class__->mInterfaceCount);
    SLuint8 *interfaceStateP = &thisObject->mInterfaceStates[index];
    SLresult result;

    // check interface state
    object_lock_exclusive(thisObject);
    SLuint8 state = *interfaceStateP;
    switch (state) {

    case INTERFACE_ADDING_1:    // normal case
        {
        // change state to indicate we are now adding the interface
        *interfaceStateP = INTERFACE_ADDING_2;
        object_unlock_exclusive(thisObject);

        // this section runs with mutex unlocked
        const struct iid_vtable *x = &class__->mInterfaces[index];
        size_t offset = x->mOffset;
        void *thisItf = (char *) thisObject + offset;
        BoolHook expose = MPH_init_table[MPH].mExpose;
        // call the optional expose hook
        if ((NULL == expose) || (*expose)(thisItf)) {
            result = SL_RESULT_SUCCESS;
        } else {
            result = SL_RESULT_FEATURE_UNSUPPORTED;
        }

        // re-lock mutex to update state
        object_lock_exclusive(thisObject);
        assert(INTERFACE_ADDING_2 == *interfaceStateP);
        if (SL_RESULT_SUCCESS == result) {
            ((size_t *) thisItf)[0] ^= ~0;
            state = INTERFACE_ADDED;
        } else {
            state = INTERFACE_INITIALIZED;
        }
        }
        break;

    case INTERFACE_ADDING_1A:   // operation was aborted while on work queue
        result = SL_RESULT_OPERATION_ABORTED;
        state = INTERFACE_INITIALIZED;
        break;

    default:                    // impossible
        assert(SL_BOOLEAN_FALSE);
        result = SL_RESULT_INTERNAL_ERROR;
        break;

    }

    // mutex is locked, update state
    *interfaceStateP = state;

    // Make a copy of these, so we can call the callback with mutex unlocked
    slDynamicInterfaceManagementCallback callback = this->mCallback;
    void *context = this->mContext;
    object_unlock_exclusive(thisObject);

    // Note that the mutex is unlocked during the callback
    if (NULL != callback) {
        const SLInterfaceID iid = &SL_IID_array[MPH]; // equal but not == to the original IID
        (*callback)(&this->mItf, context, SL_DYNAMIC_ITF_EVENT_ASYNC_TERMINATION, result, iid);
    }

}