#ifndef AWS_COMMON_THREAD_H #define AWS_COMMON_THREAD_H /** * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0. */ #include #ifndef _WIN32 # include #endif enum aws_thread_detach_state { AWS_THREAD_NOT_CREATED = 1, AWS_THREAD_JOINABLE, AWS_THREAD_JOIN_COMPLETED, AWS_THREAD_MANAGED, }; /** * Specifies the join strategy used on an aws_thread, which in turn controls whether or not a thread participates * in the managed thread system. The managed thread system provides logic to guarantee a join on all participating * threads at the cost of laziness (the user cannot control when joins happen). * * Manual - thread does not particpate in the managed thread system; any joins must be done by the user. This * is the default. * * Managed - the managed thread system will automatically perform a join some time after the thread's run function * has completed. It is an error to call aws_thread_join on a thread configured with the managed join strategy. * * Additionally, an API exists, aws_thread_join_all_managed(), which blocks and returns when all outstanding threads * with the managed strategy have fully joined. This API is useful for tests (rather than waiting for many individual * signals) and program shutdown or DLL unload. This API is automatically invoked by the common library clean up * function. If the common library clean up is called from a managed thread, this will cause deadlock. * * Lazy thread joining is done only when threads finish their run function or when the user calls * aws_thread_join_all_managed(). This means it may be a long time between thread function completion and the join * being applied, but the queue of unjoined threads is always one or fewer so there is no critical resource * backlog. * * Currently, only event loop group async cleanup and host resolver threads participate in the managed thread system. * Additionally, event loop threads will increment and decrement the pending join count (they are manually joined * internally) in order to have an accurate view of internal thread usage and also to prevent failure to release * an event loop group fully from allowing aws_thread_join_all_managed() from running to completion when its * intent is such that it should block instead. */ enum aws_thread_join_strategy { AWS_TJS_MANUAL = 0, AWS_TJS_MANAGED, }; struct aws_thread_options { size_t stack_size; /* default is -1. If you set this to anything >= 0, and the platform supports it, the thread will be pinned to * that cpu. Also, we assume you're doing this for memory throughput purposes. On unix systems, * If libnuma.so is available, upon the thread launching, the memory policy for that thread will be set to * allocate on the numa node that cpu-core is on. * * On windows, this will cause the thread affinity to be set, but currently we don't do anything to tell the OS * how to allocate memory on a node. * * On Apple and Android platforms, this setting doesn't do anything at all. */ int32_t cpu_id; enum aws_thread_join_strategy join_strategy; }; #ifdef _WIN32 typedef union { void *ptr; } aws_thread_once; # define AWS_THREAD_ONCE_STATIC_INIT \ { NULL } typedef unsigned long aws_thread_id_t; #else typedef pthread_once_t aws_thread_once; # define AWS_THREAD_ONCE_STATIC_INIT PTHREAD_ONCE_INIT typedef pthread_t aws_thread_id_t; #endif /* * Buffer size needed to represent aws_thread_id_t as a string (2 hex chars per byte * plus '\0' terminator). Needed for portable printing because pthread_t is * opaque. */ #define AWS_THREAD_ID_T_REPR_BUFSZ (sizeof(aws_thread_id_t) * 2 + 1) struct aws_thread { struct aws_allocator *allocator; enum aws_thread_detach_state detach_state; #ifdef _WIN32 void *thread_handle; #endif aws_thread_id_t thread_id; }; AWS_EXTERN_C_BEGIN /** * Returns an instance of system default thread options. */ AWS_COMMON_API const struct aws_thread_options *aws_default_thread_options(void); AWS_COMMON_API void aws_thread_call_once(aws_thread_once *flag, void (*call_once)(void *), void *user_data); /** * Initializes a new platform specific thread object struct (not the os-level * thread itself). */ AWS_COMMON_API int aws_thread_init(struct aws_thread *thread, struct aws_allocator *allocator); /** * Creates an OS level thread and associates it with func. context will be passed to func when it is executed. * options will be applied to the thread if they are applicable for the platform. * You must either call join or detach after creating the thread and before calling clean_up. */ AWS_COMMON_API int aws_thread_launch( struct aws_thread *thread, void (*func)(void *arg), void *arg, const struct aws_thread_options *options); /** * Gets the id of thread */ AWS_COMMON_API aws_thread_id_t aws_thread_get_id(struct aws_thread *thread); /** * Gets the detach state of the thread. For example, is it safe to call join on * this thread? Has it been detached()? */ AWS_COMMON_API enum aws_thread_detach_state aws_thread_get_detach_state(struct aws_thread *thread); /** * Joins the calling thread to a thread instance. Returns when thread is * finished. */ AWS_COMMON_API int aws_thread_join(struct aws_thread *thread); /** * Blocking call that waits for all managed threads to complete their join call. This can only be called * from the main thread or a non-managed thread. * * This gets called automatically from library cleanup. * * By default the wait is unbounded, but that default can be overridden via aws_thread_set_managed_join_timeout_ns() */ AWS_COMMON_API int aws_thread_join_all_managed(void); /** * Overrides how long, in nanoseconds, that aws_thread_join_all_managed will wait for threads to complete. * A value of zero will result in an unbounded wait. */ AWS_COMMON_API void aws_thread_set_managed_join_timeout_ns(uint64_t timeout_in_ns); /** * Cleans up the thread handle. Either detach or join must be called * before calling this function. */ AWS_COMMON_API void aws_thread_clean_up(struct aws_thread *thread); /** * Returns the thread id of the calling thread. */ AWS_COMMON_API aws_thread_id_t aws_thread_current_thread_id(void); /** * Compare thread ids. */ AWS_COMMON_API bool aws_thread_thread_id_equal(aws_thread_id_t t1, aws_thread_id_t t2); /** * Sleeps the current thread by nanos. */ AWS_COMMON_API void aws_thread_current_sleep(uint64_t nanos); typedef void(aws_thread_atexit_fn)(void *user_data); /** * Adds a callback to the chain to be called when the current thread joins. * Callbacks are called from the current thread, in the reverse order they * were added, after the thread function returns. * If not called from within an aws_thread, has no effect. */ AWS_COMMON_API int aws_thread_current_at_exit(aws_thread_atexit_fn *callback, void *user_data); /** * Increments the count of unjoined threads in the managed thread system. Used by managed threads and * event loop threads. Additional usage requires the user to join corresponding threads themselves and * correctly increment/decrement even in the face of launch/join errors. * * aws_thread_join_all_managed() will not return until this count has gone to zero. */ AWS_COMMON_API void aws_thread_increment_unjoined_count(void); /** * Decrements the count of unjoined threads in the managed thread system. Used by managed threads and * event loop threads. Additional usage requires the user to join corresponding threads themselves and * correctly increment/decrement even in the face of launch/join errors. * * aws_thread_join_all_managed() will not return until this count has gone to zero. */ AWS_COMMON_API void aws_thread_decrement_unjoined_count(void); AWS_EXTERN_C_END #endif /* AWS_COMMON_THREAD_H */