in inc/c_util/async_retry_wrapper.h [532:642]
static ASYNC_RETRY_WRAPPER_RESULT MU_C2(ASYNC_RETRY_WRAPPER(async_function_name), _internal)(async_handle_type async_handle, THANDLE(THREADPOOL) threadpool, uint32_t timeout_ms ASYNC_RETRY_WRAPPER_ARGS_IN_STATIC_DECLARATION(in_args), return_type* async_function_result) \
{ \
ASYNC_RETRY_WRAPPER_RESULT async_retry_wrapper_result; \
if (async_handle == NULL) \
{ \
/*Codes_SRS_ASYNC_RETRY_WRAPPER_42_003: [ If async_handle is NULL, the asynchronous retry wrapper shall fail and return ASYNC_RETRY_WRAPPER_INVALID_ARGS. ]*/ \
LogError("NULL " MU_TOSTRING(async_handle_type) " async_handle for " MU_TOSTRING(async_function_name)); \
async_retry_wrapper_result = ASYNC_RETRY_WRAPPER_INVALID_ARGS; \
} \
else if (async_function_result == NULL) \
{ \
/*Codes_SRS_ASYNC_RETRY_WRAPPER_42_042: [ If async_function_result is NULL, the asynchronous retry wrapper shall fail and return ASYNC_RETRY_WRAPPER_INVALID_ARGS. ]*/ \
LogError("NULL " MU_TOSTRING(return_type) "* async_function_result for " MU_TOSTRING(async_function_name)); \
async_retry_wrapper_result = ASYNC_RETRY_WRAPPER_INVALID_ARGS; \
} \
else if (threadpool == NULL) \
{ \
/*Codes_SRS_ASYNC_RETRY_WRAPPER_42_037: [ If threadpool is NULL, the asynchronous retry wrapper shall fail and return ASYNC_RETRY_WRAPPER_INVALID_ARGS. ]*/ \
LogError("NULL THANDLE(THREADPOOL) threadpool for " MU_TOSTRING(async_function_name)); \
async_retry_wrapper_result = ASYNC_RETRY_WRAPPER_INVALID_ARGS; \
} \
ASYNC_RETRY_WRAPPER_CHECK_ARG_PTRS(in_args) \
else \
{ \
/*Codes_SRS_ASYNC_RETRY_WRAPPER_42_005: [ The asynchronous retry wrapper shall allocate a context for the asynchronous call. ]*/ \
ASYNC_RETRY_WRAPPER_CONTEXT(async_function_name)* retry_context = malloc(sizeof(ASYNC_RETRY_WRAPPER_CONTEXT(async_function_name))); \
if (retry_context == NULL) \
{ \
LogError("Failed to create context for " MU_TOSTRING(async_function_name)); \
async_retry_wrapper_result = ASYNC_RETRY_WRAPPER_ERROR; \
} \
else \
{ \
if (timeout_ms < UINT32_MAX) \
{ \
/*Codes_SRS_ASYNC_RETRY_WRAPPER_42_048: [ {async_function_name}_async_retry_wrapper_with_timeout shall get the current time by calling timer_global_get_elapsed_ms. ]*/ \
retry_context->start_time = timer_global_get_elapsed_ms(); \
} \
retry_context->timeout_ms = timeout_ms; \
bool error_copying_args = false; \
ASYNC_RETRY_WRAPPER_COPY_ARGS_PROXY(in_args) \
if (error_copying_args) \
{ \
/*Codes_SRS_ASYNC_RETRY_WRAPPER_42_010: [ If there are any failures when copying the input arguments then the asynchronous retry wrapper shall fail and return ASYNC_RETRY_WRAPPER_COPY_ARG_ERROR. ]*/ \
async_retry_wrapper_result = ASYNC_RETRY_WRAPPER_COPY_ARG_ERROR; \
} \
else \
{ \
retry_context->handle = async_handle; \
THANDLE_INITIALIZE(THREADPOOL)(&retry_context->threadpool, threadpool); \
return_type temp_async_function_result; \
retry_context->backoff = 1; \
bool timed_out = false; \
do \
{ \
/*Codes_SRS_ASYNC_RETRY_WRAPPER_42_011: [ The asynchronous retry wrapper shall call the function async_function_name, passing the in_args that have been copied to the retry context with the exception of ARG_CB(...) and ARG_CONTEXT(...) which are instead passed as the generated callback handler and the allocated context. ]*/ \
/*Codes_SRS_ASYNC_RETRY_WRAPPER_42_043: [ The asynchronous retry wrapper shall store the result of async_function_name in async_function_result. ]*/ \
temp_async_function_result = async_function_name(retry_context->handle ASYNC_RETRY_WRAPPER_ARGS_IN_CALL(in_args), ASYNC_RETRY_WRAPPER_CALLBACK(async_function_name), retry_context); \
if (ASYNC_RETRY_WRAPPER_RETRY_CONDITIONS_PROXY(retry_sync_enums)) \
{ \
if (retry_context->timeout_ms < UINT32_MAX) \
{ \
double elapsed_time_ms = timer_global_get_elapsed_ms() - retry_context->start_time; \
if (elapsed_time_ms + retry_context->backoff > retry_context->timeout_ms) \
{ \
/*Codes_SRS_ASYNC_RETRY_WRAPPER_42_049: [ Before each retry of the function, If timeout_ms milliseconds have elapsed then {async_function_name}_async_retry_wrapper_with_timeout shall fail and return ASYNC_RETRY_WRAPPER_TIMEOUT. ]*/ \
timed_out = true; \
LogWarning("Retries for " MU_TOSTRING(async_function_name) " timed out after %lf ms (including %" PRIu32 " ms of backoff) (timeout time was %" PRIu32 " ms)", \
elapsed_time_ms, retry_context->backoff, retry_context->timeout_ms); \
break; \
} \
} \
/*Codes_SRS_ASYNC_RETRY_WRAPPER_42_044: [ Before each retry of the function, the asynchronous retry wrapper shall yield execution by a call to ThreadAPI_Sleep. ]*/ \
ThreadAPI_Sleep(retry_context->backoff); \
retry_context->backoff *= 2; \
if (retry_context->backoff > ASYNC_RETRY_WRAPPER_MAX_BACKOFF_MS) \
{ \
retry_context->backoff = ASYNC_RETRY_WRAPPER_MAX_BACKOFF_MS; \
} \
} \
/*Codes_SRS_ASYNC_RETRY_WRAPPER_42_038: [ While async_function_name returns one of the values from RETRY_ON_SYNC(...), it shall be called again in a loop. ]*/ \
} while (ASYNC_RETRY_WRAPPER_RETRY_CONDITIONS_PROXY(retry_sync_enums)); \
*async_function_result = temp_async_function_result; \
if (timed_out) \
{ \
async_retry_wrapper_result = ASYNC_RETRY_WRAPPER_TIMEOUT; \
} \
else \
{ \
if (temp_async_function_result != expected_return) \
{ \
/*Codes_SRS_ASYNC_RETRY_WRAPPER_42_039: [ If async_function_name returns a value other than expected_return then the asynchronous retry wrapper shall fail and return ASYNC_RETRY_WRAPPER_CALL_ERROR. ]*/ \
LogInfo(MU_TOSTRING(async_function_name) " failed"); \
async_retry_wrapper_result = ASYNC_RETRY_WRAPPER_CALL_ERROR; \
} \
else \
{ \
/*Codes_SRS_ASYNC_RETRY_WRAPPER_42_013: [ On success, the asynchronous retry wrapper shall return ASYNC_RETRY_WRAPPER_OK. ]*/ \
async_retry_wrapper_result = ASYNC_RETRY_WRAPPER_OK; \
goto all_ok; \
} \
} \
THANDLE_ASSIGN(THREADPOOL)(&retry_context->threadpool, NULL); \
} \
ASYNC_RETRY_WRAPPER_FREE_FAILED_PROXY(in_args); \
free(retry_context); \
} \
} \
all_ok: \
return async_retry_wrapper_result; \
} \