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