2021-06-04 16:44 (UTC-0700), Narcisa Ana Maria Vasile: [...] > diff --git a/lib/eal/include/rte_thread.h b/lib/eal/include/rte_thread.h > index 5c54cd9d67..1d481b9ad5 100644 > --- a/lib/eal/include/rte_thread.h > +++ b/lib/eal/include/rte_thread.h > @@ -208,6 +208,73 @@ __rte_experimental > int rte_thread_attr_set_priority(rte_thread_attr_t *thread_attr, > enum rte_thread_priority priority); > > +/** > + * Create a new thread that will invoke the 'thread_func' routine. > + * > + * @param thread_id > + * A pointer that will store the id of the newly created thread. > + * > + * @param thread_attr > + * Attributes that are used at the creation of the new thread. > + * > + * @param thread_func > + * The routine that the new thread will invoke when starting execution. > + * > + * @param args > + * Arguments to be passed to the 'thread_func' routine. > + * > + * @return > + * On success, return 0. > + * On failure, return a positive errno-style error number. > + */ > +__rte_experimental > +int rte_thread_create(rte_thread_t *thread_id, > + const rte_thread_attr_t *thread_attr, > + void *(*thread_func)(void *), void *args);
1. Thread function prototype is used at least in 4 places, maybe give it a name, like `rte_thread_func`? 2. We can't easily support it's `void*` return type on Windows, because it doesn't fit in DWORD. In `rte_thread_join` below you use `int`. All `pthread_join` usages in DPDK ignore return value, but I'd rather keep it. Do you think it's OK to stick to `int`? [...] > +/** > + * Terminates a thread. > + * > + * @param thread_id > + * The id of the thread to be cancelled. > + * > + * @return > + * On success, return 0. > + * On failure, return a positive errno-style error number. > + */ > +__rte_experimental > +int rte_thread_cancel(rte_thread_t thread_id); What do you think of making this function internal for now? We don't want applications to rely on this prototype. To hide it from Doxygen, `/*` comment or #ifndef __DOXYGEN__ can be used. It is worth noting in commit message that it's not implemented for Windows and why. > + > +/** > + * Detaches a thread. Please explain what it means, because detaching is a pthread concept. > + * > + * @param thread_id > + * The id of the thread to be cancelled. Not "cancelled". > + * > + * @return > + * On success, return 0. > + * On failure, return a positive errno-style error number. > + */ > +__rte_experimental > +int rte_thread_detach(rte_thread_t thread_id); > + > + Redundant empty line. > #ifdef RTE_HAS_CPUSET > > /** > diff --git a/lib/eal/windows/rte_thread.c b/lib/eal/windows/rte_thread.c > index 6dc3d575c0..5afdd54e15 100644 > --- a/lib/eal/windows/rte_thread.c > +++ b/lib/eal/windows/rte_thread.c > @@ -14,6 +14,11 @@ struct eal_tls_key { > DWORD thread_index; > }; > > +struct thread_routine_ctx { > + void* (*start_routine) (void*); > + void *routine_args; > +}; > + > /* Translates the most common error codes related to threads */ > static int > thread_translate_win32_error(DWORD error) > @@ -346,6 +351,163 @@ rte_thread_attr_set_priority(rte_thread_attr_t > *thread_attr, > return 0; > } > > +static DWORD > +thread_func_wrapper(void *args) > +{ > + struct thread_routine_ctx *pctx = args; > + intptr_t func_ret = 0; > + struct thread_routine_ctx ctx = { NULL, NULL }; > + > + ctx.start_routine = pctx->start_routine; > + ctx.routine_args = pctx->routine_args; > + > + free(pctx); > + > + func_ret = (intptr_t)ctx.start_routine(ctx.routine_args); > + return (DWORD)func_ret; > +} > + > +int > +rte_thread_create(rte_thread_t *thread_id, > + const rte_thread_attr_t *thread_attr, > + void *(*thread_func)(void *), void *args) > +{ > + int ret = 0; > + DWORD tid = 0; > + HANDLE thread_handle = NULL; > + GROUP_AFFINITY thread_affinity; > + struct thread_routine_ctx *ctx = NULL; > + > + ctx = calloc(1, sizeof(*ctx)); Why use `calloc()` for a scalar? [...] > +int > +rte_thread_cancel(rte_thread_t thread_id) > +{ > + int ret = 0; > + HANDLE thread_handle = NULL; > + > + thread_handle = OpenThread(THREAD_TERMINATE, FALSE, > thread_id.opaque_id); > + if (thread_handle == NULL) { > + ret = thread_log_last_error("OpenThread()"); > + goto cleanup; > + } > + > + /* > + * TODO: Behavior is different between POSIX and Windows threads. > + * POSIX threads wait for a cancellation point. > + * Current Windows emulation kills thread at any point. > + */ > + ret = TerminateThread(thread_handle, 0); > + if (ret != 0) { > + ret = thread_log_last_error("TerminateThread()"); > + goto cleanup; > + } > + > +cleanup: > + if (thread_handle != NULL) { > + CloseHandle(thread_handle); > + thread_handle = NULL; > + } > + return ret; > +} As we've discussed before, such implementation should never be used. I suggest removing it for now, otherwise if someone enables the code calling rte_thread_cancel() for Windows, it will almost certainly lead to deadlock bugs. > + > +int > +rte_thread_detach(rte_thread_t thread_id) > +{ > + (void)thread_id; RTE_SET_USED/__rte_unused > + return ENOTSUP; > +} > + It should return success as the thread is in detached state after the call. > int > rte_thread_key_create(rte_thread_key *key, > __rte_unused void (*destructor)(void *))