static SM_RESULT sm_close_begin_internal()

in common/src/sm.c [208:294]


static SM_RESULT sm_close_begin_internal(SM_HANDLE sm, ON_SM_CLOSING_COMPLETE_CALLBACK callback, void* callback_context, ON_SM_CLOSING_WHILE_OPENING_CALLBACK close_while_opening_callback, void* close_while_opening_context)
{
    SM_RESULT result;

    int32_t state;
    /*Codes_SRS_SM_02_045: [ sm_close_begin_internal shall set SM_CLOSE_BIT to 1. ]*/
    if (((state=interlocked_or(&sm->state, SM_CLOSE_BIT)) & SM_CLOSE_BIT) == SM_CLOSE_BIT)
    {
        /*Codes_SRS_SM_02_046: [ If SM_CLOSE_BIT was already 1 then sm_close_begin_internal shall return SM_EXEC_REFUSED. ]*/
        LogError("sm name=%s. another thread is performing close (state=%" PRI_SM_STATE ")", sm->name, SM_STATE_VALUE(state));
        result = SM_EXEC_REFUSED;
    }
    else
    {
        bool initial_iteration = true;
        do
        {
            state = interlocked_add(&sm->state, 0);

            /*Codes_SRS_SM_02_047: [ If the state is SM_OPENED then sm_close_begin_internal shall switch it to SM_OPENED_DRAINING_TO_CLOSE. ]*/
            if ((state & SM_STATE_MASK) == SM_OPENED)
            {
                if (interlocked_compare_exchange(&sm->state, state - SM_OPENED + SM_OPENED_DRAINING_TO_CLOSE + SM_STATE_INCREMENT, state) != state)
                {
                    /*go and retry*/
                }
                else
                {
                    if (callback == NULL)
                    {
                        /* Codes_SRS_SM_28_007: [ callback shall be allowed to be NULL. ] */
                    }
                    else
                    {
                        /* Codes_SRS_SM_28_008: [ If callback is not NULL, sm_close_begin_internal shall invoke callback function with callback_context as argument. ] */
                        callback(callback_context);
                    }

                    /*Codes_SRS_SM_02_048: [ sm_close_begin_internal shall wait for n to reach 0. ]*/
                    if (InterlockedHL_WaitForValue(&sm->non_barrier_call_count, 0, UINT32_MAX) != INTERLOCKED_HL_OK)
                    {
                        /*Codes_SRS_SM_02_071: [ If there are any failures then sm_close_begin_internal shall fail and return SM_ERROR. ]*/
                        LogError("sm name=%s. failure in InterlockedHL_WaitForValue(&sm->non_barrier_call_count=%p, 0, UINT32_MAX), state was %" PRI_SM_STATE "", sm->name, &sm->non_barrier_call_count, SM_STATE_VALUE(state));
                        (void)interlocked_add(&sm->state, -SM_OPENED_DRAINING_TO_CLOSE + SM_OPENED + SM_STATE_INCREMENT); /*undo state to SM_OPENED...*/
                        result = SM_ERROR;
                        break;
                    }

                    /*Codes_SRS_SM_02_049: [ sm_close_begin_internal shall switch the state to SM_CLOSING and return SM_EXEC_GRANTED. ]*/
                    (void)interlocked_add(&sm->state, -SM_OPENED_DRAINING_TO_CLOSE + SM_CLOSING + SM_STATE_INCREMENT);
                    result = SM_EXEC_GRANTED;
                    break;
                }
            }
            else if (
                /*Codes_SRS_SM_02_050: [ If the state is SM_OPENED_BARRIER then sm_close_begin_internal shall re-evaluate the state. ]*/
                ((state & SM_STATE_MASK) == SM_OPENED_BARRIER) ||
                /*Codes_SRS_SM_02_051: [ If the state is SM_OPENED_DRAINING_TO_BARRIER then sm_close_begin_internal shall re-evaluate the state. ]*/
                ((state & SM_STATE_MASK) == SM_OPENED_DRAINING_TO_BARRIER)
                )
            {
                ThreadAPI_Sleep(1);
            }
            else if (
                // Codes_SRS_SM_11_002: [ If the state is SM_OPENING, the close_while_opening_callback is non-NULL and this is the first evaluation of the close then ... ]
                ((state & SM_STATE_MASK) == SM_OPENING) && 
                (close_while_opening_callback != NULL) &&
                (initial_iteration)
                )
            {
                // Codes_SRS_SM_11_003: [ ... sm_close_begin_internal shall call the close_while_opening_callback and then re-evaluate the state. ]
                close_while_opening_callback(close_while_opening_context);
                initial_iteration = false;
            }
            else
            {
                /*Codes_SRS_SM_02_052: [ If the state is any other value then sm_close_begin_internal shall return SM_EXEC_REFUSED. ]*/
                result = SM_EXEC_REFUSED;
                break;
            }
        } while (1);

        /*Codes_SRS_SM_02_053: [ sm_close_begin_internal shall set SM_CLOSE_BIT to 0. ]*/
        (void)interlocked_and(&sm->state, ~(uint32_t)SM_CLOSE_BIT);
    }
    return result;
}