diff --git a/config.mk b/config.mk index a0ac27c..f7c6e47 100644 --- a/config.mk +++ b/config.mk @@ -25,14 +25,17 @@ # for Windows CROSS_COMPILE ?=i686-w64-mingw32- LIBS ?= -lws2_32 +DEBUG_OPTIONS ?= else # For Linux ifeq ($(strip $(ARCH)),aarch64) CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector else CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector endif endif diff --git a/config.mk b/config.mk index a0ac27c..f7c6e47 100644 --- a/config.mk +++ b/config.mk @@ -25,14 +25,17 @@ # for Windows CROSS_COMPILE ?=i686-w64-mingw32- LIBS ?= -lws2_32 +DEBUG_OPTIONS ?= else # For Linux ifeq ($(strip $(ARCH)),aarch64) CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector else CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector endif endif diff --git a/mk/base-cmd.mk b/mk/base-cmd.mk index f429bcf..e74c114 100644 --- a/mk/base-cmd.mk +++ b/mk/base-cmd.mk @@ -33,8 +33,13 @@ CC = $(CROSS_COMPILE)gcc CXX = $(CROSS_COMPILE)g++ + #CC = $(CROSS_COMPILE)clang #CXX = $(CROSS_COMPILE)clang++ + +#CC = $(CROSS_COMPILE)clang-16 +#CXX = $(CROSS_COMPILE)clang++16 + RANLIB = $(CROSS_COMPILE)ranlib ADDR2LINE = $(CROSS_COMPILE)addr2line AR = $(CROSS_COMPILE)ar diff --git a/config.mk b/config.mk index a0ac27c..f7c6e47 100644 --- a/config.mk +++ b/config.mk @@ -25,14 +25,17 @@ # for Windows CROSS_COMPILE ?=i686-w64-mingw32- LIBS ?= -lws2_32 +DEBUG_OPTIONS ?= else # For Linux ifeq ($(strip $(ARCH)),aarch64) CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector else CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector endif endif diff --git a/mk/base-cmd.mk b/mk/base-cmd.mk index f429bcf..e74c114 100644 --- a/mk/base-cmd.mk +++ b/mk/base-cmd.mk @@ -33,8 +33,13 @@ CC = $(CROSS_COMPILE)gcc CXX = $(CROSS_COMPILE)g++ + #CC = $(CROSS_COMPILE)clang #CXX = $(CROSS_COMPILE)clang++ + +#CC = $(CROSS_COMPILE)clang-16 +#CXX = $(CROSS_COMPILE)clang++16 + RANLIB = $(CROSS_COMPILE)ranlib ADDR2LINE = $(CROSS_COMPILE)addr2line AR = $(CROSS_COMPILE)ar diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 2cc417c..eebded1 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -69,7 +69,7 @@ # 値が2の場合、さらなるチェックを追加するが、規格準拠のプログラムが失敗する可能性がある。 # いくつかのチェックは、コンパイル時に実行され、コンパイラの警告として表示される。 # ------------------------------------------------------------------------------ -DEBUG_OPTIONS += -fstack-protector +# DEBUG_OPTIONS += -fstack-protector DEBUG_OPTIONS += --coverage DEBUG_OPTIONS += -g3 -ggdb DEBUG_OPTIONS += -O0 diff --git a/config.mk b/config.mk index a0ac27c..f7c6e47 100644 --- a/config.mk +++ b/config.mk @@ -25,14 +25,17 @@ # for Windows CROSS_COMPILE ?=i686-w64-mingw32- LIBS ?= -lws2_32 +DEBUG_OPTIONS ?= else # For Linux ifeq ($(strip $(ARCH)),aarch64) CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector else CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector endif endif diff --git a/mk/base-cmd.mk b/mk/base-cmd.mk index f429bcf..e74c114 100644 --- a/mk/base-cmd.mk +++ b/mk/base-cmd.mk @@ -33,8 +33,13 @@ CC = $(CROSS_COMPILE)gcc CXX = $(CROSS_COMPILE)g++ + #CC = $(CROSS_COMPILE)clang #CXX = $(CROSS_COMPILE)clang++ + +#CC = $(CROSS_COMPILE)clang-16 +#CXX = $(CROSS_COMPILE)clang++16 + RANLIB = $(CROSS_COMPILE)ranlib ADDR2LINE = $(CROSS_COMPILE)addr2line AR = $(CROSS_COMPILE)ar diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 2cc417c..eebded1 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -69,7 +69,7 @@ # 値が2の場合、さらなるチェックを追加するが、規格準拠のプログラムが失敗する可能性がある。 # いくつかのチェックは、コンパイル時に実行され、コンパイラの警告として表示される。 # ------------------------------------------------------------------------------ -DEBUG_OPTIONS += -fstack-protector +# DEBUG_OPTIONS += -fstack-protector DEBUG_OPTIONS += --coverage DEBUG_OPTIONS += -g3 -ggdb DEBUG_OPTIONS += -O0 diff --git a/mk/link-a-conf.mk b/mk/link-a-conf.mk index 0e872f3..631e6d5 100644 --- a/mk/link-a-conf.mk +++ b/mk/link-a-conf.mk @@ -10,8 +10,11 @@ HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) +HEADER_FILES_WIN = $(wildcard include/win/*.h) $(wildcard include/win/*.hpp) +TOP_HEADER_FILES_WIN = $(addprefix $(TOPDIR)/include/win/,$(notdir $(HEADER_FILES_WIN))) + CLEAN_FILES += $(TOP_TARGET) -CLEAN_FILES += $(TOP_HEADER_FILES) +CLEAN_FILES += $(TOP_HEADER_FILES) $(TOP_HEADER_FILES_WIN) endif endif diff --git a/config.mk b/config.mk index a0ac27c..f7c6e47 100644 --- a/config.mk +++ b/config.mk @@ -25,14 +25,17 @@ # for Windows CROSS_COMPILE ?=i686-w64-mingw32- LIBS ?= -lws2_32 +DEBUG_OPTIONS ?= else # For Linux ifeq ($(strip $(ARCH)),aarch64) CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector else CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector endif endif diff --git a/mk/base-cmd.mk b/mk/base-cmd.mk index f429bcf..e74c114 100644 --- a/mk/base-cmd.mk +++ b/mk/base-cmd.mk @@ -33,8 +33,13 @@ CC = $(CROSS_COMPILE)gcc CXX = $(CROSS_COMPILE)g++ + #CC = $(CROSS_COMPILE)clang #CXX = $(CROSS_COMPILE)clang++ + +#CC = $(CROSS_COMPILE)clang-16 +#CXX = $(CROSS_COMPILE)clang++16 + RANLIB = $(CROSS_COMPILE)ranlib ADDR2LINE = $(CROSS_COMPILE)addr2line AR = $(CROSS_COMPILE)ar diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 2cc417c..eebded1 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -69,7 +69,7 @@ # 値が2の場合、さらなるチェックを追加するが、規格準拠のプログラムが失敗する可能性がある。 # いくつかのチェックは、コンパイル時に実行され、コンパイラの警告として表示される。 # ------------------------------------------------------------------------------ -DEBUG_OPTIONS += -fstack-protector +# DEBUG_OPTIONS += -fstack-protector DEBUG_OPTIONS += --coverage DEBUG_OPTIONS += -g3 -ggdb DEBUG_OPTIONS += -O0 diff --git a/mk/link-a-conf.mk b/mk/link-a-conf.mk index 0e872f3..631e6d5 100644 --- a/mk/link-a-conf.mk +++ b/mk/link-a-conf.mk @@ -10,8 +10,11 @@ HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) +HEADER_FILES_WIN = $(wildcard include/win/*.h) $(wildcard include/win/*.hpp) +TOP_HEADER_FILES_WIN = $(addprefix $(TOPDIR)/include/win/,$(notdir $(HEADER_FILES_WIN))) + CLEAN_FILES += $(TOP_TARGET) -CLEAN_FILES += $(TOP_HEADER_FILES) +CLEAN_FILES += $(TOP_HEADER_FILES) $(TOP_HEADER_FILES_WIN) endif endif diff --git a/mk/link-a-rule.mk b/mk/link-a-rule.mk index d94a54a..657d858 100644 --- a/mk/link-a-rule.mk +++ b/mk/link-a-rule.mk @@ -10,6 +10,7 @@ $(TOP_TARGET): $(TARGET) ifneq ($(strip $(HEADER_FILES)),) $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ + $(CP) -f $(HEADER_FILES_WIN) $(TOPDIR)/include/win/ endif $(CP) -f $(TARGET) $(TOPDIR)/lib/ diff --git a/config.mk b/config.mk index a0ac27c..f7c6e47 100644 --- a/config.mk +++ b/config.mk @@ -25,14 +25,17 @@ # for Windows CROSS_COMPILE ?=i686-w64-mingw32- LIBS ?= -lws2_32 +DEBUG_OPTIONS ?= else # For Linux ifeq ($(strip $(ARCH)),aarch64) CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector else CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector endif endif diff --git a/mk/base-cmd.mk b/mk/base-cmd.mk index f429bcf..e74c114 100644 --- a/mk/base-cmd.mk +++ b/mk/base-cmd.mk @@ -33,8 +33,13 @@ CC = $(CROSS_COMPILE)gcc CXX = $(CROSS_COMPILE)g++ + #CC = $(CROSS_COMPILE)clang #CXX = $(CROSS_COMPILE)clang++ + +#CC = $(CROSS_COMPILE)clang-16 +#CXX = $(CROSS_COMPILE)clang++16 + RANLIB = $(CROSS_COMPILE)ranlib ADDR2LINE = $(CROSS_COMPILE)addr2line AR = $(CROSS_COMPILE)ar diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 2cc417c..eebded1 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -69,7 +69,7 @@ # 値が2の場合、さらなるチェックを追加するが、規格準拠のプログラムが失敗する可能性がある。 # いくつかのチェックは、コンパイル時に実行され、コンパイラの警告として表示される。 # ------------------------------------------------------------------------------ -DEBUG_OPTIONS += -fstack-protector +# DEBUG_OPTIONS += -fstack-protector DEBUG_OPTIONS += --coverage DEBUG_OPTIONS += -g3 -ggdb DEBUG_OPTIONS += -O0 diff --git a/mk/link-a-conf.mk b/mk/link-a-conf.mk index 0e872f3..631e6d5 100644 --- a/mk/link-a-conf.mk +++ b/mk/link-a-conf.mk @@ -10,8 +10,11 @@ HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) +HEADER_FILES_WIN = $(wildcard include/win/*.h) $(wildcard include/win/*.hpp) +TOP_HEADER_FILES_WIN = $(addprefix $(TOPDIR)/include/win/,$(notdir $(HEADER_FILES_WIN))) + CLEAN_FILES += $(TOP_TARGET) -CLEAN_FILES += $(TOP_HEADER_FILES) +CLEAN_FILES += $(TOP_HEADER_FILES) $(TOP_HEADER_FILES_WIN) endif endif diff --git a/mk/link-a-rule.mk b/mk/link-a-rule.mk index d94a54a..657d858 100644 --- a/mk/link-a-rule.mk +++ b/mk/link-a-rule.mk @@ -10,6 +10,7 @@ $(TOP_TARGET): $(TARGET) ifneq ($(strip $(HEADER_FILES)),) $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ + $(CP) -f $(HEADER_FILES_WIN) $(TOPDIR)/include/win/ endif $(CP) -f $(TARGET) $(TOPDIR)/lib/ diff --git a/modules/include/kc_lock_guard.h b/modules/include/kc_lock_guard.h index 8bb70bd..ad3915f 100644 --- a/modules/include/kc_lock_guard.h +++ b/modules/include/kc_lock_guard.h @@ -7,18 +7,16 @@ #define KC_LOCK_GUARD_H #include -#include - +#include /** * LockGuard 管理構造体 */ typedef struct { - mtx_t* mutex; + mtx_t *mutex; } KcLockGuard; - /** * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 * 使用例) @@ -34,8 +32,7 @@ * } */ #define kc_lock_guard(lock) \ - for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) - + for (KcLockGuard _guard = kc_lock_guard_init(lock, __FILE__, __func__, __LINE__); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 @@ -44,15 +41,13 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex); - +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line); /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard); +void kc_lock_guard_release(KcLockGuard *guard); - -#endif // KC_LOCK_GUARD_H +#endif // KC_LOCK_GUARD_H diff --git a/config.mk b/config.mk index a0ac27c..f7c6e47 100644 --- a/config.mk +++ b/config.mk @@ -25,14 +25,17 @@ # for Windows CROSS_COMPILE ?=i686-w64-mingw32- LIBS ?= -lws2_32 +DEBUG_OPTIONS ?= else # For Linux ifeq ($(strip $(ARCH)),aarch64) CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector else CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector endif endif diff --git a/mk/base-cmd.mk b/mk/base-cmd.mk index f429bcf..e74c114 100644 --- a/mk/base-cmd.mk +++ b/mk/base-cmd.mk @@ -33,8 +33,13 @@ CC = $(CROSS_COMPILE)gcc CXX = $(CROSS_COMPILE)g++ + #CC = $(CROSS_COMPILE)clang #CXX = $(CROSS_COMPILE)clang++ + +#CC = $(CROSS_COMPILE)clang-16 +#CXX = $(CROSS_COMPILE)clang++16 + RANLIB = $(CROSS_COMPILE)ranlib ADDR2LINE = $(CROSS_COMPILE)addr2line AR = $(CROSS_COMPILE)ar diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 2cc417c..eebded1 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -69,7 +69,7 @@ # 値が2の場合、さらなるチェックを追加するが、規格準拠のプログラムが失敗する可能性がある。 # いくつかのチェックは、コンパイル時に実行され、コンパイラの警告として表示される。 # ------------------------------------------------------------------------------ -DEBUG_OPTIONS += -fstack-protector +# DEBUG_OPTIONS += -fstack-protector DEBUG_OPTIONS += --coverage DEBUG_OPTIONS += -g3 -ggdb DEBUG_OPTIONS += -O0 diff --git a/mk/link-a-conf.mk b/mk/link-a-conf.mk index 0e872f3..631e6d5 100644 --- a/mk/link-a-conf.mk +++ b/mk/link-a-conf.mk @@ -10,8 +10,11 @@ HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) +HEADER_FILES_WIN = $(wildcard include/win/*.h) $(wildcard include/win/*.hpp) +TOP_HEADER_FILES_WIN = $(addprefix $(TOPDIR)/include/win/,$(notdir $(HEADER_FILES_WIN))) + CLEAN_FILES += $(TOP_TARGET) -CLEAN_FILES += $(TOP_HEADER_FILES) +CLEAN_FILES += $(TOP_HEADER_FILES) $(TOP_HEADER_FILES_WIN) endif endif diff --git a/mk/link-a-rule.mk b/mk/link-a-rule.mk index d94a54a..657d858 100644 --- a/mk/link-a-rule.mk +++ b/mk/link-a-rule.mk @@ -10,6 +10,7 @@ $(TOP_TARGET): $(TARGET) ifneq ($(strip $(HEADER_FILES)),) $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ + $(CP) -f $(HEADER_FILES_WIN) $(TOPDIR)/include/win/ endif $(CP) -f $(TARGET) $(TOPDIR)/lib/ diff --git a/modules/include/kc_lock_guard.h b/modules/include/kc_lock_guard.h index 8bb70bd..ad3915f 100644 --- a/modules/include/kc_lock_guard.h +++ b/modules/include/kc_lock_guard.h @@ -7,18 +7,16 @@ #define KC_LOCK_GUARD_H #include -#include - +#include /** * LockGuard 管理構造体 */ typedef struct { - mtx_t* mutex; + mtx_t *mutex; } KcLockGuard; - /** * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 * 使用例) @@ -34,8 +32,7 @@ * } */ #define kc_lock_guard(lock) \ - for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) - + for (KcLockGuard _guard = kc_lock_guard_init(lock, __FILE__, __func__, __LINE__); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 @@ -44,15 +41,13 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex); - +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line); /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard); +void kc_lock_guard_release(KcLockGuard *guard); - -#endif // KC_LOCK_GUARD_H +#endif // KC_LOCK_GUARD_H diff --git a/modules/include/kc_memory.h b/modules/include/kc_memory.h index ffce4ed..c6ba9e9 100644 --- a/modules/include/kc_memory.h +++ b/modules/include/kc_memory.h @@ -22,12 +22,13 @@ #ifndef KC_MEMORY_H #define KC_MEMORY_H -#include +#include #include #include #include #include +#include #ifdef __cplusplus extern "C" @@ -273,8 +274,6 @@ #define calloc(nmemb, size) kc_memory_manager->calloc(nmemb, size, __FILE__, __func__, __LINE__) #define realloc(ptr, size) kc_memory_manager->realloc(ptr, size, __FILE__, __func__, __LINE__) #define free(ptr) kc_memory_manager->free(ptr) -#else -#include #endif #ifdef __cplusplus diff --git a/config.mk b/config.mk index a0ac27c..f7c6e47 100644 --- a/config.mk +++ b/config.mk @@ -25,14 +25,17 @@ # for Windows CROSS_COMPILE ?=i686-w64-mingw32- LIBS ?= -lws2_32 +DEBUG_OPTIONS ?= else # For Linux ifeq ($(strip $(ARCH)),aarch64) CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector else CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector endif endif diff --git a/mk/base-cmd.mk b/mk/base-cmd.mk index f429bcf..e74c114 100644 --- a/mk/base-cmd.mk +++ b/mk/base-cmd.mk @@ -33,8 +33,13 @@ CC = $(CROSS_COMPILE)gcc CXX = $(CROSS_COMPILE)g++ + #CC = $(CROSS_COMPILE)clang #CXX = $(CROSS_COMPILE)clang++ + +#CC = $(CROSS_COMPILE)clang-16 +#CXX = $(CROSS_COMPILE)clang++16 + RANLIB = $(CROSS_COMPILE)ranlib ADDR2LINE = $(CROSS_COMPILE)addr2line AR = $(CROSS_COMPILE)ar diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 2cc417c..eebded1 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -69,7 +69,7 @@ # 値が2の場合、さらなるチェックを追加するが、規格準拠のプログラムが失敗する可能性がある。 # いくつかのチェックは、コンパイル時に実行され、コンパイラの警告として表示される。 # ------------------------------------------------------------------------------ -DEBUG_OPTIONS += -fstack-protector +# DEBUG_OPTIONS += -fstack-protector DEBUG_OPTIONS += --coverage DEBUG_OPTIONS += -g3 -ggdb DEBUG_OPTIONS += -O0 diff --git a/mk/link-a-conf.mk b/mk/link-a-conf.mk index 0e872f3..631e6d5 100644 --- a/mk/link-a-conf.mk +++ b/mk/link-a-conf.mk @@ -10,8 +10,11 @@ HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) +HEADER_FILES_WIN = $(wildcard include/win/*.h) $(wildcard include/win/*.hpp) +TOP_HEADER_FILES_WIN = $(addprefix $(TOPDIR)/include/win/,$(notdir $(HEADER_FILES_WIN))) + CLEAN_FILES += $(TOP_TARGET) -CLEAN_FILES += $(TOP_HEADER_FILES) +CLEAN_FILES += $(TOP_HEADER_FILES) $(TOP_HEADER_FILES_WIN) endif endif diff --git a/mk/link-a-rule.mk b/mk/link-a-rule.mk index d94a54a..657d858 100644 --- a/mk/link-a-rule.mk +++ b/mk/link-a-rule.mk @@ -10,6 +10,7 @@ $(TOP_TARGET): $(TARGET) ifneq ($(strip $(HEADER_FILES)),) $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ + $(CP) -f $(HEADER_FILES_WIN) $(TOPDIR)/include/win/ endif $(CP) -f $(TARGET) $(TOPDIR)/lib/ diff --git a/modules/include/kc_lock_guard.h b/modules/include/kc_lock_guard.h index 8bb70bd..ad3915f 100644 --- a/modules/include/kc_lock_guard.h +++ b/modules/include/kc_lock_guard.h @@ -7,18 +7,16 @@ #define KC_LOCK_GUARD_H #include -#include - +#include /** * LockGuard 管理構造体 */ typedef struct { - mtx_t* mutex; + mtx_t *mutex; } KcLockGuard; - /** * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 * 使用例) @@ -34,8 +32,7 @@ * } */ #define kc_lock_guard(lock) \ - for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) - + for (KcLockGuard _guard = kc_lock_guard_init(lock, __FILE__, __func__, __LINE__); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 @@ -44,15 +41,13 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex); - +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line); /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard); +void kc_lock_guard_release(KcLockGuard *guard); - -#endif // KC_LOCK_GUARD_H +#endif // KC_LOCK_GUARD_H diff --git a/modules/include/kc_memory.h b/modules/include/kc_memory.h index ffce4ed..c6ba9e9 100644 --- a/modules/include/kc_memory.h +++ b/modules/include/kc_memory.h @@ -22,12 +22,13 @@ #ifndef KC_MEMORY_H #define KC_MEMORY_H -#include +#include #include #include #include #include +#include #ifdef __cplusplus extern "C" @@ -273,8 +274,6 @@ #define calloc(nmemb, size) kc_memory_manager->calloc(nmemb, size, __FILE__, __func__, __LINE__) #define realloc(ptr, size) kc_memory_manager->realloc(ptr, size, __FILE__, __func__, __LINE__) #define free(ptr) kc_memory_manager->free(ptr) -#else -#include #endif #ifdef __cplusplus diff --git a/modules/include/kc_socket.h b/modules/include/kc_socket.h new file mode 100644 index 0000000..7d952c7 --- /dev/null +++ b/modules/include/kc_socket.h @@ -0,0 +1,291 @@ +/** + * @file kc_socket.h + * @brief ソケットモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_SOCKET_H +#define KC_SOCKET_H + +#include + +#if (KC_IS_WINDOWS) +typedef SOCKET socket_t; +#define SHUT_RD SD_RECEIVE +#define SHUT_WR SD_SEND +#define SHUT_RDWR SD_BOTH +#define sockclose(sockfd) closesocket(sockfd) +#else +#include +#include +#include +#include +#include +#include +#include +typedef int socket_t; +#define INVALID_SOCKET (-1) +#define SOCKET_ERROR (-1) +#define sockclose(sockfd) close(sockfd) +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcSocket_ + { + + /** + * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr 接続先アドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_remote_addr)(struct KcSocket_ *sock, char *addr, size_t addrlen); + + /** + * ソケットの接続先ポート番号を返します。 + * 接続先ポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_remote_port)(struct KcSocket_ *sock); + + /** + * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr ローカルアドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_local_addr)(struct KcSocket_ *sock, char *addr, size_t addlen); + + /** + * ソケットのローカルポート番号を返します。 + * ローカルポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_local_port)(struct KcSocket_ *sock); + + /** + * ソケットディスクリプタを返します。 + * 通常は、ソケットディスクリプタを直接操作しないでください。 + * + * @param sock 対象ソケット + * @return ソケットディスクリプタ + */ + socket_t (*get_socket)(struct KcSocket_ *sock); + + /** + * ソケットタイプを返します。 + * + * @param sock 対象ソケット + * @return ソケットタイプ + */ + int (*get_type)(struct KcSocket_ *sock); + + /** + * ソケットファミリを返します。 + * 実際にバインド、あるいは接続されているソケットファミリを返します。 + * + * @param sock 対象ソケット + * @return ソケットファミリ + */ + int (*get_family)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービスのアドレス情報を取得します。 + * + * @param sock 対象ソケット + * @param result アドレス情報格納用ポインタ + * @param addr アドレス + * @param service サービス + * @param is_passive サーバソケットの場合、true を指定ください。 + */ + bool (*get_addrinfo)( + struct KcSocket_ *sock, + struct addrinfo **result, + const char *addr, + const char *service, + bool is_passive); + + /** + * 指定されたアドレス、サービスにバインドします。 + * 指定されたアドレスとサービスにより、複数候補がある場合は、 + * 最初に見つかったアドレスとポートにバインドします。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス (例: "80", "http", "ssh" など) + * @return true/false (bind 成功/失敗) + */ + bool (*bind)(struct KcSocket_ *sock, const char *addr, const char *service); + + /** + * ソケットを接続待ちソケットとしてマークをつけます。 + * 保留中の接続のキュー最大長を指定します。 + * + * @param sock 対象ソケット + * @param backlog バックログ + * @return true/false (成功/失敗) + */ + bool (*listen)(struct KcSocket_ *sock, int backlog); + + /** + * ソケットへの接続を受け付けます。 + * + * @param sock 対象ソケット + * @return 受け付けたソケット + */ + struct KcSocket_ *(*accept)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービス接続します。 + * local_addr に NULL を指定した場合、自動的にローカルのアドレスが設定されます。 + * local_service に -1 を指定した場合、自動的にローカルのポート番号が割りつけられます。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス + * @param local_addr ローカルアドレス + * @param local_service ローカルサービス + * @return true/false (成功/失敗) + */ + bool (*connect)(struct KcSocket_ *sock, const char *addr, const char *service, const char *local_addr, const char *local_port); + + /** + * ソケットをクローズします。 + * + * @param sock 対象ソケット + * @return true/false (成功/失敗) + */ + bool (*close)(struct KcSocket_ *sock); + + /** + * メッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 送信されたデータサイズ + */ + ssize_t (*send)(struct KcSocket_ *sock, const char *buff, size_t size, int flags); + + /** + * メッセージを受信します。 + * 受信したメッセージは指定されたバッファに格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 読み取ったデータサイズ + */ + ssize_t (*recv)(struct KcSocket_ *sock, char *buff, size_t size, int flags); + + /** + * 指定されたアドレス、サービスにメッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param addr アドレス + * @param service サービス + * @return 送信メッセージサイズ、エラー発生時は -1 + */ + ssize_t (*sendto)( + struct KcSocket_ *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service); + + /** + * メッセージを受信します。 + * src_addr, src_service がいずれも NULL 出ない場合、 + * 送信元のアドレスとサービスが格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param src_addr 送信元アドレス格納用バッファ + * @param src_addrlen 送信元アドレス格納用バッファサイズ + * @param src_service 送信元サービス格納用バッファ + * @param src_servicelen 送信元サービス格納用バッファサイズ + * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + */ + ssize_t (*recvfrom)( + struct KcSocket_ *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen); + + /** + * TTL を設定します。 + * + * @param sock 対象ソケット + * @param val 設定する TTL + * @return true/false (成功/失敗) + */ + bool (*set_ttl)(struct KcSocket_ *sock, int val); + + /** + * マルチキャストグループに参加するために JOIN します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @param ifindex インタフェースインデックス + * @return true/false (成功/失敗) + */ + bool (*join)(struct KcSocket_ *sock, const char *addr, const char *ifname, unsigned int ifindex); + + /** + * マルチキャストグループから離脱します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @return true/false (成功/失敗) + */ + bool (*leave)(struct KcSocket_ *sock, const char *addr); + + /** + * Socket 管理情報 + */ + void *_info; + + } KcSocket; + + /** + * 指定されたタイプ、ファミリのSocket オブジェクトを構築します。 + * + * @param type タイプ(SOCK_STREAM/SOCK_DGRAM/SOCK_RAW) + * @param family ファミリ(AF_UNSPEC/AF_INET/AF_INET6) + */ + KcSocket *KcSocket_new(int type, int family); + + /** + * Socket を破棄します。 + */ + void KcSocket_delete(KcSocket *socket); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/config.mk b/config.mk index a0ac27c..f7c6e47 100644 --- a/config.mk +++ b/config.mk @@ -25,14 +25,17 @@ # for Windows CROSS_COMPILE ?=i686-w64-mingw32- LIBS ?= -lws2_32 +DEBUG_OPTIONS ?= else # For Linux ifeq ($(strip $(ARCH)),aarch64) CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector else CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector endif endif diff --git a/mk/base-cmd.mk b/mk/base-cmd.mk index f429bcf..e74c114 100644 --- a/mk/base-cmd.mk +++ b/mk/base-cmd.mk @@ -33,8 +33,13 @@ CC = $(CROSS_COMPILE)gcc CXX = $(CROSS_COMPILE)g++ + #CC = $(CROSS_COMPILE)clang #CXX = $(CROSS_COMPILE)clang++ + +#CC = $(CROSS_COMPILE)clang-16 +#CXX = $(CROSS_COMPILE)clang++16 + RANLIB = $(CROSS_COMPILE)ranlib ADDR2LINE = $(CROSS_COMPILE)addr2line AR = $(CROSS_COMPILE)ar diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 2cc417c..eebded1 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -69,7 +69,7 @@ # 値が2の場合、さらなるチェックを追加するが、規格準拠のプログラムが失敗する可能性がある。 # いくつかのチェックは、コンパイル時に実行され、コンパイラの警告として表示される。 # ------------------------------------------------------------------------------ -DEBUG_OPTIONS += -fstack-protector +# DEBUG_OPTIONS += -fstack-protector DEBUG_OPTIONS += --coverage DEBUG_OPTIONS += -g3 -ggdb DEBUG_OPTIONS += -O0 diff --git a/mk/link-a-conf.mk b/mk/link-a-conf.mk index 0e872f3..631e6d5 100644 --- a/mk/link-a-conf.mk +++ b/mk/link-a-conf.mk @@ -10,8 +10,11 @@ HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) +HEADER_FILES_WIN = $(wildcard include/win/*.h) $(wildcard include/win/*.hpp) +TOP_HEADER_FILES_WIN = $(addprefix $(TOPDIR)/include/win/,$(notdir $(HEADER_FILES_WIN))) + CLEAN_FILES += $(TOP_TARGET) -CLEAN_FILES += $(TOP_HEADER_FILES) +CLEAN_FILES += $(TOP_HEADER_FILES) $(TOP_HEADER_FILES_WIN) endif endif diff --git a/mk/link-a-rule.mk b/mk/link-a-rule.mk index d94a54a..657d858 100644 --- a/mk/link-a-rule.mk +++ b/mk/link-a-rule.mk @@ -10,6 +10,7 @@ $(TOP_TARGET): $(TARGET) ifneq ($(strip $(HEADER_FILES)),) $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ + $(CP) -f $(HEADER_FILES_WIN) $(TOPDIR)/include/win/ endif $(CP) -f $(TARGET) $(TOPDIR)/lib/ diff --git a/modules/include/kc_lock_guard.h b/modules/include/kc_lock_guard.h index 8bb70bd..ad3915f 100644 --- a/modules/include/kc_lock_guard.h +++ b/modules/include/kc_lock_guard.h @@ -7,18 +7,16 @@ #define KC_LOCK_GUARD_H #include -#include - +#include /** * LockGuard 管理構造体 */ typedef struct { - mtx_t* mutex; + mtx_t *mutex; } KcLockGuard; - /** * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 * 使用例) @@ -34,8 +32,7 @@ * } */ #define kc_lock_guard(lock) \ - for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) - + for (KcLockGuard _guard = kc_lock_guard_init(lock, __FILE__, __func__, __LINE__); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 @@ -44,15 +41,13 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex); - +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line); /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard); +void kc_lock_guard_release(KcLockGuard *guard); - -#endif // KC_LOCK_GUARD_H +#endif // KC_LOCK_GUARD_H diff --git a/modules/include/kc_memory.h b/modules/include/kc_memory.h index ffce4ed..c6ba9e9 100644 --- a/modules/include/kc_memory.h +++ b/modules/include/kc_memory.h @@ -22,12 +22,13 @@ #ifndef KC_MEMORY_H #define KC_MEMORY_H -#include +#include #include #include #include #include +#include #ifdef __cplusplus extern "C" @@ -273,8 +274,6 @@ #define calloc(nmemb, size) kc_memory_manager->calloc(nmemb, size, __FILE__, __func__, __LINE__) #define realloc(ptr, size) kc_memory_manager->realloc(ptr, size, __FILE__, __func__, __LINE__) #define free(ptr) kc_memory_manager->free(ptr) -#else -#include #endif #ifdef __cplusplus diff --git a/modules/include/kc_socket.h b/modules/include/kc_socket.h new file mode 100644 index 0000000..7d952c7 --- /dev/null +++ b/modules/include/kc_socket.h @@ -0,0 +1,291 @@ +/** + * @file kc_socket.h + * @brief ソケットモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_SOCKET_H +#define KC_SOCKET_H + +#include + +#if (KC_IS_WINDOWS) +typedef SOCKET socket_t; +#define SHUT_RD SD_RECEIVE +#define SHUT_WR SD_SEND +#define SHUT_RDWR SD_BOTH +#define sockclose(sockfd) closesocket(sockfd) +#else +#include +#include +#include +#include +#include +#include +#include +typedef int socket_t; +#define INVALID_SOCKET (-1) +#define SOCKET_ERROR (-1) +#define sockclose(sockfd) close(sockfd) +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcSocket_ + { + + /** + * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr 接続先アドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_remote_addr)(struct KcSocket_ *sock, char *addr, size_t addrlen); + + /** + * ソケットの接続先ポート番号を返します。 + * 接続先ポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_remote_port)(struct KcSocket_ *sock); + + /** + * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr ローカルアドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_local_addr)(struct KcSocket_ *sock, char *addr, size_t addlen); + + /** + * ソケットのローカルポート番号を返します。 + * ローカルポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_local_port)(struct KcSocket_ *sock); + + /** + * ソケットディスクリプタを返します。 + * 通常は、ソケットディスクリプタを直接操作しないでください。 + * + * @param sock 対象ソケット + * @return ソケットディスクリプタ + */ + socket_t (*get_socket)(struct KcSocket_ *sock); + + /** + * ソケットタイプを返します。 + * + * @param sock 対象ソケット + * @return ソケットタイプ + */ + int (*get_type)(struct KcSocket_ *sock); + + /** + * ソケットファミリを返します。 + * 実際にバインド、あるいは接続されているソケットファミリを返します。 + * + * @param sock 対象ソケット + * @return ソケットファミリ + */ + int (*get_family)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービスのアドレス情報を取得します。 + * + * @param sock 対象ソケット + * @param result アドレス情報格納用ポインタ + * @param addr アドレス + * @param service サービス + * @param is_passive サーバソケットの場合、true を指定ください。 + */ + bool (*get_addrinfo)( + struct KcSocket_ *sock, + struct addrinfo **result, + const char *addr, + const char *service, + bool is_passive); + + /** + * 指定されたアドレス、サービスにバインドします。 + * 指定されたアドレスとサービスにより、複数候補がある場合は、 + * 最初に見つかったアドレスとポートにバインドします。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス (例: "80", "http", "ssh" など) + * @return true/false (bind 成功/失敗) + */ + bool (*bind)(struct KcSocket_ *sock, const char *addr, const char *service); + + /** + * ソケットを接続待ちソケットとしてマークをつけます。 + * 保留中の接続のキュー最大長を指定します。 + * + * @param sock 対象ソケット + * @param backlog バックログ + * @return true/false (成功/失敗) + */ + bool (*listen)(struct KcSocket_ *sock, int backlog); + + /** + * ソケットへの接続を受け付けます。 + * + * @param sock 対象ソケット + * @return 受け付けたソケット + */ + struct KcSocket_ *(*accept)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービス接続します。 + * local_addr に NULL を指定した場合、自動的にローカルのアドレスが設定されます。 + * local_service に -1 を指定した場合、自動的にローカルのポート番号が割りつけられます。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス + * @param local_addr ローカルアドレス + * @param local_service ローカルサービス + * @return true/false (成功/失敗) + */ + bool (*connect)(struct KcSocket_ *sock, const char *addr, const char *service, const char *local_addr, const char *local_port); + + /** + * ソケットをクローズします。 + * + * @param sock 対象ソケット + * @return true/false (成功/失敗) + */ + bool (*close)(struct KcSocket_ *sock); + + /** + * メッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 送信されたデータサイズ + */ + ssize_t (*send)(struct KcSocket_ *sock, const char *buff, size_t size, int flags); + + /** + * メッセージを受信します。 + * 受信したメッセージは指定されたバッファに格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 読み取ったデータサイズ + */ + ssize_t (*recv)(struct KcSocket_ *sock, char *buff, size_t size, int flags); + + /** + * 指定されたアドレス、サービスにメッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param addr アドレス + * @param service サービス + * @return 送信メッセージサイズ、エラー発生時は -1 + */ + ssize_t (*sendto)( + struct KcSocket_ *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service); + + /** + * メッセージを受信します。 + * src_addr, src_service がいずれも NULL 出ない場合、 + * 送信元のアドレスとサービスが格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param src_addr 送信元アドレス格納用バッファ + * @param src_addrlen 送信元アドレス格納用バッファサイズ + * @param src_service 送信元サービス格納用バッファ + * @param src_servicelen 送信元サービス格納用バッファサイズ + * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + */ + ssize_t (*recvfrom)( + struct KcSocket_ *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen); + + /** + * TTL を設定します。 + * + * @param sock 対象ソケット + * @param val 設定する TTL + * @return true/false (成功/失敗) + */ + bool (*set_ttl)(struct KcSocket_ *sock, int val); + + /** + * マルチキャストグループに参加するために JOIN します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @param ifindex インタフェースインデックス + * @return true/false (成功/失敗) + */ + bool (*join)(struct KcSocket_ *sock, const char *addr, const char *ifname, unsigned int ifindex); + + /** + * マルチキャストグループから離脱します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @return true/false (成功/失敗) + */ + bool (*leave)(struct KcSocket_ *sock, const char *addr); + + /** + * Socket 管理情報 + */ + void *_info; + + } KcSocket; + + /** + * 指定されたタイプ、ファミリのSocket オブジェクトを構築します。 + * + * @param type タイプ(SOCK_STREAM/SOCK_DGRAM/SOCK_RAW) + * @param family ファミリ(AF_UNSPEC/AF_INET/AF_INET6) + */ + KcSocket *KcSocket_new(int type, int family); + + /** + * Socket を破棄します。 + */ + void KcSocket_delete(KcSocket *socket); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_threads.h b/modules/include/kc_threads.h new file mode 100644 index 0000000..764176f --- /dev/null +++ b/modules/include/kc_threads.h @@ -0,0 +1,98 @@ +/** + * @file kc_threads.h + * @brief スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_THREADS_H +#define KC_THREADS_H + +#include + +#if (KC_IS_WINDOWS) +#include +#else +#include +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcThread_ + { + + /** + * 指定されたスレッドが生きているか否かを返します。 + * + * @param thread 対象スレッド + * @return true/false (スレッドが生存している/していない) + */ + bool (*is_alive)(struct KcThread_ *thread); + + /** + * スレッドを開始します。 + * + * @param thread 対象スレッド + * @param args スレッドに渡す情報 + * @return true/false (スレッド起動成功/失敗) + */ + bool (*start)(struct KcThread_ *thread, void *args); + + /** + * スレッドが終了するのを待機します。 + * + * @param thread 対象スレッド + */ + bool (*join)(struct KcThread_ *thread); + + /** + * Thread 管理情報 + */ + void *_info; + + } KcThread; + + /** + * スレッドを生成します。 + * + * @param run スレッドにて実行する関数へのポインタ + */ + KcThread *KcThread_new(int (*run)(void *args)); + + /** + * Thread を破棄します。 + * + * @param thread 破棄するスレッド + */ + void KcThread_delete(KcThread *thread); + + /** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープさせるミリ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_msleep(long time, bool force); + + /** + * 指定された sec 秒 + nsec ナノ秒間現在のスレッドをスリープさせます。 + * + * @param sec スリープさせる秒数 + * @param nsec スリープさせるナノ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_sleep(long sec, long nsec, bool force); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_THREADS_H diff --git a/config.mk b/config.mk index a0ac27c..f7c6e47 100644 --- a/config.mk +++ b/config.mk @@ -25,14 +25,17 @@ # for Windows CROSS_COMPILE ?=i686-w64-mingw32- LIBS ?= -lws2_32 +DEBUG_OPTIONS ?= else # For Linux ifeq ($(strip $(ARCH)),aarch64) CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector else CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector endif endif diff --git a/mk/base-cmd.mk b/mk/base-cmd.mk index f429bcf..e74c114 100644 --- a/mk/base-cmd.mk +++ b/mk/base-cmd.mk @@ -33,8 +33,13 @@ CC = $(CROSS_COMPILE)gcc CXX = $(CROSS_COMPILE)g++ + #CC = $(CROSS_COMPILE)clang #CXX = $(CROSS_COMPILE)clang++ + +#CC = $(CROSS_COMPILE)clang-16 +#CXX = $(CROSS_COMPILE)clang++16 + RANLIB = $(CROSS_COMPILE)ranlib ADDR2LINE = $(CROSS_COMPILE)addr2line AR = $(CROSS_COMPILE)ar diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 2cc417c..eebded1 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -69,7 +69,7 @@ # 値が2の場合、さらなるチェックを追加するが、規格準拠のプログラムが失敗する可能性がある。 # いくつかのチェックは、コンパイル時に実行され、コンパイラの警告として表示される。 # ------------------------------------------------------------------------------ -DEBUG_OPTIONS += -fstack-protector +# DEBUG_OPTIONS += -fstack-protector DEBUG_OPTIONS += --coverage DEBUG_OPTIONS += -g3 -ggdb DEBUG_OPTIONS += -O0 diff --git a/mk/link-a-conf.mk b/mk/link-a-conf.mk index 0e872f3..631e6d5 100644 --- a/mk/link-a-conf.mk +++ b/mk/link-a-conf.mk @@ -10,8 +10,11 @@ HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) +HEADER_FILES_WIN = $(wildcard include/win/*.h) $(wildcard include/win/*.hpp) +TOP_HEADER_FILES_WIN = $(addprefix $(TOPDIR)/include/win/,$(notdir $(HEADER_FILES_WIN))) + CLEAN_FILES += $(TOP_TARGET) -CLEAN_FILES += $(TOP_HEADER_FILES) +CLEAN_FILES += $(TOP_HEADER_FILES) $(TOP_HEADER_FILES_WIN) endif endif diff --git a/mk/link-a-rule.mk b/mk/link-a-rule.mk index d94a54a..657d858 100644 --- a/mk/link-a-rule.mk +++ b/mk/link-a-rule.mk @@ -10,6 +10,7 @@ $(TOP_TARGET): $(TARGET) ifneq ($(strip $(HEADER_FILES)),) $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ + $(CP) -f $(HEADER_FILES_WIN) $(TOPDIR)/include/win/ endif $(CP) -f $(TARGET) $(TOPDIR)/lib/ diff --git a/modules/include/kc_lock_guard.h b/modules/include/kc_lock_guard.h index 8bb70bd..ad3915f 100644 --- a/modules/include/kc_lock_guard.h +++ b/modules/include/kc_lock_guard.h @@ -7,18 +7,16 @@ #define KC_LOCK_GUARD_H #include -#include - +#include /** * LockGuard 管理構造体 */ typedef struct { - mtx_t* mutex; + mtx_t *mutex; } KcLockGuard; - /** * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 * 使用例) @@ -34,8 +32,7 @@ * } */ #define kc_lock_guard(lock) \ - for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) - + for (KcLockGuard _guard = kc_lock_guard_init(lock, __FILE__, __func__, __LINE__); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 @@ -44,15 +41,13 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex); - +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line); /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard); +void kc_lock_guard_release(KcLockGuard *guard); - -#endif // KC_LOCK_GUARD_H +#endif // KC_LOCK_GUARD_H diff --git a/modules/include/kc_memory.h b/modules/include/kc_memory.h index ffce4ed..c6ba9e9 100644 --- a/modules/include/kc_memory.h +++ b/modules/include/kc_memory.h @@ -22,12 +22,13 @@ #ifndef KC_MEMORY_H #define KC_MEMORY_H -#include +#include #include #include #include #include +#include #ifdef __cplusplus extern "C" @@ -273,8 +274,6 @@ #define calloc(nmemb, size) kc_memory_manager->calloc(nmemb, size, __FILE__, __func__, __LINE__) #define realloc(ptr, size) kc_memory_manager->realloc(ptr, size, __FILE__, __func__, __LINE__) #define free(ptr) kc_memory_manager->free(ptr) -#else -#include #endif #ifdef __cplusplus diff --git a/modules/include/kc_socket.h b/modules/include/kc_socket.h new file mode 100644 index 0000000..7d952c7 --- /dev/null +++ b/modules/include/kc_socket.h @@ -0,0 +1,291 @@ +/** + * @file kc_socket.h + * @brief ソケットモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_SOCKET_H +#define KC_SOCKET_H + +#include + +#if (KC_IS_WINDOWS) +typedef SOCKET socket_t; +#define SHUT_RD SD_RECEIVE +#define SHUT_WR SD_SEND +#define SHUT_RDWR SD_BOTH +#define sockclose(sockfd) closesocket(sockfd) +#else +#include +#include +#include +#include +#include +#include +#include +typedef int socket_t; +#define INVALID_SOCKET (-1) +#define SOCKET_ERROR (-1) +#define sockclose(sockfd) close(sockfd) +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcSocket_ + { + + /** + * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr 接続先アドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_remote_addr)(struct KcSocket_ *sock, char *addr, size_t addrlen); + + /** + * ソケットの接続先ポート番号を返します。 + * 接続先ポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_remote_port)(struct KcSocket_ *sock); + + /** + * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr ローカルアドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_local_addr)(struct KcSocket_ *sock, char *addr, size_t addlen); + + /** + * ソケットのローカルポート番号を返します。 + * ローカルポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_local_port)(struct KcSocket_ *sock); + + /** + * ソケットディスクリプタを返します。 + * 通常は、ソケットディスクリプタを直接操作しないでください。 + * + * @param sock 対象ソケット + * @return ソケットディスクリプタ + */ + socket_t (*get_socket)(struct KcSocket_ *sock); + + /** + * ソケットタイプを返します。 + * + * @param sock 対象ソケット + * @return ソケットタイプ + */ + int (*get_type)(struct KcSocket_ *sock); + + /** + * ソケットファミリを返します。 + * 実際にバインド、あるいは接続されているソケットファミリを返します。 + * + * @param sock 対象ソケット + * @return ソケットファミリ + */ + int (*get_family)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービスのアドレス情報を取得します。 + * + * @param sock 対象ソケット + * @param result アドレス情報格納用ポインタ + * @param addr アドレス + * @param service サービス + * @param is_passive サーバソケットの場合、true を指定ください。 + */ + bool (*get_addrinfo)( + struct KcSocket_ *sock, + struct addrinfo **result, + const char *addr, + const char *service, + bool is_passive); + + /** + * 指定されたアドレス、サービスにバインドします。 + * 指定されたアドレスとサービスにより、複数候補がある場合は、 + * 最初に見つかったアドレスとポートにバインドします。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス (例: "80", "http", "ssh" など) + * @return true/false (bind 成功/失敗) + */ + bool (*bind)(struct KcSocket_ *sock, const char *addr, const char *service); + + /** + * ソケットを接続待ちソケットとしてマークをつけます。 + * 保留中の接続のキュー最大長を指定します。 + * + * @param sock 対象ソケット + * @param backlog バックログ + * @return true/false (成功/失敗) + */ + bool (*listen)(struct KcSocket_ *sock, int backlog); + + /** + * ソケットへの接続を受け付けます。 + * + * @param sock 対象ソケット + * @return 受け付けたソケット + */ + struct KcSocket_ *(*accept)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービス接続します。 + * local_addr に NULL を指定した場合、自動的にローカルのアドレスが設定されます。 + * local_service に -1 を指定した場合、自動的にローカルのポート番号が割りつけられます。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス + * @param local_addr ローカルアドレス + * @param local_service ローカルサービス + * @return true/false (成功/失敗) + */ + bool (*connect)(struct KcSocket_ *sock, const char *addr, const char *service, const char *local_addr, const char *local_port); + + /** + * ソケットをクローズします。 + * + * @param sock 対象ソケット + * @return true/false (成功/失敗) + */ + bool (*close)(struct KcSocket_ *sock); + + /** + * メッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 送信されたデータサイズ + */ + ssize_t (*send)(struct KcSocket_ *sock, const char *buff, size_t size, int flags); + + /** + * メッセージを受信します。 + * 受信したメッセージは指定されたバッファに格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 読み取ったデータサイズ + */ + ssize_t (*recv)(struct KcSocket_ *sock, char *buff, size_t size, int flags); + + /** + * 指定されたアドレス、サービスにメッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param addr アドレス + * @param service サービス + * @return 送信メッセージサイズ、エラー発生時は -1 + */ + ssize_t (*sendto)( + struct KcSocket_ *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service); + + /** + * メッセージを受信します。 + * src_addr, src_service がいずれも NULL 出ない場合、 + * 送信元のアドレスとサービスが格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param src_addr 送信元アドレス格納用バッファ + * @param src_addrlen 送信元アドレス格納用バッファサイズ + * @param src_service 送信元サービス格納用バッファ + * @param src_servicelen 送信元サービス格納用バッファサイズ + * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + */ + ssize_t (*recvfrom)( + struct KcSocket_ *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen); + + /** + * TTL を設定します。 + * + * @param sock 対象ソケット + * @param val 設定する TTL + * @return true/false (成功/失敗) + */ + bool (*set_ttl)(struct KcSocket_ *sock, int val); + + /** + * マルチキャストグループに参加するために JOIN します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @param ifindex インタフェースインデックス + * @return true/false (成功/失敗) + */ + bool (*join)(struct KcSocket_ *sock, const char *addr, const char *ifname, unsigned int ifindex); + + /** + * マルチキャストグループから離脱します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @return true/false (成功/失敗) + */ + bool (*leave)(struct KcSocket_ *sock, const char *addr); + + /** + * Socket 管理情報 + */ + void *_info; + + } KcSocket; + + /** + * 指定されたタイプ、ファミリのSocket オブジェクトを構築します。 + * + * @param type タイプ(SOCK_STREAM/SOCK_DGRAM/SOCK_RAW) + * @param family ファミリ(AF_UNSPEC/AF_INET/AF_INET6) + */ + KcSocket *KcSocket_new(int type, int family); + + /** + * Socket を破棄します。 + */ + void KcSocket_delete(KcSocket *socket); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_threads.h b/modules/include/kc_threads.h new file mode 100644 index 0000000..764176f --- /dev/null +++ b/modules/include/kc_threads.h @@ -0,0 +1,98 @@ +/** + * @file kc_threads.h + * @brief スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_THREADS_H +#define KC_THREADS_H + +#include + +#if (KC_IS_WINDOWS) +#include +#else +#include +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcThread_ + { + + /** + * 指定されたスレッドが生きているか否かを返します。 + * + * @param thread 対象スレッド + * @return true/false (スレッドが生存している/していない) + */ + bool (*is_alive)(struct KcThread_ *thread); + + /** + * スレッドを開始します。 + * + * @param thread 対象スレッド + * @param args スレッドに渡す情報 + * @return true/false (スレッド起動成功/失敗) + */ + bool (*start)(struct KcThread_ *thread, void *args); + + /** + * スレッドが終了するのを待機します。 + * + * @param thread 対象スレッド + */ + bool (*join)(struct KcThread_ *thread); + + /** + * Thread 管理情報 + */ + void *_info; + + } KcThread; + + /** + * スレッドを生成します。 + * + * @param run スレッドにて実行する関数へのポインタ + */ + KcThread *KcThread_new(int (*run)(void *args)); + + /** + * Thread を破棄します。 + * + * @param thread 破棄するスレッド + */ + void KcThread_delete(KcThread *thread); + + /** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープさせるミリ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_msleep(long time, bool force); + + /** + * 指定された sec 秒 + nsec ナノ秒間現在のスレッドをスリープさせます。 + * + * @param sec スリープさせる秒数 + * @param nsec スリープさせるナノ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_sleep(long sec, long nsec, bool force); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_THREADS_H diff --git a/modules/include/kc_threads_win.h b/modules/include/kc_threads_win.h new file mode 100644 index 0000000..b40ab10 --- /dev/null +++ b/modules/include/kc_threads_win.h @@ -0,0 +1,98 @@ +/** + * @file kc_windows_thread.h + * @brief Windows 用スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_WINDOWS_THREAD_H +#define KC_WINDOWS_THREAD_H + +#include +#if (KC_IS_WINDOWS) +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#define thread_local _Thread_local + + //////////////////////////////////////////////////////////////////////// + // + // Thread + // + + enum + { + thrd_success = 0, //!< 成功 + thrd_bussy = 1, //!< busy + thrd_error = 2, //!< エラー発生 + thrd_nomem = 3, //!< メモリ不足 + thrd_timedout = 4 //!< タイムアウト発生 + }; + + typedef struct + { + HANDLE handle; + DWORD thread_id; + } thrd_t; + + typedef int (*thrd_start_t)(void *); + int thrd_create(thrd_t *thr, thrd_start_t func, void *arg); + int thrd_join(thrd_t thr, int *res); + int thrd_detach(thrd_t thr); + thrd_t thrd_current(void); + int thrd_equal(thrd_t lhs, thrd_t rhs); + int thrd_sleep(const struct timespec *duration, struct timespec *remaining); + + //////////////////////////////////////////////////////////////////////// + // + // mutex + // + + enum + { + mtx_plain = 0, //!< 通常 + mtx_recursive = 1, //!< 再帰 + mtx_timed = 2 //!< timed + }; + + typedef struct + { + CRITICAL_SECTION cs; + int type; + } mtx_t; + + int mtx_init(mtx_t *mtx, int type); + void mtx_destroy(mtx_t *mtx); + int mtx_lock(mtx_t *mtx); + int mtx_unlock(mtx_t *mtx); + + //////////////////////////////////////////////////////////////////////// + // + // cond + // + + typedef struct + { + CONDITION_VARIABLE cond; + } cnd_t; + + int cnd_init(cnd_t *cond); + int cnd_signal(cnd_t *cond); + int cnd_broadcast(cnd_t *cond); + int cnd_wait(cnd_t *cond, mtx_t *mtx); + int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts); + void cnd_destroy(cnd_t *cond); + +#endif // (KC_IS_WINDOWS) +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/config.mk b/config.mk index a0ac27c..f7c6e47 100644 --- a/config.mk +++ b/config.mk @@ -25,14 +25,17 @@ # for Windows CROSS_COMPILE ?=i686-w64-mingw32- LIBS ?= -lws2_32 +DEBUG_OPTIONS ?= else # For Linux ifeq ($(strip $(ARCH)),aarch64) CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector else CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector endif endif diff --git a/mk/base-cmd.mk b/mk/base-cmd.mk index f429bcf..e74c114 100644 --- a/mk/base-cmd.mk +++ b/mk/base-cmd.mk @@ -33,8 +33,13 @@ CC = $(CROSS_COMPILE)gcc CXX = $(CROSS_COMPILE)g++ + #CC = $(CROSS_COMPILE)clang #CXX = $(CROSS_COMPILE)clang++ + +#CC = $(CROSS_COMPILE)clang-16 +#CXX = $(CROSS_COMPILE)clang++16 + RANLIB = $(CROSS_COMPILE)ranlib ADDR2LINE = $(CROSS_COMPILE)addr2line AR = $(CROSS_COMPILE)ar diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 2cc417c..eebded1 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -69,7 +69,7 @@ # 値が2の場合、さらなるチェックを追加するが、規格準拠のプログラムが失敗する可能性がある。 # いくつかのチェックは、コンパイル時に実行され、コンパイラの警告として表示される。 # ------------------------------------------------------------------------------ -DEBUG_OPTIONS += -fstack-protector +# DEBUG_OPTIONS += -fstack-protector DEBUG_OPTIONS += --coverage DEBUG_OPTIONS += -g3 -ggdb DEBUG_OPTIONS += -O0 diff --git a/mk/link-a-conf.mk b/mk/link-a-conf.mk index 0e872f3..631e6d5 100644 --- a/mk/link-a-conf.mk +++ b/mk/link-a-conf.mk @@ -10,8 +10,11 @@ HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) +HEADER_FILES_WIN = $(wildcard include/win/*.h) $(wildcard include/win/*.hpp) +TOP_HEADER_FILES_WIN = $(addprefix $(TOPDIR)/include/win/,$(notdir $(HEADER_FILES_WIN))) + CLEAN_FILES += $(TOP_TARGET) -CLEAN_FILES += $(TOP_HEADER_FILES) +CLEAN_FILES += $(TOP_HEADER_FILES) $(TOP_HEADER_FILES_WIN) endif endif diff --git a/mk/link-a-rule.mk b/mk/link-a-rule.mk index d94a54a..657d858 100644 --- a/mk/link-a-rule.mk +++ b/mk/link-a-rule.mk @@ -10,6 +10,7 @@ $(TOP_TARGET): $(TARGET) ifneq ($(strip $(HEADER_FILES)),) $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ + $(CP) -f $(HEADER_FILES_WIN) $(TOPDIR)/include/win/ endif $(CP) -f $(TARGET) $(TOPDIR)/lib/ diff --git a/modules/include/kc_lock_guard.h b/modules/include/kc_lock_guard.h index 8bb70bd..ad3915f 100644 --- a/modules/include/kc_lock_guard.h +++ b/modules/include/kc_lock_guard.h @@ -7,18 +7,16 @@ #define KC_LOCK_GUARD_H #include -#include - +#include /** * LockGuard 管理構造体 */ typedef struct { - mtx_t* mutex; + mtx_t *mutex; } KcLockGuard; - /** * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 * 使用例) @@ -34,8 +32,7 @@ * } */ #define kc_lock_guard(lock) \ - for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) - + for (KcLockGuard _guard = kc_lock_guard_init(lock, __FILE__, __func__, __LINE__); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 @@ -44,15 +41,13 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex); - +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line); /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard); +void kc_lock_guard_release(KcLockGuard *guard); - -#endif // KC_LOCK_GUARD_H +#endif // KC_LOCK_GUARD_H diff --git a/modules/include/kc_memory.h b/modules/include/kc_memory.h index ffce4ed..c6ba9e9 100644 --- a/modules/include/kc_memory.h +++ b/modules/include/kc_memory.h @@ -22,12 +22,13 @@ #ifndef KC_MEMORY_H #define KC_MEMORY_H -#include +#include #include #include #include #include +#include #ifdef __cplusplus extern "C" @@ -273,8 +274,6 @@ #define calloc(nmemb, size) kc_memory_manager->calloc(nmemb, size, __FILE__, __func__, __LINE__) #define realloc(ptr, size) kc_memory_manager->realloc(ptr, size, __FILE__, __func__, __LINE__) #define free(ptr) kc_memory_manager->free(ptr) -#else -#include #endif #ifdef __cplusplus diff --git a/modules/include/kc_socket.h b/modules/include/kc_socket.h new file mode 100644 index 0000000..7d952c7 --- /dev/null +++ b/modules/include/kc_socket.h @@ -0,0 +1,291 @@ +/** + * @file kc_socket.h + * @brief ソケットモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_SOCKET_H +#define KC_SOCKET_H + +#include + +#if (KC_IS_WINDOWS) +typedef SOCKET socket_t; +#define SHUT_RD SD_RECEIVE +#define SHUT_WR SD_SEND +#define SHUT_RDWR SD_BOTH +#define sockclose(sockfd) closesocket(sockfd) +#else +#include +#include +#include +#include +#include +#include +#include +typedef int socket_t; +#define INVALID_SOCKET (-1) +#define SOCKET_ERROR (-1) +#define sockclose(sockfd) close(sockfd) +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcSocket_ + { + + /** + * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr 接続先アドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_remote_addr)(struct KcSocket_ *sock, char *addr, size_t addrlen); + + /** + * ソケットの接続先ポート番号を返します。 + * 接続先ポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_remote_port)(struct KcSocket_ *sock); + + /** + * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr ローカルアドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_local_addr)(struct KcSocket_ *sock, char *addr, size_t addlen); + + /** + * ソケットのローカルポート番号を返します。 + * ローカルポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_local_port)(struct KcSocket_ *sock); + + /** + * ソケットディスクリプタを返します。 + * 通常は、ソケットディスクリプタを直接操作しないでください。 + * + * @param sock 対象ソケット + * @return ソケットディスクリプタ + */ + socket_t (*get_socket)(struct KcSocket_ *sock); + + /** + * ソケットタイプを返します。 + * + * @param sock 対象ソケット + * @return ソケットタイプ + */ + int (*get_type)(struct KcSocket_ *sock); + + /** + * ソケットファミリを返します。 + * 実際にバインド、あるいは接続されているソケットファミリを返します。 + * + * @param sock 対象ソケット + * @return ソケットファミリ + */ + int (*get_family)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービスのアドレス情報を取得します。 + * + * @param sock 対象ソケット + * @param result アドレス情報格納用ポインタ + * @param addr アドレス + * @param service サービス + * @param is_passive サーバソケットの場合、true を指定ください。 + */ + bool (*get_addrinfo)( + struct KcSocket_ *sock, + struct addrinfo **result, + const char *addr, + const char *service, + bool is_passive); + + /** + * 指定されたアドレス、サービスにバインドします。 + * 指定されたアドレスとサービスにより、複数候補がある場合は、 + * 最初に見つかったアドレスとポートにバインドします。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス (例: "80", "http", "ssh" など) + * @return true/false (bind 成功/失敗) + */ + bool (*bind)(struct KcSocket_ *sock, const char *addr, const char *service); + + /** + * ソケットを接続待ちソケットとしてマークをつけます。 + * 保留中の接続のキュー最大長を指定します。 + * + * @param sock 対象ソケット + * @param backlog バックログ + * @return true/false (成功/失敗) + */ + bool (*listen)(struct KcSocket_ *sock, int backlog); + + /** + * ソケットへの接続を受け付けます。 + * + * @param sock 対象ソケット + * @return 受け付けたソケット + */ + struct KcSocket_ *(*accept)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービス接続します。 + * local_addr に NULL を指定した場合、自動的にローカルのアドレスが設定されます。 + * local_service に -1 を指定した場合、自動的にローカルのポート番号が割りつけられます。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス + * @param local_addr ローカルアドレス + * @param local_service ローカルサービス + * @return true/false (成功/失敗) + */ + bool (*connect)(struct KcSocket_ *sock, const char *addr, const char *service, const char *local_addr, const char *local_port); + + /** + * ソケットをクローズします。 + * + * @param sock 対象ソケット + * @return true/false (成功/失敗) + */ + bool (*close)(struct KcSocket_ *sock); + + /** + * メッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 送信されたデータサイズ + */ + ssize_t (*send)(struct KcSocket_ *sock, const char *buff, size_t size, int flags); + + /** + * メッセージを受信します。 + * 受信したメッセージは指定されたバッファに格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 読み取ったデータサイズ + */ + ssize_t (*recv)(struct KcSocket_ *sock, char *buff, size_t size, int flags); + + /** + * 指定されたアドレス、サービスにメッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param addr アドレス + * @param service サービス + * @return 送信メッセージサイズ、エラー発生時は -1 + */ + ssize_t (*sendto)( + struct KcSocket_ *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service); + + /** + * メッセージを受信します。 + * src_addr, src_service がいずれも NULL 出ない場合、 + * 送信元のアドレスとサービスが格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param src_addr 送信元アドレス格納用バッファ + * @param src_addrlen 送信元アドレス格納用バッファサイズ + * @param src_service 送信元サービス格納用バッファ + * @param src_servicelen 送信元サービス格納用バッファサイズ + * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + */ + ssize_t (*recvfrom)( + struct KcSocket_ *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen); + + /** + * TTL を設定します。 + * + * @param sock 対象ソケット + * @param val 設定する TTL + * @return true/false (成功/失敗) + */ + bool (*set_ttl)(struct KcSocket_ *sock, int val); + + /** + * マルチキャストグループに参加するために JOIN します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @param ifindex インタフェースインデックス + * @return true/false (成功/失敗) + */ + bool (*join)(struct KcSocket_ *sock, const char *addr, const char *ifname, unsigned int ifindex); + + /** + * マルチキャストグループから離脱します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @return true/false (成功/失敗) + */ + bool (*leave)(struct KcSocket_ *sock, const char *addr); + + /** + * Socket 管理情報 + */ + void *_info; + + } KcSocket; + + /** + * 指定されたタイプ、ファミリのSocket オブジェクトを構築します。 + * + * @param type タイプ(SOCK_STREAM/SOCK_DGRAM/SOCK_RAW) + * @param family ファミリ(AF_UNSPEC/AF_INET/AF_INET6) + */ + KcSocket *KcSocket_new(int type, int family); + + /** + * Socket を破棄します。 + */ + void KcSocket_delete(KcSocket *socket); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_threads.h b/modules/include/kc_threads.h new file mode 100644 index 0000000..764176f --- /dev/null +++ b/modules/include/kc_threads.h @@ -0,0 +1,98 @@ +/** + * @file kc_threads.h + * @brief スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_THREADS_H +#define KC_THREADS_H + +#include + +#if (KC_IS_WINDOWS) +#include +#else +#include +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcThread_ + { + + /** + * 指定されたスレッドが生きているか否かを返します。 + * + * @param thread 対象スレッド + * @return true/false (スレッドが生存している/していない) + */ + bool (*is_alive)(struct KcThread_ *thread); + + /** + * スレッドを開始します。 + * + * @param thread 対象スレッド + * @param args スレッドに渡す情報 + * @return true/false (スレッド起動成功/失敗) + */ + bool (*start)(struct KcThread_ *thread, void *args); + + /** + * スレッドが終了するのを待機します。 + * + * @param thread 対象スレッド + */ + bool (*join)(struct KcThread_ *thread); + + /** + * Thread 管理情報 + */ + void *_info; + + } KcThread; + + /** + * スレッドを生成します。 + * + * @param run スレッドにて実行する関数へのポインタ + */ + KcThread *KcThread_new(int (*run)(void *args)); + + /** + * Thread を破棄します。 + * + * @param thread 破棄するスレッド + */ + void KcThread_delete(KcThread *thread); + + /** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープさせるミリ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_msleep(long time, bool force); + + /** + * 指定された sec 秒 + nsec ナノ秒間現在のスレッドをスリープさせます。 + * + * @param sec スリープさせる秒数 + * @param nsec スリープさせるナノ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_sleep(long sec, long nsec, bool force); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_THREADS_H diff --git a/modules/include/kc_threads_win.h b/modules/include/kc_threads_win.h new file mode 100644 index 0000000..b40ab10 --- /dev/null +++ b/modules/include/kc_threads_win.h @@ -0,0 +1,98 @@ +/** + * @file kc_windows_thread.h + * @brief Windows 用スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_WINDOWS_THREAD_H +#define KC_WINDOWS_THREAD_H + +#include +#if (KC_IS_WINDOWS) +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#define thread_local _Thread_local + + //////////////////////////////////////////////////////////////////////// + // + // Thread + // + + enum + { + thrd_success = 0, //!< 成功 + thrd_bussy = 1, //!< busy + thrd_error = 2, //!< エラー発生 + thrd_nomem = 3, //!< メモリ不足 + thrd_timedout = 4 //!< タイムアウト発生 + }; + + typedef struct + { + HANDLE handle; + DWORD thread_id; + } thrd_t; + + typedef int (*thrd_start_t)(void *); + int thrd_create(thrd_t *thr, thrd_start_t func, void *arg); + int thrd_join(thrd_t thr, int *res); + int thrd_detach(thrd_t thr); + thrd_t thrd_current(void); + int thrd_equal(thrd_t lhs, thrd_t rhs); + int thrd_sleep(const struct timespec *duration, struct timespec *remaining); + + //////////////////////////////////////////////////////////////////////// + // + // mutex + // + + enum + { + mtx_plain = 0, //!< 通常 + mtx_recursive = 1, //!< 再帰 + mtx_timed = 2 //!< timed + }; + + typedef struct + { + CRITICAL_SECTION cs; + int type; + } mtx_t; + + int mtx_init(mtx_t *mtx, int type); + void mtx_destroy(mtx_t *mtx); + int mtx_lock(mtx_t *mtx); + int mtx_unlock(mtx_t *mtx); + + //////////////////////////////////////////////////////////////////////// + // + // cond + // + + typedef struct + { + CONDITION_VARIABLE cond; + } cnd_t; + + int cnd_init(cnd_t *cond); + int cnd_signal(cnd_t *cond); + int cnd_broadcast(cnd_t *cond); + int cnd_wait(cnd_t *cond, mtx_t *mtx); + int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts); + void cnd_destroy(cnd_t *cond); + +#endif // (KC_IS_WINDOWS) +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_windows.h b/modules/include/kc_windows.h index ee70898..6e1c659 100644 --- a/modules/include/kc_windows.h +++ b/modules/include/kc_windows.h @@ -37,9 +37,9 @@ #endif // よく利用されるヘッダファイルをインクルードする -#include #include #include +#include #ifdef _MSC_VER #pragma comment(lib, "ws2_32.lib") #endif diff --git a/config.mk b/config.mk index a0ac27c..f7c6e47 100644 --- a/config.mk +++ b/config.mk @@ -25,14 +25,17 @@ # for Windows CROSS_COMPILE ?=i686-w64-mingw32- LIBS ?= -lws2_32 +DEBUG_OPTIONS ?= else # For Linux ifeq ($(strip $(ARCH)),aarch64) CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector else CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector endif endif diff --git a/mk/base-cmd.mk b/mk/base-cmd.mk index f429bcf..e74c114 100644 --- a/mk/base-cmd.mk +++ b/mk/base-cmd.mk @@ -33,8 +33,13 @@ CC = $(CROSS_COMPILE)gcc CXX = $(CROSS_COMPILE)g++ + #CC = $(CROSS_COMPILE)clang #CXX = $(CROSS_COMPILE)clang++ + +#CC = $(CROSS_COMPILE)clang-16 +#CXX = $(CROSS_COMPILE)clang++16 + RANLIB = $(CROSS_COMPILE)ranlib ADDR2LINE = $(CROSS_COMPILE)addr2line AR = $(CROSS_COMPILE)ar diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 2cc417c..eebded1 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -69,7 +69,7 @@ # 値が2の場合、さらなるチェックを追加するが、規格準拠のプログラムが失敗する可能性がある。 # いくつかのチェックは、コンパイル時に実行され、コンパイラの警告として表示される。 # ------------------------------------------------------------------------------ -DEBUG_OPTIONS += -fstack-protector +# DEBUG_OPTIONS += -fstack-protector DEBUG_OPTIONS += --coverage DEBUG_OPTIONS += -g3 -ggdb DEBUG_OPTIONS += -O0 diff --git a/mk/link-a-conf.mk b/mk/link-a-conf.mk index 0e872f3..631e6d5 100644 --- a/mk/link-a-conf.mk +++ b/mk/link-a-conf.mk @@ -10,8 +10,11 @@ HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) +HEADER_FILES_WIN = $(wildcard include/win/*.h) $(wildcard include/win/*.hpp) +TOP_HEADER_FILES_WIN = $(addprefix $(TOPDIR)/include/win/,$(notdir $(HEADER_FILES_WIN))) + CLEAN_FILES += $(TOP_TARGET) -CLEAN_FILES += $(TOP_HEADER_FILES) +CLEAN_FILES += $(TOP_HEADER_FILES) $(TOP_HEADER_FILES_WIN) endif endif diff --git a/mk/link-a-rule.mk b/mk/link-a-rule.mk index d94a54a..657d858 100644 --- a/mk/link-a-rule.mk +++ b/mk/link-a-rule.mk @@ -10,6 +10,7 @@ $(TOP_TARGET): $(TARGET) ifneq ($(strip $(HEADER_FILES)),) $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ + $(CP) -f $(HEADER_FILES_WIN) $(TOPDIR)/include/win/ endif $(CP) -f $(TARGET) $(TOPDIR)/lib/ diff --git a/modules/include/kc_lock_guard.h b/modules/include/kc_lock_guard.h index 8bb70bd..ad3915f 100644 --- a/modules/include/kc_lock_guard.h +++ b/modules/include/kc_lock_guard.h @@ -7,18 +7,16 @@ #define KC_LOCK_GUARD_H #include -#include - +#include /** * LockGuard 管理構造体 */ typedef struct { - mtx_t* mutex; + mtx_t *mutex; } KcLockGuard; - /** * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 * 使用例) @@ -34,8 +32,7 @@ * } */ #define kc_lock_guard(lock) \ - for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) - + for (KcLockGuard _guard = kc_lock_guard_init(lock, __FILE__, __func__, __LINE__); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 @@ -44,15 +41,13 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex); - +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line); /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard); +void kc_lock_guard_release(KcLockGuard *guard); - -#endif // KC_LOCK_GUARD_H +#endif // KC_LOCK_GUARD_H diff --git a/modules/include/kc_memory.h b/modules/include/kc_memory.h index ffce4ed..c6ba9e9 100644 --- a/modules/include/kc_memory.h +++ b/modules/include/kc_memory.h @@ -22,12 +22,13 @@ #ifndef KC_MEMORY_H #define KC_MEMORY_H -#include +#include #include #include #include #include +#include #ifdef __cplusplus extern "C" @@ -273,8 +274,6 @@ #define calloc(nmemb, size) kc_memory_manager->calloc(nmemb, size, __FILE__, __func__, __LINE__) #define realloc(ptr, size) kc_memory_manager->realloc(ptr, size, __FILE__, __func__, __LINE__) #define free(ptr) kc_memory_manager->free(ptr) -#else -#include #endif #ifdef __cplusplus diff --git a/modules/include/kc_socket.h b/modules/include/kc_socket.h new file mode 100644 index 0000000..7d952c7 --- /dev/null +++ b/modules/include/kc_socket.h @@ -0,0 +1,291 @@ +/** + * @file kc_socket.h + * @brief ソケットモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_SOCKET_H +#define KC_SOCKET_H + +#include + +#if (KC_IS_WINDOWS) +typedef SOCKET socket_t; +#define SHUT_RD SD_RECEIVE +#define SHUT_WR SD_SEND +#define SHUT_RDWR SD_BOTH +#define sockclose(sockfd) closesocket(sockfd) +#else +#include +#include +#include +#include +#include +#include +#include +typedef int socket_t; +#define INVALID_SOCKET (-1) +#define SOCKET_ERROR (-1) +#define sockclose(sockfd) close(sockfd) +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcSocket_ + { + + /** + * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr 接続先アドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_remote_addr)(struct KcSocket_ *sock, char *addr, size_t addrlen); + + /** + * ソケットの接続先ポート番号を返します。 + * 接続先ポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_remote_port)(struct KcSocket_ *sock); + + /** + * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr ローカルアドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_local_addr)(struct KcSocket_ *sock, char *addr, size_t addlen); + + /** + * ソケットのローカルポート番号を返します。 + * ローカルポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_local_port)(struct KcSocket_ *sock); + + /** + * ソケットディスクリプタを返します。 + * 通常は、ソケットディスクリプタを直接操作しないでください。 + * + * @param sock 対象ソケット + * @return ソケットディスクリプタ + */ + socket_t (*get_socket)(struct KcSocket_ *sock); + + /** + * ソケットタイプを返します。 + * + * @param sock 対象ソケット + * @return ソケットタイプ + */ + int (*get_type)(struct KcSocket_ *sock); + + /** + * ソケットファミリを返します。 + * 実際にバインド、あるいは接続されているソケットファミリを返します。 + * + * @param sock 対象ソケット + * @return ソケットファミリ + */ + int (*get_family)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービスのアドレス情報を取得します。 + * + * @param sock 対象ソケット + * @param result アドレス情報格納用ポインタ + * @param addr アドレス + * @param service サービス + * @param is_passive サーバソケットの場合、true を指定ください。 + */ + bool (*get_addrinfo)( + struct KcSocket_ *sock, + struct addrinfo **result, + const char *addr, + const char *service, + bool is_passive); + + /** + * 指定されたアドレス、サービスにバインドします。 + * 指定されたアドレスとサービスにより、複数候補がある場合は、 + * 最初に見つかったアドレスとポートにバインドします。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス (例: "80", "http", "ssh" など) + * @return true/false (bind 成功/失敗) + */ + bool (*bind)(struct KcSocket_ *sock, const char *addr, const char *service); + + /** + * ソケットを接続待ちソケットとしてマークをつけます。 + * 保留中の接続のキュー最大長を指定します。 + * + * @param sock 対象ソケット + * @param backlog バックログ + * @return true/false (成功/失敗) + */ + bool (*listen)(struct KcSocket_ *sock, int backlog); + + /** + * ソケットへの接続を受け付けます。 + * + * @param sock 対象ソケット + * @return 受け付けたソケット + */ + struct KcSocket_ *(*accept)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービス接続します。 + * local_addr に NULL を指定した場合、自動的にローカルのアドレスが設定されます。 + * local_service に -1 を指定した場合、自動的にローカルのポート番号が割りつけられます。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス + * @param local_addr ローカルアドレス + * @param local_service ローカルサービス + * @return true/false (成功/失敗) + */ + bool (*connect)(struct KcSocket_ *sock, const char *addr, const char *service, const char *local_addr, const char *local_port); + + /** + * ソケットをクローズします。 + * + * @param sock 対象ソケット + * @return true/false (成功/失敗) + */ + bool (*close)(struct KcSocket_ *sock); + + /** + * メッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 送信されたデータサイズ + */ + ssize_t (*send)(struct KcSocket_ *sock, const char *buff, size_t size, int flags); + + /** + * メッセージを受信します。 + * 受信したメッセージは指定されたバッファに格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 読み取ったデータサイズ + */ + ssize_t (*recv)(struct KcSocket_ *sock, char *buff, size_t size, int flags); + + /** + * 指定されたアドレス、サービスにメッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param addr アドレス + * @param service サービス + * @return 送信メッセージサイズ、エラー発生時は -1 + */ + ssize_t (*sendto)( + struct KcSocket_ *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service); + + /** + * メッセージを受信します。 + * src_addr, src_service がいずれも NULL 出ない場合、 + * 送信元のアドレスとサービスが格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param src_addr 送信元アドレス格納用バッファ + * @param src_addrlen 送信元アドレス格納用バッファサイズ + * @param src_service 送信元サービス格納用バッファ + * @param src_servicelen 送信元サービス格納用バッファサイズ + * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + */ + ssize_t (*recvfrom)( + struct KcSocket_ *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen); + + /** + * TTL を設定します。 + * + * @param sock 対象ソケット + * @param val 設定する TTL + * @return true/false (成功/失敗) + */ + bool (*set_ttl)(struct KcSocket_ *sock, int val); + + /** + * マルチキャストグループに参加するために JOIN します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @param ifindex インタフェースインデックス + * @return true/false (成功/失敗) + */ + bool (*join)(struct KcSocket_ *sock, const char *addr, const char *ifname, unsigned int ifindex); + + /** + * マルチキャストグループから離脱します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @return true/false (成功/失敗) + */ + bool (*leave)(struct KcSocket_ *sock, const char *addr); + + /** + * Socket 管理情報 + */ + void *_info; + + } KcSocket; + + /** + * 指定されたタイプ、ファミリのSocket オブジェクトを構築します。 + * + * @param type タイプ(SOCK_STREAM/SOCK_DGRAM/SOCK_RAW) + * @param family ファミリ(AF_UNSPEC/AF_INET/AF_INET6) + */ + KcSocket *KcSocket_new(int type, int family); + + /** + * Socket を破棄します。 + */ + void KcSocket_delete(KcSocket *socket); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_threads.h b/modules/include/kc_threads.h new file mode 100644 index 0000000..764176f --- /dev/null +++ b/modules/include/kc_threads.h @@ -0,0 +1,98 @@ +/** + * @file kc_threads.h + * @brief スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_THREADS_H +#define KC_THREADS_H + +#include + +#if (KC_IS_WINDOWS) +#include +#else +#include +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcThread_ + { + + /** + * 指定されたスレッドが生きているか否かを返します。 + * + * @param thread 対象スレッド + * @return true/false (スレッドが生存している/していない) + */ + bool (*is_alive)(struct KcThread_ *thread); + + /** + * スレッドを開始します。 + * + * @param thread 対象スレッド + * @param args スレッドに渡す情報 + * @return true/false (スレッド起動成功/失敗) + */ + bool (*start)(struct KcThread_ *thread, void *args); + + /** + * スレッドが終了するのを待機します。 + * + * @param thread 対象スレッド + */ + bool (*join)(struct KcThread_ *thread); + + /** + * Thread 管理情報 + */ + void *_info; + + } KcThread; + + /** + * スレッドを生成します。 + * + * @param run スレッドにて実行する関数へのポインタ + */ + KcThread *KcThread_new(int (*run)(void *args)); + + /** + * Thread を破棄します。 + * + * @param thread 破棄するスレッド + */ + void KcThread_delete(KcThread *thread); + + /** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープさせるミリ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_msleep(long time, bool force); + + /** + * 指定された sec 秒 + nsec ナノ秒間現在のスレッドをスリープさせます。 + * + * @param sec スリープさせる秒数 + * @param nsec スリープさせるナノ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_sleep(long sec, long nsec, bool force); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_THREADS_H diff --git a/modules/include/kc_threads_win.h b/modules/include/kc_threads_win.h new file mode 100644 index 0000000..b40ab10 --- /dev/null +++ b/modules/include/kc_threads_win.h @@ -0,0 +1,98 @@ +/** + * @file kc_windows_thread.h + * @brief Windows 用スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_WINDOWS_THREAD_H +#define KC_WINDOWS_THREAD_H + +#include +#if (KC_IS_WINDOWS) +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#define thread_local _Thread_local + + //////////////////////////////////////////////////////////////////////// + // + // Thread + // + + enum + { + thrd_success = 0, //!< 成功 + thrd_bussy = 1, //!< busy + thrd_error = 2, //!< エラー発生 + thrd_nomem = 3, //!< メモリ不足 + thrd_timedout = 4 //!< タイムアウト発生 + }; + + typedef struct + { + HANDLE handle; + DWORD thread_id; + } thrd_t; + + typedef int (*thrd_start_t)(void *); + int thrd_create(thrd_t *thr, thrd_start_t func, void *arg); + int thrd_join(thrd_t thr, int *res); + int thrd_detach(thrd_t thr); + thrd_t thrd_current(void); + int thrd_equal(thrd_t lhs, thrd_t rhs); + int thrd_sleep(const struct timespec *duration, struct timespec *remaining); + + //////////////////////////////////////////////////////////////////////// + // + // mutex + // + + enum + { + mtx_plain = 0, //!< 通常 + mtx_recursive = 1, //!< 再帰 + mtx_timed = 2 //!< timed + }; + + typedef struct + { + CRITICAL_SECTION cs; + int type; + } mtx_t; + + int mtx_init(mtx_t *mtx, int type); + void mtx_destroy(mtx_t *mtx); + int mtx_lock(mtx_t *mtx); + int mtx_unlock(mtx_t *mtx); + + //////////////////////////////////////////////////////////////////////// + // + // cond + // + + typedef struct + { + CONDITION_VARIABLE cond; + } cnd_t; + + int cnd_init(cnd_t *cond); + int cnd_signal(cnd_t *cond); + int cnd_broadcast(cnd_t *cond); + int cnd_wait(cnd_t *cond, mtx_t *mtx); + int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts); + void cnd_destroy(cnd_t *cond); + +#endif // (KC_IS_WINDOWS) +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_windows.h b/modules/include/kc_windows.h index ee70898..6e1c659 100644 --- a/modules/include/kc_windows.h +++ b/modules/include/kc_windows.h @@ -37,9 +37,9 @@ #endif // よく利用されるヘッダファイルをインクルードする -#include #include #include +#include #ifdef _MSC_VER #pragma comment(lib, "ws2_32.lib") #endif diff --git a/modules/src/kc_assert.c b/modules/src/kc_assert.c index 371fc8f..7cd4ecc 100644 --- a/modules/src/kc_assert.c +++ b/modules/src/kc_assert.c @@ -6,9 +6,9 @@ #include #include #include -#include #include #include +#include // プロトタイプ宣言 static void assert_default_handler(const char *msg); diff --git a/config.mk b/config.mk index a0ac27c..f7c6e47 100644 --- a/config.mk +++ b/config.mk @@ -25,14 +25,17 @@ # for Windows CROSS_COMPILE ?=i686-w64-mingw32- LIBS ?= -lws2_32 +DEBUG_OPTIONS ?= else # For Linux ifeq ($(strip $(ARCH)),aarch64) CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector else CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector endif endif diff --git a/mk/base-cmd.mk b/mk/base-cmd.mk index f429bcf..e74c114 100644 --- a/mk/base-cmd.mk +++ b/mk/base-cmd.mk @@ -33,8 +33,13 @@ CC = $(CROSS_COMPILE)gcc CXX = $(CROSS_COMPILE)g++ + #CC = $(CROSS_COMPILE)clang #CXX = $(CROSS_COMPILE)clang++ + +#CC = $(CROSS_COMPILE)clang-16 +#CXX = $(CROSS_COMPILE)clang++16 + RANLIB = $(CROSS_COMPILE)ranlib ADDR2LINE = $(CROSS_COMPILE)addr2line AR = $(CROSS_COMPILE)ar diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 2cc417c..eebded1 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -69,7 +69,7 @@ # 値が2の場合、さらなるチェックを追加するが、規格準拠のプログラムが失敗する可能性がある。 # いくつかのチェックは、コンパイル時に実行され、コンパイラの警告として表示される。 # ------------------------------------------------------------------------------ -DEBUG_OPTIONS += -fstack-protector +# DEBUG_OPTIONS += -fstack-protector DEBUG_OPTIONS += --coverage DEBUG_OPTIONS += -g3 -ggdb DEBUG_OPTIONS += -O0 diff --git a/mk/link-a-conf.mk b/mk/link-a-conf.mk index 0e872f3..631e6d5 100644 --- a/mk/link-a-conf.mk +++ b/mk/link-a-conf.mk @@ -10,8 +10,11 @@ HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) +HEADER_FILES_WIN = $(wildcard include/win/*.h) $(wildcard include/win/*.hpp) +TOP_HEADER_FILES_WIN = $(addprefix $(TOPDIR)/include/win/,$(notdir $(HEADER_FILES_WIN))) + CLEAN_FILES += $(TOP_TARGET) -CLEAN_FILES += $(TOP_HEADER_FILES) +CLEAN_FILES += $(TOP_HEADER_FILES) $(TOP_HEADER_FILES_WIN) endif endif diff --git a/mk/link-a-rule.mk b/mk/link-a-rule.mk index d94a54a..657d858 100644 --- a/mk/link-a-rule.mk +++ b/mk/link-a-rule.mk @@ -10,6 +10,7 @@ $(TOP_TARGET): $(TARGET) ifneq ($(strip $(HEADER_FILES)),) $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ + $(CP) -f $(HEADER_FILES_WIN) $(TOPDIR)/include/win/ endif $(CP) -f $(TARGET) $(TOPDIR)/lib/ diff --git a/modules/include/kc_lock_guard.h b/modules/include/kc_lock_guard.h index 8bb70bd..ad3915f 100644 --- a/modules/include/kc_lock_guard.h +++ b/modules/include/kc_lock_guard.h @@ -7,18 +7,16 @@ #define KC_LOCK_GUARD_H #include -#include - +#include /** * LockGuard 管理構造体 */ typedef struct { - mtx_t* mutex; + mtx_t *mutex; } KcLockGuard; - /** * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 * 使用例) @@ -34,8 +32,7 @@ * } */ #define kc_lock_guard(lock) \ - for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) - + for (KcLockGuard _guard = kc_lock_guard_init(lock, __FILE__, __func__, __LINE__); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 @@ -44,15 +41,13 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex); - +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line); /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard); +void kc_lock_guard_release(KcLockGuard *guard); - -#endif // KC_LOCK_GUARD_H +#endif // KC_LOCK_GUARD_H diff --git a/modules/include/kc_memory.h b/modules/include/kc_memory.h index ffce4ed..c6ba9e9 100644 --- a/modules/include/kc_memory.h +++ b/modules/include/kc_memory.h @@ -22,12 +22,13 @@ #ifndef KC_MEMORY_H #define KC_MEMORY_H -#include +#include #include #include #include #include +#include #ifdef __cplusplus extern "C" @@ -273,8 +274,6 @@ #define calloc(nmemb, size) kc_memory_manager->calloc(nmemb, size, __FILE__, __func__, __LINE__) #define realloc(ptr, size) kc_memory_manager->realloc(ptr, size, __FILE__, __func__, __LINE__) #define free(ptr) kc_memory_manager->free(ptr) -#else -#include #endif #ifdef __cplusplus diff --git a/modules/include/kc_socket.h b/modules/include/kc_socket.h new file mode 100644 index 0000000..7d952c7 --- /dev/null +++ b/modules/include/kc_socket.h @@ -0,0 +1,291 @@ +/** + * @file kc_socket.h + * @brief ソケットモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_SOCKET_H +#define KC_SOCKET_H + +#include + +#if (KC_IS_WINDOWS) +typedef SOCKET socket_t; +#define SHUT_RD SD_RECEIVE +#define SHUT_WR SD_SEND +#define SHUT_RDWR SD_BOTH +#define sockclose(sockfd) closesocket(sockfd) +#else +#include +#include +#include +#include +#include +#include +#include +typedef int socket_t; +#define INVALID_SOCKET (-1) +#define SOCKET_ERROR (-1) +#define sockclose(sockfd) close(sockfd) +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcSocket_ + { + + /** + * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr 接続先アドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_remote_addr)(struct KcSocket_ *sock, char *addr, size_t addrlen); + + /** + * ソケットの接続先ポート番号を返します。 + * 接続先ポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_remote_port)(struct KcSocket_ *sock); + + /** + * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr ローカルアドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_local_addr)(struct KcSocket_ *sock, char *addr, size_t addlen); + + /** + * ソケットのローカルポート番号を返します。 + * ローカルポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_local_port)(struct KcSocket_ *sock); + + /** + * ソケットディスクリプタを返します。 + * 通常は、ソケットディスクリプタを直接操作しないでください。 + * + * @param sock 対象ソケット + * @return ソケットディスクリプタ + */ + socket_t (*get_socket)(struct KcSocket_ *sock); + + /** + * ソケットタイプを返します。 + * + * @param sock 対象ソケット + * @return ソケットタイプ + */ + int (*get_type)(struct KcSocket_ *sock); + + /** + * ソケットファミリを返します。 + * 実際にバインド、あるいは接続されているソケットファミリを返します。 + * + * @param sock 対象ソケット + * @return ソケットファミリ + */ + int (*get_family)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービスのアドレス情報を取得します。 + * + * @param sock 対象ソケット + * @param result アドレス情報格納用ポインタ + * @param addr アドレス + * @param service サービス + * @param is_passive サーバソケットの場合、true を指定ください。 + */ + bool (*get_addrinfo)( + struct KcSocket_ *sock, + struct addrinfo **result, + const char *addr, + const char *service, + bool is_passive); + + /** + * 指定されたアドレス、サービスにバインドします。 + * 指定されたアドレスとサービスにより、複数候補がある場合は、 + * 最初に見つかったアドレスとポートにバインドします。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス (例: "80", "http", "ssh" など) + * @return true/false (bind 成功/失敗) + */ + bool (*bind)(struct KcSocket_ *sock, const char *addr, const char *service); + + /** + * ソケットを接続待ちソケットとしてマークをつけます。 + * 保留中の接続のキュー最大長を指定します。 + * + * @param sock 対象ソケット + * @param backlog バックログ + * @return true/false (成功/失敗) + */ + bool (*listen)(struct KcSocket_ *sock, int backlog); + + /** + * ソケットへの接続を受け付けます。 + * + * @param sock 対象ソケット + * @return 受け付けたソケット + */ + struct KcSocket_ *(*accept)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービス接続します。 + * local_addr に NULL を指定した場合、自動的にローカルのアドレスが設定されます。 + * local_service に -1 を指定した場合、自動的にローカルのポート番号が割りつけられます。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス + * @param local_addr ローカルアドレス + * @param local_service ローカルサービス + * @return true/false (成功/失敗) + */ + bool (*connect)(struct KcSocket_ *sock, const char *addr, const char *service, const char *local_addr, const char *local_port); + + /** + * ソケットをクローズします。 + * + * @param sock 対象ソケット + * @return true/false (成功/失敗) + */ + bool (*close)(struct KcSocket_ *sock); + + /** + * メッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 送信されたデータサイズ + */ + ssize_t (*send)(struct KcSocket_ *sock, const char *buff, size_t size, int flags); + + /** + * メッセージを受信します。 + * 受信したメッセージは指定されたバッファに格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 読み取ったデータサイズ + */ + ssize_t (*recv)(struct KcSocket_ *sock, char *buff, size_t size, int flags); + + /** + * 指定されたアドレス、サービスにメッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param addr アドレス + * @param service サービス + * @return 送信メッセージサイズ、エラー発生時は -1 + */ + ssize_t (*sendto)( + struct KcSocket_ *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service); + + /** + * メッセージを受信します。 + * src_addr, src_service がいずれも NULL 出ない場合、 + * 送信元のアドレスとサービスが格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param src_addr 送信元アドレス格納用バッファ + * @param src_addrlen 送信元アドレス格納用バッファサイズ + * @param src_service 送信元サービス格納用バッファ + * @param src_servicelen 送信元サービス格納用バッファサイズ + * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + */ + ssize_t (*recvfrom)( + struct KcSocket_ *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen); + + /** + * TTL を設定します。 + * + * @param sock 対象ソケット + * @param val 設定する TTL + * @return true/false (成功/失敗) + */ + bool (*set_ttl)(struct KcSocket_ *sock, int val); + + /** + * マルチキャストグループに参加するために JOIN します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @param ifindex インタフェースインデックス + * @return true/false (成功/失敗) + */ + bool (*join)(struct KcSocket_ *sock, const char *addr, const char *ifname, unsigned int ifindex); + + /** + * マルチキャストグループから離脱します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @return true/false (成功/失敗) + */ + bool (*leave)(struct KcSocket_ *sock, const char *addr); + + /** + * Socket 管理情報 + */ + void *_info; + + } KcSocket; + + /** + * 指定されたタイプ、ファミリのSocket オブジェクトを構築します。 + * + * @param type タイプ(SOCK_STREAM/SOCK_DGRAM/SOCK_RAW) + * @param family ファミリ(AF_UNSPEC/AF_INET/AF_INET6) + */ + KcSocket *KcSocket_new(int type, int family); + + /** + * Socket を破棄します。 + */ + void KcSocket_delete(KcSocket *socket); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_threads.h b/modules/include/kc_threads.h new file mode 100644 index 0000000..764176f --- /dev/null +++ b/modules/include/kc_threads.h @@ -0,0 +1,98 @@ +/** + * @file kc_threads.h + * @brief スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_THREADS_H +#define KC_THREADS_H + +#include + +#if (KC_IS_WINDOWS) +#include +#else +#include +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcThread_ + { + + /** + * 指定されたスレッドが生きているか否かを返します。 + * + * @param thread 対象スレッド + * @return true/false (スレッドが生存している/していない) + */ + bool (*is_alive)(struct KcThread_ *thread); + + /** + * スレッドを開始します。 + * + * @param thread 対象スレッド + * @param args スレッドに渡す情報 + * @return true/false (スレッド起動成功/失敗) + */ + bool (*start)(struct KcThread_ *thread, void *args); + + /** + * スレッドが終了するのを待機します。 + * + * @param thread 対象スレッド + */ + bool (*join)(struct KcThread_ *thread); + + /** + * Thread 管理情報 + */ + void *_info; + + } KcThread; + + /** + * スレッドを生成します。 + * + * @param run スレッドにて実行する関数へのポインタ + */ + KcThread *KcThread_new(int (*run)(void *args)); + + /** + * Thread を破棄します。 + * + * @param thread 破棄するスレッド + */ + void KcThread_delete(KcThread *thread); + + /** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープさせるミリ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_msleep(long time, bool force); + + /** + * 指定された sec 秒 + nsec ナノ秒間現在のスレッドをスリープさせます。 + * + * @param sec スリープさせる秒数 + * @param nsec スリープさせるナノ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_sleep(long sec, long nsec, bool force); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_THREADS_H diff --git a/modules/include/kc_threads_win.h b/modules/include/kc_threads_win.h new file mode 100644 index 0000000..b40ab10 --- /dev/null +++ b/modules/include/kc_threads_win.h @@ -0,0 +1,98 @@ +/** + * @file kc_windows_thread.h + * @brief Windows 用スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_WINDOWS_THREAD_H +#define KC_WINDOWS_THREAD_H + +#include +#if (KC_IS_WINDOWS) +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#define thread_local _Thread_local + + //////////////////////////////////////////////////////////////////////// + // + // Thread + // + + enum + { + thrd_success = 0, //!< 成功 + thrd_bussy = 1, //!< busy + thrd_error = 2, //!< エラー発生 + thrd_nomem = 3, //!< メモリ不足 + thrd_timedout = 4 //!< タイムアウト発生 + }; + + typedef struct + { + HANDLE handle; + DWORD thread_id; + } thrd_t; + + typedef int (*thrd_start_t)(void *); + int thrd_create(thrd_t *thr, thrd_start_t func, void *arg); + int thrd_join(thrd_t thr, int *res); + int thrd_detach(thrd_t thr); + thrd_t thrd_current(void); + int thrd_equal(thrd_t lhs, thrd_t rhs); + int thrd_sleep(const struct timespec *duration, struct timespec *remaining); + + //////////////////////////////////////////////////////////////////////// + // + // mutex + // + + enum + { + mtx_plain = 0, //!< 通常 + mtx_recursive = 1, //!< 再帰 + mtx_timed = 2 //!< timed + }; + + typedef struct + { + CRITICAL_SECTION cs; + int type; + } mtx_t; + + int mtx_init(mtx_t *mtx, int type); + void mtx_destroy(mtx_t *mtx); + int mtx_lock(mtx_t *mtx); + int mtx_unlock(mtx_t *mtx); + + //////////////////////////////////////////////////////////////////////// + // + // cond + // + + typedef struct + { + CONDITION_VARIABLE cond; + } cnd_t; + + int cnd_init(cnd_t *cond); + int cnd_signal(cnd_t *cond); + int cnd_broadcast(cnd_t *cond); + int cnd_wait(cnd_t *cond, mtx_t *mtx); + int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts); + void cnd_destroy(cnd_t *cond); + +#endif // (KC_IS_WINDOWS) +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_windows.h b/modules/include/kc_windows.h index ee70898..6e1c659 100644 --- a/modules/include/kc_windows.h +++ b/modules/include/kc_windows.h @@ -37,9 +37,9 @@ #endif // よく利用されるヘッダファイルをインクルードする -#include #include #include +#include #ifdef _MSC_VER #pragma comment(lib, "ws2_32.lib") #endif diff --git a/modules/src/kc_assert.c b/modules/src/kc_assert.c index 371fc8f..7cd4ecc 100644 --- a/modules/src/kc_assert.c +++ b/modules/src/kc_assert.c @@ -6,9 +6,9 @@ #include #include #include -#include #include #include +#include // プロトタイプ宣言 static void assert_default_handler(const char *msg); diff --git a/modules/src/kc_env.c b/modules/src/kc_env.c index 0675c73..95f185c 100644 --- a/modules/src/kc_env.c +++ b/modules/src/kc_env.c @@ -50,7 +50,7 @@ char *result = NULL; #if (KC_IS_WINDOWS) static char buff[KC_ENV_BUFFER_MAX]; - DWORD ret = GetEnvironmentVarable(key_name, (LPSTR)buff, sizeof(buff)); + DWORD ret = GetEnvironmentVariable(key_name, (LPSTR)buff, sizeof(buff)); if (ret != 0) { result = buff; diff --git a/config.mk b/config.mk index a0ac27c..f7c6e47 100644 --- a/config.mk +++ b/config.mk @@ -25,14 +25,17 @@ # for Windows CROSS_COMPILE ?=i686-w64-mingw32- LIBS ?= -lws2_32 +DEBUG_OPTIONS ?= else # For Linux ifeq ($(strip $(ARCH)),aarch64) CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector else CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector endif endif diff --git a/mk/base-cmd.mk b/mk/base-cmd.mk index f429bcf..e74c114 100644 --- a/mk/base-cmd.mk +++ b/mk/base-cmd.mk @@ -33,8 +33,13 @@ CC = $(CROSS_COMPILE)gcc CXX = $(CROSS_COMPILE)g++ + #CC = $(CROSS_COMPILE)clang #CXX = $(CROSS_COMPILE)clang++ + +#CC = $(CROSS_COMPILE)clang-16 +#CXX = $(CROSS_COMPILE)clang++16 + RANLIB = $(CROSS_COMPILE)ranlib ADDR2LINE = $(CROSS_COMPILE)addr2line AR = $(CROSS_COMPILE)ar diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 2cc417c..eebded1 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -69,7 +69,7 @@ # 値が2の場合、さらなるチェックを追加するが、規格準拠のプログラムが失敗する可能性がある。 # いくつかのチェックは、コンパイル時に実行され、コンパイラの警告として表示される。 # ------------------------------------------------------------------------------ -DEBUG_OPTIONS += -fstack-protector +# DEBUG_OPTIONS += -fstack-protector DEBUG_OPTIONS += --coverage DEBUG_OPTIONS += -g3 -ggdb DEBUG_OPTIONS += -O0 diff --git a/mk/link-a-conf.mk b/mk/link-a-conf.mk index 0e872f3..631e6d5 100644 --- a/mk/link-a-conf.mk +++ b/mk/link-a-conf.mk @@ -10,8 +10,11 @@ HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) +HEADER_FILES_WIN = $(wildcard include/win/*.h) $(wildcard include/win/*.hpp) +TOP_HEADER_FILES_WIN = $(addprefix $(TOPDIR)/include/win/,$(notdir $(HEADER_FILES_WIN))) + CLEAN_FILES += $(TOP_TARGET) -CLEAN_FILES += $(TOP_HEADER_FILES) +CLEAN_FILES += $(TOP_HEADER_FILES) $(TOP_HEADER_FILES_WIN) endif endif diff --git a/mk/link-a-rule.mk b/mk/link-a-rule.mk index d94a54a..657d858 100644 --- a/mk/link-a-rule.mk +++ b/mk/link-a-rule.mk @@ -10,6 +10,7 @@ $(TOP_TARGET): $(TARGET) ifneq ($(strip $(HEADER_FILES)),) $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ + $(CP) -f $(HEADER_FILES_WIN) $(TOPDIR)/include/win/ endif $(CP) -f $(TARGET) $(TOPDIR)/lib/ diff --git a/modules/include/kc_lock_guard.h b/modules/include/kc_lock_guard.h index 8bb70bd..ad3915f 100644 --- a/modules/include/kc_lock_guard.h +++ b/modules/include/kc_lock_guard.h @@ -7,18 +7,16 @@ #define KC_LOCK_GUARD_H #include -#include - +#include /** * LockGuard 管理構造体 */ typedef struct { - mtx_t* mutex; + mtx_t *mutex; } KcLockGuard; - /** * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 * 使用例) @@ -34,8 +32,7 @@ * } */ #define kc_lock_guard(lock) \ - for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) - + for (KcLockGuard _guard = kc_lock_guard_init(lock, __FILE__, __func__, __LINE__); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 @@ -44,15 +41,13 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex); - +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line); /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard); +void kc_lock_guard_release(KcLockGuard *guard); - -#endif // KC_LOCK_GUARD_H +#endif // KC_LOCK_GUARD_H diff --git a/modules/include/kc_memory.h b/modules/include/kc_memory.h index ffce4ed..c6ba9e9 100644 --- a/modules/include/kc_memory.h +++ b/modules/include/kc_memory.h @@ -22,12 +22,13 @@ #ifndef KC_MEMORY_H #define KC_MEMORY_H -#include +#include #include #include #include #include +#include #ifdef __cplusplus extern "C" @@ -273,8 +274,6 @@ #define calloc(nmemb, size) kc_memory_manager->calloc(nmemb, size, __FILE__, __func__, __LINE__) #define realloc(ptr, size) kc_memory_manager->realloc(ptr, size, __FILE__, __func__, __LINE__) #define free(ptr) kc_memory_manager->free(ptr) -#else -#include #endif #ifdef __cplusplus diff --git a/modules/include/kc_socket.h b/modules/include/kc_socket.h new file mode 100644 index 0000000..7d952c7 --- /dev/null +++ b/modules/include/kc_socket.h @@ -0,0 +1,291 @@ +/** + * @file kc_socket.h + * @brief ソケットモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_SOCKET_H +#define KC_SOCKET_H + +#include + +#if (KC_IS_WINDOWS) +typedef SOCKET socket_t; +#define SHUT_RD SD_RECEIVE +#define SHUT_WR SD_SEND +#define SHUT_RDWR SD_BOTH +#define sockclose(sockfd) closesocket(sockfd) +#else +#include +#include +#include +#include +#include +#include +#include +typedef int socket_t; +#define INVALID_SOCKET (-1) +#define SOCKET_ERROR (-1) +#define sockclose(sockfd) close(sockfd) +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcSocket_ + { + + /** + * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr 接続先アドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_remote_addr)(struct KcSocket_ *sock, char *addr, size_t addrlen); + + /** + * ソケットの接続先ポート番号を返します。 + * 接続先ポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_remote_port)(struct KcSocket_ *sock); + + /** + * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr ローカルアドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_local_addr)(struct KcSocket_ *sock, char *addr, size_t addlen); + + /** + * ソケットのローカルポート番号を返します。 + * ローカルポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_local_port)(struct KcSocket_ *sock); + + /** + * ソケットディスクリプタを返します。 + * 通常は、ソケットディスクリプタを直接操作しないでください。 + * + * @param sock 対象ソケット + * @return ソケットディスクリプタ + */ + socket_t (*get_socket)(struct KcSocket_ *sock); + + /** + * ソケットタイプを返します。 + * + * @param sock 対象ソケット + * @return ソケットタイプ + */ + int (*get_type)(struct KcSocket_ *sock); + + /** + * ソケットファミリを返します。 + * 実際にバインド、あるいは接続されているソケットファミリを返します。 + * + * @param sock 対象ソケット + * @return ソケットファミリ + */ + int (*get_family)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービスのアドレス情報を取得します。 + * + * @param sock 対象ソケット + * @param result アドレス情報格納用ポインタ + * @param addr アドレス + * @param service サービス + * @param is_passive サーバソケットの場合、true を指定ください。 + */ + bool (*get_addrinfo)( + struct KcSocket_ *sock, + struct addrinfo **result, + const char *addr, + const char *service, + bool is_passive); + + /** + * 指定されたアドレス、サービスにバインドします。 + * 指定されたアドレスとサービスにより、複数候補がある場合は、 + * 最初に見つかったアドレスとポートにバインドします。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス (例: "80", "http", "ssh" など) + * @return true/false (bind 成功/失敗) + */ + bool (*bind)(struct KcSocket_ *sock, const char *addr, const char *service); + + /** + * ソケットを接続待ちソケットとしてマークをつけます。 + * 保留中の接続のキュー最大長を指定します。 + * + * @param sock 対象ソケット + * @param backlog バックログ + * @return true/false (成功/失敗) + */ + bool (*listen)(struct KcSocket_ *sock, int backlog); + + /** + * ソケットへの接続を受け付けます。 + * + * @param sock 対象ソケット + * @return 受け付けたソケット + */ + struct KcSocket_ *(*accept)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービス接続します。 + * local_addr に NULL を指定した場合、自動的にローカルのアドレスが設定されます。 + * local_service に -1 を指定した場合、自動的にローカルのポート番号が割りつけられます。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス + * @param local_addr ローカルアドレス + * @param local_service ローカルサービス + * @return true/false (成功/失敗) + */ + bool (*connect)(struct KcSocket_ *sock, const char *addr, const char *service, const char *local_addr, const char *local_port); + + /** + * ソケットをクローズします。 + * + * @param sock 対象ソケット + * @return true/false (成功/失敗) + */ + bool (*close)(struct KcSocket_ *sock); + + /** + * メッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 送信されたデータサイズ + */ + ssize_t (*send)(struct KcSocket_ *sock, const char *buff, size_t size, int flags); + + /** + * メッセージを受信します。 + * 受信したメッセージは指定されたバッファに格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 読み取ったデータサイズ + */ + ssize_t (*recv)(struct KcSocket_ *sock, char *buff, size_t size, int flags); + + /** + * 指定されたアドレス、サービスにメッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param addr アドレス + * @param service サービス + * @return 送信メッセージサイズ、エラー発生時は -1 + */ + ssize_t (*sendto)( + struct KcSocket_ *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service); + + /** + * メッセージを受信します。 + * src_addr, src_service がいずれも NULL 出ない場合、 + * 送信元のアドレスとサービスが格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param src_addr 送信元アドレス格納用バッファ + * @param src_addrlen 送信元アドレス格納用バッファサイズ + * @param src_service 送信元サービス格納用バッファ + * @param src_servicelen 送信元サービス格納用バッファサイズ + * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + */ + ssize_t (*recvfrom)( + struct KcSocket_ *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen); + + /** + * TTL を設定します。 + * + * @param sock 対象ソケット + * @param val 設定する TTL + * @return true/false (成功/失敗) + */ + bool (*set_ttl)(struct KcSocket_ *sock, int val); + + /** + * マルチキャストグループに参加するために JOIN します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @param ifindex インタフェースインデックス + * @return true/false (成功/失敗) + */ + bool (*join)(struct KcSocket_ *sock, const char *addr, const char *ifname, unsigned int ifindex); + + /** + * マルチキャストグループから離脱します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @return true/false (成功/失敗) + */ + bool (*leave)(struct KcSocket_ *sock, const char *addr); + + /** + * Socket 管理情報 + */ + void *_info; + + } KcSocket; + + /** + * 指定されたタイプ、ファミリのSocket オブジェクトを構築します。 + * + * @param type タイプ(SOCK_STREAM/SOCK_DGRAM/SOCK_RAW) + * @param family ファミリ(AF_UNSPEC/AF_INET/AF_INET6) + */ + KcSocket *KcSocket_new(int type, int family); + + /** + * Socket を破棄します。 + */ + void KcSocket_delete(KcSocket *socket); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_threads.h b/modules/include/kc_threads.h new file mode 100644 index 0000000..764176f --- /dev/null +++ b/modules/include/kc_threads.h @@ -0,0 +1,98 @@ +/** + * @file kc_threads.h + * @brief スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_THREADS_H +#define KC_THREADS_H + +#include + +#if (KC_IS_WINDOWS) +#include +#else +#include +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcThread_ + { + + /** + * 指定されたスレッドが生きているか否かを返します。 + * + * @param thread 対象スレッド + * @return true/false (スレッドが生存している/していない) + */ + bool (*is_alive)(struct KcThread_ *thread); + + /** + * スレッドを開始します。 + * + * @param thread 対象スレッド + * @param args スレッドに渡す情報 + * @return true/false (スレッド起動成功/失敗) + */ + bool (*start)(struct KcThread_ *thread, void *args); + + /** + * スレッドが終了するのを待機します。 + * + * @param thread 対象スレッド + */ + bool (*join)(struct KcThread_ *thread); + + /** + * Thread 管理情報 + */ + void *_info; + + } KcThread; + + /** + * スレッドを生成します。 + * + * @param run スレッドにて実行する関数へのポインタ + */ + KcThread *KcThread_new(int (*run)(void *args)); + + /** + * Thread を破棄します。 + * + * @param thread 破棄するスレッド + */ + void KcThread_delete(KcThread *thread); + + /** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープさせるミリ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_msleep(long time, bool force); + + /** + * 指定された sec 秒 + nsec ナノ秒間現在のスレッドをスリープさせます。 + * + * @param sec スリープさせる秒数 + * @param nsec スリープさせるナノ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_sleep(long sec, long nsec, bool force); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_THREADS_H diff --git a/modules/include/kc_threads_win.h b/modules/include/kc_threads_win.h new file mode 100644 index 0000000..b40ab10 --- /dev/null +++ b/modules/include/kc_threads_win.h @@ -0,0 +1,98 @@ +/** + * @file kc_windows_thread.h + * @brief Windows 用スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_WINDOWS_THREAD_H +#define KC_WINDOWS_THREAD_H + +#include +#if (KC_IS_WINDOWS) +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#define thread_local _Thread_local + + //////////////////////////////////////////////////////////////////////// + // + // Thread + // + + enum + { + thrd_success = 0, //!< 成功 + thrd_bussy = 1, //!< busy + thrd_error = 2, //!< エラー発生 + thrd_nomem = 3, //!< メモリ不足 + thrd_timedout = 4 //!< タイムアウト発生 + }; + + typedef struct + { + HANDLE handle; + DWORD thread_id; + } thrd_t; + + typedef int (*thrd_start_t)(void *); + int thrd_create(thrd_t *thr, thrd_start_t func, void *arg); + int thrd_join(thrd_t thr, int *res); + int thrd_detach(thrd_t thr); + thrd_t thrd_current(void); + int thrd_equal(thrd_t lhs, thrd_t rhs); + int thrd_sleep(const struct timespec *duration, struct timespec *remaining); + + //////////////////////////////////////////////////////////////////////// + // + // mutex + // + + enum + { + mtx_plain = 0, //!< 通常 + mtx_recursive = 1, //!< 再帰 + mtx_timed = 2 //!< timed + }; + + typedef struct + { + CRITICAL_SECTION cs; + int type; + } mtx_t; + + int mtx_init(mtx_t *mtx, int type); + void mtx_destroy(mtx_t *mtx); + int mtx_lock(mtx_t *mtx); + int mtx_unlock(mtx_t *mtx); + + //////////////////////////////////////////////////////////////////////// + // + // cond + // + + typedef struct + { + CONDITION_VARIABLE cond; + } cnd_t; + + int cnd_init(cnd_t *cond); + int cnd_signal(cnd_t *cond); + int cnd_broadcast(cnd_t *cond); + int cnd_wait(cnd_t *cond, mtx_t *mtx); + int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts); + void cnd_destroy(cnd_t *cond); + +#endif // (KC_IS_WINDOWS) +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_windows.h b/modules/include/kc_windows.h index ee70898..6e1c659 100644 --- a/modules/include/kc_windows.h +++ b/modules/include/kc_windows.h @@ -37,9 +37,9 @@ #endif // よく利用されるヘッダファイルをインクルードする -#include #include #include +#include #ifdef _MSC_VER #pragma comment(lib, "ws2_32.lib") #endif diff --git a/modules/src/kc_assert.c b/modules/src/kc_assert.c index 371fc8f..7cd4ecc 100644 --- a/modules/src/kc_assert.c +++ b/modules/src/kc_assert.c @@ -6,9 +6,9 @@ #include #include #include -#include #include #include +#include // プロトタイプ宣言 static void assert_default_handler(const char *msg); diff --git a/modules/src/kc_env.c b/modules/src/kc_env.c index 0675c73..95f185c 100644 --- a/modules/src/kc_env.c +++ b/modules/src/kc_env.c @@ -50,7 +50,7 @@ char *result = NULL; #if (KC_IS_WINDOWS) static char buff[KC_ENV_BUFFER_MAX]; - DWORD ret = GetEnvironmentVarable(key_name, (LPSTR)buff, sizeof(buff)); + DWORD ret = GetEnvironmentVariable(key_name, (LPSTR)buff, sizeof(buff)); if (ret != 0) { result = buff; diff --git a/modules/src/kc_list_array.c b/modules/src/kc_list_array.c index 246ed14..f29fc08 100644 --- a/modules/src/kc_list_array.c +++ b/modules/src/kc_list_array.c @@ -3,10 +3,14 @@ * @brief 配列リストモジュール * @copyright 2003 - 2023 Nomura Kei */ +#include #if defined(__GNUC__) #define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) #define qsort_s qsort_r #endif +#endif // defined(__GNUC__) + #include #include #include @@ -17,6 +21,7 @@ #include #include #include + /** * KcArrayList 管理情報 */ @@ -306,7 +311,11 @@ * @param context コンテキスト(KcListSortInfo) * @return 比較結果 */ +#if (KC_IS_WINDOWS) +static int KcArrayList_comparator(void *context, const void *x, const void *y) +#else static int KcArrayList_comparator(const void *x, const void *y, void *context) +#endif { KcListSortInfo *sort_info = (KcListSortInfo *)context; int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); diff --git a/config.mk b/config.mk index a0ac27c..f7c6e47 100644 --- a/config.mk +++ b/config.mk @@ -25,14 +25,17 @@ # for Windows CROSS_COMPILE ?=i686-w64-mingw32- LIBS ?= -lws2_32 +DEBUG_OPTIONS ?= else # For Linux ifeq ($(strip $(ARCH)),aarch64) CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector else CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector endif endif diff --git a/mk/base-cmd.mk b/mk/base-cmd.mk index f429bcf..e74c114 100644 --- a/mk/base-cmd.mk +++ b/mk/base-cmd.mk @@ -33,8 +33,13 @@ CC = $(CROSS_COMPILE)gcc CXX = $(CROSS_COMPILE)g++ + #CC = $(CROSS_COMPILE)clang #CXX = $(CROSS_COMPILE)clang++ + +#CC = $(CROSS_COMPILE)clang-16 +#CXX = $(CROSS_COMPILE)clang++16 + RANLIB = $(CROSS_COMPILE)ranlib ADDR2LINE = $(CROSS_COMPILE)addr2line AR = $(CROSS_COMPILE)ar diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 2cc417c..eebded1 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -69,7 +69,7 @@ # 値が2の場合、さらなるチェックを追加するが、規格準拠のプログラムが失敗する可能性がある。 # いくつかのチェックは、コンパイル時に実行され、コンパイラの警告として表示される。 # ------------------------------------------------------------------------------ -DEBUG_OPTIONS += -fstack-protector +# DEBUG_OPTIONS += -fstack-protector DEBUG_OPTIONS += --coverage DEBUG_OPTIONS += -g3 -ggdb DEBUG_OPTIONS += -O0 diff --git a/mk/link-a-conf.mk b/mk/link-a-conf.mk index 0e872f3..631e6d5 100644 --- a/mk/link-a-conf.mk +++ b/mk/link-a-conf.mk @@ -10,8 +10,11 @@ HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) +HEADER_FILES_WIN = $(wildcard include/win/*.h) $(wildcard include/win/*.hpp) +TOP_HEADER_FILES_WIN = $(addprefix $(TOPDIR)/include/win/,$(notdir $(HEADER_FILES_WIN))) + CLEAN_FILES += $(TOP_TARGET) -CLEAN_FILES += $(TOP_HEADER_FILES) +CLEAN_FILES += $(TOP_HEADER_FILES) $(TOP_HEADER_FILES_WIN) endif endif diff --git a/mk/link-a-rule.mk b/mk/link-a-rule.mk index d94a54a..657d858 100644 --- a/mk/link-a-rule.mk +++ b/mk/link-a-rule.mk @@ -10,6 +10,7 @@ $(TOP_TARGET): $(TARGET) ifneq ($(strip $(HEADER_FILES)),) $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ + $(CP) -f $(HEADER_FILES_WIN) $(TOPDIR)/include/win/ endif $(CP) -f $(TARGET) $(TOPDIR)/lib/ diff --git a/modules/include/kc_lock_guard.h b/modules/include/kc_lock_guard.h index 8bb70bd..ad3915f 100644 --- a/modules/include/kc_lock_guard.h +++ b/modules/include/kc_lock_guard.h @@ -7,18 +7,16 @@ #define KC_LOCK_GUARD_H #include -#include - +#include /** * LockGuard 管理構造体 */ typedef struct { - mtx_t* mutex; + mtx_t *mutex; } KcLockGuard; - /** * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 * 使用例) @@ -34,8 +32,7 @@ * } */ #define kc_lock_guard(lock) \ - for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) - + for (KcLockGuard _guard = kc_lock_guard_init(lock, __FILE__, __func__, __LINE__); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 @@ -44,15 +41,13 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex); - +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line); /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard); +void kc_lock_guard_release(KcLockGuard *guard); - -#endif // KC_LOCK_GUARD_H +#endif // KC_LOCK_GUARD_H diff --git a/modules/include/kc_memory.h b/modules/include/kc_memory.h index ffce4ed..c6ba9e9 100644 --- a/modules/include/kc_memory.h +++ b/modules/include/kc_memory.h @@ -22,12 +22,13 @@ #ifndef KC_MEMORY_H #define KC_MEMORY_H -#include +#include #include #include #include #include +#include #ifdef __cplusplus extern "C" @@ -273,8 +274,6 @@ #define calloc(nmemb, size) kc_memory_manager->calloc(nmemb, size, __FILE__, __func__, __LINE__) #define realloc(ptr, size) kc_memory_manager->realloc(ptr, size, __FILE__, __func__, __LINE__) #define free(ptr) kc_memory_manager->free(ptr) -#else -#include #endif #ifdef __cplusplus diff --git a/modules/include/kc_socket.h b/modules/include/kc_socket.h new file mode 100644 index 0000000..7d952c7 --- /dev/null +++ b/modules/include/kc_socket.h @@ -0,0 +1,291 @@ +/** + * @file kc_socket.h + * @brief ソケットモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_SOCKET_H +#define KC_SOCKET_H + +#include + +#if (KC_IS_WINDOWS) +typedef SOCKET socket_t; +#define SHUT_RD SD_RECEIVE +#define SHUT_WR SD_SEND +#define SHUT_RDWR SD_BOTH +#define sockclose(sockfd) closesocket(sockfd) +#else +#include +#include +#include +#include +#include +#include +#include +typedef int socket_t; +#define INVALID_SOCKET (-1) +#define SOCKET_ERROR (-1) +#define sockclose(sockfd) close(sockfd) +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcSocket_ + { + + /** + * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr 接続先アドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_remote_addr)(struct KcSocket_ *sock, char *addr, size_t addrlen); + + /** + * ソケットの接続先ポート番号を返します。 + * 接続先ポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_remote_port)(struct KcSocket_ *sock); + + /** + * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr ローカルアドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_local_addr)(struct KcSocket_ *sock, char *addr, size_t addlen); + + /** + * ソケットのローカルポート番号を返します。 + * ローカルポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_local_port)(struct KcSocket_ *sock); + + /** + * ソケットディスクリプタを返します。 + * 通常は、ソケットディスクリプタを直接操作しないでください。 + * + * @param sock 対象ソケット + * @return ソケットディスクリプタ + */ + socket_t (*get_socket)(struct KcSocket_ *sock); + + /** + * ソケットタイプを返します。 + * + * @param sock 対象ソケット + * @return ソケットタイプ + */ + int (*get_type)(struct KcSocket_ *sock); + + /** + * ソケットファミリを返します。 + * 実際にバインド、あるいは接続されているソケットファミリを返します。 + * + * @param sock 対象ソケット + * @return ソケットファミリ + */ + int (*get_family)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービスのアドレス情報を取得します。 + * + * @param sock 対象ソケット + * @param result アドレス情報格納用ポインタ + * @param addr アドレス + * @param service サービス + * @param is_passive サーバソケットの場合、true を指定ください。 + */ + bool (*get_addrinfo)( + struct KcSocket_ *sock, + struct addrinfo **result, + const char *addr, + const char *service, + bool is_passive); + + /** + * 指定されたアドレス、サービスにバインドします。 + * 指定されたアドレスとサービスにより、複数候補がある場合は、 + * 最初に見つかったアドレスとポートにバインドします。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス (例: "80", "http", "ssh" など) + * @return true/false (bind 成功/失敗) + */ + bool (*bind)(struct KcSocket_ *sock, const char *addr, const char *service); + + /** + * ソケットを接続待ちソケットとしてマークをつけます。 + * 保留中の接続のキュー最大長を指定します。 + * + * @param sock 対象ソケット + * @param backlog バックログ + * @return true/false (成功/失敗) + */ + bool (*listen)(struct KcSocket_ *sock, int backlog); + + /** + * ソケットへの接続を受け付けます。 + * + * @param sock 対象ソケット + * @return 受け付けたソケット + */ + struct KcSocket_ *(*accept)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービス接続します。 + * local_addr に NULL を指定した場合、自動的にローカルのアドレスが設定されます。 + * local_service に -1 を指定した場合、自動的にローカルのポート番号が割りつけられます。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス + * @param local_addr ローカルアドレス + * @param local_service ローカルサービス + * @return true/false (成功/失敗) + */ + bool (*connect)(struct KcSocket_ *sock, const char *addr, const char *service, const char *local_addr, const char *local_port); + + /** + * ソケットをクローズします。 + * + * @param sock 対象ソケット + * @return true/false (成功/失敗) + */ + bool (*close)(struct KcSocket_ *sock); + + /** + * メッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 送信されたデータサイズ + */ + ssize_t (*send)(struct KcSocket_ *sock, const char *buff, size_t size, int flags); + + /** + * メッセージを受信します。 + * 受信したメッセージは指定されたバッファに格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 読み取ったデータサイズ + */ + ssize_t (*recv)(struct KcSocket_ *sock, char *buff, size_t size, int flags); + + /** + * 指定されたアドレス、サービスにメッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param addr アドレス + * @param service サービス + * @return 送信メッセージサイズ、エラー発生時は -1 + */ + ssize_t (*sendto)( + struct KcSocket_ *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service); + + /** + * メッセージを受信します。 + * src_addr, src_service がいずれも NULL 出ない場合、 + * 送信元のアドレスとサービスが格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param src_addr 送信元アドレス格納用バッファ + * @param src_addrlen 送信元アドレス格納用バッファサイズ + * @param src_service 送信元サービス格納用バッファ + * @param src_servicelen 送信元サービス格納用バッファサイズ + * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + */ + ssize_t (*recvfrom)( + struct KcSocket_ *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen); + + /** + * TTL を設定します。 + * + * @param sock 対象ソケット + * @param val 設定する TTL + * @return true/false (成功/失敗) + */ + bool (*set_ttl)(struct KcSocket_ *sock, int val); + + /** + * マルチキャストグループに参加するために JOIN します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @param ifindex インタフェースインデックス + * @return true/false (成功/失敗) + */ + bool (*join)(struct KcSocket_ *sock, const char *addr, const char *ifname, unsigned int ifindex); + + /** + * マルチキャストグループから離脱します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @return true/false (成功/失敗) + */ + bool (*leave)(struct KcSocket_ *sock, const char *addr); + + /** + * Socket 管理情報 + */ + void *_info; + + } KcSocket; + + /** + * 指定されたタイプ、ファミリのSocket オブジェクトを構築します。 + * + * @param type タイプ(SOCK_STREAM/SOCK_DGRAM/SOCK_RAW) + * @param family ファミリ(AF_UNSPEC/AF_INET/AF_INET6) + */ + KcSocket *KcSocket_new(int type, int family); + + /** + * Socket を破棄します。 + */ + void KcSocket_delete(KcSocket *socket); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_threads.h b/modules/include/kc_threads.h new file mode 100644 index 0000000..764176f --- /dev/null +++ b/modules/include/kc_threads.h @@ -0,0 +1,98 @@ +/** + * @file kc_threads.h + * @brief スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_THREADS_H +#define KC_THREADS_H + +#include + +#if (KC_IS_WINDOWS) +#include +#else +#include +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcThread_ + { + + /** + * 指定されたスレッドが生きているか否かを返します。 + * + * @param thread 対象スレッド + * @return true/false (スレッドが生存している/していない) + */ + bool (*is_alive)(struct KcThread_ *thread); + + /** + * スレッドを開始します。 + * + * @param thread 対象スレッド + * @param args スレッドに渡す情報 + * @return true/false (スレッド起動成功/失敗) + */ + bool (*start)(struct KcThread_ *thread, void *args); + + /** + * スレッドが終了するのを待機します。 + * + * @param thread 対象スレッド + */ + bool (*join)(struct KcThread_ *thread); + + /** + * Thread 管理情報 + */ + void *_info; + + } KcThread; + + /** + * スレッドを生成します。 + * + * @param run スレッドにて実行する関数へのポインタ + */ + KcThread *KcThread_new(int (*run)(void *args)); + + /** + * Thread を破棄します。 + * + * @param thread 破棄するスレッド + */ + void KcThread_delete(KcThread *thread); + + /** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープさせるミリ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_msleep(long time, bool force); + + /** + * 指定された sec 秒 + nsec ナノ秒間現在のスレッドをスリープさせます。 + * + * @param sec スリープさせる秒数 + * @param nsec スリープさせるナノ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_sleep(long sec, long nsec, bool force); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_THREADS_H diff --git a/modules/include/kc_threads_win.h b/modules/include/kc_threads_win.h new file mode 100644 index 0000000..b40ab10 --- /dev/null +++ b/modules/include/kc_threads_win.h @@ -0,0 +1,98 @@ +/** + * @file kc_windows_thread.h + * @brief Windows 用スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_WINDOWS_THREAD_H +#define KC_WINDOWS_THREAD_H + +#include +#if (KC_IS_WINDOWS) +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#define thread_local _Thread_local + + //////////////////////////////////////////////////////////////////////// + // + // Thread + // + + enum + { + thrd_success = 0, //!< 成功 + thrd_bussy = 1, //!< busy + thrd_error = 2, //!< エラー発生 + thrd_nomem = 3, //!< メモリ不足 + thrd_timedout = 4 //!< タイムアウト発生 + }; + + typedef struct + { + HANDLE handle; + DWORD thread_id; + } thrd_t; + + typedef int (*thrd_start_t)(void *); + int thrd_create(thrd_t *thr, thrd_start_t func, void *arg); + int thrd_join(thrd_t thr, int *res); + int thrd_detach(thrd_t thr); + thrd_t thrd_current(void); + int thrd_equal(thrd_t lhs, thrd_t rhs); + int thrd_sleep(const struct timespec *duration, struct timespec *remaining); + + //////////////////////////////////////////////////////////////////////// + // + // mutex + // + + enum + { + mtx_plain = 0, //!< 通常 + mtx_recursive = 1, //!< 再帰 + mtx_timed = 2 //!< timed + }; + + typedef struct + { + CRITICAL_SECTION cs; + int type; + } mtx_t; + + int mtx_init(mtx_t *mtx, int type); + void mtx_destroy(mtx_t *mtx); + int mtx_lock(mtx_t *mtx); + int mtx_unlock(mtx_t *mtx); + + //////////////////////////////////////////////////////////////////////// + // + // cond + // + + typedef struct + { + CONDITION_VARIABLE cond; + } cnd_t; + + int cnd_init(cnd_t *cond); + int cnd_signal(cnd_t *cond); + int cnd_broadcast(cnd_t *cond); + int cnd_wait(cnd_t *cond, mtx_t *mtx); + int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts); + void cnd_destroy(cnd_t *cond); + +#endif // (KC_IS_WINDOWS) +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_windows.h b/modules/include/kc_windows.h index ee70898..6e1c659 100644 --- a/modules/include/kc_windows.h +++ b/modules/include/kc_windows.h @@ -37,9 +37,9 @@ #endif // よく利用されるヘッダファイルをインクルードする -#include #include #include +#include #ifdef _MSC_VER #pragma comment(lib, "ws2_32.lib") #endif diff --git a/modules/src/kc_assert.c b/modules/src/kc_assert.c index 371fc8f..7cd4ecc 100644 --- a/modules/src/kc_assert.c +++ b/modules/src/kc_assert.c @@ -6,9 +6,9 @@ #include #include #include -#include #include #include +#include // プロトタイプ宣言 static void assert_default_handler(const char *msg); diff --git a/modules/src/kc_env.c b/modules/src/kc_env.c index 0675c73..95f185c 100644 --- a/modules/src/kc_env.c +++ b/modules/src/kc_env.c @@ -50,7 +50,7 @@ char *result = NULL; #if (KC_IS_WINDOWS) static char buff[KC_ENV_BUFFER_MAX]; - DWORD ret = GetEnvironmentVarable(key_name, (LPSTR)buff, sizeof(buff)); + DWORD ret = GetEnvironmentVariable(key_name, (LPSTR)buff, sizeof(buff)); if (ret != 0) { result = buff; diff --git a/modules/src/kc_list_array.c b/modules/src/kc_list_array.c index 246ed14..f29fc08 100644 --- a/modules/src/kc_list_array.c +++ b/modules/src/kc_list_array.c @@ -3,10 +3,14 @@ * @brief 配列リストモジュール * @copyright 2003 - 2023 Nomura Kei */ +#include #if defined(__GNUC__) #define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) #define qsort_s qsort_r #endif +#endif // defined(__GNUC__) + #include #include #include @@ -17,6 +21,7 @@ #include #include #include + /** * KcArrayList 管理情報 */ @@ -306,7 +311,11 @@ * @param context コンテキスト(KcListSortInfo) * @return 比較結果 */ +#if (KC_IS_WINDOWS) +static int KcArrayList_comparator(void *context, const void *x, const void *y) +#else static int KcArrayList_comparator(const void *x, const void *y, void *context) +#endif { KcListSortInfo *sort_info = (KcListSortInfo *)context; int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); diff --git a/modules/src/kc_list_linked.c b/modules/src/kc_list_linked.c index 30575b9..47edbe4 100644 --- a/modules/src/kc_list_linked.c +++ b/modules/src/kc_list_linked.c @@ -3,10 +3,14 @@ * @brief Linked リストモジュール * @copyright 2003 - 2023 Nomura Kei */ +#include #if defined(__GNUC__) #define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) #define qsort_s qsort_r #endif +#endif // defined(__GNUC__) + #include #include #include @@ -18,6 +22,13 @@ #include #include +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) +#define qsort_s qsort_r +#endif // KC_IS_WINDOWS +#endif // defined(__GNUC__) + /** * KcLinkedList Entry 情報 */ @@ -291,7 +302,11 @@ * @param context コンテキスト(KcListSortInfo) * @return 比較結果 */ +#if (KC_IS_WINDOWS) +static int KcLinkedList_comparator(void *context, const void *x, const void *y) +#else static int KcLinkedList_comparator(const void *x, const void *y, void *context) +#endif // KC_IS_WINDOWS { KcListSortInfo *sort_info = (KcListSortInfo *)context; const KcLinkedListEntry **entry_x = (const KcLinkedListEntry **)x; diff --git a/config.mk b/config.mk index a0ac27c..f7c6e47 100644 --- a/config.mk +++ b/config.mk @@ -25,14 +25,17 @@ # for Windows CROSS_COMPILE ?=i686-w64-mingw32- LIBS ?= -lws2_32 +DEBUG_OPTIONS ?= else # For Linux ifeq ($(strip $(ARCH)),aarch64) CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector else CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector endif endif diff --git a/mk/base-cmd.mk b/mk/base-cmd.mk index f429bcf..e74c114 100644 --- a/mk/base-cmd.mk +++ b/mk/base-cmd.mk @@ -33,8 +33,13 @@ CC = $(CROSS_COMPILE)gcc CXX = $(CROSS_COMPILE)g++ + #CC = $(CROSS_COMPILE)clang #CXX = $(CROSS_COMPILE)clang++ + +#CC = $(CROSS_COMPILE)clang-16 +#CXX = $(CROSS_COMPILE)clang++16 + RANLIB = $(CROSS_COMPILE)ranlib ADDR2LINE = $(CROSS_COMPILE)addr2line AR = $(CROSS_COMPILE)ar diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 2cc417c..eebded1 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -69,7 +69,7 @@ # 値が2の場合、さらなるチェックを追加するが、規格準拠のプログラムが失敗する可能性がある。 # いくつかのチェックは、コンパイル時に実行され、コンパイラの警告として表示される。 # ------------------------------------------------------------------------------ -DEBUG_OPTIONS += -fstack-protector +# DEBUG_OPTIONS += -fstack-protector DEBUG_OPTIONS += --coverage DEBUG_OPTIONS += -g3 -ggdb DEBUG_OPTIONS += -O0 diff --git a/mk/link-a-conf.mk b/mk/link-a-conf.mk index 0e872f3..631e6d5 100644 --- a/mk/link-a-conf.mk +++ b/mk/link-a-conf.mk @@ -10,8 +10,11 @@ HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) +HEADER_FILES_WIN = $(wildcard include/win/*.h) $(wildcard include/win/*.hpp) +TOP_HEADER_FILES_WIN = $(addprefix $(TOPDIR)/include/win/,$(notdir $(HEADER_FILES_WIN))) + CLEAN_FILES += $(TOP_TARGET) -CLEAN_FILES += $(TOP_HEADER_FILES) +CLEAN_FILES += $(TOP_HEADER_FILES) $(TOP_HEADER_FILES_WIN) endif endif diff --git a/mk/link-a-rule.mk b/mk/link-a-rule.mk index d94a54a..657d858 100644 --- a/mk/link-a-rule.mk +++ b/mk/link-a-rule.mk @@ -10,6 +10,7 @@ $(TOP_TARGET): $(TARGET) ifneq ($(strip $(HEADER_FILES)),) $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ + $(CP) -f $(HEADER_FILES_WIN) $(TOPDIR)/include/win/ endif $(CP) -f $(TARGET) $(TOPDIR)/lib/ diff --git a/modules/include/kc_lock_guard.h b/modules/include/kc_lock_guard.h index 8bb70bd..ad3915f 100644 --- a/modules/include/kc_lock_guard.h +++ b/modules/include/kc_lock_guard.h @@ -7,18 +7,16 @@ #define KC_LOCK_GUARD_H #include -#include - +#include /** * LockGuard 管理構造体 */ typedef struct { - mtx_t* mutex; + mtx_t *mutex; } KcLockGuard; - /** * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 * 使用例) @@ -34,8 +32,7 @@ * } */ #define kc_lock_guard(lock) \ - for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) - + for (KcLockGuard _guard = kc_lock_guard_init(lock, __FILE__, __func__, __LINE__); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 @@ -44,15 +41,13 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex); - +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line); /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard); +void kc_lock_guard_release(KcLockGuard *guard); - -#endif // KC_LOCK_GUARD_H +#endif // KC_LOCK_GUARD_H diff --git a/modules/include/kc_memory.h b/modules/include/kc_memory.h index ffce4ed..c6ba9e9 100644 --- a/modules/include/kc_memory.h +++ b/modules/include/kc_memory.h @@ -22,12 +22,13 @@ #ifndef KC_MEMORY_H #define KC_MEMORY_H -#include +#include #include #include #include #include +#include #ifdef __cplusplus extern "C" @@ -273,8 +274,6 @@ #define calloc(nmemb, size) kc_memory_manager->calloc(nmemb, size, __FILE__, __func__, __LINE__) #define realloc(ptr, size) kc_memory_manager->realloc(ptr, size, __FILE__, __func__, __LINE__) #define free(ptr) kc_memory_manager->free(ptr) -#else -#include #endif #ifdef __cplusplus diff --git a/modules/include/kc_socket.h b/modules/include/kc_socket.h new file mode 100644 index 0000000..7d952c7 --- /dev/null +++ b/modules/include/kc_socket.h @@ -0,0 +1,291 @@ +/** + * @file kc_socket.h + * @brief ソケットモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_SOCKET_H +#define KC_SOCKET_H + +#include + +#if (KC_IS_WINDOWS) +typedef SOCKET socket_t; +#define SHUT_RD SD_RECEIVE +#define SHUT_WR SD_SEND +#define SHUT_RDWR SD_BOTH +#define sockclose(sockfd) closesocket(sockfd) +#else +#include +#include +#include +#include +#include +#include +#include +typedef int socket_t; +#define INVALID_SOCKET (-1) +#define SOCKET_ERROR (-1) +#define sockclose(sockfd) close(sockfd) +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcSocket_ + { + + /** + * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr 接続先アドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_remote_addr)(struct KcSocket_ *sock, char *addr, size_t addrlen); + + /** + * ソケットの接続先ポート番号を返します。 + * 接続先ポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_remote_port)(struct KcSocket_ *sock); + + /** + * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr ローカルアドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_local_addr)(struct KcSocket_ *sock, char *addr, size_t addlen); + + /** + * ソケットのローカルポート番号を返します。 + * ローカルポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_local_port)(struct KcSocket_ *sock); + + /** + * ソケットディスクリプタを返します。 + * 通常は、ソケットディスクリプタを直接操作しないでください。 + * + * @param sock 対象ソケット + * @return ソケットディスクリプタ + */ + socket_t (*get_socket)(struct KcSocket_ *sock); + + /** + * ソケットタイプを返します。 + * + * @param sock 対象ソケット + * @return ソケットタイプ + */ + int (*get_type)(struct KcSocket_ *sock); + + /** + * ソケットファミリを返します。 + * 実際にバインド、あるいは接続されているソケットファミリを返します。 + * + * @param sock 対象ソケット + * @return ソケットファミリ + */ + int (*get_family)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービスのアドレス情報を取得します。 + * + * @param sock 対象ソケット + * @param result アドレス情報格納用ポインタ + * @param addr アドレス + * @param service サービス + * @param is_passive サーバソケットの場合、true を指定ください。 + */ + bool (*get_addrinfo)( + struct KcSocket_ *sock, + struct addrinfo **result, + const char *addr, + const char *service, + bool is_passive); + + /** + * 指定されたアドレス、サービスにバインドします。 + * 指定されたアドレスとサービスにより、複数候補がある場合は、 + * 最初に見つかったアドレスとポートにバインドします。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス (例: "80", "http", "ssh" など) + * @return true/false (bind 成功/失敗) + */ + bool (*bind)(struct KcSocket_ *sock, const char *addr, const char *service); + + /** + * ソケットを接続待ちソケットとしてマークをつけます。 + * 保留中の接続のキュー最大長を指定します。 + * + * @param sock 対象ソケット + * @param backlog バックログ + * @return true/false (成功/失敗) + */ + bool (*listen)(struct KcSocket_ *sock, int backlog); + + /** + * ソケットへの接続を受け付けます。 + * + * @param sock 対象ソケット + * @return 受け付けたソケット + */ + struct KcSocket_ *(*accept)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービス接続します。 + * local_addr に NULL を指定した場合、自動的にローカルのアドレスが設定されます。 + * local_service に -1 を指定した場合、自動的にローカルのポート番号が割りつけられます。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス + * @param local_addr ローカルアドレス + * @param local_service ローカルサービス + * @return true/false (成功/失敗) + */ + bool (*connect)(struct KcSocket_ *sock, const char *addr, const char *service, const char *local_addr, const char *local_port); + + /** + * ソケットをクローズします。 + * + * @param sock 対象ソケット + * @return true/false (成功/失敗) + */ + bool (*close)(struct KcSocket_ *sock); + + /** + * メッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 送信されたデータサイズ + */ + ssize_t (*send)(struct KcSocket_ *sock, const char *buff, size_t size, int flags); + + /** + * メッセージを受信します。 + * 受信したメッセージは指定されたバッファに格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 読み取ったデータサイズ + */ + ssize_t (*recv)(struct KcSocket_ *sock, char *buff, size_t size, int flags); + + /** + * 指定されたアドレス、サービスにメッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param addr アドレス + * @param service サービス + * @return 送信メッセージサイズ、エラー発生時は -1 + */ + ssize_t (*sendto)( + struct KcSocket_ *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service); + + /** + * メッセージを受信します。 + * src_addr, src_service がいずれも NULL 出ない場合、 + * 送信元のアドレスとサービスが格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param src_addr 送信元アドレス格納用バッファ + * @param src_addrlen 送信元アドレス格納用バッファサイズ + * @param src_service 送信元サービス格納用バッファ + * @param src_servicelen 送信元サービス格納用バッファサイズ + * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + */ + ssize_t (*recvfrom)( + struct KcSocket_ *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen); + + /** + * TTL を設定します。 + * + * @param sock 対象ソケット + * @param val 設定する TTL + * @return true/false (成功/失敗) + */ + bool (*set_ttl)(struct KcSocket_ *sock, int val); + + /** + * マルチキャストグループに参加するために JOIN します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @param ifindex インタフェースインデックス + * @return true/false (成功/失敗) + */ + bool (*join)(struct KcSocket_ *sock, const char *addr, const char *ifname, unsigned int ifindex); + + /** + * マルチキャストグループから離脱します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @return true/false (成功/失敗) + */ + bool (*leave)(struct KcSocket_ *sock, const char *addr); + + /** + * Socket 管理情報 + */ + void *_info; + + } KcSocket; + + /** + * 指定されたタイプ、ファミリのSocket オブジェクトを構築します。 + * + * @param type タイプ(SOCK_STREAM/SOCK_DGRAM/SOCK_RAW) + * @param family ファミリ(AF_UNSPEC/AF_INET/AF_INET6) + */ + KcSocket *KcSocket_new(int type, int family); + + /** + * Socket を破棄します。 + */ + void KcSocket_delete(KcSocket *socket); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_threads.h b/modules/include/kc_threads.h new file mode 100644 index 0000000..764176f --- /dev/null +++ b/modules/include/kc_threads.h @@ -0,0 +1,98 @@ +/** + * @file kc_threads.h + * @brief スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_THREADS_H +#define KC_THREADS_H + +#include + +#if (KC_IS_WINDOWS) +#include +#else +#include +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcThread_ + { + + /** + * 指定されたスレッドが生きているか否かを返します。 + * + * @param thread 対象スレッド + * @return true/false (スレッドが生存している/していない) + */ + bool (*is_alive)(struct KcThread_ *thread); + + /** + * スレッドを開始します。 + * + * @param thread 対象スレッド + * @param args スレッドに渡す情報 + * @return true/false (スレッド起動成功/失敗) + */ + bool (*start)(struct KcThread_ *thread, void *args); + + /** + * スレッドが終了するのを待機します。 + * + * @param thread 対象スレッド + */ + bool (*join)(struct KcThread_ *thread); + + /** + * Thread 管理情報 + */ + void *_info; + + } KcThread; + + /** + * スレッドを生成します。 + * + * @param run スレッドにて実行する関数へのポインタ + */ + KcThread *KcThread_new(int (*run)(void *args)); + + /** + * Thread を破棄します。 + * + * @param thread 破棄するスレッド + */ + void KcThread_delete(KcThread *thread); + + /** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープさせるミリ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_msleep(long time, bool force); + + /** + * 指定された sec 秒 + nsec ナノ秒間現在のスレッドをスリープさせます。 + * + * @param sec スリープさせる秒数 + * @param nsec スリープさせるナノ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_sleep(long sec, long nsec, bool force); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_THREADS_H diff --git a/modules/include/kc_threads_win.h b/modules/include/kc_threads_win.h new file mode 100644 index 0000000..b40ab10 --- /dev/null +++ b/modules/include/kc_threads_win.h @@ -0,0 +1,98 @@ +/** + * @file kc_windows_thread.h + * @brief Windows 用スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_WINDOWS_THREAD_H +#define KC_WINDOWS_THREAD_H + +#include +#if (KC_IS_WINDOWS) +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#define thread_local _Thread_local + + //////////////////////////////////////////////////////////////////////// + // + // Thread + // + + enum + { + thrd_success = 0, //!< 成功 + thrd_bussy = 1, //!< busy + thrd_error = 2, //!< エラー発生 + thrd_nomem = 3, //!< メモリ不足 + thrd_timedout = 4 //!< タイムアウト発生 + }; + + typedef struct + { + HANDLE handle; + DWORD thread_id; + } thrd_t; + + typedef int (*thrd_start_t)(void *); + int thrd_create(thrd_t *thr, thrd_start_t func, void *arg); + int thrd_join(thrd_t thr, int *res); + int thrd_detach(thrd_t thr); + thrd_t thrd_current(void); + int thrd_equal(thrd_t lhs, thrd_t rhs); + int thrd_sleep(const struct timespec *duration, struct timespec *remaining); + + //////////////////////////////////////////////////////////////////////// + // + // mutex + // + + enum + { + mtx_plain = 0, //!< 通常 + mtx_recursive = 1, //!< 再帰 + mtx_timed = 2 //!< timed + }; + + typedef struct + { + CRITICAL_SECTION cs; + int type; + } mtx_t; + + int mtx_init(mtx_t *mtx, int type); + void mtx_destroy(mtx_t *mtx); + int mtx_lock(mtx_t *mtx); + int mtx_unlock(mtx_t *mtx); + + //////////////////////////////////////////////////////////////////////// + // + // cond + // + + typedef struct + { + CONDITION_VARIABLE cond; + } cnd_t; + + int cnd_init(cnd_t *cond); + int cnd_signal(cnd_t *cond); + int cnd_broadcast(cnd_t *cond); + int cnd_wait(cnd_t *cond, mtx_t *mtx); + int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts); + void cnd_destroy(cnd_t *cond); + +#endif // (KC_IS_WINDOWS) +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_windows.h b/modules/include/kc_windows.h index ee70898..6e1c659 100644 --- a/modules/include/kc_windows.h +++ b/modules/include/kc_windows.h @@ -37,9 +37,9 @@ #endif // よく利用されるヘッダファイルをインクルードする -#include #include #include +#include #ifdef _MSC_VER #pragma comment(lib, "ws2_32.lib") #endif diff --git a/modules/src/kc_assert.c b/modules/src/kc_assert.c index 371fc8f..7cd4ecc 100644 --- a/modules/src/kc_assert.c +++ b/modules/src/kc_assert.c @@ -6,9 +6,9 @@ #include #include #include -#include #include #include +#include // プロトタイプ宣言 static void assert_default_handler(const char *msg); diff --git a/modules/src/kc_env.c b/modules/src/kc_env.c index 0675c73..95f185c 100644 --- a/modules/src/kc_env.c +++ b/modules/src/kc_env.c @@ -50,7 +50,7 @@ char *result = NULL; #if (KC_IS_WINDOWS) static char buff[KC_ENV_BUFFER_MAX]; - DWORD ret = GetEnvironmentVarable(key_name, (LPSTR)buff, sizeof(buff)); + DWORD ret = GetEnvironmentVariable(key_name, (LPSTR)buff, sizeof(buff)); if (ret != 0) { result = buff; diff --git a/modules/src/kc_list_array.c b/modules/src/kc_list_array.c index 246ed14..f29fc08 100644 --- a/modules/src/kc_list_array.c +++ b/modules/src/kc_list_array.c @@ -3,10 +3,14 @@ * @brief 配列リストモジュール * @copyright 2003 - 2023 Nomura Kei */ +#include #if defined(__GNUC__) #define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) #define qsort_s qsort_r #endif +#endif // defined(__GNUC__) + #include #include #include @@ -17,6 +21,7 @@ #include #include #include + /** * KcArrayList 管理情報 */ @@ -306,7 +311,11 @@ * @param context コンテキスト(KcListSortInfo) * @return 比較結果 */ +#if (KC_IS_WINDOWS) +static int KcArrayList_comparator(void *context, const void *x, const void *y) +#else static int KcArrayList_comparator(const void *x, const void *y, void *context) +#endif { KcListSortInfo *sort_info = (KcListSortInfo *)context; int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); diff --git a/modules/src/kc_list_linked.c b/modules/src/kc_list_linked.c index 30575b9..47edbe4 100644 --- a/modules/src/kc_list_linked.c +++ b/modules/src/kc_list_linked.c @@ -3,10 +3,14 @@ * @brief Linked リストモジュール * @copyright 2003 - 2023 Nomura Kei */ +#include #if defined(__GNUC__) #define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) #define qsort_s qsort_r #endif +#endif // defined(__GNUC__) + #include #include #include @@ -18,6 +22,13 @@ #include #include +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) +#define qsort_s qsort_r +#endif // KC_IS_WINDOWS +#endif // defined(__GNUC__) + /** * KcLinkedList Entry 情報 */ @@ -291,7 +302,11 @@ * @param context コンテキスト(KcListSortInfo) * @return 比較結果 */ +#if (KC_IS_WINDOWS) +static int KcLinkedList_comparator(void *context, const void *x, const void *y) +#else static int KcLinkedList_comparator(const void *x, const void *y, void *context) +#endif // KC_IS_WINDOWS { KcListSortInfo *sort_info = (KcListSortInfo *)context; const KcLinkedListEntry **entry_x = (const KcLinkedListEntry **)x; diff --git a/modules/src/kc_lock_guard.c b/modules/src/kc_lock_guard.c index 6ef68e5..648a153 100644 --- a/modules/src/kc_lock_guard.c +++ b/modules/src/kc_lock_guard.c @@ -7,7 +7,6 @@ #include - /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 * ロックに成功するとロック管理用のオブジェクトを返します。 @@ -15,36 +14,37 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex) +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line) { - KcLockGuard guard = { .mutex = mutex }; + KcLockGuard guard = {.mutex = mutex}; if (mutex == NULL) { errno = EINVAL; perror("kc_lock_guard_init: (mutex = NULL)"); + fprintf(stderr, "%s:%s:%d kc_lock_guard_init : mutex = NULL\n", file, func, line); return guard; } + errno = 0; int ret = mtx_lock(mutex); if (ret != thrd_success) { - perror("kc_lock_guard : mtx_lock error"); + fprintf(stderr, "%s:%s:%d kc_lock_guard : mtx_lock error\n", file, func, line); guard.mutex = NULL; } return guard; } - /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard) +void kc_lock_guard_release(KcLockGuard *guard) { - if (guard->mutex != NULL) { - mtx_unlock(guard->mutex); - guard->mutex = NULL; + if (guard->mutex != NULL) + { + mtx_unlock(guard->mutex); + guard->mutex = NULL; } } - diff --git a/config.mk b/config.mk index a0ac27c..f7c6e47 100644 --- a/config.mk +++ b/config.mk @@ -25,14 +25,17 @@ # for Windows CROSS_COMPILE ?=i686-w64-mingw32- LIBS ?= -lws2_32 +DEBUG_OPTIONS ?= else # For Linux ifeq ($(strip $(ARCH)),aarch64) CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector else CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector endif endif diff --git a/mk/base-cmd.mk b/mk/base-cmd.mk index f429bcf..e74c114 100644 --- a/mk/base-cmd.mk +++ b/mk/base-cmd.mk @@ -33,8 +33,13 @@ CC = $(CROSS_COMPILE)gcc CXX = $(CROSS_COMPILE)g++ + #CC = $(CROSS_COMPILE)clang #CXX = $(CROSS_COMPILE)clang++ + +#CC = $(CROSS_COMPILE)clang-16 +#CXX = $(CROSS_COMPILE)clang++16 + RANLIB = $(CROSS_COMPILE)ranlib ADDR2LINE = $(CROSS_COMPILE)addr2line AR = $(CROSS_COMPILE)ar diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 2cc417c..eebded1 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -69,7 +69,7 @@ # 値が2の場合、さらなるチェックを追加するが、規格準拠のプログラムが失敗する可能性がある。 # いくつかのチェックは、コンパイル時に実行され、コンパイラの警告として表示される。 # ------------------------------------------------------------------------------ -DEBUG_OPTIONS += -fstack-protector +# DEBUG_OPTIONS += -fstack-protector DEBUG_OPTIONS += --coverage DEBUG_OPTIONS += -g3 -ggdb DEBUG_OPTIONS += -O0 diff --git a/mk/link-a-conf.mk b/mk/link-a-conf.mk index 0e872f3..631e6d5 100644 --- a/mk/link-a-conf.mk +++ b/mk/link-a-conf.mk @@ -10,8 +10,11 @@ HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) +HEADER_FILES_WIN = $(wildcard include/win/*.h) $(wildcard include/win/*.hpp) +TOP_HEADER_FILES_WIN = $(addprefix $(TOPDIR)/include/win/,$(notdir $(HEADER_FILES_WIN))) + CLEAN_FILES += $(TOP_TARGET) -CLEAN_FILES += $(TOP_HEADER_FILES) +CLEAN_FILES += $(TOP_HEADER_FILES) $(TOP_HEADER_FILES_WIN) endif endif diff --git a/mk/link-a-rule.mk b/mk/link-a-rule.mk index d94a54a..657d858 100644 --- a/mk/link-a-rule.mk +++ b/mk/link-a-rule.mk @@ -10,6 +10,7 @@ $(TOP_TARGET): $(TARGET) ifneq ($(strip $(HEADER_FILES)),) $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ + $(CP) -f $(HEADER_FILES_WIN) $(TOPDIR)/include/win/ endif $(CP) -f $(TARGET) $(TOPDIR)/lib/ diff --git a/modules/include/kc_lock_guard.h b/modules/include/kc_lock_guard.h index 8bb70bd..ad3915f 100644 --- a/modules/include/kc_lock_guard.h +++ b/modules/include/kc_lock_guard.h @@ -7,18 +7,16 @@ #define KC_LOCK_GUARD_H #include -#include - +#include /** * LockGuard 管理構造体 */ typedef struct { - mtx_t* mutex; + mtx_t *mutex; } KcLockGuard; - /** * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 * 使用例) @@ -34,8 +32,7 @@ * } */ #define kc_lock_guard(lock) \ - for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) - + for (KcLockGuard _guard = kc_lock_guard_init(lock, __FILE__, __func__, __LINE__); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 @@ -44,15 +41,13 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex); - +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line); /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard); +void kc_lock_guard_release(KcLockGuard *guard); - -#endif // KC_LOCK_GUARD_H +#endif // KC_LOCK_GUARD_H diff --git a/modules/include/kc_memory.h b/modules/include/kc_memory.h index ffce4ed..c6ba9e9 100644 --- a/modules/include/kc_memory.h +++ b/modules/include/kc_memory.h @@ -22,12 +22,13 @@ #ifndef KC_MEMORY_H #define KC_MEMORY_H -#include +#include #include #include #include #include +#include #ifdef __cplusplus extern "C" @@ -273,8 +274,6 @@ #define calloc(nmemb, size) kc_memory_manager->calloc(nmemb, size, __FILE__, __func__, __LINE__) #define realloc(ptr, size) kc_memory_manager->realloc(ptr, size, __FILE__, __func__, __LINE__) #define free(ptr) kc_memory_manager->free(ptr) -#else -#include #endif #ifdef __cplusplus diff --git a/modules/include/kc_socket.h b/modules/include/kc_socket.h new file mode 100644 index 0000000..7d952c7 --- /dev/null +++ b/modules/include/kc_socket.h @@ -0,0 +1,291 @@ +/** + * @file kc_socket.h + * @brief ソケットモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_SOCKET_H +#define KC_SOCKET_H + +#include + +#if (KC_IS_WINDOWS) +typedef SOCKET socket_t; +#define SHUT_RD SD_RECEIVE +#define SHUT_WR SD_SEND +#define SHUT_RDWR SD_BOTH +#define sockclose(sockfd) closesocket(sockfd) +#else +#include +#include +#include +#include +#include +#include +#include +typedef int socket_t; +#define INVALID_SOCKET (-1) +#define SOCKET_ERROR (-1) +#define sockclose(sockfd) close(sockfd) +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcSocket_ + { + + /** + * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr 接続先アドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_remote_addr)(struct KcSocket_ *sock, char *addr, size_t addrlen); + + /** + * ソケットの接続先ポート番号を返します。 + * 接続先ポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_remote_port)(struct KcSocket_ *sock); + + /** + * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr ローカルアドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_local_addr)(struct KcSocket_ *sock, char *addr, size_t addlen); + + /** + * ソケットのローカルポート番号を返します。 + * ローカルポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_local_port)(struct KcSocket_ *sock); + + /** + * ソケットディスクリプタを返します。 + * 通常は、ソケットディスクリプタを直接操作しないでください。 + * + * @param sock 対象ソケット + * @return ソケットディスクリプタ + */ + socket_t (*get_socket)(struct KcSocket_ *sock); + + /** + * ソケットタイプを返します。 + * + * @param sock 対象ソケット + * @return ソケットタイプ + */ + int (*get_type)(struct KcSocket_ *sock); + + /** + * ソケットファミリを返します。 + * 実際にバインド、あるいは接続されているソケットファミリを返します。 + * + * @param sock 対象ソケット + * @return ソケットファミリ + */ + int (*get_family)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービスのアドレス情報を取得します。 + * + * @param sock 対象ソケット + * @param result アドレス情報格納用ポインタ + * @param addr アドレス + * @param service サービス + * @param is_passive サーバソケットの場合、true を指定ください。 + */ + bool (*get_addrinfo)( + struct KcSocket_ *sock, + struct addrinfo **result, + const char *addr, + const char *service, + bool is_passive); + + /** + * 指定されたアドレス、サービスにバインドします。 + * 指定されたアドレスとサービスにより、複数候補がある場合は、 + * 最初に見つかったアドレスとポートにバインドします。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス (例: "80", "http", "ssh" など) + * @return true/false (bind 成功/失敗) + */ + bool (*bind)(struct KcSocket_ *sock, const char *addr, const char *service); + + /** + * ソケットを接続待ちソケットとしてマークをつけます。 + * 保留中の接続のキュー最大長を指定します。 + * + * @param sock 対象ソケット + * @param backlog バックログ + * @return true/false (成功/失敗) + */ + bool (*listen)(struct KcSocket_ *sock, int backlog); + + /** + * ソケットへの接続を受け付けます。 + * + * @param sock 対象ソケット + * @return 受け付けたソケット + */ + struct KcSocket_ *(*accept)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービス接続します。 + * local_addr に NULL を指定した場合、自動的にローカルのアドレスが設定されます。 + * local_service に -1 を指定した場合、自動的にローカルのポート番号が割りつけられます。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス + * @param local_addr ローカルアドレス + * @param local_service ローカルサービス + * @return true/false (成功/失敗) + */ + bool (*connect)(struct KcSocket_ *sock, const char *addr, const char *service, const char *local_addr, const char *local_port); + + /** + * ソケットをクローズします。 + * + * @param sock 対象ソケット + * @return true/false (成功/失敗) + */ + bool (*close)(struct KcSocket_ *sock); + + /** + * メッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 送信されたデータサイズ + */ + ssize_t (*send)(struct KcSocket_ *sock, const char *buff, size_t size, int flags); + + /** + * メッセージを受信します。 + * 受信したメッセージは指定されたバッファに格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 読み取ったデータサイズ + */ + ssize_t (*recv)(struct KcSocket_ *sock, char *buff, size_t size, int flags); + + /** + * 指定されたアドレス、サービスにメッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param addr アドレス + * @param service サービス + * @return 送信メッセージサイズ、エラー発生時は -1 + */ + ssize_t (*sendto)( + struct KcSocket_ *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service); + + /** + * メッセージを受信します。 + * src_addr, src_service がいずれも NULL 出ない場合、 + * 送信元のアドレスとサービスが格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param src_addr 送信元アドレス格納用バッファ + * @param src_addrlen 送信元アドレス格納用バッファサイズ + * @param src_service 送信元サービス格納用バッファ + * @param src_servicelen 送信元サービス格納用バッファサイズ + * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + */ + ssize_t (*recvfrom)( + struct KcSocket_ *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen); + + /** + * TTL を設定します。 + * + * @param sock 対象ソケット + * @param val 設定する TTL + * @return true/false (成功/失敗) + */ + bool (*set_ttl)(struct KcSocket_ *sock, int val); + + /** + * マルチキャストグループに参加するために JOIN します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @param ifindex インタフェースインデックス + * @return true/false (成功/失敗) + */ + bool (*join)(struct KcSocket_ *sock, const char *addr, const char *ifname, unsigned int ifindex); + + /** + * マルチキャストグループから離脱します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @return true/false (成功/失敗) + */ + bool (*leave)(struct KcSocket_ *sock, const char *addr); + + /** + * Socket 管理情報 + */ + void *_info; + + } KcSocket; + + /** + * 指定されたタイプ、ファミリのSocket オブジェクトを構築します。 + * + * @param type タイプ(SOCK_STREAM/SOCK_DGRAM/SOCK_RAW) + * @param family ファミリ(AF_UNSPEC/AF_INET/AF_INET6) + */ + KcSocket *KcSocket_new(int type, int family); + + /** + * Socket を破棄します。 + */ + void KcSocket_delete(KcSocket *socket); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_threads.h b/modules/include/kc_threads.h new file mode 100644 index 0000000..764176f --- /dev/null +++ b/modules/include/kc_threads.h @@ -0,0 +1,98 @@ +/** + * @file kc_threads.h + * @brief スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_THREADS_H +#define KC_THREADS_H + +#include + +#if (KC_IS_WINDOWS) +#include +#else +#include +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcThread_ + { + + /** + * 指定されたスレッドが生きているか否かを返します。 + * + * @param thread 対象スレッド + * @return true/false (スレッドが生存している/していない) + */ + bool (*is_alive)(struct KcThread_ *thread); + + /** + * スレッドを開始します。 + * + * @param thread 対象スレッド + * @param args スレッドに渡す情報 + * @return true/false (スレッド起動成功/失敗) + */ + bool (*start)(struct KcThread_ *thread, void *args); + + /** + * スレッドが終了するのを待機します。 + * + * @param thread 対象スレッド + */ + bool (*join)(struct KcThread_ *thread); + + /** + * Thread 管理情報 + */ + void *_info; + + } KcThread; + + /** + * スレッドを生成します。 + * + * @param run スレッドにて実行する関数へのポインタ + */ + KcThread *KcThread_new(int (*run)(void *args)); + + /** + * Thread を破棄します。 + * + * @param thread 破棄するスレッド + */ + void KcThread_delete(KcThread *thread); + + /** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープさせるミリ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_msleep(long time, bool force); + + /** + * 指定された sec 秒 + nsec ナノ秒間現在のスレッドをスリープさせます。 + * + * @param sec スリープさせる秒数 + * @param nsec スリープさせるナノ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_sleep(long sec, long nsec, bool force); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_THREADS_H diff --git a/modules/include/kc_threads_win.h b/modules/include/kc_threads_win.h new file mode 100644 index 0000000..b40ab10 --- /dev/null +++ b/modules/include/kc_threads_win.h @@ -0,0 +1,98 @@ +/** + * @file kc_windows_thread.h + * @brief Windows 用スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_WINDOWS_THREAD_H +#define KC_WINDOWS_THREAD_H + +#include +#if (KC_IS_WINDOWS) +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#define thread_local _Thread_local + + //////////////////////////////////////////////////////////////////////// + // + // Thread + // + + enum + { + thrd_success = 0, //!< 成功 + thrd_bussy = 1, //!< busy + thrd_error = 2, //!< エラー発生 + thrd_nomem = 3, //!< メモリ不足 + thrd_timedout = 4 //!< タイムアウト発生 + }; + + typedef struct + { + HANDLE handle; + DWORD thread_id; + } thrd_t; + + typedef int (*thrd_start_t)(void *); + int thrd_create(thrd_t *thr, thrd_start_t func, void *arg); + int thrd_join(thrd_t thr, int *res); + int thrd_detach(thrd_t thr); + thrd_t thrd_current(void); + int thrd_equal(thrd_t lhs, thrd_t rhs); + int thrd_sleep(const struct timespec *duration, struct timespec *remaining); + + //////////////////////////////////////////////////////////////////////// + // + // mutex + // + + enum + { + mtx_plain = 0, //!< 通常 + mtx_recursive = 1, //!< 再帰 + mtx_timed = 2 //!< timed + }; + + typedef struct + { + CRITICAL_SECTION cs; + int type; + } mtx_t; + + int mtx_init(mtx_t *mtx, int type); + void mtx_destroy(mtx_t *mtx); + int mtx_lock(mtx_t *mtx); + int mtx_unlock(mtx_t *mtx); + + //////////////////////////////////////////////////////////////////////// + // + // cond + // + + typedef struct + { + CONDITION_VARIABLE cond; + } cnd_t; + + int cnd_init(cnd_t *cond); + int cnd_signal(cnd_t *cond); + int cnd_broadcast(cnd_t *cond); + int cnd_wait(cnd_t *cond, mtx_t *mtx); + int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts); + void cnd_destroy(cnd_t *cond); + +#endif // (KC_IS_WINDOWS) +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_windows.h b/modules/include/kc_windows.h index ee70898..6e1c659 100644 --- a/modules/include/kc_windows.h +++ b/modules/include/kc_windows.h @@ -37,9 +37,9 @@ #endif // よく利用されるヘッダファイルをインクルードする -#include #include #include +#include #ifdef _MSC_VER #pragma comment(lib, "ws2_32.lib") #endif diff --git a/modules/src/kc_assert.c b/modules/src/kc_assert.c index 371fc8f..7cd4ecc 100644 --- a/modules/src/kc_assert.c +++ b/modules/src/kc_assert.c @@ -6,9 +6,9 @@ #include #include #include -#include #include #include +#include // プロトタイプ宣言 static void assert_default_handler(const char *msg); diff --git a/modules/src/kc_env.c b/modules/src/kc_env.c index 0675c73..95f185c 100644 --- a/modules/src/kc_env.c +++ b/modules/src/kc_env.c @@ -50,7 +50,7 @@ char *result = NULL; #if (KC_IS_WINDOWS) static char buff[KC_ENV_BUFFER_MAX]; - DWORD ret = GetEnvironmentVarable(key_name, (LPSTR)buff, sizeof(buff)); + DWORD ret = GetEnvironmentVariable(key_name, (LPSTR)buff, sizeof(buff)); if (ret != 0) { result = buff; diff --git a/modules/src/kc_list_array.c b/modules/src/kc_list_array.c index 246ed14..f29fc08 100644 --- a/modules/src/kc_list_array.c +++ b/modules/src/kc_list_array.c @@ -3,10 +3,14 @@ * @brief 配列リストモジュール * @copyright 2003 - 2023 Nomura Kei */ +#include #if defined(__GNUC__) #define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) #define qsort_s qsort_r #endif +#endif // defined(__GNUC__) + #include #include #include @@ -17,6 +21,7 @@ #include #include #include + /** * KcArrayList 管理情報 */ @@ -306,7 +311,11 @@ * @param context コンテキスト(KcListSortInfo) * @return 比較結果 */ +#if (KC_IS_WINDOWS) +static int KcArrayList_comparator(void *context, const void *x, const void *y) +#else static int KcArrayList_comparator(const void *x, const void *y, void *context) +#endif { KcListSortInfo *sort_info = (KcListSortInfo *)context; int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); diff --git a/modules/src/kc_list_linked.c b/modules/src/kc_list_linked.c index 30575b9..47edbe4 100644 --- a/modules/src/kc_list_linked.c +++ b/modules/src/kc_list_linked.c @@ -3,10 +3,14 @@ * @brief Linked リストモジュール * @copyright 2003 - 2023 Nomura Kei */ +#include #if defined(__GNUC__) #define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) #define qsort_s qsort_r #endif +#endif // defined(__GNUC__) + #include #include #include @@ -18,6 +22,13 @@ #include #include +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) +#define qsort_s qsort_r +#endif // KC_IS_WINDOWS +#endif // defined(__GNUC__) + /** * KcLinkedList Entry 情報 */ @@ -291,7 +302,11 @@ * @param context コンテキスト(KcListSortInfo) * @return 比較結果 */ +#if (KC_IS_WINDOWS) +static int KcLinkedList_comparator(void *context, const void *x, const void *y) +#else static int KcLinkedList_comparator(const void *x, const void *y, void *context) +#endif // KC_IS_WINDOWS { KcListSortInfo *sort_info = (KcListSortInfo *)context; const KcLinkedListEntry **entry_x = (const KcLinkedListEntry **)x; diff --git a/modules/src/kc_lock_guard.c b/modules/src/kc_lock_guard.c index 6ef68e5..648a153 100644 --- a/modules/src/kc_lock_guard.c +++ b/modules/src/kc_lock_guard.c @@ -7,7 +7,6 @@ #include - /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 * ロックに成功するとロック管理用のオブジェクトを返します。 @@ -15,36 +14,37 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex) +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line) { - KcLockGuard guard = { .mutex = mutex }; + KcLockGuard guard = {.mutex = mutex}; if (mutex == NULL) { errno = EINVAL; perror("kc_lock_guard_init: (mutex = NULL)"); + fprintf(stderr, "%s:%s:%d kc_lock_guard_init : mutex = NULL\n", file, func, line); return guard; } + errno = 0; int ret = mtx_lock(mutex); if (ret != thrd_success) { - perror("kc_lock_guard : mtx_lock error"); + fprintf(stderr, "%s:%s:%d kc_lock_guard : mtx_lock error\n", file, func, line); guard.mutex = NULL; } return guard; } - /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard) +void kc_lock_guard_release(KcLockGuard *guard) { - if (guard->mutex != NULL) { - mtx_unlock(guard->mutex); - guard->mutex = NULL; + if (guard->mutex != NULL) + { + mtx_unlock(guard->mutex); + guard->mutex = NULL; } } - diff --git a/modules/src/kc_memory.c b/modules/src/kc_memory.c index 5cc4186..6dbbc88 100644 --- a/modules/src/kc_memory.c +++ b/modules/src/kc_memory.c @@ -622,7 +622,11 @@ */ void *raw_aligned_alloc(size_t alignment, size_t size) { +#if (KC_IS_WINDOWS) + return _aligned_malloc(size, alignment); +#else return aligned_alloc(alignment, size); +#endif } /** diff --git a/config.mk b/config.mk index a0ac27c..f7c6e47 100644 --- a/config.mk +++ b/config.mk @@ -25,14 +25,17 @@ # for Windows CROSS_COMPILE ?=i686-w64-mingw32- LIBS ?= -lws2_32 +DEBUG_OPTIONS ?= else # For Linux ifeq ($(strip $(ARCH)),aarch64) CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector else CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector endif endif diff --git a/mk/base-cmd.mk b/mk/base-cmd.mk index f429bcf..e74c114 100644 --- a/mk/base-cmd.mk +++ b/mk/base-cmd.mk @@ -33,8 +33,13 @@ CC = $(CROSS_COMPILE)gcc CXX = $(CROSS_COMPILE)g++ + #CC = $(CROSS_COMPILE)clang #CXX = $(CROSS_COMPILE)clang++ + +#CC = $(CROSS_COMPILE)clang-16 +#CXX = $(CROSS_COMPILE)clang++16 + RANLIB = $(CROSS_COMPILE)ranlib ADDR2LINE = $(CROSS_COMPILE)addr2line AR = $(CROSS_COMPILE)ar diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 2cc417c..eebded1 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -69,7 +69,7 @@ # 値が2の場合、さらなるチェックを追加するが、規格準拠のプログラムが失敗する可能性がある。 # いくつかのチェックは、コンパイル時に実行され、コンパイラの警告として表示される。 # ------------------------------------------------------------------------------ -DEBUG_OPTIONS += -fstack-protector +# DEBUG_OPTIONS += -fstack-protector DEBUG_OPTIONS += --coverage DEBUG_OPTIONS += -g3 -ggdb DEBUG_OPTIONS += -O0 diff --git a/mk/link-a-conf.mk b/mk/link-a-conf.mk index 0e872f3..631e6d5 100644 --- a/mk/link-a-conf.mk +++ b/mk/link-a-conf.mk @@ -10,8 +10,11 @@ HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) +HEADER_FILES_WIN = $(wildcard include/win/*.h) $(wildcard include/win/*.hpp) +TOP_HEADER_FILES_WIN = $(addprefix $(TOPDIR)/include/win/,$(notdir $(HEADER_FILES_WIN))) + CLEAN_FILES += $(TOP_TARGET) -CLEAN_FILES += $(TOP_HEADER_FILES) +CLEAN_FILES += $(TOP_HEADER_FILES) $(TOP_HEADER_FILES_WIN) endif endif diff --git a/mk/link-a-rule.mk b/mk/link-a-rule.mk index d94a54a..657d858 100644 --- a/mk/link-a-rule.mk +++ b/mk/link-a-rule.mk @@ -10,6 +10,7 @@ $(TOP_TARGET): $(TARGET) ifneq ($(strip $(HEADER_FILES)),) $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ + $(CP) -f $(HEADER_FILES_WIN) $(TOPDIR)/include/win/ endif $(CP) -f $(TARGET) $(TOPDIR)/lib/ diff --git a/modules/include/kc_lock_guard.h b/modules/include/kc_lock_guard.h index 8bb70bd..ad3915f 100644 --- a/modules/include/kc_lock_guard.h +++ b/modules/include/kc_lock_guard.h @@ -7,18 +7,16 @@ #define KC_LOCK_GUARD_H #include -#include - +#include /** * LockGuard 管理構造体 */ typedef struct { - mtx_t* mutex; + mtx_t *mutex; } KcLockGuard; - /** * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 * 使用例) @@ -34,8 +32,7 @@ * } */ #define kc_lock_guard(lock) \ - for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) - + for (KcLockGuard _guard = kc_lock_guard_init(lock, __FILE__, __func__, __LINE__); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 @@ -44,15 +41,13 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex); - +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line); /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard); +void kc_lock_guard_release(KcLockGuard *guard); - -#endif // KC_LOCK_GUARD_H +#endif // KC_LOCK_GUARD_H diff --git a/modules/include/kc_memory.h b/modules/include/kc_memory.h index ffce4ed..c6ba9e9 100644 --- a/modules/include/kc_memory.h +++ b/modules/include/kc_memory.h @@ -22,12 +22,13 @@ #ifndef KC_MEMORY_H #define KC_MEMORY_H -#include +#include #include #include #include #include +#include #ifdef __cplusplus extern "C" @@ -273,8 +274,6 @@ #define calloc(nmemb, size) kc_memory_manager->calloc(nmemb, size, __FILE__, __func__, __LINE__) #define realloc(ptr, size) kc_memory_manager->realloc(ptr, size, __FILE__, __func__, __LINE__) #define free(ptr) kc_memory_manager->free(ptr) -#else -#include #endif #ifdef __cplusplus diff --git a/modules/include/kc_socket.h b/modules/include/kc_socket.h new file mode 100644 index 0000000..7d952c7 --- /dev/null +++ b/modules/include/kc_socket.h @@ -0,0 +1,291 @@ +/** + * @file kc_socket.h + * @brief ソケットモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_SOCKET_H +#define KC_SOCKET_H + +#include + +#if (KC_IS_WINDOWS) +typedef SOCKET socket_t; +#define SHUT_RD SD_RECEIVE +#define SHUT_WR SD_SEND +#define SHUT_RDWR SD_BOTH +#define sockclose(sockfd) closesocket(sockfd) +#else +#include +#include +#include +#include +#include +#include +#include +typedef int socket_t; +#define INVALID_SOCKET (-1) +#define SOCKET_ERROR (-1) +#define sockclose(sockfd) close(sockfd) +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcSocket_ + { + + /** + * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr 接続先アドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_remote_addr)(struct KcSocket_ *sock, char *addr, size_t addrlen); + + /** + * ソケットの接続先ポート番号を返します。 + * 接続先ポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_remote_port)(struct KcSocket_ *sock); + + /** + * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr ローカルアドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_local_addr)(struct KcSocket_ *sock, char *addr, size_t addlen); + + /** + * ソケットのローカルポート番号を返します。 + * ローカルポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_local_port)(struct KcSocket_ *sock); + + /** + * ソケットディスクリプタを返します。 + * 通常は、ソケットディスクリプタを直接操作しないでください。 + * + * @param sock 対象ソケット + * @return ソケットディスクリプタ + */ + socket_t (*get_socket)(struct KcSocket_ *sock); + + /** + * ソケットタイプを返します。 + * + * @param sock 対象ソケット + * @return ソケットタイプ + */ + int (*get_type)(struct KcSocket_ *sock); + + /** + * ソケットファミリを返します。 + * 実際にバインド、あるいは接続されているソケットファミリを返します。 + * + * @param sock 対象ソケット + * @return ソケットファミリ + */ + int (*get_family)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービスのアドレス情報を取得します。 + * + * @param sock 対象ソケット + * @param result アドレス情報格納用ポインタ + * @param addr アドレス + * @param service サービス + * @param is_passive サーバソケットの場合、true を指定ください。 + */ + bool (*get_addrinfo)( + struct KcSocket_ *sock, + struct addrinfo **result, + const char *addr, + const char *service, + bool is_passive); + + /** + * 指定されたアドレス、サービスにバインドします。 + * 指定されたアドレスとサービスにより、複数候補がある場合は、 + * 最初に見つかったアドレスとポートにバインドします。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス (例: "80", "http", "ssh" など) + * @return true/false (bind 成功/失敗) + */ + bool (*bind)(struct KcSocket_ *sock, const char *addr, const char *service); + + /** + * ソケットを接続待ちソケットとしてマークをつけます。 + * 保留中の接続のキュー最大長を指定します。 + * + * @param sock 対象ソケット + * @param backlog バックログ + * @return true/false (成功/失敗) + */ + bool (*listen)(struct KcSocket_ *sock, int backlog); + + /** + * ソケットへの接続を受け付けます。 + * + * @param sock 対象ソケット + * @return 受け付けたソケット + */ + struct KcSocket_ *(*accept)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービス接続します。 + * local_addr に NULL を指定した場合、自動的にローカルのアドレスが設定されます。 + * local_service に -1 を指定した場合、自動的にローカルのポート番号が割りつけられます。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス + * @param local_addr ローカルアドレス + * @param local_service ローカルサービス + * @return true/false (成功/失敗) + */ + bool (*connect)(struct KcSocket_ *sock, const char *addr, const char *service, const char *local_addr, const char *local_port); + + /** + * ソケットをクローズします。 + * + * @param sock 対象ソケット + * @return true/false (成功/失敗) + */ + bool (*close)(struct KcSocket_ *sock); + + /** + * メッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 送信されたデータサイズ + */ + ssize_t (*send)(struct KcSocket_ *sock, const char *buff, size_t size, int flags); + + /** + * メッセージを受信します。 + * 受信したメッセージは指定されたバッファに格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 読み取ったデータサイズ + */ + ssize_t (*recv)(struct KcSocket_ *sock, char *buff, size_t size, int flags); + + /** + * 指定されたアドレス、サービスにメッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param addr アドレス + * @param service サービス + * @return 送信メッセージサイズ、エラー発生時は -1 + */ + ssize_t (*sendto)( + struct KcSocket_ *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service); + + /** + * メッセージを受信します。 + * src_addr, src_service がいずれも NULL 出ない場合、 + * 送信元のアドレスとサービスが格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param src_addr 送信元アドレス格納用バッファ + * @param src_addrlen 送信元アドレス格納用バッファサイズ + * @param src_service 送信元サービス格納用バッファ + * @param src_servicelen 送信元サービス格納用バッファサイズ + * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + */ + ssize_t (*recvfrom)( + struct KcSocket_ *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen); + + /** + * TTL を設定します。 + * + * @param sock 対象ソケット + * @param val 設定する TTL + * @return true/false (成功/失敗) + */ + bool (*set_ttl)(struct KcSocket_ *sock, int val); + + /** + * マルチキャストグループに参加するために JOIN します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @param ifindex インタフェースインデックス + * @return true/false (成功/失敗) + */ + bool (*join)(struct KcSocket_ *sock, const char *addr, const char *ifname, unsigned int ifindex); + + /** + * マルチキャストグループから離脱します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @return true/false (成功/失敗) + */ + bool (*leave)(struct KcSocket_ *sock, const char *addr); + + /** + * Socket 管理情報 + */ + void *_info; + + } KcSocket; + + /** + * 指定されたタイプ、ファミリのSocket オブジェクトを構築します。 + * + * @param type タイプ(SOCK_STREAM/SOCK_DGRAM/SOCK_RAW) + * @param family ファミリ(AF_UNSPEC/AF_INET/AF_INET6) + */ + KcSocket *KcSocket_new(int type, int family); + + /** + * Socket を破棄します。 + */ + void KcSocket_delete(KcSocket *socket); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_threads.h b/modules/include/kc_threads.h new file mode 100644 index 0000000..764176f --- /dev/null +++ b/modules/include/kc_threads.h @@ -0,0 +1,98 @@ +/** + * @file kc_threads.h + * @brief スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_THREADS_H +#define KC_THREADS_H + +#include + +#if (KC_IS_WINDOWS) +#include +#else +#include +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcThread_ + { + + /** + * 指定されたスレッドが生きているか否かを返します。 + * + * @param thread 対象スレッド + * @return true/false (スレッドが生存している/していない) + */ + bool (*is_alive)(struct KcThread_ *thread); + + /** + * スレッドを開始します。 + * + * @param thread 対象スレッド + * @param args スレッドに渡す情報 + * @return true/false (スレッド起動成功/失敗) + */ + bool (*start)(struct KcThread_ *thread, void *args); + + /** + * スレッドが終了するのを待機します。 + * + * @param thread 対象スレッド + */ + bool (*join)(struct KcThread_ *thread); + + /** + * Thread 管理情報 + */ + void *_info; + + } KcThread; + + /** + * スレッドを生成します。 + * + * @param run スレッドにて実行する関数へのポインタ + */ + KcThread *KcThread_new(int (*run)(void *args)); + + /** + * Thread を破棄します。 + * + * @param thread 破棄するスレッド + */ + void KcThread_delete(KcThread *thread); + + /** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープさせるミリ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_msleep(long time, bool force); + + /** + * 指定された sec 秒 + nsec ナノ秒間現在のスレッドをスリープさせます。 + * + * @param sec スリープさせる秒数 + * @param nsec スリープさせるナノ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_sleep(long sec, long nsec, bool force); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_THREADS_H diff --git a/modules/include/kc_threads_win.h b/modules/include/kc_threads_win.h new file mode 100644 index 0000000..b40ab10 --- /dev/null +++ b/modules/include/kc_threads_win.h @@ -0,0 +1,98 @@ +/** + * @file kc_windows_thread.h + * @brief Windows 用スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_WINDOWS_THREAD_H +#define KC_WINDOWS_THREAD_H + +#include +#if (KC_IS_WINDOWS) +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#define thread_local _Thread_local + + //////////////////////////////////////////////////////////////////////// + // + // Thread + // + + enum + { + thrd_success = 0, //!< 成功 + thrd_bussy = 1, //!< busy + thrd_error = 2, //!< エラー発生 + thrd_nomem = 3, //!< メモリ不足 + thrd_timedout = 4 //!< タイムアウト発生 + }; + + typedef struct + { + HANDLE handle; + DWORD thread_id; + } thrd_t; + + typedef int (*thrd_start_t)(void *); + int thrd_create(thrd_t *thr, thrd_start_t func, void *arg); + int thrd_join(thrd_t thr, int *res); + int thrd_detach(thrd_t thr); + thrd_t thrd_current(void); + int thrd_equal(thrd_t lhs, thrd_t rhs); + int thrd_sleep(const struct timespec *duration, struct timespec *remaining); + + //////////////////////////////////////////////////////////////////////// + // + // mutex + // + + enum + { + mtx_plain = 0, //!< 通常 + mtx_recursive = 1, //!< 再帰 + mtx_timed = 2 //!< timed + }; + + typedef struct + { + CRITICAL_SECTION cs; + int type; + } mtx_t; + + int mtx_init(mtx_t *mtx, int type); + void mtx_destroy(mtx_t *mtx); + int mtx_lock(mtx_t *mtx); + int mtx_unlock(mtx_t *mtx); + + //////////////////////////////////////////////////////////////////////// + // + // cond + // + + typedef struct + { + CONDITION_VARIABLE cond; + } cnd_t; + + int cnd_init(cnd_t *cond); + int cnd_signal(cnd_t *cond); + int cnd_broadcast(cnd_t *cond); + int cnd_wait(cnd_t *cond, mtx_t *mtx); + int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts); + void cnd_destroy(cnd_t *cond); + +#endif // (KC_IS_WINDOWS) +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_windows.h b/modules/include/kc_windows.h index ee70898..6e1c659 100644 --- a/modules/include/kc_windows.h +++ b/modules/include/kc_windows.h @@ -37,9 +37,9 @@ #endif // よく利用されるヘッダファイルをインクルードする -#include #include #include +#include #ifdef _MSC_VER #pragma comment(lib, "ws2_32.lib") #endif diff --git a/modules/src/kc_assert.c b/modules/src/kc_assert.c index 371fc8f..7cd4ecc 100644 --- a/modules/src/kc_assert.c +++ b/modules/src/kc_assert.c @@ -6,9 +6,9 @@ #include #include #include -#include #include #include +#include // プロトタイプ宣言 static void assert_default_handler(const char *msg); diff --git a/modules/src/kc_env.c b/modules/src/kc_env.c index 0675c73..95f185c 100644 --- a/modules/src/kc_env.c +++ b/modules/src/kc_env.c @@ -50,7 +50,7 @@ char *result = NULL; #if (KC_IS_WINDOWS) static char buff[KC_ENV_BUFFER_MAX]; - DWORD ret = GetEnvironmentVarable(key_name, (LPSTR)buff, sizeof(buff)); + DWORD ret = GetEnvironmentVariable(key_name, (LPSTR)buff, sizeof(buff)); if (ret != 0) { result = buff; diff --git a/modules/src/kc_list_array.c b/modules/src/kc_list_array.c index 246ed14..f29fc08 100644 --- a/modules/src/kc_list_array.c +++ b/modules/src/kc_list_array.c @@ -3,10 +3,14 @@ * @brief 配列リストモジュール * @copyright 2003 - 2023 Nomura Kei */ +#include #if defined(__GNUC__) #define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) #define qsort_s qsort_r #endif +#endif // defined(__GNUC__) + #include #include #include @@ -17,6 +21,7 @@ #include #include #include + /** * KcArrayList 管理情報 */ @@ -306,7 +311,11 @@ * @param context コンテキスト(KcListSortInfo) * @return 比較結果 */ +#if (KC_IS_WINDOWS) +static int KcArrayList_comparator(void *context, const void *x, const void *y) +#else static int KcArrayList_comparator(const void *x, const void *y, void *context) +#endif { KcListSortInfo *sort_info = (KcListSortInfo *)context; int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); diff --git a/modules/src/kc_list_linked.c b/modules/src/kc_list_linked.c index 30575b9..47edbe4 100644 --- a/modules/src/kc_list_linked.c +++ b/modules/src/kc_list_linked.c @@ -3,10 +3,14 @@ * @brief Linked リストモジュール * @copyright 2003 - 2023 Nomura Kei */ +#include #if defined(__GNUC__) #define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) #define qsort_s qsort_r #endif +#endif // defined(__GNUC__) + #include #include #include @@ -18,6 +22,13 @@ #include #include +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) +#define qsort_s qsort_r +#endif // KC_IS_WINDOWS +#endif // defined(__GNUC__) + /** * KcLinkedList Entry 情報 */ @@ -291,7 +302,11 @@ * @param context コンテキスト(KcListSortInfo) * @return 比較結果 */ +#if (KC_IS_WINDOWS) +static int KcLinkedList_comparator(void *context, const void *x, const void *y) +#else static int KcLinkedList_comparator(const void *x, const void *y, void *context) +#endif // KC_IS_WINDOWS { KcListSortInfo *sort_info = (KcListSortInfo *)context; const KcLinkedListEntry **entry_x = (const KcLinkedListEntry **)x; diff --git a/modules/src/kc_lock_guard.c b/modules/src/kc_lock_guard.c index 6ef68e5..648a153 100644 --- a/modules/src/kc_lock_guard.c +++ b/modules/src/kc_lock_guard.c @@ -7,7 +7,6 @@ #include - /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 * ロックに成功するとロック管理用のオブジェクトを返します。 @@ -15,36 +14,37 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex) +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line) { - KcLockGuard guard = { .mutex = mutex }; + KcLockGuard guard = {.mutex = mutex}; if (mutex == NULL) { errno = EINVAL; perror("kc_lock_guard_init: (mutex = NULL)"); + fprintf(stderr, "%s:%s:%d kc_lock_guard_init : mutex = NULL\n", file, func, line); return guard; } + errno = 0; int ret = mtx_lock(mutex); if (ret != thrd_success) { - perror("kc_lock_guard : mtx_lock error"); + fprintf(stderr, "%s:%s:%d kc_lock_guard : mtx_lock error\n", file, func, line); guard.mutex = NULL; } return guard; } - /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard) +void kc_lock_guard_release(KcLockGuard *guard) { - if (guard->mutex != NULL) { - mtx_unlock(guard->mutex); - guard->mutex = NULL; + if (guard->mutex != NULL) + { + mtx_unlock(guard->mutex); + guard->mutex = NULL; } } - diff --git a/modules/src/kc_memory.c b/modules/src/kc_memory.c index 5cc4186..6dbbc88 100644 --- a/modules/src/kc_memory.c +++ b/modules/src/kc_memory.c @@ -622,7 +622,11 @@ */ void *raw_aligned_alloc(size_t alignment, size_t size) { +#if (KC_IS_WINDOWS) + return _aligned_malloc(size, alignment); +#else return aligned_alloc(alignment, size); +#endif } /** diff --git a/modules/src/kc_memory_dump.c b/modules/src/kc_memory_dump.c index 111e328..49e63de 100644 --- a/modules/src/kc_memory_dump.c +++ b/modules/src/kc_memory_dump.c @@ -144,7 +144,7 @@ { // 空きバッファサイズ無しのため、何もしない。 return; } - int write_size = snprintf(info->write_ptr, info->rest_size, msg); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s", msg); if (write_size > info->rest_size) { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) write_size = (info->rest_size - 1); diff --git a/config.mk b/config.mk index a0ac27c..f7c6e47 100644 --- a/config.mk +++ b/config.mk @@ -25,14 +25,17 @@ # for Windows CROSS_COMPILE ?=i686-w64-mingw32- LIBS ?= -lws2_32 +DEBUG_OPTIONS ?= else # For Linux ifeq ($(strip $(ARCH)),aarch64) CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector else CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector endif endif diff --git a/mk/base-cmd.mk b/mk/base-cmd.mk index f429bcf..e74c114 100644 --- a/mk/base-cmd.mk +++ b/mk/base-cmd.mk @@ -33,8 +33,13 @@ CC = $(CROSS_COMPILE)gcc CXX = $(CROSS_COMPILE)g++ + #CC = $(CROSS_COMPILE)clang #CXX = $(CROSS_COMPILE)clang++ + +#CC = $(CROSS_COMPILE)clang-16 +#CXX = $(CROSS_COMPILE)clang++16 + RANLIB = $(CROSS_COMPILE)ranlib ADDR2LINE = $(CROSS_COMPILE)addr2line AR = $(CROSS_COMPILE)ar diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 2cc417c..eebded1 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -69,7 +69,7 @@ # 値が2の場合、さらなるチェックを追加するが、規格準拠のプログラムが失敗する可能性がある。 # いくつかのチェックは、コンパイル時に実行され、コンパイラの警告として表示される。 # ------------------------------------------------------------------------------ -DEBUG_OPTIONS += -fstack-protector +# DEBUG_OPTIONS += -fstack-protector DEBUG_OPTIONS += --coverage DEBUG_OPTIONS += -g3 -ggdb DEBUG_OPTIONS += -O0 diff --git a/mk/link-a-conf.mk b/mk/link-a-conf.mk index 0e872f3..631e6d5 100644 --- a/mk/link-a-conf.mk +++ b/mk/link-a-conf.mk @@ -10,8 +10,11 @@ HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) +HEADER_FILES_WIN = $(wildcard include/win/*.h) $(wildcard include/win/*.hpp) +TOP_HEADER_FILES_WIN = $(addprefix $(TOPDIR)/include/win/,$(notdir $(HEADER_FILES_WIN))) + CLEAN_FILES += $(TOP_TARGET) -CLEAN_FILES += $(TOP_HEADER_FILES) +CLEAN_FILES += $(TOP_HEADER_FILES) $(TOP_HEADER_FILES_WIN) endif endif diff --git a/mk/link-a-rule.mk b/mk/link-a-rule.mk index d94a54a..657d858 100644 --- a/mk/link-a-rule.mk +++ b/mk/link-a-rule.mk @@ -10,6 +10,7 @@ $(TOP_TARGET): $(TARGET) ifneq ($(strip $(HEADER_FILES)),) $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ + $(CP) -f $(HEADER_FILES_WIN) $(TOPDIR)/include/win/ endif $(CP) -f $(TARGET) $(TOPDIR)/lib/ diff --git a/modules/include/kc_lock_guard.h b/modules/include/kc_lock_guard.h index 8bb70bd..ad3915f 100644 --- a/modules/include/kc_lock_guard.h +++ b/modules/include/kc_lock_guard.h @@ -7,18 +7,16 @@ #define KC_LOCK_GUARD_H #include -#include - +#include /** * LockGuard 管理構造体 */ typedef struct { - mtx_t* mutex; + mtx_t *mutex; } KcLockGuard; - /** * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 * 使用例) @@ -34,8 +32,7 @@ * } */ #define kc_lock_guard(lock) \ - for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) - + for (KcLockGuard _guard = kc_lock_guard_init(lock, __FILE__, __func__, __LINE__); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 @@ -44,15 +41,13 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex); - +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line); /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard); +void kc_lock_guard_release(KcLockGuard *guard); - -#endif // KC_LOCK_GUARD_H +#endif // KC_LOCK_GUARD_H diff --git a/modules/include/kc_memory.h b/modules/include/kc_memory.h index ffce4ed..c6ba9e9 100644 --- a/modules/include/kc_memory.h +++ b/modules/include/kc_memory.h @@ -22,12 +22,13 @@ #ifndef KC_MEMORY_H #define KC_MEMORY_H -#include +#include #include #include #include #include +#include #ifdef __cplusplus extern "C" @@ -273,8 +274,6 @@ #define calloc(nmemb, size) kc_memory_manager->calloc(nmemb, size, __FILE__, __func__, __LINE__) #define realloc(ptr, size) kc_memory_manager->realloc(ptr, size, __FILE__, __func__, __LINE__) #define free(ptr) kc_memory_manager->free(ptr) -#else -#include #endif #ifdef __cplusplus diff --git a/modules/include/kc_socket.h b/modules/include/kc_socket.h new file mode 100644 index 0000000..7d952c7 --- /dev/null +++ b/modules/include/kc_socket.h @@ -0,0 +1,291 @@ +/** + * @file kc_socket.h + * @brief ソケットモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_SOCKET_H +#define KC_SOCKET_H + +#include + +#if (KC_IS_WINDOWS) +typedef SOCKET socket_t; +#define SHUT_RD SD_RECEIVE +#define SHUT_WR SD_SEND +#define SHUT_RDWR SD_BOTH +#define sockclose(sockfd) closesocket(sockfd) +#else +#include +#include +#include +#include +#include +#include +#include +typedef int socket_t; +#define INVALID_SOCKET (-1) +#define SOCKET_ERROR (-1) +#define sockclose(sockfd) close(sockfd) +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcSocket_ + { + + /** + * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr 接続先アドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_remote_addr)(struct KcSocket_ *sock, char *addr, size_t addrlen); + + /** + * ソケットの接続先ポート番号を返します。 + * 接続先ポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_remote_port)(struct KcSocket_ *sock); + + /** + * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr ローカルアドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_local_addr)(struct KcSocket_ *sock, char *addr, size_t addlen); + + /** + * ソケットのローカルポート番号を返します。 + * ローカルポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_local_port)(struct KcSocket_ *sock); + + /** + * ソケットディスクリプタを返します。 + * 通常は、ソケットディスクリプタを直接操作しないでください。 + * + * @param sock 対象ソケット + * @return ソケットディスクリプタ + */ + socket_t (*get_socket)(struct KcSocket_ *sock); + + /** + * ソケットタイプを返します。 + * + * @param sock 対象ソケット + * @return ソケットタイプ + */ + int (*get_type)(struct KcSocket_ *sock); + + /** + * ソケットファミリを返します。 + * 実際にバインド、あるいは接続されているソケットファミリを返します。 + * + * @param sock 対象ソケット + * @return ソケットファミリ + */ + int (*get_family)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービスのアドレス情報を取得します。 + * + * @param sock 対象ソケット + * @param result アドレス情報格納用ポインタ + * @param addr アドレス + * @param service サービス + * @param is_passive サーバソケットの場合、true を指定ください。 + */ + bool (*get_addrinfo)( + struct KcSocket_ *sock, + struct addrinfo **result, + const char *addr, + const char *service, + bool is_passive); + + /** + * 指定されたアドレス、サービスにバインドします。 + * 指定されたアドレスとサービスにより、複数候補がある場合は、 + * 最初に見つかったアドレスとポートにバインドします。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス (例: "80", "http", "ssh" など) + * @return true/false (bind 成功/失敗) + */ + bool (*bind)(struct KcSocket_ *sock, const char *addr, const char *service); + + /** + * ソケットを接続待ちソケットとしてマークをつけます。 + * 保留中の接続のキュー最大長を指定します。 + * + * @param sock 対象ソケット + * @param backlog バックログ + * @return true/false (成功/失敗) + */ + bool (*listen)(struct KcSocket_ *sock, int backlog); + + /** + * ソケットへの接続を受け付けます。 + * + * @param sock 対象ソケット + * @return 受け付けたソケット + */ + struct KcSocket_ *(*accept)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービス接続します。 + * local_addr に NULL を指定した場合、自動的にローカルのアドレスが設定されます。 + * local_service に -1 を指定した場合、自動的にローカルのポート番号が割りつけられます。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス + * @param local_addr ローカルアドレス + * @param local_service ローカルサービス + * @return true/false (成功/失敗) + */ + bool (*connect)(struct KcSocket_ *sock, const char *addr, const char *service, const char *local_addr, const char *local_port); + + /** + * ソケットをクローズします。 + * + * @param sock 対象ソケット + * @return true/false (成功/失敗) + */ + bool (*close)(struct KcSocket_ *sock); + + /** + * メッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 送信されたデータサイズ + */ + ssize_t (*send)(struct KcSocket_ *sock, const char *buff, size_t size, int flags); + + /** + * メッセージを受信します。 + * 受信したメッセージは指定されたバッファに格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 読み取ったデータサイズ + */ + ssize_t (*recv)(struct KcSocket_ *sock, char *buff, size_t size, int flags); + + /** + * 指定されたアドレス、サービスにメッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param addr アドレス + * @param service サービス + * @return 送信メッセージサイズ、エラー発生時は -1 + */ + ssize_t (*sendto)( + struct KcSocket_ *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service); + + /** + * メッセージを受信します。 + * src_addr, src_service がいずれも NULL 出ない場合、 + * 送信元のアドレスとサービスが格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param src_addr 送信元アドレス格納用バッファ + * @param src_addrlen 送信元アドレス格納用バッファサイズ + * @param src_service 送信元サービス格納用バッファ + * @param src_servicelen 送信元サービス格納用バッファサイズ + * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + */ + ssize_t (*recvfrom)( + struct KcSocket_ *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen); + + /** + * TTL を設定します。 + * + * @param sock 対象ソケット + * @param val 設定する TTL + * @return true/false (成功/失敗) + */ + bool (*set_ttl)(struct KcSocket_ *sock, int val); + + /** + * マルチキャストグループに参加するために JOIN します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @param ifindex インタフェースインデックス + * @return true/false (成功/失敗) + */ + bool (*join)(struct KcSocket_ *sock, const char *addr, const char *ifname, unsigned int ifindex); + + /** + * マルチキャストグループから離脱します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @return true/false (成功/失敗) + */ + bool (*leave)(struct KcSocket_ *sock, const char *addr); + + /** + * Socket 管理情報 + */ + void *_info; + + } KcSocket; + + /** + * 指定されたタイプ、ファミリのSocket オブジェクトを構築します。 + * + * @param type タイプ(SOCK_STREAM/SOCK_DGRAM/SOCK_RAW) + * @param family ファミリ(AF_UNSPEC/AF_INET/AF_INET6) + */ + KcSocket *KcSocket_new(int type, int family); + + /** + * Socket を破棄します。 + */ + void KcSocket_delete(KcSocket *socket); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_threads.h b/modules/include/kc_threads.h new file mode 100644 index 0000000..764176f --- /dev/null +++ b/modules/include/kc_threads.h @@ -0,0 +1,98 @@ +/** + * @file kc_threads.h + * @brief スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_THREADS_H +#define KC_THREADS_H + +#include + +#if (KC_IS_WINDOWS) +#include +#else +#include +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcThread_ + { + + /** + * 指定されたスレッドが生きているか否かを返します。 + * + * @param thread 対象スレッド + * @return true/false (スレッドが生存している/していない) + */ + bool (*is_alive)(struct KcThread_ *thread); + + /** + * スレッドを開始します。 + * + * @param thread 対象スレッド + * @param args スレッドに渡す情報 + * @return true/false (スレッド起動成功/失敗) + */ + bool (*start)(struct KcThread_ *thread, void *args); + + /** + * スレッドが終了するのを待機します。 + * + * @param thread 対象スレッド + */ + bool (*join)(struct KcThread_ *thread); + + /** + * Thread 管理情報 + */ + void *_info; + + } KcThread; + + /** + * スレッドを生成します。 + * + * @param run スレッドにて実行する関数へのポインタ + */ + KcThread *KcThread_new(int (*run)(void *args)); + + /** + * Thread を破棄します。 + * + * @param thread 破棄するスレッド + */ + void KcThread_delete(KcThread *thread); + + /** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープさせるミリ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_msleep(long time, bool force); + + /** + * 指定された sec 秒 + nsec ナノ秒間現在のスレッドをスリープさせます。 + * + * @param sec スリープさせる秒数 + * @param nsec スリープさせるナノ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_sleep(long sec, long nsec, bool force); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_THREADS_H diff --git a/modules/include/kc_threads_win.h b/modules/include/kc_threads_win.h new file mode 100644 index 0000000..b40ab10 --- /dev/null +++ b/modules/include/kc_threads_win.h @@ -0,0 +1,98 @@ +/** + * @file kc_windows_thread.h + * @brief Windows 用スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_WINDOWS_THREAD_H +#define KC_WINDOWS_THREAD_H + +#include +#if (KC_IS_WINDOWS) +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#define thread_local _Thread_local + + //////////////////////////////////////////////////////////////////////// + // + // Thread + // + + enum + { + thrd_success = 0, //!< 成功 + thrd_bussy = 1, //!< busy + thrd_error = 2, //!< エラー発生 + thrd_nomem = 3, //!< メモリ不足 + thrd_timedout = 4 //!< タイムアウト発生 + }; + + typedef struct + { + HANDLE handle; + DWORD thread_id; + } thrd_t; + + typedef int (*thrd_start_t)(void *); + int thrd_create(thrd_t *thr, thrd_start_t func, void *arg); + int thrd_join(thrd_t thr, int *res); + int thrd_detach(thrd_t thr); + thrd_t thrd_current(void); + int thrd_equal(thrd_t lhs, thrd_t rhs); + int thrd_sleep(const struct timespec *duration, struct timespec *remaining); + + //////////////////////////////////////////////////////////////////////// + // + // mutex + // + + enum + { + mtx_plain = 0, //!< 通常 + mtx_recursive = 1, //!< 再帰 + mtx_timed = 2 //!< timed + }; + + typedef struct + { + CRITICAL_SECTION cs; + int type; + } mtx_t; + + int mtx_init(mtx_t *mtx, int type); + void mtx_destroy(mtx_t *mtx); + int mtx_lock(mtx_t *mtx); + int mtx_unlock(mtx_t *mtx); + + //////////////////////////////////////////////////////////////////////// + // + // cond + // + + typedef struct + { + CONDITION_VARIABLE cond; + } cnd_t; + + int cnd_init(cnd_t *cond); + int cnd_signal(cnd_t *cond); + int cnd_broadcast(cnd_t *cond); + int cnd_wait(cnd_t *cond, mtx_t *mtx); + int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts); + void cnd_destroy(cnd_t *cond); + +#endif // (KC_IS_WINDOWS) +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_windows.h b/modules/include/kc_windows.h index ee70898..6e1c659 100644 --- a/modules/include/kc_windows.h +++ b/modules/include/kc_windows.h @@ -37,9 +37,9 @@ #endif // よく利用されるヘッダファイルをインクルードする -#include #include #include +#include #ifdef _MSC_VER #pragma comment(lib, "ws2_32.lib") #endif diff --git a/modules/src/kc_assert.c b/modules/src/kc_assert.c index 371fc8f..7cd4ecc 100644 --- a/modules/src/kc_assert.c +++ b/modules/src/kc_assert.c @@ -6,9 +6,9 @@ #include #include #include -#include #include #include +#include // プロトタイプ宣言 static void assert_default_handler(const char *msg); diff --git a/modules/src/kc_env.c b/modules/src/kc_env.c index 0675c73..95f185c 100644 --- a/modules/src/kc_env.c +++ b/modules/src/kc_env.c @@ -50,7 +50,7 @@ char *result = NULL; #if (KC_IS_WINDOWS) static char buff[KC_ENV_BUFFER_MAX]; - DWORD ret = GetEnvironmentVarable(key_name, (LPSTR)buff, sizeof(buff)); + DWORD ret = GetEnvironmentVariable(key_name, (LPSTR)buff, sizeof(buff)); if (ret != 0) { result = buff; diff --git a/modules/src/kc_list_array.c b/modules/src/kc_list_array.c index 246ed14..f29fc08 100644 --- a/modules/src/kc_list_array.c +++ b/modules/src/kc_list_array.c @@ -3,10 +3,14 @@ * @brief 配列リストモジュール * @copyright 2003 - 2023 Nomura Kei */ +#include #if defined(__GNUC__) #define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) #define qsort_s qsort_r #endif +#endif // defined(__GNUC__) + #include #include #include @@ -17,6 +21,7 @@ #include #include #include + /** * KcArrayList 管理情報 */ @@ -306,7 +311,11 @@ * @param context コンテキスト(KcListSortInfo) * @return 比較結果 */ +#if (KC_IS_WINDOWS) +static int KcArrayList_comparator(void *context, const void *x, const void *y) +#else static int KcArrayList_comparator(const void *x, const void *y, void *context) +#endif { KcListSortInfo *sort_info = (KcListSortInfo *)context; int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); diff --git a/modules/src/kc_list_linked.c b/modules/src/kc_list_linked.c index 30575b9..47edbe4 100644 --- a/modules/src/kc_list_linked.c +++ b/modules/src/kc_list_linked.c @@ -3,10 +3,14 @@ * @brief Linked リストモジュール * @copyright 2003 - 2023 Nomura Kei */ +#include #if defined(__GNUC__) #define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) #define qsort_s qsort_r #endif +#endif // defined(__GNUC__) + #include #include #include @@ -18,6 +22,13 @@ #include #include +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) +#define qsort_s qsort_r +#endif // KC_IS_WINDOWS +#endif // defined(__GNUC__) + /** * KcLinkedList Entry 情報 */ @@ -291,7 +302,11 @@ * @param context コンテキスト(KcListSortInfo) * @return 比較結果 */ +#if (KC_IS_WINDOWS) +static int KcLinkedList_comparator(void *context, const void *x, const void *y) +#else static int KcLinkedList_comparator(const void *x, const void *y, void *context) +#endif // KC_IS_WINDOWS { KcListSortInfo *sort_info = (KcListSortInfo *)context; const KcLinkedListEntry **entry_x = (const KcLinkedListEntry **)x; diff --git a/modules/src/kc_lock_guard.c b/modules/src/kc_lock_guard.c index 6ef68e5..648a153 100644 --- a/modules/src/kc_lock_guard.c +++ b/modules/src/kc_lock_guard.c @@ -7,7 +7,6 @@ #include - /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 * ロックに成功するとロック管理用のオブジェクトを返します。 @@ -15,36 +14,37 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex) +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line) { - KcLockGuard guard = { .mutex = mutex }; + KcLockGuard guard = {.mutex = mutex}; if (mutex == NULL) { errno = EINVAL; perror("kc_lock_guard_init: (mutex = NULL)"); + fprintf(stderr, "%s:%s:%d kc_lock_guard_init : mutex = NULL\n", file, func, line); return guard; } + errno = 0; int ret = mtx_lock(mutex); if (ret != thrd_success) { - perror("kc_lock_guard : mtx_lock error"); + fprintf(stderr, "%s:%s:%d kc_lock_guard : mtx_lock error\n", file, func, line); guard.mutex = NULL; } return guard; } - /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard) +void kc_lock_guard_release(KcLockGuard *guard) { - if (guard->mutex != NULL) { - mtx_unlock(guard->mutex); - guard->mutex = NULL; + if (guard->mutex != NULL) + { + mtx_unlock(guard->mutex); + guard->mutex = NULL; } } - diff --git a/modules/src/kc_memory.c b/modules/src/kc_memory.c index 5cc4186..6dbbc88 100644 --- a/modules/src/kc_memory.c +++ b/modules/src/kc_memory.c @@ -622,7 +622,11 @@ */ void *raw_aligned_alloc(size_t alignment, size_t size) { +#if (KC_IS_WINDOWS) + return _aligned_malloc(size, alignment); +#else return aligned_alloc(alignment, size); +#endif } /** diff --git a/modules/src/kc_memory_dump.c b/modules/src/kc_memory_dump.c index 111e328..49e63de 100644 --- a/modules/src/kc_memory_dump.c +++ b/modules/src/kc_memory_dump.c @@ -144,7 +144,7 @@ { // 空きバッファサイズ無しのため、何もしない。 return; } - int write_size = snprintf(info->write_ptr, info->rest_size, msg); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s", msg); if (write_size > info->rest_size) { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) write_size = (info->rest_size - 1); diff --git a/modules/src/kc_memory_listener.c b/modules/src/kc_memory_listener.c index 29ff2a0..ee69a13 100644 --- a/modules/src/kc_memory_listener.c +++ b/modules/src/kc_memory_listener.c @@ -98,5 +98,5 @@ KcMemoryDump_dump(buff, sizeof(buff), entry, 16, true, true, 130); fprintf(stderr, "[ERROR] %s", buff); } - fprintf(stderr, msg); + fprintf(stderr, "%s", msg); } diff --git a/config.mk b/config.mk index a0ac27c..f7c6e47 100644 --- a/config.mk +++ b/config.mk @@ -25,14 +25,17 @@ # for Windows CROSS_COMPILE ?=i686-w64-mingw32- LIBS ?= -lws2_32 +DEBUG_OPTIONS ?= else # For Linux ifeq ($(strip $(ARCH)),aarch64) CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector else CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector endif endif diff --git a/mk/base-cmd.mk b/mk/base-cmd.mk index f429bcf..e74c114 100644 --- a/mk/base-cmd.mk +++ b/mk/base-cmd.mk @@ -33,8 +33,13 @@ CC = $(CROSS_COMPILE)gcc CXX = $(CROSS_COMPILE)g++ + #CC = $(CROSS_COMPILE)clang #CXX = $(CROSS_COMPILE)clang++ + +#CC = $(CROSS_COMPILE)clang-16 +#CXX = $(CROSS_COMPILE)clang++16 + RANLIB = $(CROSS_COMPILE)ranlib ADDR2LINE = $(CROSS_COMPILE)addr2line AR = $(CROSS_COMPILE)ar diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 2cc417c..eebded1 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -69,7 +69,7 @@ # 値が2の場合、さらなるチェックを追加するが、規格準拠のプログラムが失敗する可能性がある。 # いくつかのチェックは、コンパイル時に実行され、コンパイラの警告として表示される。 # ------------------------------------------------------------------------------ -DEBUG_OPTIONS += -fstack-protector +# DEBUG_OPTIONS += -fstack-protector DEBUG_OPTIONS += --coverage DEBUG_OPTIONS += -g3 -ggdb DEBUG_OPTIONS += -O0 diff --git a/mk/link-a-conf.mk b/mk/link-a-conf.mk index 0e872f3..631e6d5 100644 --- a/mk/link-a-conf.mk +++ b/mk/link-a-conf.mk @@ -10,8 +10,11 @@ HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) +HEADER_FILES_WIN = $(wildcard include/win/*.h) $(wildcard include/win/*.hpp) +TOP_HEADER_FILES_WIN = $(addprefix $(TOPDIR)/include/win/,$(notdir $(HEADER_FILES_WIN))) + CLEAN_FILES += $(TOP_TARGET) -CLEAN_FILES += $(TOP_HEADER_FILES) +CLEAN_FILES += $(TOP_HEADER_FILES) $(TOP_HEADER_FILES_WIN) endif endif diff --git a/mk/link-a-rule.mk b/mk/link-a-rule.mk index d94a54a..657d858 100644 --- a/mk/link-a-rule.mk +++ b/mk/link-a-rule.mk @@ -10,6 +10,7 @@ $(TOP_TARGET): $(TARGET) ifneq ($(strip $(HEADER_FILES)),) $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ + $(CP) -f $(HEADER_FILES_WIN) $(TOPDIR)/include/win/ endif $(CP) -f $(TARGET) $(TOPDIR)/lib/ diff --git a/modules/include/kc_lock_guard.h b/modules/include/kc_lock_guard.h index 8bb70bd..ad3915f 100644 --- a/modules/include/kc_lock_guard.h +++ b/modules/include/kc_lock_guard.h @@ -7,18 +7,16 @@ #define KC_LOCK_GUARD_H #include -#include - +#include /** * LockGuard 管理構造体 */ typedef struct { - mtx_t* mutex; + mtx_t *mutex; } KcLockGuard; - /** * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 * 使用例) @@ -34,8 +32,7 @@ * } */ #define kc_lock_guard(lock) \ - for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) - + for (KcLockGuard _guard = kc_lock_guard_init(lock, __FILE__, __func__, __LINE__); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 @@ -44,15 +41,13 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex); - +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line); /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard); +void kc_lock_guard_release(KcLockGuard *guard); - -#endif // KC_LOCK_GUARD_H +#endif // KC_LOCK_GUARD_H diff --git a/modules/include/kc_memory.h b/modules/include/kc_memory.h index ffce4ed..c6ba9e9 100644 --- a/modules/include/kc_memory.h +++ b/modules/include/kc_memory.h @@ -22,12 +22,13 @@ #ifndef KC_MEMORY_H #define KC_MEMORY_H -#include +#include #include #include #include #include +#include #ifdef __cplusplus extern "C" @@ -273,8 +274,6 @@ #define calloc(nmemb, size) kc_memory_manager->calloc(nmemb, size, __FILE__, __func__, __LINE__) #define realloc(ptr, size) kc_memory_manager->realloc(ptr, size, __FILE__, __func__, __LINE__) #define free(ptr) kc_memory_manager->free(ptr) -#else -#include #endif #ifdef __cplusplus diff --git a/modules/include/kc_socket.h b/modules/include/kc_socket.h new file mode 100644 index 0000000..7d952c7 --- /dev/null +++ b/modules/include/kc_socket.h @@ -0,0 +1,291 @@ +/** + * @file kc_socket.h + * @brief ソケットモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_SOCKET_H +#define KC_SOCKET_H + +#include + +#if (KC_IS_WINDOWS) +typedef SOCKET socket_t; +#define SHUT_RD SD_RECEIVE +#define SHUT_WR SD_SEND +#define SHUT_RDWR SD_BOTH +#define sockclose(sockfd) closesocket(sockfd) +#else +#include +#include +#include +#include +#include +#include +#include +typedef int socket_t; +#define INVALID_SOCKET (-1) +#define SOCKET_ERROR (-1) +#define sockclose(sockfd) close(sockfd) +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcSocket_ + { + + /** + * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr 接続先アドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_remote_addr)(struct KcSocket_ *sock, char *addr, size_t addrlen); + + /** + * ソケットの接続先ポート番号を返します。 + * 接続先ポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_remote_port)(struct KcSocket_ *sock); + + /** + * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr ローカルアドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_local_addr)(struct KcSocket_ *sock, char *addr, size_t addlen); + + /** + * ソケットのローカルポート番号を返します。 + * ローカルポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_local_port)(struct KcSocket_ *sock); + + /** + * ソケットディスクリプタを返します。 + * 通常は、ソケットディスクリプタを直接操作しないでください。 + * + * @param sock 対象ソケット + * @return ソケットディスクリプタ + */ + socket_t (*get_socket)(struct KcSocket_ *sock); + + /** + * ソケットタイプを返します。 + * + * @param sock 対象ソケット + * @return ソケットタイプ + */ + int (*get_type)(struct KcSocket_ *sock); + + /** + * ソケットファミリを返します。 + * 実際にバインド、あるいは接続されているソケットファミリを返します。 + * + * @param sock 対象ソケット + * @return ソケットファミリ + */ + int (*get_family)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービスのアドレス情報を取得します。 + * + * @param sock 対象ソケット + * @param result アドレス情報格納用ポインタ + * @param addr アドレス + * @param service サービス + * @param is_passive サーバソケットの場合、true を指定ください。 + */ + bool (*get_addrinfo)( + struct KcSocket_ *sock, + struct addrinfo **result, + const char *addr, + const char *service, + bool is_passive); + + /** + * 指定されたアドレス、サービスにバインドします。 + * 指定されたアドレスとサービスにより、複数候補がある場合は、 + * 最初に見つかったアドレスとポートにバインドします。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス (例: "80", "http", "ssh" など) + * @return true/false (bind 成功/失敗) + */ + bool (*bind)(struct KcSocket_ *sock, const char *addr, const char *service); + + /** + * ソケットを接続待ちソケットとしてマークをつけます。 + * 保留中の接続のキュー最大長を指定します。 + * + * @param sock 対象ソケット + * @param backlog バックログ + * @return true/false (成功/失敗) + */ + bool (*listen)(struct KcSocket_ *sock, int backlog); + + /** + * ソケットへの接続を受け付けます。 + * + * @param sock 対象ソケット + * @return 受け付けたソケット + */ + struct KcSocket_ *(*accept)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービス接続します。 + * local_addr に NULL を指定した場合、自動的にローカルのアドレスが設定されます。 + * local_service に -1 を指定した場合、自動的にローカルのポート番号が割りつけられます。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス + * @param local_addr ローカルアドレス + * @param local_service ローカルサービス + * @return true/false (成功/失敗) + */ + bool (*connect)(struct KcSocket_ *sock, const char *addr, const char *service, const char *local_addr, const char *local_port); + + /** + * ソケットをクローズします。 + * + * @param sock 対象ソケット + * @return true/false (成功/失敗) + */ + bool (*close)(struct KcSocket_ *sock); + + /** + * メッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 送信されたデータサイズ + */ + ssize_t (*send)(struct KcSocket_ *sock, const char *buff, size_t size, int flags); + + /** + * メッセージを受信します。 + * 受信したメッセージは指定されたバッファに格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 読み取ったデータサイズ + */ + ssize_t (*recv)(struct KcSocket_ *sock, char *buff, size_t size, int flags); + + /** + * 指定されたアドレス、サービスにメッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param addr アドレス + * @param service サービス + * @return 送信メッセージサイズ、エラー発生時は -1 + */ + ssize_t (*sendto)( + struct KcSocket_ *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service); + + /** + * メッセージを受信します。 + * src_addr, src_service がいずれも NULL 出ない場合、 + * 送信元のアドレスとサービスが格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param src_addr 送信元アドレス格納用バッファ + * @param src_addrlen 送信元アドレス格納用バッファサイズ + * @param src_service 送信元サービス格納用バッファ + * @param src_servicelen 送信元サービス格納用バッファサイズ + * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + */ + ssize_t (*recvfrom)( + struct KcSocket_ *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen); + + /** + * TTL を設定します。 + * + * @param sock 対象ソケット + * @param val 設定する TTL + * @return true/false (成功/失敗) + */ + bool (*set_ttl)(struct KcSocket_ *sock, int val); + + /** + * マルチキャストグループに参加するために JOIN します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @param ifindex インタフェースインデックス + * @return true/false (成功/失敗) + */ + bool (*join)(struct KcSocket_ *sock, const char *addr, const char *ifname, unsigned int ifindex); + + /** + * マルチキャストグループから離脱します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @return true/false (成功/失敗) + */ + bool (*leave)(struct KcSocket_ *sock, const char *addr); + + /** + * Socket 管理情報 + */ + void *_info; + + } KcSocket; + + /** + * 指定されたタイプ、ファミリのSocket オブジェクトを構築します。 + * + * @param type タイプ(SOCK_STREAM/SOCK_DGRAM/SOCK_RAW) + * @param family ファミリ(AF_UNSPEC/AF_INET/AF_INET6) + */ + KcSocket *KcSocket_new(int type, int family); + + /** + * Socket を破棄します。 + */ + void KcSocket_delete(KcSocket *socket); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_threads.h b/modules/include/kc_threads.h new file mode 100644 index 0000000..764176f --- /dev/null +++ b/modules/include/kc_threads.h @@ -0,0 +1,98 @@ +/** + * @file kc_threads.h + * @brief スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_THREADS_H +#define KC_THREADS_H + +#include + +#if (KC_IS_WINDOWS) +#include +#else +#include +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcThread_ + { + + /** + * 指定されたスレッドが生きているか否かを返します。 + * + * @param thread 対象スレッド + * @return true/false (スレッドが生存している/していない) + */ + bool (*is_alive)(struct KcThread_ *thread); + + /** + * スレッドを開始します。 + * + * @param thread 対象スレッド + * @param args スレッドに渡す情報 + * @return true/false (スレッド起動成功/失敗) + */ + bool (*start)(struct KcThread_ *thread, void *args); + + /** + * スレッドが終了するのを待機します。 + * + * @param thread 対象スレッド + */ + bool (*join)(struct KcThread_ *thread); + + /** + * Thread 管理情報 + */ + void *_info; + + } KcThread; + + /** + * スレッドを生成します。 + * + * @param run スレッドにて実行する関数へのポインタ + */ + KcThread *KcThread_new(int (*run)(void *args)); + + /** + * Thread を破棄します。 + * + * @param thread 破棄するスレッド + */ + void KcThread_delete(KcThread *thread); + + /** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープさせるミリ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_msleep(long time, bool force); + + /** + * 指定された sec 秒 + nsec ナノ秒間現在のスレッドをスリープさせます。 + * + * @param sec スリープさせる秒数 + * @param nsec スリープさせるナノ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_sleep(long sec, long nsec, bool force); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_THREADS_H diff --git a/modules/include/kc_threads_win.h b/modules/include/kc_threads_win.h new file mode 100644 index 0000000..b40ab10 --- /dev/null +++ b/modules/include/kc_threads_win.h @@ -0,0 +1,98 @@ +/** + * @file kc_windows_thread.h + * @brief Windows 用スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_WINDOWS_THREAD_H +#define KC_WINDOWS_THREAD_H + +#include +#if (KC_IS_WINDOWS) +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#define thread_local _Thread_local + + //////////////////////////////////////////////////////////////////////// + // + // Thread + // + + enum + { + thrd_success = 0, //!< 成功 + thrd_bussy = 1, //!< busy + thrd_error = 2, //!< エラー発生 + thrd_nomem = 3, //!< メモリ不足 + thrd_timedout = 4 //!< タイムアウト発生 + }; + + typedef struct + { + HANDLE handle; + DWORD thread_id; + } thrd_t; + + typedef int (*thrd_start_t)(void *); + int thrd_create(thrd_t *thr, thrd_start_t func, void *arg); + int thrd_join(thrd_t thr, int *res); + int thrd_detach(thrd_t thr); + thrd_t thrd_current(void); + int thrd_equal(thrd_t lhs, thrd_t rhs); + int thrd_sleep(const struct timespec *duration, struct timespec *remaining); + + //////////////////////////////////////////////////////////////////////// + // + // mutex + // + + enum + { + mtx_plain = 0, //!< 通常 + mtx_recursive = 1, //!< 再帰 + mtx_timed = 2 //!< timed + }; + + typedef struct + { + CRITICAL_SECTION cs; + int type; + } mtx_t; + + int mtx_init(mtx_t *mtx, int type); + void mtx_destroy(mtx_t *mtx); + int mtx_lock(mtx_t *mtx); + int mtx_unlock(mtx_t *mtx); + + //////////////////////////////////////////////////////////////////////// + // + // cond + // + + typedef struct + { + CONDITION_VARIABLE cond; + } cnd_t; + + int cnd_init(cnd_t *cond); + int cnd_signal(cnd_t *cond); + int cnd_broadcast(cnd_t *cond); + int cnd_wait(cnd_t *cond, mtx_t *mtx); + int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts); + void cnd_destroy(cnd_t *cond); + +#endif // (KC_IS_WINDOWS) +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_windows.h b/modules/include/kc_windows.h index ee70898..6e1c659 100644 --- a/modules/include/kc_windows.h +++ b/modules/include/kc_windows.h @@ -37,9 +37,9 @@ #endif // よく利用されるヘッダファイルをインクルードする -#include #include #include +#include #ifdef _MSC_VER #pragma comment(lib, "ws2_32.lib") #endif diff --git a/modules/src/kc_assert.c b/modules/src/kc_assert.c index 371fc8f..7cd4ecc 100644 --- a/modules/src/kc_assert.c +++ b/modules/src/kc_assert.c @@ -6,9 +6,9 @@ #include #include #include -#include #include #include +#include // プロトタイプ宣言 static void assert_default_handler(const char *msg); diff --git a/modules/src/kc_env.c b/modules/src/kc_env.c index 0675c73..95f185c 100644 --- a/modules/src/kc_env.c +++ b/modules/src/kc_env.c @@ -50,7 +50,7 @@ char *result = NULL; #if (KC_IS_WINDOWS) static char buff[KC_ENV_BUFFER_MAX]; - DWORD ret = GetEnvironmentVarable(key_name, (LPSTR)buff, sizeof(buff)); + DWORD ret = GetEnvironmentVariable(key_name, (LPSTR)buff, sizeof(buff)); if (ret != 0) { result = buff; diff --git a/modules/src/kc_list_array.c b/modules/src/kc_list_array.c index 246ed14..f29fc08 100644 --- a/modules/src/kc_list_array.c +++ b/modules/src/kc_list_array.c @@ -3,10 +3,14 @@ * @brief 配列リストモジュール * @copyright 2003 - 2023 Nomura Kei */ +#include #if defined(__GNUC__) #define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) #define qsort_s qsort_r #endif +#endif // defined(__GNUC__) + #include #include #include @@ -17,6 +21,7 @@ #include #include #include + /** * KcArrayList 管理情報 */ @@ -306,7 +311,11 @@ * @param context コンテキスト(KcListSortInfo) * @return 比較結果 */ +#if (KC_IS_WINDOWS) +static int KcArrayList_comparator(void *context, const void *x, const void *y) +#else static int KcArrayList_comparator(const void *x, const void *y, void *context) +#endif { KcListSortInfo *sort_info = (KcListSortInfo *)context; int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); diff --git a/modules/src/kc_list_linked.c b/modules/src/kc_list_linked.c index 30575b9..47edbe4 100644 --- a/modules/src/kc_list_linked.c +++ b/modules/src/kc_list_linked.c @@ -3,10 +3,14 @@ * @brief Linked リストモジュール * @copyright 2003 - 2023 Nomura Kei */ +#include #if defined(__GNUC__) #define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) #define qsort_s qsort_r #endif +#endif // defined(__GNUC__) + #include #include #include @@ -18,6 +22,13 @@ #include #include +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) +#define qsort_s qsort_r +#endif // KC_IS_WINDOWS +#endif // defined(__GNUC__) + /** * KcLinkedList Entry 情報 */ @@ -291,7 +302,11 @@ * @param context コンテキスト(KcListSortInfo) * @return 比較結果 */ +#if (KC_IS_WINDOWS) +static int KcLinkedList_comparator(void *context, const void *x, const void *y) +#else static int KcLinkedList_comparator(const void *x, const void *y, void *context) +#endif // KC_IS_WINDOWS { KcListSortInfo *sort_info = (KcListSortInfo *)context; const KcLinkedListEntry **entry_x = (const KcLinkedListEntry **)x; diff --git a/modules/src/kc_lock_guard.c b/modules/src/kc_lock_guard.c index 6ef68e5..648a153 100644 --- a/modules/src/kc_lock_guard.c +++ b/modules/src/kc_lock_guard.c @@ -7,7 +7,6 @@ #include - /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 * ロックに成功するとロック管理用のオブジェクトを返します。 @@ -15,36 +14,37 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex) +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line) { - KcLockGuard guard = { .mutex = mutex }; + KcLockGuard guard = {.mutex = mutex}; if (mutex == NULL) { errno = EINVAL; perror("kc_lock_guard_init: (mutex = NULL)"); + fprintf(stderr, "%s:%s:%d kc_lock_guard_init : mutex = NULL\n", file, func, line); return guard; } + errno = 0; int ret = mtx_lock(mutex); if (ret != thrd_success) { - perror("kc_lock_guard : mtx_lock error"); + fprintf(stderr, "%s:%s:%d kc_lock_guard : mtx_lock error\n", file, func, line); guard.mutex = NULL; } return guard; } - /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard) +void kc_lock_guard_release(KcLockGuard *guard) { - if (guard->mutex != NULL) { - mtx_unlock(guard->mutex); - guard->mutex = NULL; + if (guard->mutex != NULL) + { + mtx_unlock(guard->mutex); + guard->mutex = NULL; } } - diff --git a/modules/src/kc_memory.c b/modules/src/kc_memory.c index 5cc4186..6dbbc88 100644 --- a/modules/src/kc_memory.c +++ b/modules/src/kc_memory.c @@ -622,7 +622,11 @@ */ void *raw_aligned_alloc(size_t alignment, size_t size) { +#if (KC_IS_WINDOWS) + return _aligned_malloc(size, alignment); +#else return aligned_alloc(alignment, size); +#endif } /** diff --git a/modules/src/kc_memory_dump.c b/modules/src/kc_memory_dump.c index 111e328..49e63de 100644 --- a/modules/src/kc_memory_dump.c +++ b/modules/src/kc_memory_dump.c @@ -144,7 +144,7 @@ { // 空きバッファサイズ無しのため、何もしない。 return; } - int write_size = snprintf(info->write_ptr, info->rest_size, msg); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s", msg); if (write_size > info->rest_size) { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) write_size = (info->rest_size - 1); diff --git a/modules/src/kc_memory_listener.c b/modules/src/kc_memory_listener.c index 29ff2a0..ee69a13 100644 --- a/modules/src/kc_memory_listener.c +++ b/modules/src/kc_memory_listener.c @@ -98,5 +98,5 @@ KcMemoryDump_dump(buff, sizeof(buff), entry, 16, true, true, 130); fprintf(stderr, "[ERROR] %s", buff); } - fprintf(stderr, msg); + fprintf(stderr, "%s", msg); } diff --git a/modules/src/kc_queue.c b/modules/src/kc_queue.c index 544d763..d9f2a14 100644 --- a/modules/src/kc_queue.c +++ b/modules/src/kc_queue.c @@ -9,11 +9,11 @@ #include #include #include -#include #include #include #include +#include #include #include @@ -122,7 +122,10 @@ static int KcQueue_size(struct KcQueue_ *queue) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - size_t size = info->list->size(info->list); + size_t size = 0; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + size = info->list->size(info->list); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== return (int)size; } @@ -138,7 +141,11 @@ static bool KcQueue_is_empty(struct KcQueue_ *queue) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - return info->list->is_empty(info->list); + bool is_empty = true; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + is_empty = info->list->is_empty(info->list); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== + return is_empty; } // ============================================================================= @@ -155,7 +162,11 @@ static bool KcQueue_contains(struct KcQueue_ *queue, const void *element, size_t size) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - return info->list->contains(info->list, element, size); + bool is_contains = false; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + is_contains = info->list->contains(info->list, element, size); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== + return is_contains; } // ============================================================================= @@ -227,7 +238,11 @@ static void *KcQueue_peek(struct KcQueue_ *queue, size_t *size) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - return info->list->get(info->list, 0, size); + void *value = NULL; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + value = info->list->get(info->list, 0, size); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== + return value; } // ============================================================================= diff --git a/config.mk b/config.mk index a0ac27c..f7c6e47 100644 --- a/config.mk +++ b/config.mk @@ -25,14 +25,17 @@ # for Windows CROSS_COMPILE ?=i686-w64-mingw32- LIBS ?= -lws2_32 +DEBUG_OPTIONS ?= else # For Linux ifeq ($(strip $(ARCH)),aarch64) CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector else CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector endif endif diff --git a/mk/base-cmd.mk b/mk/base-cmd.mk index f429bcf..e74c114 100644 --- a/mk/base-cmd.mk +++ b/mk/base-cmd.mk @@ -33,8 +33,13 @@ CC = $(CROSS_COMPILE)gcc CXX = $(CROSS_COMPILE)g++ + #CC = $(CROSS_COMPILE)clang #CXX = $(CROSS_COMPILE)clang++ + +#CC = $(CROSS_COMPILE)clang-16 +#CXX = $(CROSS_COMPILE)clang++16 + RANLIB = $(CROSS_COMPILE)ranlib ADDR2LINE = $(CROSS_COMPILE)addr2line AR = $(CROSS_COMPILE)ar diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 2cc417c..eebded1 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -69,7 +69,7 @@ # 値が2の場合、さらなるチェックを追加するが、規格準拠のプログラムが失敗する可能性がある。 # いくつかのチェックは、コンパイル時に実行され、コンパイラの警告として表示される。 # ------------------------------------------------------------------------------ -DEBUG_OPTIONS += -fstack-protector +# DEBUG_OPTIONS += -fstack-protector DEBUG_OPTIONS += --coverage DEBUG_OPTIONS += -g3 -ggdb DEBUG_OPTIONS += -O0 diff --git a/mk/link-a-conf.mk b/mk/link-a-conf.mk index 0e872f3..631e6d5 100644 --- a/mk/link-a-conf.mk +++ b/mk/link-a-conf.mk @@ -10,8 +10,11 @@ HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) +HEADER_FILES_WIN = $(wildcard include/win/*.h) $(wildcard include/win/*.hpp) +TOP_HEADER_FILES_WIN = $(addprefix $(TOPDIR)/include/win/,$(notdir $(HEADER_FILES_WIN))) + CLEAN_FILES += $(TOP_TARGET) -CLEAN_FILES += $(TOP_HEADER_FILES) +CLEAN_FILES += $(TOP_HEADER_FILES) $(TOP_HEADER_FILES_WIN) endif endif diff --git a/mk/link-a-rule.mk b/mk/link-a-rule.mk index d94a54a..657d858 100644 --- a/mk/link-a-rule.mk +++ b/mk/link-a-rule.mk @@ -10,6 +10,7 @@ $(TOP_TARGET): $(TARGET) ifneq ($(strip $(HEADER_FILES)),) $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ + $(CP) -f $(HEADER_FILES_WIN) $(TOPDIR)/include/win/ endif $(CP) -f $(TARGET) $(TOPDIR)/lib/ diff --git a/modules/include/kc_lock_guard.h b/modules/include/kc_lock_guard.h index 8bb70bd..ad3915f 100644 --- a/modules/include/kc_lock_guard.h +++ b/modules/include/kc_lock_guard.h @@ -7,18 +7,16 @@ #define KC_LOCK_GUARD_H #include -#include - +#include /** * LockGuard 管理構造体 */ typedef struct { - mtx_t* mutex; + mtx_t *mutex; } KcLockGuard; - /** * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 * 使用例) @@ -34,8 +32,7 @@ * } */ #define kc_lock_guard(lock) \ - for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) - + for (KcLockGuard _guard = kc_lock_guard_init(lock, __FILE__, __func__, __LINE__); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 @@ -44,15 +41,13 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex); - +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line); /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard); +void kc_lock_guard_release(KcLockGuard *guard); - -#endif // KC_LOCK_GUARD_H +#endif // KC_LOCK_GUARD_H diff --git a/modules/include/kc_memory.h b/modules/include/kc_memory.h index ffce4ed..c6ba9e9 100644 --- a/modules/include/kc_memory.h +++ b/modules/include/kc_memory.h @@ -22,12 +22,13 @@ #ifndef KC_MEMORY_H #define KC_MEMORY_H -#include +#include #include #include #include #include +#include #ifdef __cplusplus extern "C" @@ -273,8 +274,6 @@ #define calloc(nmemb, size) kc_memory_manager->calloc(nmemb, size, __FILE__, __func__, __LINE__) #define realloc(ptr, size) kc_memory_manager->realloc(ptr, size, __FILE__, __func__, __LINE__) #define free(ptr) kc_memory_manager->free(ptr) -#else -#include #endif #ifdef __cplusplus diff --git a/modules/include/kc_socket.h b/modules/include/kc_socket.h new file mode 100644 index 0000000..7d952c7 --- /dev/null +++ b/modules/include/kc_socket.h @@ -0,0 +1,291 @@ +/** + * @file kc_socket.h + * @brief ソケットモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_SOCKET_H +#define KC_SOCKET_H + +#include + +#if (KC_IS_WINDOWS) +typedef SOCKET socket_t; +#define SHUT_RD SD_RECEIVE +#define SHUT_WR SD_SEND +#define SHUT_RDWR SD_BOTH +#define sockclose(sockfd) closesocket(sockfd) +#else +#include +#include +#include +#include +#include +#include +#include +typedef int socket_t; +#define INVALID_SOCKET (-1) +#define SOCKET_ERROR (-1) +#define sockclose(sockfd) close(sockfd) +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcSocket_ + { + + /** + * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr 接続先アドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_remote_addr)(struct KcSocket_ *sock, char *addr, size_t addrlen); + + /** + * ソケットの接続先ポート番号を返します。 + * 接続先ポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_remote_port)(struct KcSocket_ *sock); + + /** + * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr ローカルアドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_local_addr)(struct KcSocket_ *sock, char *addr, size_t addlen); + + /** + * ソケットのローカルポート番号を返します。 + * ローカルポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_local_port)(struct KcSocket_ *sock); + + /** + * ソケットディスクリプタを返します。 + * 通常は、ソケットディスクリプタを直接操作しないでください。 + * + * @param sock 対象ソケット + * @return ソケットディスクリプタ + */ + socket_t (*get_socket)(struct KcSocket_ *sock); + + /** + * ソケットタイプを返します。 + * + * @param sock 対象ソケット + * @return ソケットタイプ + */ + int (*get_type)(struct KcSocket_ *sock); + + /** + * ソケットファミリを返します。 + * 実際にバインド、あるいは接続されているソケットファミリを返します。 + * + * @param sock 対象ソケット + * @return ソケットファミリ + */ + int (*get_family)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービスのアドレス情報を取得します。 + * + * @param sock 対象ソケット + * @param result アドレス情報格納用ポインタ + * @param addr アドレス + * @param service サービス + * @param is_passive サーバソケットの場合、true を指定ください。 + */ + bool (*get_addrinfo)( + struct KcSocket_ *sock, + struct addrinfo **result, + const char *addr, + const char *service, + bool is_passive); + + /** + * 指定されたアドレス、サービスにバインドします。 + * 指定されたアドレスとサービスにより、複数候補がある場合は、 + * 最初に見つかったアドレスとポートにバインドします。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス (例: "80", "http", "ssh" など) + * @return true/false (bind 成功/失敗) + */ + bool (*bind)(struct KcSocket_ *sock, const char *addr, const char *service); + + /** + * ソケットを接続待ちソケットとしてマークをつけます。 + * 保留中の接続のキュー最大長を指定します。 + * + * @param sock 対象ソケット + * @param backlog バックログ + * @return true/false (成功/失敗) + */ + bool (*listen)(struct KcSocket_ *sock, int backlog); + + /** + * ソケットへの接続を受け付けます。 + * + * @param sock 対象ソケット + * @return 受け付けたソケット + */ + struct KcSocket_ *(*accept)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービス接続します。 + * local_addr に NULL を指定した場合、自動的にローカルのアドレスが設定されます。 + * local_service に -1 を指定した場合、自動的にローカルのポート番号が割りつけられます。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス + * @param local_addr ローカルアドレス + * @param local_service ローカルサービス + * @return true/false (成功/失敗) + */ + bool (*connect)(struct KcSocket_ *sock, const char *addr, const char *service, const char *local_addr, const char *local_port); + + /** + * ソケットをクローズします。 + * + * @param sock 対象ソケット + * @return true/false (成功/失敗) + */ + bool (*close)(struct KcSocket_ *sock); + + /** + * メッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 送信されたデータサイズ + */ + ssize_t (*send)(struct KcSocket_ *sock, const char *buff, size_t size, int flags); + + /** + * メッセージを受信します。 + * 受信したメッセージは指定されたバッファに格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 読み取ったデータサイズ + */ + ssize_t (*recv)(struct KcSocket_ *sock, char *buff, size_t size, int flags); + + /** + * 指定されたアドレス、サービスにメッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param addr アドレス + * @param service サービス + * @return 送信メッセージサイズ、エラー発生時は -1 + */ + ssize_t (*sendto)( + struct KcSocket_ *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service); + + /** + * メッセージを受信します。 + * src_addr, src_service がいずれも NULL 出ない場合、 + * 送信元のアドレスとサービスが格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param src_addr 送信元アドレス格納用バッファ + * @param src_addrlen 送信元アドレス格納用バッファサイズ + * @param src_service 送信元サービス格納用バッファ + * @param src_servicelen 送信元サービス格納用バッファサイズ + * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + */ + ssize_t (*recvfrom)( + struct KcSocket_ *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen); + + /** + * TTL を設定します。 + * + * @param sock 対象ソケット + * @param val 設定する TTL + * @return true/false (成功/失敗) + */ + bool (*set_ttl)(struct KcSocket_ *sock, int val); + + /** + * マルチキャストグループに参加するために JOIN します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @param ifindex インタフェースインデックス + * @return true/false (成功/失敗) + */ + bool (*join)(struct KcSocket_ *sock, const char *addr, const char *ifname, unsigned int ifindex); + + /** + * マルチキャストグループから離脱します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @return true/false (成功/失敗) + */ + bool (*leave)(struct KcSocket_ *sock, const char *addr); + + /** + * Socket 管理情報 + */ + void *_info; + + } KcSocket; + + /** + * 指定されたタイプ、ファミリのSocket オブジェクトを構築します。 + * + * @param type タイプ(SOCK_STREAM/SOCK_DGRAM/SOCK_RAW) + * @param family ファミリ(AF_UNSPEC/AF_INET/AF_INET6) + */ + KcSocket *KcSocket_new(int type, int family); + + /** + * Socket を破棄します。 + */ + void KcSocket_delete(KcSocket *socket); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_threads.h b/modules/include/kc_threads.h new file mode 100644 index 0000000..764176f --- /dev/null +++ b/modules/include/kc_threads.h @@ -0,0 +1,98 @@ +/** + * @file kc_threads.h + * @brief スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_THREADS_H +#define KC_THREADS_H + +#include + +#if (KC_IS_WINDOWS) +#include +#else +#include +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcThread_ + { + + /** + * 指定されたスレッドが生きているか否かを返します。 + * + * @param thread 対象スレッド + * @return true/false (スレッドが生存している/していない) + */ + bool (*is_alive)(struct KcThread_ *thread); + + /** + * スレッドを開始します。 + * + * @param thread 対象スレッド + * @param args スレッドに渡す情報 + * @return true/false (スレッド起動成功/失敗) + */ + bool (*start)(struct KcThread_ *thread, void *args); + + /** + * スレッドが終了するのを待機します。 + * + * @param thread 対象スレッド + */ + bool (*join)(struct KcThread_ *thread); + + /** + * Thread 管理情報 + */ + void *_info; + + } KcThread; + + /** + * スレッドを生成します。 + * + * @param run スレッドにて実行する関数へのポインタ + */ + KcThread *KcThread_new(int (*run)(void *args)); + + /** + * Thread を破棄します。 + * + * @param thread 破棄するスレッド + */ + void KcThread_delete(KcThread *thread); + + /** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープさせるミリ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_msleep(long time, bool force); + + /** + * 指定された sec 秒 + nsec ナノ秒間現在のスレッドをスリープさせます。 + * + * @param sec スリープさせる秒数 + * @param nsec スリープさせるナノ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_sleep(long sec, long nsec, bool force); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_THREADS_H diff --git a/modules/include/kc_threads_win.h b/modules/include/kc_threads_win.h new file mode 100644 index 0000000..b40ab10 --- /dev/null +++ b/modules/include/kc_threads_win.h @@ -0,0 +1,98 @@ +/** + * @file kc_windows_thread.h + * @brief Windows 用スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_WINDOWS_THREAD_H +#define KC_WINDOWS_THREAD_H + +#include +#if (KC_IS_WINDOWS) +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#define thread_local _Thread_local + + //////////////////////////////////////////////////////////////////////// + // + // Thread + // + + enum + { + thrd_success = 0, //!< 成功 + thrd_bussy = 1, //!< busy + thrd_error = 2, //!< エラー発生 + thrd_nomem = 3, //!< メモリ不足 + thrd_timedout = 4 //!< タイムアウト発生 + }; + + typedef struct + { + HANDLE handle; + DWORD thread_id; + } thrd_t; + + typedef int (*thrd_start_t)(void *); + int thrd_create(thrd_t *thr, thrd_start_t func, void *arg); + int thrd_join(thrd_t thr, int *res); + int thrd_detach(thrd_t thr); + thrd_t thrd_current(void); + int thrd_equal(thrd_t lhs, thrd_t rhs); + int thrd_sleep(const struct timespec *duration, struct timespec *remaining); + + //////////////////////////////////////////////////////////////////////// + // + // mutex + // + + enum + { + mtx_plain = 0, //!< 通常 + mtx_recursive = 1, //!< 再帰 + mtx_timed = 2 //!< timed + }; + + typedef struct + { + CRITICAL_SECTION cs; + int type; + } mtx_t; + + int mtx_init(mtx_t *mtx, int type); + void mtx_destroy(mtx_t *mtx); + int mtx_lock(mtx_t *mtx); + int mtx_unlock(mtx_t *mtx); + + //////////////////////////////////////////////////////////////////////// + // + // cond + // + + typedef struct + { + CONDITION_VARIABLE cond; + } cnd_t; + + int cnd_init(cnd_t *cond); + int cnd_signal(cnd_t *cond); + int cnd_broadcast(cnd_t *cond); + int cnd_wait(cnd_t *cond, mtx_t *mtx); + int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts); + void cnd_destroy(cnd_t *cond); + +#endif // (KC_IS_WINDOWS) +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_windows.h b/modules/include/kc_windows.h index ee70898..6e1c659 100644 --- a/modules/include/kc_windows.h +++ b/modules/include/kc_windows.h @@ -37,9 +37,9 @@ #endif // よく利用されるヘッダファイルをインクルードする -#include #include #include +#include #ifdef _MSC_VER #pragma comment(lib, "ws2_32.lib") #endif diff --git a/modules/src/kc_assert.c b/modules/src/kc_assert.c index 371fc8f..7cd4ecc 100644 --- a/modules/src/kc_assert.c +++ b/modules/src/kc_assert.c @@ -6,9 +6,9 @@ #include #include #include -#include #include #include +#include // プロトタイプ宣言 static void assert_default_handler(const char *msg); diff --git a/modules/src/kc_env.c b/modules/src/kc_env.c index 0675c73..95f185c 100644 --- a/modules/src/kc_env.c +++ b/modules/src/kc_env.c @@ -50,7 +50,7 @@ char *result = NULL; #if (KC_IS_WINDOWS) static char buff[KC_ENV_BUFFER_MAX]; - DWORD ret = GetEnvironmentVarable(key_name, (LPSTR)buff, sizeof(buff)); + DWORD ret = GetEnvironmentVariable(key_name, (LPSTR)buff, sizeof(buff)); if (ret != 0) { result = buff; diff --git a/modules/src/kc_list_array.c b/modules/src/kc_list_array.c index 246ed14..f29fc08 100644 --- a/modules/src/kc_list_array.c +++ b/modules/src/kc_list_array.c @@ -3,10 +3,14 @@ * @brief 配列リストモジュール * @copyright 2003 - 2023 Nomura Kei */ +#include #if defined(__GNUC__) #define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) #define qsort_s qsort_r #endif +#endif // defined(__GNUC__) + #include #include #include @@ -17,6 +21,7 @@ #include #include #include + /** * KcArrayList 管理情報 */ @@ -306,7 +311,11 @@ * @param context コンテキスト(KcListSortInfo) * @return 比較結果 */ +#if (KC_IS_WINDOWS) +static int KcArrayList_comparator(void *context, const void *x, const void *y) +#else static int KcArrayList_comparator(const void *x, const void *y, void *context) +#endif { KcListSortInfo *sort_info = (KcListSortInfo *)context; int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); diff --git a/modules/src/kc_list_linked.c b/modules/src/kc_list_linked.c index 30575b9..47edbe4 100644 --- a/modules/src/kc_list_linked.c +++ b/modules/src/kc_list_linked.c @@ -3,10 +3,14 @@ * @brief Linked リストモジュール * @copyright 2003 - 2023 Nomura Kei */ +#include #if defined(__GNUC__) #define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) #define qsort_s qsort_r #endif +#endif // defined(__GNUC__) + #include #include #include @@ -18,6 +22,13 @@ #include #include +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) +#define qsort_s qsort_r +#endif // KC_IS_WINDOWS +#endif // defined(__GNUC__) + /** * KcLinkedList Entry 情報 */ @@ -291,7 +302,11 @@ * @param context コンテキスト(KcListSortInfo) * @return 比較結果 */ +#if (KC_IS_WINDOWS) +static int KcLinkedList_comparator(void *context, const void *x, const void *y) +#else static int KcLinkedList_comparator(const void *x, const void *y, void *context) +#endif // KC_IS_WINDOWS { KcListSortInfo *sort_info = (KcListSortInfo *)context; const KcLinkedListEntry **entry_x = (const KcLinkedListEntry **)x; diff --git a/modules/src/kc_lock_guard.c b/modules/src/kc_lock_guard.c index 6ef68e5..648a153 100644 --- a/modules/src/kc_lock_guard.c +++ b/modules/src/kc_lock_guard.c @@ -7,7 +7,6 @@ #include - /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 * ロックに成功するとロック管理用のオブジェクトを返します。 @@ -15,36 +14,37 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex) +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line) { - KcLockGuard guard = { .mutex = mutex }; + KcLockGuard guard = {.mutex = mutex}; if (mutex == NULL) { errno = EINVAL; perror("kc_lock_guard_init: (mutex = NULL)"); + fprintf(stderr, "%s:%s:%d kc_lock_guard_init : mutex = NULL\n", file, func, line); return guard; } + errno = 0; int ret = mtx_lock(mutex); if (ret != thrd_success) { - perror("kc_lock_guard : mtx_lock error"); + fprintf(stderr, "%s:%s:%d kc_lock_guard : mtx_lock error\n", file, func, line); guard.mutex = NULL; } return guard; } - /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard) +void kc_lock_guard_release(KcLockGuard *guard) { - if (guard->mutex != NULL) { - mtx_unlock(guard->mutex); - guard->mutex = NULL; + if (guard->mutex != NULL) + { + mtx_unlock(guard->mutex); + guard->mutex = NULL; } } - diff --git a/modules/src/kc_memory.c b/modules/src/kc_memory.c index 5cc4186..6dbbc88 100644 --- a/modules/src/kc_memory.c +++ b/modules/src/kc_memory.c @@ -622,7 +622,11 @@ */ void *raw_aligned_alloc(size_t alignment, size_t size) { +#if (KC_IS_WINDOWS) + return _aligned_malloc(size, alignment); +#else return aligned_alloc(alignment, size); +#endif } /** diff --git a/modules/src/kc_memory_dump.c b/modules/src/kc_memory_dump.c index 111e328..49e63de 100644 --- a/modules/src/kc_memory_dump.c +++ b/modules/src/kc_memory_dump.c @@ -144,7 +144,7 @@ { // 空きバッファサイズ無しのため、何もしない。 return; } - int write_size = snprintf(info->write_ptr, info->rest_size, msg); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s", msg); if (write_size > info->rest_size) { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) write_size = (info->rest_size - 1); diff --git a/modules/src/kc_memory_listener.c b/modules/src/kc_memory_listener.c index 29ff2a0..ee69a13 100644 --- a/modules/src/kc_memory_listener.c +++ b/modules/src/kc_memory_listener.c @@ -98,5 +98,5 @@ KcMemoryDump_dump(buff, sizeof(buff), entry, 16, true, true, 130); fprintf(stderr, "[ERROR] %s", buff); } - fprintf(stderr, msg); + fprintf(stderr, "%s", msg); } diff --git a/modules/src/kc_queue.c b/modules/src/kc_queue.c index 544d763..d9f2a14 100644 --- a/modules/src/kc_queue.c +++ b/modules/src/kc_queue.c @@ -9,11 +9,11 @@ #include #include #include -#include #include #include #include +#include #include #include @@ -122,7 +122,10 @@ static int KcQueue_size(struct KcQueue_ *queue) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - size_t size = info->list->size(info->list); + size_t size = 0; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + size = info->list->size(info->list); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== return (int)size; } @@ -138,7 +141,11 @@ static bool KcQueue_is_empty(struct KcQueue_ *queue) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - return info->list->is_empty(info->list); + bool is_empty = true; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + is_empty = info->list->is_empty(info->list); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== + return is_empty; } // ============================================================================= @@ -155,7 +162,11 @@ static bool KcQueue_contains(struct KcQueue_ *queue, const void *element, size_t size) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - return info->list->contains(info->list, element, size); + bool is_contains = false; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + is_contains = info->list->contains(info->list, element, size); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== + return is_contains; } // ============================================================================= @@ -227,7 +238,11 @@ static void *KcQueue_peek(struct KcQueue_ *queue, size_t *size) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - return info->list->get(info->list, 0, size); + void *value = NULL; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + value = info->list->get(info->list, 0, size); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== + return value; } // ============================================================================= diff --git a/modules/src/kc_socket.c b/modules/src/kc_socket.c new file mode 100644 index 0000000..cb67996 --- /dev/null +++ b/modules/src/kc_socket.c @@ -0,0 +1,753 @@ +/** + * @file kc_socket.c + * @brief ソケットモジュール + * @copyright 2003 - 2024 Nomura Kei + */ +#include +#include + +#include +#include +#include +#include + +/** + * KcSocket 管理情報 + */ +typedef struct +{ + socket_t sock_fd; //!< ソケットディスクリプタ + int sock_type; //!< ソケットタイプ + int sock_family; //!< ソケットファミリ + struct sockaddr_storage remote_addr; //!< リモートアドレス + struct sockaddr_storage local_addr; //!< ローカルアドレス + socklen_t remote_addrlen; //!< リモートアドレス長 + socklen_t local_addrlen; //!< ローカルアドレス帳 +} KcSocketInfo; + +/** + * アドレス情報 + */ +typedef struct +{ + const char *addr; //!< アドレス + const char *service; //!< サービス +} KcSocketAddress; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static void Socket_setup(void); +static void Socket_cleanup(void); +static const char *KcSocket_get_remote_addr(KcSocket *sock, char *addr, size_t addrlen); +static int KcSocket_get_remote_port(KcSocket *sock); +static const char *KcSocket_get_local_addr(KcSocket *sock, char *addr, size_t addrlen); +static int KcSocket_get_local_port(KcSocket *sock); +static socket_t KcSocket_get_socket(KcSocket *sock); +static int KcSocket_get_type(KcSocket *sock); +static int KcSocket_get_family(KcSocket *sock); +static bool KcSocket_get_addrinfo( + KcSocket *sock, struct addrinfo **result, + const char *addr, const char *service, bool is_passive); +static bool KcSocket_bind(KcSocket *sock, const char *addr, const char *service); +static bool KcSocket_listen(KcSocket *sock, int backlog); +static KcSocket *KcSocket_accept(KcSocket *sock); +static bool KcSocket_connect( + KcSocket *sock, const char *addr, const char *service, + const char *local_addr, const char *local_service); +static bool KcSocket_close(KcSocket *sock); +static ssize_t KcSocket_send(KcSocket *sock, const char *buff, size_t size, int flags); +static ssize_t KcSocket_recv(KcSocket *sock, char *buff, size_t size, int flags); +static ssize_t KcSocket_sendto( + KcSocket *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service); +static ssize_t KcSocket_recvfrom( + KcSocket *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen); +static bool KcSocket_set_ttl(KcSocket *sock, int val); +static bool KcSocket_join(KcSocket *sock, const char *addr, const char *ifname, unsigned int ifindex); +static bool KcSocket_leave(KcSocket *sock, const char *addr); + +static bool KcSocket_addrinfo_bind_and_connect( + KcSocket *sock, struct addrinfo *bind_addrinfo, struct addrinfo *conn_addrinfo); +static bool KcSocket_addrinfo_connect(KcSocket *sock, struct addrinfo *conn_addrinfo, socket_t sockfd); + +/** + * ソケットのセットアップをします。 + */ +static void Socket_setup(void) +{ + static bool is_init = false; + if (!is_init) + { +#if (KC_IS_WINDOWS) + WSADATA wsa_data; + WSAStartup(MAKEWORD(2, 0), &wsa_data); +#endif + atexit(Socket_cleanup); + is_init = true; + } +} + +/** + * ソケットライブラリのクリーンアップをします。 + */ +static void Socket_cleanup(void) +{ +#if (KC_IS_WINDOWS) + WSACleanup(); +#endif +} + +// ============================================================================= +// new +// ============================================================================= +/** + * 指定されたタイプ、ファミリのSocket オブジェクトを構築します。 + * + * @param type タイプ(SOCK_STREAM/SOCK_DGRAM/SOCK_RAW) + * @param family ファミリ(AF_UNSPEC/AF_INET/AF_INET6) + */ +KcSocket *KcSocket_new(int type, int family) +{ + // KcSocket の管理構造 + // +----------------+ + // | KcSocket | + // | ... | + // | _info -----------+ + // +----------------+ | + // | |<--+ + // +----------------+ + Socket_setup(); + KcSocket *sock = (KcSocket *)malloc(sizeof(KcSocket) + sizeof(KcSocketInfo)); + if (sock != NULL) + { + sock->get_remote_addr = KcSocket_get_remote_addr; + sock->get_remote_port = KcSocket_get_remote_port; + sock->get_local_addr = KcSocket_get_local_addr; + sock->get_local_port = KcSocket_get_local_port; + sock->get_socket = KcSocket_get_socket; + sock->get_type = KcSocket_get_type; + sock->get_family = KcSocket_get_family; + sock->get_addrinfo = KcSocket_get_addrinfo; // for local + sock->bind = KcSocket_bind; + sock->listen = KcSocket_listen; + sock->accept = KcSocket_accept; + sock->connect = KcSocket_connect; + sock->close = KcSocket_close; + sock->send = KcSocket_send; + sock->recv = KcSocket_recv; + sock->sendto = KcSocket_sendto; + sock->recvfrom = KcSocket_recvfrom; + sock->set_ttl = KcSocket_set_ttl; + sock->join = KcSocket_join; + sock->leave = KcSocket_leave; + sock->_info = (sock + 1); + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + info->sock_fd = INVALID_SOCKET; + info->sock_type = type; + info->sock_family = family; + info->remote_addrlen = 0; + info->local_addrlen = 0; + } + return sock; +} + +// ============================================================================= +// delete +// ============================================================================= +/** + * Socket を破棄します。 + */ +void KcSocket_delete(KcSocket *socket) +{ + if (socket) + { + if (((KcSocketInfo *)socket->_info)->sock_fd != INVALID_SOCKET) + { + socket->close(socket); + } + free(socket); + } +} + +// ============================================================================= +// get_remote_addr +// ============================================================================= +/** + * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr 接続先アドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ +static const char *KcSocket_get_remote_addr(KcSocket *sock, char *addr, size_t addrlen) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->remote_addrlen) + { + int ret = getnameinfo( + (const struct sockaddr *)&info->remote_addr, + info->remote_addrlen, + addr, addrlen, NULL, 0, NI_NUMERICHOST); + if (ret == 0) + { + return addr; + } + } + return NULL; +} + +/** + * ソケットの接続先ポート番号を返します。 + * 接続先ポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ +static int KcSocket_get_remote_port(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->remote_addrlen) + { + char tmp_port[NI_MAXSERV]; + int ret = getnameinfo( + (const struct sockaddr *)&info->remote_addr, + info->remote_addrlen, + NULL, 0, tmp_port, sizeof(tmp_port), NI_NUMERICSERV); + if (ret == 0) + { + return atoi(tmp_port); + } + } + return -1; +} + +/** + * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr ローカルアドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ +static const char *KcSocket_get_local_addr(KcSocket *sock, char *addr, size_t addrlen) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->local_addrlen) + { + int ret = getnameinfo( + (const struct sockaddr *)&info->local_addr, + info->remote_addrlen, + addr, addrlen, NULL, 0, NI_NUMERICHOST); + if (ret == 0) + { + return addr; + } + } + return NULL; +} + +/** + * ソケットのローカルポート番号を返します。 + * ローカルポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ +static int KcSocket_get_local_port(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->local_addrlen) + { + char tmp_port[NI_MAXSERV]; + int ret = getnameinfo( + (const struct sockaddr *)&info->local_addr, + info->remote_addrlen, + NULL, 0, tmp_port, sizeof(tmp_port), NI_NUMERICSERV); + if (ret == 0) + { + return atoi(tmp_port); + } + } + return -1; +} + +/** + * ソケットディスクリプタを返します。 + * 通常は、ソケットディスクリプタを直接操作しないでください。 + * + * @param sock 対象ソケット + * @return ソケットディスクリプタ + */ +static socket_t KcSocket_get_socket(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + return info->sock_fd; +} + +/** + * ソケットタイプを返します。 + * + * @param sock 対象ソケット + * @return ソケットタイプ + */ +static int KcSocket_get_type(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + return info->sock_type; +} + +/** + * ソケットファミリを返します。 + * ソケット接続、バインド時には、実際にバインド、 + * あるいは接続されているソケットファミリを返します。 + * + * @param sock 対象ソケット + * @return ソケットファミリ + */ +static int KcSocket_get_family(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int result_family = info->sock_family; + if (info->sock_fd != INVALID_SOCKET) + { + struct sockaddr_storage ss; + socklen_t len = sizeof(struct sockaddr_storage); + if (getsockname(info->sock_fd, (struct sockaddr *)&ss, &len) == 0) + { + result_family = ss.ss_family; + } + } + return result_family; +} + +/** + * 指定されたアドレス、サービスのアドレス情報を取得します。 + * + * @param sock 対象ソケット + * @param result アドレス情報格納用ポインタ + * @param addr アドレス + * @param service サービス + * @param is_passive サーバソケットの場合、true を指定ください。 + */ +static bool KcSocket_get_addrinfo( + KcSocket *sock, struct addrinfo **result, + const char *addr, const char *service, bool is_passive) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + + struct addrinfo hints; + memset(&hints, 0x00, sizeof(struct addrinfo)); + hints.ai_socktype = info->sock_type; + hints.ai_family = info->sock_family; + if (is_passive) + { + hints.ai_flags = AI_PASSIVE; + } + int ret = getaddrinfo(addr, service, &hints, result); + return (ret == 0); +} + +/** + * 指定されたアドレス、サービスにバインドします。 + * 指定されたアドレスとサービスにより、複数候補がある場合は、 + * 最初に見つかったアドレスとポートにバインドします。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス (例: "80", "http", "ssh" など) + * @return true/false (bind 成功/失敗) + */ +static bool KcSocket_bind(KcSocket *sock, const char *addr, const char *service) +{ + struct addrinfo *bind_addr; + bool is_success = sock->get_addrinfo(sock, &bind_addr, addr, service, true); + if (is_success) + { + is_success = KcSocket_addrinfo_bind_and_connect(sock, bind_addr, NULL); + freeaddrinfo(bind_addr); + } + return is_success; +} + +/** + * ソケットを接続待ちソケットとしてマークをつけます。 + * 保留中の接続のキュー最大長を指定します。 + * + * @param sock 対象ソケット + * @param backlog バックログ + * @return true/false (成功/失敗) + */ +static bool KcSocket_listen(KcSocket *sock, int backlog) +{ + bool result = false; + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->sock_fd != INVALID_SOCKET) + { + int ret = listen(info->sock_fd, backlog); + result = (ret != SOCKET_ERROR); + } + return result; +} + +/** + * ソケットへの接続を受け付けます。 + * + * @param sock 対象ソケット + * @return 受け付けたソケット(失敗時NULL) + */ +static KcSocket *KcSocket_accept(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + KcSocket *client = NULL; + if (info->sock_fd != INVALID_SOCKET) + { + client = KcSocket_new(info->sock_type, info->sock_family); + if (client != NULL) + { + KcSocketInfo *cinfo = (KcSocketInfo *)client->_info; + cinfo->remote_addrlen = sizeof(struct sockaddr_storage); + cinfo->sock_fd = accept(info->sock_fd, (struct sockaddr *)&cinfo->remote_addr, &cinfo->remote_addrlen); + if (cinfo->sock_fd != INVALID_SOCKET) + { + memcpy(&cinfo->local_addr, &info->local_addr, info->local_addrlen); + cinfo->local_addrlen = info->local_addrlen; + cinfo->sock_type = SOCK_STREAM; + cinfo->sock_family = cinfo->remote_addr.ss_family; + } + else + { + KcSocket_delete(client); + client = NULL; + } + } + } + return client; +} + +/** + * 指定されたアドレス、サービス接続します。 + * local_addr に NULL を指定した場合、自動的にローカルのアドレス、サービスが設定されます。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス + * @param local_addr ローカルアドレス + * @param local_service ローカルサービス + * @return true/false (成功/失敗) + */ +static bool KcSocket_connect( + KcSocket *sock, const char *addr, const char *service, + const char *local_addr, const char *local_service) +{ + // 接続先アドレス情報取得 + struct addrinfo *conn_addr = NULL; + if (!sock->get_addrinfo(sock, &conn_addr, addr, service, false)) + { + return false; + } + + // バインドアドレス情報取得 + bool is_success = false; + struct addrinfo *bind_addr = NULL; + if (local_addr != NULL) + { // bind が必要 + is_success = sock->get_addrinfo(sock, &bind_addr, local_addr, local_service, true); + if (is_success) + { + is_success = KcSocket_addrinfo_bind_and_connect(sock, bind_addr, conn_addr); + freeaddrinfo(bind_addr); + } + } + else + { + is_success = KcSocket_addrinfo_connect(sock, conn_addr, INVALID_SOCKET); + } + freeaddrinfo(conn_addr); + return is_success; +} + +/** + * ソケットをクローズします。 + * + * @param sock 対象ソケット + * @return true/false (成功/失敗) + */ +static bool KcSocket_close(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + + if (info->sock_family == SOCK_STREAM) + { // TCP の場合は、出力を閉じて、届いているデータを読み取ってから close する。 + int ret = shutdown(info->sock_fd, SHUT_WR); + if (ret == 0) + { + int read_size; + char buff[1024]; + do + { // 届いているデータを全て読み取る + read_size = recv(info->sock_fd, buff, sizeof(buff), 0); + } while (read_size > 0); + } + } + sockclose(info->sock_fd); + return true; +} + +/** + * メッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 送信されたデータサイズ + */ +static ssize_t KcSocket_send(KcSocket *sock, const char *buff, size_t size, int flags) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + ssize_t write_size = 0; + do + { + errno = 0; + write_size = send(info->sock_fd, buff, size, flags); + } while ((write_size < 0) && (errno == EINTR)); + return write_size; +} + +/** + * メッセージを受信します。 + * 受信したメッセージは指定されたバッファに格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 読み取ったデータサイズ + */ +static ssize_t KcSocket_recv(KcSocket *sock, char *buff, size_t size, int flags) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + ssize_t read_size; + do + { + errno = 0; + read_size = recv(info->sock_fd, buff, size, flags); + } while ((read_size < 0) && (errno == EINTR)); + return read_size; +} + +/** + * 指定されたアドレス、サービスにメッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param addr アドレス + * @param service サービス + * @return 送信メッセージサイズ、エラー発生時は -1 + */ +static ssize_t KcSocket_sendto( + KcSocket *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service) +{ + (void)sock; + (void)buff; + (void)size; + (void)flags; + (void)addr; + (void)service; + return 0; +} + +/** + * メッセージを受信します。 + * src_addr, src_service がいずれも NULL 出ない場合、 + * 送信元のアドレスとサービスが格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param src_addr 送信元アドレス格納用バッファ + * @param src_addrlen 送信元アドレス格納用バッファサイズ + * @param src_service 送信元サービス格納用バッファ + * @param src_servicelen 送信元サービス格納用バッファサイズ + * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + */ +static ssize_t KcSocket_recvfrom( + KcSocket *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen) +{ + (void)sock; + (void)buff; + (void)size; + (void)flags; + (void)src_addr; + (void)src_addrlen; + (void)src_service; + (void)src_servicelen; + return 0; +} + +/** + * TTL を設定します。 + * + * @param sock 対象ソケット + * @param val 設定する TTL + * @return true/false (成功/失敗) + */ +static bool KcSocket_set_ttl(KcSocket *sock, int val) +{ + (void)sock; + (void)val; + return true; +} + +/** + * マルチキャストグループに参加するために JOIN します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @param ifindex インタフェースインデックス + * @return true/false (成功/失敗) + */ +static bool KcSocket_join( + KcSocket *sock, const char *addr, const char *ifname, unsigned int ifindex) +{ + (void)sock; + (void)addr; + (void)ifname; + (void)ifindex; + return true; +} + +/** + * マルチキャストグループから離脱します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @return true/false (成功/失敗) + */ +static bool KcSocket_leave( + KcSocket *sock, const char *addr) +{ + (void)sock; + (void)addr; + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// [内部関数] +// + +/** + * 指定された接続アドレス情報をもとにソケットの生成、接続を試みます。 + * 接続済みソケット sockfd が有効な場合、sockfd を用いて接続します。 + * + * @param sock 対象ソケット + * @param conn_addrinfo 接続アドレス情報 + * @param sockfd 接続済みソケット + * @return true/false (接続成功/失敗) + */ +static bool KcSocket_addrinfo_connect(KcSocket *sock, struct addrinfo *conn_addrinfo, socket_t sockfd) +{ + bool is_success = false; + socket_t tmp_sock = sockfd; + for (struct addrinfo *rp = conn_addrinfo; rp != NULL; rp = rp->ai_next) + { + if (sockfd == INVALID_SOCKET) + { // sockfd が無効の場合、ソケットを生成する。 + tmp_sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (tmp_sock == INVALID_SOCKET) + { + continue; + } + } + + int ret = connect(tmp_sock, rp->ai_addr, rp->ai_addrlen); + is_success = (ret != SOCKET_ERROR); + if (is_success) + { + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + info->sock_fd = tmp_sock; + info->remote_addrlen = rp->ai_addrlen; + memcpy(&info->remote_addr, rp->ai_addr, rp->ai_addrlen); + break; + } + + if (sockfd != INVALID_SOCKET) + { // sockfd が無効の場合、一時的に生成したソケットをクローズする。 + sockclose(tmp_sock); + } + } + return is_success; +} + +/** + * 指定された接続アドレス情報をもとにソケットの生成、バインドを試みます。 + * conn_addrinfo が NULL でない場合、バインド後に接続を試みます。 + * + * @param sock 対象ソケット + * @param bind_addrinfo バインドアドレス情報 + * @param conn_addrinfo 接続アドレス情報 + * @return true/false (接続成功/失敗) + */ +static bool KcSocket_addrinfo_bind_and_connect( + KcSocket *sock, struct addrinfo *bind_addrinfo, struct addrinfo *conn_addrinfo) +{ + int ret; + bool is_success = false; + + for (struct addrinfo *rp = bind_addrinfo; rp != NULL; rp = rp->ai_next) + { // sokcet : ソケット生成 + socket_t tmp_sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (tmp_sock == INVALID_SOCKET) + { + continue; + } + + // bind + const int on = 1; + setsockopt(tmp_sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on)); + ret = bind(tmp_sock, rp->ai_addr, rp->ai_addrlen); + is_success = (ret != SOCKET_ERROR); + if (!is_success) + { // bind 失敗したので次へ + sockclose(tmp_sock); + continue; + } + + // connect + if (conn_addrinfo) + { + is_success = KcSocket_addrinfo_connect(sock, conn_addrinfo, tmp_sock); + if (!is_success) + { // connect 失敗したので次へ + sockclose(tmp_sock); + continue; + } + } + + // bind または、bind と connect 成功 + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + info->sock_fd = tmp_sock; + info->local_addrlen = rp->ai_addrlen; + memcpy(&info->local_addr, rp->ai_addr, rp->ai_addrlen); + break; + } + return is_success; +} diff --git a/config.mk b/config.mk index a0ac27c..f7c6e47 100644 --- a/config.mk +++ b/config.mk @@ -25,14 +25,17 @@ # for Windows CROSS_COMPILE ?=i686-w64-mingw32- LIBS ?= -lws2_32 +DEBUG_OPTIONS ?= else # For Linux ifeq ($(strip $(ARCH)),aarch64) CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector else CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector endif endif diff --git a/mk/base-cmd.mk b/mk/base-cmd.mk index f429bcf..e74c114 100644 --- a/mk/base-cmd.mk +++ b/mk/base-cmd.mk @@ -33,8 +33,13 @@ CC = $(CROSS_COMPILE)gcc CXX = $(CROSS_COMPILE)g++ + #CC = $(CROSS_COMPILE)clang #CXX = $(CROSS_COMPILE)clang++ + +#CC = $(CROSS_COMPILE)clang-16 +#CXX = $(CROSS_COMPILE)clang++16 + RANLIB = $(CROSS_COMPILE)ranlib ADDR2LINE = $(CROSS_COMPILE)addr2line AR = $(CROSS_COMPILE)ar diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 2cc417c..eebded1 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -69,7 +69,7 @@ # 値が2の場合、さらなるチェックを追加するが、規格準拠のプログラムが失敗する可能性がある。 # いくつかのチェックは、コンパイル時に実行され、コンパイラの警告として表示される。 # ------------------------------------------------------------------------------ -DEBUG_OPTIONS += -fstack-protector +# DEBUG_OPTIONS += -fstack-protector DEBUG_OPTIONS += --coverage DEBUG_OPTIONS += -g3 -ggdb DEBUG_OPTIONS += -O0 diff --git a/mk/link-a-conf.mk b/mk/link-a-conf.mk index 0e872f3..631e6d5 100644 --- a/mk/link-a-conf.mk +++ b/mk/link-a-conf.mk @@ -10,8 +10,11 @@ HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) +HEADER_FILES_WIN = $(wildcard include/win/*.h) $(wildcard include/win/*.hpp) +TOP_HEADER_FILES_WIN = $(addprefix $(TOPDIR)/include/win/,$(notdir $(HEADER_FILES_WIN))) + CLEAN_FILES += $(TOP_TARGET) -CLEAN_FILES += $(TOP_HEADER_FILES) +CLEAN_FILES += $(TOP_HEADER_FILES) $(TOP_HEADER_FILES_WIN) endif endif diff --git a/mk/link-a-rule.mk b/mk/link-a-rule.mk index d94a54a..657d858 100644 --- a/mk/link-a-rule.mk +++ b/mk/link-a-rule.mk @@ -10,6 +10,7 @@ $(TOP_TARGET): $(TARGET) ifneq ($(strip $(HEADER_FILES)),) $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ + $(CP) -f $(HEADER_FILES_WIN) $(TOPDIR)/include/win/ endif $(CP) -f $(TARGET) $(TOPDIR)/lib/ diff --git a/modules/include/kc_lock_guard.h b/modules/include/kc_lock_guard.h index 8bb70bd..ad3915f 100644 --- a/modules/include/kc_lock_guard.h +++ b/modules/include/kc_lock_guard.h @@ -7,18 +7,16 @@ #define KC_LOCK_GUARD_H #include -#include - +#include /** * LockGuard 管理構造体 */ typedef struct { - mtx_t* mutex; + mtx_t *mutex; } KcLockGuard; - /** * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 * 使用例) @@ -34,8 +32,7 @@ * } */ #define kc_lock_guard(lock) \ - for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) - + for (KcLockGuard _guard = kc_lock_guard_init(lock, __FILE__, __func__, __LINE__); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 @@ -44,15 +41,13 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex); - +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line); /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard); +void kc_lock_guard_release(KcLockGuard *guard); - -#endif // KC_LOCK_GUARD_H +#endif // KC_LOCK_GUARD_H diff --git a/modules/include/kc_memory.h b/modules/include/kc_memory.h index ffce4ed..c6ba9e9 100644 --- a/modules/include/kc_memory.h +++ b/modules/include/kc_memory.h @@ -22,12 +22,13 @@ #ifndef KC_MEMORY_H #define KC_MEMORY_H -#include +#include #include #include #include #include +#include #ifdef __cplusplus extern "C" @@ -273,8 +274,6 @@ #define calloc(nmemb, size) kc_memory_manager->calloc(nmemb, size, __FILE__, __func__, __LINE__) #define realloc(ptr, size) kc_memory_manager->realloc(ptr, size, __FILE__, __func__, __LINE__) #define free(ptr) kc_memory_manager->free(ptr) -#else -#include #endif #ifdef __cplusplus diff --git a/modules/include/kc_socket.h b/modules/include/kc_socket.h new file mode 100644 index 0000000..7d952c7 --- /dev/null +++ b/modules/include/kc_socket.h @@ -0,0 +1,291 @@ +/** + * @file kc_socket.h + * @brief ソケットモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_SOCKET_H +#define KC_SOCKET_H + +#include + +#if (KC_IS_WINDOWS) +typedef SOCKET socket_t; +#define SHUT_RD SD_RECEIVE +#define SHUT_WR SD_SEND +#define SHUT_RDWR SD_BOTH +#define sockclose(sockfd) closesocket(sockfd) +#else +#include +#include +#include +#include +#include +#include +#include +typedef int socket_t; +#define INVALID_SOCKET (-1) +#define SOCKET_ERROR (-1) +#define sockclose(sockfd) close(sockfd) +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcSocket_ + { + + /** + * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr 接続先アドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_remote_addr)(struct KcSocket_ *sock, char *addr, size_t addrlen); + + /** + * ソケットの接続先ポート番号を返します。 + * 接続先ポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_remote_port)(struct KcSocket_ *sock); + + /** + * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr ローカルアドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_local_addr)(struct KcSocket_ *sock, char *addr, size_t addlen); + + /** + * ソケットのローカルポート番号を返します。 + * ローカルポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_local_port)(struct KcSocket_ *sock); + + /** + * ソケットディスクリプタを返します。 + * 通常は、ソケットディスクリプタを直接操作しないでください。 + * + * @param sock 対象ソケット + * @return ソケットディスクリプタ + */ + socket_t (*get_socket)(struct KcSocket_ *sock); + + /** + * ソケットタイプを返します。 + * + * @param sock 対象ソケット + * @return ソケットタイプ + */ + int (*get_type)(struct KcSocket_ *sock); + + /** + * ソケットファミリを返します。 + * 実際にバインド、あるいは接続されているソケットファミリを返します。 + * + * @param sock 対象ソケット + * @return ソケットファミリ + */ + int (*get_family)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービスのアドレス情報を取得します。 + * + * @param sock 対象ソケット + * @param result アドレス情報格納用ポインタ + * @param addr アドレス + * @param service サービス + * @param is_passive サーバソケットの場合、true を指定ください。 + */ + bool (*get_addrinfo)( + struct KcSocket_ *sock, + struct addrinfo **result, + const char *addr, + const char *service, + bool is_passive); + + /** + * 指定されたアドレス、サービスにバインドします。 + * 指定されたアドレスとサービスにより、複数候補がある場合は、 + * 最初に見つかったアドレスとポートにバインドします。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス (例: "80", "http", "ssh" など) + * @return true/false (bind 成功/失敗) + */ + bool (*bind)(struct KcSocket_ *sock, const char *addr, const char *service); + + /** + * ソケットを接続待ちソケットとしてマークをつけます。 + * 保留中の接続のキュー最大長を指定します。 + * + * @param sock 対象ソケット + * @param backlog バックログ + * @return true/false (成功/失敗) + */ + bool (*listen)(struct KcSocket_ *sock, int backlog); + + /** + * ソケットへの接続を受け付けます。 + * + * @param sock 対象ソケット + * @return 受け付けたソケット + */ + struct KcSocket_ *(*accept)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービス接続します。 + * local_addr に NULL を指定した場合、自動的にローカルのアドレスが設定されます。 + * local_service に -1 を指定した場合、自動的にローカルのポート番号が割りつけられます。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス + * @param local_addr ローカルアドレス + * @param local_service ローカルサービス + * @return true/false (成功/失敗) + */ + bool (*connect)(struct KcSocket_ *sock, const char *addr, const char *service, const char *local_addr, const char *local_port); + + /** + * ソケットをクローズします。 + * + * @param sock 対象ソケット + * @return true/false (成功/失敗) + */ + bool (*close)(struct KcSocket_ *sock); + + /** + * メッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 送信されたデータサイズ + */ + ssize_t (*send)(struct KcSocket_ *sock, const char *buff, size_t size, int flags); + + /** + * メッセージを受信します。 + * 受信したメッセージは指定されたバッファに格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 読み取ったデータサイズ + */ + ssize_t (*recv)(struct KcSocket_ *sock, char *buff, size_t size, int flags); + + /** + * 指定されたアドレス、サービスにメッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param addr アドレス + * @param service サービス + * @return 送信メッセージサイズ、エラー発生時は -1 + */ + ssize_t (*sendto)( + struct KcSocket_ *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service); + + /** + * メッセージを受信します。 + * src_addr, src_service がいずれも NULL 出ない場合、 + * 送信元のアドレスとサービスが格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param src_addr 送信元アドレス格納用バッファ + * @param src_addrlen 送信元アドレス格納用バッファサイズ + * @param src_service 送信元サービス格納用バッファ + * @param src_servicelen 送信元サービス格納用バッファサイズ + * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + */ + ssize_t (*recvfrom)( + struct KcSocket_ *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen); + + /** + * TTL を設定します。 + * + * @param sock 対象ソケット + * @param val 設定する TTL + * @return true/false (成功/失敗) + */ + bool (*set_ttl)(struct KcSocket_ *sock, int val); + + /** + * マルチキャストグループに参加するために JOIN します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @param ifindex インタフェースインデックス + * @return true/false (成功/失敗) + */ + bool (*join)(struct KcSocket_ *sock, const char *addr, const char *ifname, unsigned int ifindex); + + /** + * マルチキャストグループから離脱します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @return true/false (成功/失敗) + */ + bool (*leave)(struct KcSocket_ *sock, const char *addr); + + /** + * Socket 管理情報 + */ + void *_info; + + } KcSocket; + + /** + * 指定されたタイプ、ファミリのSocket オブジェクトを構築します。 + * + * @param type タイプ(SOCK_STREAM/SOCK_DGRAM/SOCK_RAW) + * @param family ファミリ(AF_UNSPEC/AF_INET/AF_INET6) + */ + KcSocket *KcSocket_new(int type, int family); + + /** + * Socket を破棄します。 + */ + void KcSocket_delete(KcSocket *socket); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_threads.h b/modules/include/kc_threads.h new file mode 100644 index 0000000..764176f --- /dev/null +++ b/modules/include/kc_threads.h @@ -0,0 +1,98 @@ +/** + * @file kc_threads.h + * @brief スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_THREADS_H +#define KC_THREADS_H + +#include + +#if (KC_IS_WINDOWS) +#include +#else +#include +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcThread_ + { + + /** + * 指定されたスレッドが生きているか否かを返します。 + * + * @param thread 対象スレッド + * @return true/false (スレッドが生存している/していない) + */ + bool (*is_alive)(struct KcThread_ *thread); + + /** + * スレッドを開始します。 + * + * @param thread 対象スレッド + * @param args スレッドに渡す情報 + * @return true/false (スレッド起動成功/失敗) + */ + bool (*start)(struct KcThread_ *thread, void *args); + + /** + * スレッドが終了するのを待機します。 + * + * @param thread 対象スレッド + */ + bool (*join)(struct KcThread_ *thread); + + /** + * Thread 管理情報 + */ + void *_info; + + } KcThread; + + /** + * スレッドを生成します。 + * + * @param run スレッドにて実行する関数へのポインタ + */ + KcThread *KcThread_new(int (*run)(void *args)); + + /** + * Thread を破棄します。 + * + * @param thread 破棄するスレッド + */ + void KcThread_delete(KcThread *thread); + + /** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープさせるミリ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_msleep(long time, bool force); + + /** + * 指定された sec 秒 + nsec ナノ秒間現在のスレッドをスリープさせます。 + * + * @param sec スリープさせる秒数 + * @param nsec スリープさせるナノ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_sleep(long sec, long nsec, bool force); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_THREADS_H diff --git a/modules/include/kc_threads_win.h b/modules/include/kc_threads_win.h new file mode 100644 index 0000000..b40ab10 --- /dev/null +++ b/modules/include/kc_threads_win.h @@ -0,0 +1,98 @@ +/** + * @file kc_windows_thread.h + * @brief Windows 用スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_WINDOWS_THREAD_H +#define KC_WINDOWS_THREAD_H + +#include +#if (KC_IS_WINDOWS) +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#define thread_local _Thread_local + + //////////////////////////////////////////////////////////////////////// + // + // Thread + // + + enum + { + thrd_success = 0, //!< 成功 + thrd_bussy = 1, //!< busy + thrd_error = 2, //!< エラー発生 + thrd_nomem = 3, //!< メモリ不足 + thrd_timedout = 4 //!< タイムアウト発生 + }; + + typedef struct + { + HANDLE handle; + DWORD thread_id; + } thrd_t; + + typedef int (*thrd_start_t)(void *); + int thrd_create(thrd_t *thr, thrd_start_t func, void *arg); + int thrd_join(thrd_t thr, int *res); + int thrd_detach(thrd_t thr); + thrd_t thrd_current(void); + int thrd_equal(thrd_t lhs, thrd_t rhs); + int thrd_sleep(const struct timespec *duration, struct timespec *remaining); + + //////////////////////////////////////////////////////////////////////// + // + // mutex + // + + enum + { + mtx_plain = 0, //!< 通常 + mtx_recursive = 1, //!< 再帰 + mtx_timed = 2 //!< timed + }; + + typedef struct + { + CRITICAL_SECTION cs; + int type; + } mtx_t; + + int mtx_init(mtx_t *mtx, int type); + void mtx_destroy(mtx_t *mtx); + int mtx_lock(mtx_t *mtx); + int mtx_unlock(mtx_t *mtx); + + //////////////////////////////////////////////////////////////////////// + // + // cond + // + + typedef struct + { + CONDITION_VARIABLE cond; + } cnd_t; + + int cnd_init(cnd_t *cond); + int cnd_signal(cnd_t *cond); + int cnd_broadcast(cnd_t *cond); + int cnd_wait(cnd_t *cond, mtx_t *mtx); + int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts); + void cnd_destroy(cnd_t *cond); + +#endif // (KC_IS_WINDOWS) +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_windows.h b/modules/include/kc_windows.h index ee70898..6e1c659 100644 --- a/modules/include/kc_windows.h +++ b/modules/include/kc_windows.h @@ -37,9 +37,9 @@ #endif // よく利用されるヘッダファイルをインクルードする -#include #include #include +#include #ifdef _MSC_VER #pragma comment(lib, "ws2_32.lib") #endif diff --git a/modules/src/kc_assert.c b/modules/src/kc_assert.c index 371fc8f..7cd4ecc 100644 --- a/modules/src/kc_assert.c +++ b/modules/src/kc_assert.c @@ -6,9 +6,9 @@ #include #include #include -#include #include #include +#include // プロトタイプ宣言 static void assert_default_handler(const char *msg); diff --git a/modules/src/kc_env.c b/modules/src/kc_env.c index 0675c73..95f185c 100644 --- a/modules/src/kc_env.c +++ b/modules/src/kc_env.c @@ -50,7 +50,7 @@ char *result = NULL; #if (KC_IS_WINDOWS) static char buff[KC_ENV_BUFFER_MAX]; - DWORD ret = GetEnvironmentVarable(key_name, (LPSTR)buff, sizeof(buff)); + DWORD ret = GetEnvironmentVariable(key_name, (LPSTR)buff, sizeof(buff)); if (ret != 0) { result = buff; diff --git a/modules/src/kc_list_array.c b/modules/src/kc_list_array.c index 246ed14..f29fc08 100644 --- a/modules/src/kc_list_array.c +++ b/modules/src/kc_list_array.c @@ -3,10 +3,14 @@ * @brief 配列リストモジュール * @copyright 2003 - 2023 Nomura Kei */ +#include #if defined(__GNUC__) #define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) #define qsort_s qsort_r #endif +#endif // defined(__GNUC__) + #include #include #include @@ -17,6 +21,7 @@ #include #include #include + /** * KcArrayList 管理情報 */ @@ -306,7 +311,11 @@ * @param context コンテキスト(KcListSortInfo) * @return 比較結果 */ +#if (KC_IS_WINDOWS) +static int KcArrayList_comparator(void *context, const void *x, const void *y) +#else static int KcArrayList_comparator(const void *x, const void *y, void *context) +#endif { KcListSortInfo *sort_info = (KcListSortInfo *)context; int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); diff --git a/modules/src/kc_list_linked.c b/modules/src/kc_list_linked.c index 30575b9..47edbe4 100644 --- a/modules/src/kc_list_linked.c +++ b/modules/src/kc_list_linked.c @@ -3,10 +3,14 @@ * @brief Linked リストモジュール * @copyright 2003 - 2023 Nomura Kei */ +#include #if defined(__GNUC__) #define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) #define qsort_s qsort_r #endif +#endif // defined(__GNUC__) + #include #include #include @@ -18,6 +22,13 @@ #include #include +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) +#define qsort_s qsort_r +#endif // KC_IS_WINDOWS +#endif // defined(__GNUC__) + /** * KcLinkedList Entry 情報 */ @@ -291,7 +302,11 @@ * @param context コンテキスト(KcListSortInfo) * @return 比較結果 */ +#if (KC_IS_WINDOWS) +static int KcLinkedList_comparator(void *context, const void *x, const void *y) +#else static int KcLinkedList_comparator(const void *x, const void *y, void *context) +#endif // KC_IS_WINDOWS { KcListSortInfo *sort_info = (KcListSortInfo *)context; const KcLinkedListEntry **entry_x = (const KcLinkedListEntry **)x; diff --git a/modules/src/kc_lock_guard.c b/modules/src/kc_lock_guard.c index 6ef68e5..648a153 100644 --- a/modules/src/kc_lock_guard.c +++ b/modules/src/kc_lock_guard.c @@ -7,7 +7,6 @@ #include - /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 * ロックに成功するとロック管理用のオブジェクトを返します。 @@ -15,36 +14,37 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex) +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line) { - KcLockGuard guard = { .mutex = mutex }; + KcLockGuard guard = {.mutex = mutex}; if (mutex == NULL) { errno = EINVAL; perror("kc_lock_guard_init: (mutex = NULL)"); + fprintf(stderr, "%s:%s:%d kc_lock_guard_init : mutex = NULL\n", file, func, line); return guard; } + errno = 0; int ret = mtx_lock(mutex); if (ret != thrd_success) { - perror("kc_lock_guard : mtx_lock error"); + fprintf(stderr, "%s:%s:%d kc_lock_guard : mtx_lock error\n", file, func, line); guard.mutex = NULL; } return guard; } - /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard) +void kc_lock_guard_release(KcLockGuard *guard) { - if (guard->mutex != NULL) { - mtx_unlock(guard->mutex); - guard->mutex = NULL; + if (guard->mutex != NULL) + { + mtx_unlock(guard->mutex); + guard->mutex = NULL; } } - diff --git a/modules/src/kc_memory.c b/modules/src/kc_memory.c index 5cc4186..6dbbc88 100644 --- a/modules/src/kc_memory.c +++ b/modules/src/kc_memory.c @@ -622,7 +622,11 @@ */ void *raw_aligned_alloc(size_t alignment, size_t size) { +#if (KC_IS_WINDOWS) + return _aligned_malloc(size, alignment); +#else return aligned_alloc(alignment, size); +#endif } /** diff --git a/modules/src/kc_memory_dump.c b/modules/src/kc_memory_dump.c index 111e328..49e63de 100644 --- a/modules/src/kc_memory_dump.c +++ b/modules/src/kc_memory_dump.c @@ -144,7 +144,7 @@ { // 空きバッファサイズ無しのため、何もしない。 return; } - int write_size = snprintf(info->write_ptr, info->rest_size, msg); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s", msg); if (write_size > info->rest_size) { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) write_size = (info->rest_size - 1); diff --git a/modules/src/kc_memory_listener.c b/modules/src/kc_memory_listener.c index 29ff2a0..ee69a13 100644 --- a/modules/src/kc_memory_listener.c +++ b/modules/src/kc_memory_listener.c @@ -98,5 +98,5 @@ KcMemoryDump_dump(buff, sizeof(buff), entry, 16, true, true, 130); fprintf(stderr, "[ERROR] %s", buff); } - fprintf(stderr, msg); + fprintf(stderr, "%s", msg); } diff --git a/modules/src/kc_queue.c b/modules/src/kc_queue.c index 544d763..d9f2a14 100644 --- a/modules/src/kc_queue.c +++ b/modules/src/kc_queue.c @@ -9,11 +9,11 @@ #include #include #include -#include #include #include #include +#include #include #include @@ -122,7 +122,10 @@ static int KcQueue_size(struct KcQueue_ *queue) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - size_t size = info->list->size(info->list); + size_t size = 0; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + size = info->list->size(info->list); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== return (int)size; } @@ -138,7 +141,11 @@ static bool KcQueue_is_empty(struct KcQueue_ *queue) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - return info->list->is_empty(info->list); + bool is_empty = true; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + is_empty = info->list->is_empty(info->list); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== + return is_empty; } // ============================================================================= @@ -155,7 +162,11 @@ static bool KcQueue_contains(struct KcQueue_ *queue, const void *element, size_t size) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - return info->list->contains(info->list, element, size); + bool is_contains = false; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + is_contains = info->list->contains(info->list, element, size); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== + return is_contains; } // ============================================================================= @@ -227,7 +238,11 @@ static void *KcQueue_peek(struct KcQueue_ *queue, size_t *size) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - return info->list->get(info->list, 0, size); + void *value = NULL; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + value = info->list->get(info->list, 0, size); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== + return value; } // ============================================================================= diff --git a/modules/src/kc_socket.c b/modules/src/kc_socket.c new file mode 100644 index 0000000..cb67996 --- /dev/null +++ b/modules/src/kc_socket.c @@ -0,0 +1,753 @@ +/** + * @file kc_socket.c + * @brief ソケットモジュール + * @copyright 2003 - 2024 Nomura Kei + */ +#include +#include + +#include +#include +#include +#include + +/** + * KcSocket 管理情報 + */ +typedef struct +{ + socket_t sock_fd; //!< ソケットディスクリプタ + int sock_type; //!< ソケットタイプ + int sock_family; //!< ソケットファミリ + struct sockaddr_storage remote_addr; //!< リモートアドレス + struct sockaddr_storage local_addr; //!< ローカルアドレス + socklen_t remote_addrlen; //!< リモートアドレス長 + socklen_t local_addrlen; //!< ローカルアドレス帳 +} KcSocketInfo; + +/** + * アドレス情報 + */ +typedef struct +{ + const char *addr; //!< アドレス + const char *service; //!< サービス +} KcSocketAddress; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static void Socket_setup(void); +static void Socket_cleanup(void); +static const char *KcSocket_get_remote_addr(KcSocket *sock, char *addr, size_t addrlen); +static int KcSocket_get_remote_port(KcSocket *sock); +static const char *KcSocket_get_local_addr(KcSocket *sock, char *addr, size_t addrlen); +static int KcSocket_get_local_port(KcSocket *sock); +static socket_t KcSocket_get_socket(KcSocket *sock); +static int KcSocket_get_type(KcSocket *sock); +static int KcSocket_get_family(KcSocket *sock); +static bool KcSocket_get_addrinfo( + KcSocket *sock, struct addrinfo **result, + const char *addr, const char *service, bool is_passive); +static bool KcSocket_bind(KcSocket *sock, const char *addr, const char *service); +static bool KcSocket_listen(KcSocket *sock, int backlog); +static KcSocket *KcSocket_accept(KcSocket *sock); +static bool KcSocket_connect( + KcSocket *sock, const char *addr, const char *service, + const char *local_addr, const char *local_service); +static bool KcSocket_close(KcSocket *sock); +static ssize_t KcSocket_send(KcSocket *sock, const char *buff, size_t size, int flags); +static ssize_t KcSocket_recv(KcSocket *sock, char *buff, size_t size, int flags); +static ssize_t KcSocket_sendto( + KcSocket *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service); +static ssize_t KcSocket_recvfrom( + KcSocket *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen); +static bool KcSocket_set_ttl(KcSocket *sock, int val); +static bool KcSocket_join(KcSocket *sock, const char *addr, const char *ifname, unsigned int ifindex); +static bool KcSocket_leave(KcSocket *sock, const char *addr); + +static bool KcSocket_addrinfo_bind_and_connect( + KcSocket *sock, struct addrinfo *bind_addrinfo, struct addrinfo *conn_addrinfo); +static bool KcSocket_addrinfo_connect(KcSocket *sock, struct addrinfo *conn_addrinfo, socket_t sockfd); + +/** + * ソケットのセットアップをします。 + */ +static void Socket_setup(void) +{ + static bool is_init = false; + if (!is_init) + { +#if (KC_IS_WINDOWS) + WSADATA wsa_data; + WSAStartup(MAKEWORD(2, 0), &wsa_data); +#endif + atexit(Socket_cleanup); + is_init = true; + } +} + +/** + * ソケットライブラリのクリーンアップをします。 + */ +static void Socket_cleanup(void) +{ +#if (KC_IS_WINDOWS) + WSACleanup(); +#endif +} + +// ============================================================================= +// new +// ============================================================================= +/** + * 指定されたタイプ、ファミリのSocket オブジェクトを構築します。 + * + * @param type タイプ(SOCK_STREAM/SOCK_DGRAM/SOCK_RAW) + * @param family ファミリ(AF_UNSPEC/AF_INET/AF_INET6) + */ +KcSocket *KcSocket_new(int type, int family) +{ + // KcSocket の管理構造 + // +----------------+ + // | KcSocket | + // | ... | + // | _info -----------+ + // +----------------+ | + // | |<--+ + // +----------------+ + Socket_setup(); + KcSocket *sock = (KcSocket *)malloc(sizeof(KcSocket) + sizeof(KcSocketInfo)); + if (sock != NULL) + { + sock->get_remote_addr = KcSocket_get_remote_addr; + sock->get_remote_port = KcSocket_get_remote_port; + sock->get_local_addr = KcSocket_get_local_addr; + sock->get_local_port = KcSocket_get_local_port; + sock->get_socket = KcSocket_get_socket; + sock->get_type = KcSocket_get_type; + sock->get_family = KcSocket_get_family; + sock->get_addrinfo = KcSocket_get_addrinfo; // for local + sock->bind = KcSocket_bind; + sock->listen = KcSocket_listen; + sock->accept = KcSocket_accept; + sock->connect = KcSocket_connect; + sock->close = KcSocket_close; + sock->send = KcSocket_send; + sock->recv = KcSocket_recv; + sock->sendto = KcSocket_sendto; + sock->recvfrom = KcSocket_recvfrom; + sock->set_ttl = KcSocket_set_ttl; + sock->join = KcSocket_join; + sock->leave = KcSocket_leave; + sock->_info = (sock + 1); + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + info->sock_fd = INVALID_SOCKET; + info->sock_type = type; + info->sock_family = family; + info->remote_addrlen = 0; + info->local_addrlen = 0; + } + return sock; +} + +// ============================================================================= +// delete +// ============================================================================= +/** + * Socket を破棄します。 + */ +void KcSocket_delete(KcSocket *socket) +{ + if (socket) + { + if (((KcSocketInfo *)socket->_info)->sock_fd != INVALID_SOCKET) + { + socket->close(socket); + } + free(socket); + } +} + +// ============================================================================= +// get_remote_addr +// ============================================================================= +/** + * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr 接続先アドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ +static const char *KcSocket_get_remote_addr(KcSocket *sock, char *addr, size_t addrlen) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->remote_addrlen) + { + int ret = getnameinfo( + (const struct sockaddr *)&info->remote_addr, + info->remote_addrlen, + addr, addrlen, NULL, 0, NI_NUMERICHOST); + if (ret == 0) + { + return addr; + } + } + return NULL; +} + +/** + * ソケットの接続先ポート番号を返します。 + * 接続先ポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ +static int KcSocket_get_remote_port(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->remote_addrlen) + { + char tmp_port[NI_MAXSERV]; + int ret = getnameinfo( + (const struct sockaddr *)&info->remote_addr, + info->remote_addrlen, + NULL, 0, tmp_port, sizeof(tmp_port), NI_NUMERICSERV); + if (ret == 0) + { + return atoi(tmp_port); + } + } + return -1; +} + +/** + * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr ローカルアドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ +static const char *KcSocket_get_local_addr(KcSocket *sock, char *addr, size_t addrlen) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->local_addrlen) + { + int ret = getnameinfo( + (const struct sockaddr *)&info->local_addr, + info->remote_addrlen, + addr, addrlen, NULL, 0, NI_NUMERICHOST); + if (ret == 0) + { + return addr; + } + } + return NULL; +} + +/** + * ソケットのローカルポート番号を返します。 + * ローカルポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ +static int KcSocket_get_local_port(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->local_addrlen) + { + char tmp_port[NI_MAXSERV]; + int ret = getnameinfo( + (const struct sockaddr *)&info->local_addr, + info->remote_addrlen, + NULL, 0, tmp_port, sizeof(tmp_port), NI_NUMERICSERV); + if (ret == 0) + { + return atoi(tmp_port); + } + } + return -1; +} + +/** + * ソケットディスクリプタを返します。 + * 通常は、ソケットディスクリプタを直接操作しないでください。 + * + * @param sock 対象ソケット + * @return ソケットディスクリプタ + */ +static socket_t KcSocket_get_socket(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + return info->sock_fd; +} + +/** + * ソケットタイプを返します。 + * + * @param sock 対象ソケット + * @return ソケットタイプ + */ +static int KcSocket_get_type(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + return info->sock_type; +} + +/** + * ソケットファミリを返します。 + * ソケット接続、バインド時には、実際にバインド、 + * あるいは接続されているソケットファミリを返します。 + * + * @param sock 対象ソケット + * @return ソケットファミリ + */ +static int KcSocket_get_family(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int result_family = info->sock_family; + if (info->sock_fd != INVALID_SOCKET) + { + struct sockaddr_storage ss; + socklen_t len = sizeof(struct sockaddr_storage); + if (getsockname(info->sock_fd, (struct sockaddr *)&ss, &len) == 0) + { + result_family = ss.ss_family; + } + } + return result_family; +} + +/** + * 指定されたアドレス、サービスのアドレス情報を取得します。 + * + * @param sock 対象ソケット + * @param result アドレス情報格納用ポインタ + * @param addr アドレス + * @param service サービス + * @param is_passive サーバソケットの場合、true を指定ください。 + */ +static bool KcSocket_get_addrinfo( + KcSocket *sock, struct addrinfo **result, + const char *addr, const char *service, bool is_passive) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + + struct addrinfo hints; + memset(&hints, 0x00, sizeof(struct addrinfo)); + hints.ai_socktype = info->sock_type; + hints.ai_family = info->sock_family; + if (is_passive) + { + hints.ai_flags = AI_PASSIVE; + } + int ret = getaddrinfo(addr, service, &hints, result); + return (ret == 0); +} + +/** + * 指定されたアドレス、サービスにバインドします。 + * 指定されたアドレスとサービスにより、複数候補がある場合は、 + * 最初に見つかったアドレスとポートにバインドします。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス (例: "80", "http", "ssh" など) + * @return true/false (bind 成功/失敗) + */ +static bool KcSocket_bind(KcSocket *sock, const char *addr, const char *service) +{ + struct addrinfo *bind_addr; + bool is_success = sock->get_addrinfo(sock, &bind_addr, addr, service, true); + if (is_success) + { + is_success = KcSocket_addrinfo_bind_and_connect(sock, bind_addr, NULL); + freeaddrinfo(bind_addr); + } + return is_success; +} + +/** + * ソケットを接続待ちソケットとしてマークをつけます。 + * 保留中の接続のキュー最大長を指定します。 + * + * @param sock 対象ソケット + * @param backlog バックログ + * @return true/false (成功/失敗) + */ +static bool KcSocket_listen(KcSocket *sock, int backlog) +{ + bool result = false; + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->sock_fd != INVALID_SOCKET) + { + int ret = listen(info->sock_fd, backlog); + result = (ret != SOCKET_ERROR); + } + return result; +} + +/** + * ソケットへの接続を受け付けます。 + * + * @param sock 対象ソケット + * @return 受け付けたソケット(失敗時NULL) + */ +static KcSocket *KcSocket_accept(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + KcSocket *client = NULL; + if (info->sock_fd != INVALID_SOCKET) + { + client = KcSocket_new(info->sock_type, info->sock_family); + if (client != NULL) + { + KcSocketInfo *cinfo = (KcSocketInfo *)client->_info; + cinfo->remote_addrlen = sizeof(struct sockaddr_storage); + cinfo->sock_fd = accept(info->sock_fd, (struct sockaddr *)&cinfo->remote_addr, &cinfo->remote_addrlen); + if (cinfo->sock_fd != INVALID_SOCKET) + { + memcpy(&cinfo->local_addr, &info->local_addr, info->local_addrlen); + cinfo->local_addrlen = info->local_addrlen; + cinfo->sock_type = SOCK_STREAM; + cinfo->sock_family = cinfo->remote_addr.ss_family; + } + else + { + KcSocket_delete(client); + client = NULL; + } + } + } + return client; +} + +/** + * 指定されたアドレス、サービス接続します。 + * local_addr に NULL を指定した場合、自動的にローカルのアドレス、サービスが設定されます。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス + * @param local_addr ローカルアドレス + * @param local_service ローカルサービス + * @return true/false (成功/失敗) + */ +static bool KcSocket_connect( + KcSocket *sock, const char *addr, const char *service, + const char *local_addr, const char *local_service) +{ + // 接続先アドレス情報取得 + struct addrinfo *conn_addr = NULL; + if (!sock->get_addrinfo(sock, &conn_addr, addr, service, false)) + { + return false; + } + + // バインドアドレス情報取得 + bool is_success = false; + struct addrinfo *bind_addr = NULL; + if (local_addr != NULL) + { // bind が必要 + is_success = sock->get_addrinfo(sock, &bind_addr, local_addr, local_service, true); + if (is_success) + { + is_success = KcSocket_addrinfo_bind_and_connect(sock, bind_addr, conn_addr); + freeaddrinfo(bind_addr); + } + } + else + { + is_success = KcSocket_addrinfo_connect(sock, conn_addr, INVALID_SOCKET); + } + freeaddrinfo(conn_addr); + return is_success; +} + +/** + * ソケットをクローズします。 + * + * @param sock 対象ソケット + * @return true/false (成功/失敗) + */ +static bool KcSocket_close(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + + if (info->sock_family == SOCK_STREAM) + { // TCP の場合は、出力を閉じて、届いているデータを読み取ってから close する。 + int ret = shutdown(info->sock_fd, SHUT_WR); + if (ret == 0) + { + int read_size; + char buff[1024]; + do + { // 届いているデータを全て読み取る + read_size = recv(info->sock_fd, buff, sizeof(buff), 0); + } while (read_size > 0); + } + } + sockclose(info->sock_fd); + return true; +} + +/** + * メッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 送信されたデータサイズ + */ +static ssize_t KcSocket_send(KcSocket *sock, const char *buff, size_t size, int flags) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + ssize_t write_size = 0; + do + { + errno = 0; + write_size = send(info->sock_fd, buff, size, flags); + } while ((write_size < 0) && (errno == EINTR)); + return write_size; +} + +/** + * メッセージを受信します。 + * 受信したメッセージは指定されたバッファに格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 読み取ったデータサイズ + */ +static ssize_t KcSocket_recv(KcSocket *sock, char *buff, size_t size, int flags) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + ssize_t read_size; + do + { + errno = 0; + read_size = recv(info->sock_fd, buff, size, flags); + } while ((read_size < 0) && (errno == EINTR)); + return read_size; +} + +/** + * 指定されたアドレス、サービスにメッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param addr アドレス + * @param service サービス + * @return 送信メッセージサイズ、エラー発生時は -1 + */ +static ssize_t KcSocket_sendto( + KcSocket *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service) +{ + (void)sock; + (void)buff; + (void)size; + (void)flags; + (void)addr; + (void)service; + return 0; +} + +/** + * メッセージを受信します。 + * src_addr, src_service がいずれも NULL 出ない場合、 + * 送信元のアドレスとサービスが格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param src_addr 送信元アドレス格納用バッファ + * @param src_addrlen 送信元アドレス格納用バッファサイズ + * @param src_service 送信元サービス格納用バッファ + * @param src_servicelen 送信元サービス格納用バッファサイズ + * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + */ +static ssize_t KcSocket_recvfrom( + KcSocket *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen) +{ + (void)sock; + (void)buff; + (void)size; + (void)flags; + (void)src_addr; + (void)src_addrlen; + (void)src_service; + (void)src_servicelen; + return 0; +} + +/** + * TTL を設定します。 + * + * @param sock 対象ソケット + * @param val 設定する TTL + * @return true/false (成功/失敗) + */ +static bool KcSocket_set_ttl(KcSocket *sock, int val) +{ + (void)sock; + (void)val; + return true; +} + +/** + * マルチキャストグループに参加するために JOIN します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @param ifindex インタフェースインデックス + * @return true/false (成功/失敗) + */ +static bool KcSocket_join( + KcSocket *sock, const char *addr, const char *ifname, unsigned int ifindex) +{ + (void)sock; + (void)addr; + (void)ifname; + (void)ifindex; + return true; +} + +/** + * マルチキャストグループから離脱します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @return true/false (成功/失敗) + */ +static bool KcSocket_leave( + KcSocket *sock, const char *addr) +{ + (void)sock; + (void)addr; + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// [内部関数] +// + +/** + * 指定された接続アドレス情報をもとにソケットの生成、接続を試みます。 + * 接続済みソケット sockfd が有効な場合、sockfd を用いて接続します。 + * + * @param sock 対象ソケット + * @param conn_addrinfo 接続アドレス情報 + * @param sockfd 接続済みソケット + * @return true/false (接続成功/失敗) + */ +static bool KcSocket_addrinfo_connect(KcSocket *sock, struct addrinfo *conn_addrinfo, socket_t sockfd) +{ + bool is_success = false; + socket_t tmp_sock = sockfd; + for (struct addrinfo *rp = conn_addrinfo; rp != NULL; rp = rp->ai_next) + { + if (sockfd == INVALID_SOCKET) + { // sockfd が無効の場合、ソケットを生成する。 + tmp_sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (tmp_sock == INVALID_SOCKET) + { + continue; + } + } + + int ret = connect(tmp_sock, rp->ai_addr, rp->ai_addrlen); + is_success = (ret != SOCKET_ERROR); + if (is_success) + { + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + info->sock_fd = tmp_sock; + info->remote_addrlen = rp->ai_addrlen; + memcpy(&info->remote_addr, rp->ai_addr, rp->ai_addrlen); + break; + } + + if (sockfd != INVALID_SOCKET) + { // sockfd が無効の場合、一時的に生成したソケットをクローズする。 + sockclose(tmp_sock); + } + } + return is_success; +} + +/** + * 指定された接続アドレス情報をもとにソケットの生成、バインドを試みます。 + * conn_addrinfo が NULL でない場合、バインド後に接続を試みます。 + * + * @param sock 対象ソケット + * @param bind_addrinfo バインドアドレス情報 + * @param conn_addrinfo 接続アドレス情報 + * @return true/false (接続成功/失敗) + */ +static bool KcSocket_addrinfo_bind_and_connect( + KcSocket *sock, struct addrinfo *bind_addrinfo, struct addrinfo *conn_addrinfo) +{ + int ret; + bool is_success = false; + + for (struct addrinfo *rp = bind_addrinfo; rp != NULL; rp = rp->ai_next) + { // sokcet : ソケット生成 + socket_t tmp_sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (tmp_sock == INVALID_SOCKET) + { + continue; + } + + // bind + const int on = 1; + setsockopt(tmp_sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on)); + ret = bind(tmp_sock, rp->ai_addr, rp->ai_addrlen); + is_success = (ret != SOCKET_ERROR); + if (!is_success) + { // bind 失敗したので次へ + sockclose(tmp_sock); + continue; + } + + // connect + if (conn_addrinfo) + { + is_success = KcSocket_addrinfo_connect(sock, conn_addrinfo, tmp_sock); + if (!is_success) + { // connect 失敗したので次へ + sockclose(tmp_sock); + continue; + } + } + + // bind または、bind と connect 成功 + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + info->sock_fd = tmp_sock; + info->local_addrlen = rp->ai_addrlen; + memcpy(&info->local_addr, rp->ai_addr, rp->ai_addrlen); + break; + } + return is_success; +} diff --git a/modules/src/kc_threads.c b/modules/src/kc_threads.c new file mode 100644 index 0000000..dd600bb --- /dev/null +++ b/modules/src/kc_threads.c @@ -0,0 +1,194 @@ +/** + * @file kc_thread.c + * @brief スレッドモジュール + * @copyright 2020 - 2024 Nomura Kei + */ +#include +#include +#include + +#include +#include +#include + +/** + * KcThread 管理情報 + */ +typedef struct +{ + thrd_t tid; + void *args; + int (*run)(void *args); + atomic_bool alive_state; +} KcThreadInfo; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static bool KcThread_is_alive(KcThread *thread); +static bool KcThread_start(KcThread *thread, void *args); +static bool KcThread_join(KcThread *thread); +static int KcThread_run(void *thread); + +// ============================================================================= +// new +// ============================================================================= +/** + * スレッドを生成します。 + * + * @param run スレッドにて実行する関数へのポインタ + */ +KcThread *KcThread_new(int (*run)(void *args)) +{ + // KcThread の管理構造 + // +--------------+ + // | KcThread | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // +--------------+ + KcThread *thread = (KcThread *)malloc(sizeof(KcThread) + sizeof(KcThreadInfo)); + if (thread != NULL) + { + thread->is_alive = KcThread_is_alive; + thread->start = KcThread_start; + thread->join = KcThread_join; + thread->_info = (thread + 1); + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + // info->tid + info->run = run; + atomic_init(&info->alive_state, false); + } + return thread; +} + +// ============================================================================= +// delete +// ============================================================================= +/** + * Thread を破棄します。 + * + * @param thread 破棄するスレッド + */ +void KcThread_delete(KcThread *thread) +{ + free(thread); +} + +// ============================================================================= +// sleep +// ============================================================================= +/** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープさせるミリ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ +bool KcThread_msleep(long time, bool force) +{ + long sec = time / 1000; + long msec = time - (sec * 1000); + long nsec = msec * 1000 * 1000; + return KcThread_sleep(sec, nsec, force); +} + +/** + * 指定された sec 秒 + nsec ナノ秒間現在のスレッドをスリープさせます。 + * + * @param sec スリープさせる秒数 + * @param nsec スリープさせるナノ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ +bool KcThread_sleep(long sec, long nsec, bool force) +{ + struct timespec remaining = {sec, nsec}; + int ret; + do + { + errno = 0; + ret = thrd_sleep(&remaining, &remaining); + } while ((ret == -1) && force && (errno == EINTR)); + return (ret == 0); +} + +// ============================================================================= +// is_alive +// ============================================================================= +/** + * 指定されたスレッドが生きているか否かを返します。 + * + * @param thread 対象スレッド + * @return true/false (スレッドが生存している/していない) + */ +static bool KcThread_is_alive(KcThread *thread) +{ + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + return atomic_load(&info->alive_state); +} + +// ============================================================================= +// start +// ============================================================================= +/** + * スレッドを開始します。 + * + * @param thread 対象スレッド + * @param args スレッドに渡す情報 + * @return true/false (スレッド起動成功/失敗) + */ +static bool KcThread_start(KcThread *thread, void *args) +{ + int ret = thrd_error; + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + if (!atomic_load(&info->alive_state)) + { + info->args = args; + ret = thrd_create(&info->tid, KcThread_run, thread); + } + return (ret == thrd_success); +} + +// ============================================================================= +// join +// ============================================================================= +/** + * スレッドが終了するのを待機します。 + * + * @param thread 対象スレッド + * @return true/false (成功/失敗) + */ +static bool KcThread_join(KcThread *thread) +{ + int ret = thrd_error; + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + ret = thrd_join(info->tid, NULL); + return (ret == thrd_success); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// 内部関数 +// + +// ============================================================================= +// run +// ============================================================================= +/** + * スレッドで実行される関数。 + * start にて関数を別スレッドにて実行します。 + * + * @param args スレッド情報 + * @return + */ +int KcThread_run(void *args) +{ + KcThread *thread = (KcThread *)args; + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + atomic_store(&info->alive_state, true); + int ret = info->run(info->args); + atomic_store(&info->alive_state, false); + return ret; +} diff --git a/config.mk b/config.mk index a0ac27c..f7c6e47 100644 --- a/config.mk +++ b/config.mk @@ -25,14 +25,17 @@ # for Windows CROSS_COMPILE ?=i686-w64-mingw32- LIBS ?= -lws2_32 +DEBUG_OPTIONS ?= else # For Linux ifeq ($(strip $(ARCH)),aarch64) CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector else CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector endif endif diff --git a/mk/base-cmd.mk b/mk/base-cmd.mk index f429bcf..e74c114 100644 --- a/mk/base-cmd.mk +++ b/mk/base-cmd.mk @@ -33,8 +33,13 @@ CC = $(CROSS_COMPILE)gcc CXX = $(CROSS_COMPILE)g++ + #CC = $(CROSS_COMPILE)clang #CXX = $(CROSS_COMPILE)clang++ + +#CC = $(CROSS_COMPILE)clang-16 +#CXX = $(CROSS_COMPILE)clang++16 + RANLIB = $(CROSS_COMPILE)ranlib ADDR2LINE = $(CROSS_COMPILE)addr2line AR = $(CROSS_COMPILE)ar diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 2cc417c..eebded1 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -69,7 +69,7 @@ # 値が2の場合、さらなるチェックを追加するが、規格準拠のプログラムが失敗する可能性がある。 # いくつかのチェックは、コンパイル時に実行され、コンパイラの警告として表示される。 # ------------------------------------------------------------------------------ -DEBUG_OPTIONS += -fstack-protector +# DEBUG_OPTIONS += -fstack-protector DEBUG_OPTIONS += --coverage DEBUG_OPTIONS += -g3 -ggdb DEBUG_OPTIONS += -O0 diff --git a/mk/link-a-conf.mk b/mk/link-a-conf.mk index 0e872f3..631e6d5 100644 --- a/mk/link-a-conf.mk +++ b/mk/link-a-conf.mk @@ -10,8 +10,11 @@ HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) +HEADER_FILES_WIN = $(wildcard include/win/*.h) $(wildcard include/win/*.hpp) +TOP_HEADER_FILES_WIN = $(addprefix $(TOPDIR)/include/win/,$(notdir $(HEADER_FILES_WIN))) + CLEAN_FILES += $(TOP_TARGET) -CLEAN_FILES += $(TOP_HEADER_FILES) +CLEAN_FILES += $(TOP_HEADER_FILES) $(TOP_HEADER_FILES_WIN) endif endif diff --git a/mk/link-a-rule.mk b/mk/link-a-rule.mk index d94a54a..657d858 100644 --- a/mk/link-a-rule.mk +++ b/mk/link-a-rule.mk @@ -10,6 +10,7 @@ $(TOP_TARGET): $(TARGET) ifneq ($(strip $(HEADER_FILES)),) $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ + $(CP) -f $(HEADER_FILES_WIN) $(TOPDIR)/include/win/ endif $(CP) -f $(TARGET) $(TOPDIR)/lib/ diff --git a/modules/include/kc_lock_guard.h b/modules/include/kc_lock_guard.h index 8bb70bd..ad3915f 100644 --- a/modules/include/kc_lock_guard.h +++ b/modules/include/kc_lock_guard.h @@ -7,18 +7,16 @@ #define KC_LOCK_GUARD_H #include -#include - +#include /** * LockGuard 管理構造体 */ typedef struct { - mtx_t* mutex; + mtx_t *mutex; } KcLockGuard; - /** * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 * 使用例) @@ -34,8 +32,7 @@ * } */ #define kc_lock_guard(lock) \ - for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) - + for (KcLockGuard _guard = kc_lock_guard_init(lock, __FILE__, __func__, __LINE__); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 @@ -44,15 +41,13 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex); - +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line); /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard); +void kc_lock_guard_release(KcLockGuard *guard); - -#endif // KC_LOCK_GUARD_H +#endif // KC_LOCK_GUARD_H diff --git a/modules/include/kc_memory.h b/modules/include/kc_memory.h index ffce4ed..c6ba9e9 100644 --- a/modules/include/kc_memory.h +++ b/modules/include/kc_memory.h @@ -22,12 +22,13 @@ #ifndef KC_MEMORY_H #define KC_MEMORY_H -#include +#include #include #include #include #include +#include #ifdef __cplusplus extern "C" @@ -273,8 +274,6 @@ #define calloc(nmemb, size) kc_memory_manager->calloc(nmemb, size, __FILE__, __func__, __LINE__) #define realloc(ptr, size) kc_memory_manager->realloc(ptr, size, __FILE__, __func__, __LINE__) #define free(ptr) kc_memory_manager->free(ptr) -#else -#include #endif #ifdef __cplusplus diff --git a/modules/include/kc_socket.h b/modules/include/kc_socket.h new file mode 100644 index 0000000..7d952c7 --- /dev/null +++ b/modules/include/kc_socket.h @@ -0,0 +1,291 @@ +/** + * @file kc_socket.h + * @brief ソケットモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_SOCKET_H +#define KC_SOCKET_H + +#include + +#if (KC_IS_WINDOWS) +typedef SOCKET socket_t; +#define SHUT_RD SD_RECEIVE +#define SHUT_WR SD_SEND +#define SHUT_RDWR SD_BOTH +#define sockclose(sockfd) closesocket(sockfd) +#else +#include +#include +#include +#include +#include +#include +#include +typedef int socket_t; +#define INVALID_SOCKET (-1) +#define SOCKET_ERROR (-1) +#define sockclose(sockfd) close(sockfd) +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcSocket_ + { + + /** + * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr 接続先アドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_remote_addr)(struct KcSocket_ *sock, char *addr, size_t addrlen); + + /** + * ソケットの接続先ポート番号を返します。 + * 接続先ポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_remote_port)(struct KcSocket_ *sock); + + /** + * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr ローカルアドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_local_addr)(struct KcSocket_ *sock, char *addr, size_t addlen); + + /** + * ソケットのローカルポート番号を返します。 + * ローカルポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_local_port)(struct KcSocket_ *sock); + + /** + * ソケットディスクリプタを返します。 + * 通常は、ソケットディスクリプタを直接操作しないでください。 + * + * @param sock 対象ソケット + * @return ソケットディスクリプタ + */ + socket_t (*get_socket)(struct KcSocket_ *sock); + + /** + * ソケットタイプを返します。 + * + * @param sock 対象ソケット + * @return ソケットタイプ + */ + int (*get_type)(struct KcSocket_ *sock); + + /** + * ソケットファミリを返します。 + * 実際にバインド、あるいは接続されているソケットファミリを返します。 + * + * @param sock 対象ソケット + * @return ソケットファミリ + */ + int (*get_family)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービスのアドレス情報を取得します。 + * + * @param sock 対象ソケット + * @param result アドレス情報格納用ポインタ + * @param addr アドレス + * @param service サービス + * @param is_passive サーバソケットの場合、true を指定ください。 + */ + bool (*get_addrinfo)( + struct KcSocket_ *sock, + struct addrinfo **result, + const char *addr, + const char *service, + bool is_passive); + + /** + * 指定されたアドレス、サービスにバインドします。 + * 指定されたアドレスとサービスにより、複数候補がある場合は、 + * 最初に見つかったアドレスとポートにバインドします。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス (例: "80", "http", "ssh" など) + * @return true/false (bind 成功/失敗) + */ + bool (*bind)(struct KcSocket_ *sock, const char *addr, const char *service); + + /** + * ソケットを接続待ちソケットとしてマークをつけます。 + * 保留中の接続のキュー最大長を指定します。 + * + * @param sock 対象ソケット + * @param backlog バックログ + * @return true/false (成功/失敗) + */ + bool (*listen)(struct KcSocket_ *sock, int backlog); + + /** + * ソケットへの接続を受け付けます。 + * + * @param sock 対象ソケット + * @return 受け付けたソケット + */ + struct KcSocket_ *(*accept)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービス接続します。 + * local_addr に NULL を指定した場合、自動的にローカルのアドレスが設定されます。 + * local_service に -1 を指定した場合、自動的にローカルのポート番号が割りつけられます。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス + * @param local_addr ローカルアドレス + * @param local_service ローカルサービス + * @return true/false (成功/失敗) + */ + bool (*connect)(struct KcSocket_ *sock, const char *addr, const char *service, const char *local_addr, const char *local_port); + + /** + * ソケットをクローズします。 + * + * @param sock 対象ソケット + * @return true/false (成功/失敗) + */ + bool (*close)(struct KcSocket_ *sock); + + /** + * メッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 送信されたデータサイズ + */ + ssize_t (*send)(struct KcSocket_ *sock, const char *buff, size_t size, int flags); + + /** + * メッセージを受信します。 + * 受信したメッセージは指定されたバッファに格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 読み取ったデータサイズ + */ + ssize_t (*recv)(struct KcSocket_ *sock, char *buff, size_t size, int flags); + + /** + * 指定されたアドレス、サービスにメッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param addr アドレス + * @param service サービス + * @return 送信メッセージサイズ、エラー発生時は -1 + */ + ssize_t (*sendto)( + struct KcSocket_ *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service); + + /** + * メッセージを受信します。 + * src_addr, src_service がいずれも NULL 出ない場合、 + * 送信元のアドレスとサービスが格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param src_addr 送信元アドレス格納用バッファ + * @param src_addrlen 送信元アドレス格納用バッファサイズ + * @param src_service 送信元サービス格納用バッファ + * @param src_servicelen 送信元サービス格納用バッファサイズ + * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + */ + ssize_t (*recvfrom)( + struct KcSocket_ *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen); + + /** + * TTL を設定します。 + * + * @param sock 対象ソケット + * @param val 設定する TTL + * @return true/false (成功/失敗) + */ + bool (*set_ttl)(struct KcSocket_ *sock, int val); + + /** + * マルチキャストグループに参加するために JOIN します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @param ifindex インタフェースインデックス + * @return true/false (成功/失敗) + */ + bool (*join)(struct KcSocket_ *sock, const char *addr, const char *ifname, unsigned int ifindex); + + /** + * マルチキャストグループから離脱します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @return true/false (成功/失敗) + */ + bool (*leave)(struct KcSocket_ *sock, const char *addr); + + /** + * Socket 管理情報 + */ + void *_info; + + } KcSocket; + + /** + * 指定されたタイプ、ファミリのSocket オブジェクトを構築します。 + * + * @param type タイプ(SOCK_STREAM/SOCK_DGRAM/SOCK_RAW) + * @param family ファミリ(AF_UNSPEC/AF_INET/AF_INET6) + */ + KcSocket *KcSocket_new(int type, int family); + + /** + * Socket を破棄します。 + */ + void KcSocket_delete(KcSocket *socket); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_threads.h b/modules/include/kc_threads.h new file mode 100644 index 0000000..764176f --- /dev/null +++ b/modules/include/kc_threads.h @@ -0,0 +1,98 @@ +/** + * @file kc_threads.h + * @brief スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_THREADS_H +#define KC_THREADS_H + +#include + +#if (KC_IS_WINDOWS) +#include +#else +#include +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcThread_ + { + + /** + * 指定されたスレッドが生きているか否かを返します。 + * + * @param thread 対象スレッド + * @return true/false (スレッドが生存している/していない) + */ + bool (*is_alive)(struct KcThread_ *thread); + + /** + * スレッドを開始します。 + * + * @param thread 対象スレッド + * @param args スレッドに渡す情報 + * @return true/false (スレッド起動成功/失敗) + */ + bool (*start)(struct KcThread_ *thread, void *args); + + /** + * スレッドが終了するのを待機します。 + * + * @param thread 対象スレッド + */ + bool (*join)(struct KcThread_ *thread); + + /** + * Thread 管理情報 + */ + void *_info; + + } KcThread; + + /** + * スレッドを生成します。 + * + * @param run スレッドにて実行する関数へのポインタ + */ + KcThread *KcThread_new(int (*run)(void *args)); + + /** + * Thread を破棄します。 + * + * @param thread 破棄するスレッド + */ + void KcThread_delete(KcThread *thread); + + /** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープさせるミリ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_msleep(long time, bool force); + + /** + * 指定された sec 秒 + nsec ナノ秒間現在のスレッドをスリープさせます。 + * + * @param sec スリープさせる秒数 + * @param nsec スリープさせるナノ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_sleep(long sec, long nsec, bool force); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_THREADS_H diff --git a/modules/include/kc_threads_win.h b/modules/include/kc_threads_win.h new file mode 100644 index 0000000..b40ab10 --- /dev/null +++ b/modules/include/kc_threads_win.h @@ -0,0 +1,98 @@ +/** + * @file kc_windows_thread.h + * @brief Windows 用スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_WINDOWS_THREAD_H +#define KC_WINDOWS_THREAD_H + +#include +#if (KC_IS_WINDOWS) +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#define thread_local _Thread_local + + //////////////////////////////////////////////////////////////////////// + // + // Thread + // + + enum + { + thrd_success = 0, //!< 成功 + thrd_bussy = 1, //!< busy + thrd_error = 2, //!< エラー発生 + thrd_nomem = 3, //!< メモリ不足 + thrd_timedout = 4 //!< タイムアウト発生 + }; + + typedef struct + { + HANDLE handle; + DWORD thread_id; + } thrd_t; + + typedef int (*thrd_start_t)(void *); + int thrd_create(thrd_t *thr, thrd_start_t func, void *arg); + int thrd_join(thrd_t thr, int *res); + int thrd_detach(thrd_t thr); + thrd_t thrd_current(void); + int thrd_equal(thrd_t lhs, thrd_t rhs); + int thrd_sleep(const struct timespec *duration, struct timespec *remaining); + + //////////////////////////////////////////////////////////////////////// + // + // mutex + // + + enum + { + mtx_plain = 0, //!< 通常 + mtx_recursive = 1, //!< 再帰 + mtx_timed = 2 //!< timed + }; + + typedef struct + { + CRITICAL_SECTION cs; + int type; + } mtx_t; + + int mtx_init(mtx_t *mtx, int type); + void mtx_destroy(mtx_t *mtx); + int mtx_lock(mtx_t *mtx); + int mtx_unlock(mtx_t *mtx); + + //////////////////////////////////////////////////////////////////////// + // + // cond + // + + typedef struct + { + CONDITION_VARIABLE cond; + } cnd_t; + + int cnd_init(cnd_t *cond); + int cnd_signal(cnd_t *cond); + int cnd_broadcast(cnd_t *cond); + int cnd_wait(cnd_t *cond, mtx_t *mtx); + int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts); + void cnd_destroy(cnd_t *cond); + +#endif // (KC_IS_WINDOWS) +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_windows.h b/modules/include/kc_windows.h index ee70898..6e1c659 100644 --- a/modules/include/kc_windows.h +++ b/modules/include/kc_windows.h @@ -37,9 +37,9 @@ #endif // よく利用されるヘッダファイルをインクルードする -#include #include #include +#include #ifdef _MSC_VER #pragma comment(lib, "ws2_32.lib") #endif diff --git a/modules/src/kc_assert.c b/modules/src/kc_assert.c index 371fc8f..7cd4ecc 100644 --- a/modules/src/kc_assert.c +++ b/modules/src/kc_assert.c @@ -6,9 +6,9 @@ #include #include #include -#include #include #include +#include // プロトタイプ宣言 static void assert_default_handler(const char *msg); diff --git a/modules/src/kc_env.c b/modules/src/kc_env.c index 0675c73..95f185c 100644 --- a/modules/src/kc_env.c +++ b/modules/src/kc_env.c @@ -50,7 +50,7 @@ char *result = NULL; #if (KC_IS_WINDOWS) static char buff[KC_ENV_BUFFER_MAX]; - DWORD ret = GetEnvironmentVarable(key_name, (LPSTR)buff, sizeof(buff)); + DWORD ret = GetEnvironmentVariable(key_name, (LPSTR)buff, sizeof(buff)); if (ret != 0) { result = buff; diff --git a/modules/src/kc_list_array.c b/modules/src/kc_list_array.c index 246ed14..f29fc08 100644 --- a/modules/src/kc_list_array.c +++ b/modules/src/kc_list_array.c @@ -3,10 +3,14 @@ * @brief 配列リストモジュール * @copyright 2003 - 2023 Nomura Kei */ +#include #if defined(__GNUC__) #define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) #define qsort_s qsort_r #endif +#endif // defined(__GNUC__) + #include #include #include @@ -17,6 +21,7 @@ #include #include #include + /** * KcArrayList 管理情報 */ @@ -306,7 +311,11 @@ * @param context コンテキスト(KcListSortInfo) * @return 比較結果 */ +#if (KC_IS_WINDOWS) +static int KcArrayList_comparator(void *context, const void *x, const void *y) +#else static int KcArrayList_comparator(const void *x, const void *y, void *context) +#endif { KcListSortInfo *sort_info = (KcListSortInfo *)context; int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); diff --git a/modules/src/kc_list_linked.c b/modules/src/kc_list_linked.c index 30575b9..47edbe4 100644 --- a/modules/src/kc_list_linked.c +++ b/modules/src/kc_list_linked.c @@ -3,10 +3,14 @@ * @brief Linked リストモジュール * @copyright 2003 - 2023 Nomura Kei */ +#include #if defined(__GNUC__) #define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) #define qsort_s qsort_r #endif +#endif // defined(__GNUC__) + #include #include #include @@ -18,6 +22,13 @@ #include #include +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) +#define qsort_s qsort_r +#endif // KC_IS_WINDOWS +#endif // defined(__GNUC__) + /** * KcLinkedList Entry 情報 */ @@ -291,7 +302,11 @@ * @param context コンテキスト(KcListSortInfo) * @return 比較結果 */ +#if (KC_IS_WINDOWS) +static int KcLinkedList_comparator(void *context, const void *x, const void *y) +#else static int KcLinkedList_comparator(const void *x, const void *y, void *context) +#endif // KC_IS_WINDOWS { KcListSortInfo *sort_info = (KcListSortInfo *)context; const KcLinkedListEntry **entry_x = (const KcLinkedListEntry **)x; diff --git a/modules/src/kc_lock_guard.c b/modules/src/kc_lock_guard.c index 6ef68e5..648a153 100644 --- a/modules/src/kc_lock_guard.c +++ b/modules/src/kc_lock_guard.c @@ -7,7 +7,6 @@ #include - /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 * ロックに成功するとロック管理用のオブジェクトを返します。 @@ -15,36 +14,37 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex) +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line) { - KcLockGuard guard = { .mutex = mutex }; + KcLockGuard guard = {.mutex = mutex}; if (mutex == NULL) { errno = EINVAL; perror("kc_lock_guard_init: (mutex = NULL)"); + fprintf(stderr, "%s:%s:%d kc_lock_guard_init : mutex = NULL\n", file, func, line); return guard; } + errno = 0; int ret = mtx_lock(mutex); if (ret != thrd_success) { - perror("kc_lock_guard : mtx_lock error"); + fprintf(stderr, "%s:%s:%d kc_lock_guard : mtx_lock error\n", file, func, line); guard.mutex = NULL; } return guard; } - /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard) +void kc_lock_guard_release(KcLockGuard *guard) { - if (guard->mutex != NULL) { - mtx_unlock(guard->mutex); - guard->mutex = NULL; + if (guard->mutex != NULL) + { + mtx_unlock(guard->mutex); + guard->mutex = NULL; } } - diff --git a/modules/src/kc_memory.c b/modules/src/kc_memory.c index 5cc4186..6dbbc88 100644 --- a/modules/src/kc_memory.c +++ b/modules/src/kc_memory.c @@ -622,7 +622,11 @@ */ void *raw_aligned_alloc(size_t alignment, size_t size) { +#if (KC_IS_WINDOWS) + return _aligned_malloc(size, alignment); +#else return aligned_alloc(alignment, size); +#endif } /** diff --git a/modules/src/kc_memory_dump.c b/modules/src/kc_memory_dump.c index 111e328..49e63de 100644 --- a/modules/src/kc_memory_dump.c +++ b/modules/src/kc_memory_dump.c @@ -144,7 +144,7 @@ { // 空きバッファサイズ無しのため、何もしない。 return; } - int write_size = snprintf(info->write_ptr, info->rest_size, msg); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s", msg); if (write_size > info->rest_size) { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) write_size = (info->rest_size - 1); diff --git a/modules/src/kc_memory_listener.c b/modules/src/kc_memory_listener.c index 29ff2a0..ee69a13 100644 --- a/modules/src/kc_memory_listener.c +++ b/modules/src/kc_memory_listener.c @@ -98,5 +98,5 @@ KcMemoryDump_dump(buff, sizeof(buff), entry, 16, true, true, 130); fprintf(stderr, "[ERROR] %s", buff); } - fprintf(stderr, msg); + fprintf(stderr, "%s", msg); } diff --git a/modules/src/kc_queue.c b/modules/src/kc_queue.c index 544d763..d9f2a14 100644 --- a/modules/src/kc_queue.c +++ b/modules/src/kc_queue.c @@ -9,11 +9,11 @@ #include #include #include -#include #include #include #include +#include #include #include @@ -122,7 +122,10 @@ static int KcQueue_size(struct KcQueue_ *queue) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - size_t size = info->list->size(info->list); + size_t size = 0; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + size = info->list->size(info->list); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== return (int)size; } @@ -138,7 +141,11 @@ static bool KcQueue_is_empty(struct KcQueue_ *queue) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - return info->list->is_empty(info->list); + bool is_empty = true; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + is_empty = info->list->is_empty(info->list); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== + return is_empty; } // ============================================================================= @@ -155,7 +162,11 @@ static bool KcQueue_contains(struct KcQueue_ *queue, const void *element, size_t size) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - return info->list->contains(info->list, element, size); + bool is_contains = false; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + is_contains = info->list->contains(info->list, element, size); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== + return is_contains; } // ============================================================================= @@ -227,7 +238,11 @@ static void *KcQueue_peek(struct KcQueue_ *queue, size_t *size) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - return info->list->get(info->list, 0, size); + void *value = NULL; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + value = info->list->get(info->list, 0, size); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== + return value; } // ============================================================================= diff --git a/modules/src/kc_socket.c b/modules/src/kc_socket.c new file mode 100644 index 0000000..cb67996 --- /dev/null +++ b/modules/src/kc_socket.c @@ -0,0 +1,753 @@ +/** + * @file kc_socket.c + * @brief ソケットモジュール + * @copyright 2003 - 2024 Nomura Kei + */ +#include +#include + +#include +#include +#include +#include + +/** + * KcSocket 管理情報 + */ +typedef struct +{ + socket_t sock_fd; //!< ソケットディスクリプタ + int sock_type; //!< ソケットタイプ + int sock_family; //!< ソケットファミリ + struct sockaddr_storage remote_addr; //!< リモートアドレス + struct sockaddr_storage local_addr; //!< ローカルアドレス + socklen_t remote_addrlen; //!< リモートアドレス長 + socklen_t local_addrlen; //!< ローカルアドレス帳 +} KcSocketInfo; + +/** + * アドレス情報 + */ +typedef struct +{ + const char *addr; //!< アドレス + const char *service; //!< サービス +} KcSocketAddress; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static void Socket_setup(void); +static void Socket_cleanup(void); +static const char *KcSocket_get_remote_addr(KcSocket *sock, char *addr, size_t addrlen); +static int KcSocket_get_remote_port(KcSocket *sock); +static const char *KcSocket_get_local_addr(KcSocket *sock, char *addr, size_t addrlen); +static int KcSocket_get_local_port(KcSocket *sock); +static socket_t KcSocket_get_socket(KcSocket *sock); +static int KcSocket_get_type(KcSocket *sock); +static int KcSocket_get_family(KcSocket *sock); +static bool KcSocket_get_addrinfo( + KcSocket *sock, struct addrinfo **result, + const char *addr, const char *service, bool is_passive); +static bool KcSocket_bind(KcSocket *sock, const char *addr, const char *service); +static bool KcSocket_listen(KcSocket *sock, int backlog); +static KcSocket *KcSocket_accept(KcSocket *sock); +static bool KcSocket_connect( + KcSocket *sock, const char *addr, const char *service, + const char *local_addr, const char *local_service); +static bool KcSocket_close(KcSocket *sock); +static ssize_t KcSocket_send(KcSocket *sock, const char *buff, size_t size, int flags); +static ssize_t KcSocket_recv(KcSocket *sock, char *buff, size_t size, int flags); +static ssize_t KcSocket_sendto( + KcSocket *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service); +static ssize_t KcSocket_recvfrom( + KcSocket *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen); +static bool KcSocket_set_ttl(KcSocket *sock, int val); +static bool KcSocket_join(KcSocket *sock, const char *addr, const char *ifname, unsigned int ifindex); +static bool KcSocket_leave(KcSocket *sock, const char *addr); + +static bool KcSocket_addrinfo_bind_and_connect( + KcSocket *sock, struct addrinfo *bind_addrinfo, struct addrinfo *conn_addrinfo); +static bool KcSocket_addrinfo_connect(KcSocket *sock, struct addrinfo *conn_addrinfo, socket_t sockfd); + +/** + * ソケットのセットアップをします。 + */ +static void Socket_setup(void) +{ + static bool is_init = false; + if (!is_init) + { +#if (KC_IS_WINDOWS) + WSADATA wsa_data; + WSAStartup(MAKEWORD(2, 0), &wsa_data); +#endif + atexit(Socket_cleanup); + is_init = true; + } +} + +/** + * ソケットライブラリのクリーンアップをします。 + */ +static void Socket_cleanup(void) +{ +#if (KC_IS_WINDOWS) + WSACleanup(); +#endif +} + +// ============================================================================= +// new +// ============================================================================= +/** + * 指定されたタイプ、ファミリのSocket オブジェクトを構築します。 + * + * @param type タイプ(SOCK_STREAM/SOCK_DGRAM/SOCK_RAW) + * @param family ファミリ(AF_UNSPEC/AF_INET/AF_INET6) + */ +KcSocket *KcSocket_new(int type, int family) +{ + // KcSocket の管理構造 + // +----------------+ + // | KcSocket | + // | ... | + // | _info -----------+ + // +----------------+ | + // | |<--+ + // +----------------+ + Socket_setup(); + KcSocket *sock = (KcSocket *)malloc(sizeof(KcSocket) + sizeof(KcSocketInfo)); + if (sock != NULL) + { + sock->get_remote_addr = KcSocket_get_remote_addr; + sock->get_remote_port = KcSocket_get_remote_port; + sock->get_local_addr = KcSocket_get_local_addr; + sock->get_local_port = KcSocket_get_local_port; + sock->get_socket = KcSocket_get_socket; + sock->get_type = KcSocket_get_type; + sock->get_family = KcSocket_get_family; + sock->get_addrinfo = KcSocket_get_addrinfo; // for local + sock->bind = KcSocket_bind; + sock->listen = KcSocket_listen; + sock->accept = KcSocket_accept; + sock->connect = KcSocket_connect; + sock->close = KcSocket_close; + sock->send = KcSocket_send; + sock->recv = KcSocket_recv; + sock->sendto = KcSocket_sendto; + sock->recvfrom = KcSocket_recvfrom; + sock->set_ttl = KcSocket_set_ttl; + sock->join = KcSocket_join; + sock->leave = KcSocket_leave; + sock->_info = (sock + 1); + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + info->sock_fd = INVALID_SOCKET; + info->sock_type = type; + info->sock_family = family; + info->remote_addrlen = 0; + info->local_addrlen = 0; + } + return sock; +} + +// ============================================================================= +// delete +// ============================================================================= +/** + * Socket を破棄します。 + */ +void KcSocket_delete(KcSocket *socket) +{ + if (socket) + { + if (((KcSocketInfo *)socket->_info)->sock_fd != INVALID_SOCKET) + { + socket->close(socket); + } + free(socket); + } +} + +// ============================================================================= +// get_remote_addr +// ============================================================================= +/** + * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr 接続先アドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ +static const char *KcSocket_get_remote_addr(KcSocket *sock, char *addr, size_t addrlen) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->remote_addrlen) + { + int ret = getnameinfo( + (const struct sockaddr *)&info->remote_addr, + info->remote_addrlen, + addr, addrlen, NULL, 0, NI_NUMERICHOST); + if (ret == 0) + { + return addr; + } + } + return NULL; +} + +/** + * ソケットの接続先ポート番号を返します。 + * 接続先ポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ +static int KcSocket_get_remote_port(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->remote_addrlen) + { + char tmp_port[NI_MAXSERV]; + int ret = getnameinfo( + (const struct sockaddr *)&info->remote_addr, + info->remote_addrlen, + NULL, 0, tmp_port, sizeof(tmp_port), NI_NUMERICSERV); + if (ret == 0) + { + return atoi(tmp_port); + } + } + return -1; +} + +/** + * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr ローカルアドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ +static const char *KcSocket_get_local_addr(KcSocket *sock, char *addr, size_t addrlen) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->local_addrlen) + { + int ret = getnameinfo( + (const struct sockaddr *)&info->local_addr, + info->remote_addrlen, + addr, addrlen, NULL, 0, NI_NUMERICHOST); + if (ret == 0) + { + return addr; + } + } + return NULL; +} + +/** + * ソケットのローカルポート番号を返します。 + * ローカルポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ +static int KcSocket_get_local_port(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->local_addrlen) + { + char tmp_port[NI_MAXSERV]; + int ret = getnameinfo( + (const struct sockaddr *)&info->local_addr, + info->remote_addrlen, + NULL, 0, tmp_port, sizeof(tmp_port), NI_NUMERICSERV); + if (ret == 0) + { + return atoi(tmp_port); + } + } + return -1; +} + +/** + * ソケットディスクリプタを返します。 + * 通常は、ソケットディスクリプタを直接操作しないでください。 + * + * @param sock 対象ソケット + * @return ソケットディスクリプタ + */ +static socket_t KcSocket_get_socket(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + return info->sock_fd; +} + +/** + * ソケットタイプを返します。 + * + * @param sock 対象ソケット + * @return ソケットタイプ + */ +static int KcSocket_get_type(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + return info->sock_type; +} + +/** + * ソケットファミリを返します。 + * ソケット接続、バインド時には、実際にバインド、 + * あるいは接続されているソケットファミリを返します。 + * + * @param sock 対象ソケット + * @return ソケットファミリ + */ +static int KcSocket_get_family(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int result_family = info->sock_family; + if (info->sock_fd != INVALID_SOCKET) + { + struct sockaddr_storage ss; + socklen_t len = sizeof(struct sockaddr_storage); + if (getsockname(info->sock_fd, (struct sockaddr *)&ss, &len) == 0) + { + result_family = ss.ss_family; + } + } + return result_family; +} + +/** + * 指定されたアドレス、サービスのアドレス情報を取得します。 + * + * @param sock 対象ソケット + * @param result アドレス情報格納用ポインタ + * @param addr アドレス + * @param service サービス + * @param is_passive サーバソケットの場合、true を指定ください。 + */ +static bool KcSocket_get_addrinfo( + KcSocket *sock, struct addrinfo **result, + const char *addr, const char *service, bool is_passive) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + + struct addrinfo hints; + memset(&hints, 0x00, sizeof(struct addrinfo)); + hints.ai_socktype = info->sock_type; + hints.ai_family = info->sock_family; + if (is_passive) + { + hints.ai_flags = AI_PASSIVE; + } + int ret = getaddrinfo(addr, service, &hints, result); + return (ret == 0); +} + +/** + * 指定されたアドレス、サービスにバインドします。 + * 指定されたアドレスとサービスにより、複数候補がある場合は、 + * 最初に見つかったアドレスとポートにバインドします。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス (例: "80", "http", "ssh" など) + * @return true/false (bind 成功/失敗) + */ +static bool KcSocket_bind(KcSocket *sock, const char *addr, const char *service) +{ + struct addrinfo *bind_addr; + bool is_success = sock->get_addrinfo(sock, &bind_addr, addr, service, true); + if (is_success) + { + is_success = KcSocket_addrinfo_bind_and_connect(sock, bind_addr, NULL); + freeaddrinfo(bind_addr); + } + return is_success; +} + +/** + * ソケットを接続待ちソケットとしてマークをつけます。 + * 保留中の接続のキュー最大長を指定します。 + * + * @param sock 対象ソケット + * @param backlog バックログ + * @return true/false (成功/失敗) + */ +static bool KcSocket_listen(KcSocket *sock, int backlog) +{ + bool result = false; + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->sock_fd != INVALID_SOCKET) + { + int ret = listen(info->sock_fd, backlog); + result = (ret != SOCKET_ERROR); + } + return result; +} + +/** + * ソケットへの接続を受け付けます。 + * + * @param sock 対象ソケット + * @return 受け付けたソケット(失敗時NULL) + */ +static KcSocket *KcSocket_accept(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + KcSocket *client = NULL; + if (info->sock_fd != INVALID_SOCKET) + { + client = KcSocket_new(info->sock_type, info->sock_family); + if (client != NULL) + { + KcSocketInfo *cinfo = (KcSocketInfo *)client->_info; + cinfo->remote_addrlen = sizeof(struct sockaddr_storage); + cinfo->sock_fd = accept(info->sock_fd, (struct sockaddr *)&cinfo->remote_addr, &cinfo->remote_addrlen); + if (cinfo->sock_fd != INVALID_SOCKET) + { + memcpy(&cinfo->local_addr, &info->local_addr, info->local_addrlen); + cinfo->local_addrlen = info->local_addrlen; + cinfo->sock_type = SOCK_STREAM; + cinfo->sock_family = cinfo->remote_addr.ss_family; + } + else + { + KcSocket_delete(client); + client = NULL; + } + } + } + return client; +} + +/** + * 指定されたアドレス、サービス接続します。 + * local_addr に NULL を指定した場合、自動的にローカルのアドレス、サービスが設定されます。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス + * @param local_addr ローカルアドレス + * @param local_service ローカルサービス + * @return true/false (成功/失敗) + */ +static bool KcSocket_connect( + KcSocket *sock, const char *addr, const char *service, + const char *local_addr, const char *local_service) +{ + // 接続先アドレス情報取得 + struct addrinfo *conn_addr = NULL; + if (!sock->get_addrinfo(sock, &conn_addr, addr, service, false)) + { + return false; + } + + // バインドアドレス情報取得 + bool is_success = false; + struct addrinfo *bind_addr = NULL; + if (local_addr != NULL) + { // bind が必要 + is_success = sock->get_addrinfo(sock, &bind_addr, local_addr, local_service, true); + if (is_success) + { + is_success = KcSocket_addrinfo_bind_and_connect(sock, bind_addr, conn_addr); + freeaddrinfo(bind_addr); + } + } + else + { + is_success = KcSocket_addrinfo_connect(sock, conn_addr, INVALID_SOCKET); + } + freeaddrinfo(conn_addr); + return is_success; +} + +/** + * ソケットをクローズします。 + * + * @param sock 対象ソケット + * @return true/false (成功/失敗) + */ +static bool KcSocket_close(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + + if (info->sock_family == SOCK_STREAM) + { // TCP の場合は、出力を閉じて、届いているデータを読み取ってから close する。 + int ret = shutdown(info->sock_fd, SHUT_WR); + if (ret == 0) + { + int read_size; + char buff[1024]; + do + { // 届いているデータを全て読み取る + read_size = recv(info->sock_fd, buff, sizeof(buff), 0); + } while (read_size > 0); + } + } + sockclose(info->sock_fd); + return true; +} + +/** + * メッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 送信されたデータサイズ + */ +static ssize_t KcSocket_send(KcSocket *sock, const char *buff, size_t size, int flags) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + ssize_t write_size = 0; + do + { + errno = 0; + write_size = send(info->sock_fd, buff, size, flags); + } while ((write_size < 0) && (errno == EINTR)); + return write_size; +} + +/** + * メッセージを受信します。 + * 受信したメッセージは指定されたバッファに格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 読み取ったデータサイズ + */ +static ssize_t KcSocket_recv(KcSocket *sock, char *buff, size_t size, int flags) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + ssize_t read_size; + do + { + errno = 0; + read_size = recv(info->sock_fd, buff, size, flags); + } while ((read_size < 0) && (errno == EINTR)); + return read_size; +} + +/** + * 指定されたアドレス、サービスにメッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param addr アドレス + * @param service サービス + * @return 送信メッセージサイズ、エラー発生時は -1 + */ +static ssize_t KcSocket_sendto( + KcSocket *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service) +{ + (void)sock; + (void)buff; + (void)size; + (void)flags; + (void)addr; + (void)service; + return 0; +} + +/** + * メッセージを受信します。 + * src_addr, src_service がいずれも NULL 出ない場合、 + * 送信元のアドレスとサービスが格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param src_addr 送信元アドレス格納用バッファ + * @param src_addrlen 送信元アドレス格納用バッファサイズ + * @param src_service 送信元サービス格納用バッファ + * @param src_servicelen 送信元サービス格納用バッファサイズ + * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + */ +static ssize_t KcSocket_recvfrom( + KcSocket *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen) +{ + (void)sock; + (void)buff; + (void)size; + (void)flags; + (void)src_addr; + (void)src_addrlen; + (void)src_service; + (void)src_servicelen; + return 0; +} + +/** + * TTL を設定します。 + * + * @param sock 対象ソケット + * @param val 設定する TTL + * @return true/false (成功/失敗) + */ +static bool KcSocket_set_ttl(KcSocket *sock, int val) +{ + (void)sock; + (void)val; + return true; +} + +/** + * マルチキャストグループに参加するために JOIN します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @param ifindex インタフェースインデックス + * @return true/false (成功/失敗) + */ +static bool KcSocket_join( + KcSocket *sock, const char *addr, const char *ifname, unsigned int ifindex) +{ + (void)sock; + (void)addr; + (void)ifname; + (void)ifindex; + return true; +} + +/** + * マルチキャストグループから離脱します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @return true/false (成功/失敗) + */ +static bool KcSocket_leave( + KcSocket *sock, const char *addr) +{ + (void)sock; + (void)addr; + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// [内部関数] +// + +/** + * 指定された接続アドレス情報をもとにソケットの生成、接続を試みます。 + * 接続済みソケット sockfd が有効な場合、sockfd を用いて接続します。 + * + * @param sock 対象ソケット + * @param conn_addrinfo 接続アドレス情報 + * @param sockfd 接続済みソケット + * @return true/false (接続成功/失敗) + */ +static bool KcSocket_addrinfo_connect(KcSocket *sock, struct addrinfo *conn_addrinfo, socket_t sockfd) +{ + bool is_success = false; + socket_t tmp_sock = sockfd; + for (struct addrinfo *rp = conn_addrinfo; rp != NULL; rp = rp->ai_next) + { + if (sockfd == INVALID_SOCKET) + { // sockfd が無効の場合、ソケットを生成する。 + tmp_sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (tmp_sock == INVALID_SOCKET) + { + continue; + } + } + + int ret = connect(tmp_sock, rp->ai_addr, rp->ai_addrlen); + is_success = (ret != SOCKET_ERROR); + if (is_success) + { + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + info->sock_fd = tmp_sock; + info->remote_addrlen = rp->ai_addrlen; + memcpy(&info->remote_addr, rp->ai_addr, rp->ai_addrlen); + break; + } + + if (sockfd != INVALID_SOCKET) + { // sockfd が無効の場合、一時的に生成したソケットをクローズする。 + sockclose(tmp_sock); + } + } + return is_success; +} + +/** + * 指定された接続アドレス情報をもとにソケットの生成、バインドを試みます。 + * conn_addrinfo が NULL でない場合、バインド後に接続を試みます。 + * + * @param sock 対象ソケット + * @param bind_addrinfo バインドアドレス情報 + * @param conn_addrinfo 接続アドレス情報 + * @return true/false (接続成功/失敗) + */ +static bool KcSocket_addrinfo_bind_and_connect( + KcSocket *sock, struct addrinfo *bind_addrinfo, struct addrinfo *conn_addrinfo) +{ + int ret; + bool is_success = false; + + for (struct addrinfo *rp = bind_addrinfo; rp != NULL; rp = rp->ai_next) + { // sokcet : ソケット生成 + socket_t tmp_sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (tmp_sock == INVALID_SOCKET) + { + continue; + } + + // bind + const int on = 1; + setsockopt(tmp_sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on)); + ret = bind(tmp_sock, rp->ai_addr, rp->ai_addrlen); + is_success = (ret != SOCKET_ERROR); + if (!is_success) + { // bind 失敗したので次へ + sockclose(tmp_sock); + continue; + } + + // connect + if (conn_addrinfo) + { + is_success = KcSocket_addrinfo_connect(sock, conn_addrinfo, tmp_sock); + if (!is_success) + { // connect 失敗したので次へ + sockclose(tmp_sock); + continue; + } + } + + // bind または、bind と connect 成功 + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + info->sock_fd = tmp_sock; + info->local_addrlen = rp->ai_addrlen; + memcpy(&info->local_addr, rp->ai_addr, rp->ai_addrlen); + break; + } + return is_success; +} diff --git a/modules/src/kc_threads.c b/modules/src/kc_threads.c new file mode 100644 index 0000000..dd600bb --- /dev/null +++ b/modules/src/kc_threads.c @@ -0,0 +1,194 @@ +/** + * @file kc_thread.c + * @brief スレッドモジュール + * @copyright 2020 - 2024 Nomura Kei + */ +#include +#include +#include + +#include +#include +#include + +/** + * KcThread 管理情報 + */ +typedef struct +{ + thrd_t tid; + void *args; + int (*run)(void *args); + atomic_bool alive_state; +} KcThreadInfo; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static bool KcThread_is_alive(KcThread *thread); +static bool KcThread_start(KcThread *thread, void *args); +static bool KcThread_join(KcThread *thread); +static int KcThread_run(void *thread); + +// ============================================================================= +// new +// ============================================================================= +/** + * スレッドを生成します。 + * + * @param run スレッドにて実行する関数へのポインタ + */ +KcThread *KcThread_new(int (*run)(void *args)) +{ + // KcThread の管理構造 + // +--------------+ + // | KcThread | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // +--------------+ + KcThread *thread = (KcThread *)malloc(sizeof(KcThread) + sizeof(KcThreadInfo)); + if (thread != NULL) + { + thread->is_alive = KcThread_is_alive; + thread->start = KcThread_start; + thread->join = KcThread_join; + thread->_info = (thread + 1); + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + // info->tid + info->run = run; + atomic_init(&info->alive_state, false); + } + return thread; +} + +// ============================================================================= +// delete +// ============================================================================= +/** + * Thread を破棄します。 + * + * @param thread 破棄するスレッド + */ +void KcThread_delete(KcThread *thread) +{ + free(thread); +} + +// ============================================================================= +// sleep +// ============================================================================= +/** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープさせるミリ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ +bool KcThread_msleep(long time, bool force) +{ + long sec = time / 1000; + long msec = time - (sec * 1000); + long nsec = msec * 1000 * 1000; + return KcThread_sleep(sec, nsec, force); +} + +/** + * 指定された sec 秒 + nsec ナノ秒間現在のスレッドをスリープさせます。 + * + * @param sec スリープさせる秒数 + * @param nsec スリープさせるナノ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ +bool KcThread_sleep(long sec, long nsec, bool force) +{ + struct timespec remaining = {sec, nsec}; + int ret; + do + { + errno = 0; + ret = thrd_sleep(&remaining, &remaining); + } while ((ret == -1) && force && (errno == EINTR)); + return (ret == 0); +} + +// ============================================================================= +// is_alive +// ============================================================================= +/** + * 指定されたスレッドが生きているか否かを返します。 + * + * @param thread 対象スレッド + * @return true/false (スレッドが生存している/していない) + */ +static bool KcThread_is_alive(KcThread *thread) +{ + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + return atomic_load(&info->alive_state); +} + +// ============================================================================= +// start +// ============================================================================= +/** + * スレッドを開始します。 + * + * @param thread 対象スレッド + * @param args スレッドに渡す情報 + * @return true/false (スレッド起動成功/失敗) + */ +static bool KcThread_start(KcThread *thread, void *args) +{ + int ret = thrd_error; + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + if (!atomic_load(&info->alive_state)) + { + info->args = args; + ret = thrd_create(&info->tid, KcThread_run, thread); + } + return (ret == thrd_success); +} + +// ============================================================================= +// join +// ============================================================================= +/** + * スレッドが終了するのを待機します。 + * + * @param thread 対象スレッド + * @return true/false (成功/失敗) + */ +static bool KcThread_join(KcThread *thread) +{ + int ret = thrd_error; + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + ret = thrd_join(info->tid, NULL); + return (ret == thrd_success); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// 内部関数 +// + +// ============================================================================= +// run +// ============================================================================= +/** + * スレッドで実行される関数。 + * start にて関数を別スレッドにて実行します。 + * + * @param args スレッド情報 + * @return + */ +int KcThread_run(void *args) +{ + KcThread *thread = (KcThread *)args; + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + atomic_store(&info->alive_state, true); + int ret = info->run(info->args); + atomic_store(&info->alive_state, false); + return ret; +} diff --git a/modules/src/kc_threads_win.c b/modules/src/kc_threads_win.c new file mode 100644 index 0000000..0439fda --- /dev/null +++ b/modules/src/kc_threads_win.c @@ -0,0 +1,201 @@ +/** + * @file kc_thread.c + * @brief スレッドモジュール + * @copyright 2020 - 2024 Nomura Kei + */ +#include +#include + +#if (KC_IS_WINDOWS) + +int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) +{ + if ((thr == NULL) || (func == NULL)) + { + return thrd_error; + } + + thr->handle = CreateThread( + NULL, // セキュリティ属性 + 0, // スタックサイズ + (LPTHREAD_START_ROUTINE)func, // スレッド関数 + arg, // 引数 + 0, // 作成フラグ + &(thr->thread_id) // スレッドID + ); + + if (thr->handle == NULL) + { + return thrd_error; + } + return thrd_success; +} +int thrd_join(thrd_t thr, int *res) +{ + if (WaitForSingleObject(thr.handle, INFINITE) != WAIT_OBJECT_0) + { + return thrd_error; + } + if (res != NULL) + { + DWORD retcode; + if (GetExitCodeThread(thr.handle, &retcode) == 0) + { + return thrd_error; + } + *res = (int)retcode; + } + CloseHandle(thr.handle); + return thrd_success; +} + +int thrd_detach(thrd_t thr) +{ + if (thr.handle == NULL) + { + return thrd_error; + } + if (CloseHandle(thr.handle) == 0) + { + return thrd_error; + } + return thrd_success; +} + +thrd_t thrd_current(void) +{ + thrd_t current; + current.handle = GetCurrentThread(); + current.thread_id = GetThreadId(current.handle); + return current; +} + +int thrd_equal(thrd_t lhs, thrd_t rhs) +{ + return (lhs.thread_id == rhs.thread_id); +} + +void thrd_yield(void) +{ + SwitchToThread(); +} + +int thrd_sleep(const struct timespec *duration, struct timespec *remaining) +{ + if (duration == NULL) + { + return thrd_error; + } + + // Windows の Sleep は、ms 単位 + DWORD msec = (DWORD)(duration->tv_sec * 1000 + duration->tv_nsec / 1000000); + Sleep(msec); + + // 常に成功したものとして、remaining は 0 に設定する。 + if (remaining != NULL) + { + remaining->tv_sec = 0; + remaining->tv_nsec = 0; + } + return thrd_success; +} + +int mtx_init(mtx_t *mtx, int type) +{ + if (mtx == NULL) + { + return thrd_error; + } + mtx->type = type; + if (type & mtx_plain) + { + InitializeCriticalSection(&mtx->cs); + } + else if (type & mtx_recursive) + { + InitializeCriticalSection(&mtx->cs); + } + else + { + return thrd_error; + } + return thrd_success; +} + +void mtx_destroy(mtx_t *mtx) +{ + DeleteCriticalSection(&mtx->cs); +} +int mtx_lock(mtx_t *mtx) +{ + EnterCriticalSection(&mtx->cs); + return thrd_success; +} +int mtx_unlock(mtx_t *mtx) +{ + LeaveCriticalSection(&mtx->cs); + return thrd_success; +} + +int cnd_init(cnd_t *cond) +{ + if (cond == NULL) + { + return thrd_error; + } + InitializeConditionVariable(&cond->cond); + return thrd_success; +} + +int cnd_signal(cnd_t *cond) +{ + if (cond == NULL) + { + return thrd_error; + } + WakeConditionVariable(&cond->cond); + return thrd_success; +} +int cnd_broadcast(cnd_t *cond) +{ + if (cond == NULL) + { + return thrd_error; + } + WakeAllConditionVariable(&cond->cond); + return thrd_success; +} +int cnd_wait(cnd_t *cond, mtx_t *mtx) +{ + if ((cond == NULL) || (mtx == NULL)) + { + return thrd_error; + } + if (SleepConditionVariableCS(&cond->cond, &mtx->cs, INFINITE)) + { + return thrd_success; + } + return thrd_error; +} + +int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts) +{ + if ((cond == NULL) || (mtx == NULL) || (ts == NULL)) + { + return thrd_error; + } + DWORD msec = (DWORD)(ts->tv_sec * 1000 + ts->tv_nsec / 1000000); + if (SleepConditionVariableCS(&cond->cond, &mtx->cs, msec)) + { + return thrd_timedout; + } + return thrd_error; +} + +void cnd_destroy(cnd_t *cond) +{ + // Nothing to do + UNUSED_VARIABLE(cond); +} + +#endif \ No newline at end of file diff --git a/config.mk b/config.mk index a0ac27c..f7c6e47 100644 --- a/config.mk +++ b/config.mk @@ -25,14 +25,17 @@ # for Windows CROSS_COMPILE ?=i686-w64-mingw32- LIBS ?= -lws2_32 +DEBUG_OPTIONS ?= else # For Linux ifeq ($(strip $(ARCH)),aarch64) CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector else CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector endif endif diff --git a/mk/base-cmd.mk b/mk/base-cmd.mk index f429bcf..e74c114 100644 --- a/mk/base-cmd.mk +++ b/mk/base-cmd.mk @@ -33,8 +33,13 @@ CC = $(CROSS_COMPILE)gcc CXX = $(CROSS_COMPILE)g++ + #CC = $(CROSS_COMPILE)clang #CXX = $(CROSS_COMPILE)clang++ + +#CC = $(CROSS_COMPILE)clang-16 +#CXX = $(CROSS_COMPILE)clang++16 + RANLIB = $(CROSS_COMPILE)ranlib ADDR2LINE = $(CROSS_COMPILE)addr2line AR = $(CROSS_COMPILE)ar diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 2cc417c..eebded1 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -69,7 +69,7 @@ # 値が2の場合、さらなるチェックを追加するが、規格準拠のプログラムが失敗する可能性がある。 # いくつかのチェックは、コンパイル時に実行され、コンパイラの警告として表示される。 # ------------------------------------------------------------------------------ -DEBUG_OPTIONS += -fstack-protector +# DEBUG_OPTIONS += -fstack-protector DEBUG_OPTIONS += --coverage DEBUG_OPTIONS += -g3 -ggdb DEBUG_OPTIONS += -O0 diff --git a/mk/link-a-conf.mk b/mk/link-a-conf.mk index 0e872f3..631e6d5 100644 --- a/mk/link-a-conf.mk +++ b/mk/link-a-conf.mk @@ -10,8 +10,11 @@ HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) +HEADER_FILES_WIN = $(wildcard include/win/*.h) $(wildcard include/win/*.hpp) +TOP_HEADER_FILES_WIN = $(addprefix $(TOPDIR)/include/win/,$(notdir $(HEADER_FILES_WIN))) + CLEAN_FILES += $(TOP_TARGET) -CLEAN_FILES += $(TOP_HEADER_FILES) +CLEAN_FILES += $(TOP_HEADER_FILES) $(TOP_HEADER_FILES_WIN) endif endif diff --git a/mk/link-a-rule.mk b/mk/link-a-rule.mk index d94a54a..657d858 100644 --- a/mk/link-a-rule.mk +++ b/mk/link-a-rule.mk @@ -10,6 +10,7 @@ $(TOP_TARGET): $(TARGET) ifneq ($(strip $(HEADER_FILES)),) $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ + $(CP) -f $(HEADER_FILES_WIN) $(TOPDIR)/include/win/ endif $(CP) -f $(TARGET) $(TOPDIR)/lib/ diff --git a/modules/include/kc_lock_guard.h b/modules/include/kc_lock_guard.h index 8bb70bd..ad3915f 100644 --- a/modules/include/kc_lock_guard.h +++ b/modules/include/kc_lock_guard.h @@ -7,18 +7,16 @@ #define KC_LOCK_GUARD_H #include -#include - +#include /** * LockGuard 管理構造体 */ typedef struct { - mtx_t* mutex; + mtx_t *mutex; } KcLockGuard; - /** * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 * 使用例) @@ -34,8 +32,7 @@ * } */ #define kc_lock_guard(lock) \ - for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) - + for (KcLockGuard _guard = kc_lock_guard_init(lock, __FILE__, __func__, __LINE__); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 @@ -44,15 +41,13 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex); - +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line); /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard); +void kc_lock_guard_release(KcLockGuard *guard); - -#endif // KC_LOCK_GUARD_H +#endif // KC_LOCK_GUARD_H diff --git a/modules/include/kc_memory.h b/modules/include/kc_memory.h index ffce4ed..c6ba9e9 100644 --- a/modules/include/kc_memory.h +++ b/modules/include/kc_memory.h @@ -22,12 +22,13 @@ #ifndef KC_MEMORY_H #define KC_MEMORY_H -#include +#include #include #include #include #include +#include #ifdef __cplusplus extern "C" @@ -273,8 +274,6 @@ #define calloc(nmemb, size) kc_memory_manager->calloc(nmemb, size, __FILE__, __func__, __LINE__) #define realloc(ptr, size) kc_memory_manager->realloc(ptr, size, __FILE__, __func__, __LINE__) #define free(ptr) kc_memory_manager->free(ptr) -#else -#include #endif #ifdef __cplusplus diff --git a/modules/include/kc_socket.h b/modules/include/kc_socket.h new file mode 100644 index 0000000..7d952c7 --- /dev/null +++ b/modules/include/kc_socket.h @@ -0,0 +1,291 @@ +/** + * @file kc_socket.h + * @brief ソケットモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_SOCKET_H +#define KC_SOCKET_H + +#include + +#if (KC_IS_WINDOWS) +typedef SOCKET socket_t; +#define SHUT_RD SD_RECEIVE +#define SHUT_WR SD_SEND +#define SHUT_RDWR SD_BOTH +#define sockclose(sockfd) closesocket(sockfd) +#else +#include +#include +#include +#include +#include +#include +#include +typedef int socket_t; +#define INVALID_SOCKET (-1) +#define SOCKET_ERROR (-1) +#define sockclose(sockfd) close(sockfd) +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcSocket_ + { + + /** + * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr 接続先アドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_remote_addr)(struct KcSocket_ *sock, char *addr, size_t addrlen); + + /** + * ソケットの接続先ポート番号を返します。 + * 接続先ポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_remote_port)(struct KcSocket_ *sock); + + /** + * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr ローカルアドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_local_addr)(struct KcSocket_ *sock, char *addr, size_t addlen); + + /** + * ソケットのローカルポート番号を返します。 + * ローカルポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_local_port)(struct KcSocket_ *sock); + + /** + * ソケットディスクリプタを返します。 + * 通常は、ソケットディスクリプタを直接操作しないでください。 + * + * @param sock 対象ソケット + * @return ソケットディスクリプタ + */ + socket_t (*get_socket)(struct KcSocket_ *sock); + + /** + * ソケットタイプを返します。 + * + * @param sock 対象ソケット + * @return ソケットタイプ + */ + int (*get_type)(struct KcSocket_ *sock); + + /** + * ソケットファミリを返します。 + * 実際にバインド、あるいは接続されているソケットファミリを返します。 + * + * @param sock 対象ソケット + * @return ソケットファミリ + */ + int (*get_family)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービスのアドレス情報を取得します。 + * + * @param sock 対象ソケット + * @param result アドレス情報格納用ポインタ + * @param addr アドレス + * @param service サービス + * @param is_passive サーバソケットの場合、true を指定ください。 + */ + bool (*get_addrinfo)( + struct KcSocket_ *sock, + struct addrinfo **result, + const char *addr, + const char *service, + bool is_passive); + + /** + * 指定されたアドレス、サービスにバインドします。 + * 指定されたアドレスとサービスにより、複数候補がある場合は、 + * 最初に見つかったアドレスとポートにバインドします。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス (例: "80", "http", "ssh" など) + * @return true/false (bind 成功/失敗) + */ + bool (*bind)(struct KcSocket_ *sock, const char *addr, const char *service); + + /** + * ソケットを接続待ちソケットとしてマークをつけます。 + * 保留中の接続のキュー最大長を指定します。 + * + * @param sock 対象ソケット + * @param backlog バックログ + * @return true/false (成功/失敗) + */ + bool (*listen)(struct KcSocket_ *sock, int backlog); + + /** + * ソケットへの接続を受け付けます。 + * + * @param sock 対象ソケット + * @return 受け付けたソケット + */ + struct KcSocket_ *(*accept)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービス接続します。 + * local_addr に NULL を指定した場合、自動的にローカルのアドレスが設定されます。 + * local_service に -1 を指定した場合、自動的にローカルのポート番号が割りつけられます。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス + * @param local_addr ローカルアドレス + * @param local_service ローカルサービス + * @return true/false (成功/失敗) + */ + bool (*connect)(struct KcSocket_ *sock, const char *addr, const char *service, const char *local_addr, const char *local_port); + + /** + * ソケットをクローズします。 + * + * @param sock 対象ソケット + * @return true/false (成功/失敗) + */ + bool (*close)(struct KcSocket_ *sock); + + /** + * メッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 送信されたデータサイズ + */ + ssize_t (*send)(struct KcSocket_ *sock, const char *buff, size_t size, int flags); + + /** + * メッセージを受信します。 + * 受信したメッセージは指定されたバッファに格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 読み取ったデータサイズ + */ + ssize_t (*recv)(struct KcSocket_ *sock, char *buff, size_t size, int flags); + + /** + * 指定されたアドレス、サービスにメッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param addr アドレス + * @param service サービス + * @return 送信メッセージサイズ、エラー発生時は -1 + */ + ssize_t (*sendto)( + struct KcSocket_ *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service); + + /** + * メッセージを受信します。 + * src_addr, src_service がいずれも NULL 出ない場合、 + * 送信元のアドレスとサービスが格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param src_addr 送信元アドレス格納用バッファ + * @param src_addrlen 送信元アドレス格納用バッファサイズ + * @param src_service 送信元サービス格納用バッファ + * @param src_servicelen 送信元サービス格納用バッファサイズ + * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + */ + ssize_t (*recvfrom)( + struct KcSocket_ *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen); + + /** + * TTL を設定します。 + * + * @param sock 対象ソケット + * @param val 設定する TTL + * @return true/false (成功/失敗) + */ + bool (*set_ttl)(struct KcSocket_ *sock, int val); + + /** + * マルチキャストグループに参加するために JOIN します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @param ifindex インタフェースインデックス + * @return true/false (成功/失敗) + */ + bool (*join)(struct KcSocket_ *sock, const char *addr, const char *ifname, unsigned int ifindex); + + /** + * マルチキャストグループから離脱します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @return true/false (成功/失敗) + */ + bool (*leave)(struct KcSocket_ *sock, const char *addr); + + /** + * Socket 管理情報 + */ + void *_info; + + } KcSocket; + + /** + * 指定されたタイプ、ファミリのSocket オブジェクトを構築します。 + * + * @param type タイプ(SOCK_STREAM/SOCK_DGRAM/SOCK_RAW) + * @param family ファミリ(AF_UNSPEC/AF_INET/AF_INET6) + */ + KcSocket *KcSocket_new(int type, int family); + + /** + * Socket を破棄します。 + */ + void KcSocket_delete(KcSocket *socket); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_threads.h b/modules/include/kc_threads.h new file mode 100644 index 0000000..764176f --- /dev/null +++ b/modules/include/kc_threads.h @@ -0,0 +1,98 @@ +/** + * @file kc_threads.h + * @brief スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_THREADS_H +#define KC_THREADS_H + +#include + +#if (KC_IS_WINDOWS) +#include +#else +#include +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcThread_ + { + + /** + * 指定されたスレッドが生きているか否かを返します。 + * + * @param thread 対象スレッド + * @return true/false (スレッドが生存している/していない) + */ + bool (*is_alive)(struct KcThread_ *thread); + + /** + * スレッドを開始します。 + * + * @param thread 対象スレッド + * @param args スレッドに渡す情報 + * @return true/false (スレッド起動成功/失敗) + */ + bool (*start)(struct KcThread_ *thread, void *args); + + /** + * スレッドが終了するのを待機します。 + * + * @param thread 対象スレッド + */ + bool (*join)(struct KcThread_ *thread); + + /** + * Thread 管理情報 + */ + void *_info; + + } KcThread; + + /** + * スレッドを生成します。 + * + * @param run スレッドにて実行する関数へのポインタ + */ + KcThread *KcThread_new(int (*run)(void *args)); + + /** + * Thread を破棄します。 + * + * @param thread 破棄するスレッド + */ + void KcThread_delete(KcThread *thread); + + /** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープさせるミリ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_msleep(long time, bool force); + + /** + * 指定された sec 秒 + nsec ナノ秒間現在のスレッドをスリープさせます。 + * + * @param sec スリープさせる秒数 + * @param nsec スリープさせるナノ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_sleep(long sec, long nsec, bool force); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_THREADS_H diff --git a/modules/include/kc_threads_win.h b/modules/include/kc_threads_win.h new file mode 100644 index 0000000..b40ab10 --- /dev/null +++ b/modules/include/kc_threads_win.h @@ -0,0 +1,98 @@ +/** + * @file kc_windows_thread.h + * @brief Windows 用スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_WINDOWS_THREAD_H +#define KC_WINDOWS_THREAD_H + +#include +#if (KC_IS_WINDOWS) +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#define thread_local _Thread_local + + //////////////////////////////////////////////////////////////////////// + // + // Thread + // + + enum + { + thrd_success = 0, //!< 成功 + thrd_bussy = 1, //!< busy + thrd_error = 2, //!< エラー発生 + thrd_nomem = 3, //!< メモリ不足 + thrd_timedout = 4 //!< タイムアウト発生 + }; + + typedef struct + { + HANDLE handle; + DWORD thread_id; + } thrd_t; + + typedef int (*thrd_start_t)(void *); + int thrd_create(thrd_t *thr, thrd_start_t func, void *arg); + int thrd_join(thrd_t thr, int *res); + int thrd_detach(thrd_t thr); + thrd_t thrd_current(void); + int thrd_equal(thrd_t lhs, thrd_t rhs); + int thrd_sleep(const struct timespec *duration, struct timespec *remaining); + + //////////////////////////////////////////////////////////////////////// + // + // mutex + // + + enum + { + mtx_plain = 0, //!< 通常 + mtx_recursive = 1, //!< 再帰 + mtx_timed = 2 //!< timed + }; + + typedef struct + { + CRITICAL_SECTION cs; + int type; + } mtx_t; + + int mtx_init(mtx_t *mtx, int type); + void mtx_destroy(mtx_t *mtx); + int mtx_lock(mtx_t *mtx); + int mtx_unlock(mtx_t *mtx); + + //////////////////////////////////////////////////////////////////////// + // + // cond + // + + typedef struct + { + CONDITION_VARIABLE cond; + } cnd_t; + + int cnd_init(cnd_t *cond); + int cnd_signal(cnd_t *cond); + int cnd_broadcast(cnd_t *cond); + int cnd_wait(cnd_t *cond, mtx_t *mtx); + int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts); + void cnd_destroy(cnd_t *cond); + +#endif // (KC_IS_WINDOWS) +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_windows.h b/modules/include/kc_windows.h index ee70898..6e1c659 100644 --- a/modules/include/kc_windows.h +++ b/modules/include/kc_windows.h @@ -37,9 +37,9 @@ #endif // よく利用されるヘッダファイルをインクルードする -#include #include #include +#include #ifdef _MSC_VER #pragma comment(lib, "ws2_32.lib") #endif diff --git a/modules/src/kc_assert.c b/modules/src/kc_assert.c index 371fc8f..7cd4ecc 100644 --- a/modules/src/kc_assert.c +++ b/modules/src/kc_assert.c @@ -6,9 +6,9 @@ #include #include #include -#include #include #include +#include // プロトタイプ宣言 static void assert_default_handler(const char *msg); diff --git a/modules/src/kc_env.c b/modules/src/kc_env.c index 0675c73..95f185c 100644 --- a/modules/src/kc_env.c +++ b/modules/src/kc_env.c @@ -50,7 +50,7 @@ char *result = NULL; #if (KC_IS_WINDOWS) static char buff[KC_ENV_BUFFER_MAX]; - DWORD ret = GetEnvironmentVarable(key_name, (LPSTR)buff, sizeof(buff)); + DWORD ret = GetEnvironmentVariable(key_name, (LPSTR)buff, sizeof(buff)); if (ret != 0) { result = buff; diff --git a/modules/src/kc_list_array.c b/modules/src/kc_list_array.c index 246ed14..f29fc08 100644 --- a/modules/src/kc_list_array.c +++ b/modules/src/kc_list_array.c @@ -3,10 +3,14 @@ * @brief 配列リストモジュール * @copyright 2003 - 2023 Nomura Kei */ +#include #if defined(__GNUC__) #define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) #define qsort_s qsort_r #endif +#endif // defined(__GNUC__) + #include #include #include @@ -17,6 +21,7 @@ #include #include #include + /** * KcArrayList 管理情報 */ @@ -306,7 +311,11 @@ * @param context コンテキスト(KcListSortInfo) * @return 比較結果 */ +#if (KC_IS_WINDOWS) +static int KcArrayList_comparator(void *context, const void *x, const void *y) +#else static int KcArrayList_comparator(const void *x, const void *y, void *context) +#endif { KcListSortInfo *sort_info = (KcListSortInfo *)context; int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); diff --git a/modules/src/kc_list_linked.c b/modules/src/kc_list_linked.c index 30575b9..47edbe4 100644 --- a/modules/src/kc_list_linked.c +++ b/modules/src/kc_list_linked.c @@ -3,10 +3,14 @@ * @brief Linked リストモジュール * @copyright 2003 - 2023 Nomura Kei */ +#include #if defined(__GNUC__) #define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) #define qsort_s qsort_r #endif +#endif // defined(__GNUC__) + #include #include #include @@ -18,6 +22,13 @@ #include #include +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) +#define qsort_s qsort_r +#endif // KC_IS_WINDOWS +#endif // defined(__GNUC__) + /** * KcLinkedList Entry 情報 */ @@ -291,7 +302,11 @@ * @param context コンテキスト(KcListSortInfo) * @return 比較結果 */ +#if (KC_IS_WINDOWS) +static int KcLinkedList_comparator(void *context, const void *x, const void *y) +#else static int KcLinkedList_comparator(const void *x, const void *y, void *context) +#endif // KC_IS_WINDOWS { KcListSortInfo *sort_info = (KcListSortInfo *)context; const KcLinkedListEntry **entry_x = (const KcLinkedListEntry **)x; diff --git a/modules/src/kc_lock_guard.c b/modules/src/kc_lock_guard.c index 6ef68e5..648a153 100644 --- a/modules/src/kc_lock_guard.c +++ b/modules/src/kc_lock_guard.c @@ -7,7 +7,6 @@ #include - /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 * ロックに成功するとロック管理用のオブジェクトを返します。 @@ -15,36 +14,37 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex) +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line) { - KcLockGuard guard = { .mutex = mutex }; + KcLockGuard guard = {.mutex = mutex}; if (mutex == NULL) { errno = EINVAL; perror("kc_lock_guard_init: (mutex = NULL)"); + fprintf(stderr, "%s:%s:%d kc_lock_guard_init : mutex = NULL\n", file, func, line); return guard; } + errno = 0; int ret = mtx_lock(mutex); if (ret != thrd_success) { - perror("kc_lock_guard : mtx_lock error"); + fprintf(stderr, "%s:%s:%d kc_lock_guard : mtx_lock error\n", file, func, line); guard.mutex = NULL; } return guard; } - /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard) +void kc_lock_guard_release(KcLockGuard *guard) { - if (guard->mutex != NULL) { - mtx_unlock(guard->mutex); - guard->mutex = NULL; + if (guard->mutex != NULL) + { + mtx_unlock(guard->mutex); + guard->mutex = NULL; } } - diff --git a/modules/src/kc_memory.c b/modules/src/kc_memory.c index 5cc4186..6dbbc88 100644 --- a/modules/src/kc_memory.c +++ b/modules/src/kc_memory.c @@ -622,7 +622,11 @@ */ void *raw_aligned_alloc(size_t alignment, size_t size) { +#if (KC_IS_WINDOWS) + return _aligned_malloc(size, alignment); +#else return aligned_alloc(alignment, size); +#endif } /** diff --git a/modules/src/kc_memory_dump.c b/modules/src/kc_memory_dump.c index 111e328..49e63de 100644 --- a/modules/src/kc_memory_dump.c +++ b/modules/src/kc_memory_dump.c @@ -144,7 +144,7 @@ { // 空きバッファサイズ無しのため、何もしない。 return; } - int write_size = snprintf(info->write_ptr, info->rest_size, msg); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s", msg); if (write_size > info->rest_size) { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) write_size = (info->rest_size - 1); diff --git a/modules/src/kc_memory_listener.c b/modules/src/kc_memory_listener.c index 29ff2a0..ee69a13 100644 --- a/modules/src/kc_memory_listener.c +++ b/modules/src/kc_memory_listener.c @@ -98,5 +98,5 @@ KcMemoryDump_dump(buff, sizeof(buff), entry, 16, true, true, 130); fprintf(stderr, "[ERROR] %s", buff); } - fprintf(stderr, msg); + fprintf(stderr, "%s", msg); } diff --git a/modules/src/kc_queue.c b/modules/src/kc_queue.c index 544d763..d9f2a14 100644 --- a/modules/src/kc_queue.c +++ b/modules/src/kc_queue.c @@ -9,11 +9,11 @@ #include #include #include -#include #include #include #include +#include #include #include @@ -122,7 +122,10 @@ static int KcQueue_size(struct KcQueue_ *queue) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - size_t size = info->list->size(info->list); + size_t size = 0; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + size = info->list->size(info->list); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== return (int)size; } @@ -138,7 +141,11 @@ static bool KcQueue_is_empty(struct KcQueue_ *queue) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - return info->list->is_empty(info->list); + bool is_empty = true; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + is_empty = info->list->is_empty(info->list); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== + return is_empty; } // ============================================================================= @@ -155,7 +162,11 @@ static bool KcQueue_contains(struct KcQueue_ *queue, const void *element, size_t size) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - return info->list->contains(info->list, element, size); + bool is_contains = false; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + is_contains = info->list->contains(info->list, element, size); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== + return is_contains; } // ============================================================================= @@ -227,7 +238,11 @@ static void *KcQueue_peek(struct KcQueue_ *queue, size_t *size) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - return info->list->get(info->list, 0, size); + void *value = NULL; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + value = info->list->get(info->list, 0, size); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== + return value; } // ============================================================================= diff --git a/modules/src/kc_socket.c b/modules/src/kc_socket.c new file mode 100644 index 0000000..cb67996 --- /dev/null +++ b/modules/src/kc_socket.c @@ -0,0 +1,753 @@ +/** + * @file kc_socket.c + * @brief ソケットモジュール + * @copyright 2003 - 2024 Nomura Kei + */ +#include +#include + +#include +#include +#include +#include + +/** + * KcSocket 管理情報 + */ +typedef struct +{ + socket_t sock_fd; //!< ソケットディスクリプタ + int sock_type; //!< ソケットタイプ + int sock_family; //!< ソケットファミリ + struct sockaddr_storage remote_addr; //!< リモートアドレス + struct sockaddr_storage local_addr; //!< ローカルアドレス + socklen_t remote_addrlen; //!< リモートアドレス長 + socklen_t local_addrlen; //!< ローカルアドレス帳 +} KcSocketInfo; + +/** + * アドレス情報 + */ +typedef struct +{ + const char *addr; //!< アドレス + const char *service; //!< サービス +} KcSocketAddress; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static void Socket_setup(void); +static void Socket_cleanup(void); +static const char *KcSocket_get_remote_addr(KcSocket *sock, char *addr, size_t addrlen); +static int KcSocket_get_remote_port(KcSocket *sock); +static const char *KcSocket_get_local_addr(KcSocket *sock, char *addr, size_t addrlen); +static int KcSocket_get_local_port(KcSocket *sock); +static socket_t KcSocket_get_socket(KcSocket *sock); +static int KcSocket_get_type(KcSocket *sock); +static int KcSocket_get_family(KcSocket *sock); +static bool KcSocket_get_addrinfo( + KcSocket *sock, struct addrinfo **result, + const char *addr, const char *service, bool is_passive); +static bool KcSocket_bind(KcSocket *sock, const char *addr, const char *service); +static bool KcSocket_listen(KcSocket *sock, int backlog); +static KcSocket *KcSocket_accept(KcSocket *sock); +static bool KcSocket_connect( + KcSocket *sock, const char *addr, const char *service, + const char *local_addr, const char *local_service); +static bool KcSocket_close(KcSocket *sock); +static ssize_t KcSocket_send(KcSocket *sock, const char *buff, size_t size, int flags); +static ssize_t KcSocket_recv(KcSocket *sock, char *buff, size_t size, int flags); +static ssize_t KcSocket_sendto( + KcSocket *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service); +static ssize_t KcSocket_recvfrom( + KcSocket *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen); +static bool KcSocket_set_ttl(KcSocket *sock, int val); +static bool KcSocket_join(KcSocket *sock, const char *addr, const char *ifname, unsigned int ifindex); +static bool KcSocket_leave(KcSocket *sock, const char *addr); + +static bool KcSocket_addrinfo_bind_and_connect( + KcSocket *sock, struct addrinfo *bind_addrinfo, struct addrinfo *conn_addrinfo); +static bool KcSocket_addrinfo_connect(KcSocket *sock, struct addrinfo *conn_addrinfo, socket_t sockfd); + +/** + * ソケットのセットアップをします。 + */ +static void Socket_setup(void) +{ + static bool is_init = false; + if (!is_init) + { +#if (KC_IS_WINDOWS) + WSADATA wsa_data; + WSAStartup(MAKEWORD(2, 0), &wsa_data); +#endif + atexit(Socket_cleanup); + is_init = true; + } +} + +/** + * ソケットライブラリのクリーンアップをします。 + */ +static void Socket_cleanup(void) +{ +#if (KC_IS_WINDOWS) + WSACleanup(); +#endif +} + +// ============================================================================= +// new +// ============================================================================= +/** + * 指定されたタイプ、ファミリのSocket オブジェクトを構築します。 + * + * @param type タイプ(SOCK_STREAM/SOCK_DGRAM/SOCK_RAW) + * @param family ファミリ(AF_UNSPEC/AF_INET/AF_INET6) + */ +KcSocket *KcSocket_new(int type, int family) +{ + // KcSocket の管理構造 + // +----------------+ + // | KcSocket | + // | ... | + // | _info -----------+ + // +----------------+ | + // | |<--+ + // +----------------+ + Socket_setup(); + KcSocket *sock = (KcSocket *)malloc(sizeof(KcSocket) + sizeof(KcSocketInfo)); + if (sock != NULL) + { + sock->get_remote_addr = KcSocket_get_remote_addr; + sock->get_remote_port = KcSocket_get_remote_port; + sock->get_local_addr = KcSocket_get_local_addr; + sock->get_local_port = KcSocket_get_local_port; + sock->get_socket = KcSocket_get_socket; + sock->get_type = KcSocket_get_type; + sock->get_family = KcSocket_get_family; + sock->get_addrinfo = KcSocket_get_addrinfo; // for local + sock->bind = KcSocket_bind; + sock->listen = KcSocket_listen; + sock->accept = KcSocket_accept; + sock->connect = KcSocket_connect; + sock->close = KcSocket_close; + sock->send = KcSocket_send; + sock->recv = KcSocket_recv; + sock->sendto = KcSocket_sendto; + sock->recvfrom = KcSocket_recvfrom; + sock->set_ttl = KcSocket_set_ttl; + sock->join = KcSocket_join; + sock->leave = KcSocket_leave; + sock->_info = (sock + 1); + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + info->sock_fd = INVALID_SOCKET; + info->sock_type = type; + info->sock_family = family; + info->remote_addrlen = 0; + info->local_addrlen = 0; + } + return sock; +} + +// ============================================================================= +// delete +// ============================================================================= +/** + * Socket を破棄します。 + */ +void KcSocket_delete(KcSocket *socket) +{ + if (socket) + { + if (((KcSocketInfo *)socket->_info)->sock_fd != INVALID_SOCKET) + { + socket->close(socket); + } + free(socket); + } +} + +// ============================================================================= +// get_remote_addr +// ============================================================================= +/** + * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr 接続先アドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ +static const char *KcSocket_get_remote_addr(KcSocket *sock, char *addr, size_t addrlen) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->remote_addrlen) + { + int ret = getnameinfo( + (const struct sockaddr *)&info->remote_addr, + info->remote_addrlen, + addr, addrlen, NULL, 0, NI_NUMERICHOST); + if (ret == 0) + { + return addr; + } + } + return NULL; +} + +/** + * ソケットの接続先ポート番号を返します。 + * 接続先ポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ +static int KcSocket_get_remote_port(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->remote_addrlen) + { + char tmp_port[NI_MAXSERV]; + int ret = getnameinfo( + (const struct sockaddr *)&info->remote_addr, + info->remote_addrlen, + NULL, 0, tmp_port, sizeof(tmp_port), NI_NUMERICSERV); + if (ret == 0) + { + return atoi(tmp_port); + } + } + return -1; +} + +/** + * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr ローカルアドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ +static const char *KcSocket_get_local_addr(KcSocket *sock, char *addr, size_t addrlen) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->local_addrlen) + { + int ret = getnameinfo( + (const struct sockaddr *)&info->local_addr, + info->remote_addrlen, + addr, addrlen, NULL, 0, NI_NUMERICHOST); + if (ret == 0) + { + return addr; + } + } + return NULL; +} + +/** + * ソケットのローカルポート番号を返します。 + * ローカルポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ +static int KcSocket_get_local_port(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->local_addrlen) + { + char tmp_port[NI_MAXSERV]; + int ret = getnameinfo( + (const struct sockaddr *)&info->local_addr, + info->remote_addrlen, + NULL, 0, tmp_port, sizeof(tmp_port), NI_NUMERICSERV); + if (ret == 0) + { + return atoi(tmp_port); + } + } + return -1; +} + +/** + * ソケットディスクリプタを返します。 + * 通常は、ソケットディスクリプタを直接操作しないでください。 + * + * @param sock 対象ソケット + * @return ソケットディスクリプタ + */ +static socket_t KcSocket_get_socket(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + return info->sock_fd; +} + +/** + * ソケットタイプを返します。 + * + * @param sock 対象ソケット + * @return ソケットタイプ + */ +static int KcSocket_get_type(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + return info->sock_type; +} + +/** + * ソケットファミリを返します。 + * ソケット接続、バインド時には、実際にバインド、 + * あるいは接続されているソケットファミリを返します。 + * + * @param sock 対象ソケット + * @return ソケットファミリ + */ +static int KcSocket_get_family(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int result_family = info->sock_family; + if (info->sock_fd != INVALID_SOCKET) + { + struct sockaddr_storage ss; + socklen_t len = sizeof(struct sockaddr_storage); + if (getsockname(info->sock_fd, (struct sockaddr *)&ss, &len) == 0) + { + result_family = ss.ss_family; + } + } + return result_family; +} + +/** + * 指定されたアドレス、サービスのアドレス情報を取得します。 + * + * @param sock 対象ソケット + * @param result アドレス情報格納用ポインタ + * @param addr アドレス + * @param service サービス + * @param is_passive サーバソケットの場合、true を指定ください。 + */ +static bool KcSocket_get_addrinfo( + KcSocket *sock, struct addrinfo **result, + const char *addr, const char *service, bool is_passive) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + + struct addrinfo hints; + memset(&hints, 0x00, sizeof(struct addrinfo)); + hints.ai_socktype = info->sock_type; + hints.ai_family = info->sock_family; + if (is_passive) + { + hints.ai_flags = AI_PASSIVE; + } + int ret = getaddrinfo(addr, service, &hints, result); + return (ret == 0); +} + +/** + * 指定されたアドレス、サービスにバインドします。 + * 指定されたアドレスとサービスにより、複数候補がある場合は、 + * 最初に見つかったアドレスとポートにバインドします。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス (例: "80", "http", "ssh" など) + * @return true/false (bind 成功/失敗) + */ +static bool KcSocket_bind(KcSocket *sock, const char *addr, const char *service) +{ + struct addrinfo *bind_addr; + bool is_success = sock->get_addrinfo(sock, &bind_addr, addr, service, true); + if (is_success) + { + is_success = KcSocket_addrinfo_bind_and_connect(sock, bind_addr, NULL); + freeaddrinfo(bind_addr); + } + return is_success; +} + +/** + * ソケットを接続待ちソケットとしてマークをつけます。 + * 保留中の接続のキュー最大長を指定します。 + * + * @param sock 対象ソケット + * @param backlog バックログ + * @return true/false (成功/失敗) + */ +static bool KcSocket_listen(KcSocket *sock, int backlog) +{ + bool result = false; + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->sock_fd != INVALID_SOCKET) + { + int ret = listen(info->sock_fd, backlog); + result = (ret != SOCKET_ERROR); + } + return result; +} + +/** + * ソケットへの接続を受け付けます。 + * + * @param sock 対象ソケット + * @return 受け付けたソケット(失敗時NULL) + */ +static KcSocket *KcSocket_accept(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + KcSocket *client = NULL; + if (info->sock_fd != INVALID_SOCKET) + { + client = KcSocket_new(info->sock_type, info->sock_family); + if (client != NULL) + { + KcSocketInfo *cinfo = (KcSocketInfo *)client->_info; + cinfo->remote_addrlen = sizeof(struct sockaddr_storage); + cinfo->sock_fd = accept(info->sock_fd, (struct sockaddr *)&cinfo->remote_addr, &cinfo->remote_addrlen); + if (cinfo->sock_fd != INVALID_SOCKET) + { + memcpy(&cinfo->local_addr, &info->local_addr, info->local_addrlen); + cinfo->local_addrlen = info->local_addrlen; + cinfo->sock_type = SOCK_STREAM; + cinfo->sock_family = cinfo->remote_addr.ss_family; + } + else + { + KcSocket_delete(client); + client = NULL; + } + } + } + return client; +} + +/** + * 指定されたアドレス、サービス接続します。 + * local_addr に NULL を指定した場合、自動的にローカルのアドレス、サービスが設定されます。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス + * @param local_addr ローカルアドレス + * @param local_service ローカルサービス + * @return true/false (成功/失敗) + */ +static bool KcSocket_connect( + KcSocket *sock, const char *addr, const char *service, + const char *local_addr, const char *local_service) +{ + // 接続先アドレス情報取得 + struct addrinfo *conn_addr = NULL; + if (!sock->get_addrinfo(sock, &conn_addr, addr, service, false)) + { + return false; + } + + // バインドアドレス情報取得 + bool is_success = false; + struct addrinfo *bind_addr = NULL; + if (local_addr != NULL) + { // bind が必要 + is_success = sock->get_addrinfo(sock, &bind_addr, local_addr, local_service, true); + if (is_success) + { + is_success = KcSocket_addrinfo_bind_and_connect(sock, bind_addr, conn_addr); + freeaddrinfo(bind_addr); + } + } + else + { + is_success = KcSocket_addrinfo_connect(sock, conn_addr, INVALID_SOCKET); + } + freeaddrinfo(conn_addr); + return is_success; +} + +/** + * ソケットをクローズします。 + * + * @param sock 対象ソケット + * @return true/false (成功/失敗) + */ +static bool KcSocket_close(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + + if (info->sock_family == SOCK_STREAM) + { // TCP の場合は、出力を閉じて、届いているデータを読み取ってから close する。 + int ret = shutdown(info->sock_fd, SHUT_WR); + if (ret == 0) + { + int read_size; + char buff[1024]; + do + { // 届いているデータを全て読み取る + read_size = recv(info->sock_fd, buff, sizeof(buff), 0); + } while (read_size > 0); + } + } + sockclose(info->sock_fd); + return true; +} + +/** + * メッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 送信されたデータサイズ + */ +static ssize_t KcSocket_send(KcSocket *sock, const char *buff, size_t size, int flags) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + ssize_t write_size = 0; + do + { + errno = 0; + write_size = send(info->sock_fd, buff, size, flags); + } while ((write_size < 0) && (errno == EINTR)); + return write_size; +} + +/** + * メッセージを受信します。 + * 受信したメッセージは指定されたバッファに格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 読み取ったデータサイズ + */ +static ssize_t KcSocket_recv(KcSocket *sock, char *buff, size_t size, int flags) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + ssize_t read_size; + do + { + errno = 0; + read_size = recv(info->sock_fd, buff, size, flags); + } while ((read_size < 0) && (errno == EINTR)); + return read_size; +} + +/** + * 指定されたアドレス、サービスにメッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param addr アドレス + * @param service サービス + * @return 送信メッセージサイズ、エラー発生時は -1 + */ +static ssize_t KcSocket_sendto( + KcSocket *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service) +{ + (void)sock; + (void)buff; + (void)size; + (void)flags; + (void)addr; + (void)service; + return 0; +} + +/** + * メッセージを受信します。 + * src_addr, src_service がいずれも NULL 出ない場合、 + * 送信元のアドレスとサービスが格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param src_addr 送信元アドレス格納用バッファ + * @param src_addrlen 送信元アドレス格納用バッファサイズ + * @param src_service 送信元サービス格納用バッファ + * @param src_servicelen 送信元サービス格納用バッファサイズ + * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + */ +static ssize_t KcSocket_recvfrom( + KcSocket *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen) +{ + (void)sock; + (void)buff; + (void)size; + (void)flags; + (void)src_addr; + (void)src_addrlen; + (void)src_service; + (void)src_servicelen; + return 0; +} + +/** + * TTL を設定します。 + * + * @param sock 対象ソケット + * @param val 設定する TTL + * @return true/false (成功/失敗) + */ +static bool KcSocket_set_ttl(KcSocket *sock, int val) +{ + (void)sock; + (void)val; + return true; +} + +/** + * マルチキャストグループに参加するために JOIN します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @param ifindex インタフェースインデックス + * @return true/false (成功/失敗) + */ +static bool KcSocket_join( + KcSocket *sock, const char *addr, const char *ifname, unsigned int ifindex) +{ + (void)sock; + (void)addr; + (void)ifname; + (void)ifindex; + return true; +} + +/** + * マルチキャストグループから離脱します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @return true/false (成功/失敗) + */ +static bool KcSocket_leave( + KcSocket *sock, const char *addr) +{ + (void)sock; + (void)addr; + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// [内部関数] +// + +/** + * 指定された接続アドレス情報をもとにソケットの生成、接続を試みます。 + * 接続済みソケット sockfd が有効な場合、sockfd を用いて接続します。 + * + * @param sock 対象ソケット + * @param conn_addrinfo 接続アドレス情報 + * @param sockfd 接続済みソケット + * @return true/false (接続成功/失敗) + */ +static bool KcSocket_addrinfo_connect(KcSocket *sock, struct addrinfo *conn_addrinfo, socket_t sockfd) +{ + bool is_success = false; + socket_t tmp_sock = sockfd; + for (struct addrinfo *rp = conn_addrinfo; rp != NULL; rp = rp->ai_next) + { + if (sockfd == INVALID_SOCKET) + { // sockfd が無効の場合、ソケットを生成する。 + tmp_sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (tmp_sock == INVALID_SOCKET) + { + continue; + } + } + + int ret = connect(tmp_sock, rp->ai_addr, rp->ai_addrlen); + is_success = (ret != SOCKET_ERROR); + if (is_success) + { + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + info->sock_fd = tmp_sock; + info->remote_addrlen = rp->ai_addrlen; + memcpy(&info->remote_addr, rp->ai_addr, rp->ai_addrlen); + break; + } + + if (sockfd != INVALID_SOCKET) + { // sockfd が無効の場合、一時的に生成したソケットをクローズする。 + sockclose(tmp_sock); + } + } + return is_success; +} + +/** + * 指定された接続アドレス情報をもとにソケットの生成、バインドを試みます。 + * conn_addrinfo が NULL でない場合、バインド後に接続を試みます。 + * + * @param sock 対象ソケット + * @param bind_addrinfo バインドアドレス情報 + * @param conn_addrinfo 接続アドレス情報 + * @return true/false (接続成功/失敗) + */ +static bool KcSocket_addrinfo_bind_and_connect( + KcSocket *sock, struct addrinfo *bind_addrinfo, struct addrinfo *conn_addrinfo) +{ + int ret; + bool is_success = false; + + for (struct addrinfo *rp = bind_addrinfo; rp != NULL; rp = rp->ai_next) + { // sokcet : ソケット生成 + socket_t tmp_sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (tmp_sock == INVALID_SOCKET) + { + continue; + } + + // bind + const int on = 1; + setsockopt(tmp_sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on)); + ret = bind(tmp_sock, rp->ai_addr, rp->ai_addrlen); + is_success = (ret != SOCKET_ERROR); + if (!is_success) + { // bind 失敗したので次へ + sockclose(tmp_sock); + continue; + } + + // connect + if (conn_addrinfo) + { + is_success = KcSocket_addrinfo_connect(sock, conn_addrinfo, tmp_sock); + if (!is_success) + { // connect 失敗したので次へ + sockclose(tmp_sock); + continue; + } + } + + // bind または、bind と connect 成功 + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + info->sock_fd = tmp_sock; + info->local_addrlen = rp->ai_addrlen; + memcpy(&info->local_addr, rp->ai_addr, rp->ai_addrlen); + break; + } + return is_success; +} diff --git a/modules/src/kc_threads.c b/modules/src/kc_threads.c new file mode 100644 index 0000000..dd600bb --- /dev/null +++ b/modules/src/kc_threads.c @@ -0,0 +1,194 @@ +/** + * @file kc_thread.c + * @brief スレッドモジュール + * @copyright 2020 - 2024 Nomura Kei + */ +#include +#include +#include + +#include +#include +#include + +/** + * KcThread 管理情報 + */ +typedef struct +{ + thrd_t tid; + void *args; + int (*run)(void *args); + atomic_bool alive_state; +} KcThreadInfo; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static bool KcThread_is_alive(KcThread *thread); +static bool KcThread_start(KcThread *thread, void *args); +static bool KcThread_join(KcThread *thread); +static int KcThread_run(void *thread); + +// ============================================================================= +// new +// ============================================================================= +/** + * スレッドを生成します。 + * + * @param run スレッドにて実行する関数へのポインタ + */ +KcThread *KcThread_new(int (*run)(void *args)) +{ + // KcThread の管理構造 + // +--------------+ + // | KcThread | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // +--------------+ + KcThread *thread = (KcThread *)malloc(sizeof(KcThread) + sizeof(KcThreadInfo)); + if (thread != NULL) + { + thread->is_alive = KcThread_is_alive; + thread->start = KcThread_start; + thread->join = KcThread_join; + thread->_info = (thread + 1); + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + // info->tid + info->run = run; + atomic_init(&info->alive_state, false); + } + return thread; +} + +// ============================================================================= +// delete +// ============================================================================= +/** + * Thread を破棄します。 + * + * @param thread 破棄するスレッド + */ +void KcThread_delete(KcThread *thread) +{ + free(thread); +} + +// ============================================================================= +// sleep +// ============================================================================= +/** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープさせるミリ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ +bool KcThread_msleep(long time, bool force) +{ + long sec = time / 1000; + long msec = time - (sec * 1000); + long nsec = msec * 1000 * 1000; + return KcThread_sleep(sec, nsec, force); +} + +/** + * 指定された sec 秒 + nsec ナノ秒間現在のスレッドをスリープさせます。 + * + * @param sec スリープさせる秒数 + * @param nsec スリープさせるナノ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ +bool KcThread_sleep(long sec, long nsec, bool force) +{ + struct timespec remaining = {sec, nsec}; + int ret; + do + { + errno = 0; + ret = thrd_sleep(&remaining, &remaining); + } while ((ret == -1) && force && (errno == EINTR)); + return (ret == 0); +} + +// ============================================================================= +// is_alive +// ============================================================================= +/** + * 指定されたスレッドが生きているか否かを返します。 + * + * @param thread 対象スレッド + * @return true/false (スレッドが生存している/していない) + */ +static bool KcThread_is_alive(KcThread *thread) +{ + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + return atomic_load(&info->alive_state); +} + +// ============================================================================= +// start +// ============================================================================= +/** + * スレッドを開始します。 + * + * @param thread 対象スレッド + * @param args スレッドに渡す情報 + * @return true/false (スレッド起動成功/失敗) + */ +static bool KcThread_start(KcThread *thread, void *args) +{ + int ret = thrd_error; + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + if (!atomic_load(&info->alive_state)) + { + info->args = args; + ret = thrd_create(&info->tid, KcThread_run, thread); + } + return (ret == thrd_success); +} + +// ============================================================================= +// join +// ============================================================================= +/** + * スレッドが終了するのを待機します。 + * + * @param thread 対象スレッド + * @return true/false (成功/失敗) + */ +static bool KcThread_join(KcThread *thread) +{ + int ret = thrd_error; + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + ret = thrd_join(info->tid, NULL); + return (ret == thrd_success); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// 内部関数 +// + +// ============================================================================= +// run +// ============================================================================= +/** + * スレッドで実行される関数。 + * start にて関数を別スレッドにて実行します。 + * + * @param args スレッド情報 + * @return + */ +int KcThread_run(void *args) +{ + KcThread *thread = (KcThread *)args; + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + atomic_store(&info->alive_state, true); + int ret = info->run(info->args); + atomic_store(&info->alive_state, false); + return ret; +} diff --git a/modules/src/kc_threads_win.c b/modules/src/kc_threads_win.c new file mode 100644 index 0000000..0439fda --- /dev/null +++ b/modules/src/kc_threads_win.c @@ -0,0 +1,201 @@ +/** + * @file kc_thread.c + * @brief スレッドモジュール + * @copyright 2020 - 2024 Nomura Kei + */ +#include +#include + +#if (KC_IS_WINDOWS) + +int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) +{ + if ((thr == NULL) || (func == NULL)) + { + return thrd_error; + } + + thr->handle = CreateThread( + NULL, // セキュリティ属性 + 0, // スタックサイズ + (LPTHREAD_START_ROUTINE)func, // スレッド関数 + arg, // 引数 + 0, // 作成フラグ + &(thr->thread_id) // スレッドID + ); + + if (thr->handle == NULL) + { + return thrd_error; + } + return thrd_success; +} +int thrd_join(thrd_t thr, int *res) +{ + if (WaitForSingleObject(thr.handle, INFINITE) != WAIT_OBJECT_0) + { + return thrd_error; + } + if (res != NULL) + { + DWORD retcode; + if (GetExitCodeThread(thr.handle, &retcode) == 0) + { + return thrd_error; + } + *res = (int)retcode; + } + CloseHandle(thr.handle); + return thrd_success; +} + +int thrd_detach(thrd_t thr) +{ + if (thr.handle == NULL) + { + return thrd_error; + } + if (CloseHandle(thr.handle) == 0) + { + return thrd_error; + } + return thrd_success; +} + +thrd_t thrd_current(void) +{ + thrd_t current; + current.handle = GetCurrentThread(); + current.thread_id = GetThreadId(current.handle); + return current; +} + +int thrd_equal(thrd_t lhs, thrd_t rhs) +{ + return (lhs.thread_id == rhs.thread_id); +} + +void thrd_yield(void) +{ + SwitchToThread(); +} + +int thrd_sleep(const struct timespec *duration, struct timespec *remaining) +{ + if (duration == NULL) + { + return thrd_error; + } + + // Windows の Sleep は、ms 単位 + DWORD msec = (DWORD)(duration->tv_sec * 1000 + duration->tv_nsec / 1000000); + Sleep(msec); + + // 常に成功したものとして、remaining は 0 に設定する。 + if (remaining != NULL) + { + remaining->tv_sec = 0; + remaining->tv_nsec = 0; + } + return thrd_success; +} + +int mtx_init(mtx_t *mtx, int type) +{ + if (mtx == NULL) + { + return thrd_error; + } + mtx->type = type; + if (type & mtx_plain) + { + InitializeCriticalSection(&mtx->cs); + } + else if (type & mtx_recursive) + { + InitializeCriticalSection(&mtx->cs); + } + else + { + return thrd_error; + } + return thrd_success; +} + +void mtx_destroy(mtx_t *mtx) +{ + DeleteCriticalSection(&mtx->cs); +} +int mtx_lock(mtx_t *mtx) +{ + EnterCriticalSection(&mtx->cs); + return thrd_success; +} +int mtx_unlock(mtx_t *mtx) +{ + LeaveCriticalSection(&mtx->cs); + return thrd_success; +} + +int cnd_init(cnd_t *cond) +{ + if (cond == NULL) + { + return thrd_error; + } + InitializeConditionVariable(&cond->cond); + return thrd_success; +} + +int cnd_signal(cnd_t *cond) +{ + if (cond == NULL) + { + return thrd_error; + } + WakeConditionVariable(&cond->cond); + return thrd_success; +} +int cnd_broadcast(cnd_t *cond) +{ + if (cond == NULL) + { + return thrd_error; + } + WakeAllConditionVariable(&cond->cond); + return thrd_success; +} +int cnd_wait(cnd_t *cond, mtx_t *mtx) +{ + if ((cond == NULL) || (mtx == NULL)) + { + return thrd_error; + } + if (SleepConditionVariableCS(&cond->cond, &mtx->cs, INFINITE)) + { + return thrd_success; + } + return thrd_error; +} + +int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts) +{ + if ((cond == NULL) || (mtx == NULL) || (ts == NULL)) + { + return thrd_error; + } + DWORD msec = (DWORD)(ts->tv_sec * 1000 + ts->tv_nsec / 1000000); + if (SleepConditionVariableCS(&cond->cond, &mtx->cs, msec)) + { + return thrd_timedout; + } + return thrd_error; +} + +void cnd_destroy(cnd_t *cond) +{ + // Nothing to do + UNUSED_VARIABLE(cond); +} + +#endif \ No newline at end of file diff --git a/modules/test/src/test_lock_guard.c b/modules/test/src/test_lock_guard.c index f00ed0b..9c3249f 100644 --- a/modules/test/src/test_lock_guard.c +++ b/modules/test/src/test_lock_guard.c @@ -37,7 +37,7 @@ { mtx_t mutex; mtx_init(&mutex, mtx_plain | mtx_recursive); - KcLockGuard lock_guard = kc_lock_guard_init(&mutex); + KcLockGuard lock_guard = kc_lock_guard_init(&mutex, __FILE__, __func__, __LINE__); assert_not_null(lock_guard.mutex); // Lock Guard 解放 @@ -53,7 +53,7 @@ */ static void test_lock_guard_init_null(void) { - KcLockGuard lock_guard = kc_lock_guard_init(NULL); + KcLockGuard lock_guard = kc_lock_guard_init(NULL, __FILE__, __func__, __LINE__); assert_equals(EINVAL, errno); assert_null(lock_guard.mutex); } @@ -69,7 +69,7 @@ mtx_t mutex; mtx_init(&mutex, mtx_plain | mtx_recursive); mtx_destroy(&mutex); - KcLockGuard lock_guard = kc_lock_guard_init(&mutex); + KcLockGuard lock_guard = kc_lock_guard_init(&mutex, __FILE__, __func__, __LINE__); assert_null(lock_guard.mutex); } @@ -83,7 +83,7 @@ { mtx_t mutex; mtx_init(&mutex, mtx_plain | mtx_recursive); - KcLockGuard lock_guard = kc_lock_guard_init(&mutex); + KcLockGuard lock_guard = kc_lock_guard_init(&mutex, __FILE__, __func__, __LINE__); assert_not_null(lock_guard.mutex); // Lock Guard 解放 diff --git a/config.mk b/config.mk index a0ac27c..f7c6e47 100644 --- a/config.mk +++ b/config.mk @@ -25,14 +25,17 @@ # for Windows CROSS_COMPILE ?=i686-w64-mingw32- LIBS ?= -lws2_32 +DEBUG_OPTIONS ?= else # For Linux ifeq ($(strip $(ARCH)),aarch64) CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector else CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector endif endif diff --git a/mk/base-cmd.mk b/mk/base-cmd.mk index f429bcf..e74c114 100644 --- a/mk/base-cmd.mk +++ b/mk/base-cmd.mk @@ -33,8 +33,13 @@ CC = $(CROSS_COMPILE)gcc CXX = $(CROSS_COMPILE)g++ + #CC = $(CROSS_COMPILE)clang #CXX = $(CROSS_COMPILE)clang++ + +#CC = $(CROSS_COMPILE)clang-16 +#CXX = $(CROSS_COMPILE)clang++16 + RANLIB = $(CROSS_COMPILE)ranlib ADDR2LINE = $(CROSS_COMPILE)addr2line AR = $(CROSS_COMPILE)ar diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 2cc417c..eebded1 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -69,7 +69,7 @@ # 値が2の場合、さらなるチェックを追加するが、規格準拠のプログラムが失敗する可能性がある。 # いくつかのチェックは、コンパイル時に実行され、コンパイラの警告として表示される。 # ------------------------------------------------------------------------------ -DEBUG_OPTIONS += -fstack-protector +# DEBUG_OPTIONS += -fstack-protector DEBUG_OPTIONS += --coverage DEBUG_OPTIONS += -g3 -ggdb DEBUG_OPTIONS += -O0 diff --git a/mk/link-a-conf.mk b/mk/link-a-conf.mk index 0e872f3..631e6d5 100644 --- a/mk/link-a-conf.mk +++ b/mk/link-a-conf.mk @@ -10,8 +10,11 @@ HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) +HEADER_FILES_WIN = $(wildcard include/win/*.h) $(wildcard include/win/*.hpp) +TOP_HEADER_FILES_WIN = $(addprefix $(TOPDIR)/include/win/,$(notdir $(HEADER_FILES_WIN))) + CLEAN_FILES += $(TOP_TARGET) -CLEAN_FILES += $(TOP_HEADER_FILES) +CLEAN_FILES += $(TOP_HEADER_FILES) $(TOP_HEADER_FILES_WIN) endif endif diff --git a/mk/link-a-rule.mk b/mk/link-a-rule.mk index d94a54a..657d858 100644 --- a/mk/link-a-rule.mk +++ b/mk/link-a-rule.mk @@ -10,6 +10,7 @@ $(TOP_TARGET): $(TARGET) ifneq ($(strip $(HEADER_FILES)),) $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ + $(CP) -f $(HEADER_FILES_WIN) $(TOPDIR)/include/win/ endif $(CP) -f $(TARGET) $(TOPDIR)/lib/ diff --git a/modules/include/kc_lock_guard.h b/modules/include/kc_lock_guard.h index 8bb70bd..ad3915f 100644 --- a/modules/include/kc_lock_guard.h +++ b/modules/include/kc_lock_guard.h @@ -7,18 +7,16 @@ #define KC_LOCK_GUARD_H #include -#include - +#include /** * LockGuard 管理構造体 */ typedef struct { - mtx_t* mutex; + mtx_t *mutex; } KcLockGuard; - /** * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 * 使用例) @@ -34,8 +32,7 @@ * } */ #define kc_lock_guard(lock) \ - for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) - + for (KcLockGuard _guard = kc_lock_guard_init(lock, __FILE__, __func__, __LINE__); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 @@ -44,15 +41,13 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex); - +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line); /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard); +void kc_lock_guard_release(KcLockGuard *guard); - -#endif // KC_LOCK_GUARD_H +#endif // KC_LOCK_GUARD_H diff --git a/modules/include/kc_memory.h b/modules/include/kc_memory.h index ffce4ed..c6ba9e9 100644 --- a/modules/include/kc_memory.h +++ b/modules/include/kc_memory.h @@ -22,12 +22,13 @@ #ifndef KC_MEMORY_H #define KC_MEMORY_H -#include +#include #include #include #include #include +#include #ifdef __cplusplus extern "C" @@ -273,8 +274,6 @@ #define calloc(nmemb, size) kc_memory_manager->calloc(nmemb, size, __FILE__, __func__, __LINE__) #define realloc(ptr, size) kc_memory_manager->realloc(ptr, size, __FILE__, __func__, __LINE__) #define free(ptr) kc_memory_manager->free(ptr) -#else -#include #endif #ifdef __cplusplus diff --git a/modules/include/kc_socket.h b/modules/include/kc_socket.h new file mode 100644 index 0000000..7d952c7 --- /dev/null +++ b/modules/include/kc_socket.h @@ -0,0 +1,291 @@ +/** + * @file kc_socket.h + * @brief ソケットモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_SOCKET_H +#define KC_SOCKET_H + +#include + +#if (KC_IS_WINDOWS) +typedef SOCKET socket_t; +#define SHUT_RD SD_RECEIVE +#define SHUT_WR SD_SEND +#define SHUT_RDWR SD_BOTH +#define sockclose(sockfd) closesocket(sockfd) +#else +#include +#include +#include +#include +#include +#include +#include +typedef int socket_t; +#define INVALID_SOCKET (-1) +#define SOCKET_ERROR (-1) +#define sockclose(sockfd) close(sockfd) +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcSocket_ + { + + /** + * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr 接続先アドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_remote_addr)(struct KcSocket_ *sock, char *addr, size_t addrlen); + + /** + * ソケットの接続先ポート番号を返します。 + * 接続先ポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_remote_port)(struct KcSocket_ *sock); + + /** + * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr ローカルアドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_local_addr)(struct KcSocket_ *sock, char *addr, size_t addlen); + + /** + * ソケットのローカルポート番号を返します。 + * ローカルポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_local_port)(struct KcSocket_ *sock); + + /** + * ソケットディスクリプタを返します。 + * 通常は、ソケットディスクリプタを直接操作しないでください。 + * + * @param sock 対象ソケット + * @return ソケットディスクリプタ + */ + socket_t (*get_socket)(struct KcSocket_ *sock); + + /** + * ソケットタイプを返します。 + * + * @param sock 対象ソケット + * @return ソケットタイプ + */ + int (*get_type)(struct KcSocket_ *sock); + + /** + * ソケットファミリを返します。 + * 実際にバインド、あるいは接続されているソケットファミリを返します。 + * + * @param sock 対象ソケット + * @return ソケットファミリ + */ + int (*get_family)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービスのアドレス情報を取得します。 + * + * @param sock 対象ソケット + * @param result アドレス情報格納用ポインタ + * @param addr アドレス + * @param service サービス + * @param is_passive サーバソケットの場合、true を指定ください。 + */ + bool (*get_addrinfo)( + struct KcSocket_ *sock, + struct addrinfo **result, + const char *addr, + const char *service, + bool is_passive); + + /** + * 指定されたアドレス、サービスにバインドします。 + * 指定されたアドレスとサービスにより、複数候補がある場合は、 + * 最初に見つかったアドレスとポートにバインドします。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス (例: "80", "http", "ssh" など) + * @return true/false (bind 成功/失敗) + */ + bool (*bind)(struct KcSocket_ *sock, const char *addr, const char *service); + + /** + * ソケットを接続待ちソケットとしてマークをつけます。 + * 保留中の接続のキュー最大長を指定します。 + * + * @param sock 対象ソケット + * @param backlog バックログ + * @return true/false (成功/失敗) + */ + bool (*listen)(struct KcSocket_ *sock, int backlog); + + /** + * ソケットへの接続を受け付けます。 + * + * @param sock 対象ソケット + * @return 受け付けたソケット + */ + struct KcSocket_ *(*accept)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービス接続します。 + * local_addr に NULL を指定した場合、自動的にローカルのアドレスが設定されます。 + * local_service に -1 を指定した場合、自動的にローカルのポート番号が割りつけられます。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス + * @param local_addr ローカルアドレス + * @param local_service ローカルサービス + * @return true/false (成功/失敗) + */ + bool (*connect)(struct KcSocket_ *sock, const char *addr, const char *service, const char *local_addr, const char *local_port); + + /** + * ソケットをクローズします。 + * + * @param sock 対象ソケット + * @return true/false (成功/失敗) + */ + bool (*close)(struct KcSocket_ *sock); + + /** + * メッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 送信されたデータサイズ + */ + ssize_t (*send)(struct KcSocket_ *sock, const char *buff, size_t size, int flags); + + /** + * メッセージを受信します。 + * 受信したメッセージは指定されたバッファに格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 読み取ったデータサイズ + */ + ssize_t (*recv)(struct KcSocket_ *sock, char *buff, size_t size, int flags); + + /** + * 指定されたアドレス、サービスにメッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param addr アドレス + * @param service サービス + * @return 送信メッセージサイズ、エラー発生時は -1 + */ + ssize_t (*sendto)( + struct KcSocket_ *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service); + + /** + * メッセージを受信します。 + * src_addr, src_service がいずれも NULL 出ない場合、 + * 送信元のアドレスとサービスが格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param src_addr 送信元アドレス格納用バッファ + * @param src_addrlen 送信元アドレス格納用バッファサイズ + * @param src_service 送信元サービス格納用バッファ + * @param src_servicelen 送信元サービス格納用バッファサイズ + * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + */ + ssize_t (*recvfrom)( + struct KcSocket_ *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen); + + /** + * TTL を設定します。 + * + * @param sock 対象ソケット + * @param val 設定する TTL + * @return true/false (成功/失敗) + */ + bool (*set_ttl)(struct KcSocket_ *sock, int val); + + /** + * マルチキャストグループに参加するために JOIN します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @param ifindex インタフェースインデックス + * @return true/false (成功/失敗) + */ + bool (*join)(struct KcSocket_ *sock, const char *addr, const char *ifname, unsigned int ifindex); + + /** + * マルチキャストグループから離脱します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @return true/false (成功/失敗) + */ + bool (*leave)(struct KcSocket_ *sock, const char *addr); + + /** + * Socket 管理情報 + */ + void *_info; + + } KcSocket; + + /** + * 指定されたタイプ、ファミリのSocket オブジェクトを構築します。 + * + * @param type タイプ(SOCK_STREAM/SOCK_DGRAM/SOCK_RAW) + * @param family ファミリ(AF_UNSPEC/AF_INET/AF_INET6) + */ + KcSocket *KcSocket_new(int type, int family); + + /** + * Socket を破棄します。 + */ + void KcSocket_delete(KcSocket *socket); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_threads.h b/modules/include/kc_threads.h new file mode 100644 index 0000000..764176f --- /dev/null +++ b/modules/include/kc_threads.h @@ -0,0 +1,98 @@ +/** + * @file kc_threads.h + * @brief スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_THREADS_H +#define KC_THREADS_H + +#include + +#if (KC_IS_WINDOWS) +#include +#else +#include +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcThread_ + { + + /** + * 指定されたスレッドが生きているか否かを返します。 + * + * @param thread 対象スレッド + * @return true/false (スレッドが生存している/していない) + */ + bool (*is_alive)(struct KcThread_ *thread); + + /** + * スレッドを開始します。 + * + * @param thread 対象スレッド + * @param args スレッドに渡す情報 + * @return true/false (スレッド起動成功/失敗) + */ + bool (*start)(struct KcThread_ *thread, void *args); + + /** + * スレッドが終了するのを待機します。 + * + * @param thread 対象スレッド + */ + bool (*join)(struct KcThread_ *thread); + + /** + * Thread 管理情報 + */ + void *_info; + + } KcThread; + + /** + * スレッドを生成します。 + * + * @param run スレッドにて実行する関数へのポインタ + */ + KcThread *KcThread_new(int (*run)(void *args)); + + /** + * Thread を破棄します。 + * + * @param thread 破棄するスレッド + */ + void KcThread_delete(KcThread *thread); + + /** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープさせるミリ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_msleep(long time, bool force); + + /** + * 指定された sec 秒 + nsec ナノ秒間現在のスレッドをスリープさせます。 + * + * @param sec スリープさせる秒数 + * @param nsec スリープさせるナノ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_sleep(long sec, long nsec, bool force); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_THREADS_H diff --git a/modules/include/kc_threads_win.h b/modules/include/kc_threads_win.h new file mode 100644 index 0000000..b40ab10 --- /dev/null +++ b/modules/include/kc_threads_win.h @@ -0,0 +1,98 @@ +/** + * @file kc_windows_thread.h + * @brief Windows 用スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_WINDOWS_THREAD_H +#define KC_WINDOWS_THREAD_H + +#include +#if (KC_IS_WINDOWS) +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#define thread_local _Thread_local + + //////////////////////////////////////////////////////////////////////// + // + // Thread + // + + enum + { + thrd_success = 0, //!< 成功 + thrd_bussy = 1, //!< busy + thrd_error = 2, //!< エラー発生 + thrd_nomem = 3, //!< メモリ不足 + thrd_timedout = 4 //!< タイムアウト発生 + }; + + typedef struct + { + HANDLE handle; + DWORD thread_id; + } thrd_t; + + typedef int (*thrd_start_t)(void *); + int thrd_create(thrd_t *thr, thrd_start_t func, void *arg); + int thrd_join(thrd_t thr, int *res); + int thrd_detach(thrd_t thr); + thrd_t thrd_current(void); + int thrd_equal(thrd_t lhs, thrd_t rhs); + int thrd_sleep(const struct timespec *duration, struct timespec *remaining); + + //////////////////////////////////////////////////////////////////////// + // + // mutex + // + + enum + { + mtx_plain = 0, //!< 通常 + mtx_recursive = 1, //!< 再帰 + mtx_timed = 2 //!< timed + }; + + typedef struct + { + CRITICAL_SECTION cs; + int type; + } mtx_t; + + int mtx_init(mtx_t *mtx, int type); + void mtx_destroy(mtx_t *mtx); + int mtx_lock(mtx_t *mtx); + int mtx_unlock(mtx_t *mtx); + + //////////////////////////////////////////////////////////////////////// + // + // cond + // + + typedef struct + { + CONDITION_VARIABLE cond; + } cnd_t; + + int cnd_init(cnd_t *cond); + int cnd_signal(cnd_t *cond); + int cnd_broadcast(cnd_t *cond); + int cnd_wait(cnd_t *cond, mtx_t *mtx); + int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts); + void cnd_destroy(cnd_t *cond); + +#endif // (KC_IS_WINDOWS) +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_windows.h b/modules/include/kc_windows.h index ee70898..6e1c659 100644 --- a/modules/include/kc_windows.h +++ b/modules/include/kc_windows.h @@ -37,9 +37,9 @@ #endif // よく利用されるヘッダファイルをインクルードする -#include #include #include +#include #ifdef _MSC_VER #pragma comment(lib, "ws2_32.lib") #endif diff --git a/modules/src/kc_assert.c b/modules/src/kc_assert.c index 371fc8f..7cd4ecc 100644 --- a/modules/src/kc_assert.c +++ b/modules/src/kc_assert.c @@ -6,9 +6,9 @@ #include #include #include -#include #include #include +#include // プロトタイプ宣言 static void assert_default_handler(const char *msg); diff --git a/modules/src/kc_env.c b/modules/src/kc_env.c index 0675c73..95f185c 100644 --- a/modules/src/kc_env.c +++ b/modules/src/kc_env.c @@ -50,7 +50,7 @@ char *result = NULL; #if (KC_IS_WINDOWS) static char buff[KC_ENV_BUFFER_MAX]; - DWORD ret = GetEnvironmentVarable(key_name, (LPSTR)buff, sizeof(buff)); + DWORD ret = GetEnvironmentVariable(key_name, (LPSTR)buff, sizeof(buff)); if (ret != 0) { result = buff; diff --git a/modules/src/kc_list_array.c b/modules/src/kc_list_array.c index 246ed14..f29fc08 100644 --- a/modules/src/kc_list_array.c +++ b/modules/src/kc_list_array.c @@ -3,10 +3,14 @@ * @brief 配列リストモジュール * @copyright 2003 - 2023 Nomura Kei */ +#include #if defined(__GNUC__) #define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) #define qsort_s qsort_r #endif +#endif // defined(__GNUC__) + #include #include #include @@ -17,6 +21,7 @@ #include #include #include + /** * KcArrayList 管理情報 */ @@ -306,7 +311,11 @@ * @param context コンテキスト(KcListSortInfo) * @return 比較結果 */ +#if (KC_IS_WINDOWS) +static int KcArrayList_comparator(void *context, const void *x, const void *y) +#else static int KcArrayList_comparator(const void *x, const void *y, void *context) +#endif { KcListSortInfo *sort_info = (KcListSortInfo *)context; int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); diff --git a/modules/src/kc_list_linked.c b/modules/src/kc_list_linked.c index 30575b9..47edbe4 100644 --- a/modules/src/kc_list_linked.c +++ b/modules/src/kc_list_linked.c @@ -3,10 +3,14 @@ * @brief Linked リストモジュール * @copyright 2003 - 2023 Nomura Kei */ +#include #if defined(__GNUC__) #define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) #define qsort_s qsort_r #endif +#endif // defined(__GNUC__) + #include #include #include @@ -18,6 +22,13 @@ #include #include +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) +#define qsort_s qsort_r +#endif // KC_IS_WINDOWS +#endif // defined(__GNUC__) + /** * KcLinkedList Entry 情報 */ @@ -291,7 +302,11 @@ * @param context コンテキスト(KcListSortInfo) * @return 比較結果 */ +#if (KC_IS_WINDOWS) +static int KcLinkedList_comparator(void *context, const void *x, const void *y) +#else static int KcLinkedList_comparator(const void *x, const void *y, void *context) +#endif // KC_IS_WINDOWS { KcListSortInfo *sort_info = (KcListSortInfo *)context; const KcLinkedListEntry **entry_x = (const KcLinkedListEntry **)x; diff --git a/modules/src/kc_lock_guard.c b/modules/src/kc_lock_guard.c index 6ef68e5..648a153 100644 --- a/modules/src/kc_lock_guard.c +++ b/modules/src/kc_lock_guard.c @@ -7,7 +7,6 @@ #include - /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 * ロックに成功するとロック管理用のオブジェクトを返します。 @@ -15,36 +14,37 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex) +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line) { - KcLockGuard guard = { .mutex = mutex }; + KcLockGuard guard = {.mutex = mutex}; if (mutex == NULL) { errno = EINVAL; perror("kc_lock_guard_init: (mutex = NULL)"); + fprintf(stderr, "%s:%s:%d kc_lock_guard_init : mutex = NULL\n", file, func, line); return guard; } + errno = 0; int ret = mtx_lock(mutex); if (ret != thrd_success) { - perror("kc_lock_guard : mtx_lock error"); + fprintf(stderr, "%s:%s:%d kc_lock_guard : mtx_lock error\n", file, func, line); guard.mutex = NULL; } return guard; } - /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard) +void kc_lock_guard_release(KcLockGuard *guard) { - if (guard->mutex != NULL) { - mtx_unlock(guard->mutex); - guard->mutex = NULL; + if (guard->mutex != NULL) + { + mtx_unlock(guard->mutex); + guard->mutex = NULL; } } - diff --git a/modules/src/kc_memory.c b/modules/src/kc_memory.c index 5cc4186..6dbbc88 100644 --- a/modules/src/kc_memory.c +++ b/modules/src/kc_memory.c @@ -622,7 +622,11 @@ */ void *raw_aligned_alloc(size_t alignment, size_t size) { +#if (KC_IS_WINDOWS) + return _aligned_malloc(size, alignment); +#else return aligned_alloc(alignment, size); +#endif } /** diff --git a/modules/src/kc_memory_dump.c b/modules/src/kc_memory_dump.c index 111e328..49e63de 100644 --- a/modules/src/kc_memory_dump.c +++ b/modules/src/kc_memory_dump.c @@ -144,7 +144,7 @@ { // 空きバッファサイズ無しのため、何もしない。 return; } - int write_size = snprintf(info->write_ptr, info->rest_size, msg); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s", msg); if (write_size > info->rest_size) { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) write_size = (info->rest_size - 1); diff --git a/modules/src/kc_memory_listener.c b/modules/src/kc_memory_listener.c index 29ff2a0..ee69a13 100644 --- a/modules/src/kc_memory_listener.c +++ b/modules/src/kc_memory_listener.c @@ -98,5 +98,5 @@ KcMemoryDump_dump(buff, sizeof(buff), entry, 16, true, true, 130); fprintf(stderr, "[ERROR] %s", buff); } - fprintf(stderr, msg); + fprintf(stderr, "%s", msg); } diff --git a/modules/src/kc_queue.c b/modules/src/kc_queue.c index 544d763..d9f2a14 100644 --- a/modules/src/kc_queue.c +++ b/modules/src/kc_queue.c @@ -9,11 +9,11 @@ #include #include #include -#include #include #include #include +#include #include #include @@ -122,7 +122,10 @@ static int KcQueue_size(struct KcQueue_ *queue) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - size_t size = info->list->size(info->list); + size_t size = 0; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + size = info->list->size(info->list); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== return (int)size; } @@ -138,7 +141,11 @@ static bool KcQueue_is_empty(struct KcQueue_ *queue) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - return info->list->is_empty(info->list); + bool is_empty = true; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + is_empty = info->list->is_empty(info->list); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== + return is_empty; } // ============================================================================= @@ -155,7 +162,11 @@ static bool KcQueue_contains(struct KcQueue_ *queue, const void *element, size_t size) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - return info->list->contains(info->list, element, size); + bool is_contains = false; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + is_contains = info->list->contains(info->list, element, size); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== + return is_contains; } // ============================================================================= @@ -227,7 +238,11 @@ static void *KcQueue_peek(struct KcQueue_ *queue, size_t *size) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - return info->list->get(info->list, 0, size); + void *value = NULL; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + value = info->list->get(info->list, 0, size); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== + return value; } // ============================================================================= diff --git a/modules/src/kc_socket.c b/modules/src/kc_socket.c new file mode 100644 index 0000000..cb67996 --- /dev/null +++ b/modules/src/kc_socket.c @@ -0,0 +1,753 @@ +/** + * @file kc_socket.c + * @brief ソケットモジュール + * @copyright 2003 - 2024 Nomura Kei + */ +#include +#include + +#include +#include +#include +#include + +/** + * KcSocket 管理情報 + */ +typedef struct +{ + socket_t sock_fd; //!< ソケットディスクリプタ + int sock_type; //!< ソケットタイプ + int sock_family; //!< ソケットファミリ + struct sockaddr_storage remote_addr; //!< リモートアドレス + struct sockaddr_storage local_addr; //!< ローカルアドレス + socklen_t remote_addrlen; //!< リモートアドレス長 + socklen_t local_addrlen; //!< ローカルアドレス帳 +} KcSocketInfo; + +/** + * アドレス情報 + */ +typedef struct +{ + const char *addr; //!< アドレス + const char *service; //!< サービス +} KcSocketAddress; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static void Socket_setup(void); +static void Socket_cleanup(void); +static const char *KcSocket_get_remote_addr(KcSocket *sock, char *addr, size_t addrlen); +static int KcSocket_get_remote_port(KcSocket *sock); +static const char *KcSocket_get_local_addr(KcSocket *sock, char *addr, size_t addrlen); +static int KcSocket_get_local_port(KcSocket *sock); +static socket_t KcSocket_get_socket(KcSocket *sock); +static int KcSocket_get_type(KcSocket *sock); +static int KcSocket_get_family(KcSocket *sock); +static bool KcSocket_get_addrinfo( + KcSocket *sock, struct addrinfo **result, + const char *addr, const char *service, bool is_passive); +static bool KcSocket_bind(KcSocket *sock, const char *addr, const char *service); +static bool KcSocket_listen(KcSocket *sock, int backlog); +static KcSocket *KcSocket_accept(KcSocket *sock); +static bool KcSocket_connect( + KcSocket *sock, const char *addr, const char *service, + const char *local_addr, const char *local_service); +static bool KcSocket_close(KcSocket *sock); +static ssize_t KcSocket_send(KcSocket *sock, const char *buff, size_t size, int flags); +static ssize_t KcSocket_recv(KcSocket *sock, char *buff, size_t size, int flags); +static ssize_t KcSocket_sendto( + KcSocket *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service); +static ssize_t KcSocket_recvfrom( + KcSocket *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen); +static bool KcSocket_set_ttl(KcSocket *sock, int val); +static bool KcSocket_join(KcSocket *sock, const char *addr, const char *ifname, unsigned int ifindex); +static bool KcSocket_leave(KcSocket *sock, const char *addr); + +static bool KcSocket_addrinfo_bind_and_connect( + KcSocket *sock, struct addrinfo *bind_addrinfo, struct addrinfo *conn_addrinfo); +static bool KcSocket_addrinfo_connect(KcSocket *sock, struct addrinfo *conn_addrinfo, socket_t sockfd); + +/** + * ソケットのセットアップをします。 + */ +static void Socket_setup(void) +{ + static bool is_init = false; + if (!is_init) + { +#if (KC_IS_WINDOWS) + WSADATA wsa_data; + WSAStartup(MAKEWORD(2, 0), &wsa_data); +#endif + atexit(Socket_cleanup); + is_init = true; + } +} + +/** + * ソケットライブラリのクリーンアップをします。 + */ +static void Socket_cleanup(void) +{ +#if (KC_IS_WINDOWS) + WSACleanup(); +#endif +} + +// ============================================================================= +// new +// ============================================================================= +/** + * 指定されたタイプ、ファミリのSocket オブジェクトを構築します。 + * + * @param type タイプ(SOCK_STREAM/SOCK_DGRAM/SOCK_RAW) + * @param family ファミリ(AF_UNSPEC/AF_INET/AF_INET6) + */ +KcSocket *KcSocket_new(int type, int family) +{ + // KcSocket の管理構造 + // +----------------+ + // | KcSocket | + // | ... | + // | _info -----------+ + // +----------------+ | + // | |<--+ + // +----------------+ + Socket_setup(); + KcSocket *sock = (KcSocket *)malloc(sizeof(KcSocket) + sizeof(KcSocketInfo)); + if (sock != NULL) + { + sock->get_remote_addr = KcSocket_get_remote_addr; + sock->get_remote_port = KcSocket_get_remote_port; + sock->get_local_addr = KcSocket_get_local_addr; + sock->get_local_port = KcSocket_get_local_port; + sock->get_socket = KcSocket_get_socket; + sock->get_type = KcSocket_get_type; + sock->get_family = KcSocket_get_family; + sock->get_addrinfo = KcSocket_get_addrinfo; // for local + sock->bind = KcSocket_bind; + sock->listen = KcSocket_listen; + sock->accept = KcSocket_accept; + sock->connect = KcSocket_connect; + sock->close = KcSocket_close; + sock->send = KcSocket_send; + sock->recv = KcSocket_recv; + sock->sendto = KcSocket_sendto; + sock->recvfrom = KcSocket_recvfrom; + sock->set_ttl = KcSocket_set_ttl; + sock->join = KcSocket_join; + sock->leave = KcSocket_leave; + sock->_info = (sock + 1); + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + info->sock_fd = INVALID_SOCKET; + info->sock_type = type; + info->sock_family = family; + info->remote_addrlen = 0; + info->local_addrlen = 0; + } + return sock; +} + +// ============================================================================= +// delete +// ============================================================================= +/** + * Socket を破棄します。 + */ +void KcSocket_delete(KcSocket *socket) +{ + if (socket) + { + if (((KcSocketInfo *)socket->_info)->sock_fd != INVALID_SOCKET) + { + socket->close(socket); + } + free(socket); + } +} + +// ============================================================================= +// get_remote_addr +// ============================================================================= +/** + * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr 接続先アドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ +static const char *KcSocket_get_remote_addr(KcSocket *sock, char *addr, size_t addrlen) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->remote_addrlen) + { + int ret = getnameinfo( + (const struct sockaddr *)&info->remote_addr, + info->remote_addrlen, + addr, addrlen, NULL, 0, NI_NUMERICHOST); + if (ret == 0) + { + return addr; + } + } + return NULL; +} + +/** + * ソケットの接続先ポート番号を返します。 + * 接続先ポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ +static int KcSocket_get_remote_port(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->remote_addrlen) + { + char tmp_port[NI_MAXSERV]; + int ret = getnameinfo( + (const struct sockaddr *)&info->remote_addr, + info->remote_addrlen, + NULL, 0, tmp_port, sizeof(tmp_port), NI_NUMERICSERV); + if (ret == 0) + { + return atoi(tmp_port); + } + } + return -1; +} + +/** + * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr ローカルアドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ +static const char *KcSocket_get_local_addr(KcSocket *sock, char *addr, size_t addrlen) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->local_addrlen) + { + int ret = getnameinfo( + (const struct sockaddr *)&info->local_addr, + info->remote_addrlen, + addr, addrlen, NULL, 0, NI_NUMERICHOST); + if (ret == 0) + { + return addr; + } + } + return NULL; +} + +/** + * ソケットのローカルポート番号を返します。 + * ローカルポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ +static int KcSocket_get_local_port(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->local_addrlen) + { + char tmp_port[NI_MAXSERV]; + int ret = getnameinfo( + (const struct sockaddr *)&info->local_addr, + info->remote_addrlen, + NULL, 0, tmp_port, sizeof(tmp_port), NI_NUMERICSERV); + if (ret == 0) + { + return atoi(tmp_port); + } + } + return -1; +} + +/** + * ソケットディスクリプタを返します。 + * 通常は、ソケットディスクリプタを直接操作しないでください。 + * + * @param sock 対象ソケット + * @return ソケットディスクリプタ + */ +static socket_t KcSocket_get_socket(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + return info->sock_fd; +} + +/** + * ソケットタイプを返します。 + * + * @param sock 対象ソケット + * @return ソケットタイプ + */ +static int KcSocket_get_type(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + return info->sock_type; +} + +/** + * ソケットファミリを返します。 + * ソケット接続、バインド時には、実際にバインド、 + * あるいは接続されているソケットファミリを返します。 + * + * @param sock 対象ソケット + * @return ソケットファミリ + */ +static int KcSocket_get_family(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int result_family = info->sock_family; + if (info->sock_fd != INVALID_SOCKET) + { + struct sockaddr_storage ss; + socklen_t len = sizeof(struct sockaddr_storage); + if (getsockname(info->sock_fd, (struct sockaddr *)&ss, &len) == 0) + { + result_family = ss.ss_family; + } + } + return result_family; +} + +/** + * 指定されたアドレス、サービスのアドレス情報を取得します。 + * + * @param sock 対象ソケット + * @param result アドレス情報格納用ポインタ + * @param addr アドレス + * @param service サービス + * @param is_passive サーバソケットの場合、true を指定ください。 + */ +static bool KcSocket_get_addrinfo( + KcSocket *sock, struct addrinfo **result, + const char *addr, const char *service, bool is_passive) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + + struct addrinfo hints; + memset(&hints, 0x00, sizeof(struct addrinfo)); + hints.ai_socktype = info->sock_type; + hints.ai_family = info->sock_family; + if (is_passive) + { + hints.ai_flags = AI_PASSIVE; + } + int ret = getaddrinfo(addr, service, &hints, result); + return (ret == 0); +} + +/** + * 指定されたアドレス、サービスにバインドします。 + * 指定されたアドレスとサービスにより、複数候補がある場合は、 + * 最初に見つかったアドレスとポートにバインドします。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス (例: "80", "http", "ssh" など) + * @return true/false (bind 成功/失敗) + */ +static bool KcSocket_bind(KcSocket *sock, const char *addr, const char *service) +{ + struct addrinfo *bind_addr; + bool is_success = sock->get_addrinfo(sock, &bind_addr, addr, service, true); + if (is_success) + { + is_success = KcSocket_addrinfo_bind_and_connect(sock, bind_addr, NULL); + freeaddrinfo(bind_addr); + } + return is_success; +} + +/** + * ソケットを接続待ちソケットとしてマークをつけます。 + * 保留中の接続のキュー最大長を指定します。 + * + * @param sock 対象ソケット + * @param backlog バックログ + * @return true/false (成功/失敗) + */ +static bool KcSocket_listen(KcSocket *sock, int backlog) +{ + bool result = false; + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->sock_fd != INVALID_SOCKET) + { + int ret = listen(info->sock_fd, backlog); + result = (ret != SOCKET_ERROR); + } + return result; +} + +/** + * ソケットへの接続を受け付けます。 + * + * @param sock 対象ソケット + * @return 受け付けたソケット(失敗時NULL) + */ +static KcSocket *KcSocket_accept(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + KcSocket *client = NULL; + if (info->sock_fd != INVALID_SOCKET) + { + client = KcSocket_new(info->sock_type, info->sock_family); + if (client != NULL) + { + KcSocketInfo *cinfo = (KcSocketInfo *)client->_info; + cinfo->remote_addrlen = sizeof(struct sockaddr_storage); + cinfo->sock_fd = accept(info->sock_fd, (struct sockaddr *)&cinfo->remote_addr, &cinfo->remote_addrlen); + if (cinfo->sock_fd != INVALID_SOCKET) + { + memcpy(&cinfo->local_addr, &info->local_addr, info->local_addrlen); + cinfo->local_addrlen = info->local_addrlen; + cinfo->sock_type = SOCK_STREAM; + cinfo->sock_family = cinfo->remote_addr.ss_family; + } + else + { + KcSocket_delete(client); + client = NULL; + } + } + } + return client; +} + +/** + * 指定されたアドレス、サービス接続します。 + * local_addr に NULL を指定した場合、自動的にローカルのアドレス、サービスが設定されます。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス + * @param local_addr ローカルアドレス + * @param local_service ローカルサービス + * @return true/false (成功/失敗) + */ +static bool KcSocket_connect( + KcSocket *sock, const char *addr, const char *service, + const char *local_addr, const char *local_service) +{ + // 接続先アドレス情報取得 + struct addrinfo *conn_addr = NULL; + if (!sock->get_addrinfo(sock, &conn_addr, addr, service, false)) + { + return false; + } + + // バインドアドレス情報取得 + bool is_success = false; + struct addrinfo *bind_addr = NULL; + if (local_addr != NULL) + { // bind が必要 + is_success = sock->get_addrinfo(sock, &bind_addr, local_addr, local_service, true); + if (is_success) + { + is_success = KcSocket_addrinfo_bind_and_connect(sock, bind_addr, conn_addr); + freeaddrinfo(bind_addr); + } + } + else + { + is_success = KcSocket_addrinfo_connect(sock, conn_addr, INVALID_SOCKET); + } + freeaddrinfo(conn_addr); + return is_success; +} + +/** + * ソケットをクローズします。 + * + * @param sock 対象ソケット + * @return true/false (成功/失敗) + */ +static bool KcSocket_close(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + + if (info->sock_family == SOCK_STREAM) + { // TCP の場合は、出力を閉じて、届いているデータを読み取ってから close する。 + int ret = shutdown(info->sock_fd, SHUT_WR); + if (ret == 0) + { + int read_size; + char buff[1024]; + do + { // 届いているデータを全て読み取る + read_size = recv(info->sock_fd, buff, sizeof(buff), 0); + } while (read_size > 0); + } + } + sockclose(info->sock_fd); + return true; +} + +/** + * メッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 送信されたデータサイズ + */ +static ssize_t KcSocket_send(KcSocket *sock, const char *buff, size_t size, int flags) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + ssize_t write_size = 0; + do + { + errno = 0; + write_size = send(info->sock_fd, buff, size, flags); + } while ((write_size < 0) && (errno == EINTR)); + return write_size; +} + +/** + * メッセージを受信します。 + * 受信したメッセージは指定されたバッファに格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 読み取ったデータサイズ + */ +static ssize_t KcSocket_recv(KcSocket *sock, char *buff, size_t size, int flags) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + ssize_t read_size; + do + { + errno = 0; + read_size = recv(info->sock_fd, buff, size, flags); + } while ((read_size < 0) && (errno == EINTR)); + return read_size; +} + +/** + * 指定されたアドレス、サービスにメッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param addr アドレス + * @param service サービス + * @return 送信メッセージサイズ、エラー発生時は -1 + */ +static ssize_t KcSocket_sendto( + KcSocket *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service) +{ + (void)sock; + (void)buff; + (void)size; + (void)flags; + (void)addr; + (void)service; + return 0; +} + +/** + * メッセージを受信します。 + * src_addr, src_service がいずれも NULL 出ない場合、 + * 送信元のアドレスとサービスが格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param src_addr 送信元アドレス格納用バッファ + * @param src_addrlen 送信元アドレス格納用バッファサイズ + * @param src_service 送信元サービス格納用バッファ + * @param src_servicelen 送信元サービス格納用バッファサイズ + * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + */ +static ssize_t KcSocket_recvfrom( + KcSocket *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen) +{ + (void)sock; + (void)buff; + (void)size; + (void)flags; + (void)src_addr; + (void)src_addrlen; + (void)src_service; + (void)src_servicelen; + return 0; +} + +/** + * TTL を設定します。 + * + * @param sock 対象ソケット + * @param val 設定する TTL + * @return true/false (成功/失敗) + */ +static bool KcSocket_set_ttl(KcSocket *sock, int val) +{ + (void)sock; + (void)val; + return true; +} + +/** + * マルチキャストグループに参加するために JOIN します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @param ifindex インタフェースインデックス + * @return true/false (成功/失敗) + */ +static bool KcSocket_join( + KcSocket *sock, const char *addr, const char *ifname, unsigned int ifindex) +{ + (void)sock; + (void)addr; + (void)ifname; + (void)ifindex; + return true; +} + +/** + * マルチキャストグループから離脱します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @return true/false (成功/失敗) + */ +static bool KcSocket_leave( + KcSocket *sock, const char *addr) +{ + (void)sock; + (void)addr; + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// [内部関数] +// + +/** + * 指定された接続アドレス情報をもとにソケットの生成、接続を試みます。 + * 接続済みソケット sockfd が有効な場合、sockfd を用いて接続します。 + * + * @param sock 対象ソケット + * @param conn_addrinfo 接続アドレス情報 + * @param sockfd 接続済みソケット + * @return true/false (接続成功/失敗) + */ +static bool KcSocket_addrinfo_connect(KcSocket *sock, struct addrinfo *conn_addrinfo, socket_t sockfd) +{ + bool is_success = false; + socket_t tmp_sock = sockfd; + for (struct addrinfo *rp = conn_addrinfo; rp != NULL; rp = rp->ai_next) + { + if (sockfd == INVALID_SOCKET) + { // sockfd が無効の場合、ソケットを生成する。 + tmp_sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (tmp_sock == INVALID_SOCKET) + { + continue; + } + } + + int ret = connect(tmp_sock, rp->ai_addr, rp->ai_addrlen); + is_success = (ret != SOCKET_ERROR); + if (is_success) + { + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + info->sock_fd = tmp_sock; + info->remote_addrlen = rp->ai_addrlen; + memcpy(&info->remote_addr, rp->ai_addr, rp->ai_addrlen); + break; + } + + if (sockfd != INVALID_SOCKET) + { // sockfd が無効の場合、一時的に生成したソケットをクローズする。 + sockclose(tmp_sock); + } + } + return is_success; +} + +/** + * 指定された接続アドレス情報をもとにソケットの生成、バインドを試みます。 + * conn_addrinfo が NULL でない場合、バインド後に接続を試みます。 + * + * @param sock 対象ソケット + * @param bind_addrinfo バインドアドレス情報 + * @param conn_addrinfo 接続アドレス情報 + * @return true/false (接続成功/失敗) + */ +static bool KcSocket_addrinfo_bind_and_connect( + KcSocket *sock, struct addrinfo *bind_addrinfo, struct addrinfo *conn_addrinfo) +{ + int ret; + bool is_success = false; + + for (struct addrinfo *rp = bind_addrinfo; rp != NULL; rp = rp->ai_next) + { // sokcet : ソケット生成 + socket_t tmp_sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (tmp_sock == INVALID_SOCKET) + { + continue; + } + + // bind + const int on = 1; + setsockopt(tmp_sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on)); + ret = bind(tmp_sock, rp->ai_addr, rp->ai_addrlen); + is_success = (ret != SOCKET_ERROR); + if (!is_success) + { // bind 失敗したので次へ + sockclose(tmp_sock); + continue; + } + + // connect + if (conn_addrinfo) + { + is_success = KcSocket_addrinfo_connect(sock, conn_addrinfo, tmp_sock); + if (!is_success) + { // connect 失敗したので次へ + sockclose(tmp_sock); + continue; + } + } + + // bind または、bind と connect 成功 + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + info->sock_fd = tmp_sock; + info->local_addrlen = rp->ai_addrlen; + memcpy(&info->local_addr, rp->ai_addr, rp->ai_addrlen); + break; + } + return is_success; +} diff --git a/modules/src/kc_threads.c b/modules/src/kc_threads.c new file mode 100644 index 0000000..dd600bb --- /dev/null +++ b/modules/src/kc_threads.c @@ -0,0 +1,194 @@ +/** + * @file kc_thread.c + * @brief スレッドモジュール + * @copyright 2020 - 2024 Nomura Kei + */ +#include +#include +#include + +#include +#include +#include + +/** + * KcThread 管理情報 + */ +typedef struct +{ + thrd_t tid; + void *args; + int (*run)(void *args); + atomic_bool alive_state; +} KcThreadInfo; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static bool KcThread_is_alive(KcThread *thread); +static bool KcThread_start(KcThread *thread, void *args); +static bool KcThread_join(KcThread *thread); +static int KcThread_run(void *thread); + +// ============================================================================= +// new +// ============================================================================= +/** + * スレッドを生成します。 + * + * @param run スレッドにて実行する関数へのポインタ + */ +KcThread *KcThread_new(int (*run)(void *args)) +{ + // KcThread の管理構造 + // +--------------+ + // | KcThread | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // +--------------+ + KcThread *thread = (KcThread *)malloc(sizeof(KcThread) + sizeof(KcThreadInfo)); + if (thread != NULL) + { + thread->is_alive = KcThread_is_alive; + thread->start = KcThread_start; + thread->join = KcThread_join; + thread->_info = (thread + 1); + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + // info->tid + info->run = run; + atomic_init(&info->alive_state, false); + } + return thread; +} + +// ============================================================================= +// delete +// ============================================================================= +/** + * Thread を破棄します。 + * + * @param thread 破棄するスレッド + */ +void KcThread_delete(KcThread *thread) +{ + free(thread); +} + +// ============================================================================= +// sleep +// ============================================================================= +/** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープさせるミリ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ +bool KcThread_msleep(long time, bool force) +{ + long sec = time / 1000; + long msec = time - (sec * 1000); + long nsec = msec * 1000 * 1000; + return KcThread_sleep(sec, nsec, force); +} + +/** + * 指定された sec 秒 + nsec ナノ秒間現在のスレッドをスリープさせます。 + * + * @param sec スリープさせる秒数 + * @param nsec スリープさせるナノ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ +bool KcThread_sleep(long sec, long nsec, bool force) +{ + struct timespec remaining = {sec, nsec}; + int ret; + do + { + errno = 0; + ret = thrd_sleep(&remaining, &remaining); + } while ((ret == -1) && force && (errno == EINTR)); + return (ret == 0); +} + +// ============================================================================= +// is_alive +// ============================================================================= +/** + * 指定されたスレッドが生きているか否かを返します。 + * + * @param thread 対象スレッド + * @return true/false (スレッドが生存している/していない) + */ +static bool KcThread_is_alive(KcThread *thread) +{ + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + return atomic_load(&info->alive_state); +} + +// ============================================================================= +// start +// ============================================================================= +/** + * スレッドを開始します。 + * + * @param thread 対象スレッド + * @param args スレッドに渡す情報 + * @return true/false (スレッド起動成功/失敗) + */ +static bool KcThread_start(KcThread *thread, void *args) +{ + int ret = thrd_error; + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + if (!atomic_load(&info->alive_state)) + { + info->args = args; + ret = thrd_create(&info->tid, KcThread_run, thread); + } + return (ret == thrd_success); +} + +// ============================================================================= +// join +// ============================================================================= +/** + * スレッドが終了するのを待機します。 + * + * @param thread 対象スレッド + * @return true/false (成功/失敗) + */ +static bool KcThread_join(KcThread *thread) +{ + int ret = thrd_error; + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + ret = thrd_join(info->tid, NULL); + return (ret == thrd_success); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// 内部関数 +// + +// ============================================================================= +// run +// ============================================================================= +/** + * スレッドで実行される関数。 + * start にて関数を別スレッドにて実行します。 + * + * @param args スレッド情報 + * @return + */ +int KcThread_run(void *args) +{ + KcThread *thread = (KcThread *)args; + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + atomic_store(&info->alive_state, true); + int ret = info->run(info->args); + atomic_store(&info->alive_state, false); + return ret; +} diff --git a/modules/src/kc_threads_win.c b/modules/src/kc_threads_win.c new file mode 100644 index 0000000..0439fda --- /dev/null +++ b/modules/src/kc_threads_win.c @@ -0,0 +1,201 @@ +/** + * @file kc_thread.c + * @brief スレッドモジュール + * @copyright 2020 - 2024 Nomura Kei + */ +#include +#include + +#if (KC_IS_WINDOWS) + +int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) +{ + if ((thr == NULL) || (func == NULL)) + { + return thrd_error; + } + + thr->handle = CreateThread( + NULL, // セキュリティ属性 + 0, // スタックサイズ + (LPTHREAD_START_ROUTINE)func, // スレッド関数 + arg, // 引数 + 0, // 作成フラグ + &(thr->thread_id) // スレッドID + ); + + if (thr->handle == NULL) + { + return thrd_error; + } + return thrd_success; +} +int thrd_join(thrd_t thr, int *res) +{ + if (WaitForSingleObject(thr.handle, INFINITE) != WAIT_OBJECT_0) + { + return thrd_error; + } + if (res != NULL) + { + DWORD retcode; + if (GetExitCodeThread(thr.handle, &retcode) == 0) + { + return thrd_error; + } + *res = (int)retcode; + } + CloseHandle(thr.handle); + return thrd_success; +} + +int thrd_detach(thrd_t thr) +{ + if (thr.handle == NULL) + { + return thrd_error; + } + if (CloseHandle(thr.handle) == 0) + { + return thrd_error; + } + return thrd_success; +} + +thrd_t thrd_current(void) +{ + thrd_t current; + current.handle = GetCurrentThread(); + current.thread_id = GetThreadId(current.handle); + return current; +} + +int thrd_equal(thrd_t lhs, thrd_t rhs) +{ + return (lhs.thread_id == rhs.thread_id); +} + +void thrd_yield(void) +{ + SwitchToThread(); +} + +int thrd_sleep(const struct timespec *duration, struct timespec *remaining) +{ + if (duration == NULL) + { + return thrd_error; + } + + // Windows の Sleep は、ms 単位 + DWORD msec = (DWORD)(duration->tv_sec * 1000 + duration->tv_nsec / 1000000); + Sleep(msec); + + // 常に成功したものとして、remaining は 0 に設定する。 + if (remaining != NULL) + { + remaining->tv_sec = 0; + remaining->tv_nsec = 0; + } + return thrd_success; +} + +int mtx_init(mtx_t *mtx, int type) +{ + if (mtx == NULL) + { + return thrd_error; + } + mtx->type = type; + if (type & mtx_plain) + { + InitializeCriticalSection(&mtx->cs); + } + else if (type & mtx_recursive) + { + InitializeCriticalSection(&mtx->cs); + } + else + { + return thrd_error; + } + return thrd_success; +} + +void mtx_destroy(mtx_t *mtx) +{ + DeleteCriticalSection(&mtx->cs); +} +int mtx_lock(mtx_t *mtx) +{ + EnterCriticalSection(&mtx->cs); + return thrd_success; +} +int mtx_unlock(mtx_t *mtx) +{ + LeaveCriticalSection(&mtx->cs); + return thrd_success; +} + +int cnd_init(cnd_t *cond) +{ + if (cond == NULL) + { + return thrd_error; + } + InitializeConditionVariable(&cond->cond); + return thrd_success; +} + +int cnd_signal(cnd_t *cond) +{ + if (cond == NULL) + { + return thrd_error; + } + WakeConditionVariable(&cond->cond); + return thrd_success; +} +int cnd_broadcast(cnd_t *cond) +{ + if (cond == NULL) + { + return thrd_error; + } + WakeAllConditionVariable(&cond->cond); + return thrd_success; +} +int cnd_wait(cnd_t *cond, mtx_t *mtx) +{ + if ((cond == NULL) || (mtx == NULL)) + { + return thrd_error; + } + if (SleepConditionVariableCS(&cond->cond, &mtx->cs, INFINITE)) + { + return thrd_success; + } + return thrd_error; +} + +int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts) +{ + if ((cond == NULL) || (mtx == NULL) || (ts == NULL)) + { + return thrd_error; + } + DWORD msec = (DWORD)(ts->tv_sec * 1000 + ts->tv_nsec / 1000000); + if (SleepConditionVariableCS(&cond->cond, &mtx->cs, msec)) + { + return thrd_timedout; + } + return thrd_error; +} + +void cnd_destroy(cnd_t *cond) +{ + // Nothing to do + UNUSED_VARIABLE(cond); +} + +#endif \ No newline at end of file diff --git a/modules/test/src/test_lock_guard.c b/modules/test/src/test_lock_guard.c index f00ed0b..9c3249f 100644 --- a/modules/test/src/test_lock_guard.c +++ b/modules/test/src/test_lock_guard.c @@ -37,7 +37,7 @@ { mtx_t mutex; mtx_init(&mutex, mtx_plain | mtx_recursive); - KcLockGuard lock_guard = kc_lock_guard_init(&mutex); + KcLockGuard lock_guard = kc_lock_guard_init(&mutex, __FILE__, __func__, __LINE__); assert_not_null(lock_guard.mutex); // Lock Guard 解放 @@ -53,7 +53,7 @@ */ static void test_lock_guard_init_null(void) { - KcLockGuard lock_guard = kc_lock_guard_init(NULL); + KcLockGuard lock_guard = kc_lock_guard_init(NULL, __FILE__, __func__, __LINE__); assert_equals(EINVAL, errno); assert_null(lock_guard.mutex); } @@ -69,7 +69,7 @@ mtx_t mutex; mtx_init(&mutex, mtx_plain | mtx_recursive); mtx_destroy(&mutex); - KcLockGuard lock_guard = kc_lock_guard_init(&mutex); + KcLockGuard lock_guard = kc_lock_guard_init(&mutex, __FILE__, __func__, __LINE__); assert_null(lock_guard.mutex); } @@ -83,7 +83,7 @@ { mtx_t mutex; mtx_init(&mutex, mtx_plain | mtx_recursive); - KcLockGuard lock_guard = kc_lock_guard_init(&mutex); + KcLockGuard lock_guard = kc_lock_guard_init(&mutex, __FILE__, __func__, __LINE__); assert_not_null(lock_guard.mutex); // Lock Guard 解放 diff --git a/modules/test/src/test_queue.c b/modules/test/src/test_queue.c index cbec7e0..1595972 100644 --- a/modules/test/src/test_queue.c +++ b/modules/test/src/test_queue.c @@ -1,12 +1,12 @@ #include #include -#include #include #include #include #include #include +#include #include "ut.h" @@ -58,14 +58,11 @@ static int test_queue_offer_thread(void *arg) { KcQueue *queue = (KcQueue *)arg; - struct timespec spec; - spec.tv_sec = 0; - spec.tv_nsec = 1; for (int i = 0; i < 100; i++) { printf("offer: %d\n", i); queue->offer(queue, &i, sizeof(int)); - thrd_sleep(&spec, NULL); + KcThread_sleep(0, 1, false); } return 0; } @@ -73,9 +70,6 @@ // for poll thread static int test_queue_poll_thread(void *arg) { - struct timespec spec; - spec.tv_sec = 0; - spec.tv_nsec = 1; KcQueue *queue = (KcQueue *)arg; for (int i = 0; i < 100; i++) { @@ -100,7 +94,7 @@ { printf("poll: (null)\n"); } - thrd_sleep(&spec, NULL); + KcThread_sleep(0, 1, false); } return 0; } @@ -112,27 +106,28 @@ { KcQueue *queue = KcQueue_new(10); - thrd_t offer_thread, poll_thread; + KcThread *offer_thread = KcThread_new(test_queue_offer_thread); + KcThread *poll_thread = KcThread_new(test_queue_poll_thread); - thrd_create(&offer_thread, test_queue_offer_thread, queue); - thrd_create(&poll_thread, test_queue_poll_thread, queue); + offer_thread->start(offer_thread, queue); + poll_thread->start(poll_thread, queue); - thrd_join(offer_thread, NULL); - thrd_join(poll_thread, NULL); + offer_thread->join(offer_thread); + poll_thread->join(poll_thread); + KcQueue_delete(queue); + KcThread_delete(offer_thread); + KcThread_delete(poll_thread); } // for put thread static int test_queue_put_thread(void *arg) { KcQueue *queue = (KcQueue *)arg; - struct timespec spec; - spec.tv_sec = 0; - spec.tv_nsec = 1 * 1000; for (int i = 0; i < 100; i++) { queue->put(queue, &i, sizeof(int)); - thrd_sleep(&spec, NULL); + KcThread_sleep(0, 1000, false); } return 0; } @@ -140,9 +135,6 @@ // for take thread static int test_queue_take_thread(void *arg) { - struct timespec spec; - spec.tv_sec = 0; - spec.tv_nsec = 1; KcQueue *queue = (KcQueue *)arg; for (int i = 0; i < 100; i++) { @@ -150,7 +142,7 @@ size_t size = sizeof(int); queue->take(queue, &element, &size); assert_equals(i, element); - thrd_sleep(&spec, NULL); + KcThread_sleep(0, 1, false); } return 0; } @@ -158,9 +150,6 @@ // for take thread static int test_queue_take2_thread(void *arg) { - struct timespec spec; - spec.tv_sec = 0; - spec.tv_nsec = 100 * 1000; KcQueue *queue = (KcQueue *)arg; for (int i = 0; i < 100; i++) { @@ -168,7 +157,7 @@ size_t size = sizeof(int); queue->take(queue, &element, &size); assert_equals(i, element); - thrd_sleep(&spec, NULL); + KcThread_sleep(0, 100 * 1000, false); } return 0; } @@ -180,22 +169,31 @@ { { KcQueue *queue = KcQueue_new(0); - thrd_t put_thread, take_thread; - thrd_create(&put_thread, test_queue_put_thread, queue); - thrd_create(&take_thread, test_queue_take_thread, queue); - thrd_join(put_thread, NULL); - thrd_join(take_thread, NULL); + KcThread *put_thread = KcThread_new(test_queue_put_thread); + KcThread *take_thread = KcThread_new(test_queue_take_thread); + + put_thread->start(put_thread, queue); + take_thread->start(take_thread, queue); + + put_thread->join(put_thread); + take_thread->join(take_thread); + KcThread_delete(put_thread); + KcThread_delete(take_thread); KcQueue_delete(queue); } // PUT ブロックパターン { KcQueue *queue = KcQueue_new(5); - thrd_t put_thread, take_thread; - thrd_create(&put_thread, test_queue_put_thread, queue); - thrd_create(&take_thread, test_queue_take2_thread, queue); - thrd_join(put_thread, NULL); - thrd_join(take_thread, NULL); + KcThread *put_thread = KcThread_new(test_queue_put_thread); + KcThread *take_thread = KcThread_new(test_queue_take2_thread); + put_thread->start(put_thread, queue); + take_thread->start(take_thread, queue); + + put_thread->join(put_thread); + take_thread->join(take_thread); + KcThread_delete(put_thread); + KcThread_delete(take_thread); KcQueue_delete(queue); } } diff --git a/config.mk b/config.mk index a0ac27c..f7c6e47 100644 --- a/config.mk +++ b/config.mk @@ -25,14 +25,17 @@ # for Windows CROSS_COMPILE ?=i686-w64-mingw32- LIBS ?= -lws2_32 +DEBUG_OPTIONS ?= else # For Linux ifeq ($(strip $(ARCH)),aarch64) CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector else CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector endif endif diff --git a/mk/base-cmd.mk b/mk/base-cmd.mk index f429bcf..e74c114 100644 --- a/mk/base-cmd.mk +++ b/mk/base-cmd.mk @@ -33,8 +33,13 @@ CC = $(CROSS_COMPILE)gcc CXX = $(CROSS_COMPILE)g++ + #CC = $(CROSS_COMPILE)clang #CXX = $(CROSS_COMPILE)clang++ + +#CC = $(CROSS_COMPILE)clang-16 +#CXX = $(CROSS_COMPILE)clang++16 + RANLIB = $(CROSS_COMPILE)ranlib ADDR2LINE = $(CROSS_COMPILE)addr2line AR = $(CROSS_COMPILE)ar diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 2cc417c..eebded1 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -69,7 +69,7 @@ # 値が2の場合、さらなるチェックを追加するが、規格準拠のプログラムが失敗する可能性がある。 # いくつかのチェックは、コンパイル時に実行され、コンパイラの警告として表示される。 # ------------------------------------------------------------------------------ -DEBUG_OPTIONS += -fstack-protector +# DEBUG_OPTIONS += -fstack-protector DEBUG_OPTIONS += --coverage DEBUG_OPTIONS += -g3 -ggdb DEBUG_OPTIONS += -O0 diff --git a/mk/link-a-conf.mk b/mk/link-a-conf.mk index 0e872f3..631e6d5 100644 --- a/mk/link-a-conf.mk +++ b/mk/link-a-conf.mk @@ -10,8 +10,11 @@ HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) +HEADER_FILES_WIN = $(wildcard include/win/*.h) $(wildcard include/win/*.hpp) +TOP_HEADER_FILES_WIN = $(addprefix $(TOPDIR)/include/win/,$(notdir $(HEADER_FILES_WIN))) + CLEAN_FILES += $(TOP_TARGET) -CLEAN_FILES += $(TOP_HEADER_FILES) +CLEAN_FILES += $(TOP_HEADER_FILES) $(TOP_HEADER_FILES_WIN) endif endif diff --git a/mk/link-a-rule.mk b/mk/link-a-rule.mk index d94a54a..657d858 100644 --- a/mk/link-a-rule.mk +++ b/mk/link-a-rule.mk @@ -10,6 +10,7 @@ $(TOP_TARGET): $(TARGET) ifneq ($(strip $(HEADER_FILES)),) $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ + $(CP) -f $(HEADER_FILES_WIN) $(TOPDIR)/include/win/ endif $(CP) -f $(TARGET) $(TOPDIR)/lib/ diff --git a/modules/include/kc_lock_guard.h b/modules/include/kc_lock_guard.h index 8bb70bd..ad3915f 100644 --- a/modules/include/kc_lock_guard.h +++ b/modules/include/kc_lock_guard.h @@ -7,18 +7,16 @@ #define KC_LOCK_GUARD_H #include -#include - +#include /** * LockGuard 管理構造体 */ typedef struct { - mtx_t* mutex; + mtx_t *mutex; } KcLockGuard; - /** * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 * 使用例) @@ -34,8 +32,7 @@ * } */ #define kc_lock_guard(lock) \ - for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) - + for (KcLockGuard _guard = kc_lock_guard_init(lock, __FILE__, __func__, __LINE__); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 @@ -44,15 +41,13 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex); - +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line); /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard); +void kc_lock_guard_release(KcLockGuard *guard); - -#endif // KC_LOCK_GUARD_H +#endif // KC_LOCK_GUARD_H diff --git a/modules/include/kc_memory.h b/modules/include/kc_memory.h index ffce4ed..c6ba9e9 100644 --- a/modules/include/kc_memory.h +++ b/modules/include/kc_memory.h @@ -22,12 +22,13 @@ #ifndef KC_MEMORY_H #define KC_MEMORY_H -#include +#include #include #include #include #include +#include #ifdef __cplusplus extern "C" @@ -273,8 +274,6 @@ #define calloc(nmemb, size) kc_memory_manager->calloc(nmemb, size, __FILE__, __func__, __LINE__) #define realloc(ptr, size) kc_memory_manager->realloc(ptr, size, __FILE__, __func__, __LINE__) #define free(ptr) kc_memory_manager->free(ptr) -#else -#include #endif #ifdef __cplusplus diff --git a/modules/include/kc_socket.h b/modules/include/kc_socket.h new file mode 100644 index 0000000..7d952c7 --- /dev/null +++ b/modules/include/kc_socket.h @@ -0,0 +1,291 @@ +/** + * @file kc_socket.h + * @brief ソケットモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_SOCKET_H +#define KC_SOCKET_H + +#include + +#if (KC_IS_WINDOWS) +typedef SOCKET socket_t; +#define SHUT_RD SD_RECEIVE +#define SHUT_WR SD_SEND +#define SHUT_RDWR SD_BOTH +#define sockclose(sockfd) closesocket(sockfd) +#else +#include +#include +#include +#include +#include +#include +#include +typedef int socket_t; +#define INVALID_SOCKET (-1) +#define SOCKET_ERROR (-1) +#define sockclose(sockfd) close(sockfd) +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcSocket_ + { + + /** + * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr 接続先アドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_remote_addr)(struct KcSocket_ *sock, char *addr, size_t addrlen); + + /** + * ソケットの接続先ポート番号を返します。 + * 接続先ポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_remote_port)(struct KcSocket_ *sock); + + /** + * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr ローカルアドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_local_addr)(struct KcSocket_ *sock, char *addr, size_t addlen); + + /** + * ソケットのローカルポート番号を返します。 + * ローカルポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_local_port)(struct KcSocket_ *sock); + + /** + * ソケットディスクリプタを返します。 + * 通常は、ソケットディスクリプタを直接操作しないでください。 + * + * @param sock 対象ソケット + * @return ソケットディスクリプタ + */ + socket_t (*get_socket)(struct KcSocket_ *sock); + + /** + * ソケットタイプを返します。 + * + * @param sock 対象ソケット + * @return ソケットタイプ + */ + int (*get_type)(struct KcSocket_ *sock); + + /** + * ソケットファミリを返します。 + * 実際にバインド、あるいは接続されているソケットファミリを返します。 + * + * @param sock 対象ソケット + * @return ソケットファミリ + */ + int (*get_family)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービスのアドレス情報を取得します。 + * + * @param sock 対象ソケット + * @param result アドレス情報格納用ポインタ + * @param addr アドレス + * @param service サービス + * @param is_passive サーバソケットの場合、true を指定ください。 + */ + bool (*get_addrinfo)( + struct KcSocket_ *sock, + struct addrinfo **result, + const char *addr, + const char *service, + bool is_passive); + + /** + * 指定されたアドレス、サービスにバインドします。 + * 指定されたアドレスとサービスにより、複数候補がある場合は、 + * 最初に見つかったアドレスとポートにバインドします。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス (例: "80", "http", "ssh" など) + * @return true/false (bind 成功/失敗) + */ + bool (*bind)(struct KcSocket_ *sock, const char *addr, const char *service); + + /** + * ソケットを接続待ちソケットとしてマークをつけます。 + * 保留中の接続のキュー最大長を指定します。 + * + * @param sock 対象ソケット + * @param backlog バックログ + * @return true/false (成功/失敗) + */ + bool (*listen)(struct KcSocket_ *sock, int backlog); + + /** + * ソケットへの接続を受け付けます。 + * + * @param sock 対象ソケット + * @return 受け付けたソケット + */ + struct KcSocket_ *(*accept)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービス接続します。 + * local_addr に NULL を指定した場合、自動的にローカルのアドレスが設定されます。 + * local_service に -1 を指定した場合、自動的にローカルのポート番号が割りつけられます。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス + * @param local_addr ローカルアドレス + * @param local_service ローカルサービス + * @return true/false (成功/失敗) + */ + bool (*connect)(struct KcSocket_ *sock, const char *addr, const char *service, const char *local_addr, const char *local_port); + + /** + * ソケットをクローズします。 + * + * @param sock 対象ソケット + * @return true/false (成功/失敗) + */ + bool (*close)(struct KcSocket_ *sock); + + /** + * メッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 送信されたデータサイズ + */ + ssize_t (*send)(struct KcSocket_ *sock, const char *buff, size_t size, int flags); + + /** + * メッセージを受信します。 + * 受信したメッセージは指定されたバッファに格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 読み取ったデータサイズ + */ + ssize_t (*recv)(struct KcSocket_ *sock, char *buff, size_t size, int flags); + + /** + * 指定されたアドレス、サービスにメッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param addr アドレス + * @param service サービス + * @return 送信メッセージサイズ、エラー発生時は -1 + */ + ssize_t (*sendto)( + struct KcSocket_ *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service); + + /** + * メッセージを受信します。 + * src_addr, src_service がいずれも NULL 出ない場合、 + * 送信元のアドレスとサービスが格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param src_addr 送信元アドレス格納用バッファ + * @param src_addrlen 送信元アドレス格納用バッファサイズ + * @param src_service 送信元サービス格納用バッファ + * @param src_servicelen 送信元サービス格納用バッファサイズ + * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + */ + ssize_t (*recvfrom)( + struct KcSocket_ *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen); + + /** + * TTL を設定します。 + * + * @param sock 対象ソケット + * @param val 設定する TTL + * @return true/false (成功/失敗) + */ + bool (*set_ttl)(struct KcSocket_ *sock, int val); + + /** + * マルチキャストグループに参加するために JOIN します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @param ifindex インタフェースインデックス + * @return true/false (成功/失敗) + */ + bool (*join)(struct KcSocket_ *sock, const char *addr, const char *ifname, unsigned int ifindex); + + /** + * マルチキャストグループから離脱します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @return true/false (成功/失敗) + */ + bool (*leave)(struct KcSocket_ *sock, const char *addr); + + /** + * Socket 管理情報 + */ + void *_info; + + } KcSocket; + + /** + * 指定されたタイプ、ファミリのSocket オブジェクトを構築します。 + * + * @param type タイプ(SOCK_STREAM/SOCK_DGRAM/SOCK_RAW) + * @param family ファミリ(AF_UNSPEC/AF_INET/AF_INET6) + */ + KcSocket *KcSocket_new(int type, int family); + + /** + * Socket を破棄します。 + */ + void KcSocket_delete(KcSocket *socket); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_threads.h b/modules/include/kc_threads.h new file mode 100644 index 0000000..764176f --- /dev/null +++ b/modules/include/kc_threads.h @@ -0,0 +1,98 @@ +/** + * @file kc_threads.h + * @brief スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_THREADS_H +#define KC_THREADS_H + +#include + +#if (KC_IS_WINDOWS) +#include +#else +#include +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcThread_ + { + + /** + * 指定されたスレッドが生きているか否かを返します。 + * + * @param thread 対象スレッド + * @return true/false (スレッドが生存している/していない) + */ + bool (*is_alive)(struct KcThread_ *thread); + + /** + * スレッドを開始します。 + * + * @param thread 対象スレッド + * @param args スレッドに渡す情報 + * @return true/false (スレッド起動成功/失敗) + */ + bool (*start)(struct KcThread_ *thread, void *args); + + /** + * スレッドが終了するのを待機します。 + * + * @param thread 対象スレッド + */ + bool (*join)(struct KcThread_ *thread); + + /** + * Thread 管理情報 + */ + void *_info; + + } KcThread; + + /** + * スレッドを生成します。 + * + * @param run スレッドにて実行する関数へのポインタ + */ + KcThread *KcThread_new(int (*run)(void *args)); + + /** + * Thread を破棄します。 + * + * @param thread 破棄するスレッド + */ + void KcThread_delete(KcThread *thread); + + /** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープさせるミリ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_msleep(long time, bool force); + + /** + * 指定された sec 秒 + nsec ナノ秒間現在のスレッドをスリープさせます。 + * + * @param sec スリープさせる秒数 + * @param nsec スリープさせるナノ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_sleep(long sec, long nsec, bool force); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_THREADS_H diff --git a/modules/include/kc_threads_win.h b/modules/include/kc_threads_win.h new file mode 100644 index 0000000..b40ab10 --- /dev/null +++ b/modules/include/kc_threads_win.h @@ -0,0 +1,98 @@ +/** + * @file kc_windows_thread.h + * @brief Windows 用スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_WINDOWS_THREAD_H +#define KC_WINDOWS_THREAD_H + +#include +#if (KC_IS_WINDOWS) +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#define thread_local _Thread_local + + //////////////////////////////////////////////////////////////////////// + // + // Thread + // + + enum + { + thrd_success = 0, //!< 成功 + thrd_bussy = 1, //!< busy + thrd_error = 2, //!< エラー発生 + thrd_nomem = 3, //!< メモリ不足 + thrd_timedout = 4 //!< タイムアウト発生 + }; + + typedef struct + { + HANDLE handle; + DWORD thread_id; + } thrd_t; + + typedef int (*thrd_start_t)(void *); + int thrd_create(thrd_t *thr, thrd_start_t func, void *arg); + int thrd_join(thrd_t thr, int *res); + int thrd_detach(thrd_t thr); + thrd_t thrd_current(void); + int thrd_equal(thrd_t lhs, thrd_t rhs); + int thrd_sleep(const struct timespec *duration, struct timespec *remaining); + + //////////////////////////////////////////////////////////////////////// + // + // mutex + // + + enum + { + mtx_plain = 0, //!< 通常 + mtx_recursive = 1, //!< 再帰 + mtx_timed = 2 //!< timed + }; + + typedef struct + { + CRITICAL_SECTION cs; + int type; + } mtx_t; + + int mtx_init(mtx_t *mtx, int type); + void mtx_destroy(mtx_t *mtx); + int mtx_lock(mtx_t *mtx); + int mtx_unlock(mtx_t *mtx); + + //////////////////////////////////////////////////////////////////////// + // + // cond + // + + typedef struct + { + CONDITION_VARIABLE cond; + } cnd_t; + + int cnd_init(cnd_t *cond); + int cnd_signal(cnd_t *cond); + int cnd_broadcast(cnd_t *cond); + int cnd_wait(cnd_t *cond, mtx_t *mtx); + int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts); + void cnd_destroy(cnd_t *cond); + +#endif // (KC_IS_WINDOWS) +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_windows.h b/modules/include/kc_windows.h index ee70898..6e1c659 100644 --- a/modules/include/kc_windows.h +++ b/modules/include/kc_windows.h @@ -37,9 +37,9 @@ #endif // よく利用されるヘッダファイルをインクルードする -#include #include #include +#include #ifdef _MSC_VER #pragma comment(lib, "ws2_32.lib") #endif diff --git a/modules/src/kc_assert.c b/modules/src/kc_assert.c index 371fc8f..7cd4ecc 100644 --- a/modules/src/kc_assert.c +++ b/modules/src/kc_assert.c @@ -6,9 +6,9 @@ #include #include #include -#include #include #include +#include // プロトタイプ宣言 static void assert_default_handler(const char *msg); diff --git a/modules/src/kc_env.c b/modules/src/kc_env.c index 0675c73..95f185c 100644 --- a/modules/src/kc_env.c +++ b/modules/src/kc_env.c @@ -50,7 +50,7 @@ char *result = NULL; #if (KC_IS_WINDOWS) static char buff[KC_ENV_BUFFER_MAX]; - DWORD ret = GetEnvironmentVarable(key_name, (LPSTR)buff, sizeof(buff)); + DWORD ret = GetEnvironmentVariable(key_name, (LPSTR)buff, sizeof(buff)); if (ret != 0) { result = buff; diff --git a/modules/src/kc_list_array.c b/modules/src/kc_list_array.c index 246ed14..f29fc08 100644 --- a/modules/src/kc_list_array.c +++ b/modules/src/kc_list_array.c @@ -3,10 +3,14 @@ * @brief 配列リストモジュール * @copyright 2003 - 2023 Nomura Kei */ +#include #if defined(__GNUC__) #define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) #define qsort_s qsort_r #endif +#endif // defined(__GNUC__) + #include #include #include @@ -17,6 +21,7 @@ #include #include #include + /** * KcArrayList 管理情報 */ @@ -306,7 +311,11 @@ * @param context コンテキスト(KcListSortInfo) * @return 比較結果 */ +#if (KC_IS_WINDOWS) +static int KcArrayList_comparator(void *context, const void *x, const void *y) +#else static int KcArrayList_comparator(const void *x, const void *y, void *context) +#endif { KcListSortInfo *sort_info = (KcListSortInfo *)context; int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); diff --git a/modules/src/kc_list_linked.c b/modules/src/kc_list_linked.c index 30575b9..47edbe4 100644 --- a/modules/src/kc_list_linked.c +++ b/modules/src/kc_list_linked.c @@ -3,10 +3,14 @@ * @brief Linked リストモジュール * @copyright 2003 - 2023 Nomura Kei */ +#include #if defined(__GNUC__) #define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) #define qsort_s qsort_r #endif +#endif // defined(__GNUC__) + #include #include #include @@ -18,6 +22,13 @@ #include #include +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) +#define qsort_s qsort_r +#endif // KC_IS_WINDOWS +#endif // defined(__GNUC__) + /** * KcLinkedList Entry 情報 */ @@ -291,7 +302,11 @@ * @param context コンテキスト(KcListSortInfo) * @return 比較結果 */ +#if (KC_IS_WINDOWS) +static int KcLinkedList_comparator(void *context, const void *x, const void *y) +#else static int KcLinkedList_comparator(const void *x, const void *y, void *context) +#endif // KC_IS_WINDOWS { KcListSortInfo *sort_info = (KcListSortInfo *)context; const KcLinkedListEntry **entry_x = (const KcLinkedListEntry **)x; diff --git a/modules/src/kc_lock_guard.c b/modules/src/kc_lock_guard.c index 6ef68e5..648a153 100644 --- a/modules/src/kc_lock_guard.c +++ b/modules/src/kc_lock_guard.c @@ -7,7 +7,6 @@ #include - /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 * ロックに成功するとロック管理用のオブジェクトを返します。 @@ -15,36 +14,37 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex) +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line) { - KcLockGuard guard = { .mutex = mutex }; + KcLockGuard guard = {.mutex = mutex}; if (mutex == NULL) { errno = EINVAL; perror("kc_lock_guard_init: (mutex = NULL)"); + fprintf(stderr, "%s:%s:%d kc_lock_guard_init : mutex = NULL\n", file, func, line); return guard; } + errno = 0; int ret = mtx_lock(mutex); if (ret != thrd_success) { - perror("kc_lock_guard : mtx_lock error"); + fprintf(stderr, "%s:%s:%d kc_lock_guard : mtx_lock error\n", file, func, line); guard.mutex = NULL; } return guard; } - /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard) +void kc_lock_guard_release(KcLockGuard *guard) { - if (guard->mutex != NULL) { - mtx_unlock(guard->mutex); - guard->mutex = NULL; + if (guard->mutex != NULL) + { + mtx_unlock(guard->mutex); + guard->mutex = NULL; } } - diff --git a/modules/src/kc_memory.c b/modules/src/kc_memory.c index 5cc4186..6dbbc88 100644 --- a/modules/src/kc_memory.c +++ b/modules/src/kc_memory.c @@ -622,7 +622,11 @@ */ void *raw_aligned_alloc(size_t alignment, size_t size) { +#if (KC_IS_WINDOWS) + return _aligned_malloc(size, alignment); +#else return aligned_alloc(alignment, size); +#endif } /** diff --git a/modules/src/kc_memory_dump.c b/modules/src/kc_memory_dump.c index 111e328..49e63de 100644 --- a/modules/src/kc_memory_dump.c +++ b/modules/src/kc_memory_dump.c @@ -144,7 +144,7 @@ { // 空きバッファサイズ無しのため、何もしない。 return; } - int write_size = snprintf(info->write_ptr, info->rest_size, msg); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s", msg); if (write_size > info->rest_size) { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) write_size = (info->rest_size - 1); diff --git a/modules/src/kc_memory_listener.c b/modules/src/kc_memory_listener.c index 29ff2a0..ee69a13 100644 --- a/modules/src/kc_memory_listener.c +++ b/modules/src/kc_memory_listener.c @@ -98,5 +98,5 @@ KcMemoryDump_dump(buff, sizeof(buff), entry, 16, true, true, 130); fprintf(stderr, "[ERROR] %s", buff); } - fprintf(stderr, msg); + fprintf(stderr, "%s", msg); } diff --git a/modules/src/kc_queue.c b/modules/src/kc_queue.c index 544d763..d9f2a14 100644 --- a/modules/src/kc_queue.c +++ b/modules/src/kc_queue.c @@ -9,11 +9,11 @@ #include #include #include -#include #include #include #include +#include #include #include @@ -122,7 +122,10 @@ static int KcQueue_size(struct KcQueue_ *queue) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - size_t size = info->list->size(info->list); + size_t size = 0; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + size = info->list->size(info->list); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== return (int)size; } @@ -138,7 +141,11 @@ static bool KcQueue_is_empty(struct KcQueue_ *queue) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - return info->list->is_empty(info->list); + bool is_empty = true; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + is_empty = info->list->is_empty(info->list); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== + return is_empty; } // ============================================================================= @@ -155,7 +162,11 @@ static bool KcQueue_contains(struct KcQueue_ *queue, const void *element, size_t size) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - return info->list->contains(info->list, element, size); + bool is_contains = false; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + is_contains = info->list->contains(info->list, element, size); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== + return is_contains; } // ============================================================================= @@ -227,7 +238,11 @@ static void *KcQueue_peek(struct KcQueue_ *queue, size_t *size) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - return info->list->get(info->list, 0, size); + void *value = NULL; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + value = info->list->get(info->list, 0, size); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== + return value; } // ============================================================================= diff --git a/modules/src/kc_socket.c b/modules/src/kc_socket.c new file mode 100644 index 0000000..cb67996 --- /dev/null +++ b/modules/src/kc_socket.c @@ -0,0 +1,753 @@ +/** + * @file kc_socket.c + * @brief ソケットモジュール + * @copyright 2003 - 2024 Nomura Kei + */ +#include +#include + +#include +#include +#include +#include + +/** + * KcSocket 管理情報 + */ +typedef struct +{ + socket_t sock_fd; //!< ソケットディスクリプタ + int sock_type; //!< ソケットタイプ + int sock_family; //!< ソケットファミリ + struct sockaddr_storage remote_addr; //!< リモートアドレス + struct sockaddr_storage local_addr; //!< ローカルアドレス + socklen_t remote_addrlen; //!< リモートアドレス長 + socklen_t local_addrlen; //!< ローカルアドレス帳 +} KcSocketInfo; + +/** + * アドレス情報 + */ +typedef struct +{ + const char *addr; //!< アドレス + const char *service; //!< サービス +} KcSocketAddress; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static void Socket_setup(void); +static void Socket_cleanup(void); +static const char *KcSocket_get_remote_addr(KcSocket *sock, char *addr, size_t addrlen); +static int KcSocket_get_remote_port(KcSocket *sock); +static const char *KcSocket_get_local_addr(KcSocket *sock, char *addr, size_t addrlen); +static int KcSocket_get_local_port(KcSocket *sock); +static socket_t KcSocket_get_socket(KcSocket *sock); +static int KcSocket_get_type(KcSocket *sock); +static int KcSocket_get_family(KcSocket *sock); +static bool KcSocket_get_addrinfo( + KcSocket *sock, struct addrinfo **result, + const char *addr, const char *service, bool is_passive); +static bool KcSocket_bind(KcSocket *sock, const char *addr, const char *service); +static bool KcSocket_listen(KcSocket *sock, int backlog); +static KcSocket *KcSocket_accept(KcSocket *sock); +static bool KcSocket_connect( + KcSocket *sock, const char *addr, const char *service, + const char *local_addr, const char *local_service); +static bool KcSocket_close(KcSocket *sock); +static ssize_t KcSocket_send(KcSocket *sock, const char *buff, size_t size, int flags); +static ssize_t KcSocket_recv(KcSocket *sock, char *buff, size_t size, int flags); +static ssize_t KcSocket_sendto( + KcSocket *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service); +static ssize_t KcSocket_recvfrom( + KcSocket *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen); +static bool KcSocket_set_ttl(KcSocket *sock, int val); +static bool KcSocket_join(KcSocket *sock, const char *addr, const char *ifname, unsigned int ifindex); +static bool KcSocket_leave(KcSocket *sock, const char *addr); + +static bool KcSocket_addrinfo_bind_and_connect( + KcSocket *sock, struct addrinfo *bind_addrinfo, struct addrinfo *conn_addrinfo); +static bool KcSocket_addrinfo_connect(KcSocket *sock, struct addrinfo *conn_addrinfo, socket_t sockfd); + +/** + * ソケットのセットアップをします。 + */ +static void Socket_setup(void) +{ + static bool is_init = false; + if (!is_init) + { +#if (KC_IS_WINDOWS) + WSADATA wsa_data; + WSAStartup(MAKEWORD(2, 0), &wsa_data); +#endif + atexit(Socket_cleanup); + is_init = true; + } +} + +/** + * ソケットライブラリのクリーンアップをします。 + */ +static void Socket_cleanup(void) +{ +#if (KC_IS_WINDOWS) + WSACleanup(); +#endif +} + +// ============================================================================= +// new +// ============================================================================= +/** + * 指定されたタイプ、ファミリのSocket オブジェクトを構築します。 + * + * @param type タイプ(SOCK_STREAM/SOCK_DGRAM/SOCK_RAW) + * @param family ファミリ(AF_UNSPEC/AF_INET/AF_INET6) + */ +KcSocket *KcSocket_new(int type, int family) +{ + // KcSocket の管理構造 + // +----------------+ + // | KcSocket | + // | ... | + // | _info -----------+ + // +----------------+ | + // | |<--+ + // +----------------+ + Socket_setup(); + KcSocket *sock = (KcSocket *)malloc(sizeof(KcSocket) + sizeof(KcSocketInfo)); + if (sock != NULL) + { + sock->get_remote_addr = KcSocket_get_remote_addr; + sock->get_remote_port = KcSocket_get_remote_port; + sock->get_local_addr = KcSocket_get_local_addr; + sock->get_local_port = KcSocket_get_local_port; + sock->get_socket = KcSocket_get_socket; + sock->get_type = KcSocket_get_type; + sock->get_family = KcSocket_get_family; + sock->get_addrinfo = KcSocket_get_addrinfo; // for local + sock->bind = KcSocket_bind; + sock->listen = KcSocket_listen; + sock->accept = KcSocket_accept; + sock->connect = KcSocket_connect; + sock->close = KcSocket_close; + sock->send = KcSocket_send; + sock->recv = KcSocket_recv; + sock->sendto = KcSocket_sendto; + sock->recvfrom = KcSocket_recvfrom; + sock->set_ttl = KcSocket_set_ttl; + sock->join = KcSocket_join; + sock->leave = KcSocket_leave; + sock->_info = (sock + 1); + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + info->sock_fd = INVALID_SOCKET; + info->sock_type = type; + info->sock_family = family; + info->remote_addrlen = 0; + info->local_addrlen = 0; + } + return sock; +} + +// ============================================================================= +// delete +// ============================================================================= +/** + * Socket を破棄します。 + */ +void KcSocket_delete(KcSocket *socket) +{ + if (socket) + { + if (((KcSocketInfo *)socket->_info)->sock_fd != INVALID_SOCKET) + { + socket->close(socket); + } + free(socket); + } +} + +// ============================================================================= +// get_remote_addr +// ============================================================================= +/** + * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr 接続先アドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ +static const char *KcSocket_get_remote_addr(KcSocket *sock, char *addr, size_t addrlen) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->remote_addrlen) + { + int ret = getnameinfo( + (const struct sockaddr *)&info->remote_addr, + info->remote_addrlen, + addr, addrlen, NULL, 0, NI_NUMERICHOST); + if (ret == 0) + { + return addr; + } + } + return NULL; +} + +/** + * ソケットの接続先ポート番号を返します。 + * 接続先ポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ +static int KcSocket_get_remote_port(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->remote_addrlen) + { + char tmp_port[NI_MAXSERV]; + int ret = getnameinfo( + (const struct sockaddr *)&info->remote_addr, + info->remote_addrlen, + NULL, 0, tmp_port, sizeof(tmp_port), NI_NUMERICSERV); + if (ret == 0) + { + return atoi(tmp_port); + } + } + return -1; +} + +/** + * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr ローカルアドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ +static const char *KcSocket_get_local_addr(KcSocket *sock, char *addr, size_t addrlen) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->local_addrlen) + { + int ret = getnameinfo( + (const struct sockaddr *)&info->local_addr, + info->remote_addrlen, + addr, addrlen, NULL, 0, NI_NUMERICHOST); + if (ret == 0) + { + return addr; + } + } + return NULL; +} + +/** + * ソケットのローカルポート番号を返します。 + * ローカルポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ +static int KcSocket_get_local_port(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->local_addrlen) + { + char tmp_port[NI_MAXSERV]; + int ret = getnameinfo( + (const struct sockaddr *)&info->local_addr, + info->remote_addrlen, + NULL, 0, tmp_port, sizeof(tmp_port), NI_NUMERICSERV); + if (ret == 0) + { + return atoi(tmp_port); + } + } + return -1; +} + +/** + * ソケットディスクリプタを返します。 + * 通常は、ソケットディスクリプタを直接操作しないでください。 + * + * @param sock 対象ソケット + * @return ソケットディスクリプタ + */ +static socket_t KcSocket_get_socket(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + return info->sock_fd; +} + +/** + * ソケットタイプを返します。 + * + * @param sock 対象ソケット + * @return ソケットタイプ + */ +static int KcSocket_get_type(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + return info->sock_type; +} + +/** + * ソケットファミリを返します。 + * ソケット接続、バインド時には、実際にバインド、 + * あるいは接続されているソケットファミリを返します。 + * + * @param sock 対象ソケット + * @return ソケットファミリ + */ +static int KcSocket_get_family(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int result_family = info->sock_family; + if (info->sock_fd != INVALID_SOCKET) + { + struct sockaddr_storage ss; + socklen_t len = sizeof(struct sockaddr_storage); + if (getsockname(info->sock_fd, (struct sockaddr *)&ss, &len) == 0) + { + result_family = ss.ss_family; + } + } + return result_family; +} + +/** + * 指定されたアドレス、サービスのアドレス情報を取得します。 + * + * @param sock 対象ソケット + * @param result アドレス情報格納用ポインタ + * @param addr アドレス + * @param service サービス + * @param is_passive サーバソケットの場合、true を指定ください。 + */ +static bool KcSocket_get_addrinfo( + KcSocket *sock, struct addrinfo **result, + const char *addr, const char *service, bool is_passive) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + + struct addrinfo hints; + memset(&hints, 0x00, sizeof(struct addrinfo)); + hints.ai_socktype = info->sock_type; + hints.ai_family = info->sock_family; + if (is_passive) + { + hints.ai_flags = AI_PASSIVE; + } + int ret = getaddrinfo(addr, service, &hints, result); + return (ret == 0); +} + +/** + * 指定されたアドレス、サービスにバインドします。 + * 指定されたアドレスとサービスにより、複数候補がある場合は、 + * 最初に見つかったアドレスとポートにバインドします。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス (例: "80", "http", "ssh" など) + * @return true/false (bind 成功/失敗) + */ +static bool KcSocket_bind(KcSocket *sock, const char *addr, const char *service) +{ + struct addrinfo *bind_addr; + bool is_success = sock->get_addrinfo(sock, &bind_addr, addr, service, true); + if (is_success) + { + is_success = KcSocket_addrinfo_bind_and_connect(sock, bind_addr, NULL); + freeaddrinfo(bind_addr); + } + return is_success; +} + +/** + * ソケットを接続待ちソケットとしてマークをつけます。 + * 保留中の接続のキュー最大長を指定します。 + * + * @param sock 対象ソケット + * @param backlog バックログ + * @return true/false (成功/失敗) + */ +static bool KcSocket_listen(KcSocket *sock, int backlog) +{ + bool result = false; + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->sock_fd != INVALID_SOCKET) + { + int ret = listen(info->sock_fd, backlog); + result = (ret != SOCKET_ERROR); + } + return result; +} + +/** + * ソケットへの接続を受け付けます。 + * + * @param sock 対象ソケット + * @return 受け付けたソケット(失敗時NULL) + */ +static KcSocket *KcSocket_accept(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + KcSocket *client = NULL; + if (info->sock_fd != INVALID_SOCKET) + { + client = KcSocket_new(info->sock_type, info->sock_family); + if (client != NULL) + { + KcSocketInfo *cinfo = (KcSocketInfo *)client->_info; + cinfo->remote_addrlen = sizeof(struct sockaddr_storage); + cinfo->sock_fd = accept(info->sock_fd, (struct sockaddr *)&cinfo->remote_addr, &cinfo->remote_addrlen); + if (cinfo->sock_fd != INVALID_SOCKET) + { + memcpy(&cinfo->local_addr, &info->local_addr, info->local_addrlen); + cinfo->local_addrlen = info->local_addrlen; + cinfo->sock_type = SOCK_STREAM; + cinfo->sock_family = cinfo->remote_addr.ss_family; + } + else + { + KcSocket_delete(client); + client = NULL; + } + } + } + return client; +} + +/** + * 指定されたアドレス、サービス接続します。 + * local_addr に NULL を指定した場合、自動的にローカルのアドレス、サービスが設定されます。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス + * @param local_addr ローカルアドレス + * @param local_service ローカルサービス + * @return true/false (成功/失敗) + */ +static bool KcSocket_connect( + KcSocket *sock, const char *addr, const char *service, + const char *local_addr, const char *local_service) +{ + // 接続先アドレス情報取得 + struct addrinfo *conn_addr = NULL; + if (!sock->get_addrinfo(sock, &conn_addr, addr, service, false)) + { + return false; + } + + // バインドアドレス情報取得 + bool is_success = false; + struct addrinfo *bind_addr = NULL; + if (local_addr != NULL) + { // bind が必要 + is_success = sock->get_addrinfo(sock, &bind_addr, local_addr, local_service, true); + if (is_success) + { + is_success = KcSocket_addrinfo_bind_and_connect(sock, bind_addr, conn_addr); + freeaddrinfo(bind_addr); + } + } + else + { + is_success = KcSocket_addrinfo_connect(sock, conn_addr, INVALID_SOCKET); + } + freeaddrinfo(conn_addr); + return is_success; +} + +/** + * ソケットをクローズします。 + * + * @param sock 対象ソケット + * @return true/false (成功/失敗) + */ +static bool KcSocket_close(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + + if (info->sock_family == SOCK_STREAM) + { // TCP の場合は、出力を閉じて、届いているデータを読み取ってから close する。 + int ret = shutdown(info->sock_fd, SHUT_WR); + if (ret == 0) + { + int read_size; + char buff[1024]; + do + { // 届いているデータを全て読み取る + read_size = recv(info->sock_fd, buff, sizeof(buff), 0); + } while (read_size > 0); + } + } + sockclose(info->sock_fd); + return true; +} + +/** + * メッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 送信されたデータサイズ + */ +static ssize_t KcSocket_send(KcSocket *sock, const char *buff, size_t size, int flags) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + ssize_t write_size = 0; + do + { + errno = 0; + write_size = send(info->sock_fd, buff, size, flags); + } while ((write_size < 0) && (errno == EINTR)); + return write_size; +} + +/** + * メッセージを受信します。 + * 受信したメッセージは指定されたバッファに格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 読み取ったデータサイズ + */ +static ssize_t KcSocket_recv(KcSocket *sock, char *buff, size_t size, int flags) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + ssize_t read_size; + do + { + errno = 0; + read_size = recv(info->sock_fd, buff, size, flags); + } while ((read_size < 0) && (errno == EINTR)); + return read_size; +} + +/** + * 指定されたアドレス、サービスにメッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param addr アドレス + * @param service サービス + * @return 送信メッセージサイズ、エラー発生時は -1 + */ +static ssize_t KcSocket_sendto( + KcSocket *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service) +{ + (void)sock; + (void)buff; + (void)size; + (void)flags; + (void)addr; + (void)service; + return 0; +} + +/** + * メッセージを受信します。 + * src_addr, src_service がいずれも NULL 出ない場合、 + * 送信元のアドレスとサービスが格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param src_addr 送信元アドレス格納用バッファ + * @param src_addrlen 送信元アドレス格納用バッファサイズ + * @param src_service 送信元サービス格納用バッファ + * @param src_servicelen 送信元サービス格納用バッファサイズ + * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + */ +static ssize_t KcSocket_recvfrom( + KcSocket *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen) +{ + (void)sock; + (void)buff; + (void)size; + (void)flags; + (void)src_addr; + (void)src_addrlen; + (void)src_service; + (void)src_servicelen; + return 0; +} + +/** + * TTL を設定します。 + * + * @param sock 対象ソケット + * @param val 設定する TTL + * @return true/false (成功/失敗) + */ +static bool KcSocket_set_ttl(KcSocket *sock, int val) +{ + (void)sock; + (void)val; + return true; +} + +/** + * マルチキャストグループに参加するために JOIN します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @param ifindex インタフェースインデックス + * @return true/false (成功/失敗) + */ +static bool KcSocket_join( + KcSocket *sock, const char *addr, const char *ifname, unsigned int ifindex) +{ + (void)sock; + (void)addr; + (void)ifname; + (void)ifindex; + return true; +} + +/** + * マルチキャストグループから離脱します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @return true/false (成功/失敗) + */ +static bool KcSocket_leave( + KcSocket *sock, const char *addr) +{ + (void)sock; + (void)addr; + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// [内部関数] +// + +/** + * 指定された接続アドレス情報をもとにソケットの生成、接続を試みます。 + * 接続済みソケット sockfd が有効な場合、sockfd を用いて接続します。 + * + * @param sock 対象ソケット + * @param conn_addrinfo 接続アドレス情報 + * @param sockfd 接続済みソケット + * @return true/false (接続成功/失敗) + */ +static bool KcSocket_addrinfo_connect(KcSocket *sock, struct addrinfo *conn_addrinfo, socket_t sockfd) +{ + bool is_success = false; + socket_t tmp_sock = sockfd; + for (struct addrinfo *rp = conn_addrinfo; rp != NULL; rp = rp->ai_next) + { + if (sockfd == INVALID_SOCKET) + { // sockfd が無効の場合、ソケットを生成する。 + tmp_sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (tmp_sock == INVALID_SOCKET) + { + continue; + } + } + + int ret = connect(tmp_sock, rp->ai_addr, rp->ai_addrlen); + is_success = (ret != SOCKET_ERROR); + if (is_success) + { + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + info->sock_fd = tmp_sock; + info->remote_addrlen = rp->ai_addrlen; + memcpy(&info->remote_addr, rp->ai_addr, rp->ai_addrlen); + break; + } + + if (sockfd != INVALID_SOCKET) + { // sockfd が無効の場合、一時的に生成したソケットをクローズする。 + sockclose(tmp_sock); + } + } + return is_success; +} + +/** + * 指定された接続アドレス情報をもとにソケットの生成、バインドを試みます。 + * conn_addrinfo が NULL でない場合、バインド後に接続を試みます。 + * + * @param sock 対象ソケット + * @param bind_addrinfo バインドアドレス情報 + * @param conn_addrinfo 接続アドレス情報 + * @return true/false (接続成功/失敗) + */ +static bool KcSocket_addrinfo_bind_and_connect( + KcSocket *sock, struct addrinfo *bind_addrinfo, struct addrinfo *conn_addrinfo) +{ + int ret; + bool is_success = false; + + for (struct addrinfo *rp = bind_addrinfo; rp != NULL; rp = rp->ai_next) + { // sokcet : ソケット生成 + socket_t tmp_sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (tmp_sock == INVALID_SOCKET) + { + continue; + } + + // bind + const int on = 1; + setsockopt(tmp_sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on)); + ret = bind(tmp_sock, rp->ai_addr, rp->ai_addrlen); + is_success = (ret != SOCKET_ERROR); + if (!is_success) + { // bind 失敗したので次へ + sockclose(tmp_sock); + continue; + } + + // connect + if (conn_addrinfo) + { + is_success = KcSocket_addrinfo_connect(sock, conn_addrinfo, tmp_sock); + if (!is_success) + { // connect 失敗したので次へ + sockclose(tmp_sock); + continue; + } + } + + // bind または、bind と connect 成功 + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + info->sock_fd = tmp_sock; + info->local_addrlen = rp->ai_addrlen; + memcpy(&info->local_addr, rp->ai_addr, rp->ai_addrlen); + break; + } + return is_success; +} diff --git a/modules/src/kc_threads.c b/modules/src/kc_threads.c new file mode 100644 index 0000000..dd600bb --- /dev/null +++ b/modules/src/kc_threads.c @@ -0,0 +1,194 @@ +/** + * @file kc_thread.c + * @brief スレッドモジュール + * @copyright 2020 - 2024 Nomura Kei + */ +#include +#include +#include + +#include +#include +#include + +/** + * KcThread 管理情報 + */ +typedef struct +{ + thrd_t tid; + void *args; + int (*run)(void *args); + atomic_bool alive_state; +} KcThreadInfo; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static bool KcThread_is_alive(KcThread *thread); +static bool KcThread_start(KcThread *thread, void *args); +static bool KcThread_join(KcThread *thread); +static int KcThread_run(void *thread); + +// ============================================================================= +// new +// ============================================================================= +/** + * スレッドを生成します。 + * + * @param run スレッドにて実行する関数へのポインタ + */ +KcThread *KcThread_new(int (*run)(void *args)) +{ + // KcThread の管理構造 + // +--------------+ + // | KcThread | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // +--------------+ + KcThread *thread = (KcThread *)malloc(sizeof(KcThread) + sizeof(KcThreadInfo)); + if (thread != NULL) + { + thread->is_alive = KcThread_is_alive; + thread->start = KcThread_start; + thread->join = KcThread_join; + thread->_info = (thread + 1); + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + // info->tid + info->run = run; + atomic_init(&info->alive_state, false); + } + return thread; +} + +// ============================================================================= +// delete +// ============================================================================= +/** + * Thread を破棄します。 + * + * @param thread 破棄するスレッド + */ +void KcThread_delete(KcThread *thread) +{ + free(thread); +} + +// ============================================================================= +// sleep +// ============================================================================= +/** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープさせるミリ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ +bool KcThread_msleep(long time, bool force) +{ + long sec = time / 1000; + long msec = time - (sec * 1000); + long nsec = msec * 1000 * 1000; + return KcThread_sleep(sec, nsec, force); +} + +/** + * 指定された sec 秒 + nsec ナノ秒間現在のスレッドをスリープさせます。 + * + * @param sec スリープさせる秒数 + * @param nsec スリープさせるナノ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ +bool KcThread_sleep(long sec, long nsec, bool force) +{ + struct timespec remaining = {sec, nsec}; + int ret; + do + { + errno = 0; + ret = thrd_sleep(&remaining, &remaining); + } while ((ret == -1) && force && (errno == EINTR)); + return (ret == 0); +} + +// ============================================================================= +// is_alive +// ============================================================================= +/** + * 指定されたスレッドが生きているか否かを返します。 + * + * @param thread 対象スレッド + * @return true/false (スレッドが生存している/していない) + */ +static bool KcThread_is_alive(KcThread *thread) +{ + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + return atomic_load(&info->alive_state); +} + +// ============================================================================= +// start +// ============================================================================= +/** + * スレッドを開始します。 + * + * @param thread 対象スレッド + * @param args スレッドに渡す情報 + * @return true/false (スレッド起動成功/失敗) + */ +static bool KcThread_start(KcThread *thread, void *args) +{ + int ret = thrd_error; + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + if (!atomic_load(&info->alive_state)) + { + info->args = args; + ret = thrd_create(&info->tid, KcThread_run, thread); + } + return (ret == thrd_success); +} + +// ============================================================================= +// join +// ============================================================================= +/** + * スレッドが終了するのを待機します。 + * + * @param thread 対象スレッド + * @return true/false (成功/失敗) + */ +static bool KcThread_join(KcThread *thread) +{ + int ret = thrd_error; + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + ret = thrd_join(info->tid, NULL); + return (ret == thrd_success); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// 内部関数 +// + +// ============================================================================= +// run +// ============================================================================= +/** + * スレッドで実行される関数。 + * start にて関数を別スレッドにて実行します。 + * + * @param args スレッド情報 + * @return + */ +int KcThread_run(void *args) +{ + KcThread *thread = (KcThread *)args; + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + atomic_store(&info->alive_state, true); + int ret = info->run(info->args); + atomic_store(&info->alive_state, false); + return ret; +} diff --git a/modules/src/kc_threads_win.c b/modules/src/kc_threads_win.c new file mode 100644 index 0000000..0439fda --- /dev/null +++ b/modules/src/kc_threads_win.c @@ -0,0 +1,201 @@ +/** + * @file kc_thread.c + * @brief スレッドモジュール + * @copyright 2020 - 2024 Nomura Kei + */ +#include +#include + +#if (KC_IS_WINDOWS) + +int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) +{ + if ((thr == NULL) || (func == NULL)) + { + return thrd_error; + } + + thr->handle = CreateThread( + NULL, // セキュリティ属性 + 0, // スタックサイズ + (LPTHREAD_START_ROUTINE)func, // スレッド関数 + arg, // 引数 + 0, // 作成フラグ + &(thr->thread_id) // スレッドID + ); + + if (thr->handle == NULL) + { + return thrd_error; + } + return thrd_success; +} +int thrd_join(thrd_t thr, int *res) +{ + if (WaitForSingleObject(thr.handle, INFINITE) != WAIT_OBJECT_0) + { + return thrd_error; + } + if (res != NULL) + { + DWORD retcode; + if (GetExitCodeThread(thr.handle, &retcode) == 0) + { + return thrd_error; + } + *res = (int)retcode; + } + CloseHandle(thr.handle); + return thrd_success; +} + +int thrd_detach(thrd_t thr) +{ + if (thr.handle == NULL) + { + return thrd_error; + } + if (CloseHandle(thr.handle) == 0) + { + return thrd_error; + } + return thrd_success; +} + +thrd_t thrd_current(void) +{ + thrd_t current; + current.handle = GetCurrentThread(); + current.thread_id = GetThreadId(current.handle); + return current; +} + +int thrd_equal(thrd_t lhs, thrd_t rhs) +{ + return (lhs.thread_id == rhs.thread_id); +} + +void thrd_yield(void) +{ + SwitchToThread(); +} + +int thrd_sleep(const struct timespec *duration, struct timespec *remaining) +{ + if (duration == NULL) + { + return thrd_error; + } + + // Windows の Sleep は、ms 単位 + DWORD msec = (DWORD)(duration->tv_sec * 1000 + duration->tv_nsec / 1000000); + Sleep(msec); + + // 常に成功したものとして、remaining は 0 に設定する。 + if (remaining != NULL) + { + remaining->tv_sec = 0; + remaining->tv_nsec = 0; + } + return thrd_success; +} + +int mtx_init(mtx_t *mtx, int type) +{ + if (mtx == NULL) + { + return thrd_error; + } + mtx->type = type; + if (type & mtx_plain) + { + InitializeCriticalSection(&mtx->cs); + } + else if (type & mtx_recursive) + { + InitializeCriticalSection(&mtx->cs); + } + else + { + return thrd_error; + } + return thrd_success; +} + +void mtx_destroy(mtx_t *mtx) +{ + DeleteCriticalSection(&mtx->cs); +} +int mtx_lock(mtx_t *mtx) +{ + EnterCriticalSection(&mtx->cs); + return thrd_success; +} +int mtx_unlock(mtx_t *mtx) +{ + LeaveCriticalSection(&mtx->cs); + return thrd_success; +} + +int cnd_init(cnd_t *cond) +{ + if (cond == NULL) + { + return thrd_error; + } + InitializeConditionVariable(&cond->cond); + return thrd_success; +} + +int cnd_signal(cnd_t *cond) +{ + if (cond == NULL) + { + return thrd_error; + } + WakeConditionVariable(&cond->cond); + return thrd_success; +} +int cnd_broadcast(cnd_t *cond) +{ + if (cond == NULL) + { + return thrd_error; + } + WakeAllConditionVariable(&cond->cond); + return thrd_success; +} +int cnd_wait(cnd_t *cond, mtx_t *mtx) +{ + if ((cond == NULL) || (mtx == NULL)) + { + return thrd_error; + } + if (SleepConditionVariableCS(&cond->cond, &mtx->cs, INFINITE)) + { + return thrd_success; + } + return thrd_error; +} + +int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts) +{ + if ((cond == NULL) || (mtx == NULL) || (ts == NULL)) + { + return thrd_error; + } + DWORD msec = (DWORD)(ts->tv_sec * 1000 + ts->tv_nsec / 1000000); + if (SleepConditionVariableCS(&cond->cond, &mtx->cs, msec)) + { + return thrd_timedout; + } + return thrd_error; +} + +void cnd_destroy(cnd_t *cond) +{ + // Nothing to do + UNUSED_VARIABLE(cond); +} + +#endif \ No newline at end of file diff --git a/modules/test/src/test_lock_guard.c b/modules/test/src/test_lock_guard.c index f00ed0b..9c3249f 100644 --- a/modules/test/src/test_lock_guard.c +++ b/modules/test/src/test_lock_guard.c @@ -37,7 +37,7 @@ { mtx_t mutex; mtx_init(&mutex, mtx_plain | mtx_recursive); - KcLockGuard lock_guard = kc_lock_guard_init(&mutex); + KcLockGuard lock_guard = kc_lock_guard_init(&mutex, __FILE__, __func__, __LINE__); assert_not_null(lock_guard.mutex); // Lock Guard 解放 @@ -53,7 +53,7 @@ */ static void test_lock_guard_init_null(void) { - KcLockGuard lock_guard = kc_lock_guard_init(NULL); + KcLockGuard lock_guard = kc_lock_guard_init(NULL, __FILE__, __func__, __LINE__); assert_equals(EINVAL, errno); assert_null(lock_guard.mutex); } @@ -69,7 +69,7 @@ mtx_t mutex; mtx_init(&mutex, mtx_plain | mtx_recursive); mtx_destroy(&mutex); - KcLockGuard lock_guard = kc_lock_guard_init(&mutex); + KcLockGuard lock_guard = kc_lock_guard_init(&mutex, __FILE__, __func__, __LINE__); assert_null(lock_guard.mutex); } @@ -83,7 +83,7 @@ { mtx_t mutex; mtx_init(&mutex, mtx_plain | mtx_recursive); - KcLockGuard lock_guard = kc_lock_guard_init(&mutex); + KcLockGuard lock_guard = kc_lock_guard_init(&mutex, __FILE__, __func__, __LINE__); assert_not_null(lock_guard.mutex); // Lock Guard 解放 diff --git a/modules/test/src/test_queue.c b/modules/test/src/test_queue.c index cbec7e0..1595972 100644 --- a/modules/test/src/test_queue.c +++ b/modules/test/src/test_queue.c @@ -1,12 +1,12 @@ #include #include -#include #include #include #include #include #include +#include #include "ut.h" @@ -58,14 +58,11 @@ static int test_queue_offer_thread(void *arg) { KcQueue *queue = (KcQueue *)arg; - struct timespec spec; - spec.tv_sec = 0; - spec.tv_nsec = 1; for (int i = 0; i < 100; i++) { printf("offer: %d\n", i); queue->offer(queue, &i, sizeof(int)); - thrd_sleep(&spec, NULL); + KcThread_sleep(0, 1, false); } return 0; } @@ -73,9 +70,6 @@ // for poll thread static int test_queue_poll_thread(void *arg) { - struct timespec spec; - spec.tv_sec = 0; - spec.tv_nsec = 1; KcQueue *queue = (KcQueue *)arg; for (int i = 0; i < 100; i++) { @@ -100,7 +94,7 @@ { printf("poll: (null)\n"); } - thrd_sleep(&spec, NULL); + KcThread_sleep(0, 1, false); } return 0; } @@ -112,27 +106,28 @@ { KcQueue *queue = KcQueue_new(10); - thrd_t offer_thread, poll_thread; + KcThread *offer_thread = KcThread_new(test_queue_offer_thread); + KcThread *poll_thread = KcThread_new(test_queue_poll_thread); - thrd_create(&offer_thread, test_queue_offer_thread, queue); - thrd_create(&poll_thread, test_queue_poll_thread, queue); + offer_thread->start(offer_thread, queue); + poll_thread->start(poll_thread, queue); - thrd_join(offer_thread, NULL); - thrd_join(poll_thread, NULL); + offer_thread->join(offer_thread); + poll_thread->join(poll_thread); + KcQueue_delete(queue); + KcThread_delete(offer_thread); + KcThread_delete(poll_thread); } // for put thread static int test_queue_put_thread(void *arg) { KcQueue *queue = (KcQueue *)arg; - struct timespec spec; - spec.tv_sec = 0; - spec.tv_nsec = 1 * 1000; for (int i = 0; i < 100; i++) { queue->put(queue, &i, sizeof(int)); - thrd_sleep(&spec, NULL); + KcThread_sleep(0, 1000, false); } return 0; } @@ -140,9 +135,6 @@ // for take thread static int test_queue_take_thread(void *arg) { - struct timespec spec; - spec.tv_sec = 0; - spec.tv_nsec = 1; KcQueue *queue = (KcQueue *)arg; for (int i = 0; i < 100; i++) { @@ -150,7 +142,7 @@ size_t size = sizeof(int); queue->take(queue, &element, &size); assert_equals(i, element); - thrd_sleep(&spec, NULL); + KcThread_sleep(0, 1, false); } return 0; } @@ -158,9 +150,6 @@ // for take thread static int test_queue_take2_thread(void *arg) { - struct timespec spec; - spec.tv_sec = 0; - spec.tv_nsec = 100 * 1000; KcQueue *queue = (KcQueue *)arg; for (int i = 0; i < 100; i++) { @@ -168,7 +157,7 @@ size_t size = sizeof(int); queue->take(queue, &element, &size); assert_equals(i, element); - thrd_sleep(&spec, NULL); + KcThread_sleep(0, 100 * 1000, false); } return 0; } @@ -180,22 +169,31 @@ { { KcQueue *queue = KcQueue_new(0); - thrd_t put_thread, take_thread; - thrd_create(&put_thread, test_queue_put_thread, queue); - thrd_create(&take_thread, test_queue_take_thread, queue); - thrd_join(put_thread, NULL); - thrd_join(take_thread, NULL); + KcThread *put_thread = KcThread_new(test_queue_put_thread); + KcThread *take_thread = KcThread_new(test_queue_take_thread); + + put_thread->start(put_thread, queue); + take_thread->start(take_thread, queue); + + put_thread->join(put_thread); + take_thread->join(take_thread); + KcThread_delete(put_thread); + KcThread_delete(take_thread); KcQueue_delete(queue); } // PUT ブロックパターン { KcQueue *queue = KcQueue_new(5); - thrd_t put_thread, take_thread; - thrd_create(&put_thread, test_queue_put_thread, queue); - thrd_create(&take_thread, test_queue_take2_thread, queue); - thrd_join(put_thread, NULL); - thrd_join(take_thread, NULL); + KcThread *put_thread = KcThread_new(test_queue_put_thread); + KcThread *take_thread = KcThread_new(test_queue_take2_thread); + put_thread->start(put_thread, queue); + take_thread->start(take_thread, queue); + + put_thread->join(put_thread); + take_thread->join(take_thread); + KcThread_delete(put_thread); + KcThread_delete(take_thread); KcQueue_delete(queue); } } diff --git a/modules/test/src/test_socket.c b/modules/test/src/test_socket.c new file mode 100644 index 0000000..356882e --- /dev/null +++ b/modules/test/src/test_socket.c @@ -0,0 +1,52 @@ +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "ut.h" + +// プロトタイプ宣言 +static void test_socket_new(void); + +/** + * KcSocket 単体テストスイート + */ +void suite_socket(void) +{ + KcUt *ut = KcUt_get_instance(); + ut->add(ut, UT_TESTCASE, "socket new/delete", test_socket_new); +} + +/** + * Socket 生成/破棄。 + * + * @process KcSocket_new を実行する。。 + * @result KcSocket が生成されること。 + * + * @process KcSocket_delete にて Queue を破棄する。 + * @result Socket が破棄されること。 + */ +static void test_socket_new(void) +{ + KcSocket *server = KcSocket_new(SOCK_STREAM, AF_UNSPEC); + assert_not_null(server); + + KcSocket *client = KcSocket_new(SOCK_STREAM, AF_UNSPEC); + assert_not_null(server); + + bool ret = server->bind(server, "127.0.0.1", "5000"); + assert_true(ret); + ret = server->listen(server, 1); + assert_true(ret); + + ret = client->connect(client, "127.0.0.1", "5000", NULL, NULL); + assert_true(ret); + + KcSocket_delete(server); + KcSocket_delete(client); +} diff --git a/config.mk b/config.mk index a0ac27c..f7c6e47 100644 --- a/config.mk +++ b/config.mk @@ -25,14 +25,17 @@ # for Windows CROSS_COMPILE ?=i686-w64-mingw32- LIBS ?= -lws2_32 +DEBUG_OPTIONS ?= else # For Linux ifeq ($(strip $(ARCH)),aarch64) CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector else CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector endif endif diff --git a/mk/base-cmd.mk b/mk/base-cmd.mk index f429bcf..e74c114 100644 --- a/mk/base-cmd.mk +++ b/mk/base-cmd.mk @@ -33,8 +33,13 @@ CC = $(CROSS_COMPILE)gcc CXX = $(CROSS_COMPILE)g++ + #CC = $(CROSS_COMPILE)clang #CXX = $(CROSS_COMPILE)clang++ + +#CC = $(CROSS_COMPILE)clang-16 +#CXX = $(CROSS_COMPILE)clang++16 + RANLIB = $(CROSS_COMPILE)ranlib ADDR2LINE = $(CROSS_COMPILE)addr2line AR = $(CROSS_COMPILE)ar diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 2cc417c..eebded1 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -69,7 +69,7 @@ # 値が2の場合、さらなるチェックを追加するが、規格準拠のプログラムが失敗する可能性がある。 # いくつかのチェックは、コンパイル時に実行され、コンパイラの警告として表示される。 # ------------------------------------------------------------------------------ -DEBUG_OPTIONS += -fstack-protector +# DEBUG_OPTIONS += -fstack-protector DEBUG_OPTIONS += --coverage DEBUG_OPTIONS += -g3 -ggdb DEBUG_OPTIONS += -O0 diff --git a/mk/link-a-conf.mk b/mk/link-a-conf.mk index 0e872f3..631e6d5 100644 --- a/mk/link-a-conf.mk +++ b/mk/link-a-conf.mk @@ -10,8 +10,11 @@ HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) +HEADER_FILES_WIN = $(wildcard include/win/*.h) $(wildcard include/win/*.hpp) +TOP_HEADER_FILES_WIN = $(addprefix $(TOPDIR)/include/win/,$(notdir $(HEADER_FILES_WIN))) + CLEAN_FILES += $(TOP_TARGET) -CLEAN_FILES += $(TOP_HEADER_FILES) +CLEAN_FILES += $(TOP_HEADER_FILES) $(TOP_HEADER_FILES_WIN) endif endif diff --git a/mk/link-a-rule.mk b/mk/link-a-rule.mk index d94a54a..657d858 100644 --- a/mk/link-a-rule.mk +++ b/mk/link-a-rule.mk @@ -10,6 +10,7 @@ $(TOP_TARGET): $(TARGET) ifneq ($(strip $(HEADER_FILES)),) $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ + $(CP) -f $(HEADER_FILES_WIN) $(TOPDIR)/include/win/ endif $(CP) -f $(TARGET) $(TOPDIR)/lib/ diff --git a/modules/include/kc_lock_guard.h b/modules/include/kc_lock_guard.h index 8bb70bd..ad3915f 100644 --- a/modules/include/kc_lock_guard.h +++ b/modules/include/kc_lock_guard.h @@ -7,18 +7,16 @@ #define KC_LOCK_GUARD_H #include -#include - +#include /** * LockGuard 管理構造体 */ typedef struct { - mtx_t* mutex; + mtx_t *mutex; } KcLockGuard; - /** * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 * 使用例) @@ -34,8 +32,7 @@ * } */ #define kc_lock_guard(lock) \ - for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) - + for (KcLockGuard _guard = kc_lock_guard_init(lock, __FILE__, __func__, __LINE__); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 @@ -44,15 +41,13 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex); - +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line); /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard); +void kc_lock_guard_release(KcLockGuard *guard); - -#endif // KC_LOCK_GUARD_H +#endif // KC_LOCK_GUARD_H diff --git a/modules/include/kc_memory.h b/modules/include/kc_memory.h index ffce4ed..c6ba9e9 100644 --- a/modules/include/kc_memory.h +++ b/modules/include/kc_memory.h @@ -22,12 +22,13 @@ #ifndef KC_MEMORY_H #define KC_MEMORY_H -#include +#include #include #include #include #include +#include #ifdef __cplusplus extern "C" @@ -273,8 +274,6 @@ #define calloc(nmemb, size) kc_memory_manager->calloc(nmemb, size, __FILE__, __func__, __LINE__) #define realloc(ptr, size) kc_memory_manager->realloc(ptr, size, __FILE__, __func__, __LINE__) #define free(ptr) kc_memory_manager->free(ptr) -#else -#include #endif #ifdef __cplusplus diff --git a/modules/include/kc_socket.h b/modules/include/kc_socket.h new file mode 100644 index 0000000..7d952c7 --- /dev/null +++ b/modules/include/kc_socket.h @@ -0,0 +1,291 @@ +/** + * @file kc_socket.h + * @brief ソケットモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_SOCKET_H +#define KC_SOCKET_H + +#include + +#if (KC_IS_WINDOWS) +typedef SOCKET socket_t; +#define SHUT_RD SD_RECEIVE +#define SHUT_WR SD_SEND +#define SHUT_RDWR SD_BOTH +#define sockclose(sockfd) closesocket(sockfd) +#else +#include +#include +#include +#include +#include +#include +#include +typedef int socket_t; +#define INVALID_SOCKET (-1) +#define SOCKET_ERROR (-1) +#define sockclose(sockfd) close(sockfd) +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcSocket_ + { + + /** + * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr 接続先アドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_remote_addr)(struct KcSocket_ *sock, char *addr, size_t addrlen); + + /** + * ソケットの接続先ポート番号を返します。 + * 接続先ポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_remote_port)(struct KcSocket_ *sock); + + /** + * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr ローカルアドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_local_addr)(struct KcSocket_ *sock, char *addr, size_t addlen); + + /** + * ソケットのローカルポート番号を返します。 + * ローカルポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_local_port)(struct KcSocket_ *sock); + + /** + * ソケットディスクリプタを返します。 + * 通常は、ソケットディスクリプタを直接操作しないでください。 + * + * @param sock 対象ソケット + * @return ソケットディスクリプタ + */ + socket_t (*get_socket)(struct KcSocket_ *sock); + + /** + * ソケットタイプを返します。 + * + * @param sock 対象ソケット + * @return ソケットタイプ + */ + int (*get_type)(struct KcSocket_ *sock); + + /** + * ソケットファミリを返します。 + * 実際にバインド、あるいは接続されているソケットファミリを返します。 + * + * @param sock 対象ソケット + * @return ソケットファミリ + */ + int (*get_family)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービスのアドレス情報を取得します。 + * + * @param sock 対象ソケット + * @param result アドレス情報格納用ポインタ + * @param addr アドレス + * @param service サービス + * @param is_passive サーバソケットの場合、true を指定ください。 + */ + bool (*get_addrinfo)( + struct KcSocket_ *sock, + struct addrinfo **result, + const char *addr, + const char *service, + bool is_passive); + + /** + * 指定されたアドレス、サービスにバインドします。 + * 指定されたアドレスとサービスにより、複数候補がある場合は、 + * 最初に見つかったアドレスとポートにバインドします。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス (例: "80", "http", "ssh" など) + * @return true/false (bind 成功/失敗) + */ + bool (*bind)(struct KcSocket_ *sock, const char *addr, const char *service); + + /** + * ソケットを接続待ちソケットとしてマークをつけます。 + * 保留中の接続のキュー最大長を指定します。 + * + * @param sock 対象ソケット + * @param backlog バックログ + * @return true/false (成功/失敗) + */ + bool (*listen)(struct KcSocket_ *sock, int backlog); + + /** + * ソケットへの接続を受け付けます。 + * + * @param sock 対象ソケット + * @return 受け付けたソケット + */ + struct KcSocket_ *(*accept)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービス接続します。 + * local_addr に NULL を指定した場合、自動的にローカルのアドレスが設定されます。 + * local_service に -1 を指定した場合、自動的にローカルのポート番号が割りつけられます。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス + * @param local_addr ローカルアドレス + * @param local_service ローカルサービス + * @return true/false (成功/失敗) + */ + bool (*connect)(struct KcSocket_ *sock, const char *addr, const char *service, const char *local_addr, const char *local_port); + + /** + * ソケットをクローズします。 + * + * @param sock 対象ソケット + * @return true/false (成功/失敗) + */ + bool (*close)(struct KcSocket_ *sock); + + /** + * メッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 送信されたデータサイズ + */ + ssize_t (*send)(struct KcSocket_ *sock, const char *buff, size_t size, int flags); + + /** + * メッセージを受信します。 + * 受信したメッセージは指定されたバッファに格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 読み取ったデータサイズ + */ + ssize_t (*recv)(struct KcSocket_ *sock, char *buff, size_t size, int flags); + + /** + * 指定されたアドレス、サービスにメッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param addr アドレス + * @param service サービス + * @return 送信メッセージサイズ、エラー発生時は -1 + */ + ssize_t (*sendto)( + struct KcSocket_ *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service); + + /** + * メッセージを受信します。 + * src_addr, src_service がいずれも NULL 出ない場合、 + * 送信元のアドレスとサービスが格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param src_addr 送信元アドレス格納用バッファ + * @param src_addrlen 送信元アドレス格納用バッファサイズ + * @param src_service 送信元サービス格納用バッファ + * @param src_servicelen 送信元サービス格納用バッファサイズ + * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + */ + ssize_t (*recvfrom)( + struct KcSocket_ *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen); + + /** + * TTL を設定します。 + * + * @param sock 対象ソケット + * @param val 設定する TTL + * @return true/false (成功/失敗) + */ + bool (*set_ttl)(struct KcSocket_ *sock, int val); + + /** + * マルチキャストグループに参加するために JOIN します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @param ifindex インタフェースインデックス + * @return true/false (成功/失敗) + */ + bool (*join)(struct KcSocket_ *sock, const char *addr, const char *ifname, unsigned int ifindex); + + /** + * マルチキャストグループから離脱します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @return true/false (成功/失敗) + */ + bool (*leave)(struct KcSocket_ *sock, const char *addr); + + /** + * Socket 管理情報 + */ + void *_info; + + } KcSocket; + + /** + * 指定されたタイプ、ファミリのSocket オブジェクトを構築します。 + * + * @param type タイプ(SOCK_STREAM/SOCK_DGRAM/SOCK_RAW) + * @param family ファミリ(AF_UNSPEC/AF_INET/AF_INET6) + */ + KcSocket *KcSocket_new(int type, int family); + + /** + * Socket を破棄します。 + */ + void KcSocket_delete(KcSocket *socket); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_threads.h b/modules/include/kc_threads.h new file mode 100644 index 0000000..764176f --- /dev/null +++ b/modules/include/kc_threads.h @@ -0,0 +1,98 @@ +/** + * @file kc_threads.h + * @brief スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_THREADS_H +#define KC_THREADS_H + +#include + +#if (KC_IS_WINDOWS) +#include +#else +#include +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcThread_ + { + + /** + * 指定されたスレッドが生きているか否かを返します。 + * + * @param thread 対象スレッド + * @return true/false (スレッドが生存している/していない) + */ + bool (*is_alive)(struct KcThread_ *thread); + + /** + * スレッドを開始します。 + * + * @param thread 対象スレッド + * @param args スレッドに渡す情報 + * @return true/false (スレッド起動成功/失敗) + */ + bool (*start)(struct KcThread_ *thread, void *args); + + /** + * スレッドが終了するのを待機します。 + * + * @param thread 対象スレッド + */ + bool (*join)(struct KcThread_ *thread); + + /** + * Thread 管理情報 + */ + void *_info; + + } KcThread; + + /** + * スレッドを生成します。 + * + * @param run スレッドにて実行する関数へのポインタ + */ + KcThread *KcThread_new(int (*run)(void *args)); + + /** + * Thread を破棄します。 + * + * @param thread 破棄するスレッド + */ + void KcThread_delete(KcThread *thread); + + /** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープさせるミリ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_msleep(long time, bool force); + + /** + * 指定された sec 秒 + nsec ナノ秒間現在のスレッドをスリープさせます。 + * + * @param sec スリープさせる秒数 + * @param nsec スリープさせるナノ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_sleep(long sec, long nsec, bool force); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_THREADS_H diff --git a/modules/include/kc_threads_win.h b/modules/include/kc_threads_win.h new file mode 100644 index 0000000..b40ab10 --- /dev/null +++ b/modules/include/kc_threads_win.h @@ -0,0 +1,98 @@ +/** + * @file kc_windows_thread.h + * @brief Windows 用スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_WINDOWS_THREAD_H +#define KC_WINDOWS_THREAD_H + +#include +#if (KC_IS_WINDOWS) +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#define thread_local _Thread_local + + //////////////////////////////////////////////////////////////////////// + // + // Thread + // + + enum + { + thrd_success = 0, //!< 成功 + thrd_bussy = 1, //!< busy + thrd_error = 2, //!< エラー発生 + thrd_nomem = 3, //!< メモリ不足 + thrd_timedout = 4 //!< タイムアウト発生 + }; + + typedef struct + { + HANDLE handle; + DWORD thread_id; + } thrd_t; + + typedef int (*thrd_start_t)(void *); + int thrd_create(thrd_t *thr, thrd_start_t func, void *arg); + int thrd_join(thrd_t thr, int *res); + int thrd_detach(thrd_t thr); + thrd_t thrd_current(void); + int thrd_equal(thrd_t lhs, thrd_t rhs); + int thrd_sleep(const struct timespec *duration, struct timespec *remaining); + + //////////////////////////////////////////////////////////////////////// + // + // mutex + // + + enum + { + mtx_plain = 0, //!< 通常 + mtx_recursive = 1, //!< 再帰 + mtx_timed = 2 //!< timed + }; + + typedef struct + { + CRITICAL_SECTION cs; + int type; + } mtx_t; + + int mtx_init(mtx_t *mtx, int type); + void mtx_destroy(mtx_t *mtx); + int mtx_lock(mtx_t *mtx); + int mtx_unlock(mtx_t *mtx); + + //////////////////////////////////////////////////////////////////////// + // + // cond + // + + typedef struct + { + CONDITION_VARIABLE cond; + } cnd_t; + + int cnd_init(cnd_t *cond); + int cnd_signal(cnd_t *cond); + int cnd_broadcast(cnd_t *cond); + int cnd_wait(cnd_t *cond, mtx_t *mtx); + int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts); + void cnd_destroy(cnd_t *cond); + +#endif // (KC_IS_WINDOWS) +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_windows.h b/modules/include/kc_windows.h index ee70898..6e1c659 100644 --- a/modules/include/kc_windows.h +++ b/modules/include/kc_windows.h @@ -37,9 +37,9 @@ #endif // よく利用されるヘッダファイルをインクルードする -#include #include #include +#include #ifdef _MSC_VER #pragma comment(lib, "ws2_32.lib") #endif diff --git a/modules/src/kc_assert.c b/modules/src/kc_assert.c index 371fc8f..7cd4ecc 100644 --- a/modules/src/kc_assert.c +++ b/modules/src/kc_assert.c @@ -6,9 +6,9 @@ #include #include #include -#include #include #include +#include // プロトタイプ宣言 static void assert_default_handler(const char *msg); diff --git a/modules/src/kc_env.c b/modules/src/kc_env.c index 0675c73..95f185c 100644 --- a/modules/src/kc_env.c +++ b/modules/src/kc_env.c @@ -50,7 +50,7 @@ char *result = NULL; #if (KC_IS_WINDOWS) static char buff[KC_ENV_BUFFER_MAX]; - DWORD ret = GetEnvironmentVarable(key_name, (LPSTR)buff, sizeof(buff)); + DWORD ret = GetEnvironmentVariable(key_name, (LPSTR)buff, sizeof(buff)); if (ret != 0) { result = buff; diff --git a/modules/src/kc_list_array.c b/modules/src/kc_list_array.c index 246ed14..f29fc08 100644 --- a/modules/src/kc_list_array.c +++ b/modules/src/kc_list_array.c @@ -3,10 +3,14 @@ * @brief 配列リストモジュール * @copyright 2003 - 2023 Nomura Kei */ +#include #if defined(__GNUC__) #define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) #define qsort_s qsort_r #endif +#endif // defined(__GNUC__) + #include #include #include @@ -17,6 +21,7 @@ #include #include #include + /** * KcArrayList 管理情報 */ @@ -306,7 +311,11 @@ * @param context コンテキスト(KcListSortInfo) * @return 比較結果 */ +#if (KC_IS_WINDOWS) +static int KcArrayList_comparator(void *context, const void *x, const void *y) +#else static int KcArrayList_comparator(const void *x, const void *y, void *context) +#endif { KcListSortInfo *sort_info = (KcListSortInfo *)context; int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); diff --git a/modules/src/kc_list_linked.c b/modules/src/kc_list_linked.c index 30575b9..47edbe4 100644 --- a/modules/src/kc_list_linked.c +++ b/modules/src/kc_list_linked.c @@ -3,10 +3,14 @@ * @brief Linked リストモジュール * @copyright 2003 - 2023 Nomura Kei */ +#include #if defined(__GNUC__) #define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) #define qsort_s qsort_r #endif +#endif // defined(__GNUC__) + #include #include #include @@ -18,6 +22,13 @@ #include #include +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) +#define qsort_s qsort_r +#endif // KC_IS_WINDOWS +#endif // defined(__GNUC__) + /** * KcLinkedList Entry 情報 */ @@ -291,7 +302,11 @@ * @param context コンテキスト(KcListSortInfo) * @return 比較結果 */ +#if (KC_IS_WINDOWS) +static int KcLinkedList_comparator(void *context, const void *x, const void *y) +#else static int KcLinkedList_comparator(const void *x, const void *y, void *context) +#endif // KC_IS_WINDOWS { KcListSortInfo *sort_info = (KcListSortInfo *)context; const KcLinkedListEntry **entry_x = (const KcLinkedListEntry **)x; diff --git a/modules/src/kc_lock_guard.c b/modules/src/kc_lock_guard.c index 6ef68e5..648a153 100644 --- a/modules/src/kc_lock_guard.c +++ b/modules/src/kc_lock_guard.c @@ -7,7 +7,6 @@ #include - /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 * ロックに成功するとロック管理用のオブジェクトを返します。 @@ -15,36 +14,37 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex) +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line) { - KcLockGuard guard = { .mutex = mutex }; + KcLockGuard guard = {.mutex = mutex}; if (mutex == NULL) { errno = EINVAL; perror("kc_lock_guard_init: (mutex = NULL)"); + fprintf(stderr, "%s:%s:%d kc_lock_guard_init : mutex = NULL\n", file, func, line); return guard; } + errno = 0; int ret = mtx_lock(mutex); if (ret != thrd_success) { - perror("kc_lock_guard : mtx_lock error"); + fprintf(stderr, "%s:%s:%d kc_lock_guard : mtx_lock error\n", file, func, line); guard.mutex = NULL; } return guard; } - /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard) +void kc_lock_guard_release(KcLockGuard *guard) { - if (guard->mutex != NULL) { - mtx_unlock(guard->mutex); - guard->mutex = NULL; + if (guard->mutex != NULL) + { + mtx_unlock(guard->mutex); + guard->mutex = NULL; } } - diff --git a/modules/src/kc_memory.c b/modules/src/kc_memory.c index 5cc4186..6dbbc88 100644 --- a/modules/src/kc_memory.c +++ b/modules/src/kc_memory.c @@ -622,7 +622,11 @@ */ void *raw_aligned_alloc(size_t alignment, size_t size) { +#if (KC_IS_WINDOWS) + return _aligned_malloc(size, alignment); +#else return aligned_alloc(alignment, size); +#endif } /** diff --git a/modules/src/kc_memory_dump.c b/modules/src/kc_memory_dump.c index 111e328..49e63de 100644 --- a/modules/src/kc_memory_dump.c +++ b/modules/src/kc_memory_dump.c @@ -144,7 +144,7 @@ { // 空きバッファサイズ無しのため、何もしない。 return; } - int write_size = snprintf(info->write_ptr, info->rest_size, msg); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s", msg); if (write_size > info->rest_size) { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) write_size = (info->rest_size - 1); diff --git a/modules/src/kc_memory_listener.c b/modules/src/kc_memory_listener.c index 29ff2a0..ee69a13 100644 --- a/modules/src/kc_memory_listener.c +++ b/modules/src/kc_memory_listener.c @@ -98,5 +98,5 @@ KcMemoryDump_dump(buff, sizeof(buff), entry, 16, true, true, 130); fprintf(stderr, "[ERROR] %s", buff); } - fprintf(stderr, msg); + fprintf(stderr, "%s", msg); } diff --git a/modules/src/kc_queue.c b/modules/src/kc_queue.c index 544d763..d9f2a14 100644 --- a/modules/src/kc_queue.c +++ b/modules/src/kc_queue.c @@ -9,11 +9,11 @@ #include #include #include -#include #include #include #include +#include #include #include @@ -122,7 +122,10 @@ static int KcQueue_size(struct KcQueue_ *queue) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - size_t size = info->list->size(info->list); + size_t size = 0; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + size = info->list->size(info->list); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== return (int)size; } @@ -138,7 +141,11 @@ static bool KcQueue_is_empty(struct KcQueue_ *queue) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - return info->list->is_empty(info->list); + bool is_empty = true; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + is_empty = info->list->is_empty(info->list); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== + return is_empty; } // ============================================================================= @@ -155,7 +162,11 @@ static bool KcQueue_contains(struct KcQueue_ *queue, const void *element, size_t size) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - return info->list->contains(info->list, element, size); + bool is_contains = false; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + is_contains = info->list->contains(info->list, element, size); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== + return is_contains; } // ============================================================================= @@ -227,7 +238,11 @@ static void *KcQueue_peek(struct KcQueue_ *queue, size_t *size) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - return info->list->get(info->list, 0, size); + void *value = NULL; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + value = info->list->get(info->list, 0, size); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== + return value; } // ============================================================================= diff --git a/modules/src/kc_socket.c b/modules/src/kc_socket.c new file mode 100644 index 0000000..cb67996 --- /dev/null +++ b/modules/src/kc_socket.c @@ -0,0 +1,753 @@ +/** + * @file kc_socket.c + * @brief ソケットモジュール + * @copyright 2003 - 2024 Nomura Kei + */ +#include +#include + +#include +#include +#include +#include + +/** + * KcSocket 管理情報 + */ +typedef struct +{ + socket_t sock_fd; //!< ソケットディスクリプタ + int sock_type; //!< ソケットタイプ + int sock_family; //!< ソケットファミリ + struct sockaddr_storage remote_addr; //!< リモートアドレス + struct sockaddr_storage local_addr; //!< ローカルアドレス + socklen_t remote_addrlen; //!< リモートアドレス長 + socklen_t local_addrlen; //!< ローカルアドレス帳 +} KcSocketInfo; + +/** + * アドレス情報 + */ +typedef struct +{ + const char *addr; //!< アドレス + const char *service; //!< サービス +} KcSocketAddress; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static void Socket_setup(void); +static void Socket_cleanup(void); +static const char *KcSocket_get_remote_addr(KcSocket *sock, char *addr, size_t addrlen); +static int KcSocket_get_remote_port(KcSocket *sock); +static const char *KcSocket_get_local_addr(KcSocket *sock, char *addr, size_t addrlen); +static int KcSocket_get_local_port(KcSocket *sock); +static socket_t KcSocket_get_socket(KcSocket *sock); +static int KcSocket_get_type(KcSocket *sock); +static int KcSocket_get_family(KcSocket *sock); +static bool KcSocket_get_addrinfo( + KcSocket *sock, struct addrinfo **result, + const char *addr, const char *service, bool is_passive); +static bool KcSocket_bind(KcSocket *sock, const char *addr, const char *service); +static bool KcSocket_listen(KcSocket *sock, int backlog); +static KcSocket *KcSocket_accept(KcSocket *sock); +static bool KcSocket_connect( + KcSocket *sock, const char *addr, const char *service, + const char *local_addr, const char *local_service); +static bool KcSocket_close(KcSocket *sock); +static ssize_t KcSocket_send(KcSocket *sock, const char *buff, size_t size, int flags); +static ssize_t KcSocket_recv(KcSocket *sock, char *buff, size_t size, int flags); +static ssize_t KcSocket_sendto( + KcSocket *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service); +static ssize_t KcSocket_recvfrom( + KcSocket *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen); +static bool KcSocket_set_ttl(KcSocket *sock, int val); +static bool KcSocket_join(KcSocket *sock, const char *addr, const char *ifname, unsigned int ifindex); +static bool KcSocket_leave(KcSocket *sock, const char *addr); + +static bool KcSocket_addrinfo_bind_and_connect( + KcSocket *sock, struct addrinfo *bind_addrinfo, struct addrinfo *conn_addrinfo); +static bool KcSocket_addrinfo_connect(KcSocket *sock, struct addrinfo *conn_addrinfo, socket_t sockfd); + +/** + * ソケットのセットアップをします。 + */ +static void Socket_setup(void) +{ + static bool is_init = false; + if (!is_init) + { +#if (KC_IS_WINDOWS) + WSADATA wsa_data; + WSAStartup(MAKEWORD(2, 0), &wsa_data); +#endif + atexit(Socket_cleanup); + is_init = true; + } +} + +/** + * ソケットライブラリのクリーンアップをします。 + */ +static void Socket_cleanup(void) +{ +#if (KC_IS_WINDOWS) + WSACleanup(); +#endif +} + +// ============================================================================= +// new +// ============================================================================= +/** + * 指定されたタイプ、ファミリのSocket オブジェクトを構築します。 + * + * @param type タイプ(SOCK_STREAM/SOCK_DGRAM/SOCK_RAW) + * @param family ファミリ(AF_UNSPEC/AF_INET/AF_INET6) + */ +KcSocket *KcSocket_new(int type, int family) +{ + // KcSocket の管理構造 + // +----------------+ + // | KcSocket | + // | ... | + // | _info -----------+ + // +----------------+ | + // | |<--+ + // +----------------+ + Socket_setup(); + KcSocket *sock = (KcSocket *)malloc(sizeof(KcSocket) + sizeof(KcSocketInfo)); + if (sock != NULL) + { + sock->get_remote_addr = KcSocket_get_remote_addr; + sock->get_remote_port = KcSocket_get_remote_port; + sock->get_local_addr = KcSocket_get_local_addr; + sock->get_local_port = KcSocket_get_local_port; + sock->get_socket = KcSocket_get_socket; + sock->get_type = KcSocket_get_type; + sock->get_family = KcSocket_get_family; + sock->get_addrinfo = KcSocket_get_addrinfo; // for local + sock->bind = KcSocket_bind; + sock->listen = KcSocket_listen; + sock->accept = KcSocket_accept; + sock->connect = KcSocket_connect; + sock->close = KcSocket_close; + sock->send = KcSocket_send; + sock->recv = KcSocket_recv; + sock->sendto = KcSocket_sendto; + sock->recvfrom = KcSocket_recvfrom; + sock->set_ttl = KcSocket_set_ttl; + sock->join = KcSocket_join; + sock->leave = KcSocket_leave; + sock->_info = (sock + 1); + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + info->sock_fd = INVALID_SOCKET; + info->sock_type = type; + info->sock_family = family; + info->remote_addrlen = 0; + info->local_addrlen = 0; + } + return sock; +} + +// ============================================================================= +// delete +// ============================================================================= +/** + * Socket を破棄します。 + */ +void KcSocket_delete(KcSocket *socket) +{ + if (socket) + { + if (((KcSocketInfo *)socket->_info)->sock_fd != INVALID_SOCKET) + { + socket->close(socket); + } + free(socket); + } +} + +// ============================================================================= +// get_remote_addr +// ============================================================================= +/** + * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr 接続先アドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ +static const char *KcSocket_get_remote_addr(KcSocket *sock, char *addr, size_t addrlen) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->remote_addrlen) + { + int ret = getnameinfo( + (const struct sockaddr *)&info->remote_addr, + info->remote_addrlen, + addr, addrlen, NULL, 0, NI_NUMERICHOST); + if (ret == 0) + { + return addr; + } + } + return NULL; +} + +/** + * ソケットの接続先ポート番号を返します。 + * 接続先ポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ +static int KcSocket_get_remote_port(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->remote_addrlen) + { + char tmp_port[NI_MAXSERV]; + int ret = getnameinfo( + (const struct sockaddr *)&info->remote_addr, + info->remote_addrlen, + NULL, 0, tmp_port, sizeof(tmp_port), NI_NUMERICSERV); + if (ret == 0) + { + return atoi(tmp_port); + } + } + return -1; +} + +/** + * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr ローカルアドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ +static const char *KcSocket_get_local_addr(KcSocket *sock, char *addr, size_t addrlen) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->local_addrlen) + { + int ret = getnameinfo( + (const struct sockaddr *)&info->local_addr, + info->remote_addrlen, + addr, addrlen, NULL, 0, NI_NUMERICHOST); + if (ret == 0) + { + return addr; + } + } + return NULL; +} + +/** + * ソケットのローカルポート番号を返します。 + * ローカルポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ +static int KcSocket_get_local_port(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->local_addrlen) + { + char tmp_port[NI_MAXSERV]; + int ret = getnameinfo( + (const struct sockaddr *)&info->local_addr, + info->remote_addrlen, + NULL, 0, tmp_port, sizeof(tmp_port), NI_NUMERICSERV); + if (ret == 0) + { + return atoi(tmp_port); + } + } + return -1; +} + +/** + * ソケットディスクリプタを返します。 + * 通常は、ソケットディスクリプタを直接操作しないでください。 + * + * @param sock 対象ソケット + * @return ソケットディスクリプタ + */ +static socket_t KcSocket_get_socket(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + return info->sock_fd; +} + +/** + * ソケットタイプを返します。 + * + * @param sock 対象ソケット + * @return ソケットタイプ + */ +static int KcSocket_get_type(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + return info->sock_type; +} + +/** + * ソケットファミリを返します。 + * ソケット接続、バインド時には、実際にバインド、 + * あるいは接続されているソケットファミリを返します。 + * + * @param sock 対象ソケット + * @return ソケットファミリ + */ +static int KcSocket_get_family(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int result_family = info->sock_family; + if (info->sock_fd != INVALID_SOCKET) + { + struct sockaddr_storage ss; + socklen_t len = sizeof(struct sockaddr_storage); + if (getsockname(info->sock_fd, (struct sockaddr *)&ss, &len) == 0) + { + result_family = ss.ss_family; + } + } + return result_family; +} + +/** + * 指定されたアドレス、サービスのアドレス情報を取得します。 + * + * @param sock 対象ソケット + * @param result アドレス情報格納用ポインタ + * @param addr アドレス + * @param service サービス + * @param is_passive サーバソケットの場合、true を指定ください。 + */ +static bool KcSocket_get_addrinfo( + KcSocket *sock, struct addrinfo **result, + const char *addr, const char *service, bool is_passive) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + + struct addrinfo hints; + memset(&hints, 0x00, sizeof(struct addrinfo)); + hints.ai_socktype = info->sock_type; + hints.ai_family = info->sock_family; + if (is_passive) + { + hints.ai_flags = AI_PASSIVE; + } + int ret = getaddrinfo(addr, service, &hints, result); + return (ret == 0); +} + +/** + * 指定されたアドレス、サービスにバインドします。 + * 指定されたアドレスとサービスにより、複数候補がある場合は、 + * 最初に見つかったアドレスとポートにバインドします。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス (例: "80", "http", "ssh" など) + * @return true/false (bind 成功/失敗) + */ +static bool KcSocket_bind(KcSocket *sock, const char *addr, const char *service) +{ + struct addrinfo *bind_addr; + bool is_success = sock->get_addrinfo(sock, &bind_addr, addr, service, true); + if (is_success) + { + is_success = KcSocket_addrinfo_bind_and_connect(sock, bind_addr, NULL); + freeaddrinfo(bind_addr); + } + return is_success; +} + +/** + * ソケットを接続待ちソケットとしてマークをつけます。 + * 保留中の接続のキュー最大長を指定します。 + * + * @param sock 対象ソケット + * @param backlog バックログ + * @return true/false (成功/失敗) + */ +static bool KcSocket_listen(KcSocket *sock, int backlog) +{ + bool result = false; + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->sock_fd != INVALID_SOCKET) + { + int ret = listen(info->sock_fd, backlog); + result = (ret != SOCKET_ERROR); + } + return result; +} + +/** + * ソケットへの接続を受け付けます。 + * + * @param sock 対象ソケット + * @return 受け付けたソケット(失敗時NULL) + */ +static KcSocket *KcSocket_accept(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + KcSocket *client = NULL; + if (info->sock_fd != INVALID_SOCKET) + { + client = KcSocket_new(info->sock_type, info->sock_family); + if (client != NULL) + { + KcSocketInfo *cinfo = (KcSocketInfo *)client->_info; + cinfo->remote_addrlen = sizeof(struct sockaddr_storage); + cinfo->sock_fd = accept(info->sock_fd, (struct sockaddr *)&cinfo->remote_addr, &cinfo->remote_addrlen); + if (cinfo->sock_fd != INVALID_SOCKET) + { + memcpy(&cinfo->local_addr, &info->local_addr, info->local_addrlen); + cinfo->local_addrlen = info->local_addrlen; + cinfo->sock_type = SOCK_STREAM; + cinfo->sock_family = cinfo->remote_addr.ss_family; + } + else + { + KcSocket_delete(client); + client = NULL; + } + } + } + return client; +} + +/** + * 指定されたアドレス、サービス接続します。 + * local_addr に NULL を指定した場合、自動的にローカルのアドレス、サービスが設定されます。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス + * @param local_addr ローカルアドレス + * @param local_service ローカルサービス + * @return true/false (成功/失敗) + */ +static bool KcSocket_connect( + KcSocket *sock, const char *addr, const char *service, + const char *local_addr, const char *local_service) +{ + // 接続先アドレス情報取得 + struct addrinfo *conn_addr = NULL; + if (!sock->get_addrinfo(sock, &conn_addr, addr, service, false)) + { + return false; + } + + // バインドアドレス情報取得 + bool is_success = false; + struct addrinfo *bind_addr = NULL; + if (local_addr != NULL) + { // bind が必要 + is_success = sock->get_addrinfo(sock, &bind_addr, local_addr, local_service, true); + if (is_success) + { + is_success = KcSocket_addrinfo_bind_and_connect(sock, bind_addr, conn_addr); + freeaddrinfo(bind_addr); + } + } + else + { + is_success = KcSocket_addrinfo_connect(sock, conn_addr, INVALID_SOCKET); + } + freeaddrinfo(conn_addr); + return is_success; +} + +/** + * ソケットをクローズします。 + * + * @param sock 対象ソケット + * @return true/false (成功/失敗) + */ +static bool KcSocket_close(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + + if (info->sock_family == SOCK_STREAM) + { // TCP の場合は、出力を閉じて、届いているデータを読み取ってから close する。 + int ret = shutdown(info->sock_fd, SHUT_WR); + if (ret == 0) + { + int read_size; + char buff[1024]; + do + { // 届いているデータを全て読み取る + read_size = recv(info->sock_fd, buff, sizeof(buff), 0); + } while (read_size > 0); + } + } + sockclose(info->sock_fd); + return true; +} + +/** + * メッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 送信されたデータサイズ + */ +static ssize_t KcSocket_send(KcSocket *sock, const char *buff, size_t size, int flags) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + ssize_t write_size = 0; + do + { + errno = 0; + write_size = send(info->sock_fd, buff, size, flags); + } while ((write_size < 0) && (errno == EINTR)); + return write_size; +} + +/** + * メッセージを受信します。 + * 受信したメッセージは指定されたバッファに格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 読み取ったデータサイズ + */ +static ssize_t KcSocket_recv(KcSocket *sock, char *buff, size_t size, int flags) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + ssize_t read_size; + do + { + errno = 0; + read_size = recv(info->sock_fd, buff, size, flags); + } while ((read_size < 0) && (errno == EINTR)); + return read_size; +} + +/** + * 指定されたアドレス、サービスにメッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param addr アドレス + * @param service サービス + * @return 送信メッセージサイズ、エラー発生時は -1 + */ +static ssize_t KcSocket_sendto( + KcSocket *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service) +{ + (void)sock; + (void)buff; + (void)size; + (void)flags; + (void)addr; + (void)service; + return 0; +} + +/** + * メッセージを受信します。 + * src_addr, src_service がいずれも NULL 出ない場合、 + * 送信元のアドレスとサービスが格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param src_addr 送信元アドレス格納用バッファ + * @param src_addrlen 送信元アドレス格納用バッファサイズ + * @param src_service 送信元サービス格納用バッファ + * @param src_servicelen 送信元サービス格納用バッファサイズ + * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + */ +static ssize_t KcSocket_recvfrom( + KcSocket *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen) +{ + (void)sock; + (void)buff; + (void)size; + (void)flags; + (void)src_addr; + (void)src_addrlen; + (void)src_service; + (void)src_servicelen; + return 0; +} + +/** + * TTL を設定します。 + * + * @param sock 対象ソケット + * @param val 設定する TTL + * @return true/false (成功/失敗) + */ +static bool KcSocket_set_ttl(KcSocket *sock, int val) +{ + (void)sock; + (void)val; + return true; +} + +/** + * マルチキャストグループに参加するために JOIN します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @param ifindex インタフェースインデックス + * @return true/false (成功/失敗) + */ +static bool KcSocket_join( + KcSocket *sock, const char *addr, const char *ifname, unsigned int ifindex) +{ + (void)sock; + (void)addr; + (void)ifname; + (void)ifindex; + return true; +} + +/** + * マルチキャストグループから離脱します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @return true/false (成功/失敗) + */ +static bool KcSocket_leave( + KcSocket *sock, const char *addr) +{ + (void)sock; + (void)addr; + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// [内部関数] +// + +/** + * 指定された接続アドレス情報をもとにソケットの生成、接続を試みます。 + * 接続済みソケット sockfd が有効な場合、sockfd を用いて接続します。 + * + * @param sock 対象ソケット + * @param conn_addrinfo 接続アドレス情報 + * @param sockfd 接続済みソケット + * @return true/false (接続成功/失敗) + */ +static bool KcSocket_addrinfo_connect(KcSocket *sock, struct addrinfo *conn_addrinfo, socket_t sockfd) +{ + bool is_success = false; + socket_t tmp_sock = sockfd; + for (struct addrinfo *rp = conn_addrinfo; rp != NULL; rp = rp->ai_next) + { + if (sockfd == INVALID_SOCKET) + { // sockfd が無効の場合、ソケットを生成する。 + tmp_sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (tmp_sock == INVALID_SOCKET) + { + continue; + } + } + + int ret = connect(tmp_sock, rp->ai_addr, rp->ai_addrlen); + is_success = (ret != SOCKET_ERROR); + if (is_success) + { + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + info->sock_fd = tmp_sock; + info->remote_addrlen = rp->ai_addrlen; + memcpy(&info->remote_addr, rp->ai_addr, rp->ai_addrlen); + break; + } + + if (sockfd != INVALID_SOCKET) + { // sockfd が無効の場合、一時的に生成したソケットをクローズする。 + sockclose(tmp_sock); + } + } + return is_success; +} + +/** + * 指定された接続アドレス情報をもとにソケットの生成、バインドを試みます。 + * conn_addrinfo が NULL でない場合、バインド後に接続を試みます。 + * + * @param sock 対象ソケット + * @param bind_addrinfo バインドアドレス情報 + * @param conn_addrinfo 接続アドレス情報 + * @return true/false (接続成功/失敗) + */ +static bool KcSocket_addrinfo_bind_and_connect( + KcSocket *sock, struct addrinfo *bind_addrinfo, struct addrinfo *conn_addrinfo) +{ + int ret; + bool is_success = false; + + for (struct addrinfo *rp = bind_addrinfo; rp != NULL; rp = rp->ai_next) + { // sokcet : ソケット生成 + socket_t tmp_sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (tmp_sock == INVALID_SOCKET) + { + continue; + } + + // bind + const int on = 1; + setsockopt(tmp_sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on)); + ret = bind(tmp_sock, rp->ai_addr, rp->ai_addrlen); + is_success = (ret != SOCKET_ERROR); + if (!is_success) + { // bind 失敗したので次へ + sockclose(tmp_sock); + continue; + } + + // connect + if (conn_addrinfo) + { + is_success = KcSocket_addrinfo_connect(sock, conn_addrinfo, tmp_sock); + if (!is_success) + { // connect 失敗したので次へ + sockclose(tmp_sock); + continue; + } + } + + // bind または、bind と connect 成功 + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + info->sock_fd = tmp_sock; + info->local_addrlen = rp->ai_addrlen; + memcpy(&info->local_addr, rp->ai_addr, rp->ai_addrlen); + break; + } + return is_success; +} diff --git a/modules/src/kc_threads.c b/modules/src/kc_threads.c new file mode 100644 index 0000000..dd600bb --- /dev/null +++ b/modules/src/kc_threads.c @@ -0,0 +1,194 @@ +/** + * @file kc_thread.c + * @brief スレッドモジュール + * @copyright 2020 - 2024 Nomura Kei + */ +#include +#include +#include + +#include +#include +#include + +/** + * KcThread 管理情報 + */ +typedef struct +{ + thrd_t tid; + void *args; + int (*run)(void *args); + atomic_bool alive_state; +} KcThreadInfo; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static bool KcThread_is_alive(KcThread *thread); +static bool KcThread_start(KcThread *thread, void *args); +static bool KcThread_join(KcThread *thread); +static int KcThread_run(void *thread); + +// ============================================================================= +// new +// ============================================================================= +/** + * スレッドを生成します。 + * + * @param run スレッドにて実行する関数へのポインタ + */ +KcThread *KcThread_new(int (*run)(void *args)) +{ + // KcThread の管理構造 + // +--------------+ + // | KcThread | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // +--------------+ + KcThread *thread = (KcThread *)malloc(sizeof(KcThread) + sizeof(KcThreadInfo)); + if (thread != NULL) + { + thread->is_alive = KcThread_is_alive; + thread->start = KcThread_start; + thread->join = KcThread_join; + thread->_info = (thread + 1); + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + // info->tid + info->run = run; + atomic_init(&info->alive_state, false); + } + return thread; +} + +// ============================================================================= +// delete +// ============================================================================= +/** + * Thread を破棄します。 + * + * @param thread 破棄するスレッド + */ +void KcThread_delete(KcThread *thread) +{ + free(thread); +} + +// ============================================================================= +// sleep +// ============================================================================= +/** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープさせるミリ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ +bool KcThread_msleep(long time, bool force) +{ + long sec = time / 1000; + long msec = time - (sec * 1000); + long nsec = msec * 1000 * 1000; + return KcThread_sleep(sec, nsec, force); +} + +/** + * 指定された sec 秒 + nsec ナノ秒間現在のスレッドをスリープさせます。 + * + * @param sec スリープさせる秒数 + * @param nsec スリープさせるナノ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ +bool KcThread_sleep(long sec, long nsec, bool force) +{ + struct timespec remaining = {sec, nsec}; + int ret; + do + { + errno = 0; + ret = thrd_sleep(&remaining, &remaining); + } while ((ret == -1) && force && (errno == EINTR)); + return (ret == 0); +} + +// ============================================================================= +// is_alive +// ============================================================================= +/** + * 指定されたスレッドが生きているか否かを返します。 + * + * @param thread 対象スレッド + * @return true/false (スレッドが生存している/していない) + */ +static bool KcThread_is_alive(KcThread *thread) +{ + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + return atomic_load(&info->alive_state); +} + +// ============================================================================= +// start +// ============================================================================= +/** + * スレッドを開始します。 + * + * @param thread 対象スレッド + * @param args スレッドに渡す情報 + * @return true/false (スレッド起動成功/失敗) + */ +static bool KcThread_start(KcThread *thread, void *args) +{ + int ret = thrd_error; + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + if (!atomic_load(&info->alive_state)) + { + info->args = args; + ret = thrd_create(&info->tid, KcThread_run, thread); + } + return (ret == thrd_success); +} + +// ============================================================================= +// join +// ============================================================================= +/** + * スレッドが終了するのを待機します。 + * + * @param thread 対象スレッド + * @return true/false (成功/失敗) + */ +static bool KcThread_join(KcThread *thread) +{ + int ret = thrd_error; + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + ret = thrd_join(info->tid, NULL); + return (ret == thrd_success); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// 内部関数 +// + +// ============================================================================= +// run +// ============================================================================= +/** + * スレッドで実行される関数。 + * start にて関数を別スレッドにて実行します。 + * + * @param args スレッド情報 + * @return + */ +int KcThread_run(void *args) +{ + KcThread *thread = (KcThread *)args; + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + atomic_store(&info->alive_state, true); + int ret = info->run(info->args); + atomic_store(&info->alive_state, false); + return ret; +} diff --git a/modules/src/kc_threads_win.c b/modules/src/kc_threads_win.c new file mode 100644 index 0000000..0439fda --- /dev/null +++ b/modules/src/kc_threads_win.c @@ -0,0 +1,201 @@ +/** + * @file kc_thread.c + * @brief スレッドモジュール + * @copyright 2020 - 2024 Nomura Kei + */ +#include +#include + +#if (KC_IS_WINDOWS) + +int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) +{ + if ((thr == NULL) || (func == NULL)) + { + return thrd_error; + } + + thr->handle = CreateThread( + NULL, // セキュリティ属性 + 0, // スタックサイズ + (LPTHREAD_START_ROUTINE)func, // スレッド関数 + arg, // 引数 + 0, // 作成フラグ + &(thr->thread_id) // スレッドID + ); + + if (thr->handle == NULL) + { + return thrd_error; + } + return thrd_success; +} +int thrd_join(thrd_t thr, int *res) +{ + if (WaitForSingleObject(thr.handle, INFINITE) != WAIT_OBJECT_0) + { + return thrd_error; + } + if (res != NULL) + { + DWORD retcode; + if (GetExitCodeThread(thr.handle, &retcode) == 0) + { + return thrd_error; + } + *res = (int)retcode; + } + CloseHandle(thr.handle); + return thrd_success; +} + +int thrd_detach(thrd_t thr) +{ + if (thr.handle == NULL) + { + return thrd_error; + } + if (CloseHandle(thr.handle) == 0) + { + return thrd_error; + } + return thrd_success; +} + +thrd_t thrd_current(void) +{ + thrd_t current; + current.handle = GetCurrentThread(); + current.thread_id = GetThreadId(current.handle); + return current; +} + +int thrd_equal(thrd_t lhs, thrd_t rhs) +{ + return (lhs.thread_id == rhs.thread_id); +} + +void thrd_yield(void) +{ + SwitchToThread(); +} + +int thrd_sleep(const struct timespec *duration, struct timespec *remaining) +{ + if (duration == NULL) + { + return thrd_error; + } + + // Windows の Sleep は、ms 単位 + DWORD msec = (DWORD)(duration->tv_sec * 1000 + duration->tv_nsec / 1000000); + Sleep(msec); + + // 常に成功したものとして、remaining は 0 に設定する。 + if (remaining != NULL) + { + remaining->tv_sec = 0; + remaining->tv_nsec = 0; + } + return thrd_success; +} + +int mtx_init(mtx_t *mtx, int type) +{ + if (mtx == NULL) + { + return thrd_error; + } + mtx->type = type; + if (type & mtx_plain) + { + InitializeCriticalSection(&mtx->cs); + } + else if (type & mtx_recursive) + { + InitializeCriticalSection(&mtx->cs); + } + else + { + return thrd_error; + } + return thrd_success; +} + +void mtx_destroy(mtx_t *mtx) +{ + DeleteCriticalSection(&mtx->cs); +} +int mtx_lock(mtx_t *mtx) +{ + EnterCriticalSection(&mtx->cs); + return thrd_success; +} +int mtx_unlock(mtx_t *mtx) +{ + LeaveCriticalSection(&mtx->cs); + return thrd_success; +} + +int cnd_init(cnd_t *cond) +{ + if (cond == NULL) + { + return thrd_error; + } + InitializeConditionVariable(&cond->cond); + return thrd_success; +} + +int cnd_signal(cnd_t *cond) +{ + if (cond == NULL) + { + return thrd_error; + } + WakeConditionVariable(&cond->cond); + return thrd_success; +} +int cnd_broadcast(cnd_t *cond) +{ + if (cond == NULL) + { + return thrd_error; + } + WakeAllConditionVariable(&cond->cond); + return thrd_success; +} +int cnd_wait(cnd_t *cond, mtx_t *mtx) +{ + if ((cond == NULL) || (mtx == NULL)) + { + return thrd_error; + } + if (SleepConditionVariableCS(&cond->cond, &mtx->cs, INFINITE)) + { + return thrd_success; + } + return thrd_error; +} + +int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts) +{ + if ((cond == NULL) || (mtx == NULL) || (ts == NULL)) + { + return thrd_error; + } + DWORD msec = (DWORD)(ts->tv_sec * 1000 + ts->tv_nsec / 1000000); + if (SleepConditionVariableCS(&cond->cond, &mtx->cs, msec)) + { + return thrd_timedout; + } + return thrd_error; +} + +void cnd_destroy(cnd_t *cond) +{ + // Nothing to do + UNUSED_VARIABLE(cond); +} + +#endif \ No newline at end of file diff --git a/modules/test/src/test_lock_guard.c b/modules/test/src/test_lock_guard.c index f00ed0b..9c3249f 100644 --- a/modules/test/src/test_lock_guard.c +++ b/modules/test/src/test_lock_guard.c @@ -37,7 +37,7 @@ { mtx_t mutex; mtx_init(&mutex, mtx_plain | mtx_recursive); - KcLockGuard lock_guard = kc_lock_guard_init(&mutex); + KcLockGuard lock_guard = kc_lock_guard_init(&mutex, __FILE__, __func__, __LINE__); assert_not_null(lock_guard.mutex); // Lock Guard 解放 @@ -53,7 +53,7 @@ */ static void test_lock_guard_init_null(void) { - KcLockGuard lock_guard = kc_lock_guard_init(NULL); + KcLockGuard lock_guard = kc_lock_guard_init(NULL, __FILE__, __func__, __LINE__); assert_equals(EINVAL, errno); assert_null(lock_guard.mutex); } @@ -69,7 +69,7 @@ mtx_t mutex; mtx_init(&mutex, mtx_plain | mtx_recursive); mtx_destroy(&mutex); - KcLockGuard lock_guard = kc_lock_guard_init(&mutex); + KcLockGuard lock_guard = kc_lock_guard_init(&mutex, __FILE__, __func__, __LINE__); assert_null(lock_guard.mutex); } @@ -83,7 +83,7 @@ { mtx_t mutex; mtx_init(&mutex, mtx_plain | mtx_recursive); - KcLockGuard lock_guard = kc_lock_guard_init(&mutex); + KcLockGuard lock_guard = kc_lock_guard_init(&mutex, __FILE__, __func__, __LINE__); assert_not_null(lock_guard.mutex); // Lock Guard 解放 diff --git a/modules/test/src/test_queue.c b/modules/test/src/test_queue.c index cbec7e0..1595972 100644 --- a/modules/test/src/test_queue.c +++ b/modules/test/src/test_queue.c @@ -1,12 +1,12 @@ #include #include -#include #include #include #include #include #include +#include #include "ut.h" @@ -58,14 +58,11 @@ static int test_queue_offer_thread(void *arg) { KcQueue *queue = (KcQueue *)arg; - struct timespec spec; - spec.tv_sec = 0; - spec.tv_nsec = 1; for (int i = 0; i < 100; i++) { printf("offer: %d\n", i); queue->offer(queue, &i, sizeof(int)); - thrd_sleep(&spec, NULL); + KcThread_sleep(0, 1, false); } return 0; } @@ -73,9 +70,6 @@ // for poll thread static int test_queue_poll_thread(void *arg) { - struct timespec spec; - spec.tv_sec = 0; - spec.tv_nsec = 1; KcQueue *queue = (KcQueue *)arg; for (int i = 0; i < 100; i++) { @@ -100,7 +94,7 @@ { printf("poll: (null)\n"); } - thrd_sleep(&spec, NULL); + KcThread_sleep(0, 1, false); } return 0; } @@ -112,27 +106,28 @@ { KcQueue *queue = KcQueue_new(10); - thrd_t offer_thread, poll_thread; + KcThread *offer_thread = KcThread_new(test_queue_offer_thread); + KcThread *poll_thread = KcThread_new(test_queue_poll_thread); - thrd_create(&offer_thread, test_queue_offer_thread, queue); - thrd_create(&poll_thread, test_queue_poll_thread, queue); + offer_thread->start(offer_thread, queue); + poll_thread->start(poll_thread, queue); - thrd_join(offer_thread, NULL); - thrd_join(poll_thread, NULL); + offer_thread->join(offer_thread); + poll_thread->join(poll_thread); + KcQueue_delete(queue); + KcThread_delete(offer_thread); + KcThread_delete(poll_thread); } // for put thread static int test_queue_put_thread(void *arg) { KcQueue *queue = (KcQueue *)arg; - struct timespec spec; - spec.tv_sec = 0; - spec.tv_nsec = 1 * 1000; for (int i = 0; i < 100; i++) { queue->put(queue, &i, sizeof(int)); - thrd_sleep(&spec, NULL); + KcThread_sleep(0, 1000, false); } return 0; } @@ -140,9 +135,6 @@ // for take thread static int test_queue_take_thread(void *arg) { - struct timespec spec; - spec.tv_sec = 0; - spec.tv_nsec = 1; KcQueue *queue = (KcQueue *)arg; for (int i = 0; i < 100; i++) { @@ -150,7 +142,7 @@ size_t size = sizeof(int); queue->take(queue, &element, &size); assert_equals(i, element); - thrd_sleep(&spec, NULL); + KcThread_sleep(0, 1, false); } return 0; } @@ -158,9 +150,6 @@ // for take thread static int test_queue_take2_thread(void *arg) { - struct timespec spec; - spec.tv_sec = 0; - spec.tv_nsec = 100 * 1000; KcQueue *queue = (KcQueue *)arg; for (int i = 0; i < 100; i++) { @@ -168,7 +157,7 @@ size_t size = sizeof(int); queue->take(queue, &element, &size); assert_equals(i, element); - thrd_sleep(&spec, NULL); + KcThread_sleep(0, 100 * 1000, false); } return 0; } @@ -180,22 +169,31 @@ { { KcQueue *queue = KcQueue_new(0); - thrd_t put_thread, take_thread; - thrd_create(&put_thread, test_queue_put_thread, queue); - thrd_create(&take_thread, test_queue_take_thread, queue); - thrd_join(put_thread, NULL); - thrd_join(take_thread, NULL); + KcThread *put_thread = KcThread_new(test_queue_put_thread); + KcThread *take_thread = KcThread_new(test_queue_take_thread); + + put_thread->start(put_thread, queue); + take_thread->start(take_thread, queue); + + put_thread->join(put_thread); + take_thread->join(take_thread); + KcThread_delete(put_thread); + KcThread_delete(take_thread); KcQueue_delete(queue); } // PUT ブロックパターン { KcQueue *queue = KcQueue_new(5); - thrd_t put_thread, take_thread; - thrd_create(&put_thread, test_queue_put_thread, queue); - thrd_create(&take_thread, test_queue_take2_thread, queue); - thrd_join(put_thread, NULL); - thrd_join(take_thread, NULL); + KcThread *put_thread = KcThread_new(test_queue_put_thread); + KcThread *take_thread = KcThread_new(test_queue_take2_thread); + put_thread->start(put_thread, queue); + take_thread->start(take_thread, queue); + + put_thread->join(put_thread); + take_thread->join(take_thread); + KcThread_delete(put_thread); + KcThread_delete(take_thread); KcQueue_delete(queue); } } diff --git a/modules/test/src/test_socket.c b/modules/test/src/test_socket.c new file mode 100644 index 0000000..356882e --- /dev/null +++ b/modules/test/src/test_socket.c @@ -0,0 +1,52 @@ +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "ut.h" + +// プロトタイプ宣言 +static void test_socket_new(void); + +/** + * KcSocket 単体テストスイート + */ +void suite_socket(void) +{ + KcUt *ut = KcUt_get_instance(); + ut->add(ut, UT_TESTCASE, "socket new/delete", test_socket_new); +} + +/** + * Socket 生成/破棄。 + * + * @process KcSocket_new を実行する。。 + * @result KcSocket が生成されること。 + * + * @process KcSocket_delete にて Queue を破棄する。 + * @result Socket が破棄されること。 + */ +static void test_socket_new(void) +{ + KcSocket *server = KcSocket_new(SOCK_STREAM, AF_UNSPEC); + assert_not_null(server); + + KcSocket *client = KcSocket_new(SOCK_STREAM, AF_UNSPEC); + assert_not_null(server); + + bool ret = server->bind(server, "127.0.0.1", "5000"); + assert_true(ret); + ret = server->listen(server, 1); + assert_true(ret); + + ret = client->connect(client, "127.0.0.1", "5000", NULL, NULL); + assert_true(ret); + + KcSocket_delete(server); + KcSocket_delete(client); +} diff --git a/modules/test/src/test_thread.c b/modules/test/src/test_thread.c new file mode 100644 index 0000000..e217aea --- /dev/null +++ b/modules/test/src/test_thread.c @@ -0,0 +1,131 @@ +#include +#include + +#include +#include +#include +#include +#include + +#include "ut.h" + +// プロトタイプ宣言 +static void test_thread_new(void); +// static void test_thread_sleep(void); +static void test_thread_memory_error(void); + +/** + * KcThread 単体テストスイート + */ +void suite_thread(void) +{ + KcUt *ut = KcUt_get_instance(); + ut->add(ut, UT_TESTCASE, "thread new/delete", test_thread_new); + // ut->add(ut, UT_TESTCASE, "thread sleep", test_thread_sleep); + ut->add(ut, UT_TESTCASE, "thread memory error", test_thread_memory_error); +} + +static int pass_value = 0; +static int test_func(void *args) +{ + int *value = (int *)args; + if (*value == 1) + { + pass_value &= 0x01; + } + if (*value == 2) + { + pass_value &= 0x02; + } + for (int i = 0; i < 10; i++) + { + printf("Thread %d : %03d\n", *value, i); + KcThread_msleep(1, true); + } + return 0; +} + +/** + * Thread 生成/破棄。 + * + * @process KcThread_new を実行する。。 + * @result KcThread が生成されること。 + * + * @process KcThread_delete にて Thread を破棄する。 + * @result Thread が破棄されること。 + */ +static void test_thread_new(void) +{ + KcThread *thread_1 = KcThread_new(test_func); + KcThread *thread_2 = KcThread_new(test_func); + + pass_value = 0; + bool is_alive_1 = thread_1->is_alive(thread_1); + bool is_alive_2 = thread_2->is_alive(thread_2); + assert_false(is_alive_1); + assert_false(is_alive_2); + int val_1 = 1; + int val_2 = 2; + thread_1->start(thread_1, &val_1); + thread_2->start(thread_2, &val_2); + KcThread_msleep(10, false); + is_alive_1 = thread_1->is_alive(thread_1); + is_alive_2 = thread_2->is_alive(thread_2); + assert_true(is_alive_1); + assert_true(is_alive_2); + thread_1->join(thread_1); + thread_2->join(thread_2); + KcThread_delete(thread_1); + KcThread_delete(thread_2); +} + +// for シグナル処理 +#if 0 +static volatile int test_thread_interrupted = 0; +static void test_thread_sig_handler(int sig) +{ + test_thread_interrupted = 1; +} +int test_sleep_func(void *args) +{ + signal(SIGUSR1, test_thread_sig_handler); + int *value = (int *)args; + if (*value == 1) + { + KcThread_msleep(200, true); + } + else + { + KcThread_msleep(200, false); + } + return 0; +} + +/** + * Thread sleep 中のシグナル発生。 + */ +static void test_thread_sleep(void) +{ + + KcThread *thread = KcThread_new(test_sleep_func); + int value = 1; + test_thread_interrupted = 0; + thread->start(thread, &value); + KcThread_msleep(10, false); + + thread->join(thread); + KcThread_delete(thread); +} +#endif + +/** + * メモリ確保エラー + */ +static void test_thread_memory_error(void) +{ + ut_alloc_control(0) + { + KcThread *thread = KcThread_new(test_func); + assert_null(thread); + } +} \ No newline at end of file diff --git a/config.mk b/config.mk index a0ac27c..f7c6e47 100644 --- a/config.mk +++ b/config.mk @@ -25,14 +25,17 @@ # for Windows CROSS_COMPILE ?=i686-w64-mingw32- LIBS ?= -lws2_32 +DEBUG_OPTIONS ?= else # For Linux ifeq ($(strip $(ARCH)),aarch64) CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector else CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector endif endif diff --git a/mk/base-cmd.mk b/mk/base-cmd.mk index f429bcf..e74c114 100644 --- a/mk/base-cmd.mk +++ b/mk/base-cmd.mk @@ -33,8 +33,13 @@ CC = $(CROSS_COMPILE)gcc CXX = $(CROSS_COMPILE)g++ + #CC = $(CROSS_COMPILE)clang #CXX = $(CROSS_COMPILE)clang++ + +#CC = $(CROSS_COMPILE)clang-16 +#CXX = $(CROSS_COMPILE)clang++16 + RANLIB = $(CROSS_COMPILE)ranlib ADDR2LINE = $(CROSS_COMPILE)addr2line AR = $(CROSS_COMPILE)ar diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 2cc417c..eebded1 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -69,7 +69,7 @@ # 値が2の場合、さらなるチェックを追加するが、規格準拠のプログラムが失敗する可能性がある。 # いくつかのチェックは、コンパイル時に実行され、コンパイラの警告として表示される。 # ------------------------------------------------------------------------------ -DEBUG_OPTIONS += -fstack-protector +# DEBUG_OPTIONS += -fstack-protector DEBUG_OPTIONS += --coverage DEBUG_OPTIONS += -g3 -ggdb DEBUG_OPTIONS += -O0 diff --git a/mk/link-a-conf.mk b/mk/link-a-conf.mk index 0e872f3..631e6d5 100644 --- a/mk/link-a-conf.mk +++ b/mk/link-a-conf.mk @@ -10,8 +10,11 @@ HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) +HEADER_FILES_WIN = $(wildcard include/win/*.h) $(wildcard include/win/*.hpp) +TOP_HEADER_FILES_WIN = $(addprefix $(TOPDIR)/include/win/,$(notdir $(HEADER_FILES_WIN))) + CLEAN_FILES += $(TOP_TARGET) -CLEAN_FILES += $(TOP_HEADER_FILES) +CLEAN_FILES += $(TOP_HEADER_FILES) $(TOP_HEADER_FILES_WIN) endif endif diff --git a/mk/link-a-rule.mk b/mk/link-a-rule.mk index d94a54a..657d858 100644 --- a/mk/link-a-rule.mk +++ b/mk/link-a-rule.mk @@ -10,6 +10,7 @@ $(TOP_TARGET): $(TARGET) ifneq ($(strip $(HEADER_FILES)),) $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ + $(CP) -f $(HEADER_FILES_WIN) $(TOPDIR)/include/win/ endif $(CP) -f $(TARGET) $(TOPDIR)/lib/ diff --git a/modules/include/kc_lock_guard.h b/modules/include/kc_lock_guard.h index 8bb70bd..ad3915f 100644 --- a/modules/include/kc_lock_guard.h +++ b/modules/include/kc_lock_guard.h @@ -7,18 +7,16 @@ #define KC_LOCK_GUARD_H #include -#include - +#include /** * LockGuard 管理構造体 */ typedef struct { - mtx_t* mutex; + mtx_t *mutex; } KcLockGuard; - /** * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 * 使用例) @@ -34,8 +32,7 @@ * } */ #define kc_lock_guard(lock) \ - for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) - + for (KcLockGuard _guard = kc_lock_guard_init(lock, __FILE__, __func__, __LINE__); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 @@ -44,15 +41,13 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex); - +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line); /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard); +void kc_lock_guard_release(KcLockGuard *guard); - -#endif // KC_LOCK_GUARD_H +#endif // KC_LOCK_GUARD_H diff --git a/modules/include/kc_memory.h b/modules/include/kc_memory.h index ffce4ed..c6ba9e9 100644 --- a/modules/include/kc_memory.h +++ b/modules/include/kc_memory.h @@ -22,12 +22,13 @@ #ifndef KC_MEMORY_H #define KC_MEMORY_H -#include +#include #include #include #include #include +#include #ifdef __cplusplus extern "C" @@ -273,8 +274,6 @@ #define calloc(nmemb, size) kc_memory_manager->calloc(nmemb, size, __FILE__, __func__, __LINE__) #define realloc(ptr, size) kc_memory_manager->realloc(ptr, size, __FILE__, __func__, __LINE__) #define free(ptr) kc_memory_manager->free(ptr) -#else -#include #endif #ifdef __cplusplus diff --git a/modules/include/kc_socket.h b/modules/include/kc_socket.h new file mode 100644 index 0000000..7d952c7 --- /dev/null +++ b/modules/include/kc_socket.h @@ -0,0 +1,291 @@ +/** + * @file kc_socket.h + * @brief ソケットモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_SOCKET_H +#define KC_SOCKET_H + +#include + +#if (KC_IS_WINDOWS) +typedef SOCKET socket_t; +#define SHUT_RD SD_RECEIVE +#define SHUT_WR SD_SEND +#define SHUT_RDWR SD_BOTH +#define sockclose(sockfd) closesocket(sockfd) +#else +#include +#include +#include +#include +#include +#include +#include +typedef int socket_t; +#define INVALID_SOCKET (-1) +#define SOCKET_ERROR (-1) +#define sockclose(sockfd) close(sockfd) +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcSocket_ + { + + /** + * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr 接続先アドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_remote_addr)(struct KcSocket_ *sock, char *addr, size_t addrlen); + + /** + * ソケットの接続先ポート番号を返します。 + * 接続先ポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_remote_port)(struct KcSocket_ *sock); + + /** + * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr ローカルアドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_local_addr)(struct KcSocket_ *sock, char *addr, size_t addlen); + + /** + * ソケットのローカルポート番号を返します。 + * ローカルポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_local_port)(struct KcSocket_ *sock); + + /** + * ソケットディスクリプタを返します。 + * 通常は、ソケットディスクリプタを直接操作しないでください。 + * + * @param sock 対象ソケット + * @return ソケットディスクリプタ + */ + socket_t (*get_socket)(struct KcSocket_ *sock); + + /** + * ソケットタイプを返します。 + * + * @param sock 対象ソケット + * @return ソケットタイプ + */ + int (*get_type)(struct KcSocket_ *sock); + + /** + * ソケットファミリを返します。 + * 実際にバインド、あるいは接続されているソケットファミリを返します。 + * + * @param sock 対象ソケット + * @return ソケットファミリ + */ + int (*get_family)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービスのアドレス情報を取得します。 + * + * @param sock 対象ソケット + * @param result アドレス情報格納用ポインタ + * @param addr アドレス + * @param service サービス + * @param is_passive サーバソケットの場合、true を指定ください。 + */ + bool (*get_addrinfo)( + struct KcSocket_ *sock, + struct addrinfo **result, + const char *addr, + const char *service, + bool is_passive); + + /** + * 指定されたアドレス、サービスにバインドします。 + * 指定されたアドレスとサービスにより、複数候補がある場合は、 + * 最初に見つかったアドレスとポートにバインドします。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス (例: "80", "http", "ssh" など) + * @return true/false (bind 成功/失敗) + */ + bool (*bind)(struct KcSocket_ *sock, const char *addr, const char *service); + + /** + * ソケットを接続待ちソケットとしてマークをつけます。 + * 保留中の接続のキュー最大長を指定します。 + * + * @param sock 対象ソケット + * @param backlog バックログ + * @return true/false (成功/失敗) + */ + bool (*listen)(struct KcSocket_ *sock, int backlog); + + /** + * ソケットへの接続を受け付けます。 + * + * @param sock 対象ソケット + * @return 受け付けたソケット + */ + struct KcSocket_ *(*accept)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービス接続します。 + * local_addr に NULL を指定した場合、自動的にローカルのアドレスが設定されます。 + * local_service に -1 を指定した場合、自動的にローカルのポート番号が割りつけられます。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス + * @param local_addr ローカルアドレス + * @param local_service ローカルサービス + * @return true/false (成功/失敗) + */ + bool (*connect)(struct KcSocket_ *sock, const char *addr, const char *service, const char *local_addr, const char *local_port); + + /** + * ソケットをクローズします。 + * + * @param sock 対象ソケット + * @return true/false (成功/失敗) + */ + bool (*close)(struct KcSocket_ *sock); + + /** + * メッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 送信されたデータサイズ + */ + ssize_t (*send)(struct KcSocket_ *sock, const char *buff, size_t size, int flags); + + /** + * メッセージを受信します。 + * 受信したメッセージは指定されたバッファに格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 読み取ったデータサイズ + */ + ssize_t (*recv)(struct KcSocket_ *sock, char *buff, size_t size, int flags); + + /** + * 指定されたアドレス、サービスにメッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param addr アドレス + * @param service サービス + * @return 送信メッセージサイズ、エラー発生時は -1 + */ + ssize_t (*sendto)( + struct KcSocket_ *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service); + + /** + * メッセージを受信します。 + * src_addr, src_service がいずれも NULL 出ない場合、 + * 送信元のアドレスとサービスが格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param src_addr 送信元アドレス格納用バッファ + * @param src_addrlen 送信元アドレス格納用バッファサイズ + * @param src_service 送信元サービス格納用バッファ + * @param src_servicelen 送信元サービス格納用バッファサイズ + * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + */ + ssize_t (*recvfrom)( + struct KcSocket_ *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen); + + /** + * TTL を設定します。 + * + * @param sock 対象ソケット + * @param val 設定する TTL + * @return true/false (成功/失敗) + */ + bool (*set_ttl)(struct KcSocket_ *sock, int val); + + /** + * マルチキャストグループに参加するために JOIN します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @param ifindex インタフェースインデックス + * @return true/false (成功/失敗) + */ + bool (*join)(struct KcSocket_ *sock, const char *addr, const char *ifname, unsigned int ifindex); + + /** + * マルチキャストグループから離脱します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @return true/false (成功/失敗) + */ + bool (*leave)(struct KcSocket_ *sock, const char *addr); + + /** + * Socket 管理情報 + */ + void *_info; + + } KcSocket; + + /** + * 指定されたタイプ、ファミリのSocket オブジェクトを構築します。 + * + * @param type タイプ(SOCK_STREAM/SOCK_DGRAM/SOCK_RAW) + * @param family ファミリ(AF_UNSPEC/AF_INET/AF_INET6) + */ + KcSocket *KcSocket_new(int type, int family); + + /** + * Socket を破棄します。 + */ + void KcSocket_delete(KcSocket *socket); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_threads.h b/modules/include/kc_threads.h new file mode 100644 index 0000000..764176f --- /dev/null +++ b/modules/include/kc_threads.h @@ -0,0 +1,98 @@ +/** + * @file kc_threads.h + * @brief スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_THREADS_H +#define KC_THREADS_H + +#include + +#if (KC_IS_WINDOWS) +#include +#else +#include +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcThread_ + { + + /** + * 指定されたスレッドが生きているか否かを返します。 + * + * @param thread 対象スレッド + * @return true/false (スレッドが生存している/していない) + */ + bool (*is_alive)(struct KcThread_ *thread); + + /** + * スレッドを開始します。 + * + * @param thread 対象スレッド + * @param args スレッドに渡す情報 + * @return true/false (スレッド起動成功/失敗) + */ + bool (*start)(struct KcThread_ *thread, void *args); + + /** + * スレッドが終了するのを待機します。 + * + * @param thread 対象スレッド + */ + bool (*join)(struct KcThread_ *thread); + + /** + * Thread 管理情報 + */ + void *_info; + + } KcThread; + + /** + * スレッドを生成します。 + * + * @param run スレッドにて実行する関数へのポインタ + */ + KcThread *KcThread_new(int (*run)(void *args)); + + /** + * Thread を破棄します。 + * + * @param thread 破棄するスレッド + */ + void KcThread_delete(KcThread *thread); + + /** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープさせるミリ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_msleep(long time, bool force); + + /** + * 指定された sec 秒 + nsec ナノ秒間現在のスレッドをスリープさせます。 + * + * @param sec スリープさせる秒数 + * @param nsec スリープさせるナノ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_sleep(long sec, long nsec, bool force); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_THREADS_H diff --git a/modules/include/kc_threads_win.h b/modules/include/kc_threads_win.h new file mode 100644 index 0000000..b40ab10 --- /dev/null +++ b/modules/include/kc_threads_win.h @@ -0,0 +1,98 @@ +/** + * @file kc_windows_thread.h + * @brief Windows 用スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_WINDOWS_THREAD_H +#define KC_WINDOWS_THREAD_H + +#include +#if (KC_IS_WINDOWS) +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#define thread_local _Thread_local + + //////////////////////////////////////////////////////////////////////// + // + // Thread + // + + enum + { + thrd_success = 0, //!< 成功 + thrd_bussy = 1, //!< busy + thrd_error = 2, //!< エラー発生 + thrd_nomem = 3, //!< メモリ不足 + thrd_timedout = 4 //!< タイムアウト発生 + }; + + typedef struct + { + HANDLE handle; + DWORD thread_id; + } thrd_t; + + typedef int (*thrd_start_t)(void *); + int thrd_create(thrd_t *thr, thrd_start_t func, void *arg); + int thrd_join(thrd_t thr, int *res); + int thrd_detach(thrd_t thr); + thrd_t thrd_current(void); + int thrd_equal(thrd_t lhs, thrd_t rhs); + int thrd_sleep(const struct timespec *duration, struct timespec *remaining); + + //////////////////////////////////////////////////////////////////////// + // + // mutex + // + + enum + { + mtx_plain = 0, //!< 通常 + mtx_recursive = 1, //!< 再帰 + mtx_timed = 2 //!< timed + }; + + typedef struct + { + CRITICAL_SECTION cs; + int type; + } mtx_t; + + int mtx_init(mtx_t *mtx, int type); + void mtx_destroy(mtx_t *mtx); + int mtx_lock(mtx_t *mtx); + int mtx_unlock(mtx_t *mtx); + + //////////////////////////////////////////////////////////////////////// + // + // cond + // + + typedef struct + { + CONDITION_VARIABLE cond; + } cnd_t; + + int cnd_init(cnd_t *cond); + int cnd_signal(cnd_t *cond); + int cnd_broadcast(cnd_t *cond); + int cnd_wait(cnd_t *cond, mtx_t *mtx); + int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts); + void cnd_destroy(cnd_t *cond); + +#endif // (KC_IS_WINDOWS) +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_windows.h b/modules/include/kc_windows.h index ee70898..6e1c659 100644 --- a/modules/include/kc_windows.h +++ b/modules/include/kc_windows.h @@ -37,9 +37,9 @@ #endif // よく利用されるヘッダファイルをインクルードする -#include #include #include +#include #ifdef _MSC_VER #pragma comment(lib, "ws2_32.lib") #endif diff --git a/modules/src/kc_assert.c b/modules/src/kc_assert.c index 371fc8f..7cd4ecc 100644 --- a/modules/src/kc_assert.c +++ b/modules/src/kc_assert.c @@ -6,9 +6,9 @@ #include #include #include -#include #include #include +#include // プロトタイプ宣言 static void assert_default_handler(const char *msg); diff --git a/modules/src/kc_env.c b/modules/src/kc_env.c index 0675c73..95f185c 100644 --- a/modules/src/kc_env.c +++ b/modules/src/kc_env.c @@ -50,7 +50,7 @@ char *result = NULL; #if (KC_IS_WINDOWS) static char buff[KC_ENV_BUFFER_MAX]; - DWORD ret = GetEnvironmentVarable(key_name, (LPSTR)buff, sizeof(buff)); + DWORD ret = GetEnvironmentVariable(key_name, (LPSTR)buff, sizeof(buff)); if (ret != 0) { result = buff; diff --git a/modules/src/kc_list_array.c b/modules/src/kc_list_array.c index 246ed14..f29fc08 100644 --- a/modules/src/kc_list_array.c +++ b/modules/src/kc_list_array.c @@ -3,10 +3,14 @@ * @brief 配列リストモジュール * @copyright 2003 - 2023 Nomura Kei */ +#include #if defined(__GNUC__) #define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) #define qsort_s qsort_r #endif +#endif // defined(__GNUC__) + #include #include #include @@ -17,6 +21,7 @@ #include #include #include + /** * KcArrayList 管理情報 */ @@ -306,7 +311,11 @@ * @param context コンテキスト(KcListSortInfo) * @return 比較結果 */ +#if (KC_IS_WINDOWS) +static int KcArrayList_comparator(void *context, const void *x, const void *y) +#else static int KcArrayList_comparator(const void *x, const void *y, void *context) +#endif { KcListSortInfo *sort_info = (KcListSortInfo *)context; int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); diff --git a/modules/src/kc_list_linked.c b/modules/src/kc_list_linked.c index 30575b9..47edbe4 100644 --- a/modules/src/kc_list_linked.c +++ b/modules/src/kc_list_linked.c @@ -3,10 +3,14 @@ * @brief Linked リストモジュール * @copyright 2003 - 2023 Nomura Kei */ +#include #if defined(__GNUC__) #define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) #define qsort_s qsort_r #endif +#endif // defined(__GNUC__) + #include #include #include @@ -18,6 +22,13 @@ #include #include +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) +#define qsort_s qsort_r +#endif // KC_IS_WINDOWS +#endif // defined(__GNUC__) + /** * KcLinkedList Entry 情報 */ @@ -291,7 +302,11 @@ * @param context コンテキスト(KcListSortInfo) * @return 比較結果 */ +#if (KC_IS_WINDOWS) +static int KcLinkedList_comparator(void *context, const void *x, const void *y) +#else static int KcLinkedList_comparator(const void *x, const void *y, void *context) +#endif // KC_IS_WINDOWS { KcListSortInfo *sort_info = (KcListSortInfo *)context; const KcLinkedListEntry **entry_x = (const KcLinkedListEntry **)x; diff --git a/modules/src/kc_lock_guard.c b/modules/src/kc_lock_guard.c index 6ef68e5..648a153 100644 --- a/modules/src/kc_lock_guard.c +++ b/modules/src/kc_lock_guard.c @@ -7,7 +7,6 @@ #include - /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 * ロックに成功するとロック管理用のオブジェクトを返します。 @@ -15,36 +14,37 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex) +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line) { - KcLockGuard guard = { .mutex = mutex }; + KcLockGuard guard = {.mutex = mutex}; if (mutex == NULL) { errno = EINVAL; perror("kc_lock_guard_init: (mutex = NULL)"); + fprintf(stderr, "%s:%s:%d kc_lock_guard_init : mutex = NULL\n", file, func, line); return guard; } + errno = 0; int ret = mtx_lock(mutex); if (ret != thrd_success) { - perror("kc_lock_guard : mtx_lock error"); + fprintf(stderr, "%s:%s:%d kc_lock_guard : mtx_lock error\n", file, func, line); guard.mutex = NULL; } return guard; } - /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard) +void kc_lock_guard_release(KcLockGuard *guard) { - if (guard->mutex != NULL) { - mtx_unlock(guard->mutex); - guard->mutex = NULL; + if (guard->mutex != NULL) + { + mtx_unlock(guard->mutex); + guard->mutex = NULL; } } - diff --git a/modules/src/kc_memory.c b/modules/src/kc_memory.c index 5cc4186..6dbbc88 100644 --- a/modules/src/kc_memory.c +++ b/modules/src/kc_memory.c @@ -622,7 +622,11 @@ */ void *raw_aligned_alloc(size_t alignment, size_t size) { +#if (KC_IS_WINDOWS) + return _aligned_malloc(size, alignment); +#else return aligned_alloc(alignment, size); +#endif } /** diff --git a/modules/src/kc_memory_dump.c b/modules/src/kc_memory_dump.c index 111e328..49e63de 100644 --- a/modules/src/kc_memory_dump.c +++ b/modules/src/kc_memory_dump.c @@ -144,7 +144,7 @@ { // 空きバッファサイズ無しのため、何もしない。 return; } - int write_size = snprintf(info->write_ptr, info->rest_size, msg); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s", msg); if (write_size > info->rest_size) { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) write_size = (info->rest_size - 1); diff --git a/modules/src/kc_memory_listener.c b/modules/src/kc_memory_listener.c index 29ff2a0..ee69a13 100644 --- a/modules/src/kc_memory_listener.c +++ b/modules/src/kc_memory_listener.c @@ -98,5 +98,5 @@ KcMemoryDump_dump(buff, sizeof(buff), entry, 16, true, true, 130); fprintf(stderr, "[ERROR] %s", buff); } - fprintf(stderr, msg); + fprintf(stderr, "%s", msg); } diff --git a/modules/src/kc_queue.c b/modules/src/kc_queue.c index 544d763..d9f2a14 100644 --- a/modules/src/kc_queue.c +++ b/modules/src/kc_queue.c @@ -9,11 +9,11 @@ #include #include #include -#include #include #include #include +#include #include #include @@ -122,7 +122,10 @@ static int KcQueue_size(struct KcQueue_ *queue) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - size_t size = info->list->size(info->list); + size_t size = 0; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + size = info->list->size(info->list); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== return (int)size; } @@ -138,7 +141,11 @@ static bool KcQueue_is_empty(struct KcQueue_ *queue) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - return info->list->is_empty(info->list); + bool is_empty = true; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + is_empty = info->list->is_empty(info->list); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== + return is_empty; } // ============================================================================= @@ -155,7 +162,11 @@ static bool KcQueue_contains(struct KcQueue_ *queue, const void *element, size_t size) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - return info->list->contains(info->list, element, size); + bool is_contains = false; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + is_contains = info->list->contains(info->list, element, size); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== + return is_contains; } // ============================================================================= @@ -227,7 +238,11 @@ static void *KcQueue_peek(struct KcQueue_ *queue, size_t *size) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - return info->list->get(info->list, 0, size); + void *value = NULL; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + value = info->list->get(info->list, 0, size); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== + return value; } // ============================================================================= diff --git a/modules/src/kc_socket.c b/modules/src/kc_socket.c new file mode 100644 index 0000000..cb67996 --- /dev/null +++ b/modules/src/kc_socket.c @@ -0,0 +1,753 @@ +/** + * @file kc_socket.c + * @brief ソケットモジュール + * @copyright 2003 - 2024 Nomura Kei + */ +#include +#include + +#include +#include +#include +#include + +/** + * KcSocket 管理情報 + */ +typedef struct +{ + socket_t sock_fd; //!< ソケットディスクリプタ + int sock_type; //!< ソケットタイプ + int sock_family; //!< ソケットファミリ + struct sockaddr_storage remote_addr; //!< リモートアドレス + struct sockaddr_storage local_addr; //!< ローカルアドレス + socklen_t remote_addrlen; //!< リモートアドレス長 + socklen_t local_addrlen; //!< ローカルアドレス帳 +} KcSocketInfo; + +/** + * アドレス情報 + */ +typedef struct +{ + const char *addr; //!< アドレス + const char *service; //!< サービス +} KcSocketAddress; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static void Socket_setup(void); +static void Socket_cleanup(void); +static const char *KcSocket_get_remote_addr(KcSocket *sock, char *addr, size_t addrlen); +static int KcSocket_get_remote_port(KcSocket *sock); +static const char *KcSocket_get_local_addr(KcSocket *sock, char *addr, size_t addrlen); +static int KcSocket_get_local_port(KcSocket *sock); +static socket_t KcSocket_get_socket(KcSocket *sock); +static int KcSocket_get_type(KcSocket *sock); +static int KcSocket_get_family(KcSocket *sock); +static bool KcSocket_get_addrinfo( + KcSocket *sock, struct addrinfo **result, + const char *addr, const char *service, bool is_passive); +static bool KcSocket_bind(KcSocket *sock, const char *addr, const char *service); +static bool KcSocket_listen(KcSocket *sock, int backlog); +static KcSocket *KcSocket_accept(KcSocket *sock); +static bool KcSocket_connect( + KcSocket *sock, const char *addr, const char *service, + const char *local_addr, const char *local_service); +static bool KcSocket_close(KcSocket *sock); +static ssize_t KcSocket_send(KcSocket *sock, const char *buff, size_t size, int flags); +static ssize_t KcSocket_recv(KcSocket *sock, char *buff, size_t size, int flags); +static ssize_t KcSocket_sendto( + KcSocket *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service); +static ssize_t KcSocket_recvfrom( + KcSocket *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen); +static bool KcSocket_set_ttl(KcSocket *sock, int val); +static bool KcSocket_join(KcSocket *sock, const char *addr, const char *ifname, unsigned int ifindex); +static bool KcSocket_leave(KcSocket *sock, const char *addr); + +static bool KcSocket_addrinfo_bind_and_connect( + KcSocket *sock, struct addrinfo *bind_addrinfo, struct addrinfo *conn_addrinfo); +static bool KcSocket_addrinfo_connect(KcSocket *sock, struct addrinfo *conn_addrinfo, socket_t sockfd); + +/** + * ソケットのセットアップをします。 + */ +static void Socket_setup(void) +{ + static bool is_init = false; + if (!is_init) + { +#if (KC_IS_WINDOWS) + WSADATA wsa_data; + WSAStartup(MAKEWORD(2, 0), &wsa_data); +#endif + atexit(Socket_cleanup); + is_init = true; + } +} + +/** + * ソケットライブラリのクリーンアップをします。 + */ +static void Socket_cleanup(void) +{ +#if (KC_IS_WINDOWS) + WSACleanup(); +#endif +} + +// ============================================================================= +// new +// ============================================================================= +/** + * 指定されたタイプ、ファミリのSocket オブジェクトを構築します。 + * + * @param type タイプ(SOCK_STREAM/SOCK_DGRAM/SOCK_RAW) + * @param family ファミリ(AF_UNSPEC/AF_INET/AF_INET6) + */ +KcSocket *KcSocket_new(int type, int family) +{ + // KcSocket の管理構造 + // +----------------+ + // | KcSocket | + // | ... | + // | _info -----------+ + // +----------------+ | + // | |<--+ + // +----------------+ + Socket_setup(); + KcSocket *sock = (KcSocket *)malloc(sizeof(KcSocket) + sizeof(KcSocketInfo)); + if (sock != NULL) + { + sock->get_remote_addr = KcSocket_get_remote_addr; + sock->get_remote_port = KcSocket_get_remote_port; + sock->get_local_addr = KcSocket_get_local_addr; + sock->get_local_port = KcSocket_get_local_port; + sock->get_socket = KcSocket_get_socket; + sock->get_type = KcSocket_get_type; + sock->get_family = KcSocket_get_family; + sock->get_addrinfo = KcSocket_get_addrinfo; // for local + sock->bind = KcSocket_bind; + sock->listen = KcSocket_listen; + sock->accept = KcSocket_accept; + sock->connect = KcSocket_connect; + sock->close = KcSocket_close; + sock->send = KcSocket_send; + sock->recv = KcSocket_recv; + sock->sendto = KcSocket_sendto; + sock->recvfrom = KcSocket_recvfrom; + sock->set_ttl = KcSocket_set_ttl; + sock->join = KcSocket_join; + sock->leave = KcSocket_leave; + sock->_info = (sock + 1); + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + info->sock_fd = INVALID_SOCKET; + info->sock_type = type; + info->sock_family = family; + info->remote_addrlen = 0; + info->local_addrlen = 0; + } + return sock; +} + +// ============================================================================= +// delete +// ============================================================================= +/** + * Socket を破棄します。 + */ +void KcSocket_delete(KcSocket *socket) +{ + if (socket) + { + if (((KcSocketInfo *)socket->_info)->sock_fd != INVALID_SOCKET) + { + socket->close(socket); + } + free(socket); + } +} + +// ============================================================================= +// get_remote_addr +// ============================================================================= +/** + * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr 接続先アドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ +static const char *KcSocket_get_remote_addr(KcSocket *sock, char *addr, size_t addrlen) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->remote_addrlen) + { + int ret = getnameinfo( + (const struct sockaddr *)&info->remote_addr, + info->remote_addrlen, + addr, addrlen, NULL, 0, NI_NUMERICHOST); + if (ret == 0) + { + return addr; + } + } + return NULL; +} + +/** + * ソケットの接続先ポート番号を返します。 + * 接続先ポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ +static int KcSocket_get_remote_port(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->remote_addrlen) + { + char tmp_port[NI_MAXSERV]; + int ret = getnameinfo( + (const struct sockaddr *)&info->remote_addr, + info->remote_addrlen, + NULL, 0, tmp_port, sizeof(tmp_port), NI_NUMERICSERV); + if (ret == 0) + { + return atoi(tmp_port); + } + } + return -1; +} + +/** + * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr ローカルアドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ +static const char *KcSocket_get_local_addr(KcSocket *sock, char *addr, size_t addrlen) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->local_addrlen) + { + int ret = getnameinfo( + (const struct sockaddr *)&info->local_addr, + info->remote_addrlen, + addr, addrlen, NULL, 0, NI_NUMERICHOST); + if (ret == 0) + { + return addr; + } + } + return NULL; +} + +/** + * ソケットのローカルポート番号を返します。 + * ローカルポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ +static int KcSocket_get_local_port(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->local_addrlen) + { + char tmp_port[NI_MAXSERV]; + int ret = getnameinfo( + (const struct sockaddr *)&info->local_addr, + info->remote_addrlen, + NULL, 0, tmp_port, sizeof(tmp_port), NI_NUMERICSERV); + if (ret == 0) + { + return atoi(tmp_port); + } + } + return -1; +} + +/** + * ソケットディスクリプタを返します。 + * 通常は、ソケットディスクリプタを直接操作しないでください。 + * + * @param sock 対象ソケット + * @return ソケットディスクリプタ + */ +static socket_t KcSocket_get_socket(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + return info->sock_fd; +} + +/** + * ソケットタイプを返します。 + * + * @param sock 対象ソケット + * @return ソケットタイプ + */ +static int KcSocket_get_type(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + return info->sock_type; +} + +/** + * ソケットファミリを返します。 + * ソケット接続、バインド時には、実際にバインド、 + * あるいは接続されているソケットファミリを返します。 + * + * @param sock 対象ソケット + * @return ソケットファミリ + */ +static int KcSocket_get_family(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int result_family = info->sock_family; + if (info->sock_fd != INVALID_SOCKET) + { + struct sockaddr_storage ss; + socklen_t len = sizeof(struct sockaddr_storage); + if (getsockname(info->sock_fd, (struct sockaddr *)&ss, &len) == 0) + { + result_family = ss.ss_family; + } + } + return result_family; +} + +/** + * 指定されたアドレス、サービスのアドレス情報を取得します。 + * + * @param sock 対象ソケット + * @param result アドレス情報格納用ポインタ + * @param addr アドレス + * @param service サービス + * @param is_passive サーバソケットの場合、true を指定ください。 + */ +static bool KcSocket_get_addrinfo( + KcSocket *sock, struct addrinfo **result, + const char *addr, const char *service, bool is_passive) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + + struct addrinfo hints; + memset(&hints, 0x00, sizeof(struct addrinfo)); + hints.ai_socktype = info->sock_type; + hints.ai_family = info->sock_family; + if (is_passive) + { + hints.ai_flags = AI_PASSIVE; + } + int ret = getaddrinfo(addr, service, &hints, result); + return (ret == 0); +} + +/** + * 指定されたアドレス、サービスにバインドします。 + * 指定されたアドレスとサービスにより、複数候補がある場合は、 + * 最初に見つかったアドレスとポートにバインドします。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス (例: "80", "http", "ssh" など) + * @return true/false (bind 成功/失敗) + */ +static bool KcSocket_bind(KcSocket *sock, const char *addr, const char *service) +{ + struct addrinfo *bind_addr; + bool is_success = sock->get_addrinfo(sock, &bind_addr, addr, service, true); + if (is_success) + { + is_success = KcSocket_addrinfo_bind_and_connect(sock, bind_addr, NULL); + freeaddrinfo(bind_addr); + } + return is_success; +} + +/** + * ソケットを接続待ちソケットとしてマークをつけます。 + * 保留中の接続のキュー最大長を指定します。 + * + * @param sock 対象ソケット + * @param backlog バックログ + * @return true/false (成功/失敗) + */ +static bool KcSocket_listen(KcSocket *sock, int backlog) +{ + bool result = false; + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->sock_fd != INVALID_SOCKET) + { + int ret = listen(info->sock_fd, backlog); + result = (ret != SOCKET_ERROR); + } + return result; +} + +/** + * ソケットへの接続を受け付けます。 + * + * @param sock 対象ソケット + * @return 受け付けたソケット(失敗時NULL) + */ +static KcSocket *KcSocket_accept(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + KcSocket *client = NULL; + if (info->sock_fd != INVALID_SOCKET) + { + client = KcSocket_new(info->sock_type, info->sock_family); + if (client != NULL) + { + KcSocketInfo *cinfo = (KcSocketInfo *)client->_info; + cinfo->remote_addrlen = sizeof(struct sockaddr_storage); + cinfo->sock_fd = accept(info->sock_fd, (struct sockaddr *)&cinfo->remote_addr, &cinfo->remote_addrlen); + if (cinfo->sock_fd != INVALID_SOCKET) + { + memcpy(&cinfo->local_addr, &info->local_addr, info->local_addrlen); + cinfo->local_addrlen = info->local_addrlen; + cinfo->sock_type = SOCK_STREAM; + cinfo->sock_family = cinfo->remote_addr.ss_family; + } + else + { + KcSocket_delete(client); + client = NULL; + } + } + } + return client; +} + +/** + * 指定されたアドレス、サービス接続します。 + * local_addr に NULL を指定した場合、自動的にローカルのアドレス、サービスが設定されます。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス + * @param local_addr ローカルアドレス + * @param local_service ローカルサービス + * @return true/false (成功/失敗) + */ +static bool KcSocket_connect( + KcSocket *sock, const char *addr, const char *service, + const char *local_addr, const char *local_service) +{ + // 接続先アドレス情報取得 + struct addrinfo *conn_addr = NULL; + if (!sock->get_addrinfo(sock, &conn_addr, addr, service, false)) + { + return false; + } + + // バインドアドレス情報取得 + bool is_success = false; + struct addrinfo *bind_addr = NULL; + if (local_addr != NULL) + { // bind が必要 + is_success = sock->get_addrinfo(sock, &bind_addr, local_addr, local_service, true); + if (is_success) + { + is_success = KcSocket_addrinfo_bind_and_connect(sock, bind_addr, conn_addr); + freeaddrinfo(bind_addr); + } + } + else + { + is_success = KcSocket_addrinfo_connect(sock, conn_addr, INVALID_SOCKET); + } + freeaddrinfo(conn_addr); + return is_success; +} + +/** + * ソケットをクローズします。 + * + * @param sock 対象ソケット + * @return true/false (成功/失敗) + */ +static bool KcSocket_close(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + + if (info->sock_family == SOCK_STREAM) + { // TCP の場合は、出力を閉じて、届いているデータを読み取ってから close する。 + int ret = shutdown(info->sock_fd, SHUT_WR); + if (ret == 0) + { + int read_size; + char buff[1024]; + do + { // 届いているデータを全て読み取る + read_size = recv(info->sock_fd, buff, sizeof(buff), 0); + } while (read_size > 0); + } + } + sockclose(info->sock_fd); + return true; +} + +/** + * メッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 送信されたデータサイズ + */ +static ssize_t KcSocket_send(KcSocket *sock, const char *buff, size_t size, int flags) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + ssize_t write_size = 0; + do + { + errno = 0; + write_size = send(info->sock_fd, buff, size, flags); + } while ((write_size < 0) && (errno == EINTR)); + return write_size; +} + +/** + * メッセージを受信します。 + * 受信したメッセージは指定されたバッファに格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 読み取ったデータサイズ + */ +static ssize_t KcSocket_recv(KcSocket *sock, char *buff, size_t size, int flags) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + ssize_t read_size; + do + { + errno = 0; + read_size = recv(info->sock_fd, buff, size, flags); + } while ((read_size < 0) && (errno == EINTR)); + return read_size; +} + +/** + * 指定されたアドレス、サービスにメッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param addr アドレス + * @param service サービス + * @return 送信メッセージサイズ、エラー発生時は -1 + */ +static ssize_t KcSocket_sendto( + KcSocket *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service) +{ + (void)sock; + (void)buff; + (void)size; + (void)flags; + (void)addr; + (void)service; + return 0; +} + +/** + * メッセージを受信します。 + * src_addr, src_service がいずれも NULL 出ない場合、 + * 送信元のアドレスとサービスが格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param src_addr 送信元アドレス格納用バッファ + * @param src_addrlen 送信元アドレス格納用バッファサイズ + * @param src_service 送信元サービス格納用バッファ + * @param src_servicelen 送信元サービス格納用バッファサイズ + * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + */ +static ssize_t KcSocket_recvfrom( + KcSocket *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen) +{ + (void)sock; + (void)buff; + (void)size; + (void)flags; + (void)src_addr; + (void)src_addrlen; + (void)src_service; + (void)src_servicelen; + return 0; +} + +/** + * TTL を設定します。 + * + * @param sock 対象ソケット + * @param val 設定する TTL + * @return true/false (成功/失敗) + */ +static bool KcSocket_set_ttl(KcSocket *sock, int val) +{ + (void)sock; + (void)val; + return true; +} + +/** + * マルチキャストグループに参加するために JOIN します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @param ifindex インタフェースインデックス + * @return true/false (成功/失敗) + */ +static bool KcSocket_join( + KcSocket *sock, const char *addr, const char *ifname, unsigned int ifindex) +{ + (void)sock; + (void)addr; + (void)ifname; + (void)ifindex; + return true; +} + +/** + * マルチキャストグループから離脱します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @return true/false (成功/失敗) + */ +static bool KcSocket_leave( + KcSocket *sock, const char *addr) +{ + (void)sock; + (void)addr; + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// [内部関数] +// + +/** + * 指定された接続アドレス情報をもとにソケットの生成、接続を試みます。 + * 接続済みソケット sockfd が有効な場合、sockfd を用いて接続します。 + * + * @param sock 対象ソケット + * @param conn_addrinfo 接続アドレス情報 + * @param sockfd 接続済みソケット + * @return true/false (接続成功/失敗) + */ +static bool KcSocket_addrinfo_connect(KcSocket *sock, struct addrinfo *conn_addrinfo, socket_t sockfd) +{ + bool is_success = false; + socket_t tmp_sock = sockfd; + for (struct addrinfo *rp = conn_addrinfo; rp != NULL; rp = rp->ai_next) + { + if (sockfd == INVALID_SOCKET) + { // sockfd が無効の場合、ソケットを生成する。 + tmp_sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (tmp_sock == INVALID_SOCKET) + { + continue; + } + } + + int ret = connect(tmp_sock, rp->ai_addr, rp->ai_addrlen); + is_success = (ret != SOCKET_ERROR); + if (is_success) + { + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + info->sock_fd = tmp_sock; + info->remote_addrlen = rp->ai_addrlen; + memcpy(&info->remote_addr, rp->ai_addr, rp->ai_addrlen); + break; + } + + if (sockfd != INVALID_SOCKET) + { // sockfd が無効の場合、一時的に生成したソケットをクローズする。 + sockclose(tmp_sock); + } + } + return is_success; +} + +/** + * 指定された接続アドレス情報をもとにソケットの生成、バインドを試みます。 + * conn_addrinfo が NULL でない場合、バインド後に接続を試みます。 + * + * @param sock 対象ソケット + * @param bind_addrinfo バインドアドレス情報 + * @param conn_addrinfo 接続アドレス情報 + * @return true/false (接続成功/失敗) + */ +static bool KcSocket_addrinfo_bind_and_connect( + KcSocket *sock, struct addrinfo *bind_addrinfo, struct addrinfo *conn_addrinfo) +{ + int ret; + bool is_success = false; + + for (struct addrinfo *rp = bind_addrinfo; rp != NULL; rp = rp->ai_next) + { // sokcet : ソケット生成 + socket_t tmp_sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (tmp_sock == INVALID_SOCKET) + { + continue; + } + + // bind + const int on = 1; + setsockopt(tmp_sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on)); + ret = bind(tmp_sock, rp->ai_addr, rp->ai_addrlen); + is_success = (ret != SOCKET_ERROR); + if (!is_success) + { // bind 失敗したので次へ + sockclose(tmp_sock); + continue; + } + + // connect + if (conn_addrinfo) + { + is_success = KcSocket_addrinfo_connect(sock, conn_addrinfo, tmp_sock); + if (!is_success) + { // connect 失敗したので次へ + sockclose(tmp_sock); + continue; + } + } + + // bind または、bind と connect 成功 + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + info->sock_fd = tmp_sock; + info->local_addrlen = rp->ai_addrlen; + memcpy(&info->local_addr, rp->ai_addr, rp->ai_addrlen); + break; + } + return is_success; +} diff --git a/modules/src/kc_threads.c b/modules/src/kc_threads.c new file mode 100644 index 0000000..dd600bb --- /dev/null +++ b/modules/src/kc_threads.c @@ -0,0 +1,194 @@ +/** + * @file kc_thread.c + * @brief スレッドモジュール + * @copyright 2020 - 2024 Nomura Kei + */ +#include +#include +#include + +#include +#include +#include + +/** + * KcThread 管理情報 + */ +typedef struct +{ + thrd_t tid; + void *args; + int (*run)(void *args); + atomic_bool alive_state; +} KcThreadInfo; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static bool KcThread_is_alive(KcThread *thread); +static bool KcThread_start(KcThread *thread, void *args); +static bool KcThread_join(KcThread *thread); +static int KcThread_run(void *thread); + +// ============================================================================= +// new +// ============================================================================= +/** + * スレッドを生成します。 + * + * @param run スレッドにて実行する関数へのポインタ + */ +KcThread *KcThread_new(int (*run)(void *args)) +{ + // KcThread の管理構造 + // +--------------+ + // | KcThread | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // +--------------+ + KcThread *thread = (KcThread *)malloc(sizeof(KcThread) + sizeof(KcThreadInfo)); + if (thread != NULL) + { + thread->is_alive = KcThread_is_alive; + thread->start = KcThread_start; + thread->join = KcThread_join; + thread->_info = (thread + 1); + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + // info->tid + info->run = run; + atomic_init(&info->alive_state, false); + } + return thread; +} + +// ============================================================================= +// delete +// ============================================================================= +/** + * Thread を破棄します。 + * + * @param thread 破棄するスレッド + */ +void KcThread_delete(KcThread *thread) +{ + free(thread); +} + +// ============================================================================= +// sleep +// ============================================================================= +/** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープさせるミリ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ +bool KcThread_msleep(long time, bool force) +{ + long sec = time / 1000; + long msec = time - (sec * 1000); + long nsec = msec * 1000 * 1000; + return KcThread_sleep(sec, nsec, force); +} + +/** + * 指定された sec 秒 + nsec ナノ秒間現在のスレッドをスリープさせます。 + * + * @param sec スリープさせる秒数 + * @param nsec スリープさせるナノ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ +bool KcThread_sleep(long sec, long nsec, bool force) +{ + struct timespec remaining = {sec, nsec}; + int ret; + do + { + errno = 0; + ret = thrd_sleep(&remaining, &remaining); + } while ((ret == -1) && force && (errno == EINTR)); + return (ret == 0); +} + +// ============================================================================= +// is_alive +// ============================================================================= +/** + * 指定されたスレッドが生きているか否かを返します。 + * + * @param thread 対象スレッド + * @return true/false (スレッドが生存している/していない) + */ +static bool KcThread_is_alive(KcThread *thread) +{ + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + return atomic_load(&info->alive_state); +} + +// ============================================================================= +// start +// ============================================================================= +/** + * スレッドを開始します。 + * + * @param thread 対象スレッド + * @param args スレッドに渡す情報 + * @return true/false (スレッド起動成功/失敗) + */ +static bool KcThread_start(KcThread *thread, void *args) +{ + int ret = thrd_error; + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + if (!atomic_load(&info->alive_state)) + { + info->args = args; + ret = thrd_create(&info->tid, KcThread_run, thread); + } + return (ret == thrd_success); +} + +// ============================================================================= +// join +// ============================================================================= +/** + * スレッドが終了するのを待機します。 + * + * @param thread 対象スレッド + * @return true/false (成功/失敗) + */ +static bool KcThread_join(KcThread *thread) +{ + int ret = thrd_error; + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + ret = thrd_join(info->tid, NULL); + return (ret == thrd_success); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// 内部関数 +// + +// ============================================================================= +// run +// ============================================================================= +/** + * スレッドで実行される関数。 + * start にて関数を別スレッドにて実行します。 + * + * @param args スレッド情報 + * @return + */ +int KcThread_run(void *args) +{ + KcThread *thread = (KcThread *)args; + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + atomic_store(&info->alive_state, true); + int ret = info->run(info->args); + atomic_store(&info->alive_state, false); + return ret; +} diff --git a/modules/src/kc_threads_win.c b/modules/src/kc_threads_win.c new file mode 100644 index 0000000..0439fda --- /dev/null +++ b/modules/src/kc_threads_win.c @@ -0,0 +1,201 @@ +/** + * @file kc_thread.c + * @brief スレッドモジュール + * @copyright 2020 - 2024 Nomura Kei + */ +#include +#include + +#if (KC_IS_WINDOWS) + +int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) +{ + if ((thr == NULL) || (func == NULL)) + { + return thrd_error; + } + + thr->handle = CreateThread( + NULL, // セキュリティ属性 + 0, // スタックサイズ + (LPTHREAD_START_ROUTINE)func, // スレッド関数 + arg, // 引数 + 0, // 作成フラグ + &(thr->thread_id) // スレッドID + ); + + if (thr->handle == NULL) + { + return thrd_error; + } + return thrd_success; +} +int thrd_join(thrd_t thr, int *res) +{ + if (WaitForSingleObject(thr.handle, INFINITE) != WAIT_OBJECT_0) + { + return thrd_error; + } + if (res != NULL) + { + DWORD retcode; + if (GetExitCodeThread(thr.handle, &retcode) == 0) + { + return thrd_error; + } + *res = (int)retcode; + } + CloseHandle(thr.handle); + return thrd_success; +} + +int thrd_detach(thrd_t thr) +{ + if (thr.handle == NULL) + { + return thrd_error; + } + if (CloseHandle(thr.handle) == 0) + { + return thrd_error; + } + return thrd_success; +} + +thrd_t thrd_current(void) +{ + thrd_t current; + current.handle = GetCurrentThread(); + current.thread_id = GetThreadId(current.handle); + return current; +} + +int thrd_equal(thrd_t lhs, thrd_t rhs) +{ + return (lhs.thread_id == rhs.thread_id); +} + +void thrd_yield(void) +{ + SwitchToThread(); +} + +int thrd_sleep(const struct timespec *duration, struct timespec *remaining) +{ + if (duration == NULL) + { + return thrd_error; + } + + // Windows の Sleep は、ms 単位 + DWORD msec = (DWORD)(duration->tv_sec * 1000 + duration->tv_nsec / 1000000); + Sleep(msec); + + // 常に成功したものとして、remaining は 0 に設定する。 + if (remaining != NULL) + { + remaining->tv_sec = 0; + remaining->tv_nsec = 0; + } + return thrd_success; +} + +int mtx_init(mtx_t *mtx, int type) +{ + if (mtx == NULL) + { + return thrd_error; + } + mtx->type = type; + if (type & mtx_plain) + { + InitializeCriticalSection(&mtx->cs); + } + else if (type & mtx_recursive) + { + InitializeCriticalSection(&mtx->cs); + } + else + { + return thrd_error; + } + return thrd_success; +} + +void mtx_destroy(mtx_t *mtx) +{ + DeleteCriticalSection(&mtx->cs); +} +int mtx_lock(mtx_t *mtx) +{ + EnterCriticalSection(&mtx->cs); + return thrd_success; +} +int mtx_unlock(mtx_t *mtx) +{ + LeaveCriticalSection(&mtx->cs); + return thrd_success; +} + +int cnd_init(cnd_t *cond) +{ + if (cond == NULL) + { + return thrd_error; + } + InitializeConditionVariable(&cond->cond); + return thrd_success; +} + +int cnd_signal(cnd_t *cond) +{ + if (cond == NULL) + { + return thrd_error; + } + WakeConditionVariable(&cond->cond); + return thrd_success; +} +int cnd_broadcast(cnd_t *cond) +{ + if (cond == NULL) + { + return thrd_error; + } + WakeAllConditionVariable(&cond->cond); + return thrd_success; +} +int cnd_wait(cnd_t *cond, mtx_t *mtx) +{ + if ((cond == NULL) || (mtx == NULL)) + { + return thrd_error; + } + if (SleepConditionVariableCS(&cond->cond, &mtx->cs, INFINITE)) + { + return thrd_success; + } + return thrd_error; +} + +int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts) +{ + if ((cond == NULL) || (mtx == NULL) || (ts == NULL)) + { + return thrd_error; + } + DWORD msec = (DWORD)(ts->tv_sec * 1000 + ts->tv_nsec / 1000000); + if (SleepConditionVariableCS(&cond->cond, &mtx->cs, msec)) + { + return thrd_timedout; + } + return thrd_error; +} + +void cnd_destroy(cnd_t *cond) +{ + // Nothing to do + UNUSED_VARIABLE(cond); +} + +#endif \ No newline at end of file diff --git a/modules/test/src/test_lock_guard.c b/modules/test/src/test_lock_guard.c index f00ed0b..9c3249f 100644 --- a/modules/test/src/test_lock_guard.c +++ b/modules/test/src/test_lock_guard.c @@ -37,7 +37,7 @@ { mtx_t mutex; mtx_init(&mutex, mtx_plain | mtx_recursive); - KcLockGuard lock_guard = kc_lock_guard_init(&mutex); + KcLockGuard lock_guard = kc_lock_guard_init(&mutex, __FILE__, __func__, __LINE__); assert_not_null(lock_guard.mutex); // Lock Guard 解放 @@ -53,7 +53,7 @@ */ static void test_lock_guard_init_null(void) { - KcLockGuard lock_guard = kc_lock_guard_init(NULL); + KcLockGuard lock_guard = kc_lock_guard_init(NULL, __FILE__, __func__, __LINE__); assert_equals(EINVAL, errno); assert_null(lock_guard.mutex); } @@ -69,7 +69,7 @@ mtx_t mutex; mtx_init(&mutex, mtx_plain | mtx_recursive); mtx_destroy(&mutex); - KcLockGuard lock_guard = kc_lock_guard_init(&mutex); + KcLockGuard lock_guard = kc_lock_guard_init(&mutex, __FILE__, __func__, __LINE__); assert_null(lock_guard.mutex); } @@ -83,7 +83,7 @@ { mtx_t mutex; mtx_init(&mutex, mtx_plain | mtx_recursive); - KcLockGuard lock_guard = kc_lock_guard_init(&mutex); + KcLockGuard lock_guard = kc_lock_guard_init(&mutex, __FILE__, __func__, __LINE__); assert_not_null(lock_guard.mutex); // Lock Guard 解放 diff --git a/modules/test/src/test_queue.c b/modules/test/src/test_queue.c index cbec7e0..1595972 100644 --- a/modules/test/src/test_queue.c +++ b/modules/test/src/test_queue.c @@ -1,12 +1,12 @@ #include #include -#include #include #include #include #include #include +#include #include "ut.h" @@ -58,14 +58,11 @@ static int test_queue_offer_thread(void *arg) { KcQueue *queue = (KcQueue *)arg; - struct timespec spec; - spec.tv_sec = 0; - spec.tv_nsec = 1; for (int i = 0; i < 100; i++) { printf("offer: %d\n", i); queue->offer(queue, &i, sizeof(int)); - thrd_sleep(&spec, NULL); + KcThread_sleep(0, 1, false); } return 0; } @@ -73,9 +70,6 @@ // for poll thread static int test_queue_poll_thread(void *arg) { - struct timespec spec; - spec.tv_sec = 0; - spec.tv_nsec = 1; KcQueue *queue = (KcQueue *)arg; for (int i = 0; i < 100; i++) { @@ -100,7 +94,7 @@ { printf("poll: (null)\n"); } - thrd_sleep(&spec, NULL); + KcThread_sleep(0, 1, false); } return 0; } @@ -112,27 +106,28 @@ { KcQueue *queue = KcQueue_new(10); - thrd_t offer_thread, poll_thread; + KcThread *offer_thread = KcThread_new(test_queue_offer_thread); + KcThread *poll_thread = KcThread_new(test_queue_poll_thread); - thrd_create(&offer_thread, test_queue_offer_thread, queue); - thrd_create(&poll_thread, test_queue_poll_thread, queue); + offer_thread->start(offer_thread, queue); + poll_thread->start(poll_thread, queue); - thrd_join(offer_thread, NULL); - thrd_join(poll_thread, NULL); + offer_thread->join(offer_thread); + poll_thread->join(poll_thread); + KcQueue_delete(queue); + KcThread_delete(offer_thread); + KcThread_delete(poll_thread); } // for put thread static int test_queue_put_thread(void *arg) { KcQueue *queue = (KcQueue *)arg; - struct timespec spec; - spec.tv_sec = 0; - spec.tv_nsec = 1 * 1000; for (int i = 0; i < 100; i++) { queue->put(queue, &i, sizeof(int)); - thrd_sleep(&spec, NULL); + KcThread_sleep(0, 1000, false); } return 0; } @@ -140,9 +135,6 @@ // for take thread static int test_queue_take_thread(void *arg) { - struct timespec spec; - spec.tv_sec = 0; - spec.tv_nsec = 1; KcQueue *queue = (KcQueue *)arg; for (int i = 0; i < 100; i++) { @@ -150,7 +142,7 @@ size_t size = sizeof(int); queue->take(queue, &element, &size); assert_equals(i, element); - thrd_sleep(&spec, NULL); + KcThread_sleep(0, 1, false); } return 0; } @@ -158,9 +150,6 @@ // for take thread static int test_queue_take2_thread(void *arg) { - struct timespec spec; - spec.tv_sec = 0; - spec.tv_nsec = 100 * 1000; KcQueue *queue = (KcQueue *)arg; for (int i = 0; i < 100; i++) { @@ -168,7 +157,7 @@ size_t size = sizeof(int); queue->take(queue, &element, &size); assert_equals(i, element); - thrd_sleep(&spec, NULL); + KcThread_sleep(0, 100 * 1000, false); } return 0; } @@ -180,22 +169,31 @@ { { KcQueue *queue = KcQueue_new(0); - thrd_t put_thread, take_thread; - thrd_create(&put_thread, test_queue_put_thread, queue); - thrd_create(&take_thread, test_queue_take_thread, queue); - thrd_join(put_thread, NULL); - thrd_join(take_thread, NULL); + KcThread *put_thread = KcThread_new(test_queue_put_thread); + KcThread *take_thread = KcThread_new(test_queue_take_thread); + + put_thread->start(put_thread, queue); + take_thread->start(take_thread, queue); + + put_thread->join(put_thread); + take_thread->join(take_thread); + KcThread_delete(put_thread); + KcThread_delete(take_thread); KcQueue_delete(queue); } // PUT ブロックパターン { KcQueue *queue = KcQueue_new(5); - thrd_t put_thread, take_thread; - thrd_create(&put_thread, test_queue_put_thread, queue); - thrd_create(&take_thread, test_queue_take2_thread, queue); - thrd_join(put_thread, NULL); - thrd_join(take_thread, NULL); + KcThread *put_thread = KcThread_new(test_queue_put_thread); + KcThread *take_thread = KcThread_new(test_queue_take2_thread); + put_thread->start(put_thread, queue); + take_thread->start(take_thread, queue); + + put_thread->join(put_thread); + take_thread->join(take_thread); + KcThread_delete(put_thread); + KcThread_delete(take_thread); KcQueue_delete(queue); } } diff --git a/modules/test/src/test_socket.c b/modules/test/src/test_socket.c new file mode 100644 index 0000000..356882e --- /dev/null +++ b/modules/test/src/test_socket.c @@ -0,0 +1,52 @@ +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "ut.h" + +// プロトタイプ宣言 +static void test_socket_new(void); + +/** + * KcSocket 単体テストスイート + */ +void suite_socket(void) +{ + KcUt *ut = KcUt_get_instance(); + ut->add(ut, UT_TESTCASE, "socket new/delete", test_socket_new); +} + +/** + * Socket 生成/破棄。 + * + * @process KcSocket_new を実行する。。 + * @result KcSocket が生成されること。 + * + * @process KcSocket_delete にて Queue を破棄する。 + * @result Socket が破棄されること。 + */ +static void test_socket_new(void) +{ + KcSocket *server = KcSocket_new(SOCK_STREAM, AF_UNSPEC); + assert_not_null(server); + + KcSocket *client = KcSocket_new(SOCK_STREAM, AF_UNSPEC); + assert_not_null(server); + + bool ret = server->bind(server, "127.0.0.1", "5000"); + assert_true(ret); + ret = server->listen(server, 1); + assert_true(ret); + + ret = client->connect(client, "127.0.0.1", "5000", NULL, NULL); + assert_true(ret); + + KcSocket_delete(server); + KcSocket_delete(client); +} diff --git a/modules/test/src/test_thread.c b/modules/test/src/test_thread.c new file mode 100644 index 0000000..e217aea --- /dev/null +++ b/modules/test/src/test_thread.c @@ -0,0 +1,131 @@ +#include +#include + +#include +#include +#include +#include +#include + +#include "ut.h" + +// プロトタイプ宣言 +static void test_thread_new(void); +// static void test_thread_sleep(void); +static void test_thread_memory_error(void); + +/** + * KcThread 単体テストスイート + */ +void suite_thread(void) +{ + KcUt *ut = KcUt_get_instance(); + ut->add(ut, UT_TESTCASE, "thread new/delete", test_thread_new); + // ut->add(ut, UT_TESTCASE, "thread sleep", test_thread_sleep); + ut->add(ut, UT_TESTCASE, "thread memory error", test_thread_memory_error); +} + +static int pass_value = 0; +static int test_func(void *args) +{ + int *value = (int *)args; + if (*value == 1) + { + pass_value &= 0x01; + } + if (*value == 2) + { + pass_value &= 0x02; + } + for (int i = 0; i < 10; i++) + { + printf("Thread %d : %03d\n", *value, i); + KcThread_msleep(1, true); + } + return 0; +} + +/** + * Thread 生成/破棄。 + * + * @process KcThread_new を実行する。。 + * @result KcThread が生成されること。 + * + * @process KcThread_delete にて Thread を破棄する。 + * @result Thread が破棄されること。 + */ +static void test_thread_new(void) +{ + KcThread *thread_1 = KcThread_new(test_func); + KcThread *thread_2 = KcThread_new(test_func); + + pass_value = 0; + bool is_alive_1 = thread_1->is_alive(thread_1); + bool is_alive_2 = thread_2->is_alive(thread_2); + assert_false(is_alive_1); + assert_false(is_alive_2); + int val_1 = 1; + int val_2 = 2; + thread_1->start(thread_1, &val_1); + thread_2->start(thread_2, &val_2); + KcThread_msleep(10, false); + is_alive_1 = thread_1->is_alive(thread_1); + is_alive_2 = thread_2->is_alive(thread_2); + assert_true(is_alive_1); + assert_true(is_alive_2); + thread_1->join(thread_1); + thread_2->join(thread_2); + KcThread_delete(thread_1); + KcThread_delete(thread_2); +} + +// for シグナル処理 +#if 0 +static volatile int test_thread_interrupted = 0; +static void test_thread_sig_handler(int sig) +{ + test_thread_interrupted = 1; +} +int test_sleep_func(void *args) +{ + signal(SIGUSR1, test_thread_sig_handler); + int *value = (int *)args; + if (*value == 1) + { + KcThread_msleep(200, true); + } + else + { + KcThread_msleep(200, false); + } + return 0; +} + +/** + * Thread sleep 中のシグナル発生。 + */ +static void test_thread_sleep(void) +{ + + KcThread *thread = KcThread_new(test_sleep_func); + int value = 1; + test_thread_interrupted = 0; + thread->start(thread, &value); + KcThread_msleep(10, false); + + thread->join(thread); + KcThread_delete(thread); +} +#endif + +/** + * メモリ確保エラー + */ +static void test_thread_memory_error(void) +{ + ut_alloc_control(0) + { + KcThread *thread = KcThread_new(test_func); + assert_null(thread); + } +} \ No newline at end of file diff --git a/modules/test/src/ut.c b/modules/test/src/ut.c index b921513..2a4e0d2 100644 --- a/modules/test/src/ut.c +++ b/modules/test/src/ut.c @@ -8,6 +8,7 @@ int main(void) { // UT Setup + suite_assert(); suite_dl(); suite_env(); @@ -21,6 +22,8 @@ suite_memory_mark(); suite_memory(); suite_queue(); + // suite_socket(); + suite_thread(); KcMemory_start(false); KcMemory_start(true); diff --git a/config.mk b/config.mk index a0ac27c..f7c6e47 100644 --- a/config.mk +++ b/config.mk @@ -25,14 +25,17 @@ # for Windows CROSS_COMPILE ?=i686-w64-mingw32- LIBS ?= -lws2_32 +DEBUG_OPTIONS ?= else # For Linux ifeq ($(strip $(ARCH)),aarch64) CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector else CROSS_COMPILE ?= LIBS ?= -lpthread -lrt +DEBUG_OPTIONS ?= -fstack-protector endif endif diff --git a/mk/base-cmd.mk b/mk/base-cmd.mk index f429bcf..e74c114 100644 --- a/mk/base-cmd.mk +++ b/mk/base-cmd.mk @@ -33,8 +33,13 @@ CC = $(CROSS_COMPILE)gcc CXX = $(CROSS_COMPILE)g++ + #CC = $(CROSS_COMPILE)clang #CXX = $(CROSS_COMPILE)clang++ + +#CC = $(CROSS_COMPILE)clang-16 +#CXX = $(CROSS_COMPILE)clang++16 + RANLIB = $(CROSS_COMPILE)ranlib ADDR2LINE = $(CROSS_COMPILE)addr2line AR = $(CROSS_COMPILE)ar diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 2cc417c..eebded1 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -69,7 +69,7 @@ # 値が2の場合、さらなるチェックを追加するが、規格準拠のプログラムが失敗する可能性がある。 # いくつかのチェックは、コンパイル時に実行され、コンパイラの警告として表示される。 # ------------------------------------------------------------------------------ -DEBUG_OPTIONS += -fstack-protector +# DEBUG_OPTIONS += -fstack-protector DEBUG_OPTIONS += --coverage DEBUG_OPTIONS += -g3 -ggdb DEBUG_OPTIONS += -O0 diff --git a/mk/link-a-conf.mk b/mk/link-a-conf.mk index 0e872f3..631e6d5 100644 --- a/mk/link-a-conf.mk +++ b/mk/link-a-conf.mk @@ -10,8 +10,11 @@ HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) +HEADER_FILES_WIN = $(wildcard include/win/*.h) $(wildcard include/win/*.hpp) +TOP_HEADER_FILES_WIN = $(addprefix $(TOPDIR)/include/win/,$(notdir $(HEADER_FILES_WIN))) + CLEAN_FILES += $(TOP_TARGET) -CLEAN_FILES += $(TOP_HEADER_FILES) +CLEAN_FILES += $(TOP_HEADER_FILES) $(TOP_HEADER_FILES_WIN) endif endif diff --git a/mk/link-a-rule.mk b/mk/link-a-rule.mk index d94a54a..657d858 100644 --- a/mk/link-a-rule.mk +++ b/mk/link-a-rule.mk @@ -10,6 +10,7 @@ $(TOP_TARGET): $(TARGET) ifneq ($(strip $(HEADER_FILES)),) $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ + $(CP) -f $(HEADER_FILES_WIN) $(TOPDIR)/include/win/ endif $(CP) -f $(TARGET) $(TOPDIR)/lib/ diff --git a/modules/include/kc_lock_guard.h b/modules/include/kc_lock_guard.h index 8bb70bd..ad3915f 100644 --- a/modules/include/kc_lock_guard.h +++ b/modules/include/kc_lock_guard.h @@ -7,18 +7,16 @@ #define KC_LOCK_GUARD_H #include -#include - +#include /** * LockGuard 管理構造体 */ typedef struct { - mtx_t* mutex; + mtx_t *mutex; } KcLockGuard; - /** * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 * 使用例) @@ -34,8 +32,7 @@ * } */ #define kc_lock_guard(lock) \ - for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) - + for (KcLockGuard _guard = kc_lock_guard_init(lock, __FILE__, __func__, __LINE__); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 @@ -44,15 +41,13 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex); - +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line); /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard); +void kc_lock_guard_release(KcLockGuard *guard); - -#endif // KC_LOCK_GUARD_H +#endif // KC_LOCK_GUARD_H diff --git a/modules/include/kc_memory.h b/modules/include/kc_memory.h index ffce4ed..c6ba9e9 100644 --- a/modules/include/kc_memory.h +++ b/modules/include/kc_memory.h @@ -22,12 +22,13 @@ #ifndef KC_MEMORY_H #define KC_MEMORY_H -#include +#include #include #include #include #include +#include #ifdef __cplusplus extern "C" @@ -273,8 +274,6 @@ #define calloc(nmemb, size) kc_memory_manager->calloc(nmemb, size, __FILE__, __func__, __LINE__) #define realloc(ptr, size) kc_memory_manager->realloc(ptr, size, __FILE__, __func__, __LINE__) #define free(ptr) kc_memory_manager->free(ptr) -#else -#include #endif #ifdef __cplusplus diff --git a/modules/include/kc_socket.h b/modules/include/kc_socket.h new file mode 100644 index 0000000..7d952c7 --- /dev/null +++ b/modules/include/kc_socket.h @@ -0,0 +1,291 @@ +/** + * @file kc_socket.h + * @brief ソケットモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_SOCKET_H +#define KC_SOCKET_H + +#include + +#if (KC_IS_WINDOWS) +typedef SOCKET socket_t; +#define SHUT_RD SD_RECEIVE +#define SHUT_WR SD_SEND +#define SHUT_RDWR SD_BOTH +#define sockclose(sockfd) closesocket(sockfd) +#else +#include +#include +#include +#include +#include +#include +#include +typedef int socket_t; +#define INVALID_SOCKET (-1) +#define SOCKET_ERROR (-1) +#define sockclose(sockfd) close(sockfd) +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcSocket_ + { + + /** + * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr 接続先アドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_remote_addr)(struct KcSocket_ *sock, char *addr, size_t addrlen); + + /** + * ソケットの接続先ポート番号を返します。 + * 接続先ポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_remote_port)(struct KcSocket_ *sock); + + /** + * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr ローカルアドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ + const char *(*get_local_addr)(struct KcSocket_ *sock, char *addr, size_t addlen); + + /** + * ソケットのローカルポート番号を返します。 + * ローカルポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ + int (*get_local_port)(struct KcSocket_ *sock); + + /** + * ソケットディスクリプタを返します。 + * 通常は、ソケットディスクリプタを直接操作しないでください。 + * + * @param sock 対象ソケット + * @return ソケットディスクリプタ + */ + socket_t (*get_socket)(struct KcSocket_ *sock); + + /** + * ソケットタイプを返します。 + * + * @param sock 対象ソケット + * @return ソケットタイプ + */ + int (*get_type)(struct KcSocket_ *sock); + + /** + * ソケットファミリを返します。 + * 実際にバインド、あるいは接続されているソケットファミリを返します。 + * + * @param sock 対象ソケット + * @return ソケットファミリ + */ + int (*get_family)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービスのアドレス情報を取得します。 + * + * @param sock 対象ソケット + * @param result アドレス情報格納用ポインタ + * @param addr アドレス + * @param service サービス + * @param is_passive サーバソケットの場合、true を指定ください。 + */ + bool (*get_addrinfo)( + struct KcSocket_ *sock, + struct addrinfo **result, + const char *addr, + const char *service, + bool is_passive); + + /** + * 指定されたアドレス、サービスにバインドします。 + * 指定されたアドレスとサービスにより、複数候補がある場合は、 + * 最初に見つかったアドレスとポートにバインドします。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス (例: "80", "http", "ssh" など) + * @return true/false (bind 成功/失敗) + */ + bool (*bind)(struct KcSocket_ *sock, const char *addr, const char *service); + + /** + * ソケットを接続待ちソケットとしてマークをつけます。 + * 保留中の接続のキュー最大長を指定します。 + * + * @param sock 対象ソケット + * @param backlog バックログ + * @return true/false (成功/失敗) + */ + bool (*listen)(struct KcSocket_ *sock, int backlog); + + /** + * ソケットへの接続を受け付けます。 + * + * @param sock 対象ソケット + * @return 受け付けたソケット + */ + struct KcSocket_ *(*accept)(struct KcSocket_ *sock); + + /** + * 指定されたアドレス、サービス接続します。 + * local_addr に NULL を指定した場合、自動的にローカルのアドレスが設定されます。 + * local_service に -1 を指定した場合、自動的にローカルのポート番号が割りつけられます。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス + * @param local_addr ローカルアドレス + * @param local_service ローカルサービス + * @return true/false (成功/失敗) + */ + bool (*connect)(struct KcSocket_ *sock, const char *addr, const char *service, const char *local_addr, const char *local_port); + + /** + * ソケットをクローズします。 + * + * @param sock 対象ソケット + * @return true/false (成功/失敗) + */ + bool (*close)(struct KcSocket_ *sock); + + /** + * メッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 送信されたデータサイズ + */ + ssize_t (*send)(struct KcSocket_ *sock, const char *buff, size_t size, int flags); + + /** + * メッセージを受信します。 + * 受信したメッセージは指定されたバッファに格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 読み取ったデータサイズ + */ + ssize_t (*recv)(struct KcSocket_ *sock, char *buff, size_t size, int flags); + + /** + * 指定されたアドレス、サービスにメッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param addr アドレス + * @param service サービス + * @return 送信メッセージサイズ、エラー発生時は -1 + */ + ssize_t (*sendto)( + struct KcSocket_ *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service); + + /** + * メッセージを受信します。 + * src_addr, src_service がいずれも NULL 出ない場合、 + * 送信元のアドレスとサービスが格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param src_addr 送信元アドレス格納用バッファ + * @param src_addrlen 送信元アドレス格納用バッファサイズ + * @param src_service 送信元サービス格納用バッファ + * @param src_servicelen 送信元サービス格納用バッファサイズ + * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + */ + ssize_t (*recvfrom)( + struct KcSocket_ *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen); + + /** + * TTL を設定します。 + * + * @param sock 対象ソケット + * @param val 設定する TTL + * @return true/false (成功/失敗) + */ + bool (*set_ttl)(struct KcSocket_ *sock, int val); + + /** + * マルチキャストグループに参加するために JOIN します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @param ifindex インタフェースインデックス + * @return true/false (成功/失敗) + */ + bool (*join)(struct KcSocket_ *sock, const char *addr, const char *ifname, unsigned int ifindex); + + /** + * マルチキャストグループから離脱します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @return true/false (成功/失敗) + */ + bool (*leave)(struct KcSocket_ *sock, const char *addr); + + /** + * Socket 管理情報 + */ + void *_info; + + } KcSocket; + + /** + * 指定されたタイプ、ファミリのSocket オブジェクトを構築します。 + * + * @param type タイプ(SOCK_STREAM/SOCK_DGRAM/SOCK_RAW) + * @param family ファミリ(AF_UNSPEC/AF_INET/AF_INET6) + */ + KcSocket *KcSocket_new(int type, int family); + + /** + * Socket を破棄します。 + */ + void KcSocket_delete(KcSocket *socket); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_threads.h b/modules/include/kc_threads.h new file mode 100644 index 0000000..764176f --- /dev/null +++ b/modules/include/kc_threads.h @@ -0,0 +1,98 @@ +/** + * @file kc_threads.h + * @brief スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_THREADS_H +#define KC_THREADS_H + +#include + +#if (KC_IS_WINDOWS) +#include +#else +#include +#endif // KC_IS_WINDOWS + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + typedef struct KcThread_ + { + + /** + * 指定されたスレッドが生きているか否かを返します。 + * + * @param thread 対象スレッド + * @return true/false (スレッドが生存している/していない) + */ + bool (*is_alive)(struct KcThread_ *thread); + + /** + * スレッドを開始します。 + * + * @param thread 対象スレッド + * @param args スレッドに渡す情報 + * @return true/false (スレッド起動成功/失敗) + */ + bool (*start)(struct KcThread_ *thread, void *args); + + /** + * スレッドが終了するのを待機します。 + * + * @param thread 対象スレッド + */ + bool (*join)(struct KcThread_ *thread); + + /** + * Thread 管理情報 + */ + void *_info; + + } KcThread; + + /** + * スレッドを生成します。 + * + * @param run スレッドにて実行する関数へのポインタ + */ + KcThread *KcThread_new(int (*run)(void *args)); + + /** + * Thread を破棄します。 + * + * @param thread 破棄するスレッド + */ + void KcThread_delete(KcThread *thread); + + /** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープさせるミリ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_msleep(long time, bool force); + + /** + * 指定された sec 秒 + nsec ナノ秒間現在のスレッドをスリープさせます。 + * + * @param sec スリープさせる秒数 + * @param nsec スリープさせるナノ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ + bool KcThread_sleep(long sec, long nsec, bool force); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_THREADS_H diff --git a/modules/include/kc_threads_win.h b/modules/include/kc_threads_win.h new file mode 100644 index 0000000..b40ab10 --- /dev/null +++ b/modules/include/kc_threads_win.h @@ -0,0 +1,98 @@ +/** + * @file kc_windows_thread.h + * @brief Windows 用スレッドモジュールヘッダファイル + * @copyright 2020 - 2024 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_WINDOWS_THREAD_H +#define KC_WINDOWS_THREAD_H + +#include +#if (KC_IS_WINDOWS) +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#define thread_local _Thread_local + + //////////////////////////////////////////////////////////////////////// + // + // Thread + // + + enum + { + thrd_success = 0, //!< 成功 + thrd_bussy = 1, //!< busy + thrd_error = 2, //!< エラー発生 + thrd_nomem = 3, //!< メモリ不足 + thrd_timedout = 4 //!< タイムアウト発生 + }; + + typedef struct + { + HANDLE handle; + DWORD thread_id; + } thrd_t; + + typedef int (*thrd_start_t)(void *); + int thrd_create(thrd_t *thr, thrd_start_t func, void *arg); + int thrd_join(thrd_t thr, int *res); + int thrd_detach(thrd_t thr); + thrd_t thrd_current(void); + int thrd_equal(thrd_t lhs, thrd_t rhs); + int thrd_sleep(const struct timespec *duration, struct timespec *remaining); + + //////////////////////////////////////////////////////////////////////// + // + // mutex + // + + enum + { + mtx_plain = 0, //!< 通常 + mtx_recursive = 1, //!< 再帰 + mtx_timed = 2 //!< timed + }; + + typedef struct + { + CRITICAL_SECTION cs; + int type; + } mtx_t; + + int mtx_init(mtx_t *mtx, int type); + void mtx_destroy(mtx_t *mtx); + int mtx_lock(mtx_t *mtx); + int mtx_unlock(mtx_t *mtx); + + //////////////////////////////////////////////////////////////////////// + // + // cond + // + + typedef struct + { + CONDITION_VARIABLE cond; + } cnd_t; + + int cnd_init(cnd_t *cond); + int cnd_signal(cnd_t *cond); + int cnd_broadcast(cnd_t *cond); + int cnd_wait(cnd_t *cond, mtx_t *mtx); + int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts); + void cnd_destroy(cnd_t *cond); + +#endif // (KC_IS_WINDOWS) +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_SOCKET_H diff --git a/modules/include/kc_windows.h b/modules/include/kc_windows.h index ee70898..6e1c659 100644 --- a/modules/include/kc_windows.h +++ b/modules/include/kc_windows.h @@ -37,9 +37,9 @@ #endif // よく利用されるヘッダファイルをインクルードする -#include #include #include +#include #ifdef _MSC_VER #pragma comment(lib, "ws2_32.lib") #endif diff --git a/modules/src/kc_assert.c b/modules/src/kc_assert.c index 371fc8f..7cd4ecc 100644 --- a/modules/src/kc_assert.c +++ b/modules/src/kc_assert.c @@ -6,9 +6,9 @@ #include #include #include -#include #include #include +#include // プロトタイプ宣言 static void assert_default_handler(const char *msg); diff --git a/modules/src/kc_env.c b/modules/src/kc_env.c index 0675c73..95f185c 100644 --- a/modules/src/kc_env.c +++ b/modules/src/kc_env.c @@ -50,7 +50,7 @@ char *result = NULL; #if (KC_IS_WINDOWS) static char buff[KC_ENV_BUFFER_MAX]; - DWORD ret = GetEnvironmentVarable(key_name, (LPSTR)buff, sizeof(buff)); + DWORD ret = GetEnvironmentVariable(key_name, (LPSTR)buff, sizeof(buff)); if (ret != 0) { result = buff; diff --git a/modules/src/kc_list_array.c b/modules/src/kc_list_array.c index 246ed14..f29fc08 100644 --- a/modules/src/kc_list_array.c +++ b/modules/src/kc_list_array.c @@ -3,10 +3,14 @@ * @brief 配列リストモジュール * @copyright 2003 - 2023 Nomura Kei */ +#include #if defined(__GNUC__) #define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) #define qsort_s qsort_r #endif +#endif // defined(__GNUC__) + #include #include #include @@ -17,6 +21,7 @@ #include #include #include + /** * KcArrayList 管理情報 */ @@ -306,7 +311,11 @@ * @param context コンテキスト(KcListSortInfo) * @return 比較結果 */ +#if (KC_IS_WINDOWS) +static int KcArrayList_comparator(void *context, const void *x, const void *y) +#else static int KcArrayList_comparator(const void *x, const void *y, void *context) +#endif { KcListSortInfo *sort_info = (KcListSortInfo *)context; int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); diff --git a/modules/src/kc_list_linked.c b/modules/src/kc_list_linked.c index 30575b9..47edbe4 100644 --- a/modules/src/kc_list_linked.c +++ b/modules/src/kc_list_linked.c @@ -3,10 +3,14 @@ * @brief Linked リストモジュール * @copyright 2003 - 2023 Nomura Kei */ +#include #if defined(__GNUC__) #define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) #define qsort_s qsort_r #endif +#endif // defined(__GNUC__) + #include #include #include @@ -18,6 +22,13 @@ #include #include +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#if !(KC_IS_WINDOWS) +#define qsort_s qsort_r +#endif // KC_IS_WINDOWS +#endif // defined(__GNUC__) + /** * KcLinkedList Entry 情報 */ @@ -291,7 +302,11 @@ * @param context コンテキスト(KcListSortInfo) * @return 比較結果 */ +#if (KC_IS_WINDOWS) +static int KcLinkedList_comparator(void *context, const void *x, const void *y) +#else static int KcLinkedList_comparator(const void *x, const void *y, void *context) +#endif // KC_IS_WINDOWS { KcListSortInfo *sort_info = (KcListSortInfo *)context; const KcLinkedListEntry **entry_x = (const KcLinkedListEntry **)x; diff --git a/modules/src/kc_lock_guard.c b/modules/src/kc_lock_guard.c index 6ef68e5..648a153 100644 --- a/modules/src/kc_lock_guard.c +++ b/modules/src/kc_lock_guard.c @@ -7,7 +7,6 @@ #include - /** * 指定された mutex がロックされるまで現在のスレッドをロックします。 * ロックに成功するとロック管理用のオブジェクトを返します。 @@ -15,36 +14,37 @@ * @param mutex mutex * @return ロック管理用オブジェクト */ -KcLockGuard kc_lock_guard_init(mtx_t* mutex) +KcLockGuard kc_lock_guard_init(mtx_t *mutex, const char *file, const char *func, int line) { - KcLockGuard guard = { .mutex = mutex }; + KcLockGuard guard = {.mutex = mutex}; if (mutex == NULL) { errno = EINVAL; perror("kc_lock_guard_init: (mutex = NULL)"); + fprintf(stderr, "%s:%s:%d kc_lock_guard_init : mutex = NULL\n", file, func, line); return guard; } + errno = 0; int ret = mtx_lock(mutex); if (ret != thrd_success) { - perror("kc_lock_guard : mtx_lock error"); + fprintf(stderr, "%s:%s:%d kc_lock_guard : mtx_lock error\n", file, func, line); guard.mutex = NULL; } return guard; } - /** * 指定されたロック管理用オブジェクトのロックを解除します。 * * @param guard ロック管理用オブジェクト */ -void kc_lock_guard_release(KcLockGuard* guard) +void kc_lock_guard_release(KcLockGuard *guard) { - if (guard->mutex != NULL) { - mtx_unlock(guard->mutex); - guard->mutex = NULL; + if (guard->mutex != NULL) + { + mtx_unlock(guard->mutex); + guard->mutex = NULL; } } - diff --git a/modules/src/kc_memory.c b/modules/src/kc_memory.c index 5cc4186..6dbbc88 100644 --- a/modules/src/kc_memory.c +++ b/modules/src/kc_memory.c @@ -622,7 +622,11 @@ */ void *raw_aligned_alloc(size_t alignment, size_t size) { +#if (KC_IS_WINDOWS) + return _aligned_malloc(size, alignment); +#else return aligned_alloc(alignment, size); +#endif } /** diff --git a/modules/src/kc_memory_dump.c b/modules/src/kc_memory_dump.c index 111e328..49e63de 100644 --- a/modules/src/kc_memory_dump.c +++ b/modules/src/kc_memory_dump.c @@ -144,7 +144,7 @@ { // 空きバッファサイズ無しのため、何もしない。 return; } - int write_size = snprintf(info->write_ptr, info->rest_size, msg); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s", msg); if (write_size > info->rest_size) { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) write_size = (info->rest_size - 1); diff --git a/modules/src/kc_memory_listener.c b/modules/src/kc_memory_listener.c index 29ff2a0..ee69a13 100644 --- a/modules/src/kc_memory_listener.c +++ b/modules/src/kc_memory_listener.c @@ -98,5 +98,5 @@ KcMemoryDump_dump(buff, sizeof(buff), entry, 16, true, true, 130); fprintf(stderr, "[ERROR] %s", buff); } - fprintf(stderr, msg); + fprintf(stderr, "%s", msg); } diff --git a/modules/src/kc_queue.c b/modules/src/kc_queue.c index 544d763..d9f2a14 100644 --- a/modules/src/kc_queue.c +++ b/modules/src/kc_queue.c @@ -9,11 +9,11 @@ #include #include #include -#include #include #include #include +#include #include #include @@ -122,7 +122,10 @@ static int KcQueue_size(struct KcQueue_ *queue) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - size_t size = info->list->size(info->list); + size_t size = 0; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + size = info->list->size(info->list); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== return (int)size; } @@ -138,7 +141,11 @@ static bool KcQueue_is_empty(struct KcQueue_ *queue) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - return info->list->is_empty(info->list); + bool is_empty = true; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + is_empty = info->list->is_empty(info->list); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== + return is_empty; } // ============================================================================= @@ -155,7 +162,11 @@ static bool KcQueue_contains(struct KcQueue_ *queue, const void *element, size_t size) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - return info->list->contains(info->list, element, size); + bool is_contains = false; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + is_contains = info->list->contains(info->list, element, size); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== + return is_contains; } // ============================================================================= @@ -227,7 +238,11 @@ static void *KcQueue_peek(struct KcQueue_ *queue, size_t *size) { KcQueueInfo *info = (KcQueueInfo *)queue->_info; - return info->list->get(info->list, 0, size); + void *value = NULL; + mtx_lock(&(info->mutex)); // ===== Lock Start =============== + value = info->list->get(info->list, 0, size); + mtx_unlock(&(info->mutex)); // ===== Lock End =============== + return value; } // ============================================================================= diff --git a/modules/src/kc_socket.c b/modules/src/kc_socket.c new file mode 100644 index 0000000..cb67996 --- /dev/null +++ b/modules/src/kc_socket.c @@ -0,0 +1,753 @@ +/** + * @file kc_socket.c + * @brief ソケットモジュール + * @copyright 2003 - 2024 Nomura Kei + */ +#include +#include + +#include +#include +#include +#include + +/** + * KcSocket 管理情報 + */ +typedef struct +{ + socket_t sock_fd; //!< ソケットディスクリプタ + int sock_type; //!< ソケットタイプ + int sock_family; //!< ソケットファミリ + struct sockaddr_storage remote_addr; //!< リモートアドレス + struct sockaddr_storage local_addr; //!< ローカルアドレス + socklen_t remote_addrlen; //!< リモートアドレス長 + socklen_t local_addrlen; //!< ローカルアドレス帳 +} KcSocketInfo; + +/** + * アドレス情報 + */ +typedef struct +{ + const char *addr; //!< アドレス + const char *service; //!< サービス +} KcSocketAddress; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static void Socket_setup(void); +static void Socket_cleanup(void); +static const char *KcSocket_get_remote_addr(KcSocket *sock, char *addr, size_t addrlen); +static int KcSocket_get_remote_port(KcSocket *sock); +static const char *KcSocket_get_local_addr(KcSocket *sock, char *addr, size_t addrlen); +static int KcSocket_get_local_port(KcSocket *sock); +static socket_t KcSocket_get_socket(KcSocket *sock); +static int KcSocket_get_type(KcSocket *sock); +static int KcSocket_get_family(KcSocket *sock); +static bool KcSocket_get_addrinfo( + KcSocket *sock, struct addrinfo **result, + const char *addr, const char *service, bool is_passive); +static bool KcSocket_bind(KcSocket *sock, const char *addr, const char *service); +static bool KcSocket_listen(KcSocket *sock, int backlog); +static KcSocket *KcSocket_accept(KcSocket *sock); +static bool KcSocket_connect( + KcSocket *sock, const char *addr, const char *service, + const char *local_addr, const char *local_service); +static bool KcSocket_close(KcSocket *sock); +static ssize_t KcSocket_send(KcSocket *sock, const char *buff, size_t size, int flags); +static ssize_t KcSocket_recv(KcSocket *sock, char *buff, size_t size, int flags); +static ssize_t KcSocket_sendto( + KcSocket *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service); +static ssize_t KcSocket_recvfrom( + KcSocket *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen); +static bool KcSocket_set_ttl(KcSocket *sock, int val); +static bool KcSocket_join(KcSocket *sock, const char *addr, const char *ifname, unsigned int ifindex); +static bool KcSocket_leave(KcSocket *sock, const char *addr); + +static bool KcSocket_addrinfo_bind_and_connect( + KcSocket *sock, struct addrinfo *bind_addrinfo, struct addrinfo *conn_addrinfo); +static bool KcSocket_addrinfo_connect(KcSocket *sock, struct addrinfo *conn_addrinfo, socket_t sockfd); + +/** + * ソケットのセットアップをします。 + */ +static void Socket_setup(void) +{ + static bool is_init = false; + if (!is_init) + { +#if (KC_IS_WINDOWS) + WSADATA wsa_data; + WSAStartup(MAKEWORD(2, 0), &wsa_data); +#endif + atexit(Socket_cleanup); + is_init = true; + } +} + +/** + * ソケットライブラリのクリーンアップをします。 + */ +static void Socket_cleanup(void) +{ +#if (KC_IS_WINDOWS) + WSACleanup(); +#endif +} + +// ============================================================================= +// new +// ============================================================================= +/** + * 指定されたタイプ、ファミリのSocket オブジェクトを構築します。 + * + * @param type タイプ(SOCK_STREAM/SOCK_DGRAM/SOCK_RAW) + * @param family ファミリ(AF_UNSPEC/AF_INET/AF_INET6) + */ +KcSocket *KcSocket_new(int type, int family) +{ + // KcSocket の管理構造 + // +----------------+ + // | KcSocket | + // | ... | + // | _info -----------+ + // +----------------+ | + // | |<--+ + // +----------------+ + Socket_setup(); + KcSocket *sock = (KcSocket *)malloc(sizeof(KcSocket) + sizeof(KcSocketInfo)); + if (sock != NULL) + { + sock->get_remote_addr = KcSocket_get_remote_addr; + sock->get_remote_port = KcSocket_get_remote_port; + sock->get_local_addr = KcSocket_get_local_addr; + sock->get_local_port = KcSocket_get_local_port; + sock->get_socket = KcSocket_get_socket; + sock->get_type = KcSocket_get_type; + sock->get_family = KcSocket_get_family; + sock->get_addrinfo = KcSocket_get_addrinfo; // for local + sock->bind = KcSocket_bind; + sock->listen = KcSocket_listen; + sock->accept = KcSocket_accept; + sock->connect = KcSocket_connect; + sock->close = KcSocket_close; + sock->send = KcSocket_send; + sock->recv = KcSocket_recv; + sock->sendto = KcSocket_sendto; + sock->recvfrom = KcSocket_recvfrom; + sock->set_ttl = KcSocket_set_ttl; + sock->join = KcSocket_join; + sock->leave = KcSocket_leave; + sock->_info = (sock + 1); + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + info->sock_fd = INVALID_SOCKET; + info->sock_type = type; + info->sock_family = family; + info->remote_addrlen = 0; + info->local_addrlen = 0; + } + return sock; +} + +// ============================================================================= +// delete +// ============================================================================= +/** + * Socket を破棄します。 + */ +void KcSocket_delete(KcSocket *socket) +{ + if (socket) + { + if (((KcSocketInfo *)socket->_info)->sock_fd != INVALID_SOCKET) + { + socket->close(socket); + } + free(socket); + } +} + +// ============================================================================= +// get_remote_addr +// ============================================================================= +/** + * ソケットの接続先アドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr 接続先アドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ +static const char *KcSocket_get_remote_addr(KcSocket *sock, char *addr, size_t addrlen) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->remote_addrlen) + { + int ret = getnameinfo( + (const struct sockaddr *)&info->remote_addr, + info->remote_addrlen, + addr, addrlen, NULL, 0, NI_NUMERICHOST); + if (ret == 0) + { + return addr; + } + } + return NULL; +} + +/** + * ソケットの接続先ポート番号を返します。 + * 接続先ポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ +static int KcSocket_get_remote_port(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->remote_addrlen) + { + char tmp_port[NI_MAXSERV]; + int ret = getnameinfo( + (const struct sockaddr *)&info->remote_addr, + info->remote_addrlen, + NULL, 0, tmp_port, sizeof(tmp_port), NI_NUMERICSERV); + if (ret == 0) + { + return atoi(tmp_port); + } + } + return -1; +} + +/** + * ソケットのローカルアドレスを指定されたバッファ addr に格納します。 + * addr のバッファサイズは、KC_NI_MAXHOST とすべきです。 + * + * @param sock 対象ソケット + * @param addr ローカルアドレス格納用バッファ + * @param addrlen addr のバッファサイズ + * @return addr へのポインタ + */ +static const char *KcSocket_get_local_addr(KcSocket *sock, char *addr, size_t addrlen) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->local_addrlen) + { + int ret = getnameinfo( + (const struct sockaddr *)&info->local_addr, + info->remote_addrlen, + addr, addrlen, NULL, 0, NI_NUMERICHOST); + if (ret == 0) + { + return addr; + } + } + return NULL; +} + +/** + * ソケットのローカルポート番号を返します。 + * ローカルポート番号が取得できなかった場合、-1 を返します。 + * + * @param sock 対象ソケット + * @return ポート番号 + */ +static int KcSocket_get_local_port(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->local_addrlen) + { + char tmp_port[NI_MAXSERV]; + int ret = getnameinfo( + (const struct sockaddr *)&info->local_addr, + info->remote_addrlen, + NULL, 0, tmp_port, sizeof(tmp_port), NI_NUMERICSERV); + if (ret == 0) + { + return atoi(tmp_port); + } + } + return -1; +} + +/** + * ソケットディスクリプタを返します。 + * 通常は、ソケットディスクリプタを直接操作しないでください。 + * + * @param sock 対象ソケット + * @return ソケットディスクリプタ + */ +static socket_t KcSocket_get_socket(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + return info->sock_fd; +} + +/** + * ソケットタイプを返します。 + * + * @param sock 対象ソケット + * @return ソケットタイプ + */ +static int KcSocket_get_type(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + return info->sock_type; +} + +/** + * ソケットファミリを返します。 + * ソケット接続、バインド時には、実際にバインド、 + * あるいは接続されているソケットファミリを返します。 + * + * @param sock 対象ソケット + * @return ソケットファミリ + */ +static int KcSocket_get_family(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + int result_family = info->sock_family; + if (info->sock_fd != INVALID_SOCKET) + { + struct sockaddr_storage ss; + socklen_t len = sizeof(struct sockaddr_storage); + if (getsockname(info->sock_fd, (struct sockaddr *)&ss, &len) == 0) + { + result_family = ss.ss_family; + } + } + return result_family; +} + +/** + * 指定されたアドレス、サービスのアドレス情報を取得します。 + * + * @param sock 対象ソケット + * @param result アドレス情報格納用ポインタ + * @param addr アドレス + * @param service サービス + * @param is_passive サーバソケットの場合、true を指定ください。 + */ +static bool KcSocket_get_addrinfo( + KcSocket *sock, struct addrinfo **result, + const char *addr, const char *service, bool is_passive) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + + struct addrinfo hints; + memset(&hints, 0x00, sizeof(struct addrinfo)); + hints.ai_socktype = info->sock_type; + hints.ai_family = info->sock_family; + if (is_passive) + { + hints.ai_flags = AI_PASSIVE; + } + int ret = getaddrinfo(addr, service, &hints, result); + return (ret == 0); +} + +/** + * 指定されたアドレス、サービスにバインドします。 + * 指定されたアドレスとサービスにより、複数候補がある場合は、 + * 最初に見つかったアドレスとポートにバインドします。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス (例: "80", "http", "ssh" など) + * @return true/false (bind 成功/失敗) + */ +static bool KcSocket_bind(KcSocket *sock, const char *addr, const char *service) +{ + struct addrinfo *bind_addr; + bool is_success = sock->get_addrinfo(sock, &bind_addr, addr, service, true); + if (is_success) + { + is_success = KcSocket_addrinfo_bind_and_connect(sock, bind_addr, NULL); + freeaddrinfo(bind_addr); + } + return is_success; +} + +/** + * ソケットを接続待ちソケットとしてマークをつけます。 + * 保留中の接続のキュー最大長を指定します。 + * + * @param sock 対象ソケット + * @param backlog バックログ + * @return true/false (成功/失敗) + */ +static bool KcSocket_listen(KcSocket *sock, int backlog) +{ + bool result = false; + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + if (info->sock_fd != INVALID_SOCKET) + { + int ret = listen(info->sock_fd, backlog); + result = (ret != SOCKET_ERROR); + } + return result; +} + +/** + * ソケットへの接続を受け付けます。 + * + * @param sock 対象ソケット + * @return 受け付けたソケット(失敗時NULL) + */ +static KcSocket *KcSocket_accept(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + KcSocket *client = NULL; + if (info->sock_fd != INVALID_SOCKET) + { + client = KcSocket_new(info->sock_type, info->sock_family); + if (client != NULL) + { + KcSocketInfo *cinfo = (KcSocketInfo *)client->_info; + cinfo->remote_addrlen = sizeof(struct sockaddr_storage); + cinfo->sock_fd = accept(info->sock_fd, (struct sockaddr *)&cinfo->remote_addr, &cinfo->remote_addrlen); + if (cinfo->sock_fd != INVALID_SOCKET) + { + memcpy(&cinfo->local_addr, &info->local_addr, info->local_addrlen); + cinfo->local_addrlen = info->local_addrlen; + cinfo->sock_type = SOCK_STREAM; + cinfo->sock_family = cinfo->remote_addr.ss_family; + } + else + { + KcSocket_delete(client); + client = NULL; + } + } + } + return client; +} + +/** + * 指定されたアドレス、サービス接続します。 + * local_addr に NULL を指定した場合、自動的にローカルのアドレス、サービスが設定されます。 + * + * @param sock 対象ソケット + * @param addr アドレス + * @param service サービス + * @param local_addr ローカルアドレス + * @param local_service ローカルサービス + * @return true/false (成功/失敗) + */ +static bool KcSocket_connect( + KcSocket *sock, const char *addr, const char *service, + const char *local_addr, const char *local_service) +{ + // 接続先アドレス情報取得 + struct addrinfo *conn_addr = NULL; + if (!sock->get_addrinfo(sock, &conn_addr, addr, service, false)) + { + return false; + } + + // バインドアドレス情報取得 + bool is_success = false; + struct addrinfo *bind_addr = NULL; + if (local_addr != NULL) + { // bind が必要 + is_success = sock->get_addrinfo(sock, &bind_addr, local_addr, local_service, true); + if (is_success) + { + is_success = KcSocket_addrinfo_bind_and_connect(sock, bind_addr, conn_addr); + freeaddrinfo(bind_addr); + } + } + else + { + is_success = KcSocket_addrinfo_connect(sock, conn_addr, INVALID_SOCKET); + } + freeaddrinfo(conn_addr); + return is_success; +} + +/** + * ソケットをクローズします。 + * + * @param sock 対象ソケット + * @return true/false (成功/失敗) + */ +static bool KcSocket_close(KcSocket *sock) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + + if (info->sock_family == SOCK_STREAM) + { // TCP の場合は、出力を閉じて、届いているデータを読み取ってから close する。 + int ret = shutdown(info->sock_fd, SHUT_WR); + if (ret == 0) + { + int read_size; + char buff[1024]; + do + { // 届いているデータを全て読み取る + read_size = recv(info->sock_fd, buff, sizeof(buff), 0); + } while (read_size > 0); + } + } + sockclose(info->sock_fd); + return true; +} + +/** + * メッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 送信されたデータサイズ + */ +static ssize_t KcSocket_send(KcSocket *sock, const char *buff, size_t size, int flags) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + ssize_t write_size = 0; + do + { + errno = 0; + write_size = send(info->sock_fd, buff, size, flags); + } while ((write_size < 0) && (errno == EINTR)); + return write_size; +} + +/** + * メッセージを受信します。 + * 受信したメッセージは指定されたバッファに格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @return 読み取ったデータサイズ + */ +static ssize_t KcSocket_recv(KcSocket *sock, char *buff, size_t size, int flags) +{ + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + ssize_t read_size; + do + { + errno = 0; + read_size = recv(info->sock_fd, buff, size, flags); + } while ((read_size < 0) && (errno == EINTR)); + return read_size; +} + +/** + * 指定されたアドレス、サービスにメッセージを送信します。 + * + * @param sock 対象ソケット + * @param buff メッセージ + * @param size サイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param addr アドレス + * @param service サービス + * @return 送信メッセージサイズ、エラー発生時は -1 + */ +static ssize_t KcSocket_sendto( + KcSocket *sock, + const char *buff, size_t size, int flags, + const char *addr, const char *service) +{ + (void)sock; + (void)buff; + (void)size; + (void)flags; + (void)addr; + (void)service; + return 0; +} + +/** + * メッセージを受信します。 + * src_addr, src_service がいずれも NULL 出ない場合、 + * 送信元のアドレスとサービスが格納されます。 + * + * @param sock 対象ソケット + * @param buff メッセージ受信用バッファ + * @param size バッファサイズ + * @param flags フラグ (MSG_CONFIRM/MSG_DONTROUTE/MSG_DONTWAIT/MSG_EOR/MSG_MORE/MSG_NOSIGNAL/MSG_OOB) + * @param src_addr 送信元アドレス格納用バッファ + * @param src_addrlen 送信元アドレス格納用バッファサイズ + * @param src_service 送信元サービス格納用バッファ + * @param src_servicelen 送信元サービス格納用バッファサイズ + * @return 受信メッセージサイズ、エラー発生時は -1, 接続が正しく shutdown した場合は 0 + */ +static ssize_t KcSocket_recvfrom( + KcSocket *sock, + char *buff, size_t size, int flags, + char *src_addr, size_t src_addrlen, + char *src_service, size_t src_servicelen) +{ + (void)sock; + (void)buff; + (void)size; + (void)flags; + (void)src_addr; + (void)src_addrlen; + (void)src_service; + (void)src_servicelen; + return 0; +} + +/** + * TTL を設定します。 + * + * @param sock 対象ソケット + * @param val 設定する TTL + * @return true/false (成功/失敗) + */ +static bool KcSocket_set_ttl(KcSocket *sock, int val) +{ + (void)sock; + (void)val; + return true; +} + +/** + * マルチキャストグループに参加するために JOIN します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @param ifname インタフェース名 + * @param ifindex インタフェースインデックス + * @return true/false (成功/失敗) + */ +static bool KcSocket_join( + KcSocket *sock, const char *addr, const char *ifname, unsigned int ifindex) +{ + (void)sock; + (void)addr; + (void)ifname; + (void)ifindex; + return true; +} + +/** + * マルチキャストグループから離脱します。 + * + * @param sock 対象ソケット + * @param addr マルチキャストアドレス + * @return true/false (成功/失敗) + */ +static bool KcSocket_leave( + KcSocket *sock, const char *addr) +{ + (void)sock; + (void)addr; + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// [内部関数] +// + +/** + * 指定された接続アドレス情報をもとにソケットの生成、接続を試みます。 + * 接続済みソケット sockfd が有効な場合、sockfd を用いて接続します。 + * + * @param sock 対象ソケット + * @param conn_addrinfo 接続アドレス情報 + * @param sockfd 接続済みソケット + * @return true/false (接続成功/失敗) + */ +static bool KcSocket_addrinfo_connect(KcSocket *sock, struct addrinfo *conn_addrinfo, socket_t sockfd) +{ + bool is_success = false; + socket_t tmp_sock = sockfd; + for (struct addrinfo *rp = conn_addrinfo; rp != NULL; rp = rp->ai_next) + { + if (sockfd == INVALID_SOCKET) + { // sockfd が無効の場合、ソケットを生成する。 + tmp_sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (tmp_sock == INVALID_SOCKET) + { + continue; + } + } + + int ret = connect(tmp_sock, rp->ai_addr, rp->ai_addrlen); + is_success = (ret != SOCKET_ERROR); + if (is_success) + { + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + info->sock_fd = tmp_sock; + info->remote_addrlen = rp->ai_addrlen; + memcpy(&info->remote_addr, rp->ai_addr, rp->ai_addrlen); + break; + } + + if (sockfd != INVALID_SOCKET) + { // sockfd が無効の場合、一時的に生成したソケットをクローズする。 + sockclose(tmp_sock); + } + } + return is_success; +} + +/** + * 指定された接続アドレス情報をもとにソケットの生成、バインドを試みます。 + * conn_addrinfo が NULL でない場合、バインド後に接続を試みます。 + * + * @param sock 対象ソケット + * @param bind_addrinfo バインドアドレス情報 + * @param conn_addrinfo 接続アドレス情報 + * @return true/false (接続成功/失敗) + */ +static bool KcSocket_addrinfo_bind_and_connect( + KcSocket *sock, struct addrinfo *bind_addrinfo, struct addrinfo *conn_addrinfo) +{ + int ret; + bool is_success = false; + + for (struct addrinfo *rp = bind_addrinfo; rp != NULL; rp = rp->ai_next) + { // sokcet : ソケット生成 + socket_t tmp_sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (tmp_sock == INVALID_SOCKET) + { + continue; + } + + // bind + const int on = 1; + setsockopt(tmp_sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on)); + ret = bind(tmp_sock, rp->ai_addr, rp->ai_addrlen); + is_success = (ret != SOCKET_ERROR); + if (!is_success) + { // bind 失敗したので次へ + sockclose(tmp_sock); + continue; + } + + // connect + if (conn_addrinfo) + { + is_success = KcSocket_addrinfo_connect(sock, conn_addrinfo, tmp_sock); + if (!is_success) + { // connect 失敗したので次へ + sockclose(tmp_sock); + continue; + } + } + + // bind または、bind と connect 成功 + KcSocketInfo *info = (KcSocketInfo *)sock->_info; + info->sock_fd = tmp_sock; + info->local_addrlen = rp->ai_addrlen; + memcpy(&info->local_addr, rp->ai_addr, rp->ai_addrlen); + break; + } + return is_success; +} diff --git a/modules/src/kc_threads.c b/modules/src/kc_threads.c new file mode 100644 index 0000000..dd600bb --- /dev/null +++ b/modules/src/kc_threads.c @@ -0,0 +1,194 @@ +/** + * @file kc_thread.c + * @brief スレッドモジュール + * @copyright 2020 - 2024 Nomura Kei + */ +#include +#include +#include + +#include +#include +#include + +/** + * KcThread 管理情報 + */ +typedef struct +{ + thrd_t tid; + void *args; + int (*run)(void *args); + atomic_bool alive_state; +} KcThreadInfo; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static bool KcThread_is_alive(KcThread *thread); +static bool KcThread_start(KcThread *thread, void *args); +static bool KcThread_join(KcThread *thread); +static int KcThread_run(void *thread); + +// ============================================================================= +// new +// ============================================================================= +/** + * スレッドを生成します。 + * + * @param run スレッドにて実行する関数へのポインタ + */ +KcThread *KcThread_new(int (*run)(void *args)) +{ + // KcThread の管理構造 + // +--------------+ + // | KcThread | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // +--------------+ + KcThread *thread = (KcThread *)malloc(sizeof(KcThread) + sizeof(KcThreadInfo)); + if (thread != NULL) + { + thread->is_alive = KcThread_is_alive; + thread->start = KcThread_start; + thread->join = KcThread_join; + thread->_info = (thread + 1); + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + // info->tid + info->run = run; + atomic_init(&info->alive_state, false); + } + return thread; +} + +// ============================================================================= +// delete +// ============================================================================= +/** + * Thread を破棄します。 + * + * @param thread 破棄するスレッド + */ +void KcThread_delete(KcThread *thread) +{ + free(thread); +} + +// ============================================================================= +// sleep +// ============================================================================= +/** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープさせるミリ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ +bool KcThread_msleep(long time, bool force) +{ + long sec = time / 1000; + long msec = time - (sec * 1000); + long nsec = msec * 1000 * 1000; + return KcThread_sleep(sec, nsec, force); +} + +/** + * 指定された sec 秒 + nsec ナノ秒間現在のスレッドをスリープさせます。 + * + * @param sec スリープさせる秒数 + * @param nsec スリープさせるナノ秒数 + * @param force true の場合、シグナルにより割り込まれた場合でも継続してスリープします。 + * @return true/false (成功/中断された) + */ +bool KcThread_sleep(long sec, long nsec, bool force) +{ + struct timespec remaining = {sec, nsec}; + int ret; + do + { + errno = 0; + ret = thrd_sleep(&remaining, &remaining); + } while ((ret == -1) && force && (errno == EINTR)); + return (ret == 0); +} + +// ============================================================================= +// is_alive +// ============================================================================= +/** + * 指定されたスレッドが生きているか否かを返します。 + * + * @param thread 対象スレッド + * @return true/false (スレッドが生存している/していない) + */ +static bool KcThread_is_alive(KcThread *thread) +{ + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + return atomic_load(&info->alive_state); +} + +// ============================================================================= +// start +// ============================================================================= +/** + * スレッドを開始します。 + * + * @param thread 対象スレッド + * @param args スレッドに渡す情報 + * @return true/false (スレッド起動成功/失敗) + */ +static bool KcThread_start(KcThread *thread, void *args) +{ + int ret = thrd_error; + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + if (!atomic_load(&info->alive_state)) + { + info->args = args; + ret = thrd_create(&info->tid, KcThread_run, thread); + } + return (ret == thrd_success); +} + +// ============================================================================= +// join +// ============================================================================= +/** + * スレッドが終了するのを待機します。 + * + * @param thread 対象スレッド + * @return true/false (成功/失敗) + */ +static bool KcThread_join(KcThread *thread) +{ + int ret = thrd_error; + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + ret = thrd_join(info->tid, NULL); + return (ret == thrd_success); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// 内部関数 +// + +// ============================================================================= +// run +// ============================================================================= +/** + * スレッドで実行される関数。 + * start にて関数を別スレッドにて実行します。 + * + * @param args スレッド情報 + * @return + */ +int KcThread_run(void *args) +{ + KcThread *thread = (KcThread *)args; + KcThreadInfo *info = (KcThreadInfo *)thread->_info; + atomic_store(&info->alive_state, true); + int ret = info->run(info->args); + atomic_store(&info->alive_state, false); + return ret; +} diff --git a/modules/src/kc_threads_win.c b/modules/src/kc_threads_win.c new file mode 100644 index 0000000..0439fda --- /dev/null +++ b/modules/src/kc_threads_win.c @@ -0,0 +1,201 @@ +/** + * @file kc_thread.c + * @brief スレッドモジュール + * @copyright 2020 - 2024 Nomura Kei + */ +#include +#include + +#if (KC_IS_WINDOWS) + +int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) +{ + if ((thr == NULL) || (func == NULL)) + { + return thrd_error; + } + + thr->handle = CreateThread( + NULL, // セキュリティ属性 + 0, // スタックサイズ + (LPTHREAD_START_ROUTINE)func, // スレッド関数 + arg, // 引数 + 0, // 作成フラグ + &(thr->thread_id) // スレッドID + ); + + if (thr->handle == NULL) + { + return thrd_error; + } + return thrd_success; +} +int thrd_join(thrd_t thr, int *res) +{ + if (WaitForSingleObject(thr.handle, INFINITE) != WAIT_OBJECT_0) + { + return thrd_error; + } + if (res != NULL) + { + DWORD retcode; + if (GetExitCodeThread(thr.handle, &retcode) == 0) + { + return thrd_error; + } + *res = (int)retcode; + } + CloseHandle(thr.handle); + return thrd_success; +} + +int thrd_detach(thrd_t thr) +{ + if (thr.handle == NULL) + { + return thrd_error; + } + if (CloseHandle(thr.handle) == 0) + { + return thrd_error; + } + return thrd_success; +} + +thrd_t thrd_current(void) +{ + thrd_t current; + current.handle = GetCurrentThread(); + current.thread_id = GetThreadId(current.handle); + return current; +} + +int thrd_equal(thrd_t lhs, thrd_t rhs) +{ + return (lhs.thread_id == rhs.thread_id); +} + +void thrd_yield(void) +{ + SwitchToThread(); +} + +int thrd_sleep(const struct timespec *duration, struct timespec *remaining) +{ + if (duration == NULL) + { + return thrd_error; + } + + // Windows の Sleep は、ms 単位 + DWORD msec = (DWORD)(duration->tv_sec * 1000 + duration->tv_nsec / 1000000); + Sleep(msec); + + // 常に成功したものとして、remaining は 0 に設定する。 + if (remaining != NULL) + { + remaining->tv_sec = 0; + remaining->tv_nsec = 0; + } + return thrd_success; +} + +int mtx_init(mtx_t *mtx, int type) +{ + if (mtx == NULL) + { + return thrd_error; + } + mtx->type = type; + if (type & mtx_plain) + { + InitializeCriticalSection(&mtx->cs); + } + else if (type & mtx_recursive) + { + InitializeCriticalSection(&mtx->cs); + } + else + { + return thrd_error; + } + return thrd_success; +} + +void mtx_destroy(mtx_t *mtx) +{ + DeleteCriticalSection(&mtx->cs); +} +int mtx_lock(mtx_t *mtx) +{ + EnterCriticalSection(&mtx->cs); + return thrd_success; +} +int mtx_unlock(mtx_t *mtx) +{ + LeaveCriticalSection(&mtx->cs); + return thrd_success; +} + +int cnd_init(cnd_t *cond) +{ + if (cond == NULL) + { + return thrd_error; + } + InitializeConditionVariable(&cond->cond); + return thrd_success; +} + +int cnd_signal(cnd_t *cond) +{ + if (cond == NULL) + { + return thrd_error; + } + WakeConditionVariable(&cond->cond); + return thrd_success; +} +int cnd_broadcast(cnd_t *cond) +{ + if (cond == NULL) + { + return thrd_error; + } + WakeAllConditionVariable(&cond->cond); + return thrd_success; +} +int cnd_wait(cnd_t *cond, mtx_t *mtx) +{ + if ((cond == NULL) || (mtx == NULL)) + { + return thrd_error; + } + if (SleepConditionVariableCS(&cond->cond, &mtx->cs, INFINITE)) + { + return thrd_success; + } + return thrd_error; +} + +int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts) +{ + if ((cond == NULL) || (mtx == NULL) || (ts == NULL)) + { + return thrd_error; + } + DWORD msec = (DWORD)(ts->tv_sec * 1000 + ts->tv_nsec / 1000000); + if (SleepConditionVariableCS(&cond->cond, &mtx->cs, msec)) + { + return thrd_timedout; + } + return thrd_error; +} + +void cnd_destroy(cnd_t *cond) +{ + // Nothing to do + UNUSED_VARIABLE(cond); +} + +#endif \ No newline at end of file diff --git a/modules/test/src/test_lock_guard.c b/modules/test/src/test_lock_guard.c index f00ed0b..9c3249f 100644 --- a/modules/test/src/test_lock_guard.c +++ b/modules/test/src/test_lock_guard.c @@ -37,7 +37,7 @@ { mtx_t mutex; mtx_init(&mutex, mtx_plain | mtx_recursive); - KcLockGuard lock_guard = kc_lock_guard_init(&mutex); + KcLockGuard lock_guard = kc_lock_guard_init(&mutex, __FILE__, __func__, __LINE__); assert_not_null(lock_guard.mutex); // Lock Guard 解放 @@ -53,7 +53,7 @@ */ static void test_lock_guard_init_null(void) { - KcLockGuard lock_guard = kc_lock_guard_init(NULL); + KcLockGuard lock_guard = kc_lock_guard_init(NULL, __FILE__, __func__, __LINE__); assert_equals(EINVAL, errno); assert_null(lock_guard.mutex); } @@ -69,7 +69,7 @@ mtx_t mutex; mtx_init(&mutex, mtx_plain | mtx_recursive); mtx_destroy(&mutex); - KcLockGuard lock_guard = kc_lock_guard_init(&mutex); + KcLockGuard lock_guard = kc_lock_guard_init(&mutex, __FILE__, __func__, __LINE__); assert_null(lock_guard.mutex); } @@ -83,7 +83,7 @@ { mtx_t mutex; mtx_init(&mutex, mtx_plain | mtx_recursive); - KcLockGuard lock_guard = kc_lock_guard_init(&mutex); + KcLockGuard lock_guard = kc_lock_guard_init(&mutex, __FILE__, __func__, __LINE__); assert_not_null(lock_guard.mutex); // Lock Guard 解放 diff --git a/modules/test/src/test_queue.c b/modules/test/src/test_queue.c index cbec7e0..1595972 100644 --- a/modules/test/src/test_queue.c +++ b/modules/test/src/test_queue.c @@ -1,12 +1,12 @@ #include #include -#include #include #include #include #include #include +#include #include "ut.h" @@ -58,14 +58,11 @@ static int test_queue_offer_thread(void *arg) { KcQueue *queue = (KcQueue *)arg; - struct timespec spec; - spec.tv_sec = 0; - spec.tv_nsec = 1; for (int i = 0; i < 100; i++) { printf("offer: %d\n", i); queue->offer(queue, &i, sizeof(int)); - thrd_sleep(&spec, NULL); + KcThread_sleep(0, 1, false); } return 0; } @@ -73,9 +70,6 @@ // for poll thread static int test_queue_poll_thread(void *arg) { - struct timespec spec; - spec.tv_sec = 0; - spec.tv_nsec = 1; KcQueue *queue = (KcQueue *)arg; for (int i = 0; i < 100; i++) { @@ -100,7 +94,7 @@ { printf("poll: (null)\n"); } - thrd_sleep(&spec, NULL); + KcThread_sleep(0, 1, false); } return 0; } @@ -112,27 +106,28 @@ { KcQueue *queue = KcQueue_new(10); - thrd_t offer_thread, poll_thread; + KcThread *offer_thread = KcThread_new(test_queue_offer_thread); + KcThread *poll_thread = KcThread_new(test_queue_poll_thread); - thrd_create(&offer_thread, test_queue_offer_thread, queue); - thrd_create(&poll_thread, test_queue_poll_thread, queue); + offer_thread->start(offer_thread, queue); + poll_thread->start(poll_thread, queue); - thrd_join(offer_thread, NULL); - thrd_join(poll_thread, NULL); + offer_thread->join(offer_thread); + poll_thread->join(poll_thread); + KcQueue_delete(queue); + KcThread_delete(offer_thread); + KcThread_delete(poll_thread); } // for put thread static int test_queue_put_thread(void *arg) { KcQueue *queue = (KcQueue *)arg; - struct timespec spec; - spec.tv_sec = 0; - spec.tv_nsec = 1 * 1000; for (int i = 0; i < 100; i++) { queue->put(queue, &i, sizeof(int)); - thrd_sleep(&spec, NULL); + KcThread_sleep(0, 1000, false); } return 0; } @@ -140,9 +135,6 @@ // for take thread static int test_queue_take_thread(void *arg) { - struct timespec spec; - spec.tv_sec = 0; - spec.tv_nsec = 1; KcQueue *queue = (KcQueue *)arg; for (int i = 0; i < 100; i++) { @@ -150,7 +142,7 @@ size_t size = sizeof(int); queue->take(queue, &element, &size); assert_equals(i, element); - thrd_sleep(&spec, NULL); + KcThread_sleep(0, 1, false); } return 0; } @@ -158,9 +150,6 @@ // for take thread static int test_queue_take2_thread(void *arg) { - struct timespec spec; - spec.tv_sec = 0; - spec.tv_nsec = 100 * 1000; KcQueue *queue = (KcQueue *)arg; for (int i = 0; i < 100; i++) { @@ -168,7 +157,7 @@ size_t size = sizeof(int); queue->take(queue, &element, &size); assert_equals(i, element); - thrd_sleep(&spec, NULL); + KcThread_sleep(0, 100 * 1000, false); } return 0; } @@ -180,22 +169,31 @@ { { KcQueue *queue = KcQueue_new(0); - thrd_t put_thread, take_thread; - thrd_create(&put_thread, test_queue_put_thread, queue); - thrd_create(&take_thread, test_queue_take_thread, queue); - thrd_join(put_thread, NULL); - thrd_join(take_thread, NULL); + KcThread *put_thread = KcThread_new(test_queue_put_thread); + KcThread *take_thread = KcThread_new(test_queue_take_thread); + + put_thread->start(put_thread, queue); + take_thread->start(take_thread, queue); + + put_thread->join(put_thread); + take_thread->join(take_thread); + KcThread_delete(put_thread); + KcThread_delete(take_thread); KcQueue_delete(queue); } // PUT ブロックパターン { KcQueue *queue = KcQueue_new(5); - thrd_t put_thread, take_thread; - thrd_create(&put_thread, test_queue_put_thread, queue); - thrd_create(&take_thread, test_queue_take2_thread, queue); - thrd_join(put_thread, NULL); - thrd_join(take_thread, NULL); + KcThread *put_thread = KcThread_new(test_queue_put_thread); + KcThread *take_thread = KcThread_new(test_queue_take2_thread); + put_thread->start(put_thread, queue); + take_thread->start(take_thread, queue); + + put_thread->join(put_thread); + take_thread->join(take_thread); + KcThread_delete(put_thread); + KcThread_delete(take_thread); KcQueue_delete(queue); } } diff --git a/modules/test/src/test_socket.c b/modules/test/src/test_socket.c new file mode 100644 index 0000000..356882e --- /dev/null +++ b/modules/test/src/test_socket.c @@ -0,0 +1,52 @@ +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "ut.h" + +// プロトタイプ宣言 +static void test_socket_new(void); + +/** + * KcSocket 単体テストスイート + */ +void suite_socket(void) +{ + KcUt *ut = KcUt_get_instance(); + ut->add(ut, UT_TESTCASE, "socket new/delete", test_socket_new); +} + +/** + * Socket 生成/破棄。 + * + * @process KcSocket_new を実行する。。 + * @result KcSocket が生成されること。 + * + * @process KcSocket_delete にて Queue を破棄する。 + * @result Socket が破棄されること。 + */ +static void test_socket_new(void) +{ + KcSocket *server = KcSocket_new(SOCK_STREAM, AF_UNSPEC); + assert_not_null(server); + + KcSocket *client = KcSocket_new(SOCK_STREAM, AF_UNSPEC); + assert_not_null(server); + + bool ret = server->bind(server, "127.0.0.1", "5000"); + assert_true(ret); + ret = server->listen(server, 1); + assert_true(ret); + + ret = client->connect(client, "127.0.0.1", "5000", NULL, NULL); + assert_true(ret); + + KcSocket_delete(server); + KcSocket_delete(client); +} diff --git a/modules/test/src/test_thread.c b/modules/test/src/test_thread.c new file mode 100644 index 0000000..e217aea --- /dev/null +++ b/modules/test/src/test_thread.c @@ -0,0 +1,131 @@ +#include +#include + +#include +#include +#include +#include +#include + +#include "ut.h" + +// プロトタイプ宣言 +static void test_thread_new(void); +// static void test_thread_sleep(void); +static void test_thread_memory_error(void); + +/** + * KcThread 単体テストスイート + */ +void suite_thread(void) +{ + KcUt *ut = KcUt_get_instance(); + ut->add(ut, UT_TESTCASE, "thread new/delete", test_thread_new); + // ut->add(ut, UT_TESTCASE, "thread sleep", test_thread_sleep); + ut->add(ut, UT_TESTCASE, "thread memory error", test_thread_memory_error); +} + +static int pass_value = 0; +static int test_func(void *args) +{ + int *value = (int *)args; + if (*value == 1) + { + pass_value &= 0x01; + } + if (*value == 2) + { + pass_value &= 0x02; + } + for (int i = 0; i < 10; i++) + { + printf("Thread %d : %03d\n", *value, i); + KcThread_msleep(1, true); + } + return 0; +} + +/** + * Thread 生成/破棄。 + * + * @process KcThread_new を実行する。。 + * @result KcThread が生成されること。 + * + * @process KcThread_delete にて Thread を破棄する。 + * @result Thread が破棄されること。 + */ +static void test_thread_new(void) +{ + KcThread *thread_1 = KcThread_new(test_func); + KcThread *thread_2 = KcThread_new(test_func); + + pass_value = 0; + bool is_alive_1 = thread_1->is_alive(thread_1); + bool is_alive_2 = thread_2->is_alive(thread_2); + assert_false(is_alive_1); + assert_false(is_alive_2); + int val_1 = 1; + int val_2 = 2; + thread_1->start(thread_1, &val_1); + thread_2->start(thread_2, &val_2); + KcThread_msleep(10, false); + is_alive_1 = thread_1->is_alive(thread_1); + is_alive_2 = thread_2->is_alive(thread_2); + assert_true(is_alive_1); + assert_true(is_alive_2); + thread_1->join(thread_1); + thread_2->join(thread_2); + KcThread_delete(thread_1); + KcThread_delete(thread_2); +} + +// for シグナル処理 +#if 0 +static volatile int test_thread_interrupted = 0; +static void test_thread_sig_handler(int sig) +{ + test_thread_interrupted = 1; +} +int test_sleep_func(void *args) +{ + signal(SIGUSR1, test_thread_sig_handler); + int *value = (int *)args; + if (*value == 1) + { + KcThread_msleep(200, true); + } + else + { + KcThread_msleep(200, false); + } + return 0; +} + +/** + * Thread sleep 中のシグナル発生。 + */ +static void test_thread_sleep(void) +{ + + KcThread *thread = KcThread_new(test_sleep_func); + int value = 1; + test_thread_interrupted = 0; + thread->start(thread, &value); + KcThread_msleep(10, false); + + thread->join(thread); + KcThread_delete(thread); +} +#endif + +/** + * メモリ確保エラー + */ +static void test_thread_memory_error(void) +{ + ut_alloc_control(0) + { + KcThread *thread = KcThread_new(test_func); + assert_null(thread); + } +} \ No newline at end of file diff --git a/modules/test/src/ut.c b/modules/test/src/ut.c index b921513..2a4e0d2 100644 --- a/modules/test/src/ut.c +++ b/modules/test/src/ut.c @@ -8,6 +8,7 @@ int main(void) { // UT Setup + suite_assert(); suite_dl(); suite_env(); @@ -21,6 +22,8 @@ suite_memory_mark(); suite_memory(); suite_queue(); + // suite_socket(); + suite_thread(); KcMemory_start(false); KcMemory_start(true); diff --git a/modules/test/src/ut.h b/modules/test/src/ut.h index de5fe82..846009e 100644 --- a/modules/test/src/ut.h +++ b/modules/test/src/ut.h @@ -17,6 +17,8 @@ extern void suite_memory_mark(void); extern void suite_memory(void); extern void suite_queue(void); +extern void suite_socket(void); +extern void suite_thread(void); //////////////////////////////////////////////////////////////////////////////// //