From 909fe2e7443f85bcb4d3f36e4bccd69bbec95257 Mon Sep 17 00:00:00 2001 From: Stephen Seo Date: Sat, 26 Jan 2019 16:22:31 +0900 Subject: [PATCH] Add some init/destruct stuff, thread stuff too --- CMakeLists.txt | 12 +++- src/UDPConnection.c | 160 ++++++++++++++++++++++++++++++++++++++++++++ src/UDPConnection.h | 66 +++++++++++++++++- 3 files changed, 235 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b6730b3..9fed4fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,16 @@ set(UDPConnection_SOURCES src/UDPConnection.c ) +set(CMAKE_C_FLAGS "-Wall -Wno-missing-braces") +set(CMAKE_C_FLAGS_DEBUG "-O0 -g") +set(CMAKE_C_FLAGS_RELEASE "-O3 -D NDEBUG") + +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message(STATUS "Setting build type to 'Debug', none was specified.") + set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release") +endif() + add_library(UDPConnection ${UDPConnection_SOURCES}) - +target_compile_features(UDPConnection PUBLIC c_std_11) diff --git a/src/UDPConnection.c b/src/UDPConnection.c index 95d32e2..a78aa18 100644 --- a/src/UDPConnection.c +++ b/src/UDPConnection.c @@ -1 +1,161 @@ #include "UDPConnection.h" + +#include + +UDPC_Context UDPC_init(unsigned short listenPort) +{ + UDPC_Context context; + context.error = 0; + context.flags = 0; + context.threadFlags = 0; + + // create socket + context.socketHandle = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if(context.socketHandle <= 0) + { + context.socketHandle = 0; + context.error = UDPCON_ERR_SOCKETFAIL; + fprintf(stderr, "Failed to create socket\n"); + return context; + } + + // bind socket + context.socketInfo.sin_family = AF_INET; + context.socketInfo.sin_addr.s_addr = INADDR_ANY; + context.socketInfo.sin_port = listenPort; + if(bind( + context.socketHandle, + (const struct sockaddr*) &context.socketInfo, + sizeof(struct sockaddr_in) + ) < 0) + { + context.error = UDPCON_ERR_SOCKETBINDF; + CleanupSocket(context.socketHandle); + context.socketHandle = 0; + fprintf(stderr, "Failed to bind socket\n"); + return context; + } + + // set nonblocking on socket +#if PLATFORM == PLATFORM_MAC || PLATFORM == PLATFORM_LINUX + int nonblocking = 1; + if(fcntl(context.socketHandle, F_SETFL, O_NONBLOCK, nonblocking) == -1) + { +#elif PLATFORM == PLATFORM_WINDOWS + DWORD nonblocking = 1; + if(ioctlsocket(context.socketHandle, FIONBIO, &nonblocking) != 0) + { +#endif + context.error = UDPCON_ERR_SOCKETNONBF; + CleanupSocket(context.socketHandle); + context.socketHandle = 0; + fprintf(stderr, "Failed to set non-blocking on socket\n"); + return context; + } + + return context; +} + +UDPC_Context UDPC_init_threaded_update(unsigned short listenPort) +{ + UDPC_Context context = UDPC_init(listenPort); + + context.error = mtx_init(&context.tCVMtx, mtx_timed); + if(context.error != thrd_success) + { + CleanupSocket(context.socketHandle); + context.socketHandle = 0; + fprintf(stderr, "Failed to create mutex\n"); + context.error = UDPCON_ERR_MTXFAIL; + } + context.error = 0; + + context.error = mtx_init(&context.tflagsMtx, mtx_timed); + if(context.error != thrd_success) + { + CleanupSocket(context.socketHandle); + context.socketHandle = 0; + mtx_destroy(&context.tCVMtx); + fprintf(stderr, "Failed to create mutex\n"); + context.error = UDPCON_ERR_MTXFAIL; + return context; + } + context.error = 0; + + context.error = cnd_init(&context.threadCV); + if(context.error != thrd_success) + { + CleanupSocket(context.socketHandle); + context.socketHandle = 0; + mtx_destroy(&context.tCVMtx); + mtx_destroy(&context.tflagsMtx); + fprintf(stderr, "Failed to create condition variable\n"); + context.error = UDPCON_ERR_CVFAIL; + return context; + } + context.error = 0; + + context.error = thrd_create( + &context.threadHandle, UDPC_INTERNAL_threadfn, &context); + if(context.error != thrd_success) + { + CleanupSocket(context.socketHandle); + context.socketHandle = 0; + mtx_destroy(&context.tCVMtx); + mtx_destroy(&context.tflagsMtx); + cnd_destroy(&context.threadCV); + fprintf(stderr, "Failed to create thread\n"); + context.error = UDPCON_ERR_THREADFAIL; + return context; + } + context.error = 0; + + return context; +} + +void UDPC_destroy(UDPC_Context *ctx) +{ + CleanupSocket(ctx->socketHandle); + + if((ctx->flags & 0x1) != 0) + { + mtx_lock(&ctx->tflagsMtx); + ctx->threadFlags |= 0x1; + mtx_unlock(&ctx->tflagsMtx); + cnd_broadcast(&ctx->threadCV); + + thrd_join(ctx->threadHandle, NULL); + + mtx_destroy(&ctx->tCVMtx); + mtx_destroy(&ctx->tflagsMtx); + cnd_destroy(&ctx->threadCV); + } +} + +int UDPC_INTERNAL_threadfn(void *context) +{ + UDPC_Context *ctx = (UDPC_Context*)context; + + int shouldStop = 0; + struct timespec ts; + + while(shouldStop == 0) + { + timespec_get(&ts, TIME_UTC); + ts.tv_nsec += 16666666; + while(ts.tv_nsec >= 1000000000) + { + ts.tv_nsec -= 1000000000; + ts.tv_sec += 1; + } + mtx_lock(&ctx->tCVMtx); + cnd_timedwait(&ctx->threadCV, &ctx->tCVMtx, &ts); + mtx_unlock(&ctx->tCVMtx); + + mtx_lock(&ctx->tflagsMtx); + shouldStop = ctx->threadFlags & 0x1; + mtx_unlock(&ctx->tflagsMtx); + } + + return 0; +} diff --git a/src/UDPConnection.h b/src/UDPConnection.h index 365b317..15e3954 100644 --- a/src/UDPConnection.h +++ b/src/UDPConnection.h @@ -1,8 +1,70 @@ #ifndef UDPCONNECTION_H #define UDPCONNECTION_H -struct UDPC_Context +#include +#include + +#define PLATFORM_WINDOWS 1 +#define PLATFORM_MAC 2 +#define PLATFORM_LINUX 3 +#define PLATFORM_UNKNOWN 0 + +#if defined _WIN32 + #define PLATFORM PLATFORM_WINDOWS +#elif defined __APPLE__ + #define PLATFORM PLATFORM_MAC +#elif defined __linux__ + #define PLATFORM PLATFORM_LINUX +#else + #define PLATFORM PLATFORM_UNKNOWN +#endif + +#if PLATFORM == PLATFORM_WINDOWS + #include + + #define CleanupSocket(x) closesocket(x) +#elif PLATFORM == PLATFORM_MAC || PLATFORM == PLATFORM_LINUX + #include + #include + #include + #include + + #define CleanupSocket(x) close(x) +#endif + +#define UDPCON_ERR_SOCKETFAIL 1 // failed to create socket +#define UDPCON_ERR_SOCKETBINDF 2 // failed to bind socket +#define UDPCON_ERR_SOCKETNONBF 3 // failed to set non-blocking on socket +#define UDPCON_ERR_MTXFAIL 4 // failed to create mutex +#define UDPCON_ERR_CVFAIL 5 // failed to create condition variable +#define UDPCON_ERR_THREADFAIL 6 // failed to create thread + +// This struct should not be modified, only passed to functions that require it +typedef struct { -}; + int error; + int socketHandle; + struct sockaddr_in socketInfo; + thrd_t threadHandle; + mtx_t tCVMtx; + mtx_t tflagsMtx; + cnd_t threadCV; + /* + * 0x1 - is threaded + */ + int flags; + /* + * 0x1 - thread should stop + */ + int threadFlags; +} UDPC_Context; + +UDPC_Context UDPC_init(unsigned short listenPort); + +UDPC_Context UDPC_init_threaded_update(unsigned short listenPort); + +void UDPC_destroy(UDPC_Context *ctx); + +int UDPC_INTERNAL_threadfn(void *context); // internal usage only #endif