inc/c_util/tcall_dispatcher_ll.h (227 lines of code) (raw):

// Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. #ifndef TCALL_DISPATCHER_LL_H #define TCALL_DISPATCHER_LL_H #include "c_pal/interlocked.h" #include "c_pal/thandle_ll.h" #include "c_pal/srw_lock_ll.h" #include "c_util/doublylinkedlist.h" #include "umock_c/umock_c_prod.h" /*TCALL_DISPATCHER is backed by a THANDLE build on the structure below*/ #define TCALL_DISPATCHER_STRUCT_TYPE_NAME_TAG(T) MU_C2(TCALL_DISPATCHER_TYPEDEF_NAME(T), _TAG) #define TCALL_DISPATCHER_TYPEDEF_NAME(T) MU_C2(TCALL_DISPATCHER_STRUCT_, T) #define TCALL_DISPATCHER_TARGET_STRUCT_TYPE_NAME_TAG(T) MU_C2(TCALL_DISPATCHER_TARGET_TYPEDEF_NAME(T), _TAG) #define TCALL_DISPATCHER_TARGET_TYPEDEF_NAME(T) MU_C2(TCALL_DISPATCHER_TARGET_STRUCT_, T) #define TCALL_DISPATCHER_TARGET_HANDLE_NAME(T) MU_C2(TCALL_DISPATCHER_TARGET_HANDLE_, T) #define TCALL_DISPATCHER_TARGET_FUNC_TYPE_NAME(T) MU_C2(TCALL_DISPATCHER_TARGET_FUNC_, T) // These macros help expand the arguments in the function signature // This macro pastes the arguments as expected by a regular C function signature #define TCALL_DISPATCHER_ARG_IN_SIGNATURE(arg_type, arg_name) , arg_type arg_name // This macro pastes the arguments as expected by a MOCKABLE_FUNCTION signature (with commas between argument type and argument name) #define TCALL_DISPATCHER_ARG_IN_MOCKABLE_SIGNATURE(arg_type, arg_name) , arg_type, arg_name // This macro pastes only the argument name so that it is used when calling functions #define TCALL_DISPATCHER_ARG_VALUE_IN_CALL(arg_type, arg_name) , arg_name /*TCALL_DISPATCHER_DEFINE_CALL_TYPE(T) introduces the base type that holds the dispatcher typed as T*/ #define TCALL_DISPATCHER_DEFINE_CALL_TYPE(T, ...) \ /*forward define the typedef of the TCALL_DISPATCHER struct so that it can be used for a function pointer definition*/ \ typedef void (*TCALL_DISPATCHER_TARGET_FUNC_TYPE_NAME(T))(void* context MU_FOR_EACH_2(TCALL_DISPATCHER_ARG_IN_SIGNATURE, __VA_ARGS__)); \ typedef struct TCALL_DISPATCHER_TARGET_STRUCT_TYPE_NAME_TAG(T)* TCALL_DISPATCHER_TARGET_HANDLE_NAME(T); \ typedef struct TCALL_DISPATCHER_TARGET_STRUCT_TYPE_NAME_TAG(T) \ { \ DLIST_ENTRY anchor; \ TCALL_DISPATCHER_LL_TARGET_FUNC(T) function_to_call; \ void* call_context; \ } TCALL_DISPATCHER_TARGET_TYPEDEF_NAME(T); \ typedef struct TCALL_DISPATCHER_STRUCT_TYPE_NAME_TAG(T) \ { \ SRW_LOCK_LL lock; \ DLIST_ENTRY call_targets; \ } TCALL_DISPATCHER_TYPEDEF_NAME(T); \ /*TCALL_DISPATCHER is-a THANDLE*/ /*given a type "T" TCALL_DISPATCHER_LL(T) expands to the name of the type. */ #define TCALL_DISPATCHER_LL(T) THANDLE(TCALL_DISPATCHER_TYPEDEF_NAME(T)) /*introduces a target handle for the call dispatcher type*/ #define TCALL_DISPATCHER_LL_TARGET_HANDLE(T) TCALL_DISPATCHER_TARGET_HANDLE_NAME(T) /*introduces the target function type*/ #define TCALL_DISPATCHER_LL_TARGET_FUNC(T) TCALL_DISPATCHER_TARGET_FUNC_TYPE_NAME(T) /*because TCALL_DISPATCHER is a THANDLE, all THANDLE's macro APIs are useable with TCALL_DISPATCHER.*/ /*the below are just shortcuts of THANDLE's public ones*/ #define TCALL_DISPATCHER_LL_INITIALIZE(T) THANDLE_INITIALIZE(TCALL_DISPATCHER_TYPEDEF_NAME(T)) #define TCALL_DISPATCHER_LL_ASSIGN(T) THANDLE_ASSIGN(TCALL_DISPATCHER_TYPEDEF_NAME(T)) #define TCALL_DISPATCHER_LL_MOVE(T) THANDLE_MOVE(TCALL_DISPATCHER_TYPEDEF_NAME(T)) #define TCALL_DISPATCHER_LL_INITIALIZE_MOVE(T) THANDLE_INITIALIZE_MOVE(TCALL_DISPATCHER_TYPEDEF_NAME(T)) /*introduces a new name for a function that returns a TCALL_DISPATCHER_LL(T)*/ #define TCALL_DISPATCHER_LL_CREATE_NAME(C) MU_C2(TCALL_DISPATCHER_LL_CREATE_, C) #define TCALL_DISPATCHER_LL_CREATE(C) TCALL_DISPATCHER_LL_CREATE_NAME(C) /*introduces a new name for the register_target function */ #define TCALL_DISPATCHER_LL_REGISTER_TARGET_NAME(C) MU_C2(TCALL_DISPATCHER_LL_REGISTER_TARGET_, C) #define TCALL_DISPATCHER_LL_REGISTER_TARGET(C) TCALL_DISPATCHER_LL_REGISTER_TARGET_NAME(C) /*introduces a new name for the unregister_target function */ #define TCALL_DISPATCHER_LL_UNREGISTER_TARGET_NAME(C) MU_C2(TCALL_DISPATCHER_LL_UNREGISTER_TARGET_, C) #define TCALL_DISPATCHER_LL_UNREGISTER_TARGET(C) TCALL_DISPATCHER_LL_UNREGISTER_TARGET_NAME(C) /*introduces a new name for the dispatch_call function */ #define TCALL_DISPATCHER_LL_DISPATCH_CALL_NAME(C) MU_C2(TCALL_DISPATCHER_LL_DISPATCH_CALL_, C) #define TCALL_DISPATCHER_LL_DISPATCH_CALL(C) TCALL_DISPATCHER_LL_DISPATCH_CALL_NAME(C) /*introduces a function declaration for tcall_dispatcher_create*/ #define TCALL_DISPATCHER_LL_CREATE_DECLARE(C, T) MOCKABLE_FUNCTION(, TCALL_DISPATCHER_LL(T), TCALL_DISPATCHER_LL_CREATE(C)); /*introduces a function declaration for tcall_dispatcher_register_target*/ #define TCALL_DISPATCHER_LL_REGISTER_TARGET_DECLARE(C, T) MOCKABLE_FUNCTION(, TCALL_DISPATCHER_LL_TARGET_HANDLE(T), TCALL_DISPATCHER_LL_REGISTER_TARGET(C), TCALL_DISPATCHER_LL(T), tcall_dispatcher, TCALL_DISPATCHER_LL_TARGET_FUNC(T), function_to_call, void*, call_context); /*introduces a function declaration for tcall_dispatcher_unregister_target*/ #define TCALL_DISPATCHER_LL_UNREGISTER_TARGET_DECLARE(C, T) MOCKABLE_FUNCTION(, int, TCALL_DISPATCHER_LL_UNREGISTER_TARGET(C), TCALL_DISPATCHER_LL(T), tcall_dispatcher, TCALL_DISPATCHER_LL_TARGET_HANDLE(T), call_target); /*introduces a function declaration for tcall_dispatcher_dispatch_call*/ #define TCALL_DISPATCHER_LL_DISPATCH_CALL_DECLARE(C, T, ...) MOCKABLE_FUNCTION(, int, TCALL_DISPATCHER_LL_DISPATCH_CALL(C), TCALL_DISPATCHER_LL(T), tcall_dispatcher MU_FOR_EACH_2(TCALL_DISPATCHER_ARG_IN_MOCKABLE_SIGNATURE, __VA_ARGS__)); /*introduces a name for the function that free's a TCALL_DISPATCHER when it's ref count got to 0*/ #define TCALL_DISPATCHER_LL_FREE_NAME(C) MU_C2(TCALL_DISPATCHER_LL_FREE_, C) /*introduces a function definition for freeing the allocated resources for a TCALL_DISPATCHER*/ #define TCALL_DISPATCHER_LL_FREE_DEFINE(C, T) \ static void TCALL_DISPATCHER_LL_FREE_NAME(C)(TCALL_DISPATCHER_TYPEDEF_NAME(T)* tcall_dispatcher) \ { \ if (tcall_dispatcher == NULL) \ { \ LogError("invalid arguments " MU_TOSTRING(TCALL_DISPATCHER_TYPEDEF_NAME(T)) "* tcall_dispatcher=%p", \ tcall_dispatcher); \ } \ else \ { \ /* Codes_SRS_TCALL_DISPATCHER_01_026: [ TCALL_DISPATCHER_LL_FREE_{T} shall call srw_lock_ll_deinit to de-initialize the lock. ]*/ \ srw_lock_ll_deinit(&tcall_dispatcher->lock); \ DLIST_ENTRY* current_entry = tcall_dispatcher->call_targets.Flink; \ while (current_entry != &tcall_dispatcher->call_targets) \ { \ /* Codes_SRS_TCALL_DISPATCHER_01_027: [ For each call target in the list, TCALL_DISPATCHER_LL_FREE_{T} shall free the resources associated with the call target. ]*/ \ TCALL_DISPATCHER_TARGET_HANDLE_NAME(T) call_target_handle = CONTAINING_RECORD(current_entry, TCALL_DISPATCHER_TARGET_TYPEDEF_NAME(T), anchor); \ DList_RemoveEntryList(current_entry); \ free(call_target_handle); \ current_entry = tcall_dispatcher->call_targets.Flink; \ } \ } \ } \ /*introduces a function definition for tcall_dispatcher_create*/ #define TCALL_DISPATCHER_LL_CREATE_DEFINE(C, T) \ TCALL_DISPATCHER_LL(T) TCALL_DISPATCHER_LL_CREATE(C)(void) \ { \ /* Codes_SRS_TCALL_DISPATCHER_01_001: [ TCALL_DISPATCHER_CREATE(T) shall call THANDLE_MALLOC to allocate the result. ]*/ \ TCALL_DISPATCHER_TYPEDEF_NAME(T)* result = THANDLE_MALLOC(TCALL_DISPATCHER_TYPEDEF_NAME(C))(TCALL_DISPATCHER_LL_FREE_NAME(C)); \ if(result == NULL) \ { \ LogError("failure in " MU_TOSTRING(THANDLE_MALLOC) "(" MU_TOSTRING(TCALL_DISPATCHER_TYPEDEF_NAME(T)) "=%zu)", sizeof(TCALL_DISPATCHER_TYPEDEF_NAME(T))); \ /*return as is*/ \ } \ else \ { \ /* Codes_SRS_TCALL_DISPATCHER_01_002: [ TCALL_DISPATCHER_CREATE(T) shall call srw_lock_ll_init to initialize the lock used by the TCALL_DISPATCHER instance. ]*/ \ if (srw_lock_ll_init(&result->lock) != 0) \ { \ /* Codes_SRS_TCALL_DISPATCHER_01_005: [ If there are any failures then TCALL_DISPATCHER_CREATE(T) shall fail and return NULL. ]*/ \ LogError("srw_lock_ll_init failed"); \ } \ else \ { \ /* Codes_SRS_TCALL_DISPATCHER_01_003: [ TCALL_DISPATCHER_CREATE(T) shall call DList_InitializeListHead to initialize the doubly linked list that holds the target call registrations. ]*/ \ DList_InitializeListHead(&result->call_targets); \ /*return as is*/ \ /* Codes_SRS_TCALL_DISPATCHER_01_004: [ TCALL_DISPATCHER_CREATE(T) shall succeed and return a non-NULL value. ]*/ \ goto all_ok; \ } \ THANDLE_FREE(TCALL_DISPATCHER_TYPEDEF_NAME(C))(result); \ result = NULL; \ } \ all_ok: \ return result; \ } /*introduces a function definition for tcall_dispatcher_register_target*/ #define TCALL_DISPATCHER_LL_REGISTER_TARGET_DEFINE(C, T) \ TCALL_DISPATCHER_LL_TARGET_HANDLE(T) TCALL_DISPATCHER_LL_REGISTER_TARGET(C)(TCALL_DISPATCHER_LL(T) tcall_dispatcher, TCALL_DISPATCHER_LL_TARGET_FUNC(T) function_to_call, void* call_context) \ { \ TCALL_DISPATCHER_LL_TARGET_HANDLE(T) result; \ if ( \ /* Codes_SRS_TCALL_DISPATCHER_01_006: [ If tcall_dispatcher is NULL then TCALL_DISPATCHER_REGISTER_TARGET(T) shall fail and return NULL. ] */ \ (tcall_dispatcher == NULL) || \ /* Codes_SRS_TCALL_DISPATCHER_01_007: [ If function_to_call is NULL then TCALL_DISPATCHER_REGISTER_TARGET(T) shall fail and return NULL. ]*/ \ (function_to_call == NULL) \ ) \ { \ LogError("Invalid arguments TCALL_DISPATCHER_LL(T) tcall_dispatcher=%p, TCALL_DISPATCHER_LL_TARGET_FUNC(T) function_to_call=%p, void* call_context=%p", \ tcall_dispatcher, function_to_call, call_context); \ result = NULL; \ } \ else \ { \ /* Codes_SRS_TCALL_DISPATCHER_01_008: [ TCALL_DISPATCHER_REGISTER_TARGET(T) shall allocate memory for a TCALL_DISPATCHER_TARGET_HANDLE(T) that holds function_to_call and context. ]*/ \ result = malloc(sizeof(TCALL_DISPATCHER_TARGET_TYPEDEF_NAME(T))); \ if (result == NULL) \ { \ /* Codes_SRS_TCALL_DISPATCHER_01_013: [ If there are any failures then TCALL_DISPATCHER_REGISTER_TARGET(T) shall fail and return NULL. ] */ \ LogError("malloc(sizeof(TCALL_DISPATCHER_TARGET_TYPEDEF_NAME(" MU_TOSTRING(T) "))=%zu) failed", sizeof(TCALL_DISPATCHER_TARGET_TYPEDEF_NAME(T))); \ /* return as is */ \ } \ else \ { \ result->function_to_call = function_to_call; \ result->call_context = call_context; \ \ /* Codes_SRS_TCALL_DISPATCHER_01_009: [ TCALL_DISPATCHER_REGISTER_TARGET(T) shall acquire exclusivly the lock. ]*/ \ srw_lock_ll_acquire_exclusive((SRW_LOCK_LL*)&tcall_dispatcher->lock); \ \ /* Codes_SRS_TCALL_DISPATCHER_01_010: [ TCALL_DISPATCHER_REGISTER_TARGET(T) shall add the new TCALL_DISPATCHER_TARGET_HANDLE(T) containing function_to_call and context in the doubly linked list. ]*/ \ DList_InsertTailList((DLIST_ENTRY*)&tcall_dispatcher->call_targets, &result->anchor); \ \ /* Codes_SRS_TCALL_DISPATCHER_01_011: [ TCALL_DISPATCHER_REGISTER_TARGET(T) shall release the lock. ]*/ \ srw_lock_ll_release_exclusive((SRW_LOCK_LL*)&tcall_dispatcher->lock); \ /* Codes_SRS_TCALL_DISPATCHER_01_012: [ TCALL_DISPATCHER_REGISTER_TARGET(T) shall succeed and return the TCALL_DISPATCHER_TARGET_HANDLE(T). ] */ \ /* return as is */ \ } \ } \ return result; \ } /*introduces a function definition for tcall_dispatcher_unregister_target*/ #define TCALL_DISPATCHER_LL_UNREGISTER_TARGET_DEFINE(C, T) \ int TCALL_DISPATCHER_LL_UNREGISTER_TARGET(C)(TCALL_DISPATCHER_LL(T) tcall_dispatcher, TCALL_DISPATCHER_LL_TARGET_HANDLE(T) call_target) \ { \ int result; \ if ( \ /* Codes_SRS_TCALL_DISPATCHER_01_014: [ If tcall_dispatcher is NULL then TCALL_DISPATCHER_UNREGISTER_TARGET(T) shall fail and return a non-zero value. ]*/ \ (tcall_dispatcher == NULL) || \ /* Codes_SRS_TCALL_DISPATCHER_01_015: [ If call_target is NULL then TCALL_DISPATCHER_UNREGISTER_TARGET(T) shall fail and return a non-zero value. ]*/ \ (call_target == NULL) \ ) \ { \ LogError("Invalid arguments TCALL_DISPATCHER_LL(" MU_TOSTRING(T) ") tcall_dispatcher=%p, TCALL_DISPATCHER_LL_TARGET_HANDLE(" MU_TOSTRING(T) ") call_target=%p", \ tcall_dispatcher, call_target); \ result = MU_FAILURE; \ } \ else \ { \ /* Codes_SRS_TCALL_DISPATCHER_01_016: [ TCALL_DISPATCHER_UNREGISTER_TARGET(T) shall acquire exclusivly the lock. ]*/ \ srw_lock_ll_acquire_exclusive((SRW_LOCK_LL*)&tcall_dispatcher->lock); \ /* Codes_SRS_TCALL_DISPATCHER_01_017: [ TCALL_DISPATCHER_UNREGISTER_TARGET(T) shall remove from the doubly linked list the call target call_target. ]*/ \ DList_RemoveEntryList(&call_target->anchor); \ /* Codes_SRS_TCALL_DISPATCHER_01_018: [ TCALL_DISPATCHER_UNREGISTER_TARGET(T) shall release the lock. ]*/ \ srw_lock_ll_release_exclusive((SRW_LOCK_LL*)&tcall_dispatcher->lock); \ /* Codes_SRS_TCALL_DISPATCHER_01_019: [ TCALL_DISPATCHER_UNREGISTER_TARGET(T) shall free the call_target resources. ]*/ \ free(call_target); \ /* Codes_SRS_TCALL_DISPATCHER_01_020: [ TCALL_DISPATCHER_UNREGISTER_TARGET(T) shall succeed and return 0. ]*/ \ result = 0; \ } \ return result; \ } /*introduces a function definition for tcall_dispatcher_unregister_target*/ #define TCALL_DISPATCHER_LL_DISPATCH_CALL_DEFINE(C, T, ...) \ int TCALL_DISPATCHER_LL_DISPATCH_CALL(C)(TCALL_DISPATCHER_LL(T) tcall_dispatcher MU_FOR_EACH_2(TCALL_DISPATCHER_ARG_IN_SIGNATURE, __VA_ARGS__)) \ { \ int result; \ if (tcall_dispatcher == NULL) \ { \ /* Codes_SRS_TCALL_DISPATCHER_01_021: [ If tcall_dispatcher is NULL then TCALL_DISPATCHER_DISPATCH_CALL(T) shall fail and return a non-zero value. ]*/ \ LogError("Invalid arguments TCALL_DISPATCHER_LL(" MU_TOSTRING(T) ") tcall_dispatcher=%p, ...", \ tcall_dispatcher); \ result = MU_FAILURE; \ } \ else \ { \ /* Codes_SRS_TCALL_DISPATCHER_01_022: [ Otherwise, TCALL_DISPATCHER_DISPATCH_CALL(T) shall acquire the lock in shared mode. ]*/ \ srw_lock_ll_acquire_shared((SRW_LOCK_LL*)&tcall_dispatcher->lock); \ DLIST_ENTRY* current_entry = tcall_dispatcher->call_targets.Flink; \ while (current_entry != &tcall_dispatcher->call_targets) \ { \ TCALL_DISPATCHER_TARGET_HANDLE_NAME(T) call_target_handle = CONTAINING_RECORD(current_entry, TCALL_DISPATCHER_TARGET_TYPEDEF_NAME(T), anchor); \ /* Codes_SRS_TCALL_DISPATCHER_01_023: [ For each call target that was registered, TCALL_DISPATCHER_DISPATCH_CALL(T) shall call the function_to_call with the associated context and the parameters in .... ]*/ \ call_target_handle->function_to_call(call_target_handle->call_context MU_FOR_EACH_2(TCALL_DISPATCHER_ARG_VALUE_IN_CALL, __VA_ARGS__)); \ current_entry = current_entry->Flink; \ } \ /* Codes_SRS_TCALL_DISPATCHER_01_024: [ TCALL_DISPATCHER_DISPATCH_CALL(T) shall release the lock. ]*/ \ srw_lock_ll_release_shared((SRW_LOCK_LL*)&tcall_dispatcher->lock); \ /* Codes_SRS_TCALL_DISPATCHER_01_025: [ TCALL_DISPATCHER_DISPATCH_CALL(T) shall succeed and return 0. ]*/ \ result = 0; \ } \ return result; \ } /*macro to be used in headers*/ \ #define TCALL_DISPATCHER_LL_TYPE_DECLARE(C, T, ...) \ /*hint: have TCALL_DISPATCHER_DEFINE_CALL_TYPE(T) before TCALL_DISPATCHER_LL_TYPE_DECLARE*/ \ THANDLE_LL_TYPE_DECLARE(TCALL_DISPATCHER_TYPEDEF_NAME(C), TCALL_DISPATCHER_TYPEDEF_NAME(T)) \ TCALL_DISPATCHER_LL_CREATE_DECLARE(C, T) \ TCALL_DISPATCHER_LL_REGISTER_TARGET_DECLARE(C, T) \ TCALL_DISPATCHER_LL_UNREGISTER_TARGET_DECLARE(C, T) \ TCALL_DISPATCHER_LL_DISPATCH_CALL_DECLARE(C, T, __VA_ARGS__) \ /*macro to be used in .c*/ \ #define TCALL_DISPATCHER_LL_TYPE_DEFINE(C, T, ...) \ /*hint: have THANDLE_TYPE_DEFINE(TCALL_DISPATCHER_TYPEDEF_NAME(T)) before TCALL_DISPATCHER_LL_TYPE_DEFINE*/ \ TCALL_DISPATCHER_LL_FREE_DEFINE(C, T) \ TCALL_DISPATCHER_LL_CREATE_DEFINE(C, T) \ TCALL_DISPATCHER_LL_REGISTER_TARGET_DEFINE(C, T) \ TCALL_DISPATCHER_LL_UNREGISTER_TARGET_DEFINE(C, T) \ TCALL_DISPATCHER_LL_DISPATCH_CALL_DEFINE(C, T, __VA_ARGS__) \ #endif /*TCALL_DISPATCHER_LL_H*/