|
| 1 | +#ifndef AWS_IO_EVENT_LOOP_IMPL_H |
| 2 | +#define AWS_IO_EVENT_LOOP_IMPL_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/io/io.h> |
| 10 | + |
| 11 | +#include <aws/common/atomics.h> |
| 12 | +#include <aws/common/hash_table.h> |
| 13 | +#include <aws/common/ref_count.h> |
| 14 | +#include <aws/io/event_loop.h> |
| 15 | + |
| 16 | +AWS_PUSH_SANE_WARNING_LEVEL |
| 17 | + |
| 18 | +struct aws_event_loop; |
| 19 | +struct aws_overlapped; |
| 20 | + |
| 21 | +typedef void(aws_event_loop_on_completion_fn)( |
| 22 | + struct aws_event_loop *event_loop, |
| 23 | + struct aws_overlapped *overlapped, |
| 24 | + int status_code, |
| 25 | + size_t num_bytes_transferred); |
| 26 | + |
| 27 | +/** |
| 28 | + * The aws_win32_OVERLAPPED struct is layout-compatible with OVERLAPPED as defined in <Windows.h>. It is used |
| 29 | + * here to avoid pulling in a dependency on <Windows.h> which would also bring along a lot of bad macros, such |
| 30 | + * as redefinitions of GetMessage and GetObject. Note that the OVERLAPPED struct layout in the Windows SDK can |
| 31 | + * never be altered without breaking binary compatibility for every existing third-party executable, so there |
| 32 | + * is no need to worry about keeping this definition in sync. |
| 33 | + */ |
| 34 | +struct aws_win32_OVERLAPPED { |
| 35 | + uintptr_t Internal; |
| 36 | + uintptr_t InternalHigh; |
| 37 | + union { |
| 38 | + struct { |
| 39 | + uint32_t Offset; |
| 40 | + uint32_t OffsetHigh; |
| 41 | + } s; |
| 42 | + void *Pointer; |
| 43 | + } u; |
| 44 | + void *hEvent; |
| 45 | +}; |
| 46 | + |
| 47 | +/** |
| 48 | + * Use aws_overlapped when a handle connected to the event loop needs an OVERLAPPED struct. |
| 49 | + * OVERLAPPED structs are needed to make OS-level async I/O calls. |
| 50 | + * When the I/O completes, the assigned aws_event_loop_on_completion_fn is called from the event_loop's thread. |
| 51 | + * While the I/O is pending, it is not safe to modify or delete aws_overlapped. |
| 52 | + * Call aws_overlapped_init() before first use. If the aws_overlapped will be used multiple times, call |
| 53 | + * aws_overlapped_reset() or aws_overlapped_init() between uses. |
| 54 | + */ |
| 55 | +struct aws_overlapped { |
| 56 | + struct aws_win32_OVERLAPPED overlapped; |
| 57 | + aws_event_loop_on_completion_fn *on_completion; |
| 58 | + void *user_data; |
| 59 | +}; |
| 60 | + |
| 61 | +enum aws_io_event_type { |
| 62 | + AWS_IO_EVENT_TYPE_READABLE = 1, |
| 63 | + AWS_IO_EVENT_TYPE_WRITABLE = 2, |
| 64 | + AWS_IO_EVENT_TYPE_REMOTE_HANG_UP = 4, |
| 65 | + AWS_IO_EVENT_TYPE_CLOSED = 8, |
| 66 | + AWS_IO_EVENT_TYPE_ERROR = 16, |
| 67 | +}; |
| 68 | + |
| 69 | +struct aws_event_loop { |
| 70 | + struct aws_event_loop_vtable *vtable; |
| 71 | + struct aws_allocator *alloc; |
| 72 | + aws_io_clock_fn *clock; |
| 73 | + struct aws_hash_table local_data; |
| 74 | + struct aws_atomic_var current_load_factor; |
| 75 | + uint64_t latest_tick_start; |
| 76 | + size_t current_tick_latency_sum; |
| 77 | + struct aws_atomic_var next_flush_time; |
| 78 | + void *impl_data; |
| 79 | +}; |
| 80 | + |
| 81 | +struct aws_event_loop_local_object; |
| 82 | +typedef void(aws_event_loop_on_local_object_removed_fn)(struct aws_event_loop_local_object *); |
| 83 | + |
| 84 | +struct aws_event_loop_local_object { |
| 85 | + const void *key; |
| 86 | + void *object; |
| 87 | + aws_event_loop_on_local_object_removed_fn *on_object_removed; |
| 88 | +}; |
| 89 | + |
| 90 | +struct aws_event_loop_options { |
| 91 | + aws_io_clock_fn *clock; |
| 92 | + struct aws_thread_options *thread_options; |
| 93 | +}; |
| 94 | + |
| 95 | +typedef struct aws_event_loop *(aws_new_event_loop_fn)(struct aws_allocator *alloc, |
| 96 | + const struct aws_event_loop_options *options, |
| 97 | + void *new_loop_user_data); |
| 98 | + |
| 99 | +struct aws_event_loop_group { |
| 100 | + struct aws_allocator *allocator; |
| 101 | + struct aws_array_list event_loops; |
| 102 | + struct aws_ref_count ref_count; |
| 103 | + struct aws_shutdown_callback_options shutdown_options; |
| 104 | +}; |
| 105 | + |
| 106 | +AWS_EXTERN_C_BEGIN |
| 107 | + |
| 108 | +#ifdef AWS_USE_IO_COMPLETION_PORTS |
| 109 | + |
| 110 | +/** |
| 111 | + * Prepares aws_overlapped for use, and sets a function to call when the overlapped operation completes. |
| 112 | + */ |
| 113 | +AWS_IO_API |
| 114 | +void aws_overlapped_init( |
| 115 | + struct aws_overlapped *overlapped, |
| 116 | + aws_event_loop_on_completion_fn *on_completion, |
| 117 | + void *user_data); |
| 118 | + |
| 119 | +/** |
| 120 | + * Prepares aws_overlapped for re-use without changing the assigned aws_event_loop_on_completion_fn. |
| 121 | + * Call aws_overlapped_init(), instead of aws_overlapped_reset(), to change the aws_event_loop_on_completion_fn. |
| 122 | + */ |
| 123 | +AWS_IO_API |
| 124 | +void aws_overlapped_reset(struct aws_overlapped *overlapped); |
| 125 | + |
| 126 | +/** |
| 127 | + * Casts an aws_overlapped pointer for use as a LPOVERLAPPED parameter to Windows API functions |
| 128 | + */ |
| 129 | +AWS_IO_API |
| 130 | +struct _OVERLAPPED *aws_overlapped_to_windows_overlapped(struct aws_overlapped *overlapped); |
| 131 | + |
| 132 | +/** |
| 133 | + * Associates an aws_io_handle with the event loop's I/O Completion Port. |
| 134 | + * |
| 135 | + * The handle must use aws_overlapped for all async operations requiring an OVERLAPPED struct. |
| 136 | + * When the operation completes, the aws_overlapped's completion function will run on the event loop thread. |
| 137 | + * Note that completion functions will not be invoked while the event loop is stopped. Users should wait for all async |
| 138 | + * operations on connected handles to complete before cleaning up or destroying the event loop. |
| 139 | + * |
| 140 | + * A handle may only be connected to one event loop in its lifetime. |
| 141 | + */ |
| 142 | +AWS_IO_API |
| 143 | +int aws_event_loop_connect_handle_to_io_completion_port( |
| 144 | + struct aws_event_loop *event_loop, |
| 145 | + struct aws_io_handle *handle); |
| 146 | + |
| 147 | +#else |
| 148 | + |
| 149 | +/** |
| 150 | + * Subscribes on_event to events on the event-loop for handle. events is a bitwise concatenation of the events that were |
| 151 | + * received. The definition for these values can be found in aws_io_event_type. Currently, only |
| 152 | + * AWS_IO_EVENT_TYPE_READABLE and AWS_IO_EVENT_TYPE_WRITABLE are honored. You always are registered for error conditions |
| 153 | + * and closure. This function may be called from outside or inside the event loop thread. However, the unsubscribe |
| 154 | + * function must be called inside the event-loop's thread. |
| 155 | + */ |
| 156 | +AWS_IO_API |
| 157 | +int aws_event_loop_subscribe_to_io_events( |
| 158 | + struct aws_event_loop *event_loop, |
| 159 | + struct aws_io_handle *handle, |
| 160 | + int events, |
| 161 | + aws_event_loop_on_event_fn *on_event, |
| 162 | + void *user_data); |
| 163 | + |
| 164 | +#endif /* AWS_USE_IO_COMPLETION_PORTS */ |
| 165 | + |
| 166 | +/** |
| 167 | + * Creates an instance of the default event loop implementation for the current architecture and operating system. |
| 168 | + */ |
| 169 | +AWS_IO_API |
| 170 | +struct aws_event_loop *aws_event_loop_new_default(struct aws_allocator *alloc, aws_io_clock_fn *clock); |
| 171 | + |
| 172 | +/** |
| 173 | + * Creates an instance of the default event loop implementation for the current architecture and operating system using |
| 174 | + * extendable options. |
| 175 | + */ |
| 176 | +AWS_IO_API |
| 177 | +struct aws_event_loop *aws_event_loop_new_default_with_options( |
| 178 | + struct aws_allocator *alloc, |
| 179 | + const struct aws_event_loop_options *options); |
| 180 | + |
| 181 | +/** |
| 182 | + * Initializes common event-loop data structures. |
| 183 | + * This is only called from the *new() function of event loop implementations. |
| 184 | + */ |
| 185 | +AWS_IO_API |
| 186 | +int aws_event_loop_init_base(struct aws_event_loop *event_loop, struct aws_allocator *alloc, aws_io_clock_fn *clock); |
| 187 | + |
| 188 | +/** |
| 189 | + * Fetches an object from the event-loop's data store. Key will be taken as the memory address of the memory pointed to |
| 190 | + * by key. This function is not thread safe and should be called inside the event-loop's thread. |
| 191 | + */ |
| 192 | +AWS_IO_API |
| 193 | +int aws_event_loop_fetch_local_object( |
| 194 | + struct aws_event_loop *event_loop, |
| 195 | + void *key, |
| 196 | + struct aws_event_loop_local_object *obj); |
| 197 | + |
| 198 | +/** |
| 199 | + * Puts an item object the event-loop's data store. Key will be taken as the memory address of the memory pointed to by |
| 200 | + * key. The lifetime of item must live until remove or a put item overrides it. This function is not thread safe and |
| 201 | + * should be called inside the event-loop's thread. |
| 202 | + */ |
| 203 | +AWS_IO_API |
| 204 | +int aws_event_loop_put_local_object(struct aws_event_loop *event_loop, struct aws_event_loop_local_object *obj); |
| 205 | + |
| 206 | +/** |
| 207 | + * Removes an object from the event-loop's data store. Key will be taken as the memory address of the memory pointed to |
| 208 | + * by key. If removed_item is not null, the removed item will be moved to it if it exists. Otherwise, the default |
| 209 | + * deallocation strategy will be used. This function is not thread safe and should be called inside the event-loop's |
| 210 | + * thread. |
| 211 | + */ |
| 212 | +AWS_IO_API |
| 213 | +int aws_event_loop_remove_local_object( |
| 214 | + struct aws_event_loop *event_loop, |
| 215 | + void *key, |
| 216 | + struct aws_event_loop_local_object *removed_obj); |
| 217 | + |
| 218 | +/** |
| 219 | + * Triggers the running of the event loop. This function must not block. The event loop is not active until this |
| 220 | + * function is invoked. This function can be called again on an event loop after calling aws_event_loop_stop() and |
| 221 | + * aws_event_loop_wait_for_stop_completion(). |
| 222 | + */ |
| 223 | +AWS_IO_API |
| 224 | +int aws_event_loop_run(struct aws_event_loop *event_loop); |
| 225 | + |
| 226 | +/** |
| 227 | + * Triggers the event loop to stop, but does not wait for the loop to stop completely. |
| 228 | + * This function may be called from outside or inside the event loop thread. It is safe to call multiple times. |
| 229 | + * This function is called from destroy(). |
| 230 | + * |
| 231 | + * If you do not call destroy(), an event loop can be run again by calling stop(), wait_for_stop_completion(), run(). |
| 232 | + */ |
| 233 | +AWS_IO_API |
| 234 | +int aws_event_loop_stop(struct aws_event_loop *event_loop); |
| 235 | + |
| 236 | +/** |
| 237 | + * For event-loop implementations to use for providing metrics info to the base event-loop. This enables the |
| 238 | + * event-loop load balancer to take into account load when vending another event-loop to a caller. |
| 239 | + * |
| 240 | + * Call this function at the beginning of your event-loop tick: after wake-up, but before processing any IO or tasks. |
| 241 | + */ |
| 242 | +AWS_IO_API |
| 243 | +void aws_event_loop_register_tick_start(struct aws_event_loop *event_loop); |
| 244 | + |
| 245 | +/** |
| 246 | + * For event-loop implementations to use for providing metrics info to the base event-loop. This enables the |
| 247 | + * event-loop load balancer to take into account load when vending another event-loop to a caller. |
| 248 | + * |
| 249 | + * Call this function at the end of your event-loop tick: after processing IO and tasks. |
| 250 | + */ |
| 251 | +AWS_IO_API |
| 252 | +void aws_event_loop_register_tick_end(struct aws_event_loop *event_loop); |
| 253 | + |
| 254 | +/** |
| 255 | + * Returns the current load factor (however that may be calculated). If the event-loop is not invoking |
| 256 | + * aws_event_loop_register_tick_start() and aws_event_loop_register_tick_end(), this value will always be 0. |
| 257 | + */ |
| 258 | +AWS_IO_API |
| 259 | +size_t aws_event_loop_get_load_factor(struct aws_event_loop *event_loop); |
| 260 | + |
| 261 | +/** |
| 262 | + * Blocks until the event loop stops completely. |
| 263 | + * If you want to call aws_event_loop_run() again, you must call this after aws_event_loop_stop(). |
| 264 | + * It is not safe to call this function from inside the event loop thread. |
| 265 | + */ |
| 266 | +AWS_IO_API |
| 267 | +int aws_event_loop_wait_for_stop_completion(struct aws_event_loop *event_loop); |
| 268 | + |
| 269 | +/** |
| 270 | + * Unsubscribes handle from event-loop notifications. |
| 271 | + * This function is not thread safe and should be called inside the event-loop's thread. |
| 272 | + * |
| 273 | + * NOTE: if you are using io completion ports, this is a risky call. We use it in places, but only when we're certain |
| 274 | + * 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 |
| 275 | + * calling it. |
| 276 | + */ |
| 277 | +AWS_IO_API |
| 278 | +int aws_event_loop_unsubscribe_from_io_events(struct aws_event_loop *event_loop, struct aws_io_handle *handle); |
| 279 | + |
| 280 | +/** |
| 281 | + * Cleans up resources (user_data) associated with the I/O eventing subsystem for a given handle. This should only |
| 282 | + * ever be necessary in the case where you are cleaning up an event loop during shutdown and its thread has already |
| 283 | + * been joined. |
| 284 | + */ |
| 285 | +AWS_IO_API |
| 286 | +void aws_event_loop_free_io_event_resources(struct aws_event_loop *event_loop, struct aws_io_handle *handle); |
| 287 | + |
| 288 | +AWS_IO_API |
| 289 | +struct aws_event_loop_group *aws_event_loop_group_new_internal( |
| 290 | + struct aws_allocator *allocator, |
| 291 | + const struct aws_event_loop_group_options *options, |
| 292 | + aws_new_event_loop_fn *new_loop_fn, |
| 293 | + void *new_loop_user_data); |
| 294 | + |
| 295 | +AWS_EXTERN_C_END |
| 296 | + |
| 297 | +AWS_POP_SANE_WARNING_LEVEL |
| 298 | + |
| 299 | +#endif /* AWS_IO_EVENT_LOOP_IMPL_H */ |
0 commit comments