diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f9774c160..d0e25f7f8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -158,12 +158,16 @@ jobs: macos: runs-on: macos-14 # latest + strategy: + fail-fast: false + matrix: + eventloop: ["-DAWS_USE_APPLE_NETWORK_FRAMEWORK=ON", "-DAWS_USE_APPLE_NETWORK_FRAMEWORK=OFF"] steps: - name: Build ${{ env.PACKAGE_NAME }} + consumers run: | python3 -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz?run=${{ env.RUN }}', 'builder')" chmod a+x builder - ./builder build -p ${{ env.PACKAGE_NAME }} + ./builder build -p ${{ env.PACKAGE_NAME }} --cmake-extra=${{ matrix.eventloop }} macos-x64: runs-on: macos-14-large # latest @@ -176,12 +180,16 @@ jobs: macos-debug: runs-on: macos-14 # latest + strategy: + fail-fast: false + matrix: + eventloop: ["-DAWS_USE_APPLE_NETWORK_FRAMEWORK=ON", "-DAWS_USE_APPLE_NETWORK_FRAMEWORK=OFF"] steps: - name: Build ${{ env.PACKAGE_NAME }} + consumers run: | python3 -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz?run=${{ env.RUN }}', 'builder')" chmod a+x builder - ./builder build -p ${{ env.PACKAGE_NAME }} --config Debug + ./builder build -p ${{ env.PACKAGE_NAME }} --cmake-extra=${{ matrix.eventloop }} --config Debug freebsd: runs-on: ubuntu-22.04 # latest diff --git a/CMakeLists.txt b/CMakeLists.txt index c0f030b98..ba759dc21 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,7 +75,7 @@ if (WIN32) ) list(APPEND AWS_IO_OS_SRC ${AWS_IO_IOCP_SRC}) - set(EVENT_LOOP_DEFINE "IO_COMPLETION_PORTS") + list(APPEND EVENT_LOOP_DEFINES "IO_COMPLETION_PORTS") endif () if (MSVC) @@ -102,7 +102,7 @@ elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "Androi ) set(PLATFORM_LIBS "") - set(EVENT_LOOP_DEFINE "EPOLL") + list(APPEND EVENT_LOOP_DEFINES "EPOLL") set(USE_S2N ON) elseif (APPLE) @@ -117,13 +117,18 @@ elseif (APPLE) ) find_library(SECURITY_LIB Security) - if (NOT SECURITY_LIB) - message(FATAL_ERROR "Security framework not found") + find_library(NETWORK_LIB Network) + + # Enable dispatch queue if the libraries are avaliable + if (NETWORK_LIB AND SECURITY_LIB) + list(APPEND PLATFORM_LIBS "-framework Security -framework Network") + list(APPEND EVENT_LOOP_DEFINES "DISPATCH_QUEUE") endif () - #No choice on TLS for apple, darwinssl will always be used. - list(APPEND PLATFORM_LIBS "-framework Security") - set(EVENT_LOOP_DEFINE "KQUEUE") + # Enable KQUEUE on MacOS + if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + list(APPEND EVENT_LOOP_DEFINES "KQUEUE") + endif() elseif (CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR CMAKE_SYSTEM_NAME STREQUAL "NetBSD" OR CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") file(GLOB AWS_IO_OS_HEADERS @@ -134,7 +139,7 @@ elseif (CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR CMAKE_SYSTEM_NAME STREQUAL "NetB "source/posix/*.c" ) - set(EVENT_LOOP_DEFINE "KQUEUE") + list(APPEND EVENT_LOOP_DEFINES "KQUEUE") set(USE_S2N ON) endif() @@ -187,7 +192,12 @@ aws_add_sanitizers(${PROJECT_NAME}) # We are not ABI stable yet set_target_properties(${PROJECT_NAME} PROPERTIES VERSION 1.0.0) -target_compile_definitions(${PROJECT_NAME} PUBLIC "-DAWS_USE_${EVENT_LOOP_DEFINE}") +if (NOT EVENT_LOOP_DEFINES) + message(FATAL_ERROR "Event Loop is not setup on the platform.") +endif() +foreach(EVENT_LOOP_DEFINE IN LISTS EVENT_LOOP_DEFINES) + target_compile_definitions(${PROJECT_NAME} PUBLIC "-DAWS_ENABLE_${EVENT_LOOP_DEFINE}") +endforeach() if (BYO_CRYPTO) target_compile_definitions(${PROJECT_NAME} PUBLIC "-DBYO_CRYPTO") @@ -205,6 +215,10 @@ if (USE_VSOCK) target_compile_definitions(${PROJECT_NAME} PUBLIC "-DUSE_VSOCK") endif() +if (AWS_USE_APPLE_NETWORK_FRAMEWORK) + target_compile_definitions(${PROJECT_NAME} PUBLIC "-DAWS_USE_APPLE_NETWORK_FRAMEWORK") +endif() + target_include_directories(${PROJECT_NAME} PUBLIC $ $) diff --git a/include/aws/io/event_loop.h b/include/aws/io/event_loop.h index 098e9428e..ac3532424 100644 --- a/include/aws/io/event_loop.h +++ b/include/aws/io/event_loop.h @@ -12,6 +12,7 @@ AWS_PUSH_SANE_WARNING_LEVEL struct aws_event_loop; struct aws_event_loop_group; +struct aws_event_loop_options; struct aws_shutdown_callback_options; struct aws_task; @@ -47,6 +48,25 @@ struct aws_event_loop_vtable { bool (*is_on_callers_thread)(struct aws_event_loop *event_loop); }; +/** + * Event Loop Type. If set to `AWS_EVENT_LOOP_PLATFORM_DEFAULT`, the event loop will automatically use the platform’s + * default. + * + * Default Event Loop Type + * Linux | AWS_EVENT_LOOP_EPOLL + * Windows | AWS_EVENT_LOOP_IOCP + * BSD Variants| AWS_EVENT_LOOP_KQUEUE + * MacOS | AWS_EVENT_LOOP_KQUEUE + * iOS | AWS_EVENT_LOOP_DISPATCH_QUEUE + */ +enum aws_event_loop_type { + AWS_EVENT_LOOP_PLATFORM_DEFAULT = 0, + AWS_EVENT_LOOP_EPOLL, + AWS_EVENT_LOOP_IOCP, + AWS_EVENT_LOOP_KQUEUE, + AWS_EVENT_LOOP_DISPATCH_QUEUE, +}; + /** * Event loop group configuration options */ @@ -58,6 +78,12 @@ struct aws_event_loop_group_options { */ uint16_t loop_count; + /** + * Event loop type. If the event loop type is set to AWS_EVENT_LOOP_PLATFORM_DEFAULT, the + * creation function will automatically use the platform’s default event loop type. + */ + enum aws_event_loop_type type; + /** * Optional callback to invoke when the event loop group finishes destruction. */ diff --git a/include/aws/io/private/event_loop_impl.h b/include/aws/io/private/event_loop_impl.h index 4eb2f6230..ec47bb685 100644 --- a/include/aws/io/private/event_loop_impl.h +++ b/include/aws/io/private/event_loop_impl.h @@ -90,8 +90,30 @@ struct aws_event_loop_local_object { struct aws_event_loop_options { aws_io_clock_fn *clock; struct aws_thread_options *thread_options; + + /** + * Event loop type. If the event loop type is set to AWS_EVENT_LOOP_PLATFORM_DEFAULT, the + * creation function will automatically use the platform’s default event loop type. + */ + enum aws_event_loop_type type; }; +struct aws_event_loop *aws_event_loop_new_with_iocp( + struct aws_allocator *alloc, + const struct aws_event_loop_options *options); + +struct aws_event_loop *aws_event_loop_new_with_dispatch_queue( + struct aws_allocator *alloc, + const struct aws_event_loop_options *options); + +struct aws_event_loop *aws_event_loop_new_with_kqueue( + struct aws_allocator *alloc, + const struct aws_event_loop_options *options); + +struct aws_event_loop *aws_event_loop_new_with_epoll( + struct aws_allocator *alloc, + const struct aws_event_loop_options *options); + typedef struct aws_event_loop *(aws_new_event_loop_fn)(struct aws_allocator *alloc, const struct aws_event_loop_options *options, void *new_loop_user_data); @@ -105,7 +127,7 @@ struct aws_event_loop_group { AWS_EXTERN_C_BEGIN -#ifdef AWS_USE_IO_COMPLETION_PORTS +#ifdef AWS_ENABLE_IO_COMPLETION_PORTS /** * Prepares aws_overlapped for use, and sets a function to call when the overlapped operation completes. @@ -128,6 +150,7 @@ void aws_overlapped_reset(struct aws_overlapped *overlapped); */ AWS_IO_API struct _OVERLAPPED *aws_overlapped_to_windows_overlapped(struct aws_overlapped *overlapped); +#endif /* AWS_ENABLE_IO_COMPLETION_PORTS */ /** * Associates an aws_io_handle with the event loop's I/O Completion Port. @@ -144,8 +167,6 @@ int aws_event_loop_connect_handle_to_io_completion_port( struct aws_event_loop *event_loop, struct aws_io_handle *handle); -#else - /** * Subscribes on_event to events on the event-loop for handle. events is a bitwise concatenation of the events that were * received. The definition for these values can be found in aws_io_event_type. Currently, only @@ -161,8 +182,6 @@ int aws_event_loop_subscribe_to_io_events( aws_event_loop_on_event_fn *on_event, void *user_data); -#endif /* AWS_USE_IO_COMPLETION_PORTS */ - /** * Creates an instance of the default event loop implementation for the current architecture and operating system. */ @@ -174,9 +193,7 @@ struct aws_event_loop *aws_event_loop_new_default(struct aws_allocator *alloc, a * extendable options. */ AWS_IO_API -struct aws_event_loop *aws_event_loop_new_default_with_options( - struct aws_allocator *alloc, - const struct aws_event_loop_options *options); +struct aws_event_loop *aws_event_loop_new(struct aws_allocator *alloc, const struct aws_event_loop_options *options); /** * Initializes common event-loop data structures. diff --git a/include/aws/io/private/socket_impl.h b/include/aws/io/private/socket_impl.h new file mode 100644 index 000000000..2cfcf7ff1 --- /dev/null +++ b/include/aws/io/private/socket_impl.h @@ -0,0 +1,72 @@ +#ifndef AWS_IO_SOCKET_IMPL_H +#define AWS_IO_SOCKET_IMPL_H + +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include +#include + +/* These are hacks for working around headers and functions we need for IO work but aren't directly includable or + linkable. these are purposely not exported. These functions only get called internally. The awkward aws_ prefixes are + just in case someone includes this header somewhere they were able to get these definitions included. */ +#ifdef _WIN32 +typedef void (*aws_ms_fn_ptr)(void); + +void aws_check_and_init_winsock(void); +aws_ms_fn_ptr aws_winsock_get_connectex_fn(void); +aws_ms_fn_ptr aws_winsock_get_acceptex_fn(void); +#endif + +int aws_socket_init_posix( + struct aws_socket *socket, + struct aws_allocator *alloc, + const struct aws_socket_options *options); + +int aws_socket_init_winsock( + struct aws_socket *socket, + struct aws_allocator *alloc, + const struct aws_socket_options *options); + +int aws_socket_init_apple_nw_socket( + struct aws_socket *socket, + struct aws_allocator *alloc, + const struct aws_socket_options *options); + +struct aws_socket_vtable { + void (*socket_cleanup_fn)(struct aws_socket *socket); + int (*socket_connect_fn)( + struct aws_socket *socket, + const struct aws_socket_endpoint *remote_endpoint, + struct aws_event_loop *event_loop, + aws_socket_on_connection_result_fn *on_connection_result, + void *user_data); + int (*socket_bind_fn)(struct aws_socket *socket, const struct aws_socket_endpoint *local_endpoint); + int (*socket_listen_fn)(struct aws_socket *socket, int backlog_size); + int (*socket_start_accept_fn)( + struct aws_socket *socket, + struct aws_event_loop *accept_loop, + aws_socket_on_accept_result_fn *on_accept_result, + void *user_data); + int (*socket_stop_accept_fn)(struct aws_socket *socket); + int (*socket_close_fn)(struct aws_socket *socket); + int (*socket_shutdown_dir_fn)(struct aws_socket *socket, enum aws_channel_direction dir); + int (*socket_set_options_fn)(struct aws_socket *socket, const struct aws_socket_options *options); + int (*socket_assign_to_event_loop_fn)(struct aws_socket *socket, struct aws_event_loop *event_loop); + int (*socket_subscribe_to_readable_events_fn)( + struct aws_socket *socket, + aws_socket_on_readable_fn *on_readable, + void *user_data); + int (*socket_read_fn)(struct aws_socket *socket, struct aws_byte_buf *buffer, size_t *amount_read); + int (*socket_write_fn)( + struct aws_socket *socket, + const struct aws_byte_cursor *cursor, + aws_socket_on_write_completed_fn *written_fn, + void *user_data); + int (*socket_get_error_fn)(struct aws_socket *socket); + bool (*socket_is_open_fn)(struct aws_socket *socket); +}; + +#endif // AWS_IO_SOCKET_IMPL_H diff --git a/include/aws/io/socket.h b/include/aws/io/socket.h index b0758e222..3506f7f1b 100644 --- a/include/aws/io/socket.h +++ b/include/aws/io/socket.h @@ -6,6 +6,7 @@ */ #include +#include #include AWS_PUSH_SANE_WARNING_LEVEL @@ -30,11 +31,30 @@ enum aws_socket_type { AWS_SOCKET_DGRAM, }; +/** + * Socket Implementation type. Decides which socket implementation is used. If set to + * `AWS_SOCKET_IMPL_PLATFORM_DEFAULT`, it will automatically use the platform’s default. + * + * PLATFORM DEFAULT SOCKET IMPLEMENTATION TYPE + * Linux | AWS_SOCKET_IMPL_POSIX + * Windows | AWS_SOCKET_IMPL_WINSOCK + * BSD Variants| AWS_SOCKET_IMPL_POSIX + * MacOS | AWS_SOCKET_IMPL_POSIX + * iOS | AWS_SOCKET_IMPL_APPLE_NETWORK_FRAMEWORK + */ +enum aws_socket_impl_type { + AWS_SOCKET_IMPL_PLATFORM_DEFAULT = 0, + AWS_SOCKET_IMPL_POSIX, + AWS_SOCKET_IMPL_WINSOCK, + AWS_SOCKET_IMPL_APPLE_NETWORK_FRAMEWORK, +}; + #define AWS_NETWORK_INTERFACE_NAME_MAX 16 struct aws_socket_options { enum aws_socket_type type; enum aws_socket_domain domain; + enum aws_socket_impl_type impl_type; uint32_t connect_timeout_ms; /* Keepalive properties are TCP only. * Set keepalive true to periodically transmit messages for detecting a disconnected peer. @@ -52,8 +72,9 @@ struct aws_socket_options { * This property is used to bind the socket to a particular network interface by name, such as eth0 and ens32. * If this is empty, the socket will not be bound to any interface and will use OS defaults. If the provided name * is invalid, `aws_socket_init()` will error out with AWS_IO_SOCKET_INVALID_OPTIONS. This option is only - * supported on Linux, macOS, and platforms that have either SO_BINDTODEVICE or IP_BOUND_IF. It is not supported on - * Windows. `AWS_ERROR_PLATFORM_NOT_SUPPORTED` will be raised on unsupported platforms. + * supported on Linux, macOS(bsd socket), and platforms that have either SO_BINDTODEVICE or IP_BOUND_IF. It is not + * supported on Windows and Apple Network Framework. `AWS_ERROR_PLATFORM_NOT_SUPPORTED` will be raised on + * unsupported platforms. */ char network_interface_name[AWS_NETWORK_INTERFACE_NAME_MAX]; }; @@ -78,7 +99,7 @@ typedef void(aws_socket_on_connection_result_fn)(struct aws_socket *socket, int * A user may want to call aws_socket_set_options() on the new socket if different options are desired. * * new_socket is not yet assigned to an event-loop. The user should call aws_socket_assign_to_event_loop() before - * performing IO operations. + * performing IO operations. The user must call `aws_socket_release()` when they're done with the socket, to free it. * * When error_code is AWS_ERROR_SUCCESS, new_socket is the recently accepted connection. * If error_code is non-zero, an error occurred and you should aws_socket_close() the socket. @@ -94,6 +115,9 @@ typedef void(aws_socket_on_accept_result_fn)( /** * Callback for when the data passed to a call to aws_socket_write() has either completed or failed. * On success, error_code will be AWS_ERROR_SUCCESS. + * + * `socket` may be NULL in the callback if the socket is released and cleaned up before a callback is triggered. + * by the system I/O handler, */ typedef void( aws_socket_on_write_completed_fn)(struct aws_socket *socket, int error_code, size_t bytes_written, void *user_data); @@ -114,7 +138,10 @@ struct aws_socket_endpoint { uint32_t port; }; +struct aws_socket; + struct aws_socket { + struct aws_socket_vtable *vtable; struct aws_allocator *allocator; struct aws_socket_endpoint local_endpoint; struct aws_socket_endpoint remote_endpoint; @@ -134,17 +161,6 @@ struct aws_socket { struct aws_byte_buf; struct aws_byte_cursor; -/* These are hacks for working around headers and functions we need for IO work but aren't directly includable or - linkable. these are purposely not exported. These functions only get called internally. The awkward aws_ prefixes are - just in case someone includes this header somewhere they were able to get these definitions included. */ -#ifdef _WIN32 -typedef void (*aws_ms_fn_ptr)(void); - -void aws_check_and_init_winsock(void); -aws_ms_fn_ptr aws_winsock_get_connectex_fn(void); -aws_ms_fn_ptr aws_winsock_get_acceptex_fn(void); -#endif - AWS_EXTERN_C_BEGIN /** @@ -172,10 +188,15 @@ AWS_IO_API void aws_socket_clean_up(struct aws_socket *socket); * In TCP, LOCAL and VSOCK this function will not block. If the return value is successful, then you must wait on the * `on_connection_result()` callback to be invoked before using the socket. * + * The function will failed with error if the endpoint is invalid, except for Apple Network Framework. In Apple network + * framework, as connect is an async api, we would not know if the local endpoint is valid until we have the connection + * state returned in callback. The error will returned in `on_connection_result` callback + * * If an event_loop is provided for UDP sockets, a notification will be sent on * on_connection_result in the event-loop's thread. Upon completion, the socket will already be assigned * an event loop. If NULL is passed for UDP, it will immediately return upon success, but you must call * aws_socket_assign_to_event_loop before use. + * */ AWS_IO_API int aws_socket_connect( struct aws_socket *socket, @@ -207,6 +228,7 @@ AWS_IO_API int aws_socket_listen(struct aws_socket *socket, int backlog_size); * connections or errors will arrive via the `on_accept_result` callback. * * aws_socket_bind() and aws_socket_listen() must be called before calling this function. + * */ AWS_IO_API int aws_socket_start_accept( struct aws_socket *socket, @@ -260,7 +282,7 @@ AWS_IO_API int aws_socket_assign_to_event_loop(struct aws_socket *socket, struct AWS_IO_API struct aws_event_loop *aws_socket_get_event_loop(struct aws_socket *socket); /** - * Subscribes on_readable to notifications when the socket goes readable (edge-triggered). Errors will also be recieved + * Subscribes on_readable to notifications when the socket goes readable (edge-triggered). Errors will also be received * in the callback. * * Note! This function is technically not thread safe, but we do not enforce which thread you call from. diff --git a/include/aws/testing/io_testing_channel.h b/include/aws/testing/io_testing_channel.h index c202168cb..8fa118ca4 100644 --- a/include/aws/testing/io_testing_channel.h +++ b/include/aws/testing/io_testing_channel.h @@ -9,7 +9,6 @@ #include #include #include -// #include #include #include diff --git a/source/bsd/kqueue_event_loop.c b/source/bsd/kqueue_event_loop.c index e0f8ed63b..7e6b918d9 100644 --- a/source/bsd/kqueue_event_loop.c +++ b/source/bsd/kqueue_event_loop.c @@ -131,7 +131,8 @@ struct aws_event_loop_vtable s_kqueue_vtable = { .is_on_callers_thread = s_is_event_thread, }; -struct aws_event_loop *aws_event_loop_new_default_with_options( +#ifdef AWS_ENABLE_KQUEUE +struct aws_event_loop *aws_event_loop_new_with_kqueue( struct aws_allocator *alloc, const struct aws_event_loop_options *options) { AWS_ASSERT(alloc); @@ -291,6 +292,7 @@ struct aws_event_loop *aws_event_loop_new_default_with_options( } return NULL; } +#endif // AWS_ENABLE_KQUEUE static void s_destroy(struct aws_event_loop *event_loop) { AWS_LOGF_INFO(AWS_LS_IO_EVENT_LOOP, "id=%p: destroying event_loop", (void *)event_loop); diff --git a/source/event_loop.c b/source/event_loop.c index 2e0861964..946fcd9a8 100644 --- a/source/event_loop.c +++ b/source/event_loop.c @@ -6,20 +6,124 @@ #include #include +#include #include #include #include +#include #include #include +#ifdef AWS_USE_APPLE_NETWORK_FRAMEWORK +static enum aws_event_loop_type s_default_event_loop_type_override = AWS_EVENT_LOOP_DISPATCH_QUEUE; +#else +static enum aws_event_loop_type s_default_event_loop_type_override = AWS_EVENT_LOOP_PLATFORM_DEFAULT; +#endif + struct aws_event_loop *aws_event_loop_new_default(struct aws_allocator *alloc, aws_io_clock_fn *clock) { struct aws_event_loop_options options = { .thread_options = NULL, .clock = clock, + .type = AWS_EVENT_LOOP_PLATFORM_DEFAULT, }; - return aws_event_loop_new_default_with_options(alloc, &options); + return aws_event_loop_new(alloc, &options); +} + +#ifndef AWS_ENABLE_IO_COMPLETION_PORTS +struct aws_event_loop *aws_event_loop_new_with_iocp( + struct aws_allocator *alloc, + const struct aws_event_loop_options *options) { + (void)alloc; + (void)options; + + AWS_LOGF_DEBUG(AWS_LS_IO_EVENT_LOOP, "IOCP is not supported on the platform"); + aws_raise_error(AWS_ERROR_PLATFORM_NOT_SUPPORTED); + return NULL; +} +#endif // AWS_ENABLE_IO_COMPLETION_PORTS + +#ifndef AWS_ENABLE_KQUEUE +struct aws_event_loop *aws_event_loop_new_with_kqueue( + struct aws_allocator *alloc, + const struct aws_event_loop_options *options) { + (void)alloc; + (void)options; + + AWS_LOGF_DEBUG(AWS_LS_IO_EVENT_LOOP, "Kqueue is not supported on the platform"); + aws_raise_error(AWS_ERROR_PLATFORM_NOT_SUPPORTED); + return NULL; +} +#endif // AWS_ENABLE_EPOLL + +#ifndef AWS_ENABLE_EPOLL +struct aws_event_loop *aws_event_loop_new_with_epoll( + struct aws_allocator *alloc, + const struct aws_event_loop_options *options) { + (void)alloc; + (void)options; + + AWS_LOGF_DEBUG(AWS_LS_IO_EVENT_LOOP, "Epoll is not supported on the platform"); + return NULL; +} +#endif // AWS_ENABLE_KQUEUE + +/** + * Return the default event loop type. If the return value is `AWS_EVENT_LOOP_PLATFORM_DEFAULT`, the function failed to + * retrieve the default type value. + * If `aws_event_loop_override_default_type` has been called, return the override default type. + */ +static enum aws_event_loop_type aws_event_loop_get_default_type(void) { + if (s_default_event_loop_type_override != AWS_EVENT_LOOP_PLATFORM_DEFAULT) { + return s_default_event_loop_type_override; + } +/** + * Ideally we should use the platform definition (e.x.: AWS_OS_APPLE) here, however the platform + * definition was declared in aws-c-common. We probably do not want to introduce extra dependency here. + */ +#ifdef AWS_ENABLE_KQUEUE + return AWS_EVENT_LOOP_KQUEUE; +#elif defined(AWS_ENABLE_DISPATCH_QUEUE) + return AWS_EVENT_LOOP_DISPATCH_QUEUE; +#elif defined(AWS_ENABLE_EPOLL) + return AWS_EVENT_LOOP_EPOLL; +#elif defined(AWS_OS_WINDOWS) + return AWS_EVENT_LOOP_IOCP; +#else + AWS_LOGF_ERROR( + AWS_LS_IO_EVENT_LOOP, + "Failed to get default event loop type. The library is not built correctly on the platform."); +#endif +} + +static int aws_event_loop_type_validate_platform(enum aws_event_loop_type type); +struct aws_event_loop *aws_event_loop_new(struct aws_allocator *alloc, const struct aws_event_loop_options *options) { + + enum aws_event_loop_type type = options->type; + if (type == AWS_EVENT_LOOP_PLATFORM_DEFAULT) { + type = aws_event_loop_get_default_type(); + } + + if (aws_event_loop_type_validate_platform(type)) { + AWS_LOGF_DEBUG(AWS_LS_IO_EVENT_LOOP, "Invalid event loop type on the platform."); + return NULL; + } + + switch (type) { + case AWS_EVENT_LOOP_EPOLL: + return aws_event_loop_new_with_epoll(alloc, options); + case AWS_EVENT_LOOP_IOCP: + return aws_event_loop_new_with_iocp(alloc, options); + case AWS_EVENT_LOOP_KQUEUE: + return aws_event_loop_new_with_kqueue(alloc, options); + case AWS_EVENT_LOOP_DISPATCH_QUEUE: + return aws_event_loop_new_with_dispatch_queue(alloc, options); + default: + AWS_LOGF_DEBUG(AWS_LS_IO_EVENT_LOOP, "Invalid event loop type on the platform."); + aws_raise_error(AWS_ERROR_PLATFORM_NOT_SUPPORTED); + return NULL; + } } static void s_event_loop_group_thread_exit(void *user_data) { @@ -136,6 +240,7 @@ struct aws_event_loop_group *aws_event_loop_group_new_internal( struct aws_event_loop_options el_options = { .clock = clock, .thread_options = &thread_options, + .type = options->type, }; if (pin_threads) { @@ -195,7 +300,7 @@ static struct aws_event_loop *s_default_new_event_loop( void *user_data) { (void)user_data; - return aws_event_loop_new_default_with_options(allocator, options); + return aws_event_loop_new(allocator, options); } struct aws_event_loop_group *aws_event_loop_group_new( @@ -443,17 +548,16 @@ void aws_event_loop_cancel_task(struct aws_event_loop *event_loop, struct aws_ta event_loop->vtable->cancel_task(event_loop, task); } -#if AWS_USE_IO_COMPLETION_PORTS - int aws_event_loop_connect_handle_to_io_completion_port( struct aws_event_loop *event_loop, struct aws_io_handle *handle) { - AWS_ASSERT(event_loop->vtable && event_loop->vtable->connect_to_io_completion_port); - return event_loop->vtable->connect_to_io_completion_port(event_loop, handle); -} + if (event_loop->vtable && event_loop->vtable->connect_to_io_completion_port) { + return event_loop->vtable->connect_to_io_completion_port(event_loop, handle); + } -#else /* !AWS_USE_IO_COMPLETION_PORTS */ + return aws_raise_error(AWS_ERROR_UNSUPPORTED_OPERATION); +} int aws_event_loop_subscribe_to_io_events( struct aws_event_loop *event_loop, @@ -462,10 +566,11 @@ int aws_event_loop_subscribe_to_io_events( aws_event_loop_on_event_fn *on_event, void *user_data) { - AWS_ASSERT(event_loop->vtable && event_loop->vtable->subscribe_to_io_events); - return event_loop->vtable->subscribe_to_io_events(event_loop, handle, events, on_event, user_data); + if (event_loop->vtable && event_loop->vtable->subscribe_to_io_events) { + return event_loop->vtable->subscribe_to_io_events(event_loop, handle, events, on_event, user_data); + } + return aws_raise_error(AWS_ERROR_UNSUPPORTED_OPERATION); } -#endif /* AWS_USE_IO_COMPLETION_PORTS */ int aws_event_loop_unsubscribe_from_io_events(struct aws_event_loop *event_loop, struct aws_io_handle *handle) { AWS_ASSERT(aws_event_loop_thread_is_callers_thread(event_loop)); @@ -532,3 +637,62 @@ struct aws_event_loop *aws_event_loop_new_base( return event_loop; } + +/** + * Override default event loop type. Only used internally in tests. + * + * If the defined type is not supported on the current platform, the event loop type would reset to + * AWS_EVENT_LOOP_PLATFORM_DEFAULT. + */ +void aws_event_loop_override_default_type(enum aws_event_loop_type default_type_override) { + if (aws_event_loop_type_validate_platform(default_type_override) == AWS_OP_SUCCESS) { + s_default_event_loop_type_override = default_type_override; + } else { + s_default_event_loop_type_override = AWS_EVENT_LOOP_PLATFORM_DEFAULT; + } +} + +static int aws_event_loop_type_validate_platform(enum aws_event_loop_type type) { + switch (type) { + case AWS_EVENT_LOOP_EPOLL: +#ifndef AWS_ENABLE_EPOLL + AWS_LOGF_DEBUG(AWS_LS_IO_EVENT_LOOP, "Event loop type EPOLL is not supported on the platform."); + return aws_raise_error(AWS_ERROR_PLATFORM_NOT_SUPPORTED); +#endif // AWS_ENABLE_EPOLL + break; + case AWS_EVENT_LOOP_IOCP: +#ifndef AWS_ENABLE_IO_COMPLETION_PORTS + AWS_LOGF_DEBUG(AWS_LS_IO_EVENT_LOOP, "Event loop type IOCP is not supported on the platform."); + return aws_raise_error(AWS_ERROR_PLATFORM_NOT_SUPPORTED); +#endif // AWS_ENABLE_IO_COMPLETION_PORTS + break; + case AWS_EVENT_LOOP_KQUEUE: +#ifndef AWS_ENABLE_KQUEUE + AWS_LOGF_DEBUG(AWS_LS_IO_EVENT_LOOP, "Event loop type KQUEUE is not supported on the platform."); + return aws_raise_error(AWS_ERROR_PLATFORM_NOT_SUPPORTED); +#endif // AWS_ENABLE_KQUEUE + break; + case AWS_EVENT_LOOP_DISPATCH_QUEUE: +#ifndef AWS_ENABLE_DISPATCH_QUEUE + AWS_LOGF_DEBUG(AWS_LS_IO_EVENT_LOOP, "Event loop type Dispatch Queue is not supported on the platform."); + return aws_raise_error(AWS_ERROR_PLATFORM_NOT_SUPPORTED); +#endif // AWS_ENABLE_DISPATCH_QUEUE + break; + default: + AWS_LOGF_DEBUG(AWS_LS_IO_EVENT_LOOP, "Invalid event loop type."); + return aws_raise_error(AWS_ERROR_UNSUPPORTED_OPERATION); + break; + } + return AWS_OP_SUCCESS; +} + +struct aws_event_loop *aws_event_loop_new_with_dispatch_queue( + struct aws_allocator *alloc, + const struct aws_event_loop_options *options) { + (void)alloc; + (void)options; + + AWS_LOGF_DEBUG(AWS_LS_IO_EVENT_LOOP, "Dispatch Queue is not supported on the platform"); + aws_raise_error(AWS_ERROR_PLATFORM_NOT_SUPPORTED); + return NULL; +} diff --git a/source/linux/epoll_event_loop.c b/source/linux/epoll_event_loop.c index a99d5a8cf..147b0001b 100644 --- a/source/linux/epoll_event_loop.c +++ b/source/linux/epoll_event_loop.c @@ -112,7 +112,7 @@ enum { int aws_open_nonblocking_posix_pipe(int pipe_fds[2]); /* Setup edge triggered epoll with a scheduler. */ -struct aws_event_loop *aws_event_loop_new_default_with_options( +struct aws_event_loop *aws_event_loop_new_with_epoll( struct aws_allocator *alloc, const struct aws_event_loop_options *options) { AWS_PRECONDITION(options); diff --git a/source/posix/socket.c b/source/posix/socket.c index 49e18f47e..266ad2de2 100644 --- a/source/posix/socket.c +++ b/source/posix/socket.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -188,6 +189,56 @@ struct posix_socket { bool *close_happened; }; +static void s_socket_clean_up(struct aws_socket *socket); +static int s_socket_connect( + struct aws_socket *socket, + const struct aws_socket_endpoint *remote_endpoint, + struct aws_event_loop *event_loop, + aws_socket_on_connection_result_fn *on_connection_result, + void *user_data); +static int s_socket_bind(struct aws_socket *socket, const struct aws_socket_endpoint *local_endpoint); +static int s_socket_listen(struct aws_socket *socket, int backlog_size); +static int s_socket_start_accept( + struct aws_socket *socket, + struct aws_event_loop *accept_loop, + aws_socket_on_accept_result_fn *on_accept_result, + void *user_data); +static int s_socket_stop_accept(struct aws_socket *socket); +static int s_socket_set_options(struct aws_socket *socket, const struct aws_socket_options *options); +static int s_socket_close(struct aws_socket *socket); +static int s_socket_shutdown_dir(struct aws_socket *socket, enum aws_channel_direction dir); +static int s_socket_assign_to_event_loop(struct aws_socket *socket, struct aws_event_loop *event_loop); +static int s_socket_subscribe_to_readable_events( + struct aws_socket *socket, + aws_socket_on_readable_fn *on_readable, + void *user_data); +static int s_socket_read(struct aws_socket *socket, struct aws_byte_buf *buffer, size_t *amount_read); +static int s_socket_write( + struct aws_socket *socket, + const struct aws_byte_cursor *cursor, + aws_socket_on_write_completed_fn *written_fn, + void *user_data); +static int s_socket_get_error(struct aws_socket *socket); +static bool s_socket_is_open(struct aws_socket *socket); + +struct aws_socket_vtable s_posix_socket_vtable = { + .socket_cleanup_fn = s_socket_clean_up, + .socket_connect_fn = s_socket_connect, + .socket_bind_fn = s_socket_bind, + .socket_listen_fn = s_socket_listen, + .socket_start_accept_fn = s_socket_start_accept, + .socket_stop_accept_fn = s_socket_stop_accept, + .socket_set_options_fn = s_socket_set_options, + .socket_close_fn = s_socket_close, + .socket_shutdown_dir_fn = s_socket_shutdown_dir, + .socket_assign_to_event_loop_fn = s_socket_assign_to_event_loop, + .socket_subscribe_to_readable_events_fn = s_socket_subscribe_to_readable_events, + .socket_read_fn = s_socket_read, + .socket_write_fn = s_socket_write, + .socket_get_error_fn = s_socket_get_error, + .socket_is_open_fn = s_socket_is_open, +}; + static void s_socket_destroy_impl(void *user_data) { struct posix_socket *socket_impl = user_data; aws_mem_release(socket_impl->allocator, socket_impl); @@ -199,6 +250,7 @@ static int s_socket_init( const struct aws_socket_options *options, int existing_socket_fd) { AWS_ASSERT(options); + AWS_ZERO_STRUCT(*socket); struct posix_socket *posix_socket = aws_mem_calloc(alloc, 1, sizeof(struct posix_socket)); @@ -211,6 +263,8 @@ static int s_socket_init( socket->io_handle.data.fd = -1; socket->state = INIT; socket->options = *options; + socket->impl = posix_socket; + socket->vtable = &s_posix_socket_vtable; if (existing_socket_fd < 0) { int err = s_create_socket(socket, options); @@ -235,16 +289,19 @@ static int s_socket_init( posix_socket->allocator = alloc; posix_socket->connect_args = NULL; posix_socket->close_happened = NULL; - socket->impl = posix_socket; + return AWS_OP_SUCCESS; } -int aws_socket_init(struct aws_socket *socket, struct aws_allocator *alloc, const struct aws_socket_options *options) { +int aws_socket_init_posix( + struct aws_socket *socket, + struct aws_allocator *alloc, + const struct aws_socket_options *options) { AWS_ASSERT(options); return s_socket_init(socket, alloc, options, -1); } -void aws_socket_clean_up(struct aws_socket *socket) { +static void s_socket_clean_up(struct aws_socket *socket) { if (!socket->impl) { /* protect from double clean */ return; @@ -601,7 +658,7 @@ static int parse_cid(const char *cid_str, unsigned int *value) { } #endif -int aws_socket_connect( +static int s_socket_connect( struct aws_socket *socket, const struct aws_socket_endpoint *remote_endpoint, struct aws_event_loop *event_loop, @@ -786,7 +843,7 @@ int aws_socket_connect( return AWS_OP_ERR; } -int aws_socket_bind(struct aws_socket *socket, const struct aws_socket_endpoint *local_endpoint) { +static int s_socket_bind(struct aws_socket *socket, const struct aws_socket_endpoint *local_endpoint) { if (socket->state != INIT) { AWS_LOGF_ERROR( AWS_LS_IO_SOCKET, @@ -894,20 +951,7 @@ int aws_socket_bind(struct aws_socket *socket, const struct aws_socket_endpoint return AWS_OP_ERR; } -int aws_socket_get_bound_address(const struct aws_socket *socket, struct aws_socket_endpoint *out_address) { - if (socket->local_endpoint.address[0] == 0) { - AWS_LOGF_ERROR( - AWS_LS_IO_SOCKET, - "id=%p fd=%d: Socket has no local address. Socket must be bound first.", - (void *)socket, - socket->io_handle.data.fd); - return aws_raise_error(AWS_IO_SOCKET_ILLEGAL_OPERATION_FOR_STATE); - } - *out_address = socket->local_endpoint; - return AWS_OP_SUCCESS; -} - -int aws_socket_listen(struct aws_socket *socket, int backlog_size) { +static int s_socket_listen(struct aws_socket *socket, int backlog_size) { if (socket->state != BOUND) { AWS_LOGF_ERROR( AWS_LS_IO_SOCKET, @@ -979,7 +1023,7 @@ static void s_socket_accept_event( AWS_LOGF_DEBUG( AWS_LS_IO_SOCKET, "id=%p fd=%d: incoming connection", (void *)socket, socket->io_handle.data.fd); - struct aws_socket *new_sock = aws_mem_acquire(socket->allocator, sizeof(struct aws_socket)); + struct aws_socket *new_sock = aws_mem_calloc(socket->allocator, 1, sizeof(struct aws_socket)); if (!new_sock) { close(in_fd); @@ -1073,7 +1117,7 @@ static void s_socket_accept_event( socket->io_handle.data.fd); } -int aws_socket_start_accept( +static int s_socket_start_accept( struct aws_socket *socket, struct aws_event_loop *accept_loop, aws_socket_on_accept_result_fn *on_accept_result, @@ -1154,7 +1198,7 @@ static void s_stop_accept_task(struct aws_task *task, void *arg, enum aws_task_s aws_mutex_unlock(&stop_accept_args->mutex); } -int aws_socket_stop_accept(struct aws_socket *socket) { +static int s_socket_stop_accept(struct aws_socket *socket) { if (socket->state != LISTENING) { AWS_LOGF_ERROR( AWS_LS_IO_SOCKET, @@ -1214,7 +1258,7 @@ int aws_socket_stop_accept(struct aws_socket *socket) { return ret_val; } -int aws_socket_set_options(struct aws_socket *socket, const struct aws_socket_options *options) { +static int s_socket_set_options(struct aws_socket *socket, const struct aws_socket_options *options) { if (socket->options.domain != options->domain || socket->options.type != options->type) { return aws_raise_error(AWS_IO_SOCKET_INVALID_OPTIONS); } @@ -1446,7 +1490,7 @@ static void s_close_task(struct aws_task *task, void *arg, enum aws_task_status aws_mutex_unlock(&close_args->mutex); } -int aws_socket_close(struct aws_socket *socket) { +static int s_socket_close(struct aws_socket *socket) { struct posix_socket *socket_impl = socket->impl; AWS_LOGF_DEBUG(AWS_LS_IO_SOCKET, "id=%p fd=%d: closing", (void *)socket, socket->io_handle.data.fd); struct aws_event_loop *event_loop = socket->event_loop; @@ -1548,7 +1592,7 @@ int aws_socket_close(struct aws_socket *socket) { return AWS_OP_SUCCESS; } -int aws_socket_shutdown_dir(struct aws_socket *socket, enum aws_channel_direction dir) { +static int s_socket_shutdown_dir(struct aws_socket *socket, enum aws_channel_direction dir) { int how = dir == AWS_CHANNEL_DIR_READ ? 0 : 1; AWS_LOGF_DEBUG( AWS_LS_IO_SOCKET, "id=%p fd=%d: shutting down in direction %d", (void *)socket, socket->io_handle.data.fd, dir); @@ -1800,7 +1844,7 @@ static void s_on_socket_io_event( aws_ref_count_release(&socket_impl->internal_refcount); } -int aws_socket_assign_to_event_loop(struct aws_socket *socket, struct aws_event_loop *event_loop) { +static int s_socket_assign_to_event_loop(struct aws_socket *socket, struct aws_event_loop *event_loop) { if (!socket->event_loop) { AWS_LOGF_DEBUG( AWS_LS_IO_SOCKET, @@ -1835,11 +1879,7 @@ int aws_socket_assign_to_event_loop(struct aws_socket *socket, struct aws_event_ return aws_raise_error(AWS_IO_EVENT_LOOP_ALREADY_ASSIGNED); } -struct aws_event_loop *aws_socket_get_event_loop(struct aws_socket *socket) { - return socket->event_loop; -} - -int aws_socket_subscribe_to_readable_events( +static int s_socket_subscribe_to_readable_events( struct aws_socket *socket, aws_socket_on_readable_fn *on_readable, void *user_data) { @@ -1871,7 +1911,7 @@ int aws_socket_subscribe_to_readable_events( return AWS_OP_SUCCESS; } -int aws_socket_read(struct aws_socket *socket, struct aws_byte_buf *buffer, size_t *amount_read) { +static int s_socket_read(struct aws_socket *socket, struct aws_byte_buf *buffer, size_t *amount_read) { AWS_ASSERT(amount_read); if (!aws_event_loop_thread_is_callers_thread(socket->event_loop)) { @@ -1946,7 +1986,7 @@ int aws_socket_read(struct aws_socket *socket, struct aws_byte_buf *buffer, size return aws_raise_error(s_determine_socket_error(errno_value)); } -int aws_socket_write( +static int s_socket_write( struct aws_socket *socket, const struct aws_byte_cursor *cursor, aws_socket_on_write_completed_fn *written_fn, @@ -1982,7 +2022,7 @@ int aws_socket_write( return s_process_socket_write_requests(socket, write_request); } -int aws_socket_get_error(struct aws_socket *socket) { +static int s_socket_get_error(struct aws_socket *socket) { int connect_result; socklen_t result_length = sizeof(connect_result); @@ -1997,19 +2037,10 @@ int aws_socket_get_error(struct aws_socket *socket) { return AWS_OP_SUCCESS; } -bool aws_socket_is_open(struct aws_socket *socket) { +static bool s_socket_is_open(struct aws_socket *socket) { return socket->io_handle.data.fd >= 0; } -void aws_socket_endpoint_init_local_address_for_test(struct aws_socket_endpoint *endpoint) { - struct aws_uuid uuid; - AWS_FATAL_ASSERT(aws_uuid_init(&uuid) == AWS_OP_SUCCESS); - char uuid_str[AWS_UUID_STR_LEN] = {0}; - struct aws_byte_buf uuid_buf = aws_byte_buf_from_empty_array(uuid_str, sizeof(uuid_str)); - AWS_FATAL_ASSERT(aws_uuid_to_str(&uuid, &uuid_buf) == AWS_OP_SUCCESS); - snprintf(endpoint->address, sizeof(endpoint->address), "testsock" PRInSTR ".sock", AWS_BYTE_BUF_PRI(uuid_buf)); -} - bool aws_is_network_interface_name_valid(const char *interface_name) { if (if_nametoindex(interface_name) == 0) { AWS_LOGF_ERROR(AWS_LS_IO_SOCKET, "network_interface_name(%s) is invalid with errno: %d", interface_name, errno); diff --git a/source/socket.c b/source/socket.c new file mode 100644 index 000000000..4eda7d002 --- /dev/null +++ b/source/socket.c @@ -0,0 +1,259 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include +#include +#include +#include +#include + +void aws_socket_clean_up(struct aws_socket *socket) { + AWS_PRECONDITION(socket->vtable && socket->vtable->socket_cleanup_fn); + socket->vtable->socket_cleanup_fn(socket); +} + +int aws_socket_connect( + struct aws_socket *socket, + const struct aws_socket_endpoint *remote_endpoint, + struct aws_event_loop *event_loop, + aws_socket_on_connection_result_fn *on_connection_result, + void *user_data) { + AWS_PRECONDITION(socket->vtable && socket->vtable->socket_connect_fn); + return socket->vtable->socket_connect_fn(socket, remote_endpoint, event_loop, on_connection_result, user_data); +} + +int aws_socket_bind(struct aws_socket *socket, const struct aws_socket_endpoint *local_endpoint) { + AWS_PRECONDITION(socket->vtable && socket->vtable->socket_bind_fn); + return socket->vtable->socket_bind_fn(socket, local_endpoint); +} + +int aws_socket_listen(struct aws_socket *socket, int backlog_size) { + AWS_PRECONDITION(socket->vtable && socket->vtable->socket_listen_fn); + return socket->vtable->socket_listen_fn(socket, backlog_size); +} + +int aws_socket_start_accept( + struct aws_socket *socket, + struct aws_event_loop *accept_loop, + aws_socket_on_accept_result_fn *on_accept_result, + void *user_data) { + AWS_PRECONDITION(socket->vtable && socket->vtable->socket_listen_fn); + return socket->vtable->socket_start_accept_fn(socket, accept_loop, on_accept_result, user_data); +} + +int aws_socket_stop_accept(struct aws_socket *socket) { + AWS_PRECONDITION(socket->vtable && socket->vtable->socket_stop_accept_fn); + return socket->vtable->socket_stop_accept_fn(socket); +} + +int aws_socket_close(struct aws_socket *socket) { + AWS_PRECONDITION(socket->vtable && socket->vtable->socket_close_fn); + return socket->vtable->socket_close_fn(socket); +} + +int aws_socket_shutdown_dir(struct aws_socket *socket, enum aws_channel_direction dir) { + AWS_PRECONDITION(socket->vtable && socket->vtable->socket_shutdown_dir_fn); + return socket->vtable->socket_shutdown_dir_fn(socket, dir); +} + +int aws_socket_set_options(struct aws_socket *socket, const struct aws_socket_options *options) { + AWS_PRECONDITION(socket->vtable && socket->vtable->socket_set_options_fn); + return socket->vtable->socket_set_options_fn(socket, options); +} + +int aws_socket_assign_to_event_loop(struct aws_socket *socket, struct aws_event_loop *event_loop) { + AWS_PRECONDITION(socket->vtable && socket->vtable->socket_assign_to_event_loop_fn); + return socket->vtable->socket_assign_to_event_loop_fn(socket, event_loop); +} + +struct aws_event_loop *aws_socket_get_event_loop(struct aws_socket *socket) { + return socket->event_loop; +} + +int aws_socket_subscribe_to_readable_events( + struct aws_socket *socket, + aws_socket_on_readable_fn *on_readable, + void *user_data) { + AWS_PRECONDITION(socket->vtable && socket->vtable->socket_subscribe_to_readable_events_fn); + return socket->vtable->socket_subscribe_to_readable_events_fn(socket, on_readable, user_data); +} + +int aws_socket_read(struct aws_socket *socket, struct aws_byte_buf *buffer, size_t *amount_read) { + AWS_PRECONDITION(socket->vtable && socket->vtable->socket_read_fn); + return socket->vtable->socket_read_fn(socket, buffer, amount_read); +} + +int aws_socket_write( + struct aws_socket *socket, + const struct aws_byte_cursor *cursor, + aws_socket_on_write_completed_fn *written_fn, + void *user_data) { + + AWS_PRECONDITION(socket->vtable && socket->vtable->socket_write_fn); + return socket->vtable->socket_write_fn(socket, cursor, written_fn, user_data); +} + +int aws_socket_get_error(struct aws_socket *socket) { + AWS_PRECONDITION(socket->vtable && socket->vtable->socket_get_error_fn); + return socket->vtable->socket_get_error_fn(socket); +} + +bool aws_socket_is_open(struct aws_socket *socket) { + AWS_PRECONDITION(socket->vtable && socket->vtable->socket_is_open_fn); + return socket->vtable->socket_is_open_fn(socket); +} + +/** + * Return the default socket implementation type. If the return value is `AWS_SOCKET_IMPL_PLATFORM_DEFAULT`, the + * function failed to retrieve the default type value. + */ +static enum aws_socket_impl_type aws_socket_get_default_impl_type(void) { + enum aws_socket_impl_type type = AWS_SOCKET_IMPL_PLATFORM_DEFAULT; +// override default socket +#ifdef AWS_USE_APPLE_NETWORK_FRAMEWORK + type = AWS_SOCKET_IMPL_APPLE_NETWORK_FRAMEWORK; +#endif // AWS_USE_APPLE_NETWORK_FRAMEWORK + if (type != AWS_SOCKET_IMPL_PLATFORM_DEFAULT) { + return type; + } +/** + * Ideally we should use the platform definition (e.x.: AWS_OS_APPLE) here, however the platform + * definition was declared in aws-c-common. We probably do not want to introduce extra dependency here. + */ +#if defined(AWS_ENABLE_KQUEUE) || defined(AWS_ENABLE_EPOLL) + return AWS_SOCKET_IMPL_POSIX; +#elif AWS_ENABLE_DISPATCH_QUEUE + return AWS_SOCKET_IMPL_APPLE_NETWORK_FRAMEWORK; +#elif AWS_ENABLE_IO_COMPLETION_PORTS + return AWS_SOCKET_IMPL_WINSOCK; +#else + return AWS_SOCKET_IMPL_PLATFORM_DEFAULT; +#endif +} + +static int aws_socket_impl_type_validate_platform(enum aws_socket_impl_type type); +int aws_socket_init(struct aws_socket *socket, struct aws_allocator *alloc, const struct aws_socket_options *options) { + + // 1. get socket type & validate type is available on the platform + enum aws_socket_impl_type type = options->impl_type; + if (type == AWS_SOCKET_IMPL_PLATFORM_DEFAULT) { + type = aws_socket_get_default_impl_type(); + } + + if (aws_socket_impl_type_validate_platform(type)) { + AWS_LOGF_DEBUG(AWS_LS_IO_SOCKET, "Invalid event loop type on the platform."); + return aws_raise_error(AWS_ERROR_PLATFORM_NOT_SUPPORTED); + } + + // 2. setup vtable based on socket type + switch (type) { + case AWS_SOCKET_IMPL_POSIX: + return aws_socket_init_posix(socket, alloc, options); + case AWS_SOCKET_IMPL_WINSOCK: + return aws_socket_init_winsock(socket, alloc, options); + case AWS_SOCKET_IMPL_APPLE_NETWORK_FRAMEWORK: + // Apple Network Framework is not implemented yet. We should not use it yet. + AWS_ASSERT(false && "Invalid socket implementation on platform."); + return aws_socket_init_apple_nw_socket(socket, alloc, options); + default: + AWS_ASSERT(false && "Invalid socket implementation on platform."); + return aws_raise_error(AWS_ERROR_PLATFORM_NOT_SUPPORTED); + } +} + +int aws_socket_get_bound_address(const struct aws_socket *socket, struct aws_socket_endpoint *out_address) { + if (socket->local_endpoint.address[0] == 0) { + AWS_LOGF_ERROR( + AWS_LS_IO_SOCKET, + "id=%p fd=%d: Socket has no local address. Socket must be bound first.", + (void *)socket, + socket->io_handle.data.fd); + return aws_raise_error(AWS_IO_SOCKET_ILLEGAL_OPERATION_FOR_STATE); + } + *out_address = socket->local_endpoint; + return AWS_OP_SUCCESS; +} + +void aws_socket_endpoint_init_local_address_for_test(struct aws_socket_endpoint *endpoint) { + (void)endpoint; + struct aws_uuid uuid; + AWS_FATAL_ASSERT(aws_uuid_init(&uuid) == AWS_OP_SUCCESS); + char uuid_str[AWS_UUID_STR_LEN] = {0}; + struct aws_byte_buf uuid_buf = aws_byte_buf_from_empty_array(uuid_str, sizeof(uuid_str)); + AWS_FATAL_ASSERT(aws_uuid_to_str(&uuid, &uuid_buf) == AWS_OP_SUCCESS); + + enum aws_socket_impl_type socket_type = aws_socket_get_default_impl_type(); + if (socket_type == AWS_SOCKET_IMPL_POSIX) + snprintf(endpoint->address, sizeof(endpoint->address), "testsock" PRInSTR ".sock", AWS_BYTE_BUF_PRI(uuid_buf)); + else if (socket_type == AWS_SOCKET_IMPL_WINSOCK) { + snprintf( + endpoint->address, sizeof(endpoint->address), "\\\\.\\pipe\\testsock" PRInSTR, AWS_BYTE_BUF_PRI(uuid_buf)); + } +} + +static int aws_socket_impl_type_validate_platform(enum aws_socket_impl_type type) { + switch (type) { + case AWS_SOCKET_IMPL_POSIX: +#if !defined(AWS_ENABLE_EPOLL) && !defined(AWS_ENABLE_KQUEUE) + AWS_LOGF_DEBUG(AWS_LS_IO_SOCKET, "Posix socket is not supported on the platform."); + return aws_raise_error(AWS_ERROR_PLATFORM_NOT_SUPPORTED); +#endif // AWS_SOCKET_IMPL_POSIX + break; + case AWS_SOCKET_IMPL_WINSOCK: +#ifndef AWS_ENABLE_IO_COMPLETION_PORTS + AWS_LOGF_DEBUG(AWS_LS_IO_SOCKET, "WINSOCK is not supported on the platform."); + return aws_raise_error(AWS_ERROR_PLATFORM_NOT_SUPPORTED); +#endif // AWS_ENABLE_IO_COMPLETION_PORTS + break; + case AWS_SOCKET_IMPL_APPLE_NETWORK_FRAMEWORK: +#ifndef AWS_ENABLE_DISPATCH_QUEUE + AWS_LOGF_DEBUG(AWS_LS_IO_SOCKET, "Apple Network Framework is not supported on the platform."); + return aws_raise_error(AWS_ERROR_PLATFORM_NOT_SUPPORTED); +#endif // AWS_ENABLE_DISPATCH_QUEUE + break; + default: + AWS_LOGF_DEBUG(AWS_LS_IO_SOCKET, "Invalid socket implementation type."); + return aws_raise_error(AWS_ERROR_PLATFORM_NOT_SUPPORTED); + break; + } + return AWS_OP_SUCCESS; +} + +#if !defined(AWS_ENABLE_EPOLL) && !defined(AWS_ENABLE_KQUEUE) +int aws_socket_init_posix( + struct aws_socket *socket, + struct aws_allocator *alloc, + const struct aws_socket_options *options) { + (void)socket; + (void)alloc; + (void)options; + AWS_LOGF_DEBUG(AWS_LS_IO_SOCKET, "Posix socket is not supported on the platform."); + return aws_raise_error(AWS_ERROR_PLATFORM_NOT_SUPPORTED); +} +#endif + +#ifndef AWS_ENABLE_IO_COMPLETION_PORTS +int aws_socket_init_winsock( + struct aws_socket *socket, + struct aws_allocator *alloc, + const struct aws_socket_options *options) { + (void)socket; + (void)alloc; + (void)options; + AWS_LOGF_DEBUG(AWS_LS_IO_SOCKET, "WINSOCK is not supported on the platform."); + return aws_raise_error(AWS_ERROR_PLATFORM_NOT_SUPPORTED); +} +#endif + +int aws_socket_init_apple_nw_socket( + struct aws_socket *socket, + struct aws_allocator *alloc, + const struct aws_socket_options *options) { + (void)socket; + (void)alloc; + (void)options; + AWS_LOGF_DEBUG(AWS_LS_IO_SOCKET, "Apple Network Framework is not supported on the platform."); + return aws_raise_error(AWS_ERROR_PLATFORM_NOT_SUPPORTED); +} diff --git a/source/windows/host_resolver.c b/source/windows/host_resolver.c index 59fbb858d..7bc10580e 100644 --- a/source/windows/host_resolver.c +++ b/source/windows/host_resolver.c @@ -10,6 +10,7 @@ #include #include #include +#include #include int aws_default_dns_resolve( diff --git a/source/windows/iocp/iocp_event_loop.c b/source/windows/iocp/iocp_event_loop.c index 1d0801e4b..ff390670f 100644 --- a/source/windows/iocp/iocp_event_loop.c +++ b/source/windows/iocp/iocp_event_loop.c @@ -144,7 +144,7 @@ struct aws_event_loop_vtable s_iocp_vtable = { .free_io_event_resources = s_free_io_event_resources, }; -struct aws_event_loop *aws_event_loop_new_default_with_options( +struct aws_event_loop *aws_event_loop_new_with_iocp( struct aws_allocator *alloc, const struct aws_event_loop_options *options) { AWS_ASSERT(alloc); diff --git a/source/windows/iocp/socket.c b/source/windows/iocp/socket.c index 7286bd6ba..b2d8ad16a 100644 --- a/source/windows/iocp/socket.c +++ b/source/windows/iocp/socket.c @@ -14,6 +14,7 @@ below, clang-format doesn't work (at least on my version) with the c-style comme #include // clang-format on +#include #include #include @@ -21,7 +22,6 @@ below, clang-format doesn't work (at least on my version) with the c-style comme #include #include #include -#include #include #include @@ -57,7 +57,7 @@ below, clang-format doesn't work (at least on my version) with the c-style comme #define PIPE_BUFFER_SIZE 512 -struct socket_vtable { +struct winsock_vtable { int (*connection_success)(struct aws_socket *socket); void (*connection_error)(struct aws_socket *socket, int error_code); int (*close)(struct aws_socket *socket); @@ -137,7 +137,7 @@ static int s_local_listen(struct aws_socket *socket, int backlog_size); static int s_tcp_read(struct aws_socket *socket, struct aws_byte_buf *buffer, size_t *amount_read); static int s_local_read(struct aws_socket *socket, struct aws_byte_buf *buffer, size_t *amount_read); static int s_dgram_read(struct aws_socket *socket, struct aws_byte_buf *buffer, size_t *amount_read); -static int s_socket_close(struct aws_socket *socket); +static int s_protocol_socket_close(struct aws_socket *socket); static int s_local_close(struct aws_socket *socket); static int s_ipv4_stream_bind(struct aws_socket *socket, const struct aws_socket_endpoint *local_endpoint); static int s_ipv4_dgram_bind(struct aws_socket *socket, const struct aws_socket_endpoint *local_endpoint); @@ -145,6 +145,38 @@ static int s_ipv6_stream_bind(struct aws_socket *socket, const struct aws_socket static int s_ipv6_dgram_bind(struct aws_socket *socket, const struct aws_socket_endpoint *local_endpoint); static int s_local_bind(struct aws_socket *socket, const struct aws_socket_endpoint *local_endpoint); +static void s_socket_clean_up(struct aws_socket *socket); +static int s_socket_connect( + struct aws_socket *socket, + const struct aws_socket_endpoint *remote_endpoint, + struct aws_event_loop *event_loop, + aws_socket_on_connection_result_fn *on_connection_result, + void *user_data); +static int s_socket_bind(struct aws_socket *socket, const struct aws_socket_endpoint *local_endpoint); +static int s_socket_listen(struct aws_socket *socket, int backlog_size); +static int s_socket_start_accept( + struct aws_socket *socket, + struct aws_event_loop *accept_loop, + aws_socket_on_accept_result_fn *on_accept_result, + void *user_data); +static int s_socket_stop_accept(struct aws_socket *socket); +static int s_socket_set_options(struct aws_socket *socket, const struct aws_socket_options *options); +static int s_socket_close(struct aws_socket *socket); +static int s_socket_shutdown_dir(struct aws_socket *socket, enum aws_channel_direction dir); +static int s_socket_assign_to_event_loop(struct aws_socket *socket, struct aws_event_loop *event_loop); +static int s_socket_subscribe_to_readable_events( + struct aws_socket *socket, + aws_socket_on_readable_fn *on_readable, + void *user_data); +static int s_socket_read(struct aws_socket *socket, struct aws_byte_buf *buffer, size_t *amount_read); +static int s_socket_write( + struct aws_socket *socket, + const struct aws_byte_cursor *cursor, + aws_socket_on_write_completed_fn *written_fn, + void *user_data); +static int s_socket_get_error(struct aws_socket *socket); +static bool s_socket_is_open(struct aws_socket *socket); + static int s_stream_subscribe_to_read( struct aws_socket *socket, aws_socket_on_readable_fn *on_readable, @@ -161,7 +193,7 @@ static int s_determine_socket_error(int error); as well thought out. There were so many branches to handle three entirely different APIs we decided it was less painful to just have a bunch of function pointers in a table than to want to gouge our eyes out while looking at a ridiculous number of branches. */ -static struct socket_vtable vtables[3][2] = { +static struct winsock_vtable s_winsock_vtables[3][2] = { [AWS_SOCKET_IPV4] = { [AWS_SOCKET_STREAM] = @@ -174,7 +206,7 @@ static struct socket_vtable vtables[3][2] = { .bind = s_ipv4_stream_bind, .listen = s_tcp_listen, .read = s_tcp_read, - .close = s_socket_close, + .close = s_protocol_socket_close, .subscribe_to_read = s_stream_subscribe_to_read, }, [AWS_SOCKET_DGRAM] = @@ -187,7 +219,7 @@ static struct socket_vtable vtables[3][2] = { .bind = s_ipv4_dgram_bind, .listen = s_udp_listen, .read = s_dgram_read, - .close = s_socket_close, + .close = s_protocol_socket_close, .subscribe_to_read = s_dgram_subscribe_to_read, }, }, @@ -203,7 +235,7 @@ static struct socket_vtable vtables[3][2] = { .bind = s_ipv6_stream_bind, .listen = s_tcp_listen, .read = s_tcp_read, - .close = s_socket_close, + .close = s_protocol_socket_close, .subscribe_to_read = s_stream_subscribe_to_read, }, [AWS_SOCKET_DGRAM] = @@ -216,7 +248,7 @@ static struct socket_vtable vtables[3][2] = { .bind = s_ipv6_dgram_bind, .listen = s_udp_listen, .read = s_dgram_read, - .close = s_socket_close, + .close = s_protocol_socket_close, .subscribe_to_read = s_dgram_subscribe_to_read, }, }, @@ -239,6 +271,24 @@ static struct socket_vtable vtables[3][2] = { }, }; +struct aws_socket_vtable s_winsock_vtable = { + .socket_cleanup_fn = s_socket_clean_up, + .socket_connect_fn = s_socket_connect, + .socket_bind_fn = s_socket_bind, + .socket_listen_fn = s_socket_listen, + .socket_start_accept_fn = s_socket_start_accept, + .socket_stop_accept_fn = s_socket_stop_accept, + .socket_set_options_fn = s_socket_set_options, + .socket_close_fn = s_socket_close, + .socket_shutdown_dir_fn = s_socket_shutdown_dir, + .socket_assign_to_event_loop_fn = s_socket_assign_to_event_loop, + .socket_subscribe_to_readable_events_fn = s_socket_subscribe_to_readable_events, + .socket_read_fn = s_socket_read, + .socket_write_fn = s_socket_write, + .socket_get_error_fn = s_socket_get_error, + .socket_is_open_fn = s_socket_is_open, +}; + /* When socket is connected, any of the CONNECT_*** flags might be set. Otherwise, only one state flag is active at a time. */ enum socket_state { @@ -298,7 +348,7 @@ struct io_operation_data { }; struct iocp_socket { - struct socket_vtable *vtable; + struct winsock_vtable *winsock_vtable; struct io_operation_data *read_io_data; struct aws_socket *incoming_socket; uint8_t accept_buffer[SOCK_STORAGE_SIZE * 2]; @@ -357,8 +407,10 @@ static int s_socket_init( return AWS_OP_ERR; } - impl->vtable = &vtables[options->domain][options->type]; - if (!impl->vtable || !impl->vtable->read) { + socket->vtable = &s_winsock_vtable; + + impl->winsock_vtable = &s_winsock_vtables[options->domain][options->type]; + if (!impl->winsock_vtable || !impl->winsock_vtable->connection_success) { aws_mem_release(alloc, impl); socket->impl = NULL; return aws_raise_error(AWS_IO_SOCKET_INVALID_OPTIONS); @@ -393,7 +445,10 @@ static int s_socket_init( return AWS_OP_SUCCESS; } -int aws_socket_init(struct aws_socket *socket, struct aws_allocator *alloc, const struct aws_socket_options *options) { +int aws_socket_init_winsock( + struct aws_socket *socket, + struct aws_allocator *alloc, + const struct aws_socket_options *options) { AWS_ASSERT(options); aws_check_and_init_winsock(); @@ -403,7 +458,7 @@ int aws_socket_init(struct aws_socket *socket, struct aws_allocator *alloc, cons return err; } -void aws_socket_clean_up(struct aws_socket *socket) { +static void s_socket_clean_up(struct aws_socket *socket) { if (!socket->impl) { /* protect from double clean */ return; @@ -414,7 +469,7 @@ void aws_socket_clean_up(struct aws_socket *socket) { (void *)socket, (void *)socket->io_handle.data.handle); struct iocp_socket *socket_impl = socket->impl; - socket_impl->vtable->close(socket); + socket_impl->winsock_vtable->close(socket); if (socket_impl->incoming_socket) { aws_socket_clean_up(socket_impl->incoming_socket); @@ -430,7 +485,7 @@ void aws_socket_clean_up(struct aws_socket *socket) { socket->io_handle.data.handle = INVALID_HANDLE_VALUE; } -int aws_socket_connect( +static int s_socket_connect( struct aws_socket *socket, const struct aws_socket_endpoint *remote_endpoint, struct aws_event_loop *event_loop, @@ -455,10 +510,10 @@ int aws_socket_connect( return AWS_OP_ERR; } - return socket_impl->vtable->connect(socket, remote_endpoint, event_loop, on_connection_result, user_data); + return socket_impl->winsock_vtable->connect(socket, remote_endpoint, event_loop, on_connection_result, user_data); } -int aws_socket_bind(struct aws_socket *socket, const struct aws_socket_endpoint *local_endpoint) { +static int s_socket_bind(struct aws_socket *socket, const struct aws_socket_endpoint *local_endpoint) { if (socket->state != INIT) { socket->state = ERRORED; return aws_raise_error(AWS_IO_SOCKET_ILLEGAL_OPERATION_FOR_STATE); @@ -469,20 +524,7 @@ int aws_socket_bind(struct aws_socket *socket, const struct aws_socket_endpoint } struct iocp_socket *socket_impl = socket->impl; - return socket_impl->vtable->bind(socket, local_endpoint); -} - -int aws_socket_get_bound_address(const struct aws_socket *socket, struct aws_socket_endpoint *out_address) { - if (socket->local_endpoint.address[0] == 0) { - AWS_LOGF_ERROR( - AWS_LS_IO_SOCKET, - "id=%p fd=%d: Socket has no local address. Socket must be bound first.", - (void *)socket, - socket->io_handle.data.fd); - return aws_raise_error(AWS_IO_SOCKET_ILLEGAL_OPERATION_FOR_STATE); - } - *out_address = socket->local_endpoint; - return AWS_OP_SUCCESS; + return socket_impl->winsock_vtable->bind(socket, local_endpoint); } /* Update IPV4 or IPV6 socket->local_endpoint based on the results of getsockname() */ @@ -542,31 +584,31 @@ static int s_update_local_endpoint_ipv4_ipv6(struct aws_socket *socket) { return AWS_OP_SUCCESS; } -int aws_socket_listen(struct aws_socket *socket, int backlog_size) { +static int s_socket_listen(struct aws_socket *socket, int backlog_size) { struct iocp_socket *socket_impl = socket->impl; - return socket_impl->vtable->listen(socket, backlog_size); + return socket_impl->winsock_vtable->listen(socket, backlog_size); } -int aws_socket_start_accept( +static int s_socket_start_accept( struct aws_socket *socket, struct aws_event_loop *accept_loop, aws_socket_on_accept_result_fn *on_accept_result, void *user_data) { struct iocp_socket *socket_impl = socket->impl; - return socket_impl->vtable->start_accept(socket, accept_loop, on_accept_result, user_data); + return socket_impl->winsock_vtable->start_accept(socket, accept_loop, on_accept_result, user_data); } -int aws_socket_stop_accept(struct aws_socket *socket) { +static int s_socket_stop_accept(struct aws_socket *socket) { struct iocp_socket *socket_impl = socket->impl; - return socket_impl->vtable->stop_accept(socket); + return socket_impl->winsock_vtable->stop_accept(socket); } -int aws_socket_close(struct aws_socket *socket) { +static int s_socket_close(struct aws_socket *socket) { struct iocp_socket *socket_impl = socket->impl; - return socket_impl->vtable->close(socket); + return socket_impl->winsock_vtable->close(socket); } -int aws_socket_shutdown_dir(struct aws_socket *socket, enum aws_channel_direction dir) { +static int s_socket_shutdown_dir(struct aws_socket *socket, enum aws_channel_direction dir) { int how = dir == AWS_CHANNEL_DIR_READ ? 0 : 1; if (shutdown((SOCKET)socket->io_handle.data.handle, how)) { @@ -583,7 +625,7 @@ int aws_socket_shutdown_dir(struct aws_socket *socket, enum aws_channel_directio return AWS_OP_SUCCESS; } -int aws_socket_read(struct aws_socket *socket, struct aws_byte_buf *buffer, size_t *amount_read) { +static int s_socket_read(struct aws_socket *socket, struct aws_byte_buf *buffer, size_t *amount_read) { struct iocp_socket *socket_impl = socket->impl; AWS_ASSERT(socket->readable_fn); @@ -605,10 +647,10 @@ int aws_socket_read(struct aws_socket *socket, struct aws_byte_buf *buffer, size return aws_raise_error(AWS_IO_SOCKET_NOT_CONNECTED); } - return socket_impl->vtable->read(socket, buffer, amount_read); + return socket_impl->winsock_vtable->read(socket, buffer, amount_read); } -int aws_socket_subscribe_to_readable_events( +static int s_socket_subscribe_to_readable_events( struct aws_socket *socket, aws_socket_on_readable_fn *on_readable, void *user_data) { @@ -625,7 +667,7 @@ int aws_socket_subscribe_to_readable_events( return aws_raise_error(AWS_IO_SOCKET_NOT_CONNECTED); } - return socket_impl->vtable->subscribe_to_read(socket, on_readable, user_data); + return socket_impl->winsock_vtable->subscribe_to_read(socket, on_readable, user_data); } static int s_determine_socket_error(int error) { @@ -735,7 +777,7 @@ static int s_ipv4_stream_connection_success(struct aws_socket *socket) { return AWS_OP_SUCCESS; error: socket->state = ERRORED; - socket_impl->vtable->connection_error(socket, aws_last_error()); + socket_impl->winsock_vtable->connection_error(socket, aws_last_error()); return AWS_OP_ERR; } @@ -798,7 +840,7 @@ static int s_ipv6_stream_connection_success(struct aws_socket *socket) { error: socket->state = ERRORED; - socket_impl->vtable->connection_error(socket, aws_last_error()); + socket_impl->winsock_vtable->connection_error(socket, aws_last_error()); return AWS_OP_ERR; } @@ -871,7 +913,7 @@ void s_socket_connection_completion( socket_args->socket = NULL; if (!status_code) { - socket_impl->vtable->connection_success(socket); + socket_impl->winsock_vtable->connection_success(socket); } else { AWS_LOGF_ERROR( AWS_LS_IO_SOCKET, @@ -880,7 +922,7 @@ void s_socket_connection_completion( (void *)socket->io_handle.data.handle, status_code); int error = s_determine_socket_error(status_code); - socket_impl->vtable->connection_error(socket, error); + socket_impl->winsock_vtable->connection_error(socket, error); } } @@ -1175,7 +1217,7 @@ static void s_connection_success_task(struct aws_task *task, void *arg, enum aws struct aws_socket *socket = io_data->socket; struct iocp_socket *socket_impl = socket->impl; - socket_impl->vtable->connection_success(socket); + socket_impl->winsock_vtable->connection_success(socket); } /* initiate the client end of a named pipe. */ @@ -1663,7 +1705,7 @@ static void s_incoming_pipe_connection_event( socket->state = ERRORED; } - socket_impl->vtable->connection_error(socket, aws_last_error()); + socket_impl->winsock_vtable->connection_error(socket, aws_last_error()); operation_data->in_use = false; return; } @@ -1681,7 +1723,7 @@ static void s_incoming_pipe_connection_event( if (!new_socket) { socket->state = ERRORED; operation_data->in_use = false; - socket_impl->vtable->connection_error(socket, AWS_ERROR_OOM); + socket_impl->winsock_vtable->connection_error(socket, AWS_ERROR_OOM); return; } @@ -1689,7 +1731,7 @@ static void s_incoming_pipe_connection_event( aws_mem_release(socket->allocator, new_socket); socket->state = ERRORED; operation_data->in_use = false; - socket_impl->vtable->connection_error(socket, aws_last_error()); + socket_impl->winsock_vtable->connection_error(socket, aws_last_error()); return; } @@ -1721,7 +1763,7 @@ static void s_incoming_pipe_connection_event( (int)GetLastError()); socket->state = ERRORED; operation_data->in_use = false; - socket_impl->vtable->connection_error(socket, aws_last_error()); + socket_impl->winsock_vtable->connection_error(socket, aws_last_error()); return; } @@ -1731,7 +1773,7 @@ static void s_incoming_pipe_connection_event( socket->state = ERRORED; operation_data->in_use = false; aws_socket_clean_up(new_socket); - socket_impl->vtable->connection_error(socket, aws_last_error()); + socket_impl->winsock_vtable->connection_error(socket, aws_last_error()); return; } @@ -1762,7 +1804,7 @@ static void s_incoming_pipe_connection_event( socket->state = ERRORED; socket_impl->read_io_data->in_use = false; int aws_err = s_determine_socket_error(error_code); - socket_impl->vtable->connection_error(socket, aws_err); + socket_impl->winsock_vtable->connection_error(socket, aws_err); return; } else if (error_code == ERROR_PIPE_CONNECTED) { continue_accept_loop = true; @@ -1953,7 +1995,7 @@ static void s_tcp_accept_event( if (err) { if (aws_last_error() != AWS_IO_READ_WOULD_BLOCK) { socket->state = ERRORED; - socket_impl->vtable->connection_error(socket, aws_last_error()); + socket_impl->winsock_vtable->connection_error(socket, aws_last_error()); } return; } @@ -1968,7 +2010,7 @@ static void s_tcp_accept_event( socket->state = ERRORED; int aws_error = s_determine_socket_error(status_code); aws_raise_error(aws_error); - socket_impl->vtable->connection_error(socket, aws_error); + socket_impl->winsock_vtable->connection_error(socket, aws_error); operation_data->in_use = false; } } @@ -2242,7 +2284,7 @@ static int s_dgram_stop_accept(struct aws_socket *socket) { return aws_raise_error(AWS_IO_SOCKET_ILLEGAL_OPERATION_FOR_STATE); } -int aws_socket_set_options(struct aws_socket *socket, const struct aws_socket_options *options) { +static int s_socket_set_options(struct aws_socket *socket, const struct aws_socket_options *options) { if (socket->options.domain != options->domain || socket->options.type != options->type) { return aws_raise_error(AWS_IO_SOCKET_INVALID_OPTIONS); } @@ -2369,8 +2411,6 @@ static bool s_close_predicate(void *arg) { return close_args->invoked; } -static int s_socket_close(struct aws_socket *socket); - static void s_close_task(struct aws_task *task, void *arg, enum aws_task_status status) { (void)task; @@ -2438,7 +2478,7 @@ static int s_wait_on_close(struct aws_socket *socket) { return AWS_OP_SUCCESS; } -static int s_socket_close(struct aws_socket *socket) { +static int s_protocol_socket_close(struct aws_socket *socket) { struct iocp_socket *socket_impl = socket->impl; AWS_LOGF_DEBUG(AWS_LS_IO_SOCKET, "id=%p handle=%p: closing", (void *)socket, (void *)socket->io_handle.data.handle); @@ -2539,7 +2579,7 @@ int aws_socket_half_close(struct aws_socket *socket, enum aws_channel_direction int error = WSAGetLastError(); int aws_error = s_determine_socket_error(error); aws_raise_error(aws_error); - socket_impl->vtable->connection_error(socket, aws_error); + socket_impl->winsock_vtable->connection_error(socket, aws_error); return AWS_OP_ERR; } @@ -2550,7 +2590,7 @@ struct aws_io_handle *aws_socket_get_io_handle(struct aws_socket *socket) { return &socket->io_handle; } -int aws_socket_assign_to_event_loop(struct aws_socket *socket, struct aws_event_loop *event_loop) { +static int s_socket_assign_to_event_loop(struct aws_socket *socket, struct aws_event_loop *event_loop) { if (socket->event_loop) { return aws_raise_error(AWS_IO_EVENT_LOOP_ALREADY_ASSIGNED); } @@ -2559,10 +2599,6 @@ int aws_socket_assign_to_event_loop(struct aws_socket *socket, struct aws_event_ return aws_event_loop_connect_handle_to_io_completion_port(event_loop, &socket->io_handle); } -struct aws_event_loop *aws_socket_get_event_loop(struct aws_socket *socket) { - return socket->event_loop; -} - struct read_cb_args { struct aws_socket *socket; aws_socket_on_readable_fn *user_callback; @@ -3167,7 +3203,7 @@ static void s_socket_written_event( aws_mem_release(operation_data->allocator, write_cb_args); } -int aws_socket_write( +static int s_socket_write( struct aws_socket *socket, const struct aws_byte_cursor *cursor, aws_socket_on_write_completed_fn *written_fn, @@ -3241,7 +3277,7 @@ int aws_socket_write( return AWS_OP_SUCCESS; } -int aws_socket_get_error(struct aws_socket *socket) { +static int s_socket_get_error(struct aws_socket *socket) { if (socket->options.domain != AWS_SOCKET_LOCAL) { int connect_result; socklen_t result_length = sizeof(connect_result); @@ -3261,19 +3297,10 @@ int aws_socket_get_error(struct aws_socket *socket) { return AWS_OP_SUCCESS; } -bool aws_socket_is_open(struct aws_socket *socket) { +static bool s_socket_is_open(struct aws_socket *socket) { return socket->io_handle.data.handle != INVALID_HANDLE_VALUE; } -void aws_socket_endpoint_init_local_address_for_test(struct aws_socket_endpoint *endpoint) { - struct aws_uuid uuid; - AWS_FATAL_ASSERT(aws_uuid_init(&uuid) == AWS_OP_SUCCESS); - char uuid_str[AWS_UUID_STR_LEN] = {0}; - struct aws_byte_buf uuid_buf = aws_byte_buf_from_empty_array(uuid_str, sizeof(uuid_str)); - AWS_FATAL_ASSERT(aws_uuid_to_str(&uuid, &uuid_buf) == AWS_OP_SUCCESS); - snprintf(endpoint->address, sizeof(endpoint->address), "\\\\.\\pipe\\testsock" PRInSTR, AWS_BYTE_BUF_PRI(uuid_buf)); -} - bool aws_is_network_interface_name_valid(const char *interface_name) { (void)interface_name; AWS_LOGF_ERROR(AWS_LS_IO_SOCKET, "network_interface_names are not supported on Windows"); diff --git a/source/windows/winsock_init.c b/source/windows/winsock_init.c index 669ae84b8..cba580e56 100644 --- a/source/windows/winsock_init.c +++ b/source/windows/winsock_init.c @@ -14,6 +14,7 @@ below, clang-format doesn't work (at least on my version) with the c-style comme // clang-format on #include +#include #include #include diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index dc4c07b41..294f86060 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -50,9 +50,11 @@ add_test_case(event_loop_multiple_stops) add_test_case(event_loop_group_setup_and_shutdown) add_test_case(event_loop_group_setup_and_shutdown_async) add_test_case(numa_aware_event_loop_group_setup_and_shutdown) +add_test_case(event_loop_all_types_creation) add_test_case(io_testing_channel) +add_test_case(test_socket_impl_types_creation) add_test_case(local_socket_communication) add_net_test_case(tcp_socket_communication) add_net_test_case(udp_socket_communication) diff --git a/tests/event_loop_test.c b/tests/event_loop_test.c index 92b739156..3cc319f96 100644 --- a/tests/event_loop_test.c +++ b/tests/event_loop_test.c @@ -172,7 +172,7 @@ static int s_test_event_loop_canceled_tasks_run_in_el_thread(struct aws_allocato AWS_TEST_CASE(event_loop_canceled_tasks_run_in_el_thread, s_test_event_loop_canceled_tasks_run_in_el_thread) -#if AWS_USE_IO_COMPLETION_PORTS +#if AWS_ENABLE_IO_COMPLETION_PORTS int aws_pipe_get_unique_name(char *dst, size_t dst_size); @@ -311,7 +311,7 @@ static int s_test_event_loop_completion_events(struct aws_allocator *allocator, AWS_TEST_CASE(event_loop_completion_events, s_test_event_loop_completion_events) -#else /* !AWS_USE_IO_COMPLETION_PORTS */ +#else /* !AWS_ENABLE_IO_COMPLETION_PORTS */ # include @@ -971,7 +971,60 @@ static int s_test_event_loop_readable_event_on_2nd_time_readable(struct aws_allo } AWS_TEST_CASE(event_loop_readable_event_on_2nd_time_readable, s_test_event_loop_readable_event_on_2nd_time_readable); -#endif /* AWS_USE_IO_COMPLETION_PORTS */ +#endif /* AWS_ENABLE_IO_COMPLETION_PORTS */ + +/* Verify default event loop type */ +static int s_test_event_loop_creation( + struct aws_allocator *allocator, + enum aws_event_loop_type type, + bool expect_success) { + struct aws_event_loop_options event_loop_options = { + .thread_options = NULL, + .clock = aws_high_res_clock_get_ticks, + .type = type, + }; + + struct aws_event_loop *event_loop = aws_event_loop_new(allocator, &event_loop_options); + + if (expect_success) { + ASSERT_NOT_NULL(event_loop); + /* Clean up tester*/ + aws_event_loop_destroy(event_loop); + } else { + ASSERT_NULL(event_loop); + } + + return AWS_OP_SUCCESS; +} + +/* Verify default event loop type */ +static int s_test_event_loop_all_types_creation(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + bool enable_kqueue = false; + bool enable_epoll = false; + bool enable_iocp = false; + bool enable_dispatch_queue = false; +#ifdef AWS_ENABLE_KQUEUE + enable_kqueue = true; +#endif +#ifdef AWS_ENABLE_EPOLL + enable_epoll = true; +#endif +#ifdef AWS_ENABLE_IO_COMPLETION_PORTS + enable_iocp = true; +#endif +#ifdef AWS_ENABLE_DISPATCH_QUEUE +// TODO: Dispatch queue support is not yet implemented. Uncomment the following line once the dispatch queue is ready. +// enable_dispatch_queue = true; +#endif + + return s_test_event_loop_creation(allocator, AWS_EVENT_LOOP_EPOLL, enable_epoll) || + s_test_event_loop_creation(allocator, AWS_EVENT_LOOP_IOCP, enable_iocp) || + s_test_event_loop_creation(allocator, AWS_EVENT_LOOP_KQUEUE, enable_kqueue) || + s_test_event_loop_creation(allocator, AWS_EVENT_LOOP_DISPATCH_QUEUE, enable_dispatch_queue); +} + +AWS_TEST_CASE(event_loop_all_types_creation, s_test_event_loop_all_types_creation) static int s_event_loop_test_stop_then_restart(struct aws_allocator *allocator, void *ctx) { (void)ctx; diff --git a/tests/socket_handler_test.c b/tests/socket_handler_test.c index ee7290d4e..1f301bfee 100644 --- a/tests/socket_handler_test.c +++ b/tests/socket_handler_test.c @@ -994,7 +994,7 @@ static struct aws_event_loop *s_default_new_event_loop( void *user_data) { (void)user_data; - return aws_event_loop_new_default_with_options(allocator, options); + return aws_event_loop_new(allocator, options); } static int s_statistic_test_clock_fn(uint64_t *timestamp) { diff --git a/tests/socket_test.c b/tests/socket_test.c index e01834a75..f96b20e4f 100644 --- a/tests/socket_test.c +++ b/tests/socket_test.c @@ -389,6 +389,47 @@ static int s_test_socket_ex( return 0; } +static int s_test_socket_creation(struct aws_allocator *alloc, enum aws_socket_impl_type type, int expected_result) { + struct aws_socket socket; + + struct aws_socket_options options = { + .type = AWS_SOCKET_STREAM, + .domain = AWS_SOCKET_IPV4, + .keep_alive_interval_sec = 0, + .keep_alive_timeout_sec = 0, + .connect_timeout_ms = 0, + .keepalive = 0, + .impl_type = type, + }; + + int err = aws_socket_init(&socket, alloc, &options); + if (err == AWS_OP_SUCCESS) { + aws_socket_clean_up(&socket); + ASSERT_INT_EQUALS(err, expected_result); + } else { // socket init failed, validate the last error + ASSERT_INT_EQUALS(aws_last_error(), expected_result); + } + return AWS_OP_SUCCESS; +} + +static int s_test_socket_impl_types_creation(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + int posix_expected_result = AWS_ERROR_PLATFORM_NOT_SUPPORTED; + int winsock_expected_result = AWS_ERROR_PLATFORM_NOT_SUPPORTED; +#if defined(AWS_ENABLE_KQUEUE) || defined(AWS_ENABLE_EPOLL) + posix_expected_result = AWS_OP_SUCCESS; +#endif +#ifdef AWS_ENABLE_IO_COMPLETION_PORTS + winsock_expected_result = AWS_OP_SUCCESS; +#endif + // TODO: Apple Network Framework is not implemented yet. Add the related socket test later. + + return s_test_socket_creation(allocator, AWS_SOCKET_IMPL_POSIX, posix_expected_result) || + s_test_socket_creation(allocator, AWS_SOCKET_IMPL_WINSOCK, winsock_expected_result); +} + +AWS_TEST_CASE(test_socket_impl_types_creation, s_test_socket_impl_types_creation) + static int s_test_socket( struct aws_allocator *allocator, struct aws_socket_options *options, diff --git a/tests/tls_handler_test.c b/tests/tls_handler_test.c index 7b1a68c32..f943c3371 100644 --- a/tests/tls_handler_test.c +++ b/tests/tls_handler_test.c @@ -1890,7 +1890,7 @@ static struct aws_event_loop *s_default_new_event_loop( void *user_data) { (void)user_data; - return aws_event_loop_new_default_with_options(allocator, options); + return aws_event_loop_new(allocator, options); } static int s_statistic_test_clock_fn(uint64_t *timestamp) {