1 #ifndef AWS_IO_EVENT_LOOP_H 2 #define AWS_IO_EVENT_LOOP_H 3 4 /** 5 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 6 * SPDX-License-Identifier: Apache-2.0. 7 */ 8 9 #include <aws/common/atomics.h> 10 #include <aws/common/hash_table.h> 11 #include <aws/common/ref_count.h> 12 13 #include <aws/io/io.h> 14 15 enum aws_io_event_type { 16 AWS_IO_EVENT_TYPE_READABLE = 1, 17 AWS_IO_EVENT_TYPE_WRITABLE = 2, 18 AWS_IO_EVENT_TYPE_REMOTE_HANG_UP = 4, 19 AWS_IO_EVENT_TYPE_CLOSED = 8, 20 AWS_IO_EVENT_TYPE_ERROR = 16, 21 }; 22 23 struct aws_event_loop; 24 struct aws_task; 25 struct aws_thread_options; 26 27 #if AWS_USE_IO_COMPLETION_PORTS 28 # include <Windows.h> 29 30 struct aws_overlapped; 31 32 typedef void(aws_event_loop_on_completion_fn)( 33 struct aws_event_loop *event_loop, 34 struct aws_overlapped *overlapped, 35 int status_code, 36 size_t num_bytes_transferred); 37 38 /** 39 * Use aws_overlapped when a handle connected to the event loop needs an OVERLAPPED struct. 40 * OVERLAPPED structs are needed to make OS-level async I/O calls. 41 * When the I/O completes, the assigned aws_event_loop_on_completion_fn is called from the event_loop's thread. 42 * While the I/O is pending, it is not safe to modify or delete aws_overlapped. 43 * Call aws_overlapped_init() before first use. If the aws_overlapped will be used multiple times, call 44 * aws_overlapped_reset() or aws_overlapped_init() between uses. 45 */ 46 struct aws_overlapped { 47 OVERLAPPED overlapped; 48 aws_event_loop_on_completion_fn *on_completion; 49 void *user_data; 50 }; 51 52 #else /* !AWS_USE_IO_COMPLETION_PORTS */ 53 54 typedef void(aws_event_loop_on_event_fn)( 55 struct aws_event_loop *event_loop, 56 struct aws_io_handle *handle, 57 int events, 58 void *user_data); 59 60 #endif /* AWS_USE_IO_COMPLETION_PORTS */ 61 62 struct aws_event_loop_vtable { 63 void (*destroy)(struct aws_event_loop *event_loop); 64 int (*run)(struct aws_event_loop *event_loop); 65 int (*stop)(struct aws_event_loop *event_loop); 66 int (*wait_for_stop_completion)(struct aws_event_loop *event_loop); 67 void (*schedule_task_now)(struct aws_event_loop *event_loop, struct aws_task *task); 68 void (*schedule_task_future)(struct aws_event_loop *event_loop, struct aws_task *task, uint64_t run_at_nanos); 69 void (*cancel_task)(struct aws_event_loop *event_loop, struct aws_task *task); 70 #if AWS_USE_IO_COMPLETION_PORTS 71 int (*connect_to_io_completion_port)(struct aws_event_loop *event_loop, struct aws_io_handle *handle); 72 #else 73 int (*subscribe_to_io_events)( 74 struct aws_event_loop *event_loop, 75 struct aws_io_handle *handle, 76 int events, 77 aws_event_loop_on_event_fn *on_event, 78 void *user_data); 79 #endif 80 int (*unsubscribe_from_io_events)(struct aws_event_loop *event_loop, struct aws_io_handle *handle); 81 void (*free_io_event_resources)(void *user_data); 82 bool (*is_on_callers_thread)(struct aws_event_loop *event_loop); 83 }; 84 85 struct aws_event_loop { 86 struct aws_event_loop_vtable *vtable; 87 struct aws_allocator *alloc; 88 aws_io_clock_fn *clock; 89 struct aws_hash_table local_data; 90 struct aws_atomic_var current_load_factor; 91 uint64_t latest_tick_start; 92 size_t current_tick_latency_sum; 93 struct aws_atomic_var next_flush_time; 94 void *impl_data; 95 }; 96 97 struct aws_event_loop_local_object; 98 typedef void(aws_event_loop_on_local_object_removed_fn)(struct aws_event_loop_local_object *); 99 100 struct aws_event_loop_local_object { 101 const void *key; 102 void *object; 103 aws_event_loop_on_local_object_removed_fn *on_object_removed; 104 }; 105 106 struct aws_event_loop_options { 107 aws_io_clock_fn *clock; 108 struct aws_thread_options *thread_options; 109 }; 110 111 typedef struct aws_event_loop *(aws_new_event_loop_fn)( 112 struct aws_allocator *alloc, 113 const struct aws_event_loop_options *options, 114 void *new_loop_user_data); 115 116 struct aws_event_loop_group { 117 struct aws_allocator *allocator; 118 struct aws_array_list event_loops; 119 struct aws_ref_count ref_count; 120 struct aws_shutdown_callback_options shutdown_options; 121 }; 122 123 AWS_EXTERN_C_BEGIN 124 125 #ifdef AWS_USE_IO_COMPLETION_PORTS 126 /** 127 * Prepares aws_overlapped for use, and sets a function to call when the overlapped operation completes. 128 */ 129 AWS_IO_API 130 void aws_overlapped_init( 131 struct aws_overlapped *overlapped, 132 aws_event_loop_on_completion_fn *on_completion, 133 void *user_data); 134 135 /** 136 * Prepares aws_overlapped for re-use without changing the assigned aws_event_loop_on_completion_fn. 137 * Call aws_overlapped_init(), instead of aws_overlapped_reset(), to change the aws_event_loop_on_completion_fn. 138 */ 139 AWS_IO_API 140 void aws_overlapped_reset(struct aws_overlapped *overlapped); 141 #endif /* AWS_USE_IO_COMPLETION_PORTS */ 142 143 /** 144 * Creates an instance of the default event loop implementation for the current architecture and operating system. 145 */ 146 AWS_IO_API 147 struct aws_event_loop *aws_event_loop_new_default(struct aws_allocator *alloc, aws_io_clock_fn *clock); 148 149 /** 150 * Creates an instance of the default event loop implementation for the current architecture and operating system using 151 * extendable options. 152 */ 153 AWS_IO_API 154 struct aws_event_loop *aws_event_loop_new_default_with_options( 155 struct aws_allocator *alloc, 156 const struct aws_event_loop_options *options); 157 158 /** 159 * Invokes the destroy() fn for the event loop implementation. 160 * If the event loop is still in a running state, this function will block waiting on the event loop to shutdown. 161 * If you do not want this function to block, call aws_event_loop_stop() manually first. 162 * If the event loop is shared by multiple threads then destroy must be called by exactly one thread. All other threads 163 * must ensure their API calls to the event loop happen-before the call to destroy. 164 */ 165 AWS_IO_API 166 void aws_event_loop_destroy(struct aws_event_loop *event_loop); 167 168 /** 169 * Initializes common event-loop data structures. 170 * This is only called from the *new() function of event loop implementations. 171 */ 172 AWS_IO_API 173 int aws_event_loop_init_base(struct aws_event_loop *event_loop, struct aws_allocator *alloc, aws_io_clock_fn *clock); 174 175 /** 176 * Common cleanup code for all implementations. 177 * This is only called from the *destroy() function of event loop implementations. 178 */ 179 AWS_IO_API 180 void aws_event_loop_clean_up_base(struct aws_event_loop *event_loop); 181 182 /** 183 * Fetches an object from the event-loop's data store. Key will be taken as the memory address of the memory pointed to 184 * by key. This function is not thread safe and should be called inside the event-loop's thread. 185 */ 186 AWS_IO_API 187 int aws_event_loop_fetch_local_object( 188 struct aws_event_loop *event_loop, 189 void *key, 190 struct aws_event_loop_local_object *obj); 191 192 /** 193 * Puts an item object the event-loop's data store. Key will be taken as the memory address of the memory pointed to by 194 * key. The lifetime of item must live until remove or a put item overrides it. This function is not thread safe and 195 * should be called inside the event-loop's thread. 196 */ 197 AWS_IO_API 198 int aws_event_loop_put_local_object(struct aws_event_loop *event_loop, struct aws_event_loop_local_object *obj); 199 200 /** 201 * Removes an object from the event-loop's data store. Key will be taken as the memory address of the memory pointed to 202 * by key. If removed_item is not null, the removed item will be moved to it if it exists. Otherwise, the default 203 * deallocation strategy will be used. This function is not thread safe and should be called inside the event-loop's 204 * thread. 205 */ 206 AWS_IO_API 207 int aws_event_loop_remove_local_object( 208 struct aws_event_loop *event_loop, 209 void *key, 210 struct aws_event_loop_local_object *removed_obj); 211 212 /** 213 * Triggers the running of the event loop. This function must not block. The event loop is not active until this 214 * function is invoked. This function can be called again on an event loop after calling aws_event_loop_stop() and 215 * aws_event_loop_wait_for_stop_completion(). 216 */ 217 AWS_IO_API 218 int aws_event_loop_run(struct aws_event_loop *event_loop); 219 220 /** 221 * Triggers the event loop to stop, but does not wait for the loop to stop completely. 222 * This function may be called from outside or inside the event loop thread. It is safe to call multiple times. 223 * This function is called from destroy(). 224 * 225 * If you do not call destroy(), an event loop can be run again by calling stop(), wait_for_stop_completion(), run(). 226 */ 227 AWS_IO_API 228 int aws_event_loop_stop(struct aws_event_loop *event_loop); 229 230 /** 231 * For event-loop implementations to use for providing metrics info to the base event-loop. This enables the 232 * event-loop load balancer to take into account load when vending another event-loop to a caller. 233 * 234 * Call this function at the beginning of your event-loop tick: after wake-up, but before processing any IO or tasks. 235 */ 236 AWS_IO_API 237 void aws_event_loop_register_tick_start(struct aws_event_loop *event_loop); 238 239 /** 240 * For event-loop implementations to use for providing metrics info to the base event-loop. This enables the 241 * event-loop load balancer to take into account load when vending another event-loop to a caller. 242 * 243 * Call this function at the end of your event-loop tick: after processing IO and tasks. 244 */ 245 AWS_IO_API 246 void aws_event_loop_register_tick_end(struct aws_event_loop *event_loop); 247 248 /** 249 * Returns the current load factor (however that may be calculated). If the event-loop is not invoking 250 * aws_event_loop_register_tick_start() and aws_event_loop_register_tick_end(), this value will always be 0. 251 */ 252 AWS_IO_API 253 size_t aws_event_loop_get_load_factor(struct aws_event_loop *event_loop); 254 255 /** 256 * Blocks until the event loop stops completely. 257 * If you want to call aws_event_loop_run() again, you must call this after aws_event_loop_stop(). 258 * It is not safe to call this function from inside the event loop thread. 259 */ 260 AWS_IO_API 261 int aws_event_loop_wait_for_stop_completion(struct aws_event_loop *event_loop); 262 263 /** 264 * The event loop will schedule the task and run it on the event loop thread as soon as possible. 265 * Note that cancelled tasks may execute outside the event loop thread. 266 * This function may be called from outside or inside the event loop thread. 267 * 268 * The task should not be cleaned up or modified until its function is executed. 269 */ 270 AWS_IO_API 271 void aws_event_loop_schedule_task_now(struct aws_event_loop *event_loop, struct aws_task *task); 272 273 /** 274 * The event loop will schedule the task and run it at the specified time. 275 * Use aws_event_loop_current_clock_time() to query the current time in nanoseconds. 276 * Note that cancelled tasks may execute outside the event loop thread. 277 * This function may be called from outside or inside the event loop thread. 278 * 279 * The task should not be cleaned up or modified until its function is executed. 280 */ 281 AWS_IO_API 282 void aws_event_loop_schedule_task_future( 283 struct aws_event_loop *event_loop, 284 struct aws_task *task, 285 uint64_t run_at_nanos); 286 287 /** 288 * Cancels task. 289 * This function must be called from the event loop's thread, and is only guaranteed 290 * to work properly on tasks scheduled from within the event loop's thread. 291 * The task will be executed with the AWS_TASK_STATUS_CANCELED status inside this call. 292 */ 293 AWS_IO_API 294 void aws_event_loop_cancel_task(struct aws_event_loop *event_loop, struct aws_task *task); 295 296 #if AWS_USE_IO_COMPLETION_PORTS 297 298 /** 299 * Associates an aws_io_handle with the event loop's I/O Completion Port. 300 * 301 * The handle must use aws_overlapped for all async operations requiring an OVERLAPPED struct. 302 * When the operation completes, the aws_overlapped's completion function will run on the event loop thread. 303 * Note that completion functions will not be invoked while the event loop is stopped. Users should wait for all async 304 * operations on connected handles to complete before cleaning up or destroying the event loop. 305 * 306 * A handle may only be connected to one event loop in its lifetime. 307 */ 308 AWS_IO_API 309 int aws_event_loop_connect_handle_to_io_completion_port( 310 struct aws_event_loop *event_loop, 311 struct aws_io_handle *handle); 312 313 #else /* !AWS_USE_IO_COMPLETION_PORTS */ 314 315 /** 316 * Subscribes on_event to events on the event-loop for handle. events is a bitwise concatenation of the events that were 317 * received. The definition for these values can be found in aws_io_event_type. Currently, only 318 * AWS_IO_EVENT_TYPE_READABLE and AWS_IO_EVENT_TYPE_WRITABLE are honored. You always are registered for error conditions 319 * and closure. This function may be called from outside or inside the event loop thread. However, the unsubscribe 320 * function must be called inside the event-loop's thread. 321 */ 322 AWS_IO_API 323 int aws_event_loop_subscribe_to_io_events( 324 struct aws_event_loop *event_loop, 325 struct aws_io_handle *handle, 326 int events, 327 aws_event_loop_on_event_fn *on_event, 328 void *user_data); 329 330 #endif /* AWS_USE_IO_COMPLETION_PORTS */ 331 332 /** 333 * Unsubscribes handle from event-loop notifications. 334 * This function is not thread safe and should be called inside the event-loop's thread. 335 * 336 * NOTE: if you are using io completion ports, this is a risky call. We use it in places, but only when we're certain 337 * there's no pending events. If you want to use it, it's your job to make sure you don't have pending events before 338 * calling it. 339 */ 340 AWS_IO_API 341 int aws_event_loop_unsubscribe_from_io_events(struct aws_event_loop *event_loop, struct aws_io_handle *handle); 342 343 /** 344 * Cleans up resources (user_data) associated with the I/O eventing subsystem for a given handle. This should only 345 * ever be necessary in the case where you are cleaning up an event loop during shutdown and its thread has already 346 * been joined. 347 */ 348 AWS_IO_API 349 void aws_event_loop_free_io_event_resources(struct aws_event_loop *event_loop, struct aws_io_handle *handle); 350 351 /** 352 * Returns true if the event loop's thread is the same thread that called this function, otherwise false. 353 */ 354 AWS_IO_API 355 bool aws_event_loop_thread_is_callers_thread(struct aws_event_loop *event_loop); 356 357 /** 358 * Gets the current timestamp for the event loop's clock, in nanoseconds. This function is thread-safe. 359 */ 360 AWS_IO_API 361 int aws_event_loop_current_clock_time(struct aws_event_loop *event_loop, uint64_t *time_nanos); 362 363 /** 364 * Creates an event loop group, with clock, number of loops to manage, and the function to call for creating a new 365 * event loop. 366 */ 367 AWS_IO_API 368 struct aws_event_loop_group *aws_event_loop_group_new( 369 struct aws_allocator *alloc, 370 aws_io_clock_fn *clock, 371 uint16_t el_count, 372 aws_new_event_loop_fn *new_loop_fn, 373 void *new_loop_user_data, 374 const struct aws_shutdown_callback_options *shutdown_options); 375 376 /** Creates an event loop group, with clock, number of loops to manage, the function to call for creating a new 377 * event loop, and also pins all loops to hw threads on the same cpu_group (e.g. NUMA nodes). Note: 378 * If el_count exceeds the number of hw threads in the cpu_group it will be ignored on the assumption that if you 379 * care about NUMA, you don't want hyper-threads doing your IO and you especially don't want IO on a different node. 380 */ 381 AWS_IO_API 382 struct aws_event_loop_group *aws_event_loop_group_new_pinned_to_cpu_group( 383 struct aws_allocator *alloc, 384 aws_io_clock_fn *clock, 385 uint16_t el_count, 386 uint16_t cpu_group, 387 aws_new_event_loop_fn *new_loop_fn, 388 void *new_loop_user_data, 389 const struct aws_shutdown_callback_options *shutdown_options); 390 391 /** 392 * Initializes an event loop group with platform defaults. If max_threads == 0, then the 393 * loop count will be the number of available processors on the machine / 2 (to exclude hyper-threads). 394 * Otherwise, max_threads will be the number of event loops in the group. 395 */ 396 AWS_IO_API 397 struct aws_event_loop_group *aws_event_loop_group_new_default( 398 struct aws_allocator *alloc, 399 uint16_t max_threads, 400 const struct aws_shutdown_callback_options *shutdown_options); 401 402 /** Creates an event loop group, with clock, number of loops to manage, the function to call for creating a new 403 * event loop, and also pins all loops to hw threads on the same cpu_group (e.g. NUMA nodes). Note: 404 * If el_count exceeds the number of hw threads in the cpu_group it will be clamped to the number of hw threads 405 * on the assumption that if you care about NUMA, you don't want hyper-threads doing your IO and you especially 406 * don't want IO on a different node. 407 * 408 * If max_threads == 0, then the 409 * loop count will be the number of available processors in the cpu_group / 2 (to exclude hyper-threads) 410 */ 411 AWS_IO_API 412 struct aws_event_loop_group *aws_event_loop_group_new_default_pinned_to_cpu_group( 413 struct aws_allocator *alloc, 414 uint16_t max_threads, 415 uint16_t cpu_group, 416 const struct aws_shutdown_callback_options *shutdown_options); 417 418 /** 419 * Increments the reference count on the event loop group, allowing the caller to take a reference to it. 420 * 421 * Returns the same event loop group passed in. 422 */ 423 AWS_IO_API 424 struct aws_event_loop_group *aws_event_loop_group_acquire(struct aws_event_loop_group *el_group); 425 426 /** 427 * Decrements an event loop group's ref count. When the ref count drops to zero, the event loop group will be 428 * destroyed. 429 */ 430 AWS_IO_API 431 void aws_event_loop_group_release(struct aws_event_loop_group *el_group); 432 433 AWS_IO_API 434 struct aws_event_loop *aws_event_loop_group_get_loop_at(struct aws_event_loop_group *el_group, size_t index); 435 436 AWS_IO_API 437 size_t aws_event_loop_group_get_loop_count(struct aws_event_loop_group *el_group); 438 439 /** 440 * Fetches the next loop for use. The purpose is to enable load balancing across loops. You should not depend on how 441 * this load balancing is done as it is subject to change in the future. Currently it uses the "best-of-two" algorithm 442 * based on the load factor of each loop. 443 */ 444 AWS_IO_API 445 struct aws_event_loop *aws_event_loop_group_get_next_loop(struct aws_event_loop_group *el_group); 446 447 AWS_EXTERN_C_END 448 449 #endif /* AWS_IO_EVENT_LOOP_H */ 450