From 8c1dd06fb3ac144bf8527961b8bc566918d1e6df Mon Sep 17 00:00:00 2001 From: Nishikant Deshmukh Date: Tue, 3 Sep 2024 17:35:36 -0700 Subject: [PATCH] Initial checkin with specs (#362) * Initial checkin with specs * Fixed Traceability * Fixed PVOID on Linux * Fixed PVOID in threadpool.h * Forgot to add threadpool.h into the commit, adding it now * Addressed comments by Matt * Addressed ALL comments by Matt --- CMakeLists.txt | 22 +++--- interfaces/devdoc/threadpool_requirements.md | 58 ++++++++++++++- interfaces/inc/c_pal/threadpool.h | 8 +- linux/devdoc/threadpool_linux_requirements.md | 72 +++++++++++++++++- linux/linux_reals/real_threadpool.h | 3 + linux/linux_reals/real_threadpool_renames.h | 3 + linux/src/threadpool_linux.c | 25 +++++++ win32/devdoc/threadpool_win32.md | 74 ++++++++++++++++++- win32/reals/real_threadpool.h | 6 ++ win32/reals/real_threadpool_renames.h | 3 + win32/src/threadpool_win32.c | 34 ++++++++- 11 files changed, 292 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b72220..bc7f8a7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ endif() #if ((NOT TARGET c_pal) AND (EXISTS ${CMAKE_CURRENT_LIST_DIR}/deps/c-pal/CMakeLists.txt)) # add_subdirectory(deps/c-pal) # include_directories(${C_PAL_INC_FOLDER}) -#endif() +#endif() if (TARGET c_pal) RETURN() @@ -93,7 +93,7 @@ set(run_reals_check OFF) if ((NOT TARGET c_build_tools) AND (EXISTS ${CMAKE_CURRENT_LIST_DIR}/deps/c-build-tools/CMakeLists.txt)) add_subdirectory(deps/c-build-tools) set_default_build_options() -endif() +endif() if ((WIN32) AND ("${GBALLOC_LL_TYPE}" STREQUAL "JEMALLOC")) # Bring in vcpkg @@ -125,37 +125,37 @@ endif() if ((NOT TARGET macro_utils_c) AND (EXISTS ${CMAKE_CURRENT_LIST_DIR}/deps/macro-utils-c/CMakeLists.txt)) add_subdirectory(deps/macro-utils-c) include_directories(${MACRO_UTILS_INC_FOLDER}) -endif() +endif() if ((NOT TARGET c_logging_v2) AND (EXISTS ${CMAKE_CURRENT_LIST_DIR}/deps/c-logging/CMakeLists.txt)) add_subdirectory(deps/c-logging) include_directories(deps/c-logging/v2/inc) -endif() +endif() if ((NOT TARGET ctest) AND (EXISTS ${CMAKE_CURRENT_LIST_DIR}/deps/ctest/CMakeLists.txt)) add_subdirectory(deps/ctest) include_directories(${CTEST_INC_FOLDER}) -endif() +endif() if ((NOT TARGET testrunnerswitcher) AND (EXISTS ${CMAKE_CURRENT_LIST_DIR}/deps/c-testrunnerswitcher/CMakeLists.txt)) add_subdirectory(deps/c-testrunnerswitcher) include_directories(${TESTRUNNERSWITCHER_INC_FOLDER}) -endif() +endif() if ((NOT TARGET umock_c) AND (EXISTS ${CMAKE_CURRENT_LIST_DIR}/deps/umock-c/CMakeLists.txt)) add_subdirectory(deps/umock-c) include_directories(${UMOCK_C_INC_FOLDER}) -endif() +endif() # Skip for Linux iwyu if (WIN32) if ( - (NOT TARGET mimalloc-obj) AND + (NOT TARGET mimalloc-obj) AND (${GBALLOC_LL_TYPE} STREQUAL "MIMALLOC") AND (EXISTS ${CMAKE_CURRENT_LIST_DIR}/deps/mimalloc/CMakeLists.txt) ) set(MI_BUILD_SHARED OFF CACHE BOOL "Build shared library" FORCE) #not building a dll allows building on 32 bit, otherwise there's some errors on init.c about not finding a imported symbol - set(MI_BUILD_TESTS OFF CACHE BOOL "Build test executables" FORCE) + set(MI_BUILD_TESTS OFF CACHE BOOL "Build test executables" FORCE) #for mimalloc disable this warning: Warning C4459: declaration of 'os_page_size' hides global declaration #for mimalloc disable this warning: Warning C4100: 'try_alignment': unreferenced formal parameter #for mimalloc disable this warning: warning C4505: 'mi_os_get_aligned_hint': unreferenced local function has been removed @@ -169,7 +169,7 @@ if (WIN32) add_subdirectory(deps/mimalloc) include_directories(deps/mimalloc/include) - + set(CMAKE_C_FLAGS ${PREV_CMAKE_C_FLAGS}) set(CMAKE_CXX_FLAGS ${PREV_CMAKE_CXX_FLAGS}) endif() @@ -185,7 +185,7 @@ set(run_reals_check ${original_run_reals_check}) include(CTest) enable_testing() -set(C_PAL_INC_FOLDER +set(C_PAL_INC_FOLDER ${CMAKE_CURRENT_LIST_DIR}/interfaces/inc ${CMAKE_CURRENT_LIST_DIR}/common/inc ${CMAKE_CURRENT_LIST_DIR}/c_pal_ll/interfaces/inc diff --git a/interfaces/devdoc/threadpool_requirements.md b/interfaces/devdoc/threadpool_requirements.md index ec0bf00..2e20c40 100644 --- a/interfaces/devdoc/threadpool_requirements.md +++ b/interfaces/devdoc/threadpool_requirements.md @@ -21,7 +21,7 @@ The lifetime of the execution engine should supersede the lifetime of the `threa ```c typedef struct THREADPOOL_TAG THREADPOOL; typedef struct TIMER_INSTANCE_TAG* TIMER_INSTANCE_HANDLE; - +typedef struct THREADPOOL_WORK_ITEM_TAG* THREADPOOL_WORK_ITEM_HANDLE; typedef void (*THREADPOOL_WORK_FUNCTION)(void* context); THANDLE_TYPE_DECLARE(THREADPOOL); @@ -31,6 +31,12 @@ MOCKABLE_FUNCTION(, THANDLE(THREADPOOL), threadpool_create, EXECUTION_ENGINE_HAN MOCKABLE_FUNCTION(, int, threadpool_open, THANDLE(THREADPOOL), threadpool); MOCKABLE_FUNCTION(, void, threadpool_close, THANDLE(THREADPOOL), threadpool); +MOCKABLE_FUNCTION(, THREADPOOL_WORK_ITEM_HANDLE, threadpool_create_work_item, THANDLE(THREADPOOL), threadpool, THREADPOOL_WORK_FUNCTION, work_function, void*, work_function_context); + +MOCKABLE_FUNCTION(, int, threadpool_schedule_work_item, THANDLE(THREADPOOL), threadpool, THREADPOOL_WORK_ITEM_HANDLE, work_item_context); + +MOCKABLE_FUNCTION(, void, threadpool_destroy_work_item, THREADPOOL_WORK_ITEM_HANDLE, work_item_context); + MOCKABLE_FUNCTION(, int, threadpool_schedule_work, THANDLE(THREADPOOL), threadpool, THREADPOOL_WORK_FUNCTION, work_function, void*, work_function_context); MOCKABLE_FUNCTION(, int, threadpool_timer_start, THANDLE(THREADPOOL), threadpool, uint32_t, start_delay_ms, uint32_t, timer_period_ms, THREADPOOL_WORK_FUNCTION, work_function, void*, work_function_context, TIMER_INSTANCE_HANDLE*, timer_handle); @@ -104,6 +110,56 @@ MOCKABLE_FUNCTION(, void, threadpool_close, THREADPOOL_HANDLE, threadpool); **NON_THREADPOOL_01_019: [** If `threadpool` is not OPEN, `threadpool_close` shall return. **]** +### threadpool_create_work_item + +```c +MOCKABLE_FUNCTION(, THREADPOOL_WORK_ITEM_HANDLE, threadpool_create_work_item, THANDLE(THREADPOOL), threadpool, THREADPOOL_WORK_FUNCTION, work_function, void*, work_function_context); +``` + +`threadpool_create_work_item` creates a work item to be executed by the threadpool. This function separates the creation of the work item from scheduling it so that threadpool_schedule_work_item can be called later and cannot fail. + +**NON_THREADPOOL_05_001: [** If `threadpool` is `NULL`, `threadpool_create_work_item` shall fail and return a `NULL` value. **]** + +**NON_THREADPOOL_05_002: [** If `work_function` is `NULL`, `threadpool_create_work_item` shall fail and return a `NULL` value. **]** + +**NON_THREADPOOL_05_003: [** Otherwise `threadpool_create_work_item` shall allocate a context of type `THREADPOOL_WORK_ITEM_HANDLE` to save threadpool related variables. **]** + +**NON_THREADPOOL_05_004: [** If any error occurs, `threadpool_create_work_item` shall fail and return a `NULL` value. **]** + +**NON_THREADPOOL_05_005: [** `threadpool_create_work_item` shall create an individual work item context of type `THREADPOOL_WORK_ITEM_HANDLE` **]** + +**NON_THREADPOOL_05_006: [** If there are no errors then the work item of type `THREADPOOL_WORK_ITEM_HANDLE` is created and returned to the caller. **]** + +**NON_THREADPOOL_05_007: [** If any error occurs, `threadpool_create_work_item` shall fail, free the newly created context and return a `NULL` value. **]** + +### threadpool_schedule_work_item + +```c +MOCKABLE_FUNCTION(, int, threadpool_schedule_work_item, THANDLE(THREADPOOL), threadpool, THREADPOOL_WORK_ITEM_HANDLE, work_item_context); +``` + +`threadpool_schedule_work_item` schedules a work item to be executed by the threadpool. This API is called multiple times on the same `THREADPOOL_WORK_ITEM_HANDLE` and fails only if the arguments are passed `NULL`. + +**NON_THREADPOOL_05_008: [** If `threadpool` is NULL, `threadpool_schedule_work_item` shall fail and return a `non-zero` value. **]** + +**NON_THREADPOOL_05_009: [** If `work_item_context` is NULL, `threadpool_schedule_work_item` shall fail and return a `non-zero` value. **]** + +**NON_THREADPOOL_05_010: [** `threadpool_schedule_work_item` shall submit the threadpool work item for execution. **]** + +### threadpool_destroy_work_item + +```c +MOCKABLE_FUNCTION(, void, threadpool_destroy_work_item, THREADPOOL_WORK_ITEM_HANDLE, work_item_context); +``` + +`threadpool_destroy_work_item` closes and frees all the member variables in the context of type `THREADPOOL_WORK_ITEM_HANDLE` and then frees the context + +**NON_THREADPOOL_05_011: [** If `work_item_context` is `NULL`, `threadpool_destroy_work_item` shall fail and not do anything before returning. **]** + +**NON_THREADPOOL_05_012: [** `threadpool_destroy_work_item` shall close and free all the members of `THREADPOOL_WORK_ITEM_HANDLE`. **]** + +**NON_THREADPOOL_05_013: [** `threadpool_destroy_work_item` shall free the `work_item_context` of type `THREADPOOL_WORK_ITEM_HANDLE`. **]** + ### threadpool_schedule_work ```c diff --git a/interfaces/inc/c_pal/threadpool.h b/interfaces/inc/c_pal/threadpool.h index 6ef7515..3809e5b 100644 --- a/interfaces/inc/c_pal/threadpool.h +++ b/interfaces/inc/c_pal/threadpool.h @@ -22,7 +22,7 @@ extern "C" { typedef struct THREADPOOL_TAG THREADPOOL; typedef struct TIMER_INSTANCE_TAG* TIMER_INSTANCE_HANDLE; - +typedef struct THREADPOOL_WORK_ITEM_TAG* THREADPOOL_WORK_ITEM_HANDLE; typedef void (*THREADPOOL_WORK_FUNCTION)(void* context); THANDLE_TYPE_DECLARE(THREADPOOL); @@ -32,6 +32,12 @@ MOCKABLE_FUNCTION(, THANDLE(THREADPOOL), threadpool_create, EXECUTION_ENGINE_HAN MOCKABLE_FUNCTION(, int, threadpool_open, THANDLE(THREADPOOL), threadpool); MOCKABLE_FUNCTION(, void, threadpool_close, THANDLE(THREADPOOL), threadpool); +MOCKABLE_FUNCTION(, THREADPOOL_WORK_ITEM_HANDLE, threadpool_create_work_item, THANDLE(THREADPOOL), threadpool, THREADPOOL_WORK_FUNCTION, work_function, void*, work_function_context); + +MOCKABLE_FUNCTION(, int, threadpool_schedule_work_item, THANDLE(THREADPOOL), threadpool, THREADPOOL_WORK_ITEM_HANDLE, work_item_context); + +MOCKABLE_FUNCTION(, void, threadpool_destroy_work_item, THREADPOOL_WORK_ITEM_HANDLE, work_item_context); + MOCKABLE_FUNCTION(, int, threadpool_schedule_work, THANDLE(THREADPOOL), threadpool, THREADPOOL_WORK_FUNCTION, work_function, void*, work_function_context); MOCKABLE_FUNCTION(, int, threadpool_timer_start, THANDLE(THREADPOOL), threadpool, uint32_t, start_delay_ms, uint32_t, timer_period_ms, THREADPOOL_WORK_FUNCTION, work_function, void*, work_function_context, TIMER_INSTANCE_HANDLE*, timer_handle); diff --git a/linux/devdoc/threadpool_linux_requirements.md b/linux/devdoc/threadpool_linux_requirements.md index 4b41f6e..f346947 100644 --- a/linux/devdoc/threadpool_linux_requirements.md +++ b/linux/devdoc/threadpool_linux_requirements.md @@ -67,7 +67,7 @@ The guard prevents any work from happening, but the event callback can still be ```C typedef struct THREADPOOL_TAG THREADPOOL; typedef struct TIMER_INSTANCE_TAG* TIMER_INSTANCE_HANDLE; - +typedef struct THREADPOOL_WORK_ITEM_TAG* THREADPOOL_WORK_ITEM_HANDLE; typedef void (*THREADPOOL_WORK_FUNCTION)(void* context); THANDLE_TYPE_DECLARE(THREADPOOL); @@ -77,6 +77,12 @@ MOCKABLE_FUNCTION(, THANDLE(THREADPOOL), threadpool_create, EXECUTION_ENGINE_HAN MOCKABLE_FUNCTION(, int, threadpool_open, THANDLE(THREADPOOL), threadpool); MOCKABLE_FUNCTION(, void, threadpool_close, THANDLE(THREADPOOL), threadpool); +MOCKABLE_FUNCTION(, THREADPOOL_WORK_ITEM_HANDLE, threadpool_create_work_item, THANDLE(THREADPOOL), threadpool, THREADPOOL_WORK_FUNCTION, work_function, void*, work_function_context); + +MOCKABLE_FUNCTION(, int, threadpool_schedule_work_item, THANDLE(THREADPOOL), threadpool, THREADPOOL_WORK_ITEM_HANDLE, work_item_context); + +MOCKABLE_FUNCTION(, void, threadpool_work_context_destroy, THREADPOOL_WORK_ITEM_HANDLE, work_item_context); + MOCKABLE_FUNCTION(, int, threadpool_schedule_work, THANDLE(THREADPOOL), threadpool, THREADPOOL_WORK_FUNCTION, work_function, void*, work_function_context); MOCKABLE_FUNCTION(, int, threadpool_timer_start, THANDLE(THREADPOOL), threadpool, uint32_t, start_delay_ms, uint32_t, timer_period_ms, THREADPOOL_WORK_FUNCTION, work_function, void*, work_function_context, TIMER_INSTANCE_HANDLE*, timer_handle); @@ -381,3 +387,67 @@ static int threadpool_work_func(void* param); **SRS_THREADPOOL_LINUX_07_084: [** If the work item function is not `NULL`, `threadpool_work_func` shall execute it with `work_function_ctx`. **]** **SRS_THREADPOOL_LINUX_07_085: [** `threadpool_work_func` shall loop until `threadpool_close` or `threadpool_destroy` is called. **]** + +### threadpool_create_work_item + +```c +MOCKABLE_FUNCTION(, THREADPOOL_WORK_ITEM_HANDLE, threadpool_create_work_item, THANDLE(THREADPOOL), threadpool, THREADPOOL_WORK_FUNCTION, work_function, void*, work_function_context); +``` + +`threadpool_create_work_item` creates a work item to be executed by the threadpool. + +**S_R_S THREADPOOL_LINUX_05_001: [** If `threadpool` is `NULL`, `threadpool_create_work_item` shall fail and return a `NULL` value. **]** + +**S_R_S_THREADPOOL_LINUX_05_002: [** If `work_function` is `NULL`, `threadpool_create_work_item` shall fail and return a `NULL` value. **]** + +**S_R_S_THREADPOOL_LINUX_05_003: [** `threadpool_create_work_item` shall allocate memory for `threadpool_work_item` of type `THREADPOOL_WORK_ITEM_HANDLE`. **]** + +**S_R_S_THREADPOOL_LINUX_05_004: [** If the `malloc` fails and `threadpool_work_item` is `NULL` then log the error and return `NULL`. **]** + +**S_R_S_THREADPOOL_LINUX_05_005: [** `threadpool_create_work_item` shall copy the `work_function` and `work_function_context` into the `threadpool_work_item` and return `threadpool_work_item` to indicate success. **]** + +### threadpool_schedule_work_item + +```c +MOCKABLE_FUNCTION(, int, threadpool_schedule_work_item, THANDLE(THREADPOOL), threadpool, THREADPOOL_WORK_ITEM_HANDLE, work_item); +``` + +`threadpool_schedule_work_item` schedules a work item to be executed by the threadpool. + +**S_R_S_THREADPOOL_LINUX_05_006: [** If `threadpool` is `NULL`, `threadpool_schedule_work_item` shall fail and return a non-zero value. **]** + +**S_R_S_THREADPOOL_LINUX_05_007: [** If `work_item` is `NULL`, `threadpool_schedule_work_item` shall fail and return a non-zero value. **]** + +**S_R_S_THREADPOOL_LINUX_05_008: [** `threadpool_schedule_work_item` shall call `sm_exec_begin`. **]** + +**S_R_S_THREADPOOL_LINUX_05_009: [** If `sm_exec_begin` returns `SM_EXEC_REFUSED`, `threadpool_schedule_work_item` shall fail and return a non-zero value. **]** + +**S_R_S_THREADPOOL_LINUX_05_010: [** `threadpool_schedule_work_item` shall acquire the `SRW lock` in shared mode by calling `srw_lock_acquire_shared`. **]** + +**S_R_S_THREADPOOL_LINUX_05_011: [** `threadpool_schedule_work_item` shall increment the `insert_pos`. **]** + +**S_R_S_THREADPOOL_LINUX_05_012: [** If task state is `TASK_NOT_USED`, `threadpool_schedule_work_item` shall set the current task state to `TASK_INITIALIZING`. **]** + +**S_R_S_THREADPOOL_LINUX_05_013: [** Otherwise, `threadpool_schedule_work_item` shall release the shared SRW lock by calling `srw_lock_release_shared` and increase `task_array` capacity: **]** + +**S_R_S_THREADPOOL_LINUX_05_014: [** If reallocating the task array fails, `threadpool_schedule_work_item` shall fail and return a `NULL` value. **]** + +**S_R_S_THREADPOOL_LINUX_05_015: [** `threadpool_schedule_work_item` shall copy the `work_function` and `work_function_context` from `work_item` into insert position in the task array and return `0` on success. **]** + +**S_R_S_THREADPOOL_LINUX_05_016: [** `threadpool_schedule_work_item` shall set the `task_state` to `TASK_WAITING` and then release the shared SRW lock. **]** + +**S_R_S_THREADPOOL_LINUX_05_017: [** `threadpool_schedule_work_item` shall unblock the `threadpool` semaphore by calling `sem_post`. **]** + +**S_R_S_THREADPOOL_LINUX_05_018: [** `threadpool_schedule_work_item` shall return `0` on success. **]** + +**S_R_S_THREADPOOL_LINUX_05_019: [** `threadpool_schedule_work_item` shall call `sm_exec_end`. **]** + +### threadpool_destroy_work_item + +```c +MOCKABLE_FUNCTION(, void, threadpool_destroy_work_item, THREADPOOL_WORK_ITEM_HANDLE, work_item_context); +``` + +`threadpool_work_context_destroy` Does nothing and a placeholder for WIN32 equivalent function stub + +**S_R_S_THREADPOOL_LINUX_05_020: [** Free memory allocated to the work item of type `THREADPOOL_WORK_ITEM_HANDLE` created in `threadpool_create_work_item`. **]** diff --git a/linux/linux_reals/real_threadpool.h b/linux/linux_reals/real_threadpool.h index 6240b2e..5a88129 100644 --- a/linux/linux_reals/real_threadpool.h +++ b/linux/linux_reals/real_threadpool.h @@ -36,6 +36,9 @@ extern "C" { THANDLE(THREADPOOL) real_threadpool_create(EXECUTION_ENGINE_HANDLE execution_engine); int real_threadpool_open(THANDLE(THREADPOOL) threadpool); void real_threadpool_close(THANDLE(THREADPOOL) threadpool); + THREADPOOL_WORK_ITEM_HANDLE real_threadpool_create_work_item(THANDLE(THREADPOOL) threadpool, THREADPOOL_WORK_FUNCTION work_function, void* work_function_context); + int real_threadpool_schedule_work_item(THANDLE(THREADPOOL) threadpool, THREADPOOL_WORK_ITEM_HANDLE work_item_context); + void real_threadpool_destroy_work_item(THREADPOOL_WORK_ITEM_HANDLE work_item_context); int real_threadpool_schedule_work(THANDLE(THREADPOOL) threadpool, THREADPOOL_WORK_FUNCTION work_function, void* work_function_context); int real_threadpool_timer_start(THANDLE(THREADPOOL) threadpool, uint32_t start_delay_ms, uint32_t timer_period_ms, THREADPOOL_WORK_FUNCTION work_function, void* work_function_context, TIMER_INSTANCE_HANDLE* timer_handle); int real_threadpool_timer_restart(TIMER_INSTANCE_HANDLE timer, uint32_t start_delay_ms, uint32_t timer_period_ms); diff --git a/linux/linux_reals/real_threadpool_renames.h b/linux/linux_reals/real_threadpool_renames.h index 5551554..f63b535 100644 --- a/linux/linux_reals/real_threadpool_renames.h +++ b/linux/linux_reals/real_threadpool_renames.h @@ -6,6 +6,9 @@ #define threadpool_create real_threadpool_create #define threadpool_open real_threadpool_open #define threadpool_close real_threadpool_close +#define threadpool_create_work_item real_threadpool_create_work_item +#define threadpool_schedule_work_item real_threadpool_schedule_work_item +#define threadpool_destroy_work_item real_threadpool_destroy_work_item #define threadpool_schedule_work real_threadpool_schedule_work #define threadpool_timer_start real_threadpool_timer_start #define threadpool_timer_restart real_threadpool_timer_restart diff --git a/linux/src/threadpool_linux.c b/linux/src/threadpool_linux.c index f3e8fdf..9c09bdf 100644 --- a/linux/src/threadpool_linux.c +++ b/linux/src/threadpool_linux.c @@ -73,6 +73,14 @@ typedef struct THREADPOOL_TASK_TAG volatile uint32_t pending_api_calls; } THREADPOOL_TASK; +typedef struct THREADPOOL_WORK_ITEM_TAG +{ + volatile_atomic int32_t task_state; + THREADPOOL_WORK_FUNCTION work_function; + void* work_function_ctx; + volatile uint32_t pending_api_calls; +} THREADPOOL_WORK_ITEM, * THREADPOOL_WORK_ITEM_HANDLE; + typedef struct THREADPOOL_TAG { volatile_atomic int32_t run_thread; @@ -507,6 +515,23 @@ void threadpool_close(THANDLE(THREADPOOL) threadpool) } } +THREADPOOL_WORK_ITEM_HANDLE threadpool_create_work_item(THANDLE(THREADPOOL) threadpool, THREADPOOL_WORK_FUNCTION work_function, void* work_function_context) +{ + LogWarning("Not Implemented: threadpool_create_work_item(%p, %p, %p)", threadpool, work_function, work_function_context); + return NULL; +} + +int threadpool_schedule_work_item(THANDLE(THREADPOOL) threadpool, THREADPOOL_WORK_ITEM_HANDLE work_item_context) +{ + LogWarning("Not Implemented: threadpool_schedule_work_item(%p)", work_item_context); + return 0; +} + +void threadpool_work_context_destroy(THREADPOOL_WORK_ITEM_HANDLE work_item_context) +{ + LogWarning("Not Implemented: threadpool_work_context_destroy(%p)", work_item_context); +} + int threadpool_schedule_work(THANDLE(THREADPOOL) threadpool, THREADPOOL_WORK_FUNCTION work_function, void* work_function_ctx) { int result; diff --git a/win32/devdoc/threadpool_win32.md b/win32/devdoc/threadpool_win32.md index 111a780..40e0c17 100644 --- a/win32/devdoc/threadpool_win32.md +++ b/win32/devdoc/threadpool_win32.md @@ -13,7 +13,7 @@ It uses the PTP_POOL associated with the execution engine passed as argument to ```c typedef struct THREADPOOL_TAG THREADPOOL; typedef struct TIMER_INSTANCE_TAG* TIMER_INSTANCE_HANDLE; - +typedef struct THREADPOOL_WORK_ITEM_TAG* THREADPOOL_WORK_ITEM_HANDLE; typedef void (*THREADPOOL_WORK_FUNCTION)(void* context); THANDLE_TYPE_DECLARE(THREADPOOL); @@ -23,6 +23,12 @@ MOCKABLE_FUNCTION(, THANDLE(THREADPOOL), threadpool_create, EXECUTION_ENGINE_HAN MOCKABLE_FUNCTION(, int, threadpool_open, THANDLE(THREADPOOL), threadpool); MOCKABLE_FUNCTION(, void, threadpool_close, THANDLE(THREADPOOL), threadpool); +MOCKABLE_FUNCTION(, THREADPOOL_WORK_ITEM_HANDLE, threadpool_create_work_item, THANDLE(THREADPOOL), threadpool, THREADPOOL_WORK_FUNCTION, work_function, void*, work_function_context); + +MOCKABLE_FUNCTION(, int, threadpool_schedule_work_item, THANDLE(THREADPOOL), threadpool, THREADPOOL_WORK_ITEM_HANDLE, work_item_context); + +MOCKABLE_FUNCTION(, void, threadpool_destroy_work_item, THREADPOOL_WORK_ITEM_HANDLE, work_item_context); + MOCKABLE_FUNCTION(, int, threadpool_schedule_work, THANDLE(THREADPOOL), threadpool, THREADPOOL_WORK_FUNCTION, work_function, void*, work_function_context); MOCKABLE_FUNCTION(, int, threadpool_timer_start, THANDLE(THREADPOOL), threadpool, uint32_t, start_delay_ms, uint32_t, timer_period_ms, THREADPOOL_WORK_FUNCTION, work_function, void*, work_function_context, TIMER_INSTANCE_HANDLE*, timer_handle); @@ -156,6 +162,21 @@ static VOID CALLBACK on_work_callback(PTP_CALLBACK_INSTANCE instance, PVOID cont **SRS_THREADPOOL_WIN32_01_039: [** `on_work_callback` shall free the context allocated in `threadpool_schedule_work`. **]** +### on_work_callback_v2 + +```c +static VOID CALLBACK on_work_callback_v2(PTP_CALLBACK_INSTANCE instance, void* context, PTP_WORK work); +``` + +`on_work_callback_v2` executes the work function passed to `threadpool_create_work_item`. + +**S_R_S_THREADPOOL_WIN32_05_001: [** If `context` is `NULL`, `on_work_callback_v2` shall return. **]** + +**S_R_S_THREADPOOL_WIN32_05_002: [** Otherwise `context` shall be used as the context created in `threadpool_create_work_item`. **]** + +**S_R_S_THREADPOOL_WIN32_05_003: [** The `work_function` callback passed to `threadpool_create_work_item` shall be called with the `work_function_context` as an argument. `work_function_context` was set inside the `threadpool_create_work_item` as an argument to `CreateThreadpoolContext`. **]** + + ### threadpool_timer_start ```c @@ -243,3 +264,54 @@ static VOID CALLBACK on_timer_callback(PTP_CALLBACK_INSTANCE instance, PVOID con **SRS_THREADPOOL_WIN32_42_017: [** Otherwise `context` shall be used as the context created in `threadpool_schedule_work`. **]** **SRS_THREADPOOL_WIN32_42_018: [** The `work_function` callback passed to `threadpool_schedule_work` shall be called, passing to it the `work_function_context` argument passed to `threadpool_schedule_work`. **]** + +### threadpool_create_work_item + +```c +MOCKABLE_FUNCTION(, THREADPOOL_WORK_ITEM_HANDLE, threadpool_create_work_item, THANDLE(THREADPOOL), threadpool, THREADPOOL_WORK_FUNCTION, work_function, void*, work_function_context); +``` + +`threadpool_create_work_item` creates a work item to be executed by the threadpool. + +**S_R_S_THREADPOOL_WIN32_05_004: [** If `threadpool` is `NULL`, `threadpool_create_work_item` shall fail and return a `NULL` value. **]** + +**S_R_S_THREADPOOL_WIN32_05_005: [** If `work_function` is `NULL`, `threadpool_create_work_item` shall fail and return a `NULL` value. **]** + +**S_R_S_THREADPOOL_WIN32_05_006: [** Otherwise `threadpool_create_work_item` shall allocate a context `work_item_context` of type `THREADPOOL_WORK_ITEM_HANDLE` where `work_function`, `work_function_context`, and `ptp_work` shall be saved. **]** + +**S_R_S_THREADPOOL_WIN32_05_007: [** If any error occurs, `threadpool_create_work_item` shall fail and return a `NULL` value. **]** + +**S_R_S_THREADPOOL_WIN32_05_008: [** `threadpool_create_work_item` shall create `work_item_context` member variable `ptp_work` of type `PTP_WORK` by calling `CreateThreadpoolWork` to set the callback function as `on_work_callback_v2`. **]** + +**S_R_S_THREADPOOL_WIN32_05_009: [** If there are no errors then this `work_item_context` of type `THREADPOOL_WORK_ITEM_HANDLE` would be returned indicating a succcess to the caller**]** + +**S_R_S_THREADPOOL_WIN32_05_010: [** If any error occurs, `threadpool_create_work_item` shall fail, free the newly created context and return a `NULL` value. **]** + + +### threadpool_schedule_work_item + +```c +MOCKABLE_FUNCTION(, int, threadpool_schedule_work_item, THANDLE(THREADPOOL), threadpool, THREADPOOL_WORK_ITEM_HANDLE, work_item_context); +``` + +`threadpool_schedule_work_item` schedules a work item to be executed by the threadpool. + +**S_R_S_THREADPOOL_WIN32_05_011: [** If `threadpool` is NULL, `threadpool_schedule_work_item` shall fail and return a `non-zero` value. **]** + +**S_R_S_THREADPOOL_WIN32_05_012: [** If `work_item_context` is NULL, `threadpool_schedule_work_item` shall fail and return a `non-zero` value. **]** + +**S_R_S_THREADPOOL_WIN32_05_013: [** `threadpool_schedule_work_item` shall call `SubmitThreadpoolWork` to submit the work item for execution. **]** + +### threadpool_destroy_work_item + +```c +MOCKABLE_FUNCTION(, void, threadpool_destroy_work_item, THREADPOOL_WORK_ITEM_HANDLE, work_item_context); +``` + +`threadpool_destroy_work_item` closes the `ptp_work` member variable in `work_item_context`and then frees the `work_item_context` + +**S_R_S_THREADPOOL_WIN32_05_014: [** If `work_item_context` is `NULL`, `threadpool_destroy_work_item` shall fail and not do anything before returning. **]** + +**S_R_S_THREADPOOL_WIN32_05_015: [** `threadpool_destroy_work_item` shall call `CloseThreadpoolWork` to close `ptp_work`. **]** + +**S_R_S_THREADPOOL_WIN32_05_016: [** `threadpool_destroy_work_item` shall free the `work_item_context`. **]** \ No newline at end of file diff --git a/win32/reals/real_threadpool.h b/win32/reals/real_threadpool.h index 1594b6a..9ef9c87 100644 --- a/win32/reals/real_threadpool.h +++ b/win32/reals/real_threadpool.h @@ -37,6 +37,12 @@ extern "C" { int real_threadpool_open(THANDLE(THREADPOOL) threadpool); void real_threadpool_close(THANDLE(THREADPOOL) threadpool); + THREADPOOL_WORK_ITEM_HANDLE real_threadpool_create_work_item(THANDLE(THREADPOOL) threadpool, THREADPOOL_WORK_FUNCTION work_function, void* work_function_context); + + int real_threadpool_schedule_work_item(THANDLE(THREADPOOL) threadpool, THREADPOOL_WORK_ITEM_HANDLE work_item_context); + + void real_threadpool_destroy_work_item(THREADPOOL_WORK_ITEM_HANDLE work_item_context); + int real_threadpool_schedule_work(THANDLE(THREADPOOL) threadpool, THREADPOOL_WORK_FUNCTION work_function, void* work_function_context); int real_threadpool_timer_start(THANDLE(THREADPOOL) threadpool, uint32_t start_delay_ms, uint32_t timer_period_ms, THREADPOOL_WORK_FUNCTION work_function, void* work_function_context, TIMER_INSTANCE_HANDLE* timer_handle); diff --git a/win32/reals/real_threadpool_renames.h b/win32/reals/real_threadpool_renames.h index f5ddeb7..54aca1a 100644 --- a/win32/reals/real_threadpool_renames.h +++ b/win32/reals/real_threadpool_renames.h @@ -5,6 +5,9 @@ #define threadpool_create real_threadpool_create #define threadpool_open real_threadpool_open #define threadpool_close real_threadpool_close +#define threadpool_create_work_item real_threadpool_create_work_item +#define threadpool_schedule_work_item real_threadpool_schedule_work_item +#define threadpool_destroy_work_item real_threadpool_destroy_work_item #define threadpool_schedule_work real_threadpool_schedule_work #define threadpool_timer_start real_threadpool_timer_start #define threadpool_timer_restart real_threadpool_timer_restart diff --git a/win32/src/threadpool_win32.c b/win32/src/threadpool_win32.c index 687e5f3..7e35d3b 100644 --- a/win32/src/threadpool_win32.c +++ b/win32/src/threadpool_win32.c @@ -29,6 +29,13 @@ typedef struct WORK_ITEM_CONTEXT_TAG void* work_function_context; } WORK_ITEM_CONTEXT; +typedef struct THREADPOOL_WORK_ITEM_TAG +{ + THREADPOOL_WORK_FUNCTION work_function; + void* work_function_context; + PTP_WORK* ptp_work; +} THREADPOOL_WORK_ITEM, *THREADPOOL_WORK_ITEM_HANDLE; + typedef struct TIMER_INSTANCE_TAG { PTP_TIMER timer; @@ -78,6 +85,11 @@ static VOID CALLBACK on_work_callback(PTP_CALLBACK_INSTANCE instance, PVOID cont } } +static VOID CALLBACK on_work_callback_v2(PTP_CALLBACK_INSTANCE instance, PVOID context, PTP_WORK work) +{ + LogWarning("Printing %p %p %p", instance, context, work); +} + static void internal_close(THREADPOOL* threadpool) { do @@ -113,7 +125,7 @@ static void threadpool_dispose(THREADPOOL* threadpool) int32_t current_state = InterlockedCompareExchange(&threadpool->state, THREADPOOL_WIN32_STATE_CLOSING, THREADPOOL_WIN32_STATE_OPEN); if (current_state == THREADPOOL_WIN32_STATE_OPEN) { - /* Codes_SRS_THREADPOOL_WIN32_01_007: [ threadpool_destroy shall perform an implicit close if threadpool is OPEN. ]*/ + /* Codes_SRS_THREADPOOL_WIN32_01_007: [ threadpool_dispose shall perform an implicit close if threadpool is OPEN. ]*/ internal_close(threadpool); break; } @@ -265,6 +277,26 @@ void threadpool_close(THANDLE(THREADPOOL) threadpool) } } +THREADPOOL_WORK_ITEM_HANDLE threadpool_create_work_item(THANDLE(THREADPOOL) threadpool, THREADPOOL_WORK_FUNCTION work_function, void* work_function_context) +{ + (void*)threadpool; + (void*)work_function; + (void*)work_function_context; + return NULL; +} + +int threadpool_schedule_work_item(THANDLE(THREADPOOL) threadpool, THREADPOOL_WORK_ITEM_HANDLE work_item_context) +{ + (void*)threadpool; + (void*)work_item_context; + return 0; +} + +void threadpool_destroy_work_item(THREADPOOL_WORK_ITEM_HANDLE work_item_context) +{ + (void*)work_item_context; +} + int threadpool_schedule_work(THANDLE(THREADPOOL) threadpool, THREADPOOL_WORK_FUNCTION work_function, void* work_function_context) { int result;