diff --git a/.bash.d/tmp/util/Makefile b/.bash.d/tmp/util/Makefile new file mode 100644 index 0000000..722430a --- /dev/null +++ b/.bash.d/tmp/util/Makefile @@ -0,0 +1,29 @@ +# ============================================================================== +# Makefile +# ============================================================================== +TOPDIR ?= ../.. +TARGET = libutil.so +SUBDIRS = +VERSION = +#TARGET_TYPE = target-exe +#TARGET_TYPE = target-a +TARGET_TYPE = target-so +#TARGET_TYPE = target-dll + +include $(TOPDIR)/mk/conf.mk +include $(TOPDIR)/mk/$(USE)/com.*.mk +include $(TOPDIR)/mk/$(USE)/conf.def.*.mk + +CFLAGS += -fPIC +CXXFLAGS += +LDFLAGS += + +.PHONY: all test +all: all-subdir $(TARGET) + +test: + $(MAKE) -C test + + +include $(TOPDIR)/mk/$(USE)/rule.*.mk + diff --git a/.bash.d/tmp/util/Makefile b/.bash.d/tmp/util/Makefile new file mode 100644 index 0000000..722430a --- /dev/null +++ b/.bash.d/tmp/util/Makefile @@ -0,0 +1,29 @@ +# ============================================================================== +# Makefile +# ============================================================================== +TOPDIR ?= ../.. +TARGET = libutil.so +SUBDIRS = +VERSION = +#TARGET_TYPE = target-exe +#TARGET_TYPE = target-a +TARGET_TYPE = target-so +#TARGET_TYPE = target-dll + +include $(TOPDIR)/mk/conf.mk +include $(TOPDIR)/mk/$(USE)/com.*.mk +include $(TOPDIR)/mk/$(USE)/conf.def.*.mk + +CFLAGS += -fPIC +CXXFLAGS += +LDFLAGS += + +.PHONY: all test +all: all-subdir $(TARGET) + +test: + $(MAKE) -C test + + +include $(TOPDIR)/mk/$(USE)/rule.*.mk + diff --git a/.bash.d/tmp/util/include/util_blockingqueue.h b/.bash.d/tmp/util/include/util_blockingqueue.h new file mode 100644 index 0000000..0309bff --- /dev/null +++ b/.bash.d/tmp/util/include/util_blockingqueue.h @@ -0,0 +1,24 @@ +#ifndef UTIL_BLOCKINGQUEUE_H +#define UTIL_BLOCKINGQUEUE_H + +#include + + +/** + * ブロッキングキューのインスタンス構造体 + */ +struct blockingqueue; + +struct blockingqueue* blockingqueue_new(int cap); +void blockingqueue_destroy(struct blockingqueue* queue); +bool blockingqueue_push(struct blockingqueue* queue, void* data, size_t size); +bool blockingqueue_push_blocking(struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_pop( struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_pop_blocking( struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_peek(struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_size(struct blockingqueue* queue); +void blockingqueue_notify(struct blockingqueue* queue); + + +#endif /* UTIL_BLOCKINGQUEUE_H */ + diff --git a/.bash.d/tmp/util/Makefile b/.bash.d/tmp/util/Makefile new file mode 100644 index 0000000..722430a --- /dev/null +++ b/.bash.d/tmp/util/Makefile @@ -0,0 +1,29 @@ +# ============================================================================== +# Makefile +# ============================================================================== +TOPDIR ?= ../.. +TARGET = libutil.so +SUBDIRS = +VERSION = +#TARGET_TYPE = target-exe +#TARGET_TYPE = target-a +TARGET_TYPE = target-so +#TARGET_TYPE = target-dll + +include $(TOPDIR)/mk/conf.mk +include $(TOPDIR)/mk/$(USE)/com.*.mk +include $(TOPDIR)/mk/$(USE)/conf.def.*.mk + +CFLAGS += -fPIC +CXXFLAGS += +LDFLAGS += + +.PHONY: all test +all: all-subdir $(TARGET) + +test: + $(MAKE) -C test + + +include $(TOPDIR)/mk/$(USE)/rule.*.mk + diff --git a/.bash.d/tmp/util/include/util_blockingqueue.h b/.bash.d/tmp/util/include/util_blockingqueue.h new file mode 100644 index 0000000..0309bff --- /dev/null +++ b/.bash.d/tmp/util/include/util_blockingqueue.h @@ -0,0 +1,24 @@ +#ifndef UTIL_BLOCKINGQUEUE_H +#define UTIL_BLOCKINGQUEUE_H + +#include + + +/** + * ブロッキングキューのインスタンス構造体 + */ +struct blockingqueue; + +struct blockingqueue* blockingqueue_new(int cap); +void blockingqueue_destroy(struct blockingqueue* queue); +bool blockingqueue_push(struct blockingqueue* queue, void* data, size_t size); +bool blockingqueue_push_blocking(struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_pop( struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_pop_blocking( struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_peek(struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_size(struct blockingqueue* queue); +void blockingqueue_notify(struct blockingqueue* queue); + + +#endif /* UTIL_BLOCKINGQUEUE_H */ + diff --git a/.bash.d/tmp/util/include/util_queue.h b/.bash.d/tmp/util/include/util_queue.h new file mode 100644 index 0000000..2de9762 --- /dev/null +++ b/.bash.d/tmp/util/include/util_queue.h @@ -0,0 +1,21 @@ +#ifndef UTIL_QUEUE_H +#define UTIL_QUEUE_H + +#include + + +/** キュー */ +struct queue; +struct queue* queue_new(size_t cap, size_t size); +void queue_destroy(struct queue* queue); +bool queue_push(struct queue* queue, void* data, size_t size); +size_t queue_pop( struct queue* queue, void* data, size_t size); +size_t queue_peek(struct queue* queue, void* data, size_t size); +size_t queue_size(struct queue* queue); +void queue_entries(struct queue* queue, bool (*handler)(void* data, size_t size)); +void queue_entries_full(struct queue* queue, + bool (*handler)(void* data, size_t size, int index, bool enabled, void* arg), + void* arg); + +#endif /* UTIL_QUEUE_H */ + diff --git a/.bash.d/tmp/util/Makefile b/.bash.d/tmp/util/Makefile new file mode 100644 index 0000000..722430a --- /dev/null +++ b/.bash.d/tmp/util/Makefile @@ -0,0 +1,29 @@ +# ============================================================================== +# Makefile +# ============================================================================== +TOPDIR ?= ../.. +TARGET = libutil.so +SUBDIRS = +VERSION = +#TARGET_TYPE = target-exe +#TARGET_TYPE = target-a +TARGET_TYPE = target-so +#TARGET_TYPE = target-dll + +include $(TOPDIR)/mk/conf.mk +include $(TOPDIR)/mk/$(USE)/com.*.mk +include $(TOPDIR)/mk/$(USE)/conf.def.*.mk + +CFLAGS += -fPIC +CXXFLAGS += +LDFLAGS += + +.PHONY: all test +all: all-subdir $(TARGET) + +test: + $(MAKE) -C test + + +include $(TOPDIR)/mk/$(USE)/rule.*.mk + diff --git a/.bash.d/tmp/util/include/util_blockingqueue.h b/.bash.d/tmp/util/include/util_blockingqueue.h new file mode 100644 index 0000000..0309bff --- /dev/null +++ b/.bash.d/tmp/util/include/util_blockingqueue.h @@ -0,0 +1,24 @@ +#ifndef UTIL_BLOCKINGQUEUE_H +#define UTIL_BLOCKINGQUEUE_H + +#include + + +/** + * ブロッキングキューのインスタンス構造体 + */ +struct blockingqueue; + +struct blockingqueue* blockingqueue_new(int cap); +void blockingqueue_destroy(struct blockingqueue* queue); +bool blockingqueue_push(struct blockingqueue* queue, void* data, size_t size); +bool blockingqueue_push_blocking(struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_pop( struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_pop_blocking( struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_peek(struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_size(struct blockingqueue* queue); +void blockingqueue_notify(struct blockingqueue* queue); + + +#endif /* UTIL_BLOCKINGQUEUE_H */ + diff --git a/.bash.d/tmp/util/include/util_queue.h b/.bash.d/tmp/util/include/util_queue.h new file mode 100644 index 0000000..2de9762 --- /dev/null +++ b/.bash.d/tmp/util/include/util_queue.h @@ -0,0 +1,21 @@ +#ifndef UTIL_QUEUE_H +#define UTIL_QUEUE_H + +#include + + +/** キュー */ +struct queue; +struct queue* queue_new(size_t cap, size_t size); +void queue_destroy(struct queue* queue); +bool queue_push(struct queue* queue, void* data, size_t size); +size_t queue_pop( struct queue* queue, void* data, size_t size); +size_t queue_peek(struct queue* queue, void* data, size_t size); +size_t queue_size(struct queue* queue); +void queue_entries(struct queue* queue, bool (*handler)(void* data, size_t size)); +void queue_entries_full(struct queue* queue, + bool (*handler)(void* data, size_t size, int index, bool enabled, void* arg), + void* arg); + +#endif /* UTIL_QUEUE_H */ + diff --git a/.bash.d/tmp/util/include/util_thread.h b/.bash.d/tmp/util/include/util_thread.h new file mode 100644 index 0000000..60e7bb0 --- /dev/null +++ b/.bash.d/tmp/util/include/util_thread.h @@ -0,0 +1,38 @@ +/** + * @file util_thread.c + * スレッドを扱うモジュール + * + * 実装側のみ複数のOS,ライブラリ用に変更できるよう + * 各オブジェクトを typedef ではなく、構造体にしている。 + */ +#ifndef UTIL_THREAD_H +#define UTIL_THREAD_H + +#include + +/* スレッド */ +struct thread; +struct thread* thread_new(void (*start_routine)(void*), void* arg); +void thread_delete(struct thread* thread); +void thread_start(struct thread* thread); +bool thread_join(struct thread* thread); +bool thread_equals(struct thread* thread); + +/* mutex */ +struct mutex; +struct mutex* mutex_new(void); +bool mutex_delete(struct mutex* mutex); +void mutex_lock(struct mutex* mutex); +void mutex_unlock(struct mutex* mutex); + +/* cond */ +struct cond; +struct cond* cond_new(void); +bool cond_delete(struct cond* cond); +void cond_wait(struct cond* cond, struct mutex* mutex); +void cond_signal(struct cond* cond); +void cond_broadcast(struct cond* cond); + + +#endif /* THREAD_H */ + diff --git a/.bash.d/tmp/util/Makefile b/.bash.d/tmp/util/Makefile new file mode 100644 index 0000000..722430a --- /dev/null +++ b/.bash.d/tmp/util/Makefile @@ -0,0 +1,29 @@ +# ============================================================================== +# Makefile +# ============================================================================== +TOPDIR ?= ../.. +TARGET = libutil.so +SUBDIRS = +VERSION = +#TARGET_TYPE = target-exe +#TARGET_TYPE = target-a +TARGET_TYPE = target-so +#TARGET_TYPE = target-dll + +include $(TOPDIR)/mk/conf.mk +include $(TOPDIR)/mk/$(USE)/com.*.mk +include $(TOPDIR)/mk/$(USE)/conf.def.*.mk + +CFLAGS += -fPIC +CXXFLAGS += +LDFLAGS += + +.PHONY: all test +all: all-subdir $(TARGET) + +test: + $(MAKE) -C test + + +include $(TOPDIR)/mk/$(USE)/rule.*.mk + diff --git a/.bash.d/tmp/util/include/util_blockingqueue.h b/.bash.d/tmp/util/include/util_blockingqueue.h new file mode 100644 index 0000000..0309bff --- /dev/null +++ b/.bash.d/tmp/util/include/util_blockingqueue.h @@ -0,0 +1,24 @@ +#ifndef UTIL_BLOCKINGQUEUE_H +#define UTIL_BLOCKINGQUEUE_H + +#include + + +/** + * ブロッキングキューのインスタンス構造体 + */ +struct blockingqueue; + +struct blockingqueue* blockingqueue_new(int cap); +void blockingqueue_destroy(struct blockingqueue* queue); +bool blockingqueue_push(struct blockingqueue* queue, void* data, size_t size); +bool blockingqueue_push_blocking(struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_pop( struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_pop_blocking( struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_peek(struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_size(struct blockingqueue* queue); +void blockingqueue_notify(struct blockingqueue* queue); + + +#endif /* UTIL_BLOCKINGQUEUE_H */ + diff --git a/.bash.d/tmp/util/include/util_queue.h b/.bash.d/tmp/util/include/util_queue.h new file mode 100644 index 0000000..2de9762 --- /dev/null +++ b/.bash.d/tmp/util/include/util_queue.h @@ -0,0 +1,21 @@ +#ifndef UTIL_QUEUE_H +#define UTIL_QUEUE_H + +#include + + +/** キュー */ +struct queue; +struct queue* queue_new(size_t cap, size_t size); +void queue_destroy(struct queue* queue); +bool queue_push(struct queue* queue, void* data, size_t size); +size_t queue_pop( struct queue* queue, void* data, size_t size); +size_t queue_peek(struct queue* queue, void* data, size_t size); +size_t queue_size(struct queue* queue); +void queue_entries(struct queue* queue, bool (*handler)(void* data, size_t size)); +void queue_entries_full(struct queue* queue, + bool (*handler)(void* data, size_t size, int index, bool enabled, void* arg), + void* arg); + +#endif /* UTIL_QUEUE_H */ + diff --git a/.bash.d/tmp/util/include/util_thread.h b/.bash.d/tmp/util/include/util_thread.h new file mode 100644 index 0000000..60e7bb0 --- /dev/null +++ b/.bash.d/tmp/util/include/util_thread.h @@ -0,0 +1,38 @@ +/** + * @file util_thread.c + * スレッドを扱うモジュール + * + * 実装側のみ複数のOS,ライブラリ用に変更できるよう + * 各オブジェクトを typedef ではなく、構造体にしている。 + */ +#ifndef UTIL_THREAD_H +#define UTIL_THREAD_H + +#include + +/* スレッド */ +struct thread; +struct thread* thread_new(void (*start_routine)(void*), void* arg); +void thread_delete(struct thread* thread); +void thread_start(struct thread* thread); +bool thread_join(struct thread* thread); +bool thread_equals(struct thread* thread); + +/* mutex */ +struct mutex; +struct mutex* mutex_new(void); +bool mutex_delete(struct mutex* mutex); +void mutex_lock(struct mutex* mutex); +void mutex_unlock(struct mutex* mutex); + +/* cond */ +struct cond; +struct cond* cond_new(void); +bool cond_delete(struct cond* cond); +void cond_wait(struct cond* cond, struct mutex* mutex); +void cond_signal(struct cond* cond); +void cond_broadcast(struct cond* cond); + + +#endif /* THREAD_H */ + diff --git a/.bash.d/tmp/util/src/logger.c b/.bash.d/tmp/util/src/logger.c new file mode 100644 index 0000000..e96204d --- /dev/null +++ b/.bash.d/tmp/util/src/logger.c @@ -0,0 +1,117 @@ +#include +#include +#include +#include +#include +#include + +#include "logger.h" + + + +// ============================================================================= +// 内部でのみ使用する関数のプロトタイプ宣言 +// ============================================================================= +static void logger_log_output(int priority, const char* message); +static int get_date(char* buff, size_t size); + + + + + +// ============================================================================= +// 関数定義 +// ============================================================================= + +/** + * 指定されたログレベルのログを出力可能か否かを返します。 + * + * @param priority ログレベル + * @return true/false (ログ出力可能/ログ出力不可) + */ +bool logger_is_logable(int priority) +{ + int current_logmask = setlogmask(0); + return (current_logmask & priority); +} + + + +/** + * ログを出力します。 + * + * @param priority syslog と同じログのレベルを指定します。 + * @param file __FILE__ を指定します。 + * @param line __LINE__ を指定します。 + * @param format 書式を指定します。 + */ +void logger_log_(int priority, const char* file, int line, const char* format, ...) +{ + va_list ap; + va_start(ap, format); + bool is_logable = logger_is_logable(priority); + if (is_logable) + { + char log_buffer[512]; + int len = get_date(log_buffer, sizeof(log_buffer)); + len += snprintf(&log_buffer[len], sizeof(log_buffer) - len, " %s:%d: ", file, line); + vsnprintf(&log_buffer[len], sizeof(log_buffer) - len, format, ap); + logger_log_output(priority, log_buffer); + } + va_end(ap); +} + + +/** + * 指定されたメッセージをログに出力します。 + * + * @param priority ログレベル + * @param message 出力するメッセージ + */ +static void logger_log_output(int priority, const char* message) +{ +#ifdef USE_SYSLOG + static bool is_logopend = false; + if (!is_logopend) + { + openlog("ifconv", LOG_CONS | LOG_PID, LOG_USER); + is_logopend = true; + } + syslog(priority, "%s", message); +#else + pid_t current_pid = getpid(); + printf("ifconv[%d][pri=%d]: %s\n", current_pid, priority, message); +#endif +} + + +/** + * 指定されたバッファに、現在の時刻(ナノ秒まで)を文字列形式で格納します。 + * + * @param buff バッファ + * @param size バッファサイズ + * @return バッファに書き込んだ文字数 + */ +static int get_date(char* buff, size_t size) +{ + // ナノ秒まで取得する。 + struct timespec current_time; + clock_gettime(CLOCK_REALTIME, ¤t_time); + + // 秒を日時に変換する。 + struct tm* local_tm = localtime(¤t_time.tv_sec); + + // YYYY/MM/DD HH:MM:SS.XXXXXXXXX に変換する。 + int len = snprintf(buff, size, "%04d/%02d/%02d %02d:%02d:%02d.%09ld", + local_tm->tm_year + 1900, + local_tm->tm_mon + 1, + local_tm->tm_mday, + local_tm->tm_hour, + local_tm->tm_min, + local_tm->tm_sec, + current_time.tv_nsec); + + return len; +} + + diff --git a/.bash.d/tmp/util/Makefile b/.bash.d/tmp/util/Makefile new file mode 100644 index 0000000..722430a --- /dev/null +++ b/.bash.d/tmp/util/Makefile @@ -0,0 +1,29 @@ +# ============================================================================== +# Makefile +# ============================================================================== +TOPDIR ?= ../.. +TARGET = libutil.so +SUBDIRS = +VERSION = +#TARGET_TYPE = target-exe +#TARGET_TYPE = target-a +TARGET_TYPE = target-so +#TARGET_TYPE = target-dll + +include $(TOPDIR)/mk/conf.mk +include $(TOPDIR)/mk/$(USE)/com.*.mk +include $(TOPDIR)/mk/$(USE)/conf.def.*.mk + +CFLAGS += -fPIC +CXXFLAGS += +LDFLAGS += + +.PHONY: all test +all: all-subdir $(TARGET) + +test: + $(MAKE) -C test + + +include $(TOPDIR)/mk/$(USE)/rule.*.mk + diff --git a/.bash.d/tmp/util/include/util_blockingqueue.h b/.bash.d/tmp/util/include/util_blockingqueue.h new file mode 100644 index 0000000..0309bff --- /dev/null +++ b/.bash.d/tmp/util/include/util_blockingqueue.h @@ -0,0 +1,24 @@ +#ifndef UTIL_BLOCKINGQUEUE_H +#define UTIL_BLOCKINGQUEUE_H + +#include + + +/** + * ブロッキングキューのインスタンス構造体 + */ +struct blockingqueue; + +struct blockingqueue* blockingqueue_new(int cap); +void blockingqueue_destroy(struct blockingqueue* queue); +bool blockingqueue_push(struct blockingqueue* queue, void* data, size_t size); +bool blockingqueue_push_blocking(struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_pop( struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_pop_blocking( struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_peek(struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_size(struct blockingqueue* queue); +void blockingqueue_notify(struct blockingqueue* queue); + + +#endif /* UTIL_BLOCKINGQUEUE_H */ + diff --git a/.bash.d/tmp/util/include/util_queue.h b/.bash.d/tmp/util/include/util_queue.h new file mode 100644 index 0000000..2de9762 --- /dev/null +++ b/.bash.d/tmp/util/include/util_queue.h @@ -0,0 +1,21 @@ +#ifndef UTIL_QUEUE_H +#define UTIL_QUEUE_H + +#include + + +/** キュー */ +struct queue; +struct queue* queue_new(size_t cap, size_t size); +void queue_destroy(struct queue* queue); +bool queue_push(struct queue* queue, void* data, size_t size); +size_t queue_pop( struct queue* queue, void* data, size_t size); +size_t queue_peek(struct queue* queue, void* data, size_t size); +size_t queue_size(struct queue* queue); +void queue_entries(struct queue* queue, bool (*handler)(void* data, size_t size)); +void queue_entries_full(struct queue* queue, + bool (*handler)(void* data, size_t size, int index, bool enabled, void* arg), + void* arg); + +#endif /* UTIL_QUEUE_H */ + diff --git a/.bash.d/tmp/util/include/util_thread.h b/.bash.d/tmp/util/include/util_thread.h new file mode 100644 index 0000000..60e7bb0 --- /dev/null +++ b/.bash.d/tmp/util/include/util_thread.h @@ -0,0 +1,38 @@ +/** + * @file util_thread.c + * スレッドを扱うモジュール + * + * 実装側のみ複数のOS,ライブラリ用に変更できるよう + * 各オブジェクトを typedef ではなく、構造体にしている。 + */ +#ifndef UTIL_THREAD_H +#define UTIL_THREAD_H + +#include + +/* スレッド */ +struct thread; +struct thread* thread_new(void (*start_routine)(void*), void* arg); +void thread_delete(struct thread* thread); +void thread_start(struct thread* thread); +bool thread_join(struct thread* thread); +bool thread_equals(struct thread* thread); + +/* mutex */ +struct mutex; +struct mutex* mutex_new(void); +bool mutex_delete(struct mutex* mutex); +void mutex_lock(struct mutex* mutex); +void mutex_unlock(struct mutex* mutex); + +/* cond */ +struct cond; +struct cond* cond_new(void); +bool cond_delete(struct cond* cond); +void cond_wait(struct cond* cond, struct mutex* mutex); +void cond_signal(struct cond* cond); +void cond_broadcast(struct cond* cond); + + +#endif /* THREAD_H */ + diff --git a/.bash.d/tmp/util/src/logger.c b/.bash.d/tmp/util/src/logger.c new file mode 100644 index 0000000..e96204d --- /dev/null +++ b/.bash.d/tmp/util/src/logger.c @@ -0,0 +1,117 @@ +#include +#include +#include +#include +#include +#include + +#include "logger.h" + + + +// ============================================================================= +// 内部でのみ使用する関数のプロトタイプ宣言 +// ============================================================================= +static void logger_log_output(int priority, const char* message); +static int get_date(char* buff, size_t size); + + + + + +// ============================================================================= +// 関数定義 +// ============================================================================= + +/** + * 指定されたログレベルのログを出力可能か否かを返します。 + * + * @param priority ログレベル + * @return true/false (ログ出力可能/ログ出力不可) + */ +bool logger_is_logable(int priority) +{ + int current_logmask = setlogmask(0); + return (current_logmask & priority); +} + + + +/** + * ログを出力します。 + * + * @param priority syslog と同じログのレベルを指定します。 + * @param file __FILE__ を指定します。 + * @param line __LINE__ を指定します。 + * @param format 書式を指定します。 + */ +void logger_log_(int priority, const char* file, int line, const char* format, ...) +{ + va_list ap; + va_start(ap, format); + bool is_logable = logger_is_logable(priority); + if (is_logable) + { + char log_buffer[512]; + int len = get_date(log_buffer, sizeof(log_buffer)); + len += snprintf(&log_buffer[len], sizeof(log_buffer) - len, " %s:%d: ", file, line); + vsnprintf(&log_buffer[len], sizeof(log_buffer) - len, format, ap); + logger_log_output(priority, log_buffer); + } + va_end(ap); +} + + +/** + * 指定されたメッセージをログに出力します。 + * + * @param priority ログレベル + * @param message 出力するメッセージ + */ +static void logger_log_output(int priority, const char* message) +{ +#ifdef USE_SYSLOG + static bool is_logopend = false; + if (!is_logopend) + { + openlog("ifconv", LOG_CONS | LOG_PID, LOG_USER); + is_logopend = true; + } + syslog(priority, "%s", message); +#else + pid_t current_pid = getpid(); + printf("ifconv[%d][pri=%d]: %s\n", current_pid, priority, message); +#endif +} + + +/** + * 指定されたバッファに、現在の時刻(ナノ秒まで)を文字列形式で格納します。 + * + * @param buff バッファ + * @param size バッファサイズ + * @return バッファに書き込んだ文字数 + */ +static int get_date(char* buff, size_t size) +{ + // ナノ秒まで取得する。 + struct timespec current_time; + clock_gettime(CLOCK_REALTIME, ¤t_time); + + // 秒を日時に変換する。 + struct tm* local_tm = localtime(¤t_time.tv_sec); + + // YYYY/MM/DD HH:MM:SS.XXXXXXXXX に変換する。 + int len = snprintf(buff, size, "%04d/%02d/%02d %02d:%02d:%02d.%09ld", + local_tm->tm_year + 1900, + local_tm->tm_mon + 1, + local_tm->tm_mday, + local_tm->tm_hour, + local_tm->tm_min, + local_tm->tm_sec, + current_time.tv_nsec); + + return len; +} + + diff --git a/.bash.d/tmp/util/src/util_blockingqueue.c b/.bash.d/tmp/util/src/util_blockingqueue.c new file mode 100644 index 0000000..5b66c33 --- /dev/null +++ b/.bash.d/tmp/util/src/util_blockingqueue.c @@ -0,0 +1,200 @@ +#include +#include +#include +#include + +#include "util_queue.h" +#include "util_blockingqueue.h" + +/** + * ブロッキングキューのインスタンス構造体 + */ +struct blockingqueue { + struct queue* queue; /**< キュー */ + struct mutex* data_lock; /**< データロック */ + struct cond* empty_block; /**< pop のブロック用 */ + struct cond* full_block; /**< push のブロック用 */ +}; + + +/** + * ブロッキングキューを生成します。 + * インスタンスを生成できない場合、NULL を返します。 + * + * @param cap キューの容量 + * @param size キューに格納するデータサイズ + * @return イベントキューのインスタンス + */ +struct blockingqueue* blockingqueue_new(size_t cap, size_t size) +{ + struct blockingqueue* blockingqueue = (struct blockingqueue*) malloc(sizeof(struct blockingqueue)); + if (blockingqueue != NULL) + { + blockingqueue->queue = queue_new(cap,4096); + if (blockingqueue->queue != NULL) + { // pthread_mutex_lock ですでに保持している mutex をロックしようとしたとき、 + // デフォルトのスレッドを停止させる動作とするため、NULL (デフォルト設定)を指定する。 + pthread_mutex_init(&blockingqueue->data_lock, NULL); + + // データがない場合 pop をブロックするための cond オブジェクトを初期化する。 + pthread_cond_init(&blockingqueue->pop_block, NULL); + } + else + { + free(blockingqueue); + blockingqueue = NULL; + } + + } + return blockingqueue; +} + + + + +/** + * 指定されたイベントキューを破棄します。 + * + * @param blockingqueue イベントキューのインスタンス + */ +void blockingqueue_destroy(struct blockingqueue* blockingqueue) +{ + if (blockingqueue != NULL) + { + // T.B.D. + queue_destroy(blockingqueue->queue); + blockingqueue->queue = NULL; + } + free(blockingqueue); +} + + +/** + * 指定された mutex をロックします。 + * + * @param mutex mutexオブジェクト + */ +static +void blockingqueue_mutex_lock(pthread_mutex_t* mutex) +{ + int errcode = pthread_mutex_lock(mutex); + if (errcode != 0) + { // エラーが発生する条件は、下記の何れかであり、本プログラムの設定では発生しない。 + // => 発生するのであれば、プログラム誤り。 + // EINVAL : mutex が適切に初期化されていない。 + // EDEADLK : mutex は既に呼び出しスレッドによりロックされている (「エラー検査を行う」mutexes のみ) + printf("###!!! [ERROR] pthred_mutex_lock !!!###\n"); + } +} + + +/** + * 指定された mutex をアンロックします。 + * + * @param mutex mutexオブジェクト + */ +static +void blockingqueue_mutex_unlock(pthread_mutex_t* mutex) +{ + int errcode = pthread_mutex_unlock(mutex); + if (errcode != 0) + { // エラーが発生する条件は、下記の何れかであり、本プログラムの設定では発生しない。 + // => 発生するのであれば、プログラム誤り。 + // EINVAL : mutex が適切に初期化されていない。 + // EDEADLK : mutex は既に呼び出しスレッドによりロックされている (「エラー検査を行う」mutexes のみ) + printf("###!!! [ERROR] pthred_mutex_lock !!!###\n"); + } +} + + +/** + * イベントキューにデータを追加します。 + * + * @param blockingqueue キューのインスタンス + * @param data キューに追加するエントリ(データ) + * @param size キューに追加するエントリ(データ)のサイズ + * @return true/false (追加成功/追加失敗) + */ +bool blockingqueue_push(struct blockingqueue* blockingqueue, void* data, size_t size) +{ + blockingqueue_mutex_lock(&blockingqueue->data_lock); + + // キューにデータがない間ブロックする。 + size_t data_count = queue_size(blockingqueue->queue); + if (data_count == 0) + { + pthread_cond_signal(&blockingqueue->pop_block); + } + + bool result = queue_push(blockingqueue->queue, data, size); + + blockingqueue_mutex_unlock(&blockingqueue->data_lock); + + return result; +} + + +/** + * キューの先頭よりデータを取り出します。 + * キューにデータがない場合、ブロックされます。 + * + * @param blockingqueue キューのインスタンス + * @param buf 取り出したデータ格納用バッファ + * @param buflen バッファのサイズ + * @return 取り出したデータのサイズ + */ +size_t blockingqueue_pop( struct blockingqueue* blockingqueue, void* buf, size_t buflen) +{ + blockingqueue_mutex_lock(&blockingqueue->data_lock); + + // キューにデータがない間ブロックする。 + size_t data_count = queue_size(blockingqueue->queue); + while (data_count == 0) + { + pthread_cond_wait(&blockingqueue->pop_block, &blockingqueue->data_lock); + data_count = queue_size(blockingqueue->queue); + } + + size_t data_size = queue_pop(blockingqueue->queue, buf, buflen); + blockingqueue_mutex_unlock(&blockingqueue->data_lock); + return data_size; +} + + +/** + * キューの先頭よりデータを取り出します。 + * データの取り出しに失敗した場合、0を返します。 + * blockingqueue_pop と異なり、キューからデータは削除されません。 + * + * @param blockingqueue キューのインスタンス + * @param buf 取り出したデータ格納用バッファ + * @param buflen バッファのサイズ + * @return 取り出したデータのサイズ + */ +size_t blockingqueue_peek( struct blockingqueue* blockingqueue, void* buf, size_t buflen) +{ + blockingqueue_mutex_lock(&blockingqueue->data_lock); + size_t size = queue_peek(blockingqueue->queue, buf, buflen); + blockingqueue_mutex_unlock(&blockingqueue->data_lock); + return size; +} + + + +/** + * キューのサイズ(キューに入っているデータの個数)を取得します。 + * + * @param blockingqueue キューのインスタンス + * @return キューのサイズ + */ +size_t blockingqueue_size(struct blockingqueue* blockingqueue) +{ + size_t size; + blockingqueue_mutex_lock(&blockingqueue->data_lock); + size = queue_size(blockingqueue->queue); + blockingqueue_mutex_unlock(&blockingqueue->data_lock); + return size; +} + + +#endif diff --git a/.bash.d/tmp/util/Makefile b/.bash.d/tmp/util/Makefile new file mode 100644 index 0000000..722430a --- /dev/null +++ b/.bash.d/tmp/util/Makefile @@ -0,0 +1,29 @@ +# ============================================================================== +# Makefile +# ============================================================================== +TOPDIR ?= ../.. +TARGET = libutil.so +SUBDIRS = +VERSION = +#TARGET_TYPE = target-exe +#TARGET_TYPE = target-a +TARGET_TYPE = target-so +#TARGET_TYPE = target-dll + +include $(TOPDIR)/mk/conf.mk +include $(TOPDIR)/mk/$(USE)/com.*.mk +include $(TOPDIR)/mk/$(USE)/conf.def.*.mk + +CFLAGS += -fPIC +CXXFLAGS += +LDFLAGS += + +.PHONY: all test +all: all-subdir $(TARGET) + +test: + $(MAKE) -C test + + +include $(TOPDIR)/mk/$(USE)/rule.*.mk + diff --git a/.bash.d/tmp/util/include/util_blockingqueue.h b/.bash.d/tmp/util/include/util_blockingqueue.h new file mode 100644 index 0000000..0309bff --- /dev/null +++ b/.bash.d/tmp/util/include/util_blockingqueue.h @@ -0,0 +1,24 @@ +#ifndef UTIL_BLOCKINGQUEUE_H +#define UTIL_BLOCKINGQUEUE_H + +#include + + +/** + * ブロッキングキューのインスタンス構造体 + */ +struct blockingqueue; + +struct blockingqueue* blockingqueue_new(int cap); +void blockingqueue_destroy(struct blockingqueue* queue); +bool blockingqueue_push(struct blockingqueue* queue, void* data, size_t size); +bool blockingqueue_push_blocking(struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_pop( struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_pop_blocking( struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_peek(struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_size(struct blockingqueue* queue); +void blockingqueue_notify(struct blockingqueue* queue); + + +#endif /* UTIL_BLOCKINGQUEUE_H */ + diff --git a/.bash.d/tmp/util/include/util_queue.h b/.bash.d/tmp/util/include/util_queue.h new file mode 100644 index 0000000..2de9762 --- /dev/null +++ b/.bash.d/tmp/util/include/util_queue.h @@ -0,0 +1,21 @@ +#ifndef UTIL_QUEUE_H +#define UTIL_QUEUE_H + +#include + + +/** キュー */ +struct queue; +struct queue* queue_new(size_t cap, size_t size); +void queue_destroy(struct queue* queue); +bool queue_push(struct queue* queue, void* data, size_t size); +size_t queue_pop( struct queue* queue, void* data, size_t size); +size_t queue_peek(struct queue* queue, void* data, size_t size); +size_t queue_size(struct queue* queue); +void queue_entries(struct queue* queue, bool (*handler)(void* data, size_t size)); +void queue_entries_full(struct queue* queue, + bool (*handler)(void* data, size_t size, int index, bool enabled, void* arg), + void* arg); + +#endif /* UTIL_QUEUE_H */ + diff --git a/.bash.d/tmp/util/include/util_thread.h b/.bash.d/tmp/util/include/util_thread.h new file mode 100644 index 0000000..60e7bb0 --- /dev/null +++ b/.bash.d/tmp/util/include/util_thread.h @@ -0,0 +1,38 @@ +/** + * @file util_thread.c + * スレッドを扱うモジュール + * + * 実装側のみ複数のOS,ライブラリ用に変更できるよう + * 各オブジェクトを typedef ではなく、構造体にしている。 + */ +#ifndef UTIL_THREAD_H +#define UTIL_THREAD_H + +#include + +/* スレッド */ +struct thread; +struct thread* thread_new(void (*start_routine)(void*), void* arg); +void thread_delete(struct thread* thread); +void thread_start(struct thread* thread); +bool thread_join(struct thread* thread); +bool thread_equals(struct thread* thread); + +/* mutex */ +struct mutex; +struct mutex* mutex_new(void); +bool mutex_delete(struct mutex* mutex); +void mutex_lock(struct mutex* mutex); +void mutex_unlock(struct mutex* mutex); + +/* cond */ +struct cond; +struct cond* cond_new(void); +bool cond_delete(struct cond* cond); +void cond_wait(struct cond* cond, struct mutex* mutex); +void cond_signal(struct cond* cond); +void cond_broadcast(struct cond* cond); + + +#endif /* THREAD_H */ + diff --git a/.bash.d/tmp/util/src/logger.c b/.bash.d/tmp/util/src/logger.c new file mode 100644 index 0000000..e96204d --- /dev/null +++ b/.bash.d/tmp/util/src/logger.c @@ -0,0 +1,117 @@ +#include +#include +#include +#include +#include +#include + +#include "logger.h" + + + +// ============================================================================= +// 内部でのみ使用する関数のプロトタイプ宣言 +// ============================================================================= +static void logger_log_output(int priority, const char* message); +static int get_date(char* buff, size_t size); + + + + + +// ============================================================================= +// 関数定義 +// ============================================================================= + +/** + * 指定されたログレベルのログを出力可能か否かを返します。 + * + * @param priority ログレベル + * @return true/false (ログ出力可能/ログ出力不可) + */ +bool logger_is_logable(int priority) +{ + int current_logmask = setlogmask(0); + return (current_logmask & priority); +} + + + +/** + * ログを出力します。 + * + * @param priority syslog と同じログのレベルを指定します。 + * @param file __FILE__ を指定します。 + * @param line __LINE__ を指定します。 + * @param format 書式を指定します。 + */ +void logger_log_(int priority, const char* file, int line, const char* format, ...) +{ + va_list ap; + va_start(ap, format); + bool is_logable = logger_is_logable(priority); + if (is_logable) + { + char log_buffer[512]; + int len = get_date(log_buffer, sizeof(log_buffer)); + len += snprintf(&log_buffer[len], sizeof(log_buffer) - len, " %s:%d: ", file, line); + vsnprintf(&log_buffer[len], sizeof(log_buffer) - len, format, ap); + logger_log_output(priority, log_buffer); + } + va_end(ap); +} + + +/** + * 指定されたメッセージをログに出力します。 + * + * @param priority ログレベル + * @param message 出力するメッセージ + */ +static void logger_log_output(int priority, const char* message) +{ +#ifdef USE_SYSLOG + static bool is_logopend = false; + if (!is_logopend) + { + openlog("ifconv", LOG_CONS | LOG_PID, LOG_USER); + is_logopend = true; + } + syslog(priority, "%s", message); +#else + pid_t current_pid = getpid(); + printf("ifconv[%d][pri=%d]: %s\n", current_pid, priority, message); +#endif +} + + +/** + * 指定されたバッファに、現在の時刻(ナノ秒まで)を文字列形式で格納します。 + * + * @param buff バッファ + * @param size バッファサイズ + * @return バッファに書き込んだ文字数 + */ +static int get_date(char* buff, size_t size) +{ + // ナノ秒まで取得する。 + struct timespec current_time; + clock_gettime(CLOCK_REALTIME, ¤t_time); + + // 秒を日時に変換する。 + struct tm* local_tm = localtime(¤t_time.tv_sec); + + // YYYY/MM/DD HH:MM:SS.XXXXXXXXX に変換する。 + int len = snprintf(buff, size, "%04d/%02d/%02d %02d:%02d:%02d.%09ld", + local_tm->tm_year + 1900, + local_tm->tm_mon + 1, + local_tm->tm_mday, + local_tm->tm_hour, + local_tm->tm_min, + local_tm->tm_sec, + current_time.tv_nsec); + + return len; +} + + diff --git a/.bash.d/tmp/util/src/util_blockingqueue.c b/.bash.d/tmp/util/src/util_blockingqueue.c new file mode 100644 index 0000000..5b66c33 --- /dev/null +++ b/.bash.d/tmp/util/src/util_blockingqueue.c @@ -0,0 +1,200 @@ +#include +#include +#include +#include + +#include "util_queue.h" +#include "util_blockingqueue.h" + +/** + * ブロッキングキューのインスタンス構造体 + */ +struct blockingqueue { + struct queue* queue; /**< キュー */ + struct mutex* data_lock; /**< データロック */ + struct cond* empty_block; /**< pop のブロック用 */ + struct cond* full_block; /**< push のブロック用 */ +}; + + +/** + * ブロッキングキューを生成します。 + * インスタンスを生成できない場合、NULL を返します。 + * + * @param cap キューの容量 + * @param size キューに格納するデータサイズ + * @return イベントキューのインスタンス + */ +struct blockingqueue* blockingqueue_new(size_t cap, size_t size) +{ + struct blockingqueue* blockingqueue = (struct blockingqueue*) malloc(sizeof(struct blockingqueue)); + if (blockingqueue != NULL) + { + blockingqueue->queue = queue_new(cap,4096); + if (blockingqueue->queue != NULL) + { // pthread_mutex_lock ですでに保持している mutex をロックしようとしたとき、 + // デフォルトのスレッドを停止させる動作とするため、NULL (デフォルト設定)を指定する。 + pthread_mutex_init(&blockingqueue->data_lock, NULL); + + // データがない場合 pop をブロックするための cond オブジェクトを初期化する。 + pthread_cond_init(&blockingqueue->pop_block, NULL); + } + else + { + free(blockingqueue); + blockingqueue = NULL; + } + + } + return blockingqueue; +} + + + + +/** + * 指定されたイベントキューを破棄します。 + * + * @param blockingqueue イベントキューのインスタンス + */ +void blockingqueue_destroy(struct blockingqueue* blockingqueue) +{ + if (blockingqueue != NULL) + { + // T.B.D. + queue_destroy(blockingqueue->queue); + blockingqueue->queue = NULL; + } + free(blockingqueue); +} + + +/** + * 指定された mutex をロックします。 + * + * @param mutex mutexオブジェクト + */ +static +void blockingqueue_mutex_lock(pthread_mutex_t* mutex) +{ + int errcode = pthread_mutex_lock(mutex); + if (errcode != 0) + { // エラーが発生する条件は、下記の何れかであり、本プログラムの設定では発生しない。 + // => 発生するのであれば、プログラム誤り。 + // EINVAL : mutex が適切に初期化されていない。 + // EDEADLK : mutex は既に呼び出しスレッドによりロックされている (「エラー検査を行う」mutexes のみ) + printf("###!!! [ERROR] pthred_mutex_lock !!!###\n"); + } +} + + +/** + * 指定された mutex をアンロックします。 + * + * @param mutex mutexオブジェクト + */ +static +void blockingqueue_mutex_unlock(pthread_mutex_t* mutex) +{ + int errcode = pthread_mutex_unlock(mutex); + if (errcode != 0) + { // エラーが発生する条件は、下記の何れかであり、本プログラムの設定では発生しない。 + // => 発生するのであれば、プログラム誤り。 + // EINVAL : mutex が適切に初期化されていない。 + // EDEADLK : mutex は既に呼び出しスレッドによりロックされている (「エラー検査を行う」mutexes のみ) + printf("###!!! [ERROR] pthred_mutex_lock !!!###\n"); + } +} + + +/** + * イベントキューにデータを追加します。 + * + * @param blockingqueue キューのインスタンス + * @param data キューに追加するエントリ(データ) + * @param size キューに追加するエントリ(データ)のサイズ + * @return true/false (追加成功/追加失敗) + */ +bool blockingqueue_push(struct blockingqueue* blockingqueue, void* data, size_t size) +{ + blockingqueue_mutex_lock(&blockingqueue->data_lock); + + // キューにデータがない間ブロックする。 + size_t data_count = queue_size(blockingqueue->queue); + if (data_count == 0) + { + pthread_cond_signal(&blockingqueue->pop_block); + } + + bool result = queue_push(blockingqueue->queue, data, size); + + blockingqueue_mutex_unlock(&blockingqueue->data_lock); + + return result; +} + + +/** + * キューの先頭よりデータを取り出します。 + * キューにデータがない場合、ブロックされます。 + * + * @param blockingqueue キューのインスタンス + * @param buf 取り出したデータ格納用バッファ + * @param buflen バッファのサイズ + * @return 取り出したデータのサイズ + */ +size_t blockingqueue_pop( struct blockingqueue* blockingqueue, void* buf, size_t buflen) +{ + blockingqueue_mutex_lock(&blockingqueue->data_lock); + + // キューにデータがない間ブロックする。 + size_t data_count = queue_size(blockingqueue->queue); + while (data_count == 0) + { + pthread_cond_wait(&blockingqueue->pop_block, &blockingqueue->data_lock); + data_count = queue_size(blockingqueue->queue); + } + + size_t data_size = queue_pop(blockingqueue->queue, buf, buflen); + blockingqueue_mutex_unlock(&blockingqueue->data_lock); + return data_size; +} + + +/** + * キューの先頭よりデータを取り出します。 + * データの取り出しに失敗した場合、0を返します。 + * blockingqueue_pop と異なり、キューからデータは削除されません。 + * + * @param blockingqueue キューのインスタンス + * @param buf 取り出したデータ格納用バッファ + * @param buflen バッファのサイズ + * @return 取り出したデータのサイズ + */ +size_t blockingqueue_peek( struct blockingqueue* blockingqueue, void* buf, size_t buflen) +{ + blockingqueue_mutex_lock(&blockingqueue->data_lock); + size_t size = queue_peek(blockingqueue->queue, buf, buflen); + blockingqueue_mutex_unlock(&blockingqueue->data_lock); + return size; +} + + + +/** + * キューのサイズ(キューに入っているデータの個数)を取得します。 + * + * @param blockingqueue キューのインスタンス + * @return キューのサイズ + */ +size_t blockingqueue_size(struct blockingqueue* blockingqueue) +{ + size_t size; + blockingqueue_mutex_lock(&blockingqueue->data_lock); + size = queue_size(blockingqueue->queue); + blockingqueue_mutex_unlock(&blockingqueue->data_lock); + return size; +} + + +#endif diff --git a/.bash.d/tmp/util/src/util_queue.c b/.bash.d/tmp/util/src/util_queue.c new file mode 100644 index 0000000..fb2e153 --- /dev/null +++ b/.bash.d/tmp/util/src/util_queue.c @@ -0,0 +1,341 @@ +#include +#include +#include + +#include "util_thread.h" +#include "util_queue.h" + + + +/* ============================================================================= + * 構造体定義 + * ============================================================================= + */ + +/** + * キューのエントリ構造体 + */ +struct queue_entry { + size_t size; /**< 実データサイズ */ + char* data; /**< データバッファ */ +}; + + +/** + * キューのインスタンス構造体 + */ +struct queue { + size_t capacity; /**< キューの容量 */ + size_t size; /**< キューのサイズ */ + size_t max_data_size; /**< データの最大サイズ */ + struct queue_entry* entries; /**< データのエントリ */ + struct queue_entry* head; /**< キューの取出位置 */ + struct queue_entry* tail; /**< キューの挿入位置 */ +}; + + + +/* ============================================================================= + * 内部関数プロトタイプ宣言 + * ============================================================================= + */ +static struct queue_entry* queue_next_entry(struct queue* queue, struct queue_entry* entry); +static bool queue_is_enabled(struct queue* queue, struct queue_entry* entry); + + + +/* ============================================================================= + * 公開関数 + * ============================================================================= + */ + +/** + * キューを生成します。 + * インスタンスを生成できない場合、NULL を返します。 + * + * [注意] + * このキューは、同期化されません。 + * 同期化が必要な場合は、blocking_queue を使用してください。 + * + * @param cap キューの容量(格納可能なデータの個数) + * @param size キューに格納する1つあたりのデータサイズ + * @return キューのインスタンス + */ +/* [実装メモ] + * キューのデータ管理構造 + * メモリ確保を 1 回の malloc でできるようにしている。 + * +-------------------+ + * | queue 構造体 | + * +-------------------+ + * | entry[0] | + * +-------------------+ + * : + * +-------------------+ + * | entry[cap-1] | + +-------------------+ + * | entry[0].data | + * | 用バッファ | + * +-------------------+ + * : + * +-------------------+ + * | entry[cap-1].data | + * | 用バッファ | + * +-------------------+ + */ +struct queue* queue_new(size_t cap, size_t size) +{ + size_t queue_size = sizeof(struct queue) + + ((sizeof(struct queue_entry) + size) * cap); + struct queue* queue = (struct queue*) malloc(queue_size); + if (queue != NULL) + { + queue->capacity = cap; + queue->size = 0; + queue->max_data_size = size; + queue->entries = (struct queue_entry*) (queue + 1); + queue->head = queue->entries; + queue->tail = queue->entries; + + + char* tmp_data_ptr = (char*) (queue->entries + queue->capacity); + struct queue_entry* tmp_entry = queue->entries; + for (size_t i = 0; i < queue->capacity; i++) + { + tmp_entry->data = tmp_data_ptr; + tmp_entry++; + tmp_data_ptr += queue->max_data_size; + } + } + return queue; +} + + + +/** + * 指定されたキューを破棄します。 + * + * @param queue キューのインスタンス + */ +void queue_destroy(struct queue* queue) +{ + free(queue); +} + + +/** + * キューにデータを追加します。 + * + * @param queue キューのインスタンス + * @param data キューに追加するエントリ(データ) + * @param size キューに追加するエントリ(データ)のサイズ + * @return true/false (追加成功/追加失敗) + */ +bool queue_push(struct queue* queue, void* data, size_t size) +{ + bool result = false; + + if (size < queue->max_data_size) + { + if (queue->size < queue->capacity) + { + memset(queue->tail->data, 0x00, sizeof(struct queue_entry)); + memcpy(queue->tail->data, data, size); + queue->tail->size = size; + queue->tail++; + if (queue->tail >= (queue->entries + queue->capacity)) + { + queue->tail = queue->entries; + } + queue->size++; + result = true; + } + } + return result; +} + + +/** + * キューの先頭よりデータを取り出します。 + * データの取り出しに失敗した場合、0を返します。 + * + * @param queue キューのインスタンス + * @param buf 取り出したデータ格納用バッファ + * @param buflen バッファのサイズ + * @return 取り出したデータのサイズ + */ +size_t queue_pop( struct queue* queue, void* buf, size_t buflen) +{ + size_t size = 0; + if (queue->size > 0) + { + if (queue->head->size < buflen) + { + size = queue->head->size; + memset(buf, 0x00, buflen); + memcpy(buf, queue->head->data, size); + + // データクリア (なくても動作上問題ない) + memset(queue->head->data, 0x00, queue->max_data_size); + queue->head->size = 0; + + queue->head++; + if (queue->head >= (queue->entries + queue->capacity)) + { + queue->head = queue->entries; + } + queue->size--; + } + } + return size; +} + + +/** + * キューの先頭よりデータを取り出します。 + * データの取り出しに失敗した場合、0を返します。 + * queue_pop と異なり、キューからデータは削除されません。 + * + * @param queue キューのインスタンス + * @param buf 取り出したデータ格納用バッファ + * @param buflen バッファのサイズ + * @return 取り出したデータのサイズ + */ +size_t queue_peek( struct queue* queue, void* buf, size_t buflen) +{ + size_t size = 0; + if (queue->size > 0) + { + if (queue->head->size < buflen) + { + size = queue->head->size; + memset(buf, 0x00, buflen); + memcpy(buf, queue->head->data, size); + } + } + return size; +} + + +/** + * キューのサイズ(キューに入っているデータの個数)を取得します。 + * + * @param queue キューのインスタンス + * @return キューのサイズ + */ +size_t queue_size(struct queue* queue) +{ + return queue->size; +} + + +/** + * キューに格納されている全エントリーを引数に、指定された handler を呼び出します。 + * + * handler の引数: + * - data : データ + * - size : データのサイズ + * + * @param queue キューのインスタンス + * @param handler ハンドラ + */ +void queue_entries(struct queue* queue, bool (*handler)(void* data, size_t size)) +{ + bool is_continue = true; + size_t counter = queue->size; + struct queue_entry* entry = queue->head; + while (is_continue && (counter > 0)) + { + is_continue = handler(entry->data, entry->size); + entry = queue_next_entry(queue, entry); + counter--; + } +} + + +/** + * キューインスタンスの全エントリを引数に、指定された handler を呼び出します。 + * この関数は、デバッグ用の関数です。 + * handler には、実際にデータが入っていないキューのエントリも渡されます。 + * + * handler の引数: + * - data : データ + * - size : データのサイズ + * - index : キューの管理上のインデックス + * - enabled : true/false (有効なデータ/無効なデータ) + * - arg : ユーザーデータ + * + * @param queue キューのインスタンス + * @param handler ハンドラ + * @param arg ハンドラに渡すユーザーデータ + */ +void queue_entries_full(struct queue* queue, + bool (*handler)(void* data, size_t size, int index, bool enabled, void* arg), + void* arg) +{ + bool is_enabled; + bool is_continue = true; + struct queue_entry* entry = queue->entries; + for (int i = 0; is_continue && (i < (int) queue->capacity); i++) + { + is_enabled = queue_is_enabled(queue, entry); + is_continue = handler(entry->data, entry->size, i, is_enabled, arg); + entry++; + } +} + + + +/* ============================================================================= + * 内部関数 + * ============================================================================= + */ + + +/** + * 指定されたエントリの次のエントリーを取得します。 + * エントリー配列の末尾に到達した場合、先頭のエントリを返します。 + * + * @param queue キューのインスタンス + * @param entry エントリ + * @return 次のエントリ + */ +static +struct queue_entry* queue_next_entry(struct queue* queue, struct queue_entry* entry) +{ + struct queue_entry* next_entry = entry; + next_entry++; + if (next_entry >= (queue->entries + queue->capacity)) + { + next_entry = queue->entries; + } + return next_entry; +} + + +/** + * 指定されたエントリが、有効か否かを返します。 + * + * @param queue キューのインスタンス + * @param entry エントリ + * @return true/false (有効/無効) + */ +static +bool queue_is_enabled(struct queue* queue, struct queue_entry* entry) +{ + bool is_enabled; + if ((queue->size != queue->capacity) && (queue->head <= queue->tail)) + { /* キューのエントリ配列が以下のような状態の場合 + * [- |- |HT-|- |- |- |- |- ] + * [- |- |Ho |o |o |T-|- |- ] (-:空/o:データ有/H:Head/T:Tail) + */ + is_enabled = ((queue->head <= entry) && (entry < queue->tail)); + } + else + { /* キューのエントリ配列が以下のような状態の場合 + * [o |o |HTo|o |o |o |o |o ] + * [o |o |T- |- |- |Ho|o |o ] (-:空/o:データ有/H:Head/T:Tail) + */ + is_enabled = ((entry < queue->tail) || (queue->head <= entry)); + } + return is_enabled; +} diff --git a/.bash.d/tmp/util/Makefile b/.bash.d/tmp/util/Makefile new file mode 100644 index 0000000..722430a --- /dev/null +++ b/.bash.d/tmp/util/Makefile @@ -0,0 +1,29 @@ +# ============================================================================== +# Makefile +# ============================================================================== +TOPDIR ?= ../.. +TARGET = libutil.so +SUBDIRS = +VERSION = +#TARGET_TYPE = target-exe +#TARGET_TYPE = target-a +TARGET_TYPE = target-so +#TARGET_TYPE = target-dll + +include $(TOPDIR)/mk/conf.mk +include $(TOPDIR)/mk/$(USE)/com.*.mk +include $(TOPDIR)/mk/$(USE)/conf.def.*.mk + +CFLAGS += -fPIC +CXXFLAGS += +LDFLAGS += + +.PHONY: all test +all: all-subdir $(TARGET) + +test: + $(MAKE) -C test + + +include $(TOPDIR)/mk/$(USE)/rule.*.mk + diff --git a/.bash.d/tmp/util/include/util_blockingqueue.h b/.bash.d/tmp/util/include/util_blockingqueue.h new file mode 100644 index 0000000..0309bff --- /dev/null +++ b/.bash.d/tmp/util/include/util_blockingqueue.h @@ -0,0 +1,24 @@ +#ifndef UTIL_BLOCKINGQUEUE_H +#define UTIL_BLOCKINGQUEUE_H + +#include + + +/** + * ブロッキングキューのインスタンス構造体 + */ +struct blockingqueue; + +struct blockingqueue* blockingqueue_new(int cap); +void blockingqueue_destroy(struct blockingqueue* queue); +bool blockingqueue_push(struct blockingqueue* queue, void* data, size_t size); +bool blockingqueue_push_blocking(struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_pop( struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_pop_blocking( struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_peek(struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_size(struct blockingqueue* queue); +void blockingqueue_notify(struct blockingqueue* queue); + + +#endif /* UTIL_BLOCKINGQUEUE_H */ + diff --git a/.bash.d/tmp/util/include/util_queue.h b/.bash.d/tmp/util/include/util_queue.h new file mode 100644 index 0000000..2de9762 --- /dev/null +++ b/.bash.d/tmp/util/include/util_queue.h @@ -0,0 +1,21 @@ +#ifndef UTIL_QUEUE_H +#define UTIL_QUEUE_H + +#include + + +/** キュー */ +struct queue; +struct queue* queue_new(size_t cap, size_t size); +void queue_destroy(struct queue* queue); +bool queue_push(struct queue* queue, void* data, size_t size); +size_t queue_pop( struct queue* queue, void* data, size_t size); +size_t queue_peek(struct queue* queue, void* data, size_t size); +size_t queue_size(struct queue* queue); +void queue_entries(struct queue* queue, bool (*handler)(void* data, size_t size)); +void queue_entries_full(struct queue* queue, + bool (*handler)(void* data, size_t size, int index, bool enabled, void* arg), + void* arg); + +#endif /* UTIL_QUEUE_H */ + diff --git a/.bash.d/tmp/util/include/util_thread.h b/.bash.d/tmp/util/include/util_thread.h new file mode 100644 index 0000000..60e7bb0 --- /dev/null +++ b/.bash.d/tmp/util/include/util_thread.h @@ -0,0 +1,38 @@ +/** + * @file util_thread.c + * スレッドを扱うモジュール + * + * 実装側のみ複数のOS,ライブラリ用に変更できるよう + * 各オブジェクトを typedef ではなく、構造体にしている。 + */ +#ifndef UTIL_THREAD_H +#define UTIL_THREAD_H + +#include + +/* スレッド */ +struct thread; +struct thread* thread_new(void (*start_routine)(void*), void* arg); +void thread_delete(struct thread* thread); +void thread_start(struct thread* thread); +bool thread_join(struct thread* thread); +bool thread_equals(struct thread* thread); + +/* mutex */ +struct mutex; +struct mutex* mutex_new(void); +bool mutex_delete(struct mutex* mutex); +void mutex_lock(struct mutex* mutex); +void mutex_unlock(struct mutex* mutex); + +/* cond */ +struct cond; +struct cond* cond_new(void); +bool cond_delete(struct cond* cond); +void cond_wait(struct cond* cond, struct mutex* mutex); +void cond_signal(struct cond* cond); +void cond_broadcast(struct cond* cond); + + +#endif /* THREAD_H */ + diff --git a/.bash.d/tmp/util/src/logger.c b/.bash.d/tmp/util/src/logger.c new file mode 100644 index 0000000..e96204d --- /dev/null +++ b/.bash.d/tmp/util/src/logger.c @@ -0,0 +1,117 @@ +#include +#include +#include +#include +#include +#include + +#include "logger.h" + + + +// ============================================================================= +// 内部でのみ使用する関数のプロトタイプ宣言 +// ============================================================================= +static void logger_log_output(int priority, const char* message); +static int get_date(char* buff, size_t size); + + + + + +// ============================================================================= +// 関数定義 +// ============================================================================= + +/** + * 指定されたログレベルのログを出力可能か否かを返します。 + * + * @param priority ログレベル + * @return true/false (ログ出力可能/ログ出力不可) + */ +bool logger_is_logable(int priority) +{ + int current_logmask = setlogmask(0); + return (current_logmask & priority); +} + + + +/** + * ログを出力します。 + * + * @param priority syslog と同じログのレベルを指定します。 + * @param file __FILE__ を指定します。 + * @param line __LINE__ を指定します。 + * @param format 書式を指定します。 + */ +void logger_log_(int priority, const char* file, int line, const char* format, ...) +{ + va_list ap; + va_start(ap, format); + bool is_logable = logger_is_logable(priority); + if (is_logable) + { + char log_buffer[512]; + int len = get_date(log_buffer, sizeof(log_buffer)); + len += snprintf(&log_buffer[len], sizeof(log_buffer) - len, " %s:%d: ", file, line); + vsnprintf(&log_buffer[len], sizeof(log_buffer) - len, format, ap); + logger_log_output(priority, log_buffer); + } + va_end(ap); +} + + +/** + * 指定されたメッセージをログに出力します。 + * + * @param priority ログレベル + * @param message 出力するメッセージ + */ +static void logger_log_output(int priority, const char* message) +{ +#ifdef USE_SYSLOG + static bool is_logopend = false; + if (!is_logopend) + { + openlog("ifconv", LOG_CONS | LOG_PID, LOG_USER); + is_logopend = true; + } + syslog(priority, "%s", message); +#else + pid_t current_pid = getpid(); + printf("ifconv[%d][pri=%d]: %s\n", current_pid, priority, message); +#endif +} + + +/** + * 指定されたバッファに、現在の時刻(ナノ秒まで)を文字列形式で格納します。 + * + * @param buff バッファ + * @param size バッファサイズ + * @return バッファに書き込んだ文字数 + */ +static int get_date(char* buff, size_t size) +{ + // ナノ秒まで取得する。 + struct timespec current_time; + clock_gettime(CLOCK_REALTIME, ¤t_time); + + // 秒を日時に変換する。 + struct tm* local_tm = localtime(¤t_time.tv_sec); + + // YYYY/MM/DD HH:MM:SS.XXXXXXXXX に変換する。 + int len = snprintf(buff, size, "%04d/%02d/%02d %02d:%02d:%02d.%09ld", + local_tm->tm_year + 1900, + local_tm->tm_mon + 1, + local_tm->tm_mday, + local_tm->tm_hour, + local_tm->tm_min, + local_tm->tm_sec, + current_time.tv_nsec); + + return len; +} + + diff --git a/.bash.d/tmp/util/src/util_blockingqueue.c b/.bash.d/tmp/util/src/util_blockingqueue.c new file mode 100644 index 0000000..5b66c33 --- /dev/null +++ b/.bash.d/tmp/util/src/util_blockingqueue.c @@ -0,0 +1,200 @@ +#include +#include +#include +#include + +#include "util_queue.h" +#include "util_blockingqueue.h" + +/** + * ブロッキングキューのインスタンス構造体 + */ +struct blockingqueue { + struct queue* queue; /**< キュー */ + struct mutex* data_lock; /**< データロック */ + struct cond* empty_block; /**< pop のブロック用 */ + struct cond* full_block; /**< push のブロック用 */ +}; + + +/** + * ブロッキングキューを生成します。 + * インスタンスを生成できない場合、NULL を返します。 + * + * @param cap キューの容量 + * @param size キューに格納するデータサイズ + * @return イベントキューのインスタンス + */ +struct blockingqueue* blockingqueue_new(size_t cap, size_t size) +{ + struct blockingqueue* blockingqueue = (struct blockingqueue*) malloc(sizeof(struct blockingqueue)); + if (blockingqueue != NULL) + { + blockingqueue->queue = queue_new(cap,4096); + if (blockingqueue->queue != NULL) + { // pthread_mutex_lock ですでに保持している mutex をロックしようとしたとき、 + // デフォルトのスレッドを停止させる動作とするため、NULL (デフォルト設定)を指定する。 + pthread_mutex_init(&blockingqueue->data_lock, NULL); + + // データがない場合 pop をブロックするための cond オブジェクトを初期化する。 + pthread_cond_init(&blockingqueue->pop_block, NULL); + } + else + { + free(blockingqueue); + blockingqueue = NULL; + } + + } + return blockingqueue; +} + + + + +/** + * 指定されたイベントキューを破棄します。 + * + * @param blockingqueue イベントキューのインスタンス + */ +void blockingqueue_destroy(struct blockingqueue* blockingqueue) +{ + if (blockingqueue != NULL) + { + // T.B.D. + queue_destroy(blockingqueue->queue); + blockingqueue->queue = NULL; + } + free(blockingqueue); +} + + +/** + * 指定された mutex をロックします。 + * + * @param mutex mutexオブジェクト + */ +static +void blockingqueue_mutex_lock(pthread_mutex_t* mutex) +{ + int errcode = pthread_mutex_lock(mutex); + if (errcode != 0) + { // エラーが発生する条件は、下記の何れかであり、本プログラムの設定では発生しない。 + // => 発生するのであれば、プログラム誤り。 + // EINVAL : mutex が適切に初期化されていない。 + // EDEADLK : mutex は既に呼び出しスレッドによりロックされている (「エラー検査を行う」mutexes のみ) + printf("###!!! [ERROR] pthred_mutex_lock !!!###\n"); + } +} + + +/** + * 指定された mutex をアンロックします。 + * + * @param mutex mutexオブジェクト + */ +static +void blockingqueue_mutex_unlock(pthread_mutex_t* mutex) +{ + int errcode = pthread_mutex_unlock(mutex); + if (errcode != 0) + { // エラーが発生する条件は、下記の何れかであり、本プログラムの設定では発生しない。 + // => 発生するのであれば、プログラム誤り。 + // EINVAL : mutex が適切に初期化されていない。 + // EDEADLK : mutex は既に呼び出しスレッドによりロックされている (「エラー検査を行う」mutexes のみ) + printf("###!!! [ERROR] pthred_mutex_lock !!!###\n"); + } +} + + +/** + * イベントキューにデータを追加します。 + * + * @param blockingqueue キューのインスタンス + * @param data キューに追加するエントリ(データ) + * @param size キューに追加するエントリ(データ)のサイズ + * @return true/false (追加成功/追加失敗) + */ +bool blockingqueue_push(struct blockingqueue* blockingqueue, void* data, size_t size) +{ + blockingqueue_mutex_lock(&blockingqueue->data_lock); + + // キューにデータがない間ブロックする。 + size_t data_count = queue_size(blockingqueue->queue); + if (data_count == 0) + { + pthread_cond_signal(&blockingqueue->pop_block); + } + + bool result = queue_push(blockingqueue->queue, data, size); + + blockingqueue_mutex_unlock(&blockingqueue->data_lock); + + return result; +} + + +/** + * キューの先頭よりデータを取り出します。 + * キューにデータがない場合、ブロックされます。 + * + * @param blockingqueue キューのインスタンス + * @param buf 取り出したデータ格納用バッファ + * @param buflen バッファのサイズ + * @return 取り出したデータのサイズ + */ +size_t blockingqueue_pop( struct blockingqueue* blockingqueue, void* buf, size_t buflen) +{ + blockingqueue_mutex_lock(&blockingqueue->data_lock); + + // キューにデータがない間ブロックする。 + size_t data_count = queue_size(blockingqueue->queue); + while (data_count == 0) + { + pthread_cond_wait(&blockingqueue->pop_block, &blockingqueue->data_lock); + data_count = queue_size(blockingqueue->queue); + } + + size_t data_size = queue_pop(blockingqueue->queue, buf, buflen); + blockingqueue_mutex_unlock(&blockingqueue->data_lock); + return data_size; +} + + +/** + * キューの先頭よりデータを取り出します。 + * データの取り出しに失敗した場合、0を返します。 + * blockingqueue_pop と異なり、キューからデータは削除されません。 + * + * @param blockingqueue キューのインスタンス + * @param buf 取り出したデータ格納用バッファ + * @param buflen バッファのサイズ + * @return 取り出したデータのサイズ + */ +size_t blockingqueue_peek( struct blockingqueue* blockingqueue, void* buf, size_t buflen) +{ + blockingqueue_mutex_lock(&blockingqueue->data_lock); + size_t size = queue_peek(blockingqueue->queue, buf, buflen); + blockingqueue_mutex_unlock(&blockingqueue->data_lock); + return size; +} + + + +/** + * キューのサイズ(キューに入っているデータの個数)を取得します。 + * + * @param blockingqueue キューのインスタンス + * @return キューのサイズ + */ +size_t blockingqueue_size(struct blockingqueue* blockingqueue) +{ + size_t size; + blockingqueue_mutex_lock(&blockingqueue->data_lock); + size = queue_size(blockingqueue->queue); + blockingqueue_mutex_unlock(&blockingqueue->data_lock); + return size; +} + + +#endif diff --git a/.bash.d/tmp/util/src/util_queue.c b/.bash.d/tmp/util/src/util_queue.c new file mode 100644 index 0000000..fb2e153 --- /dev/null +++ b/.bash.d/tmp/util/src/util_queue.c @@ -0,0 +1,341 @@ +#include +#include +#include + +#include "util_thread.h" +#include "util_queue.h" + + + +/* ============================================================================= + * 構造体定義 + * ============================================================================= + */ + +/** + * キューのエントリ構造体 + */ +struct queue_entry { + size_t size; /**< 実データサイズ */ + char* data; /**< データバッファ */ +}; + + +/** + * キューのインスタンス構造体 + */ +struct queue { + size_t capacity; /**< キューの容量 */ + size_t size; /**< キューのサイズ */ + size_t max_data_size; /**< データの最大サイズ */ + struct queue_entry* entries; /**< データのエントリ */ + struct queue_entry* head; /**< キューの取出位置 */ + struct queue_entry* tail; /**< キューの挿入位置 */ +}; + + + +/* ============================================================================= + * 内部関数プロトタイプ宣言 + * ============================================================================= + */ +static struct queue_entry* queue_next_entry(struct queue* queue, struct queue_entry* entry); +static bool queue_is_enabled(struct queue* queue, struct queue_entry* entry); + + + +/* ============================================================================= + * 公開関数 + * ============================================================================= + */ + +/** + * キューを生成します。 + * インスタンスを生成できない場合、NULL を返します。 + * + * [注意] + * このキューは、同期化されません。 + * 同期化が必要な場合は、blocking_queue を使用してください。 + * + * @param cap キューの容量(格納可能なデータの個数) + * @param size キューに格納する1つあたりのデータサイズ + * @return キューのインスタンス + */ +/* [実装メモ] + * キューのデータ管理構造 + * メモリ確保を 1 回の malloc でできるようにしている。 + * +-------------------+ + * | queue 構造体 | + * +-------------------+ + * | entry[0] | + * +-------------------+ + * : + * +-------------------+ + * | entry[cap-1] | + +-------------------+ + * | entry[0].data | + * | 用バッファ | + * +-------------------+ + * : + * +-------------------+ + * | entry[cap-1].data | + * | 用バッファ | + * +-------------------+ + */ +struct queue* queue_new(size_t cap, size_t size) +{ + size_t queue_size = sizeof(struct queue) + + ((sizeof(struct queue_entry) + size) * cap); + struct queue* queue = (struct queue*) malloc(queue_size); + if (queue != NULL) + { + queue->capacity = cap; + queue->size = 0; + queue->max_data_size = size; + queue->entries = (struct queue_entry*) (queue + 1); + queue->head = queue->entries; + queue->tail = queue->entries; + + + char* tmp_data_ptr = (char*) (queue->entries + queue->capacity); + struct queue_entry* tmp_entry = queue->entries; + for (size_t i = 0; i < queue->capacity; i++) + { + tmp_entry->data = tmp_data_ptr; + tmp_entry++; + tmp_data_ptr += queue->max_data_size; + } + } + return queue; +} + + + +/** + * 指定されたキューを破棄します。 + * + * @param queue キューのインスタンス + */ +void queue_destroy(struct queue* queue) +{ + free(queue); +} + + +/** + * キューにデータを追加します。 + * + * @param queue キューのインスタンス + * @param data キューに追加するエントリ(データ) + * @param size キューに追加するエントリ(データ)のサイズ + * @return true/false (追加成功/追加失敗) + */ +bool queue_push(struct queue* queue, void* data, size_t size) +{ + bool result = false; + + if (size < queue->max_data_size) + { + if (queue->size < queue->capacity) + { + memset(queue->tail->data, 0x00, sizeof(struct queue_entry)); + memcpy(queue->tail->data, data, size); + queue->tail->size = size; + queue->tail++; + if (queue->tail >= (queue->entries + queue->capacity)) + { + queue->tail = queue->entries; + } + queue->size++; + result = true; + } + } + return result; +} + + +/** + * キューの先頭よりデータを取り出します。 + * データの取り出しに失敗した場合、0を返します。 + * + * @param queue キューのインスタンス + * @param buf 取り出したデータ格納用バッファ + * @param buflen バッファのサイズ + * @return 取り出したデータのサイズ + */ +size_t queue_pop( struct queue* queue, void* buf, size_t buflen) +{ + size_t size = 0; + if (queue->size > 0) + { + if (queue->head->size < buflen) + { + size = queue->head->size; + memset(buf, 0x00, buflen); + memcpy(buf, queue->head->data, size); + + // データクリア (なくても動作上問題ない) + memset(queue->head->data, 0x00, queue->max_data_size); + queue->head->size = 0; + + queue->head++; + if (queue->head >= (queue->entries + queue->capacity)) + { + queue->head = queue->entries; + } + queue->size--; + } + } + return size; +} + + +/** + * キューの先頭よりデータを取り出します。 + * データの取り出しに失敗した場合、0を返します。 + * queue_pop と異なり、キューからデータは削除されません。 + * + * @param queue キューのインスタンス + * @param buf 取り出したデータ格納用バッファ + * @param buflen バッファのサイズ + * @return 取り出したデータのサイズ + */ +size_t queue_peek( struct queue* queue, void* buf, size_t buflen) +{ + size_t size = 0; + if (queue->size > 0) + { + if (queue->head->size < buflen) + { + size = queue->head->size; + memset(buf, 0x00, buflen); + memcpy(buf, queue->head->data, size); + } + } + return size; +} + + +/** + * キューのサイズ(キューに入っているデータの個数)を取得します。 + * + * @param queue キューのインスタンス + * @return キューのサイズ + */ +size_t queue_size(struct queue* queue) +{ + return queue->size; +} + + +/** + * キューに格納されている全エントリーを引数に、指定された handler を呼び出します。 + * + * handler の引数: + * - data : データ + * - size : データのサイズ + * + * @param queue キューのインスタンス + * @param handler ハンドラ + */ +void queue_entries(struct queue* queue, bool (*handler)(void* data, size_t size)) +{ + bool is_continue = true; + size_t counter = queue->size; + struct queue_entry* entry = queue->head; + while (is_continue && (counter > 0)) + { + is_continue = handler(entry->data, entry->size); + entry = queue_next_entry(queue, entry); + counter--; + } +} + + +/** + * キューインスタンスの全エントリを引数に、指定された handler を呼び出します。 + * この関数は、デバッグ用の関数です。 + * handler には、実際にデータが入っていないキューのエントリも渡されます。 + * + * handler の引数: + * - data : データ + * - size : データのサイズ + * - index : キューの管理上のインデックス + * - enabled : true/false (有効なデータ/無効なデータ) + * - arg : ユーザーデータ + * + * @param queue キューのインスタンス + * @param handler ハンドラ + * @param arg ハンドラに渡すユーザーデータ + */ +void queue_entries_full(struct queue* queue, + bool (*handler)(void* data, size_t size, int index, bool enabled, void* arg), + void* arg) +{ + bool is_enabled; + bool is_continue = true; + struct queue_entry* entry = queue->entries; + for (int i = 0; is_continue && (i < (int) queue->capacity); i++) + { + is_enabled = queue_is_enabled(queue, entry); + is_continue = handler(entry->data, entry->size, i, is_enabled, arg); + entry++; + } +} + + + +/* ============================================================================= + * 内部関数 + * ============================================================================= + */ + + +/** + * 指定されたエントリの次のエントリーを取得します。 + * エントリー配列の末尾に到達した場合、先頭のエントリを返します。 + * + * @param queue キューのインスタンス + * @param entry エントリ + * @return 次のエントリ + */ +static +struct queue_entry* queue_next_entry(struct queue* queue, struct queue_entry* entry) +{ + struct queue_entry* next_entry = entry; + next_entry++; + if (next_entry >= (queue->entries + queue->capacity)) + { + next_entry = queue->entries; + } + return next_entry; +} + + +/** + * 指定されたエントリが、有効か否かを返します。 + * + * @param queue キューのインスタンス + * @param entry エントリ + * @return true/false (有効/無効) + */ +static +bool queue_is_enabled(struct queue* queue, struct queue_entry* entry) +{ + bool is_enabled; + if ((queue->size != queue->capacity) && (queue->head <= queue->tail)) + { /* キューのエントリ配列が以下のような状態の場合 + * [- |- |HT-|- |- |- |- |- ] + * [- |- |Ho |o |o |T-|- |- ] (-:空/o:データ有/H:Head/T:Tail) + */ + is_enabled = ((queue->head <= entry) && (entry < queue->tail)); + } + else + { /* キューのエントリ配列が以下のような状態の場合 + * [o |o |HTo|o |o |o |o |o ] + * [o |o |T- |- |- |Ho|o |o ] (-:空/o:データ有/H:Head/T:Tail) + */ + is_enabled = ((entry < queue->tail) || (queue->head <= entry)); + } + return is_enabled; +} diff --git a/.bash.d/tmp/util/src/util_thread.c b/.bash.d/tmp/util/src/util_thread.c new file mode 100644 index 0000000..20c6fc6 --- /dev/null +++ b/.bash.d/tmp/util/src/util_thread.c @@ -0,0 +1,341 @@ +/** + * @file util_thread.c + * スレッドを扱うモジュール。 + */ +#include +#include +#include +#include + +#include "util_thread.h" + + + +/* ///////////////////////////////////////////////////////////////////////////// +// +// スレッド +// +*/ + + +/* ============================================================================ + * 構造体定義 + * ============================================================================ + */ + +/** + * スレッドインスタンス構造体。 + */ +struct thread { + void (*start_routine)(void*); /**< スレッドとして実行する関数 */ + void* arg; /**< スレッドに渡すデータ */ + pthread_t tid; /**< スレッドのID */ +}; + + + +/* ============================================================================ + * プロトタイプ宣言 (内部でのみ利用する関数) + * ============================================================================ + */ +static void* thread_run(void* args); + + + +/* ============================================================================ + * 公開関数 + * ============================================================================ + */ + +/** + * スレッドを生成します。 + * スレッドの生成に失敗した場合、NULL を返します。 + * + * @param start_routine スレッドで実行する関数 + * @param arg スレッドに渡すデータへのポインタ + * @return スレッドインスタンス + */ +struct thread* thread_new(void (*start_routine)(void*), void* arg) +{ + struct thread* thread = (struct thread*) malloc(sizeof(struct thread)); + if (thread != NULL) + { + thread->start_routine = start_routine; + thread->arg = arg; + } + return thread; +} + + +/** + * スレッドインスタンスのリソースを開放します。 + * スレッド自体が終了するわけではありません。 + * + * @param thread スレッドインスタンス + */ +void thread_delete(struct thread* thread) +{ + free(thread); +} + + +/** + * スレッドを開始します。 + * + * @param thread スレッドインスタンス + */ +void thread_start(struct thread* thread) +{ + pthread_create(&thread->tid, NULL, thread_run, thread); +} + + +/** + * 指定されたスレッドが終了するのを待ちます。 + * + * @param thread スレッドインスタンス + */ +bool thread_join(struct thread* thread) +{ + int ret = pthread_join(thread->tid, NULL); + return (ret == 0); +} + + +/** + * 現在のスレッドが指定されたスレッドと同一か否かを返します。 + * + * @param thread スレッドインスタンス + * @return true/false (一致/不一致) + */ +bool thread_equals(struct thread* thread) +{ + pthread_t tid = pthread_self(); + return (tid == thread->tid); +} + + + +/* ============================================================================ + * 内部関数 + * ============================================================================ + */ + +/** + * スレッドとして実行される関数。 + * この関数の中で、スレッド起動に指定された関数を実行します。 + * + * @param arg スレッドインスタンス + */ +static +void* thread_run(void* arg) +{ + struct thread* thread = (struct thread*) arg; + thread->start_routine(thread->arg); + return NULL; +} + + + +/* ///////////////////////////////////////////////////////////////////////////// +// +// mutex +// +*/ + + +/* ============================================================================ + * 構造体定義 + * ============================================================================ + */ + +/** + * mutex インスタンス構造体。 + */ +struct mutex { + pthread_mutex_t mutex; /**< mutex オブジェクト */ +}; + + + +/* ============================================================================ + * 公開関数 + * ============================================================================ + */ + +/** + * mutex を生成します。 + * mutex の生成に失敗した場合、NULL を返します。 + * + * @return mutex + */ + +struct mutex* mutex_new(void) +{ + struct mutex* mutex = (struct mutex*) malloc(sizeof(struct mutex)); + if (mutex != NULL) + { + pthread_mutex_init(&mutex->mutex, NULL); + } + return mutex; +} + + +/** + * mutex を破棄します。 + * mutex がロックされている場合は、破棄に失敗しエラーを返します。 + * + * @param mutex 破棄する mutex + * @return true/false (破棄成功/破棄失敗 [mutex がロックされている]) + */ +bool mutex_delete(struct mutex* mutex) +{ + if (mutex != NULL) + { + int res = pthread_mutex_destroy(&mutex->mutex); + if (res == 0) + { + free(mutex); + return true; + } + } + return false; +} + + +/** + * mutex をロックします。 + * + * @param mutex ロックする mutex + */ +void mutex_lock(struct mutex* mutex) +{ + // mutex は、デフォルトの種別で初期化済みのため、 + // EINVAL, EDEADLK のエラーは発生しない。 + pthread_mutex_lock(&mutex->mutex); +} + + +/** + * mutex をアンロックします。 + * + * @param mutex アンロックする mutex + */ +void mutex_unlock(struct mutex* mutex) +{ + // mutex は、デフォルトの種別で初期化済みのため、 + // EINVAL, EPERM のエラーは発生しない。 + pthread_mutex_unlock(&mutex->mutex); +} + + + +/* ///////////////////////////////////////////////////////////////////////////// +// +// cond +// +*/ + + +/* ============================================================================ + * 構造体定義 + * ============================================================================ + */ + +/** + * cond インスタンス構造体。 + */ +struct cond { + pthread_cond_t cond; /**< cond オブジェクト */ +}; + + + +/* ============================================================================ + * 公開関数 + * ============================================================================ + */ + +/** + * cond を生成します。 + * cond の生成に失敗した場合、NULL を返します。 + * + * @return cond + */ + +struct cond* cond_new(void) +{ + struct cond* cond = (struct cond*) malloc(sizeof(struct cond)); + if (cond != NULL) + { + pthread_cond_init(&cond->cond, NULL); + } + return cond; +} + + +/** + * cond を破棄します。 + * cond が条件変数を待っている場合、破棄に失敗しエラーを返します。 + * + * @param cond 破棄する cond + * @return true/false (破棄成功/破棄失敗 [cond が条件変数を待っている]) + */ +bool cond_delete(struct cond* cond) +{ + if (cond!= NULL) + { + int res = pthread_cond_destroy(&cond->cond); + if (res == 0) + { + free(cond); + return true; + } + } + return false; +} + + +/** + * 指定された mutex のアンロックと、条件変数 cond の送信に対する待機を + * アトミックに行います。条件変数が送信されるまで、スレッドの実行は停止され、 + * CPU時間を消費しません。 + * + * 本関数を実行する前に、 mutex はロックされている必要があります。 + * 本関数を呼び出しスレッドが、条件変数の待機完了により動作する際、 + * mutex は再びロックされます。 + * + * @param cond cond インスタンス + * @param mutex mutex ロック済みの mutex + */ +void cond_wait(struct cond* cond, struct mutex* mutex) +{ + pthread_cond_wait(&cond->cond, &mutex->mutex); +} + + +/** + * 条件変数 cond に備えて待機しているスレッドの一つの実行を再開させます。 + * cond を待機しているスレッドがなければ何もしません。 + * 複数のスレッドが cond を待機している場合、どのスレッドが再開されるかはわからない。 + * + * @param cond 再開させる cond + */ +void cond_signal(struct cond* cond) +{ + pthread_cond_signal(&cond->cond); +} + + +/** + * 条件変数 cond に備えて待機しているすべてのスレッドを再開させます。 + * cond を待機しているスレッドがなければ何もしません。 + * + * @param cond 再開させる cond + */ +void cond_broadcast(struct cond* cond) +{ + pthread_cond_broadcast(&cond->cond); +} + + diff --git a/.bash.d/tmp/util/Makefile b/.bash.d/tmp/util/Makefile new file mode 100644 index 0000000..722430a --- /dev/null +++ b/.bash.d/tmp/util/Makefile @@ -0,0 +1,29 @@ +# ============================================================================== +# Makefile +# ============================================================================== +TOPDIR ?= ../.. +TARGET = libutil.so +SUBDIRS = +VERSION = +#TARGET_TYPE = target-exe +#TARGET_TYPE = target-a +TARGET_TYPE = target-so +#TARGET_TYPE = target-dll + +include $(TOPDIR)/mk/conf.mk +include $(TOPDIR)/mk/$(USE)/com.*.mk +include $(TOPDIR)/mk/$(USE)/conf.def.*.mk + +CFLAGS += -fPIC +CXXFLAGS += +LDFLAGS += + +.PHONY: all test +all: all-subdir $(TARGET) + +test: + $(MAKE) -C test + + +include $(TOPDIR)/mk/$(USE)/rule.*.mk + diff --git a/.bash.d/tmp/util/include/util_blockingqueue.h b/.bash.d/tmp/util/include/util_blockingqueue.h new file mode 100644 index 0000000..0309bff --- /dev/null +++ b/.bash.d/tmp/util/include/util_blockingqueue.h @@ -0,0 +1,24 @@ +#ifndef UTIL_BLOCKINGQUEUE_H +#define UTIL_BLOCKINGQUEUE_H + +#include + + +/** + * ブロッキングキューのインスタンス構造体 + */ +struct blockingqueue; + +struct blockingqueue* blockingqueue_new(int cap); +void blockingqueue_destroy(struct blockingqueue* queue); +bool blockingqueue_push(struct blockingqueue* queue, void* data, size_t size); +bool blockingqueue_push_blocking(struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_pop( struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_pop_blocking( struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_peek(struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_size(struct blockingqueue* queue); +void blockingqueue_notify(struct blockingqueue* queue); + + +#endif /* UTIL_BLOCKINGQUEUE_H */ + diff --git a/.bash.d/tmp/util/include/util_queue.h b/.bash.d/tmp/util/include/util_queue.h new file mode 100644 index 0000000..2de9762 --- /dev/null +++ b/.bash.d/tmp/util/include/util_queue.h @@ -0,0 +1,21 @@ +#ifndef UTIL_QUEUE_H +#define UTIL_QUEUE_H + +#include + + +/** キュー */ +struct queue; +struct queue* queue_new(size_t cap, size_t size); +void queue_destroy(struct queue* queue); +bool queue_push(struct queue* queue, void* data, size_t size); +size_t queue_pop( struct queue* queue, void* data, size_t size); +size_t queue_peek(struct queue* queue, void* data, size_t size); +size_t queue_size(struct queue* queue); +void queue_entries(struct queue* queue, bool (*handler)(void* data, size_t size)); +void queue_entries_full(struct queue* queue, + bool (*handler)(void* data, size_t size, int index, bool enabled, void* arg), + void* arg); + +#endif /* UTIL_QUEUE_H */ + diff --git a/.bash.d/tmp/util/include/util_thread.h b/.bash.d/tmp/util/include/util_thread.h new file mode 100644 index 0000000..60e7bb0 --- /dev/null +++ b/.bash.d/tmp/util/include/util_thread.h @@ -0,0 +1,38 @@ +/** + * @file util_thread.c + * スレッドを扱うモジュール + * + * 実装側のみ複数のOS,ライブラリ用に変更できるよう + * 各オブジェクトを typedef ではなく、構造体にしている。 + */ +#ifndef UTIL_THREAD_H +#define UTIL_THREAD_H + +#include + +/* スレッド */ +struct thread; +struct thread* thread_new(void (*start_routine)(void*), void* arg); +void thread_delete(struct thread* thread); +void thread_start(struct thread* thread); +bool thread_join(struct thread* thread); +bool thread_equals(struct thread* thread); + +/* mutex */ +struct mutex; +struct mutex* mutex_new(void); +bool mutex_delete(struct mutex* mutex); +void mutex_lock(struct mutex* mutex); +void mutex_unlock(struct mutex* mutex); + +/* cond */ +struct cond; +struct cond* cond_new(void); +bool cond_delete(struct cond* cond); +void cond_wait(struct cond* cond, struct mutex* mutex); +void cond_signal(struct cond* cond); +void cond_broadcast(struct cond* cond); + + +#endif /* THREAD_H */ + diff --git a/.bash.d/tmp/util/src/logger.c b/.bash.d/tmp/util/src/logger.c new file mode 100644 index 0000000..e96204d --- /dev/null +++ b/.bash.d/tmp/util/src/logger.c @@ -0,0 +1,117 @@ +#include +#include +#include +#include +#include +#include + +#include "logger.h" + + + +// ============================================================================= +// 内部でのみ使用する関数のプロトタイプ宣言 +// ============================================================================= +static void logger_log_output(int priority, const char* message); +static int get_date(char* buff, size_t size); + + + + + +// ============================================================================= +// 関数定義 +// ============================================================================= + +/** + * 指定されたログレベルのログを出力可能か否かを返します。 + * + * @param priority ログレベル + * @return true/false (ログ出力可能/ログ出力不可) + */ +bool logger_is_logable(int priority) +{ + int current_logmask = setlogmask(0); + return (current_logmask & priority); +} + + + +/** + * ログを出力します。 + * + * @param priority syslog と同じログのレベルを指定します。 + * @param file __FILE__ を指定します。 + * @param line __LINE__ を指定します。 + * @param format 書式を指定します。 + */ +void logger_log_(int priority, const char* file, int line, const char* format, ...) +{ + va_list ap; + va_start(ap, format); + bool is_logable = logger_is_logable(priority); + if (is_logable) + { + char log_buffer[512]; + int len = get_date(log_buffer, sizeof(log_buffer)); + len += snprintf(&log_buffer[len], sizeof(log_buffer) - len, " %s:%d: ", file, line); + vsnprintf(&log_buffer[len], sizeof(log_buffer) - len, format, ap); + logger_log_output(priority, log_buffer); + } + va_end(ap); +} + + +/** + * 指定されたメッセージをログに出力します。 + * + * @param priority ログレベル + * @param message 出力するメッセージ + */ +static void logger_log_output(int priority, const char* message) +{ +#ifdef USE_SYSLOG + static bool is_logopend = false; + if (!is_logopend) + { + openlog("ifconv", LOG_CONS | LOG_PID, LOG_USER); + is_logopend = true; + } + syslog(priority, "%s", message); +#else + pid_t current_pid = getpid(); + printf("ifconv[%d][pri=%d]: %s\n", current_pid, priority, message); +#endif +} + + +/** + * 指定されたバッファに、現在の時刻(ナノ秒まで)を文字列形式で格納します。 + * + * @param buff バッファ + * @param size バッファサイズ + * @return バッファに書き込んだ文字数 + */ +static int get_date(char* buff, size_t size) +{ + // ナノ秒まで取得する。 + struct timespec current_time; + clock_gettime(CLOCK_REALTIME, ¤t_time); + + // 秒を日時に変換する。 + struct tm* local_tm = localtime(¤t_time.tv_sec); + + // YYYY/MM/DD HH:MM:SS.XXXXXXXXX に変換する。 + int len = snprintf(buff, size, "%04d/%02d/%02d %02d:%02d:%02d.%09ld", + local_tm->tm_year + 1900, + local_tm->tm_mon + 1, + local_tm->tm_mday, + local_tm->tm_hour, + local_tm->tm_min, + local_tm->tm_sec, + current_time.tv_nsec); + + return len; +} + + diff --git a/.bash.d/tmp/util/src/util_blockingqueue.c b/.bash.d/tmp/util/src/util_blockingqueue.c new file mode 100644 index 0000000..5b66c33 --- /dev/null +++ b/.bash.d/tmp/util/src/util_blockingqueue.c @@ -0,0 +1,200 @@ +#include +#include +#include +#include + +#include "util_queue.h" +#include "util_blockingqueue.h" + +/** + * ブロッキングキューのインスタンス構造体 + */ +struct blockingqueue { + struct queue* queue; /**< キュー */ + struct mutex* data_lock; /**< データロック */ + struct cond* empty_block; /**< pop のブロック用 */ + struct cond* full_block; /**< push のブロック用 */ +}; + + +/** + * ブロッキングキューを生成します。 + * インスタンスを生成できない場合、NULL を返します。 + * + * @param cap キューの容量 + * @param size キューに格納するデータサイズ + * @return イベントキューのインスタンス + */ +struct blockingqueue* blockingqueue_new(size_t cap, size_t size) +{ + struct blockingqueue* blockingqueue = (struct blockingqueue*) malloc(sizeof(struct blockingqueue)); + if (blockingqueue != NULL) + { + blockingqueue->queue = queue_new(cap,4096); + if (blockingqueue->queue != NULL) + { // pthread_mutex_lock ですでに保持している mutex をロックしようとしたとき、 + // デフォルトのスレッドを停止させる動作とするため、NULL (デフォルト設定)を指定する。 + pthread_mutex_init(&blockingqueue->data_lock, NULL); + + // データがない場合 pop をブロックするための cond オブジェクトを初期化する。 + pthread_cond_init(&blockingqueue->pop_block, NULL); + } + else + { + free(blockingqueue); + blockingqueue = NULL; + } + + } + return blockingqueue; +} + + + + +/** + * 指定されたイベントキューを破棄します。 + * + * @param blockingqueue イベントキューのインスタンス + */ +void blockingqueue_destroy(struct blockingqueue* blockingqueue) +{ + if (blockingqueue != NULL) + { + // T.B.D. + queue_destroy(blockingqueue->queue); + blockingqueue->queue = NULL; + } + free(blockingqueue); +} + + +/** + * 指定された mutex をロックします。 + * + * @param mutex mutexオブジェクト + */ +static +void blockingqueue_mutex_lock(pthread_mutex_t* mutex) +{ + int errcode = pthread_mutex_lock(mutex); + if (errcode != 0) + { // エラーが発生する条件は、下記の何れかであり、本プログラムの設定では発生しない。 + // => 発生するのであれば、プログラム誤り。 + // EINVAL : mutex が適切に初期化されていない。 + // EDEADLK : mutex は既に呼び出しスレッドによりロックされている (「エラー検査を行う」mutexes のみ) + printf("###!!! [ERROR] pthred_mutex_lock !!!###\n"); + } +} + + +/** + * 指定された mutex をアンロックします。 + * + * @param mutex mutexオブジェクト + */ +static +void blockingqueue_mutex_unlock(pthread_mutex_t* mutex) +{ + int errcode = pthread_mutex_unlock(mutex); + if (errcode != 0) + { // エラーが発生する条件は、下記の何れかであり、本プログラムの設定では発生しない。 + // => 発生するのであれば、プログラム誤り。 + // EINVAL : mutex が適切に初期化されていない。 + // EDEADLK : mutex は既に呼び出しスレッドによりロックされている (「エラー検査を行う」mutexes のみ) + printf("###!!! [ERROR] pthred_mutex_lock !!!###\n"); + } +} + + +/** + * イベントキューにデータを追加します。 + * + * @param blockingqueue キューのインスタンス + * @param data キューに追加するエントリ(データ) + * @param size キューに追加するエントリ(データ)のサイズ + * @return true/false (追加成功/追加失敗) + */ +bool blockingqueue_push(struct blockingqueue* blockingqueue, void* data, size_t size) +{ + blockingqueue_mutex_lock(&blockingqueue->data_lock); + + // キューにデータがない間ブロックする。 + size_t data_count = queue_size(blockingqueue->queue); + if (data_count == 0) + { + pthread_cond_signal(&blockingqueue->pop_block); + } + + bool result = queue_push(blockingqueue->queue, data, size); + + blockingqueue_mutex_unlock(&blockingqueue->data_lock); + + return result; +} + + +/** + * キューの先頭よりデータを取り出します。 + * キューにデータがない場合、ブロックされます。 + * + * @param blockingqueue キューのインスタンス + * @param buf 取り出したデータ格納用バッファ + * @param buflen バッファのサイズ + * @return 取り出したデータのサイズ + */ +size_t blockingqueue_pop( struct blockingqueue* blockingqueue, void* buf, size_t buflen) +{ + blockingqueue_mutex_lock(&blockingqueue->data_lock); + + // キューにデータがない間ブロックする。 + size_t data_count = queue_size(blockingqueue->queue); + while (data_count == 0) + { + pthread_cond_wait(&blockingqueue->pop_block, &blockingqueue->data_lock); + data_count = queue_size(blockingqueue->queue); + } + + size_t data_size = queue_pop(blockingqueue->queue, buf, buflen); + blockingqueue_mutex_unlock(&blockingqueue->data_lock); + return data_size; +} + + +/** + * キューの先頭よりデータを取り出します。 + * データの取り出しに失敗した場合、0を返します。 + * blockingqueue_pop と異なり、キューからデータは削除されません。 + * + * @param blockingqueue キューのインスタンス + * @param buf 取り出したデータ格納用バッファ + * @param buflen バッファのサイズ + * @return 取り出したデータのサイズ + */ +size_t blockingqueue_peek( struct blockingqueue* blockingqueue, void* buf, size_t buflen) +{ + blockingqueue_mutex_lock(&blockingqueue->data_lock); + size_t size = queue_peek(blockingqueue->queue, buf, buflen); + blockingqueue_mutex_unlock(&blockingqueue->data_lock); + return size; +} + + + +/** + * キューのサイズ(キューに入っているデータの個数)を取得します。 + * + * @param blockingqueue キューのインスタンス + * @return キューのサイズ + */ +size_t blockingqueue_size(struct blockingqueue* blockingqueue) +{ + size_t size; + blockingqueue_mutex_lock(&blockingqueue->data_lock); + size = queue_size(blockingqueue->queue); + blockingqueue_mutex_unlock(&blockingqueue->data_lock); + return size; +} + + +#endif diff --git a/.bash.d/tmp/util/src/util_queue.c b/.bash.d/tmp/util/src/util_queue.c new file mode 100644 index 0000000..fb2e153 --- /dev/null +++ b/.bash.d/tmp/util/src/util_queue.c @@ -0,0 +1,341 @@ +#include +#include +#include + +#include "util_thread.h" +#include "util_queue.h" + + + +/* ============================================================================= + * 構造体定義 + * ============================================================================= + */ + +/** + * キューのエントリ構造体 + */ +struct queue_entry { + size_t size; /**< 実データサイズ */ + char* data; /**< データバッファ */ +}; + + +/** + * キューのインスタンス構造体 + */ +struct queue { + size_t capacity; /**< キューの容量 */ + size_t size; /**< キューのサイズ */ + size_t max_data_size; /**< データの最大サイズ */ + struct queue_entry* entries; /**< データのエントリ */ + struct queue_entry* head; /**< キューの取出位置 */ + struct queue_entry* tail; /**< キューの挿入位置 */ +}; + + + +/* ============================================================================= + * 内部関数プロトタイプ宣言 + * ============================================================================= + */ +static struct queue_entry* queue_next_entry(struct queue* queue, struct queue_entry* entry); +static bool queue_is_enabled(struct queue* queue, struct queue_entry* entry); + + + +/* ============================================================================= + * 公開関数 + * ============================================================================= + */ + +/** + * キューを生成します。 + * インスタンスを生成できない場合、NULL を返します。 + * + * [注意] + * このキューは、同期化されません。 + * 同期化が必要な場合は、blocking_queue を使用してください。 + * + * @param cap キューの容量(格納可能なデータの個数) + * @param size キューに格納する1つあたりのデータサイズ + * @return キューのインスタンス + */ +/* [実装メモ] + * キューのデータ管理構造 + * メモリ確保を 1 回の malloc でできるようにしている。 + * +-------------------+ + * | queue 構造体 | + * +-------------------+ + * | entry[0] | + * +-------------------+ + * : + * +-------------------+ + * | entry[cap-1] | + +-------------------+ + * | entry[0].data | + * | 用バッファ | + * +-------------------+ + * : + * +-------------------+ + * | entry[cap-1].data | + * | 用バッファ | + * +-------------------+ + */ +struct queue* queue_new(size_t cap, size_t size) +{ + size_t queue_size = sizeof(struct queue) + + ((sizeof(struct queue_entry) + size) * cap); + struct queue* queue = (struct queue*) malloc(queue_size); + if (queue != NULL) + { + queue->capacity = cap; + queue->size = 0; + queue->max_data_size = size; + queue->entries = (struct queue_entry*) (queue + 1); + queue->head = queue->entries; + queue->tail = queue->entries; + + + char* tmp_data_ptr = (char*) (queue->entries + queue->capacity); + struct queue_entry* tmp_entry = queue->entries; + for (size_t i = 0; i < queue->capacity; i++) + { + tmp_entry->data = tmp_data_ptr; + tmp_entry++; + tmp_data_ptr += queue->max_data_size; + } + } + return queue; +} + + + +/** + * 指定されたキューを破棄します。 + * + * @param queue キューのインスタンス + */ +void queue_destroy(struct queue* queue) +{ + free(queue); +} + + +/** + * キューにデータを追加します。 + * + * @param queue キューのインスタンス + * @param data キューに追加するエントリ(データ) + * @param size キューに追加するエントリ(データ)のサイズ + * @return true/false (追加成功/追加失敗) + */ +bool queue_push(struct queue* queue, void* data, size_t size) +{ + bool result = false; + + if (size < queue->max_data_size) + { + if (queue->size < queue->capacity) + { + memset(queue->tail->data, 0x00, sizeof(struct queue_entry)); + memcpy(queue->tail->data, data, size); + queue->tail->size = size; + queue->tail++; + if (queue->tail >= (queue->entries + queue->capacity)) + { + queue->tail = queue->entries; + } + queue->size++; + result = true; + } + } + return result; +} + + +/** + * キューの先頭よりデータを取り出します。 + * データの取り出しに失敗した場合、0を返します。 + * + * @param queue キューのインスタンス + * @param buf 取り出したデータ格納用バッファ + * @param buflen バッファのサイズ + * @return 取り出したデータのサイズ + */ +size_t queue_pop( struct queue* queue, void* buf, size_t buflen) +{ + size_t size = 0; + if (queue->size > 0) + { + if (queue->head->size < buflen) + { + size = queue->head->size; + memset(buf, 0x00, buflen); + memcpy(buf, queue->head->data, size); + + // データクリア (なくても動作上問題ない) + memset(queue->head->data, 0x00, queue->max_data_size); + queue->head->size = 0; + + queue->head++; + if (queue->head >= (queue->entries + queue->capacity)) + { + queue->head = queue->entries; + } + queue->size--; + } + } + return size; +} + + +/** + * キューの先頭よりデータを取り出します。 + * データの取り出しに失敗した場合、0を返します。 + * queue_pop と異なり、キューからデータは削除されません。 + * + * @param queue キューのインスタンス + * @param buf 取り出したデータ格納用バッファ + * @param buflen バッファのサイズ + * @return 取り出したデータのサイズ + */ +size_t queue_peek( struct queue* queue, void* buf, size_t buflen) +{ + size_t size = 0; + if (queue->size > 0) + { + if (queue->head->size < buflen) + { + size = queue->head->size; + memset(buf, 0x00, buflen); + memcpy(buf, queue->head->data, size); + } + } + return size; +} + + +/** + * キューのサイズ(キューに入っているデータの個数)を取得します。 + * + * @param queue キューのインスタンス + * @return キューのサイズ + */ +size_t queue_size(struct queue* queue) +{ + return queue->size; +} + + +/** + * キューに格納されている全エントリーを引数に、指定された handler を呼び出します。 + * + * handler の引数: + * - data : データ + * - size : データのサイズ + * + * @param queue キューのインスタンス + * @param handler ハンドラ + */ +void queue_entries(struct queue* queue, bool (*handler)(void* data, size_t size)) +{ + bool is_continue = true; + size_t counter = queue->size; + struct queue_entry* entry = queue->head; + while (is_continue && (counter > 0)) + { + is_continue = handler(entry->data, entry->size); + entry = queue_next_entry(queue, entry); + counter--; + } +} + + +/** + * キューインスタンスの全エントリを引数に、指定された handler を呼び出します。 + * この関数は、デバッグ用の関数です。 + * handler には、実際にデータが入っていないキューのエントリも渡されます。 + * + * handler の引数: + * - data : データ + * - size : データのサイズ + * - index : キューの管理上のインデックス + * - enabled : true/false (有効なデータ/無効なデータ) + * - arg : ユーザーデータ + * + * @param queue キューのインスタンス + * @param handler ハンドラ + * @param arg ハンドラに渡すユーザーデータ + */ +void queue_entries_full(struct queue* queue, + bool (*handler)(void* data, size_t size, int index, bool enabled, void* arg), + void* arg) +{ + bool is_enabled; + bool is_continue = true; + struct queue_entry* entry = queue->entries; + for (int i = 0; is_continue && (i < (int) queue->capacity); i++) + { + is_enabled = queue_is_enabled(queue, entry); + is_continue = handler(entry->data, entry->size, i, is_enabled, arg); + entry++; + } +} + + + +/* ============================================================================= + * 内部関数 + * ============================================================================= + */ + + +/** + * 指定されたエントリの次のエントリーを取得します。 + * エントリー配列の末尾に到達した場合、先頭のエントリを返します。 + * + * @param queue キューのインスタンス + * @param entry エントリ + * @return 次のエントリ + */ +static +struct queue_entry* queue_next_entry(struct queue* queue, struct queue_entry* entry) +{ + struct queue_entry* next_entry = entry; + next_entry++; + if (next_entry >= (queue->entries + queue->capacity)) + { + next_entry = queue->entries; + } + return next_entry; +} + + +/** + * 指定されたエントリが、有効か否かを返します。 + * + * @param queue キューのインスタンス + * @param entry エントリ + * @return true/false (有効/無効) + */ +static +bool queue_is_enabled(struct queue* queue, struct queue_entry* entry) +{ + bool is_enabled; + if ((queue->size != queue->capacity) && (queue->head <= queue->tail)) + { /* キューのエントリ配列が以下のような状態の場合 + * [- |- |HT-|- |- |- |- |- ] + * [- |- |Ho |o |o |T-|- |- ] (-:空/o:データ有/H:Head/T:Tail) + */ + is_enabled = ((queue->head <= entry) && (entry < queue->tail)); + } + else + { /* キューのエントリ配列が以下のような状態の場合 + * [o |o |HTo|o |o |o |o |o ] + * [o |o |T- |- |- |Ho|o |o ] (-:空/o:データ有/H:Head/T:Tail) + */ + is_enabled = ((entry < queue->tail) || (queue->head <= entry)); + } + return is_enabled; +} diff --git a/.bash.d/tmp/util/src/util_thread.c b/.bash.d/tmp/util/src/util_thread.c new file mode 100644 index 0000000..20c6fc6 --- /dev/null +++ b/.bash.d/tmp/util/src/util_thread.c @@ -0,0 +1,341 @@ +/** + * @file util_thread.c + * スレッドを扱うモジュール。 + */ +#include +#include +#include +#include + +#include "util_thread.h" + + + +/* ///////////////////////////////////////////////////////////////////////////// +// +// スレッド +// +*/ + + +/* ============================================================================ + * 構造体定義 + * ============================================================================ + */ + +/** + * スレッドインスタンス構造体。 + */ +struct thread { + void (*start_routine)(void*); /**< スレッドとして実行する関数 */ + void* arg; /**< スレッドに渡すデータ */ + pthread_t tid; /**< スレッドのID */ +}; + + + +/* ============================================================================ + * プロトタイプ宣言 (内部でのみ利用する関数) + * ============================================================================ + */ +static void* thread_run(void* args); + + + +/* ============================================================================ + * 公開関数 + * ============================================================================ + */ + +/** + * スレッドを生成します。 + * スレッドの生成に失敗した場合、NULL を返します。 + * + * @param start_routine スレッドで実行する関数 + * @param arg スレッドに渡すデータへのポインタ + * @return スレッドインスタンス + */ +struct thread* thread_new(void (*start_routine)(void*), void* arg) +{ + struct thread* thread = (struct thread*) malloc(sizeof(struct thread)); + if (thread != NULL) + { + thread->start_routine = start_routine; + thread->arg = arg; + } + return thread; +} + + +/** + * スレッドインスタンスのリソースを開放します。 + * スレッド自体が終了するわけではありません。 + * + * @param thread スレッドインスタンス + */ +void thread_delete(struct thread* thread) +{ + free(thread); +} + + +/** + * スレッドを開始します。 + * + * @param thread スレッドインスタンス + */ +void thread_start(struct thread* thread) +{ + pthread_create(&thread->tid, NULL, thread_run, thread); +} + + +/** + * 指定されたスレッドが終了するのを待ちます。 + * + * @param thread スレッドインスタンス + */ +bool thread_join(struct thread* thread) +{ + int ret = pthread_join(thread->tid, NULL); + return (ret == 0); +} + + +/** + * 現在のスレッドが指定されたスレッドと同一か否かを返します。 + * + * @param thread スレッドインスタンス + * @return true/false (一致/不一致) + */ +bool thread_equals(struct thread* thread) +{ + pthread_t tid = pthread_self(); + return (tid == thread->tid); +} + + + +/* ============================================================================ + * 内部関数 + * ============================================================================ + */ + +/** + * スレッドとして実行される関数。 + * この関数の中で、スレッド起動に指定された関数を実行します。 + * + * @param arg スレッドインスタンス + */ +static +void* thread_run(void* arg) +{ + struct thread* thread = (struct thread*) arg; + thread->start_routine(thread->arg); + return NULL; +} + + + +/* ///////////////////////////////////////////////////////////////////////////// +// +// mutex +// +*/ + + +/* ============================================================================ + * 構造体定義 + * ============================================================================ + */ + +/** + * mutex インスタンス構造体。 + */ +struct mutex { + pthread_mutex_t mutex; /**< mutex オブジェクト */ +}; + + + +/* ============================================================================ + * 公開関数 + * ============================================================================ + */ + +/** + * mutex を生成します。 + * mutex の生成に失敗した場合、NULL を返します。 + * + * @return mutex + */ + +struct mutex* mutex_new(void) +{ + struct mutex* mutex = (struct mutex*) malloc(sizeof(struct mutex)); + if (mutex != NULL) + { + pthread_mutex_init(&mutex->mutex, NULL); + } + return mutex; +} + + +/** + * mutex を破棄します。 + * mutex がロックされている場合は、破棄に失敗しエラーを返します。 + * + * @param mutex 破棄する mutex + * @return true/false (破棄成功/破棄失敗 [mutex がロックされている]) + */ +bool mutex_delete(struct mutex* mutex) +{ + if (mutex != NULL) + { + int res = pthread_mutex_destroy(&mutex->mutex); + if (res == 0) + { + free(mutex); + return true; + } + } + return false; +} + + +/** + * mutex をロックします。 + * + * @param mutex ロックする mutex + */ +void mutex_lock(struct mutex* mutex) +{ + // mutex は、デフォルトの種別で初期化済みのため、 + // EINVAL, EDEADLK のエラーは発生しない。 + pthread_mutex_lock(&mutex->mutex); +} + + +/** + * mutex をアンロックします。 + * + * @param mutex アンロックする mutex + */ +void mutex_unlock(struct mutex* mutex) +{ + // mutex は、デフォルトの種別で初期化済みのため、 + // EINVAL, EPERM のエラーは発生しない。 + pthread_mutex_unlock(&mutex->mutex); +} + + + +/* ///////////////////////////////////////////////////////////////////////////// +// +// cond +// +*/ + + +/* ============================================================================ + * 構造体定義 + * ============================================================================ + */ + +/** + * cond インスタンス構造体。 + */ +struct cond { + pthread_cond_t cond; /**< cond オブジェクト */ +}; + + + +/* ============================================================================ + * 公開関数 + * ============================================================================ + */ + +/** + * cond を生成します。 + * cond の生成に失敗した場合、NULL を返します。 + * + * @return cond + */ + +struct cond* cond_new(void) +{ + struct cond* cond = (struct cond*) malloc(sizeof(struct cond)); + if (cond != NULL) + { + pthread_cond_init(&cond->cond, NULL); + } + return cond; +} + + +/** + * cond を破棄します。 + * cond が条件変数を待っている場合、破棄に失敗しエラーを返します。 + * + * @param cond 破棄する cond + * @return true/false (破棄成功/破棄失敗 [cond が条件変数を待っている]) + */ +bool cond_delete(struct cond* cond) +{ + if (cond!= NULL) + { + int res = pthread_cond_destroy(&cond->cond); + if (res == 0) + { + free(cond); + return true; + } + } + return false; +} + + +/** + * 指定された mutex のアンロックと、条件変数 cond の送信に対する待機を + * アトミックに行います。条件変数が送信されるまで、スレッドの実行は停止され、 + * CPU時間を消費しません。 + * + * 本関数を実行する前に、 mutex はロックされている必要があります。 + * 本関数を呼び出しスレッドが、条件変数の待機完了により動作する際、 + * mutex は再びロックされます。 + * + * @param cond cond インスタンス + * @param mutex mutex ロック済みの mutex + */ +void cond_wait(struct cond* cond, struct mutex* mutex) +{ + pthread_cond_wait(&cond->cond, &mutex->mutex); +} + + +/** + * 条件変数 cond に備えて待機しているスレッドの一つの実行を再開させます。 + * cond を待機しているスレッドがなければ何もしません。 + * 複数のスレッドが cond を待機している場合、どのスレッドが再開されるかはわからない。 + * + * @param cond 再開させる cond + */ +void cond_signal(struct cond* cond) +{ + pthread_cond_signal(&cond->cond); +} + + +/** + * 条件変数 cond に備えて待機しているすべてのスレッドを再開させます。 + * cond を待機しているスレッドがなければ何もしません。 + * + * @param cond 再開させる cond + */ +void cond_broadcast(struct cond* cond) +{ + pthread_cond_broadcast(&cond->cond); +} + + diff --git a/.bash.d/tmp/util/test/Makefile b/.bash.d/tmp/util/test/Makefile new file mode 100644 index 0000000..395b46a --- /dev/null +++ b/.bash.d/tmp/util/test/Makefile @@ -0,0 +1,29 @@ +# ============================================================================== +# Makefile +# ============================================================================== +TOPDIR ?= ../../.. +TARGET = ut_libutil.exe +SUBDIRS = +VERSION = +TARGET_TYPE = target-exe +#TARGET_TYPE = target-a +#TARGET_TYPE = target-so +#TARGET_TYPE = target-dll + +EXCLUDES = ../src/ut_queue.c + +include $(TOPDIR)/mk/conf.mk +include $(TOPDIR)/mk/$(USE)/com.*.mk +include $(TOPDIR)/mk/$(USE)/conf.ut.*.mk + +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -lcunit -lpthread + +.PHONY: all +all: all-subdir $(TARGET) + + +include $(TOPDIR)/mk/$(USE)/rule.*.mk + diff --git a/.bash.d/tmp/util/Makefile b/.bash.d/tmp/util/Makefile new file mode 100644 index 0000000..722430a --- /dev/null +++ b/.bash.d/tmp/util/Makefile @@ -0,0 +1,29 @@ +# ============================================================================== +# Makefile +# ============================================================================== +TOPDIR ?= ../.. +TARGET = libutil.so +SUBDIRS = +VERSION = +#TARGET_TYPE = target-exe +#TARGET_TYPE = target-a +TARGET_TYPE = target-so +#TARGET_TYPE = target-dll + +include $(TOPDIR)/mk/conf.mk +include $(TOPDIR)/mk/$(USE)/com.*.mk +include $(TOPDIR)/mk/$(USE)/conf.def.*.mk + +CFLAGS += -fPIC +CXXFLAGS += +LDFLAGS += + +.PHONY: all test +all: all-subdir $(TARGET) + +test: + $(MAKE) -C test + + +include $(TOPDIR)/mk/$(USE)/rule.*.mk + diff --git a/.bash.d/tmp/util/include/util_blockingqueue.h b/.bash.d/tmp/util/include/util_blockingqueue.h new file mode 100644 index 0000000..0309bff --- /dev/null +++ b/.bash.d/tmp/util/include/util_blockingqueue.h @@ -0,0 +1,24 @@ +#ifndef UTIL_BLOCKINGQUEUE_H +#define UTIL_BLOCKINGQUEUE_H + +#include + + +/** + * ブロッキングキューのインスタンス構造体 + */ +struct blockingqueue; + +struct blockingqueue* blockingqueue_new(int cap); +void blockingqueue_destroy(struct blockingqueue* queue); +bool blockingqueue_push(struct blockingqueue* queue, void* data, size_t size); +bool blockingqueue_push_blocking(struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_pop( struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_pop_blocking( struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_peek(struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_size(struct blockingqueue* queue); +void blockingqueue_notify(struct blockingqueue* queue); + + +#endif /* UTIL_BLOCKINGQUEUE_H */ + diff --git a/.bash.d/tmp/util/include/util_queue.h b/.bash.d/tmp/util/include/util_queue.h new file mode 100644 index 0000000..2de9762 --- /dev/null +++ b/.bash.d/tmp/util/include/util_queue.h @@ -0,0 +1,21 @@ +#ifndef UTIL_QUEUE_H +#define UTIL_QUEUE_H + +#include + + +/** キュー */ +struct queue; +struct queue* queue_new(size_t cap, size_t size); +void queue_destroy(struct queue* queue); +bool queue_push(struct queue* queue, void* data, size_t size); +size_t queue_pop( struct queue* queue, void* data, size_t size); +size_t queue_peek(struct queue* queue, void* data, size_t size); +size_t queue_size(struct queue* queue); +void queue_entries(struct queue* queue, bool (*handler)(void* data, size_t size)); +void queue_entries_full(struct queue* queue, + bool (*handler)(void* data, size_t size, int index, bool enabled, void* arg), + void* arg); + +#endif /* UTIL_QUEUE_H */ + diff --git a/.bash.d/tmp/util/include/util_thread.h b/.bash.d/tmp/util/include/util_thread.h new file mode 100644 index 0000000..60e7bb0 --- /dev/null +++ b/.bash.d/tmp/util/include/util_thread.h @@ -0,0 +1,38 @@ +/** + * @file util_thread.c + * スレッドを扱うモジュール + * + * 実装側のみ複数のOS,ライブラリ用に変更できるよう + * 各オブジェクトを typedef ではなく、構造体にしている。 + */ +#ifndef UTIL_THREAD_H +#define UTIL_THREAD_H + +#include + +/* スレッド */ +struct thread; +struct thread* thread_new(void (*start_routine)(void*), void* arg); +void thread_delete(struct thread* thread); +void thread_start(struct thread* thread); +bool thread_join(struct thread* thread); +bool thread_equals(struct thread* thread); + +/* mutex */ +struct mutex; +struct mutex* mutex_new(void); +bool mutex_delete(struct mutex* mutex); +void mutex_lock(struct mutex* mutex); +void mutex_unlock(struct mutex* mutex); + +/* cond */ +struct cond; +struct cond* cond_new(void); +bool cond_delete(struct cond* cond); +void cond_wait(struct cond* cond, struct mutex* mutex); +void cond_signal(struct cond* cond); +void cond_broadcast(struct cond* cond); + + +#endif /* THREAD_H */ + diff --git a/.bash.d/tmp/util/src/logger.c b/.bash.d/tmp/util/src/logger.c new file mode 100644 index 0000000..e96204d --- /dev/null +++ b/.bash.d/tmp/util/src/logger.c @@ -0,0 +1,117 @@ +#include +#include +#include +#include +#include +#include + +#include "logger.h" + + + +// ============================================================================= +// 内部でのみ使用する関数のプロトタイプ宣言 +// ============================================================================= +static void logger_log_output(int priority, const char* message); +static int get_date(char* buff, size_t size); + + + + + +// ============================================================================= +// 関数定義 +// ============================================================================= + +/** + * 指定されたログレベルのログを出力可能か否かを返します。 + * + * @param priority ログレベル + * @return true/false (ログ出力可能/ログ出力不可) + */ +bool logger_is_logable(int priority) +{ + int current_logmask = setlogmask(0); + return (current_logmask & priority); +} + + + +/** + * ログを出力します。 + * + * @param priority syslog と同じログのレベルを指定します。 + * @param file __FILE__ を指定します。 + * @param line __LINE__ を指定します。 + * @param format 書式を指定します。 + */ +void logger_log_(int priority, const char* file, int line, const char* format, ...) +{ + va_list ap; + va_start(ap, format); + bool is_logable = logger_is_logable(priority); + if (is_logable) + { + char log_buffer[512]; + int len = get_date(log_buffer, sizeof(log_buffer)); + len += snprintf(&log_buffer[len], sizeof(log_buffer) - len, " %s:%d: ", file, line); + vsnprintf(&log_buffer[len], sizeof(log_buffer) - len, format, ap); + logger_log_output(priority, log_buffer); + } + va_end(ap); +} + + +/** + * 指定されたメッセージをログに出力します。 + * + * @param priority ログレベル + * @param message 出力するメッセージ + */ +static void logger_log_output(int priority, const char* message) +{ +#ifdef USE_SYSLOG + static bool is_logopend = false; + if (!is_logopend) + { + openlog("ifconv", LOG_CONS | LOG_PID, LOG_USER); + is_logopend = true; + } + syslog(priority, "%s", message); +#else + pid_t current_pid = getpid(); + printf("ifconv[%d][pri=%d]: %s\n", current_pid, priority, message); +#endif +} + + +/** + * 指定されたバッファに、現在の時刻(ナノ秒まで)を文字列形式で格納します。 + * + * @param buff バッファ + * @param size バッファサイズ + * @return バッファに書き込んだ文字数 + */ +static int get_date(char* buff, size_t size) +{ + // ナノ秒まで取得する。 + struct timespec current_time; + clock_gettime(CLOCK_REALTIME, ¤t_time); + + // 秒を日時に変換する。 + struct tm* local_tm = localtime(¤t_time.tv_sec); + + // YYYY/MM/DD HH:MM:SS.XXXXXXXXX に変換する。 + int len = snprintf(buff, size, "%04d/%02d/%02d %02d:%02d:%02d.%09ld", + local_tm->tm_year + 1900, + local_tm->tm_mon + 1, + local_tm->tm_mday, + local_tm->tm_hour, + local_tm->tm_min, + local_tm->tm_sec, + current_time.tv_nsec); + + return len; +} + + diff --git a/.bash.d/tmp/util/src/util_blockingqueue.c b/.bash.d/tmp/util/src/util_blockingqueue.c new file mode 100644 index 0000000..5b66c33 --- /dev/null +++ b/.bash.d/tmp/util/src/util_blockingqueue.c @@ -0,0 +1,200 @@ +#include +#include +#include +#include + +#include "util_queue.h" +#include "util_blockingqueue.h" + +/** + * ブロッキングキューのインスタンス構造体 + */ +struct blockingqueue { + struct queue* queue; /**< キュー */ + struct mutex* data_lock; /**< データロック */ + struct cond* empty_block; /**< pop のブロック用 */ + struct cond* full_block; /**< push のブロック用 */ +}; + + +/** + * ブロッキングキューを生成します。 + * インスタンスを生成できない場合、NULL を返します。 + * + * @param cap キューの容量 + * @param size キューに格納するデータサイズ + * @return イベントキューのインスタンス + */ +struct blockingqueue* blockingqueue_new(size_t cap, size_t size) +{ + struct blockingqueue* blockingqueue = (struct blockingqueue*) malloc(sizeof(struct blockingqueue)); + if (blockingqueue != NULL) + { + blockingqueue->queue = queue_new(cap,4096); + if (blockingqueue->queue != NULL) + { // pthread_mutex_lock ですでに保持している mutex をロックしようとしたとき、 + // デフォルトのスレッドを停止させる動作とするため、NULL (デフォルト設定)を指定する。 + pthread_mutex_init(&blockingqueue->data_lock, NULL); + + // データがない場合 pop をブロックするための cond オブジェクトを初期化する。 + pthread_cond_init(&blockingqueue->pop_block, NULL); + } + else + { + free(blockingqueue); + blockingqueue = NULL; + } + + } + return blockingqueue; +} + + + + +/** + * 指定されたイベントキューを破棄します。 + * + * @param blockingqueue イベントキューのインスタンス + */ +void blockingqueue_destroy(struct blockingqueue* blockingqueue) +{ + if (blockingqueue != NULL) + { + // T.B.D. + queue_destroy(blockingqueue->queue); + blockingqueue->queue = NULL; + } + free(blockingqueue); +} + + +/** + * 指定された mutex をロックします。 + * + * @param mutex mutexオブジェクト + */ +static +void blockingqueue_mutex_lock(pthread_mutex_t* mutex) +{ + int errcode = pthread_mutex_lock(mutex); + if (errcode != 0) + { // エラーが発生する条件は、下記の何れかであり、本プログラムの設定では発生しない。 + // => 発生するのであれば、プログラム誤り。 + // EINVAL : mutex が適切に初期化されていない。 + // EDEADLK : mutex は既に呼び出しスレッドによりロックされている (「エラー検査を行う」mutexes のみ) + printf("###!!! [ERROR] pthred_mutex_lock !!!###\n"); + } +} + + +/** + * 指定された mutex をアンロックします。 + * + * @param mutex mutexオブジェクト + */ +static +void blockingqueue_mutex_unlock(pthread_mutex_t* mutex) +{ + int errcode = pthread_mutex_unlock(mutex); + if (errcode != 0) + { // エラーが発生する条件は、下記の何れかであり、本プログラムの設定では発生しない。 + // => 発生するのであれば、プログラム誤り。 + // EINVAL : mutex が適切に初期化されていない。 + // EDEADLK : mutex は既に呼び出しスレッドによりロックされている (「エラー検査を行う」mutexes のみ) + printf("###!!! [ERROR] pthred_mutex_lock !!!###\n"); + } +} + + +/** + * イベントキューにデータを追加します。 + * + * @param blockingqueue キューのインスタンス + * @param data キューに追加するエントリ(データ) + * @param size キューに追加するエントリ(データ)のサイズ + * @return true/false (追加成功/追加失敗) + */ +bool blockingqueue_push(struct blockingqueue* blockingqueue, void* data, size_t size) +{ + blockingqueue_mutex_lock(&blockingqueue->data_lock); + + // キューにデータがない間ブロックする。 + size_t data_count = queue_size(blockingqueue->queue); + if (data_count == 0) + { + pthread_cond_signal(&blockingqueue->pop_block); + } + + bool result = queue_push(blockingqueue->queue, data, size); + + blockingqueue_mutex_unlock(&blockingqueue->data_lock); + + return result; +} + + +/** + * キューの先頭よりデータを取り出します。 + * キューにデータがない場合、ブロックされます。 + * + * @param blockingqueue キューのインスタンス + * @param buf 取り出したデータ格納用バッファ + * @param buflen バッファのサイズ + * @return 取り出したデータのサイズ + */ +size_t blockingqueue_pop( struct blockingqueue* blockingqueue, void* buf, size_t buflen) +{ + blockingqueue_mutex_lock(&blockingqueue->data_lock); + + // キューにデータがない間ブロックする。 + size_t data_count = queue_size(blockingqueue->queue); + while (data_count == 0) + { + pthread_cond_wait(&blockingqueue->pop_block, &blockingqueue->data_lock); + data_count = queue_size(blockingqueue->queue); + } + + size_t data_size = queue_pop(blockingqueue->queue, buf, buflen); + blockingqueue_mutex_unlock(&blockingqueue->data_lock); + return data_size; +} + + +/** + * キューの先頭よりデータを取り出します。 + * データの取り出しに失敗した場合、0を返します。 + * blockingqueue_pop と異なり、キューからデータは削除されません。 + * + * @param blockingqueue キューのインスタンス + * @param buf 取り出したデータ格納用バッファ + * @param buflen バッファのサイズ + * @return 取り出したデータのサイズ + */ +size_t blockingqueue_peek( struct blockingqueue* blockingqueue, void* buf, size_t buflen) +{ + blockingqueue_mutex_lock(&blockingqueue->data_lock); + size_t size = queue_peek(blockingqueue->queue, buf, buflen); + blockingqueue_mutex_unlock(&blockingqueue->data_lock); + return size; +} + + + +/** + * キューのサイズ(キューに入っているデータの個数)を取得します。 + * + * @param blockingqueue キューのインスタンス + * @return キューのサイズ + */ +size_t blockingqueue_size(struct blockingqueue* blockingqueue) +{ + size_t size; + blockingqueue_mutex_lock(&blockingqueue->data_lock); + size = queue_size(blockingqueue->queue); + blockingqueue_mutex_unlock(&blockingqueue->data_lock); + return size; +} + + +#endif diff --git a/.bash.d/tmp/util/src/util_queue.c b/.bash.d/tmp/util/src/util_queue.c new file mode 100644 index 0000000..fb2e153 --- /dev/null +++ b/.bash.d/tmp/util/src/util_queue.c @@ -0,0 +1,341 @@ +#include +#include +#include + +#include "util_thread.h" +#include "util_queue.h" + + + +/* ============================================================================= + * 構造体定義 + * ============================================================================= + */ + +/** + * キューのエントリ構造体 + */ +struct queue_entry { + size_t size; /**< 実データサイズ */ + char* data; /**< データバッファ */ +}; + + +/** + * キューのインスタンス構造体 + */ +struct queue { + size_t capacity; /**< キューの容量 */ + size_t size; /**< キューのサイズ */ + size_t max_data_size; /**< データの最大サイズ */ + struct queue_entry* entries; /**< データのエントリ */ + struct queue_entry* head; /**< キューの取出位置 */ + struct queue_entry* tail; /**< キューの挿入位置 */ +}; + + + +/* ============================================================================= + * 内部関数プロトタイプ宣言 + * ============================================================================= + */ +static struct queue_entry* queue_next_entry(struct queue* queue, struct queue_entry* entry); +static bool queue_is_enabled(struct queue* queue, struct queue_entry* entry); + + + +/* ============================================================================= + * 公開関数 + * ============================================================================= + */ + +/** + * キューを生成します。 + * インスタンスを生成できない場合、NULL を返します。 + * + * [注意] + * このキューは、同期化されません。 + * 同期化が必要な場合は、blocking_queue を使用してください。 + * + * @param cap キューの容量(格納可能なデータの個数) + * @param size キューに格納する1つあたりのデータサイズ + * @return キューのインスタンス + */ +/* [実装メモ] + * キューのデータ管理構造 + * メモリ確保を 1 回の malloc でできるようにしている。 + * +-------------------+ + * | queue 構造体 | + * +-------------------+ + * | entry[0] | + * +-------------------+ + * : + * +-------------------+ + * | entry[cap-1] | + +-------------------+ + * | entry[0].data | + * | 用バッファ | + * +-------------------+ + * : + * +-------------------+ + * | entry[cap-1].data | + * | 用バッファ | + * +-------------------+ + */ +struct queue* queue_new(size_t cap, size_t size) +{ + size_t queue_size = sizeof(struct queue) + + ((sizeof(struct queue_entry) + size) * cap); + struct queue* queue = (struct queue*) malloc(queue_size); + if (queue != NULL) + { + queue->capacity = cap; + queue->size = 0; + queue->max_data_size = size; + queue->entries = (struct queue_entry*) (queue + 1); + queue->head = queue->entries; + queue->tail = queue->entries; + + + char* tmp_data_ptr = (char*) (queue->entries + queue->capacity); + struct queue_entry* tmp_entry = queue->entries; + for (size_t i = 0; i < queue->capacity; i++) + { + tmp_entry->data = tmp_data_ptr; + tmp_entry++; + tmp_data_ptr += queue->max_data_size; + } + } + return queue; +} + + + +/** + * 指定されたキューを破棄します。 + * + * @param queue キューのインスタンス + */ +void queue_destroy(struct queue* queue) +{ + free(queue); +} + + +/** + * キューにデータを追加します。 + * + * @param queue キューのインスタンス + * @param data キューに追加するエントリ(データ) + * @param size キューに追加するエントリ(データ)のサイズ + * @return true/false (追加成功/追加失敗) + */ +bool queue_push(struct queue* queue, void* data, size_t size) +{ + bool result = false; + + if (size < queue->max_data_size) + { + if (queue->size < queue->capacity) + { + memset(queue->tail->data, 0x00, sizeof(struct queue_entry)); + memcpy(queue->tail->data, data, size); + queue->tail->size = size; + queue->tail++; + if (queue->tail >= (queue->entries + queue->capacity)) + { + queue->tail = queue->entries; + } + queue->size++; + result = true; + } + } + return result; +} + + +/** + * キューの先頭よりデータを取り出します。 + * データの取り出しに失敗した場合、0を返します。 + * + * @param queue キューのインスタンス + * @param buf 取り出したデータ格納用バッファ + * @param buflen バッファのサイズ + * @return 取り出したデータのサイズ + */ +size_t queue_pop( struct queue* queue, void* buf, size_t buflen) +{ + size_t size = 0; + if (queue->size > 0) + { + if (queue->head->size < buflen) + { + size = queue->head->size; + memset(buf, 0x00, buflen); + memcpy(buf, queue->head->data, size); + + // データクリア (なくても動作上問題ない) + memset(queue->head->data, 0x00, queue->max_data_size); + queue->head->size = 0; + + queue->head++; + if (queue->head >= (queue->entries + queue->capacity)) + { + queue->head = queue->entries; + } + queue->size--; + } + } + return size; +} + + +/** + * キューの先頭よりデータを取り出します。 + * データの取り出しに失敗した場合、0を返します。 + * queue_pop と異なり、キューからデータは削除されません。 + * + * @param queue キューのインスタンス + * @param buf 取り出したデータ格納用バッファ + * @param buflen バッファのサイズ + * @return 取り出したデータのサイズ + */ +size_t queue_peek( struct queue* queue, void* buf, size_t buflen) +{ + size_t size = 0; + if (queue->size > 0) + { + if (queue->head->size < buflen) + { + size = queue->head->size; + memset(buf, 0x00, buflen); + memcpy(buf, queue->head->data, size); + } + } + return size; +} + + +/** + * キューのサイズ(キューに入っているデータの個数)を取得します。 + * + * @param queue キューのインスタンス + * @return キューのサイズ + */ +size_t queue_size(struct queue* queue) +{ + return queue->size; +} + + +/** + * キューに格納されている全エントリーを引数に、指定された handler を呼び出します。 + * + * handler の引数: + * - data : データ + * - size : データのサイズ + * + * @param queue キューのインスタンス + * @param handler ハンドラ + */ +void queue_entries(struct queue* queue, bool (*handler)(void* data, size_t size)) +{ + bool is_continue = true; + size_t counter = queue->size; + struct queue_entry* entry = queue->head; + while (is_continue && (counter > 0)) + { + is_continue = handler(entry->data, entry->size); + entry = queue_next_entry(queue, entry); + counter--; + } +} + + +/** + * キューインスタンスの全エントリを引数に、指定された handler を呼び出します。 + * この関数は、デバッグ用の関数です。 + * handler には、実際にデータが入っていないキューのエントリも渡されます。 + * + * handler の引数: + * - data : データ + * - size : データのサイズ + * - index : キューの管理上のインデックス + * - enabled : true/false (有効なデータ/無効なデータ) + * - arg : ユーザーデータ + * + * @param queue キューのインスタンス + * @param handler ハンドラ + * @param arg ハンドラに渡すユーザーデータ + */ +void queue_entries_full(struct queue* queue, + bool (*handler)(void* data, size_t size, int index, bool enabled, void* arg), + void* arg) +{ + bool is_enabled; + bool is_continue = true; + struct queue_entry* entry = queue->entries; + for (int i = 0; is_continue && (i < (int) queue->capacity); i++) + { + is_enabled = queue_is_enabled(queue, entry); + is_continue = handler(entry->data, entry->size, i, is_enabled, arg); + entry++; + } +} + + + +/* ============================================================================= + * 内部関数 + * ============================================================================= + */ + + +/** + * 指定されたエントリの次のエントリーを取得します。 + * エントリー配列の末尾に到達した場合、先頭のエントリを返します。 + * + * @param queue キューのインスタンス + * @param entry エントリ + * @return 次のエントリ + */ +static +struct queue_entry* queue_next_entry(struct queue* queue, struct queue_entry* entry) +{ + struct queue_entry* next_entry = entry; + next_entry++; + if (next_entry >= (queue->entries + queue->capacity)) + { + next_entry = queue->entries; + } + return next_entry; +} + + +/** + * 指定されたエントリが、有効か否かを返します。 + * + * @param queue キューのインスタンス + * @param entry エントリ + * @return true/false (有効/無効) + */ +static +bool queue_is_enabled(struct queue* queue, struct queue_entry* entry) +{ + bool is_enabled; + if ((queue->size != queue->capacity) && (queue->head <= queue->tail)) + { /* キューのエントリ配列が以下のような状態の場合 + * [- |- |HT-|- |- |- |- |- ] + * [- |- |Ho |o |o |T-|- |- ] (-:空/o:データ有/H:Head/T:Tail) + */ + is_enabled = ((queue->head <= entry) && (entry < queue->tail)); + } + else + { /* キューのエントリ配列が以下のような状態の場合 + * [o |o |HTo|o |o |o |o |o ] + * [o |o |T- |- |- |Ho|o |o ] (-:空/o:データ有/H:Head/T:Tail) + */ + is_enabled = ((entry < queue->tail) || (queue->head <= entry)); + } + return is_enabled; +} diff --git a/.bash.d/tmp/util/src/util_thread.c b/.bash.d/tmp/util/src/util_thread.c new file mode 100644 index 0000000..20c6fc6 --- /dev/null +++ b/.bash.d/tmp/util/src/util_thread.c @@ -0,0 +1,341 @@ +/** + * @file util_thread.c + * スレッドを扱うモジュール。 + */ +#include +#include +#include +#include + +#include "util_thread.h" + + + +/* ///////////////////////////////////////////////////////////////////////////// +// +// スレッド +// +*/ + + +/* ============================================================================ + * 構造体定義 + * ============================================================================ + */ + +/** + * スレッドインスタンス構造体。 + */ +struct thread { + void (*start_routine)(void*); /**< スレッドとして実行する関数 */ + void* arg; /**< スレッドに渡すデータ */ + pthread_t tid; /**< スレッドのID */ +}; + + + +/* ============================================================================ + * プロトタイプ宣言 (内部でのみ利用する関数) + * ============================================================================ + */ +static void* thread_run(void* args); + + + +/* ============================================================================ + * 公開関数 + * ============================================================================ + */ + +/** + * スレッドを生成します。 + * スレッドの生成に失敗した場合、NULL を返します。 + * + * @param start_routine スレッドで実行する関数 + * @param arg スレッドに渡すデータへのポインタ + * @return スレッドインスタンス + */ +struct thread* thread_new(void (*start_routine)(void*), void* arg) +{ + struct thread* thread = (struct thread*) malloc(sizeof(struct thread)); + if (thread != NULL) + { + thread->start_routine = start_routine; + thread->arg = arg; + } + return thread; +} + + +/** + * スレッドインスタンスのリソースを開放します。 + * スレッド自体が終了するわけではありません。 + * + * @param thread スレッドインスタンス + */ +void thread_delete(struct thread* thread) +{ + free(thread); +} + + +/** + * スレッドを開始します。 + * + * @param thread スレッドインスタンス + */ +void thread_start(struct thread* thread) +{ + pthread_create(&thread->tid, NULL, thread_run, thread); +} + + +/** + * 指定されたスレッドが終了するのを待ちます。 + * + * @param thread スレッドインスタンス + */ +bool thread_join(struct thread* thread) +{ + int ret = pthread_join(thread->tid, NULL); + return (ret == 0); +} + + +/** + * 現在のスレッドが指定されたスレッドと同一か否かを返します。 + * + * @param thread スレッドインスタンス + * @return true/false (一致/不一致) + */ +bool thread_equals(struct thread* thread) +{ + pthread_t tid = pthread_self(); + return (tid == thread->tid); +} + + + +/* ============================================================================ + * 内部関数 + * ============================================================================ + */ + +/** + * スレッドとして実行される関数。 + * この関数の中で、スレッド起動に指定された関数を実行します。 + * + * @param arg スレッドインスタンス + */ +static +void* thread_run(void* arg) +{ + struct thread* thread = (struct thread*) arg; + thread->start_routine(thread->arg); + return NULL; +} + + + +/* ///////////////////////////////////////////////////////////////////////////// +// +// mutex +// +*/ + + +/* ============================================================================ + * 構造体定義 + * ============================================================================ + */ + +/** + * mutex インスタンス構造体。 + */ +struct mutex { + pthread_mutex_t mutex; /**< mutex オブジェクト */ +}; + + + +/* ============================================================================ + * 公開関数 + * ============================================================================ + */ + +/** + * mutex を生成します。 + * mutex の生成に失敗した場合、NULL を返します。 + * + * @return mutex + */ + +struct mutex* mutex_new(void) +{ + struct mutex* mutex = (struct mutex*) malloc(sizeof(struct mutex)); + if (mutex != NULL) + { + pthread_mutex_init(&mutex->mutex, NULL); + } + return mutex; +} + + +/** + * mutex を破棄します。 + * mutex がロックされている場合は、破棄に失敗しエラーを返します。 + * + * @param mutex 破棄する mutex + * @return true/false (破棄成功/破棄失敗 [mutex がロックされている]) + */ +bool mutex_delete(struct mutex* mutex) +{ + if (mutex != NULL) + { + int res = pthread_mutex_destroy(&mutex->mutex); + if (res == 0) + { + free(mutex); + return true; + } + } + return false; +} + + +/** + * mutex をロックします。 + * + * @param mutex ロックする mutex + */ +void mutex_lock(struct mutex* mutex) +{ + // mutex は、デフォルトの種別で初期化済みのため、 + // EINVAL, EDEADLK のエラーは発生しない。 + pthread_mutex_lock(&mutex->mutex); +} + + +/** + * mutex をアンロックします。 + * + * @param mutex アンロックする mutex + */ +void mutex_unlock(struct mutex* mutex) +{ + // mutex は、デフォルトの種別で初期化済みのため、 + // EINVAL, EPERM のエラーは発生しない。 + pthread_mutex_unlock(&mutex->mutex); +} + + + +/* ///////////////////////////////////////////////////////////////////////////// +// +// cond +// +*/ + + +/* ============================================================================ + * 構造体定義 + * ============================================================================ + */ + +/** + * cond インスタンス構造体。 + */ +struct cond { + pthread_cond_t cond; /**< cond オブジェクト */ +}; + + + +/* ============================================================================ + * 公開関数 + * ============================================================================ + */ + +/** + * cond を生成します。 + * cond の生成に失敗した場合、NULL を返します。 + * + * @return cond + */ + +struct cond* cond_new(void) +{ + struct cond* cond = (struct cond*) malloc(sizeof(struct cond)); + if (cond != NULL) + { + pthread_cond_init(&cond->cond, NULL); + } + return cond; +} + + +/** + * cond を破棄します。 + * cond が条件変数を待っている場合、破棄に失敗しエラーを返します。 + * + * @param cond 破棄する cond + * @return true/false (破棄成功/破棄失敗 [cond が条件変数を待っている]) + */ +bool cond_delete(struct cond* cond) +{ + if (cond!= NULL) + { + int res = pthread_cond_destroy(&cond->cond); + if (res == 0) + { + free(cond); + return true; + } + } + return false; +} + + +/** + * 指定された mutex のアンロックと、条件変数 cond の送信に対する待機を + * アトミックに行います。条件変数が送信されるまで、スレッドの実行は停止され、 + * CPU時間を消費しません。 + * + * 本関数を実行する前に、 mutex はロックされている必要があります。 + * 本関数を呼び出しスレッドが、条件変数の待機完了により動作する際、 + * mutex は再びロックされます。 + * + * @param cond cond インスタンス + * @param mutex mutex ロック済みの mutex + */ +void cond_wait(struct cond* cond, struct mutex* mutex) +{ + pthread_cond_wait(&cond->cond, &mutex->mutex); +} + + +/** + * 条件変数 cond に備えて待機しているスレッドの一つの実行を再開させます。 + * cond を待機しているスレッドがなければ何もしません。 + * 複数のスレッドが cond を待機している場合、どのスレッドが再開されるかはわからない。 + * + * @param cond 再開させる cond + */ +void cond_signal(struct cond* cond) +{ + pthread_cond_signal(&cond->cond); +} + + +/** + * 条件変数 cond に備えて待機しているすべてのスレッドを再開させます。 + * cond を待機しているスレッドがなければ何もしません。 + * + * @param cond 再開させる cond + */ +void cond_broadcast(struct cond* cond) +{ + pthread_cond_broadcast(&cond->cond); +} + + diff --git a/.bash.d/tmp/util/test/Makefile b/.bash.d/tmp/util/test/Makefile new file mode 100644 index 0000000..395b46a --- /dev/null +++ b/.bash.d/tmp/util/test/Makefile @@ -0,0 +1,29 @@ +# ============================================================================== +# Makefile +# ============================================================================== +TOPDIR ?= ../../.. +TARGET = ut_libutil.exe +SUBDIRS = +VERSION = +TARGET_TYPE = target-exe +#TARGET_TYPE = target-a +#TARGET_TYPE = target-so +#TARGET_TYPE = target-dll + +EXCLUDES = ../src/ut_queue.c + +include $(TOPDIR)/mk/conf.mk +include $(TOPDIR)/mk/$(USE)/com.*.mk +include $(TOPDIR)/mk/$(USE)/conf.ut.*.mk + +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -lcunit -lpthread + +.PHONY: all +all: all-subdir $(TARGET) + + +include $(TOPDIR)/mk/$(USE)/rule.*.mk + diff --git a/.bash.d/tmp/util/test/src/test_main.c_ b/.bash.d/tmp/util/test/src/test_main.c_ new file mode 100644 index 0000000..ed7cf1d --- /dev/null +++ b/.bash.d/tmp/util/test/src/test_main.c_ @@ -0,0 +1,26 @@ +#include +#include +#include + +#include "test_thread.h" + + +int main(void) +{ + test_thread(); + return 0; +} +int t(void) +{ + CU_pSuite testSuite; + + CU_initialize_registry(); + testSuite = CU_add_suite("libutil.so",NULL,NULL); + + CU_add_test(testSuite, "thread", test_thread); + + CU_console_run_tests(); + CU_cleanup_registry(); + + return 0; +} diff --git a/.bash.d/tmp/util/Makefile b/.bash.d/tmp/util/Makefile new file mode 100644 index 0000000..722430a --- /dev/null +++ b/.bash.d/tmp/util/Makefile @@ -0,0 +1,29 @@ +# ============================================================================== +# Makefile +# ============================================================================== +TOPDIR ?= ../.. +TARGET = libutil.so +SUBDIRS = +VERSION = +#TARGET_TYPE = target-exe +#TARGET_TYPE = target-a +TARGET_TYPE = target-so +#TARGET_TYPE = target-dll + +include $(TOPDIR)/mk/conf.mk +include $(TOPDIR)/mk/$(USE)/com.*.mk +include $(TOPDIR)/mk/$(USE)/conf.def.*.mk + +CFLAGS += -fPIC +CXXFLAGS += +LDFLAGS += + +.PHONY: all test +all: all-subdir $(TARGET) + +test: + $(MAKE) -C test + + +include $(TOPDIR)/mk/$(USE)/rule.*.mk + diff --git a/.bash.d/tmp/util/include/util_blockingqueue.h b/.bash.d/tmp/util/include/util_blockingqueue.h new file mode 100644 index 0000000..0309bff --- /dev/null +++ b/.bash.d/tmp/util/include/util_blockingqueue.h @@ -0,0 +1,24 @@ +#ifndef UTIL_BLOCKINGQUEUE_H +#define UTIL_BLOCKINGQUEUE_H + +#include + + +/** + * ブロッキングキューのインスタンス構造体 + */ +struct blockingqueue; + +struct blockingqueue* blockingqueue_new(int cap); +void blockingqueue_destroy(struct blockingqueue* queue); +bool blockingqueue_push(struct blockingqueue* queue, void* data, size_t size); +bool blockingqueue_push_blocking(struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_pop( struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_pop_blocking( struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_peek(struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_size(struct blockingqueue* queue); +void blockingqueue_notify(struct blockingqueue* queue); + + +#endif /* UTIL_BLOCKINGQUEUE_H */ + diff --git a/.bash.d/tmp/util/include/util_queue.h b/.bash.d/tmp/util/include/util_queue.h new file mode 100644 index 0000000..2de9762 --- /dev/null +++ b/.bash.d/tmp/util/include/util_queue.h @@ -0,0 +1,21 @@ +#ifndef UTIL_QUEUE_H +#define UTIL_QUEUE_H + +#include + + +/** キュー */ +struct queue; +struct queue* queue_new(size_t cap, size_t size); +void queue_destroy(struct queue* queue); +bool queue_push(struct queue* queue, void* data, size_t size); +size_t queue_pop( struct queue* queue, void* data, size_t size); +size_t queue_peek(struct queue* queue, void* data, size_t size); +size_t queue_size(struct queue* queue); +void queue_entries(struct queue* queue, bool (*handler)(void* data, size_t size)); +void queue_entries_full(struct queue* queue, + bool (*handler)(void* data, size_t size, int index, bool enabled, void* arg), + void* arg); + +#endif /* UTIL_QUEUE_H */ + diff --git a/.bash.d/tmp/util/include/util_thread.h b/.bash.d/tmp/util/include/util_thread.h new file mode 100644 index 0000000..60e7bb0 --- /dev/null +++ b/.bash.d/tmp/util/include/util_thread.h @@ -0,0 +1,38 @@ +/** + * @file util_thread.c + * スレッドを扱うモジュール + * + * 実装側のみ複数のOS,ライブラリ用に変更できるよう + * 各オブジェクトを typedef ではなく、構造体にしている。 + */ +#ifndef UTIL_THREAD_H +#define UTIL_THREAD_H + +#include + +/* スレッド */ +struct thread; +struct thread* thread_new(void (*start_routine)(void*), void* arg); +void thread_delete(struct thread* thread); +void thread_start(struct thread* thread); +bool thread_join(struct thread* thread); +bool thread_equals(struct thread* thread); + +/* mutex */ +struct mutex; +struct mutex* mutex_new(void); +bool mutex_delete(struct mutex* mutex); +void mutex_lock(struct mutex* mutex); +void mutex_unlock(struct mutex* mutex); + +/* cond */ +struct cond; +struct cond* cond_new(void); +bool cond_delete(struct cond* cond); +void cond_wait(struct cond* cond, struct mutex* mutex); +void cond_signal(struct cond* cond); +void cond_broadcast(struct cond* cond); + + +#endif /* THREAD_H */ + diff --git a/.bash.d/tmp/util/src/logger.c b/.bash.d/tmp/util/src/logger.c new file mode 100644 index 0000000..e96204d --- /dev/null +++ b/.bash.d/tmp/util/src/logger.c @@ -0,0 +1,117 @@ +#include +#include +#include +#include +#include +#include + +#include "logger.h" + + + +// ============================================================================= +// 内部でのみ使用する関数のプロトタイプ宣言 +// ============================================================================= +static void logger_log_output(int priority, const char* message); +static int get_date(char* buff, size_t size); + + + + + +// ============================================================================= +// 関数定義 +// ============================================================================= + +/** + * 指定されたログレベルのログを出力可能か否かを返します。 + * + * @param priority ログレベル + * @return true/false (ログ出力可能/ログ出力不可) + */ +bool logger_is_logable(int priority) +{ + int current_logmask = setlogmask(0); + return (current_logmask & priority); +} + + + +/** + * ログを出力します。 + * + * @param priority syslog と同じログのレベルを指定します。 + * @param file __FILE__ を指定します。 + * @param line __LINE__ を指定します。 + * @param format 書式を指定します。 + */ +void logger_log_(int priority, const char* file, int line, const char* format, ...) +{ + va_list ap; + va_start(ap, format); + bool is_logable = logger_is_logable(priority); + if (is_logable) + { + char log_buffer[512]; + int len = get_date(log_buffer, sizeof(log_buffer)); + len += snprintf(&log_buffer[len], sizeof(log_buffer) - len, " %s:%d: ", file, line); + vsnprintf(&log_buffer[len], sizeof(log_buffer) - len, format, ap); + logger_log_output(priority, log_buffer); + } + va_end(ap); +} + + +/** + * 指定されたメッセージをログに出力します。 + * + * @param priority ログレベル + * @param message 出力するメッセージ + */ +static void logger_log_output(int priority, const char* message) +{ +#ifdef USE_SYSLOG + static bool is_logopend = false; + if (!is_logopend) + { + openlog("ifconv", LOG_CONS | LOG_PID, LOG_USER); + is_logopend = true; + } + syslog(priority, "%s", message); +#else + pid_t current_pid = getpid(); + printf("ifconv[%d][pri=%d]: %s\n", current_pid, priority, message); +#endif +} + + +/** + * 指定されたバッファに、現在の時刻(ナノ秒まで)を文字列形式で格納します。 + * + * @param buff バッファ + * @param size バッファサイズ + * @return バッファに書き込んだ文字数 + */ +static int get_date(char* buff, size_t size) +{ + // ナノ秒まで取得する。 + struct timespec current_time; + clock_gettime(CLOCK_REALTIME, ¤t_time); + + // 秒を日時に変換する。 + struct tm* local_tm = localtime(¤t_time.tv_sec); + + // YYYY/MM/DD HH:MM:SS.XXXXXXXXX に変換する。 + int len = snprintf(buff, size, "%04d/%02d/%02d %02d:%02d:%02d.%09ld", + local_tm->tm_year + 1900, + local_tm->tm_mon + 1, + local_tm->tm_mday, + local_tm->tm_hour, + local_tm->tm_min, + local_tm->tm_sec, + current_time.tv_nsec); + + return len; +} + + diff --git a/.bash.d/tmp/util/src/util_blockingqueue.c b/.bash.d/tmp/util/src/util_blockingqueue.c new file mode 100644 index 0000000..5b66c33 --- /dev/null +++ b/.bash.d/tmp/util/src/util_blockingqueue.c @@ -0,0 +1,200 @@ +#include +#include +#include +#include + +#include "util_queue.h" +#include "util_blockingqueue.h" + +/** + * ブロッキングキューのインスタンス構造体 + */ +struct blockingqueue { + struct queue* queue; /**< キュー */ + struct mutex* data_lock; /**< データロック */ + struct cond* empty_block; /**< pop のブロック用 */ + struct cond* full_block; /**< push のブロック用 */ +}; + + +/** + * ブロッキングキューを生成します。 + * インスタンスを生成できない場合、NULL を返します。 + * + * @param cap キューの容量 + * @param size キューに格納するデータサイズ + * @return イベントキューのインスタンス + */ +struct blockingqueue* blockingqueue_new(size_t cap, size_t size) +{ + struct blockingqueue* blockingqueue = (struct blockingqueue*) malloc(sizeof(struct blockingqueue)); + if (blockingqueue != NULL) + { + blockingqueue->queue = queue_new(cap,4096); + if (blockingqueue->queue != NULL) + { // pthread_mutex_lock ですでに保持している mutex をロックしようとしたとき、 + // デフォルトのスレッドを停止させる動作とするため、NULL (デフォルト設定)を指定する。 + pthread_mutex_init(&blockingqueue->data_lock, NULL); + + // データがない場合 pop をブロックするための cond オブジェクトを初期化する。 + pthread_cond_init(&blockingqueue->pop_block, NULL); + } + else + { + free(blockingqueue); + blockingqueue = NULL; + } + + } + return blockingqueue; +} + + + + +/** + * 指定されたイベントキューを破棄します。 + * + * @param blockingqueue イベントキューのインスタンス + */ +void blockingqueue_destroy(struct blockingqueue* blockingqueue) +{ + if (blockingqueue != NULL) + { + // T.B.D. + queue_destroy(blockingqueue->queue); + blockingqueue->queue = NULL; + } + free(blockingqueue); +} + + +/** + * 指定された mutex をロックします。 + * + * @param mutex mutexオブジェクト + */ +static +void blockingqueue_mutex_lock(pthread_mutex_t* mutex) +{ + int errcode = pthread_mutex_lock(mutex); + if (errcode != 0) + { // エラーが発生する条件は、下記の何れかであり、本プログラムの設定では発生しない。 + // => 発生するのであれば、プログラム誤り。 + // EINVAL : mutex が適切に初期化されていない。 + // EDEADLK : mutex は既に呼び出しスレッドによりロックされている (「エラー検査を行う」mutexes のみ) + printf("###!!! [ERROR] pthred_mutex_lock !!!###\n"); + } +} + + +/** + * 指定された mutex をアンロックします。 + * + * @param mutex mutexオブジェクト + */ +static +void blockingqueue_mutex_unlock(pthread_mutex_t* mutex) +{ + int errcode = pthread_mutex_unlock(mutex); + if (errcode != 0) + { // エラーが発生する条件は、下記の何れかであり、本プログラムの設定では発生しない。 + // => 発生するのであれば、プログラム誤り。 + // EINVAL : mutex が適切に初期化されていない。 + // EDEADLK : mutex は既に呼び出しスレッドによりロックされている (「エラー検査を行う」mutexes のみ) + printf("###!!! [ERROR] pthred_mutex_lock !!!###\n"); + } +} + + +/** + * イベントキューにデータを追加します。 + * + * @param blockingqueue キューのインスタンス + * @param data キューに追加するエントリ(データ) + * @param size キューに追加するエントリ(データ)のサイズ + * @return true/false (追加成功/追加失敗) + */ +bool blockingqueue_push(struct blockingqueue* blockingqueue, void* data, size_t size) +{ + blockingqueue_mutex_lock(&blockingqueue->data_lock); + + // キューにデータがない間ブロックする。 + size_t data_count = queue_size(blockingqueue->queue); + if (data_count == 0) + { + pthread_cond_signal(&blockingqueue->pop_block); + } + + bool result = queue_push(blockingqueue->queue, data, size); + + blockingqueue_mutex_unlock(&blockingqueue->data_lock); + + return result; +} + + +/** + * キューの先頭よりデータを取り出します。 + * キューにデータがない場合、ブロックされます。 + * + * @param blockingqueue キューのインスタンス + * @param buf 取り出したデータ格納用バッファ + * @param buflen バッファのサイズ + * @return 取り出したデータのサイズ + */ +size_t blockingqueue_pop( struct blockingqueue* blockingqueue, void* buf, size_t buflen) +{ + blockingqueue_mutex_lock(&blockingqueue->data_lock); + + // キューにデータがない間ブロックする。 + size_t data_count = queue_size(blockingqueue->queue); + while (data_count == 0) + { + pthread_cond_wait(&blockingqueue->pop_block, &blockingqueue->data_lock); + data_count = queue_size(blockingqueue->queue); + } + + size_t data_size = queue_pop(blockingqueue->queue, buf, buflen); + blockingqueue_mutex_unlock(&blockingqueue->data_lock); + return data_size; +} + + +/** + * キューの先頭よりデータを取り出します。 + * データの取り出しに失敗した場合、0を返します。 + * blockingqueue_pop と異なり、キューからデータは削除されません。 + * + * @param blockingqueue キューのインスタンス + * @param buf 取り出したデータ格納用バッファ + * @param buflen バッファのサイズ + * @return 取り出したデータのサイズ + */ +size_t blockingqueue_peek( struct blockingqueue* blockingqueue, void* buf, size_t buflen) +{ + blockingqueue_mutex_lock(&blockingqueue->data_lock); + size_t size = queue_peek(blockingqueue->queue, buf, buflen); + blockingqueue_mutex_unlock(&blockingqueue->data_lock); + return size; +} + + + +/** + * キューのサイズ(キューに入っているデータの個数)を取得します。 + * + * @param blockingqueue キューのインスタンス + * @return キューのサイズ + */ +size_t blockingqueue_size(struct blockingqueue* blockingqueue) +{ + size_t size; + blockingqueue_mutex_lock(&blockingqueue->data_lock); + size = queue_size(blockingqueue->queue); + blockingqueue_mutex_unlock(&blockingqueue->data_lock); + return size; +} + + +#endif diff --git a/.bash.d/tmp/util/src/util_queue.c b/.bash.d/tmp/util/src/util_queue.c new file mode 100644 index 0000000..fb2e153 --- /dev/null +++ b/.bash.d/tmp/util/src/util_queue.c @@ -0,0 +1,341 @@ +#include +#include +#include + +#include "util_thread.h" +#include "util_queue.h" + + + +/* ============================================================================= + * 構造体定義 + * ============================================================================= + */ + +/** + * キューのエントリ構造体 + */ +struct queue_entry { + size_t size; /**< 実データサイズ */ + char* data; /**< データバッファ */ +}; + + +/** + * キューのインスタンス構造体 + */ +struct queue { + size_t capacity; /**< キューの容量 */ + size_t size; /**< キューのサイズ */ + size_t max_data_size; /**< データの最大サイズ */ + struct queue_entry* entries; /**< データのエントリ */ + struct queue_entry* head; /**< キューの取出位置 */ + struct queue_entry* tail; /**< キューの挿入位置 */ +}; + + + +/* ============================================================================= + * 内部関数プロトタイプ宣言 + * ============================================================================= + */ +static struct queue_entry* queue_next_entry(struct queue* queue, struct queue_entry* entry); +static bool queue_is_enabled(struct queue* queue, struct queue_entry* entry); + + + +/* ============================================================================= + * 公開関数 + * ============================================================================= + */ + +/** + * キューを生成します。 + * インスタンスを生成できない場合、NULL を返します。 + * + * [注意] + * このキューは、同期化されません。 + * 同期化が必要な場合は、blocking_queue を使用してください。 + * + * @param cap キューの容量(格納可能なデータの個数) + * @param size キューに格納する1つあたりのデータサイズ + * @return キューのインスタンス + */ +/* [実装メモ] + * キューのデータ管理構造 + * メモリ確保を 1 回の malloc でできるようにしている。 + * +-------------------+ + * | queue 構造体 | + * +-------------------+ + * | entry[0] | + * +-------------------+ + * : + * +-------------------+ + * | entry[cap-1] | + +-------------------+ + * | entry[0].data | + * | 用バッファ | + * +-------------------+ + * : + * +-------------------+ + * | entry[cap-1].data | + * | 用バッファ | + * +-------------------+ + */ +struct queue* queue_new(size_t cap, size_t size) +{ + size_t queue_size = sizeof(struct queue) + + ((sizeof(struct queue_entry) + size) * cap); + struct queue* queue = (struct queue*) malloc(queue_size); + if (queue != NULL) + { + queue->capacity = cap; + queue->size = 0; + queue->max_data_size = size; + queue->entries = (struct queue_entry*) (queue + 1); + queue->head = queue->entries; + queue->tail = queue->entries; + + + char* tmp_data_ptr = (char*) (queue->entries + queue->capacity); + struct queue_entry* tmp_entry = queue->entries; + for (size_t i = 0; i < queue->capacity; i++) + { + tmp_entry->data = tmp_data_ptr; + tmp_entry++; + tmp_data_ptr += queue->max_data_size; + } + } + return queue; +} + + + +/** + * 指定されたキューを破棄します。 + * + * @param queue キューのインスタンス + */ +void queue_destroy(struct queue* queue) +{ + free(queue); +} + + +/** + * キューにデータを追加します。 + * + * @param queue キューのインスタンス + * @param data キューに追加するエントリ(データ) + * @param size キューに追加するエントリ(データ)のサイズ + * @return true/false (追加成功/追加失敗) + */ +bool queue_push(struct queue* queue, void* data, size_t size) +{ + bool result = false; + + if (size < queue->max_data_size) + { + if (queue->size < queue->capacity) + { + memset(queue->tail->data, 0x00, sizeof(struct queue_entry)); + memcpy(queue->tail->data, data, size); + queue->tail->size = size; + queue->tail++; + if (queue->tail >= (queue->entries + queue->capacity)) + { + queue->tail = queue->entries; + } + queue->size++; + result = true; + } + } + return result; +} + + +/** + * キューの先頭よりデータを取り出します。 + * データの取り出しに失敗した場合、0を返します。 + * + * @param queue キューのインスタンス + * @param buf 取り出したデータ格納用バッファ + * @param buflen バッファのサイズ + * @return 取り出したデータのサイズ + */ +size_t queue_pop( struct queue* queue, void* buf, size_t buflen) +{ + size_t size = 0; + if (queue->size > 0) + { + if (queue->head->size < buflen) + { + size = queue->head->size; + memset(buf, 0x00, buflen); + memcpy(buf, queue->head->data, size); + + // データクリア (なくても動作上問題ない) + memset(queue->head->data, 0x00, queue->max_data_size); + queue->head->size = 0; + + queue->head++; + if (queue->head >= (queue->entries + queue->capacity)) + { + queue->head = queue->entries; + } + queue->size--; + } + } + return size; +} + + +/** + * キューの先頭よりデータを取り出します。 + * データの取り出しに失敗した場合、0を返します。 + * queue_pop と異なり、キューからデータは削除されません。 + * + * @param queue キューのインスタンス + * @param buf 取り出したデータ格納用バッファ + * @param buflen バッファのサイズ + * @return 取り出したデータのサイズ + */ +size_t queue_peek( struct queue* queue, void* buf, size_t buflen) +{ + size_t size = 0; + if (queue->size > 0) + { + if (queue->head->size < buflen) + { + size = queue->head->size; + memset(buf, 0x00, buflen); + memcpy(buf, queue->head->data, size); + } + } + return size; +} + + +/** + * キューのサイズ(キューに入っているデータの個数)を取得します。 + * + * @param queue キューのインスタンス + * @return キューのサイズ + */ +size_t queue_size(struct queue* queue) +{ + return queue->size; +} + + +/** + * キューに格納されている全エントリーを引数に、指定された handler を呼び出します。 + * + * handler の引数: + * - data : データ + * - size : データのサイズ + * + * @param queue キューのインスタンス + * @param handler ハンドラ + */ +void queue_entries(struct queue* queue, bool (*handler)(void* data, size_t size)) +{ + bool is_continue = true; + size_t counter = queue->size; + struct queue_entry* entry = queue->head; + while (is_continue && (counter > 0)) + { + is_continue = handler(entry->data, entry->size); + entry = queue_next_entry(queue, entry); + counter--; + } +} + + +/** + * キューインスタンスの全エントリを引数に、指定された handler を呼び出します。 + * この関数は、デバッグ用の関数です。 + * handler には、実際にデータが入っていないキューのエントリも渡されます。 + * + * handler の引数: + * - data : データ + * - size : データのサイズ + * - index : キューの管理上のインデックス + * - enabled : true/false (有効なデータ/無効なデータ) + * - arg : ユーザーデータ + * + * @param queue キューのインスタンス + * @param handler ハンドラ + * @param arg ハンドラに渡すユーザーデータ + */ +void queue_entries_full(struct queue* queue, + bool (*handler)(void* data, size_t size, int index, bool enabled, void* arg), + void* arg) +{ + bool is_enabled; + bool is_continue = true; + struct queue_entry* entry = queue->entries; + for (int i = 0; is_continue && (i < (int) queue->capacity); i++) + { + is_enabled = queue_is_enabled(queue, entry); + is_continue = handler(entry->data, entry->size, i, is_enabled, arg); + entry++; + } +} + + + +/* ============================================================================= + * 内部関数 + * ============================================================================= + */ + + +/** + * 指定されたエントリの次のエントリーを取得します。 + * エントリー配列の末尾に到達した場合、先頭のエントリを返します。 + * + * @param queue キューのインスタンス + * @param entry エントリ + * @return 次のエントリ + */ +static +struct queue_entry* queue_next_entry(struct queue* queue, struct queue_entry* entry) +{ + struct queue_entry* next_entry = entry; + next_entry++; + if (next_entry >= (queue->entries + queue->capacity)) + { + next_entry = queue->entries; + } + return next_entry; +} + + +/** + * 指定されたエントリが、有効か否かを返します。 + * + * @param queue キューのインスタンス + * @param entry エントリ + * @return true/false (有効/無効) + */ +static +bool queue_is_enabled(struct queue* queue, struct queue_entry* entry) +{ + bool is_enabled; + if ((queue->size != queue->capacity) && (queue->head <= queue->tail)) + { /* キューのエントリ配列が以下のような状態の場合 + * [- |- |HT-|- |- |- |- |- ] + * [- |- |Ho |o |o |T-|- |- ] (-:空/o:データ有/H:Head/T:Tail) + */ + is_enabled = ((queue->head <= entry) && (entry < queue->tail)); + } + else + { /* キューのエントリ配列が以下のような状態の場合 + * [o |o |HTo|o |o |o |o |o ] + * [o |o |T- |- |- |Ho|o |o ] (-:空/o:データ有/H:Head/T:Tail) + */ + is_enabled = ((entry < queue->tail) || (queue->head <= entry)); + } + return is_enabled; +} diff --git a/.bash.d/tmp/util/src/util_thread.c b/.bash.d/tmp/util/src/util_thread.c new file mode 100644 index 0000000..20c6fc6 --- /dev/null +++ b/.bash.d/tmp/util/src/util_thread.c @@ -0,0 +1,341 @@ +/** + * @file util_thread.c + * スレッドを扱うモジュール。 + */ +#include +#include +#include +#include + +#include "util_thread.h" + + + +/* ///////////////////////////////////////////////////////////////////////////// +// +// スレッド +// +*/ + + +/* ============================================================================ + * 構造体定義 + * ============================================================================ + */ + +/** + * スレッドインスタンス構造体。 + */ +struct thread { + void (*start_routine)(void*); /**< スレッドとして実行する関数 */ + void* arg; /**< スレッドに渡すデータ */ + pthread_t tid; /**< スレッドのID */ +}; + + + +/* ============================================================================ + * プロトタイプ宣言 (内部でのみ利用する関数) + * ============================================================================ + */ +static void* thread_run(void* args); + + + +/* ============================================================================ + * 公開関数 + * ============================================================================ + */ + +/** + * スレッドを生成します。 + * スレッドの生成に失敗した場合、NULL を返します。 + * + * @param start_routine スレッドで実行する関数 + * @param arg スレッドに渡すデータへのポインタ + * @return スレッドインスタンス + */ +struct thread* thread_new(void (*start_routine)(void*), void* arg) +{ + struct thread* thread = (struct thread*) malloc(sizeof(struct thread)); + if (thread != NULL) + { + thread->start_routine = start_routine; + thread->arg = arg; + } + return thread; +} + + +/** + * スレッドインスタンスのリソースを開放します。 + * スレッド自体が終了するわけではありません。 + * + * @param thread スレッドインスタンス + */ +void thread_delete(struct thread* thread) +{ + free(thread); +} + + +/** + * スレッドを開始します。 + * + * @param thread スレッドインスタンス + */ +void thread_start(struct thread* thread) +{ + pthread_create(&thread->tid, NULL, thread_run, thread); +} + + +/** + * 指定されたスレッドが終了するのを待ちます。 + * + * @param thread スレッドインスタンス + */ +bool thread_join(struct thread* thread) +{ + int ret = pthread_join(thread->tid, NULL); + return (ret == 0); +} + + +/** + * 現在のスレッドが指定されたスレッドと同一か否かを返します。 + * + * @param thread スレッドインスタンス + * @return true/false (一致/不一致) + */ +bool thread_equals(struct thread* thread) +{ + pthread_t tid = pthread_self(); + return (tid == thread->tid); +} + + + +/* ============================================================================ + * 内部関数 + * ============================================================================ + */ + +/** + * スレッドとして実行される関数。 + * この関数の中で、スレッド起動に指定された関数を実行します。 + * + * @param arg スレッドインスタンス + */ +static +void* thread_run(void* arg) +{ + struct thread* thread = (struct thread*) arg; + thread->start_routine(thread->arg); + return NULL; +} + + + +/* ///////////////////////////////////////////////////////////////////////////// +// +// mutex +// +*/ + + +/* ============================================================================ + * 構造体定義 + * ============================================================================ + */ + +/** + * mutex インスタンス構造体。 + */ +struct mutex { + pthread_mutex_t mutex; /**< mutex オブジェクト */ +}; + + + +/* ============================================================================ + * 公開関数 + * ============================================================================ + */ + +/** + * mutex を生成します。 + * mutex の生成に失敗した場合、NULL を返します。 + * + * @return mutex + */ + +struct mutex* mutex_new(void) +{ + struct mutex* mutex = (struct mutex*) malloc(sizeof(struct mutex)); + if (mutex != NULL) + { + pthread_mutex_init(&mutex->mutex, NULL); + } + return mutex; +} + + +/** + * mutex を破棄します。 + * mutex がロックされている場合は、破棄に失敗しエラーを返します。 + * + * @param mutex 破棄する mutex + * @return true/false (破棄成功/破棄失敗 [mutex がロックされている]) + */ +bool mutex_delete(struct mutex* mutex) +{ + if (mutex != NULL) + { + int res = pthread_mutex_destroy(&mutex->mutex); + if (res == 0) + { + free(mutex); + return true; + } + } + return false; +} + + +/** + * mutex をロックします。 + * + * @param mutex ロックする mutex + */ +void mutex_lock(struct mutex* mutex) +{ + // mutex は、デフォルトの種別で初期化済みのため、 + // EINVAL, EDEADLK のエラーは発生しない。 + pthread_mutex_lock(&mutex->mutex); +} + + +/** + * mutex をアンロックします。 + * + * @param mutex アンロックする mutex + */ +void mutex_unlock(struct mutex* mutex) +{ + // mutex は、デフォルトの種別で初期化済みのため、 + // EINVAL, EPERM のエラーは発生しない。 + pthread_mutex_unlock(&mutex->mutex); +} + + + +/* ///////////////////////////////////////////////////////////////////////////// +// +// cond +// +*/ + + +/* ============================================================================ + * 構造体定義 + * ============================================================================ + */ + +/** + * cond インスタンス構造体。 + */ +struct cond { + pthread_cond_t cond; /**< cond オブジェクト */ +}; + + + +/* ============================================================================ + * 公開関数 + * ============================================================================ + */ + +/** + * cond を生成します。 + * cond の生成に失敗した場合、NULL を返します。 + * + * @return cond + */ + +struct cond* cond_new(void) +{ + struct cond* cond = (struct cond*) malloc(sizeof(struct cond)); + if (cond != NULL) + { + pthread_cond_init(&cond->cond, NULL); + } + return cond; +} + + +/** + * cond を破棄します。 + * cond が条件変数を待っている場合、破棄に失敗しエラーを返します。 + * + * @param cond 破棄する cond + * @return true/false (破棄成功/破棄失敗 [cond が条件変数を待っている]) + */ +bool cond_delete(struct cond* cond) +{ + if (cond!= NULL) + { + int res = pthread_cond_destroy(&cond->cond); + if (res == 0) + { + free(cond); + return true; + } + } + return false; +} + + +/** + * 指定された mutex のアンロックと、条件変数 cond の送信に対する待機を + * アトミックに行います。条件変数が送信されるまで、スレッドの実行は停止され、 + * CPU時間を消費しません。 + * + * 本関数を実行する前に、 mutex はロックされている必要があります。 + * 本関数を呼び出しスレッドが、条件変数の待機完了により動作する際、 + * mutex は再びロックされます。 + * + * @param cond cond インスタンス + * @param mutex mutex ロック済みの mutex + */ +void cond_wait(struct cond* cond, struct mutex* mutex) +{ + pthread_cond_wait(&cond->cond, &mutex->mutex); +} + + +/** + * 条件変数 cond に備えて待機しているスレッドの一つの実行を再開させます。 + * cond を待機しているスレッドがなければ何もしません。 + * 複数のスレッドが cond を待機している場合、どのスレッドが再開されるかはわからない。 + * + * @param cond 再開させる cond + */ +void cond_signal(struct cond* cond) +{ + pthread_cond_signal(&cond->cond); +} + + +/** + * 条件変数 cond に備えて待機しているすべてのスレッドを再開させます。 + * cond を待機しているスレッドがなければ何もしません。 + * + * @param cond 再開させる cond + */ +void cond_broadcast(struct cond* cond) +{ + pthread_cond_broadcast(&cond->cond); +} + + diff --git a/.bash.d/tmp/util/test/Makefile b/.bash.d/tmp/util/test/Makefile new file mode 100644 index 0000000..395b46a --- /dev/null +++ b/.bash.d/tmp/util/test/Makefile @@ -0,0 +1,29 @@ +# ============================================================================== +# Makefile +# ============================================================================== +TOPDIR ?= ../../.. +TARGET = ut_libutil.exe +SUBDIRS = +VERSION = +TARGET_TYPE = target-exe +#TARGET_TYPE = target-a +#TARGET_TYPE = target-so +#TARGET_TYPE = target-dll + +EXCLUDES = ../src/ut_queue.c + +include $(TOPDIR)/mk/conf.mk +include $(TOPDIR)/mk/$(USE)/com.*.mk +include $(TOPDIR)/mk/$(USE)/conf.ut.*.mk + +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -lcunit -lpthread + +.PHONY: all +all: all-subdir $(TARGET) + + +include $(TOPDIR)/mk/$(USE)/rule.*.mk + diff --git a/.bash.d/tmp/util/test/src/test_main.c_ b/.bash.d/tmp/util/test/src/test_main.c_ new file mode 100644 index 0000000..ed7cf1d --- /dev/null +++ b/.bash.d/tmp/util/test/src/test_main.c_ @@ -0,0 +1,26 @@ +#include +#include +#include + +#include "test_thread.h" + + +int main(void) +{ + test_thread(); + return 0; +} +int t(void) +{ + CU_pSuite testSuite; + + CU_initialize_registry(); + testSuite = CU_add_suite("libutil.so",NULL,NULL); + + CU_add_test(testSuite, "thread", test_thread); + + CU_console_run_tests(); + CU_cleanup_registry(); + + return 0; +} diff --git a/.bash.d/tmp/util/test/src/test_thread.c b/.bash.d/tmp/util/test/src/test_thread.c new file mode 100644 index 0000000..682dcd4 --- /dev/null +++ b/.bash.d/tmp/util/test/src/test_thread.c @@ -0,0 +1,42 @@ +#include +#include + +#include "util_thread.h" +#include "test_thread.h" + + + +static void* thread_1_arg = NULL; +void thread_1(void* arg) +{ + thread_1_arg = arg; + for (int i = 0; i < 5; i++) + { + sleep(1); + printf("[%s] %d \n", (const char*)thread_1_arg, i); + } +} +static void* thread_2_arg = NULL; +void thread_2(void* arg) +{ + thread_2_arg = arg; + for (int i = 0; i < 5; i++) + { + printf("[%s] %d\n", (const char*)thread_2_arg, i); + sleep(1); + } +} + +void test_thread(void) +{ + struct thread* thread1 = thread_new(thread_1, "thread_1"); + struct thread* thread2 = thread_new(thread_2, "thread_2"); + + thread_start(thread1); + thread_start(thread2); + + thread_join(thread1); + thread_join(thread2); + +} + diff --git a/.bash.d/tmp/util/Makefile b/.bash.d/tmp/util/Makefile new file mode 100644 index 0000000..722430a --- /dev/null +++ b/.bash.d/tmp/util/Makefile @@ -0,0 +1,29 @@ +# ============================================================================== +# Makefile +# ============================================================================== +TOPDIR ?= ../.. +TARGET = libutil.so +SUBDIRS = +VERSION = +#TARGET_TYPE = target-exe +#TARGET_TYPE = target-a +TARGET_TYPE = target-so +#TARGET_TYPE = target-dll + +include $(TOPDIR)/mk/conf.mk +include $(TOPDIR)/mk/$(USE)/com.*.mk +include $(TOPDIR)/mk/$(USE)/conf.def.*.mk + +CFLAGS += -fPIC +CXXFLAGS += +LDFLAGS += + +.PHONY: all test +all: all-subdir $(TARGET) + +test: + $(MAKE) -C test + + +include $(TOPDIR)/mk/$(USE)/rule.*.mk + diff --git a/.bash.d/tmp/util/include/util_blockingqueue.h b/.bash.d/tmp/util/include/util_blockingqueue.h new file mode 100644 index 0000000..0309bff --- /dev/null +++ b/.bash.d/tmp/util/include/util_blockingqueue.h @@ -0,0 +1,24 @@ +#ifndef UTIL_BLOCKINGQUEUE_H +#define UTIL_BLOCKINGQUEUE_H + +#include + + +/** + * ブロッキングキューのインスタンス構造体 + */ +struct blockingqueue; + +struct blockingqueue* blockingqueue_new(int cap); +void blockingqueue_destroy(struct blockingqueue* queue); +bool blockingqueue_push(struct blockingqueue* queue, void* data, size_t size); +bool blockingqueue_push_blocking(struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_pop( struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_pop_blocking( struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_peek(struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_size(struct blockingqueue* queue); +void blockingqueue_notify(struct blockingqueue* queue); + + +#endif /* UTIL_BLOCKINGQUEUE_H */ + diff --git a/.bash.d/tmp/util/include/util_queue.h b/.bash.d/tmp/util/include/util_queue.h new file mode 100644 index 0000000..2de9762 --- /dev/null +++ b/.bash.d/tmp/util/include/util_queue.h @@ -0,0 +1,21 @@ +#ifndef UTIL_QUEUE_H +#define UTIL_QUEUE_H + +#include + + +/** キュー */ +struct queue; +struct queue* queue_new(size_t cap, size_t size); +void queue_destroy(struct queue* queue); +bool queue_push(struct queue* queue, void* data, size_t size); +size_t queue_pop( struct queue* queue, void* data, size_t size); +size_t queue_peek(struct queue* queue, void* data, size_t size); +size_t queue_size(struct queue* queue); +void queue_entries(struct queue* queue, bool (*handler)(void* data, size_t size)); +void queue_entries_full(struct queue* queue, + bool (*handler)(void* data, size_t size, int index, bool enabled, void* arg), + void* arg); + +#endif /* UTIL_QUEUE_H */ + diff --git a/.bash.d/tmp/util/include/util_thread.h b/.bash.d/tmp/util/include/util_thread.h new file mode 100644 index 0000000..60e7bb0 --- /dev/null +++ b/.bash.d/tmp/util/include/util_thread.h @@ -0,0 +1,38 @@ +/** + * @file util_thread.c + * スレッドを扱うモジュール + * + * 実装側のみ複数のOS,ライブラリ用に変更できるよう + * 各オブジェクトを typedef ではなく、構造体にしている。 + */ +#ifndef UTIL_THREAD_H +#define UTIL_THREAD_H + +#include + +/* スレッド */ +struct thread; +struct thread* thread_new(void (*start_routine)(void*), void* arg); +void thread_delete(struct thread* thread); +void thread_start(struct thread* thread); +bool thread_join(struct thread* thread); +bool thread_equals(struct thread* thread); + +/* mutex */ +struct mutex; +struct mutex* mutex_new(void); +bool mutex_delete(struct mutex* mutex); +void mutex_lock(struct mutex* mutex); +void mutex_unlock(struct mutex* mutex); + +/* cond */ +struct cond; +struct cond* cond_new(void); +bool cond_delete(struct cond* cond); +void cond_wait(struct cond* cond, struct mutex* mutex); +void cond_signal(struct cond* cond); +void cond_broadcast(struct cond* cond); + + +#endif /* THREAD_H */ + diff --git a/.bash.d/tmp/util/src/logger.c b/.bash.d/tmp/util/src/logger.c new file mode 100644 index 0000000..e96204d --- /dev/null +++ b/.bash.d/tmp/util/src/logger.c @@ -0,0 +1,117 @@ +#include +#include +#include +#include +#include +#include + +#include "logger.h" + + + +// ============================================================================= +// 内部でのみ使用する関数のプロトタイプ宣言 +// ============================================================================= +static void logger_log_output(int priority, const char* message); +static int get_date(char* buff, size_t size); + + + + + +// ============================================================================= +// 関数定義 +// ============================================================================= + +/** + * 指定されたログレベルのログを出力可能か否かを返します。 + * + * @param priority ログレベル + * @return true/false (ログ出力可能/ログ出力不可) + */ +bool logger_is_logable(int priority) +{ + int current_logmask = setlogmask(0); + return (current_logmask & priority); +} + + + +/** + * ログを出力します。 + * + * @param priority syslog と同じログのレベルを指定します。 + * @param file __FILE__ を指定します。 + * @param line __LINE__ を指定します。 + * @param format 書式を指定します。 + */ +void logger_log_(int priority, const char* file, int line, const char* format, ...) +{ + va_list ap; + va_start(ap, format); + bool is_logable = logger_is_logable(priority); + if (is_logable) + { + char log_buffer[512]; + int len = get_date(log_buffer, sizeof(log_buffer)); + len += snprintf(&log_buffer[len], sizeof(log_buffer) - len, " %s:%d: ", file, line); + vsnprintf(&log_buffer[len], sizeof(log_buffer) - len, format, ap); + logger_log_output(priority, log_buffer); + } + va_end(ap); +} + + +/** + * 指定されたメッセージをログに出力します。 + * + * @param priority ログレベル + * @param message 出力するメッセージ + */ +static void logger_log_output(int priority, const char* message) +{ +#ifdef USE_SYSLOG + static bool is_logopend = false; + if (!is_logopend) + { + openlog("ifconv", LOG_CONS | LOG_PID, LOG_USER); + is_logopend = true; + } + syslog(priority, "%s", message); +#else + pid_t current_pid = getpid(); + printf("ifconv[%d][pri=%d]: %s\n", current_pid, priority, message); +#endif +} + + +/** + * 指定されたバッファに、現在の時刻(ナノ秒まで)を文字列形式で格納します。 + * + * @param buff バッファ + * @param size バッファサイズ + * @return バッファに書き込んだ文字数 + */ +static int get_date(char* buff, size_t size) +{ + // ナノ秒まで取得する。 + struct timespec current_time; + clock_gettime(CLOCK_REALTIME, ¤t_time); + + // 秒を日時に変換する。 + struct tm* local_tm = localtime(¤t_time.tv_sec); + + // YYYY/MM/DD HH:MM:SS.XXXXXXXXX に変換する。 + int len = snprintf(buff, size, "%04d/%02d/%02d %02d:%02d:%02d.%09ld", + local_tm->tm_year + 1900, + local_tm->tm_mon + 1, + local_tm->tm_mday, + local_tm->tm_hour, + local_tm->tm_min, + local_tm->tm_sec, + current_time.tv_nsec); + + return len; +} + + diff --git a/.bash.d/tmp/util/src/util_blockingqueue.c b/.bash.d/tmp/util/src/util_blockingqueue.c new file mode 100644 index 0000000..5b66c33 --- /dev/null +++ b/.bash.d/tmp/util/src/util_blockingqueue.c @@ -0,0 +1,200 @@ +#include +#include +#include +#include + +#include "util_queue.h" +#include "util_blockingqueue.h" + +/** + * ブロッキングキューのインスタンス構造体 + */ +struct blockingqueue { + struct queue* queue; /**< キュー */ + struct mutex* data_lock; /**< データロック */ + struct cond* empty_block; /**< pop のブロック用 */ + struct cond* full_block; /**< push のブロック用 */ +}; + + +/** + * ブロッキングキューを生成します。 + * インスタンスを生成できない場合、NULL を返します。 + * + * @param cap キューの容量 + * @param size キューに格納するデータサイズ + * @return イベントキューのインスタンス + */ +struct blockingqueue* blockingqueue_new(size_t cap, size_t size) +{ + struct blockingqueue* blockingqueue = (struct blockingqueue*) malloc(sizeof(struct blockingqueue)); + if (blockingqueue != NULL) + { + blockingqueue->queue = queue_new(cap,4096); + if (blockingqueue->queue != NULL) + { // pthread_mutex_lock ですでに保持している mutex をロックしようとしたとき、 + // デフォルトのスレッドを停止させる動作とするため、NULL (デフォルト設定)を指定する。 + pthread_mutex_init(&blockingqueue->data_lock, NULL); + + // データがない場合 pop をブロックするための cond オブジェクトを初期化する。 + pthread_cond_init(&blockingqueue->pop_block, NULL); + } + else + { + free(blockingqueue); + blockingqueue = NULL; + } + + } + return blockingqueue; +} + + + + +/** + * 指定されたイベントキューを破棄します。 + * + * @param blockingqueue イベントキューのインスタンス + */ +void blockingqueue_destroy(struct blockingqueue* blockingqueue) +{ + if (blockingqueue != NULL) + { + // T.B.D. + queue_destroy(blockingqueue->queue); + blockingqueue->queue = NULL; + } + free(blockingqueue); +} + + +/** + * 指定された mutex をロックします。 + * + * @param mutex mutexオブジェクト + */ +static +void blockingqueue_mutex_lock(pthread_mutex_t* mutex) +{ + int errcode = pthread_mutex_lock(mutex); + if (errcode != 0) + { // エラーが発生する条件は、下記の何れかであり、本プログラムの設定では発生しない。 + // => 発生するのであれば、プログラム誤り。 + // EINVAL : mutex が適切に初期化されていない。 + // EDEADLK : mutex は既に呼び出しスレッドによりロックされている (「エラー検査を行う」mutexes のみ) + printf("###!!! [ERROR] pthred_mutex_lock !!!###\n"); + } +} + + +/** + * 指定された mutex をアンロックします。 + * + * @param mutex mutexオブジェクト + */ +static +void blockingqueue_mutex_unlock(pthread_mutex_t* mutex) +{ + int errcode = pthread_mutex_unlock(mutex); + if (errcode != 0) + { // エラーが発生する条件は、下記の何れかであり、本プログラムの設定では発生しない。 + // => 発生するのであれば、プログラム誤り。 + // EINVAL : mutex が適切に初期化されていない。 + // EDEADLK : mutex は既に呼び出しスレッドによりロックされている (「エラー検査を行う」mutexes のみ) + printf("###!!! [ERROR] pthred_mutex_lock !!!###\n"); + } +} + + +/** + * イベントキューにデータを追加します。 + * + * @param blockingqueue キューのインスタンス + * @param data キューに追加するエントリ(データ) + * @param size キューに追加するエントリ(データ)のサイズ + * @return true/false (追加成功/追加失敗) + */ +bool blockingqueue_push(struct blockingqueue* blockingqueue, void* data, size_t size) +{ + blockingqueue_mutex_lock(&blockingqueue->data_lock); + + // キューにデータがない間ブロックする。 + size_t data_count = queue_size(blockingqueue->queue); + if (data_count == 0) + { + pthread_cond_signal(&blockingqueue->pop_block); + } + + bool result = queue_push(blockingqueue->queue, data, size); + + blockingqueue_mutex_unlock(&blockingqueue->data_lock); + + return result; +} + + +/** + * キューの先頭よりデータを取り出します。 + * キューにデータがない場合、ブロックされます。 + * + * @param blockingqueue キューのインスタンス + * @param buf 取り出したデータ格納用バッファ + * @param buflen バッファのサイズ + * @return 取り出したデータのサイズ + */ +size_t blockingqueue_pop( struct blockingqueue* blockingqueue, void* buf, size_t buflen) +{ + blockingqueue_mutex_lock(&blockingqueue->data_lock); + + // キューにデータがない間ブロックする。 + size_t data_count = queue_size(blockingqueue->queue); + while (data_count == 0) + { + pthread_cond_wait(&blockingqueue->pop_block, &blockingqueue->data_lock); + data_count = queue_size(blockingqueue->queue); + } + + size_t data_size = queue_pop(blockingqueue->queue, buf, buflen); + blockingqueue_mutex_unlock(&blockingqueue->data_lock); + return data_size; +} + + +/** + * キューの先頭よりデータを取り出します。 + * データの取り出しに失敗した場合、0を返します。 + * blockingqueue_pop と異なり、キューからデータは削除されません。 + * + * @param blockingqueue キューのインスタンス + * @param buf 取り出したデータ格納用バッファ + * @param buflen バッファのサイズ + * @return 取り出したデータのサイズ + */ +size_t blockingqueue_peek( struct blockingqueue* blockingqueue, void* buf, size_t buflen) +{ + blockingqueue_mutex_lock(&blockingqueue->data_lock); + size_t size = queue_peek(blockingqueue->queue, buf, buflen); + blockingqueue_mutex_unlock(&blockingqueue->data_lock); + return size; +} + + + +/** + * キューのサイズ(キューに入っているデータの個数)を取得します。 + * + * @param blockingqueue キューのインスタンス + * @return キューのサイズ + */ +size_t blockingqueue_size(struct blockingqueue* blockingqueue) +{ + size_t size; + blockingqueue_mutex_lock(&blockingqueue->data_lock); + size = queue_size(blockingqueue->queue); + blockingqueue_mutex_unlock(&blockingqueue->data_lock); + return size; +} + + +#endif diff --git a/.bash.d/tmp/util/src/util_queue.c b/.bash.d/tmp/util/src/util_queue.c new file mode 100644 index 0000000..fb2e153 --- /dev/null +++ b/.bash.d/tmp/util/src/util_queue.c @@ -0,0 +1,341 @@ +#include +#include +#include + +#include "util_thread.h" +#include "util_queue.h" + + + +/* ============================================================================= + * 構造体定義 + * ============================================================================= + */ + +/** + * キューのエントリ構造体 + */ +struct queue_entry { + size_t size; /**< 実データサイズ */ + char* data; /**< データバッファ */ +}; + + +/** + * キューのインスタンス構造体 + */ +struct queue { + size_t capacity; /**< キューの容量 */ + size_t size; /**< キューのサイズ */ + size_t max_data_size; /**< データの最大サイズ */ + struct queue_entry* entries; /**< データのエントリ */ + struct queue_entry* head; /**< キューの取出位置 */ + struct queue_entry* tail; /**< キューの挿入位置 */ +}; + + + +/* ============================================================================= + * 内部関数プロトタイプ宣言 + * ============================================================================= + */ +static struct queue_entry* queue_next_entry(struct queue* queue, struct queue_entry* entry); +static bool queue_is_enabled(struct queue* queue, struct queue_entry* entry); + + + +/* ============================================================================= + * 公開関数 + * ============================================================================= + */ + +/** + * キューを生成します。 + * インスタンスを生成できない場合、NULL を返します。 + * + * [注意] + * このキューは、同期化されません。 + * 同期化が必要な場合は、blocking_queue を使用してください。 + * + * @param cap キューの容量(格納可能なデータの個数) + * @param size キューに格納する1つあたりのデータサイズ + * @return キューのインスタンス + */ +/* [実装メモ] + * キューのデータ管理構造 + * メモリ確保を 1 回の malloc でできるようにしている。 + * +-------------------+ + * | queue 構造体 | + * +-------------------+ + * | entry[0] | + * +-------------------+ + * : + * +-------------------+ + * | entry[cap-1] | + +-------------------+ + * | entry[0].data | + * | 用バッファ | + * +-------------------+ + * : + * +-------------------+ + * | entry[cap-1].data | + * | 用バッファ | + * +-------------------+ + */ +struct queue* queue_new(size_t cap, size_t size) +{ + size_t queue_size = sizeof(struct queue) + + ((sizeof(struct queue_entry) + size) * cap); + struct queue* queue = (struct queue*) malloc(queue_size); + if (queue != NULL) + { + queue->capacity = cap; + queue->size = 0; + queue->max_data_size = size; + queue->entries = (struct queue_entry*) (queue + 1); + queue->head = queue->entries; + queue->tail = queue->entries; + + + char* tmp_data_ptr = (char*) (queue->entries + queue->capacity); + struct queue_entry* tmp_entry = queue->entries; + for (size_t i = 0; i < queue->capacity; i++) + { + tmp_entry->data = tmp_data_ptr; + tmp_entry++; + tmp_data_ptr += queue->max_data_size; + } + } + return queue; +} + + + +/** + * 指定されたキューを破棄します。 + * + * @param queue キューのインスタンス + */ +void queue_destroy(struct queue* queue) +{ + free(queue); +} + + +/** + * キューにデータを追加します。 + * + * @param queue キューのインスタンス + * @param data キューに追加するエントリ(データ) + * @param size キューに追加するエントリ(データ)のサイズ + * @return true/false (追加成功/追加失敗) + */ +bool queue_push(struct queue* queue, void* data, size_t size) +{ + bool result = false; + + if (size < queue->max_data_size) + { + if (queue->size < queue->capacity) + { + memset(queue->tail->data, 0x00, sizeof(struct queue_entry)); + memcpy(queue->tail->data, data, size); + queue->tail->size = size; + queue->tail++; + if (queue->tail >= (queue->entries + queue->capacity)) + { + queue->tail = queue->entries; + } + queue->size++; + result = true; + } + } + return result; +} + + +/** + * キューの先頭よりデータを取り出します。 + * データの取り出しに失敗した場合、0を返します。 + * + * @param queue キューのインスタンス + * @param buf 取り出したデータ格納用バッファ + * @param buflen バッファのサイズ + * @return 取り出したデータのサイズ + */ +size_t queue_pop( struct queue* queue, void* buf, size_t buflen) +{ + size_t size = 0; + if (queue->size > 0) + { + if (queue->head->size < buflen) + { + size = queue->head->size; + memset(buf, 0x00, buflen); + memcpy(buf, queue->head->data, size); + + // データクリア (なくても動作上問題ない) + memset(queue->head->data, 0x00, queue->max_data_size); + queue->head->size = 0; + + queue->head++; + if (queue->head >= (queue->entries + queue->capacity)) + { + queue->head = queue->entries; + } + queue->size--; + } + } + return size; +} + + +/** + * キューの先頭よりデータを取り出します。 + * データの取り出しに失敗した場合、0を返します。 + * queue_pop と異なり、キューからデータは削除されません。 + * + * @param queue キューのインスタンス + * @param buf 取り出したデータ格納用バッファ + * @param buflen バッファのサイズ + * @return 取り出したデータのサイズ + */ +size_t queue_peek( struct queue* queue, void* buf, size_t buflen) +{ + size_t size = 0; + if (queue->size > 0) + { + if (queue->head->size < buflen) + { + size = queue->head->size; + memset(buf, 0x00, buflen); + memcpy(buf, queue->head->data, size); + } + } + return size; +} + + +/** + * キューのサイズ(キューに入っているデータの個数)を取得します。 + * + * @param queue キューのインスタンス + * @return キューのサイズ + */ +size_t queue_size(struct queue* queue) +{ + return queue->size; +} + + +/** + * キューに格納されている全エントリーを引数に、指定された handler を呼び出します。 + * + * handler の引数: + * - data : データ + * - size : データのサイズ + * + * @param queue キューのインスタンス + * @param handler ハンドラ + */ +void queue_entries(struct queue* queue, bool (*handler)(void* data, size_t size)) +{ + bool is_continue = true; + size_t counter = queue->size; + struct queue_entry* entry = queue->head; + while (is_continue && (counter > 0)) + { + is_continue = handler(entry->data, entry->size); + entry = queue_next_entry(queue, entry); + counter--; + } +} + + +/** + * キューインスタンスの全エントリを引数に、指定された handler を呼び出します。 + * この関数は、デバッグ用の関数です。 + * handler には、実際にデータが入っていないキューのエントリも渡されます。 + * + * handler の引数: + * - data : データ + * - size : データのサイズ + * - index : キューの管理上のインデックス + * - enabled : true/false (有効なデータ/無効なデータ) + * - arg : ユーザーデータ + * + * @param queue キューのインスタンス + * @param handler ハンドラ + * @param arg ハンドラに渡すユーザーデータ + */ +void queue_entries_full(struct queue* queue, + bool (*handler)(void* data, size_t size, int index, bool enabled, void* arg), + void* arg) +{ + bool is_enabled; + bool is_continue = true; + struct queue_entry* entry = queue->entries; + for (int i = 0; is_continue && (i < (int) queue->capacity); i++) + { + is_enabled = queue_is_enabled(queue, entry); + is_continue = handler(entry->data, entry->size, i, is_enabled, arg); + entry++; + } +} + + + +/* ============================================================================= + * 内部関数 + * ============================================================================= + */ + + +/** + * 指定されたエントリの次のエントリーを取得します。 + * エントリー配列の末尾に到達した場合、先頭のエントリを返します。 + * + * @param queue キューのインスタンス + * @param entry エントリ + * @return 次のエントリ + */ +static +struct queue_entry* queue_next_entry(struct queue* queue, struct queue_entry* entry) +{ + struct queue_entry* next_entry = entry; + next_entry++; + if (next_entry >= (queue->entries + queue->capacity)) + { + next_entry = queue->entries; + } + return next_entry; +} + + +/** + * 指定されたエントリが、有効か否かを返します。 + * + * @param queue キューのインスタンス + * @param entry エントリ + * @return true/false (有効/無効) + */ +static +bool queue_is_enabled(struct queue* queue, struct queue_entry* entry) +{ + bool is_enabled; + if ((queue->size != queue->capacity) && (queue->head <= queue->tail)) + { /* キューのエントリ配列が以下のような状態の場合 + * [- |- |HT-|- |- |- |- |- ] + * [- |- |Ho |o |o |T-|- |- ] (-:空/o:データ有/H:Head/T:Tail) + */ + is_enabled = ((queue->head <= entry) && (entry < queue->tail)); + } + else + { /* キューのエントリ配列が以下のような状態の場合 + * [o |o |HTo|o |o |o |o |o ] + * [o |o |T- |- |- |Ho|o |o ] (-:空/o:データ有/H:Head/T:Tail) + */ + is_enabled = ((entry < queue->tail) || (queue->head <= entry)); + } + return is_enabled; +} diff --git a/.bash.d/tmp/util/src/util_thread.c b/.bash.d/tmp/util/src/util_thread.c new file mode 100644 index 0000000..20c6fc6 --- /dev/null +++ b/.bash.d/tmp/util/src/util_thread.c @@ -0,0 +1,341 @@ +/** + * @file util_thread.c + * スレッドを扱うモジュール。 + */ +#include +#include +#include +#include + +#include "util_thread.h" + + + +/* ///////////////////////////////////////////////////////////////////////////// +// +// スレッド +// +*/ + + +/* ============================================================================ + * 構造体定義 + * ============================================================================ + */ + +/** + * スレッドインスタンス構造体。 + */ +struct thread { + void (*start_routine)(void*); /**< スレッドとして実行する関数 */ + void* arg; /**< スレッドに渡すデータ */ + pthread_t tid; /**< スレッドのID */ +}; + + + +/* ============================================================================ + * プロトタイプ宣言 (内部でのみ利用する関数) + * ============================================================================ + */ +static void* thread_run(void* args); + + + +/* ============================================================================ + * 公開関数 + * ============================================================================ + */ + +/** + * スレッドを生成します。 + * スレッドの生成に失敗した場合、NULL を返します。 + * + * @param start_routine スレッドで実行する関数 + * @param arg スレッドに渡すデータへのポインタ + * @return スレッドインスタンス + */ +struct thread* thread_new(void (*start_routine)(void*), void* arg) +{ + struct thread* thread = (struct thread*) malloc(sizeof(struct thread)); + if (thread != NULL) + { + thread->start_routine = start_routine; + thread->arg = arg; + } + return thread; +} + + +/** + * スレッドインスタンスのリソースを開放します。 + * スレッド自体が終了するわけではありません。 + * + * @param thread スレッドインスタンス + */ +void thread_delete(struct thread* thread) +{ + free(thread); +} + + +/** + * スレッドを開始します。 + * + * @param thread スレッドインスタンス + */ +void thread_start(struct thread* thread) +{ + pthread_create(&thread->tid, NULL, thread_run, thread); +} + + +/** + * 指定されたスレッドが終了するのを待ちます。 + * + * @param thread スレッドインスタンス + */ +bool thread_join(struct thread* thread) +{ + int ret = pthread_join(thread->tid, NULL); + return (ret == 0); +} + + +/** + * 現在のスレッドが指定されたスレッドと同一か否かを返します。 + * + * @param thread スレッドインスタンス + * @return true/false (一致/不一致) + */ +bool thread_equals(struct thread* thread) +{ + pthread_t tid = pthread_self(); + return (tid == thread->tid); +} + + + +/* ============================================================================ + * 内部関数 + * ============================================================================ + */ + +/** + * スレッドとして実行される関数。 + * この関数の中で、スレッド起動に指定された関数を実行します。 + * + * @param arg スレッドインスタンス + */ +static +void* thread_run(void* arg) +{ + struct thread* thread = (struct thread*) arg; + thread->start_routine(thread->arg); + return NULL; +} + + + +/* ///////////////////////////////////////////////////////////////////////////// +// +// mutex +// +*/ + + +/* ============================================================================ + * 構造体定義 + * ============================================================================ + */ + +/** + * mutex インスタンス構造体。 + */ +struct mutex { + pthread_mutex_t mutex; /**< mutex オブジェクト */ +}; + + + +/* ============================================================================ + * 公開関数 + * ============================================================================ + */ + +/** + * mutex を生成します。 + * mutex の生成に失敗した場合、NULL を返します。 + * + * @return mutex + */ + +struct mutex* mutex_new(void) +{ + struct mutex* mutex = (struct mutex*) malloc(sizeof(struct mutex)); + if (mutex != NULL) + { + pthread_mutex_init(&mutex->mutex, NULL); + } + return mutex; +} + + +/** + * mutex を破棄します。 + * mutex がロックされている場合は、破棄に失敗しエラーを返します。 + * + * @param mutex 破棄する mutex + * @return true/false (破棄成功/破棄失敗 [mutex がロックされている]) + */ +bool mutex_delete(struct mutex* mutex) +{ + if (mutex != NULL) + { + int res = pthread_mutex_destroy(&mutex->mutex); + if (res == 0) + { + free(mutex); + return true; + } + } + return false; +} + + +/** + * mutex をロックします。 + * + * @param mutex ロックする mutex + */ +void mutex_lock(struct mutex* mutex) +{ + // mutex は、デフォルトの種別で初期化済みのため、 + // EINVAL, EDEADLK のエラーは発生しない。 + pthread_mutex_lock(&mutex->mutex); +} + + +/** + * mutex をアンロックします。 + * + * @param mutex アンロックする mutex + */ +void mutex_unlock(struct mutex* mutex) +{ + // mutex は、デフォルトの種別で初期化済みのため、 + // EINVAL, EPERM のエラーは発生しない。 + pthread_mutex_unlock(&mutex->mutex); +} + + + +/* ///////////////////////////////////////////////////////////////////////////// +// +// cond +// +*/ + + +/* ============================================================================ + * 構造体定義 + * ============================================================================ + */ + +/** + * cond インスタンス構造体。 + */ +struct cond { + pthread_cond_t cond; /**< cond オブジェクト */ +}; + + + +/* ============================================================================ + * 公開関数 + * ============================================================================ + */ + +/** + * cond を生成します。 + * cond の生成に失敗した場合、NULL を返します。 + * + * @return cond + */ + +struct cond* cond_new(void) +{ + struct cond* cond = (struct cond*) malloc(sizeof(struct cond)); + if (cond != NULL) + { + pthread_cond_init(&cond->cond, NULL); + } + return cond; +} + + +/** + * cond を破棄します。 + * cond が条件変数を待っている場合、破棄に失敗しエラーを返します。 + * + * @param cond 破棄する cond + * @return true/false (破棄成功/破棄失敗 [cond が条件変数を待っている]) + */ +bool cond_delete(struct cond* cond) +{ + if (cond!= NULL) + { + int res = pthread_cond_destroy(&cond->cond); + if (res == 0) + { + free(cond); + return true; + } + } + return false; +} + + +/** + * 指定された mutex のアンロックと、条件変数 cond の送信に対する待機を + * アトミックに行います。条件変数が送信されるまで、スレッドの実行は停止され、 + * CPU時間を消費しません。 + * + * 本関数を実行する前に、 mutex はロックされている必要があります。 + * 本関数を呼び出しスレッドが、条件変数の待機完了により動作する際、 + * mutex は再びロックされます。 + * + * @param cond cond インスタンス + * @param mutex mutex ロック済みの mutex + */ +void cond_wait(struct cond* cond, struct mutex* mutex) +{ + pthread_cond_wait(&cond->cond, &mutex->mutex); +} + + +/** + * 条件変数 cond に備えて待機しているスレッドの一つの実行を再開させます。 + * cond を待機しているスレッドがなければ何もしません。 + * 複数のスレッドが cond を待機している場合、どのスレッドが再開されるかはわからない。 + * + * @param cond 再開させる cond + */ +void cond_signal(struct cond* cond) +{ + pthread_cond_signal(&cond->cond); +} + + +/** + * 条件変数 cond に備えて待機しているすべてのスレッドを再開させます。 + * cond を待機しているスレッドがなければ何もしません。 + * + * @param cond 再開させる cond + */ +void cond_broadcast(struct cond* cond) +{ + pthread_cond_broadcast(&cond->cond); +} + + diff --git a/.bash.d/tmp/util/test/Makefile b/.bash.d/tmp/util/test/Makefile new file mode 100644 index 0000000..395b46a --- /dev/null +++ b/.bash.d/tmp/util/test/Makefile @@ -0,0 +1,29 @@ +# ============================================================================== +# Makefile +# ============================================================================== +TOPDIR ?= ../../.. +TARGET = ut_libutil.exe +SUBDIRS = +VERSION = +TARGET_TYPE = target-exe +#TARGET_TYPE = target-a +#TARGET_TYPE = target-so +#TARGET_TYPE = target-dll + +EXCLUDES = ../src/ut_queue.c + +include $(TOPDIR)/mk/conf.mk +include $(TOPDIR)/mk/$(USE)/com.*.mk +include $(TOPDIR)/mk/$(USE)/conf.ut.*.mk + +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -lcunit -lpthread + +.PHONY: all +all: all-subdir $(TARGET) + + +include $(TOPDIR)/mk/$(USE)/rule.*.mk + diff --git a/.bash.d/tmp/util/test/src/test_main.c_ b/.bash.d/tmp/util/test/src/test_main.c_ new file mode 100644 index 0000000..ed7cf1d --- /dev/null +++ b/.bash.d/tmp/util/test/src/test_main.c_ @@ -0,0 +1,26 @@ +#include +#include +#include + +#include "test_thread.h" + + +int main(void) +{ + test_thread(); + return 0; +} +int t(void) +{ + CU_pSuite testSuite; + + CU_initialize_registry(); + testSuite = CU_add_suite("libutil.so",NULL,NULL); + + CU_add_test(testSuite, "thread", test_thread); + + CU_console_run_tests(); + CU_cleanup_registry(); + + return 0; +} diff --git a/.bash.d/tmp/util/test/src/test_thread.c b/.bash.d/tmp/util/test/src/test_thread.c new file mode 100644 index 0000000..682dcd4 --- /dev/null +++ b/.bash.d/tmp/util/test/src/test_thread.c @@ -0,0 +1,42 @@ +#include +#include + +#include "util_thread.h" +#include "test_thread.h" + + + +static void* thread_1_arg = NULL; +void thread_1(void* arg) +{ + thread_1_arg = arg; + for (int i = 0; i < 5; i++) + { + sleep(1); + printf("[%s] %d \n", (const char*)thread_1_arg, i); + } +} +static void* thread_2_arg = NULL; +void thread_2(void* arg) +{ + thread_2_arg = arg; + for (int i = 0; i < 5; i++) + { + printf("[%s] %d\n", (const char*)thread_2_arg, i); + sleep(1); + } +} + +void test_thread(void) +{ + struct thread* thread1 = thread_new(thread_1, "thread_1"); + struct thread* thread2 = thread_new(thread_2, "thread_2"); + + thread_start(thread1); + thread_start(thread2); + + thread_join(thread1); + thread_join(thread2); + +} + diff --git a/.bash.d/tmp/util/test/src/test_thread.h b/.bash.d/tmp/util/test/src/test_thread.h new file mode 100644 index 0000000..82e91db --- /dev/null +++ b/.bash.d/tmp/util/test/src/test_thread.h @@ -0,0 +1,7 @@ +#ifndef TEST_THREAD_H +#define TEST_THREAD_H + +void test_thread(void); + +#endif + diff --git a/.bash.d/tmp/util/Makefile b/.bash.d/tmp/util/Makefile new file mode 100644 index 0000000..722430a --- /dev/null +++ b/.bash.d/tmp/util/Makefile @@ -0,0 +1,29 @@ +# ============================================================================== +# Makefile +# ============================================================================== +TOPDIR ?= ../.. +TARGET = libutil.so +SUBDIRS = +VERSION = +#TARGET_TYPE = target-exe +#TARGET_TYPE = target-a +TARGET_TYPE = target-so +#TARGET_TYPE = target-dll + +include $(TOPDIR)/mk/conf.mk +include $(TOPDIR)/mk/$(USE)/com.*.mk +include $(TOPDIR)/mk/$(USE)/conf.def.*.mk + +CFLAGS += -fPIC +CXXFLAGS += +LDFLAGS += + +.PHONY: all test +all: all-subdir $(TARGET) + +test: + $(MAKE) -C test + + +include $(TOPDIR)/mk/$(USE)/rule.*.mk + diff --git a/.bash.d/tmp/util/include/util_blockingqueue.h b/.bash.d/tmp/util/include/util_blockingqueue.h new file mode 100644 index 0000000..0309bff --- /dev/null +++ b/.bash.d/tmp/util/include/util_blockingqueue.h @@ -0,0 +1,24 @@ +#ifndef UTIL_BLOCKINGQUEUE_H +#define UTIL_BLOCKINGQUEUE_H + +#include + + +/** + * ブロッキングキューのインスタンス構造体 + */ +struct blockingqueue; + +struct blockingqueue* blockingqueue_new(int cap); +void blockingqueue_destroy(struct blockingqueue* queue); +bool blockingqueue_push(struct blockingqueue* queue, void* data, size_t size); +bool blockingqueue_push_blocking(struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_pop( struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_pop_blocking( struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_peek(struct blockingqueue* queue, void* data, size_t size); +size_t blockingqueue_size(struct blockingqueue* queue); +void blockingqueue_notify(struct blockingqueue* queue); + + +#endif /* UTIL_BLOCKINGQUEUE_H */ + diff --git a/.bash.d/tmp/util/include/util_queue.h b/.bash.d/tmp/util/include/util_queue.h new file mode 100644 index 0000000..2de9762 --- /dev/null +++ b/.bash.d/tmp/util/include/util_queue.h @@ -0,0 +1,21 @@ +#ifndef UTIL_QUEUE_H +#define UTIL_QUEUE_H + +#include + + +/** キュー */ +struct queue; +struct queue* queue_new(size_t cap, size_t size); +void queue_destroy(struct queue* queue); +bool queue_push(struct queue* queue, void* data, size_t size); +size_t queue_pop( struct queue* queue, void* data, size_t size); +size_t queue_peek(struct queue* queue, void* data, size_t size); +size_t queue_size(struct queue* queue); +void queue_entries(struct queue* queue, bool (*handler)(void* data, size_t size)); +void queue_entries_full(struct queue* queue, + bool (*handler)(void* data, size_t size, int index, bool enabled, void* arg), + void* arg); + +#endif /* UTIL_QUEUE_H */ + diff --git a/.bash.d/tmp/util/include/util_thread.h b/.bash.d/tmp/util/include/util_thread.h new file mode 100644 index 0000000..60e7bb0 --- /dev/null +++ b/.bash.d/tmp/util/include/util_thread.h @@ -0,0 +1,38 @@ +/** + * @file util_thread.c + * スレッドを扱うモジュール + * + * 実装側のみ複数のOS,ライブラリ用に変更できるよう + * 各オブジェクトを typedef ではなく、構造体にしている。 + */ +#ifndef UTIL_THREAD_H +#define UTIL_THREAD_H + +#include + +/* スレッド */ +struct thread; +struct thread* thread_new(void (*start_routine)(void*), void* arg); +void thread_delete(struct thread* thread); +void thread_start(struct thread* thread); +bool thread_join(struct thread* thread); +bool thread_equals(struct thread* thread); + +/* mutex */ +struct mutex; +struct mutex* mutex_new(void); +bool mutex_delete(struct mutex* mutex); +void mutex_lock(struct mutex* mutex); +void mutex_unlock(struct mutex* mutex); + +/* cond */ +struct cond; +struct cond* cond_new(void); +bool cond_delete(struct cond* cond); +void cond_wait(struct cond* cond, struct mutex* mutex); +void cond_signal(struct cond* cond); +void cond_broadcast(struct cond* cond); + + +#endif /* THREAD_H */ + diff --git a/.bash.d/tmp/util/src/logger.c b/.bash.d/tmp/util/src/logger.c new file mode 100644 index 0000000..e96204d --- /dev/null +++ b/.bash.d/tmp/util/src/logger.c @@ -0,0 +1,117 @@ +#include +#include +#include +#include +#include +#include + +#include "logger.h" + + + +// ============================================================================= +// 内部でのみ使用する関数のプロトタイプ宣言 +// ============================================================================= +static void logger_log_output(int priority, const char* message); +static int get_date(char* buff, size_t size); + + + + + +// ============================================================================= +// 関数定義 +// ============================================================================= + +/** + * 指定されたログレベルのログを出力可能か否かを返します。 + * + * @param priority ログレベル + * @return true/false (ログ出力可能/ログ出力不可) + */ +bool logger_is_logable(int priority) +{ + int current_logmask = setlogmask(0); + return (current_logmask & priority); +} + + + +/** + * ログを出力します。 + * + * @param priority syslog と同じログのレベルを指定します。 + * @param file __FILE__ を指定します。 + * @param line __LINE__ を指定します。 + * @param format 書式を指定します。 + */ +void logger_log_(int priority, const char* file, int line, const char* format, ...) +{ + va_list ap; + va_start(ap, format); + bool is_logable = logger_is_logable(priority); + if (is_logable) + { + char log_buffer[512]; + int len = get_date(log_buffer, sizeof(log_buffer)); + len += snprintf(&log_buffer[len], sizeof(log_buffer) - len, " %s:%d: ", file, line); + vsnprintf(&log_buffer[len], sizeof(log_buffer) - len, format, ap); + logger_log_output(priority, log_buffer); + } + va_end(ap); +} + + +/** + * 指定されたメッセージをログに出力します。 + * + * @param priority ログレベル + * @param message 出力するメッセージ + */ +static void logger_log_output(int priority, const char* message) +{ +#ifdef USE_SYSLOG + static bool is_logopend = false; + if (!is_logopend) + { + openlog("ifconv", LOG_CONS | LOG_PID, LOG_USER); + is_logopend = true; + } + syslog(priority, "%s", message); +#else + pid_t current_pid = getpid(); + printf("ifconv[%d][pri=%d]: %s\n", current_pid, priority, message); +#endif +} + + +/** + * 指定されたバッファに、現在の時刻(ナノ秒まで)を文字列形式で格納します。 + * + * @param buff バッファ + * @param size バッファサイズ + * @return バッファに書き込んだ文字数 + */ +static int get_date(char* buff, size_t size) +{ + // ナノ秒まで取得する。 + struct timespec current_time; + clock_gettime(CLOCK_REALTIME, ¤t_time); + + // 秒を日時に変換する。 + struct tm* local_tm = localtime(¤t_time.tv_sec); + + // YYYY/MM/DD HH:MM:SS.XXXXXXXXX に変換する。 + int len = snprintf(buff, size, "%04d/%02d/%02d %02d:%02d:%02d.%09ld", + local_tm->tm_year + 1900, + local_tm->tm_mon + 1, + local_tm->tm_mday, + local_tm->tm_hour, + local_tm->tm_min, + local_tm->tm_sec, + current_time.tv_nsec); + + return len; +} + + diff --git a/.bash.d/tmp/util/src/util_blockingqueue.c b/.bash.d/tmp/util/src/util_blockingqueue.c new file mode 100644 index 0000000..5b66c33 --- /dev/null +++ b/.bash.d/tmp/util/src/util_blockingqueue.c @@ -0,0 +1,200 @@ +#include +#include +#include +#include + +#include "util_queue.h" +#include "util_blockingqueue.h" + +/** + * ブロッキングキューのインスタンス構造体 + */ +struct blockingqueue { + struct queue* queue; /**< キュー */ + struct mutex* data_lock; /**< データロック */ + struct cond* empty_block; /**< pop のブロック用 */ + struct cond* full_block; /**< push のブロック用 */ +}; + + +/** + * ブロッキングキューを生成します。 + * インスタンスを生成できない場合、NULL を返します。 + * + * @param cap キューの容量 + * @param size キューに格納するデータサイズ + * @return イベントキューのインスタンス + */ +struct blockingqueue* blockingqueue_new(size_t cap, size_t size) +{ + struct blockingqueue* blockingqueue = (struct blockingqueue*) malloc(sizeof(struct blockingqueue)); + if (blockingqueue != NULL) + { + blockingqueue->queue = queue_new(cap,4096); + if (blockingqueue->queue != NULL) + { // pthread_mutex_lock ですでに保持している mutex をロックしようとしたとき、 + // デフォルトのスレッドを停止させる動作とするため、NULL (デフォルト設定)を指定する。 + pthread_mutex_init(&blockingqueue->data_lock, NULL); + + // データがない場合 pop をブロックするための cond オブジェクトを初期化する。 + pthread_cond_init(&blockingqueue->pop_block, NULL); + } + else + { + free(blockingqueue); + blockingqueue = NULL; + } + + } + return blockingqueue; +} + + + + +/** + * 指定されたイベントキューを破棄します。 + * + * @param blockingqueue イベントキューのインスタンス + */ +void blockingqueue_destroy(struct blockingqueue* blockingqueue) +{ + if (blockingqueue != NULL) + { + // T.B.D. + queue_destroy(blockingqueue->queue); + blockingqueue->queue = NULL; + } + free(blockingqueue); +} + + +/** + * 指定された mutex をロックします。 + * + * @param mutex mutexオブジェクト + */ +static +void blockingqueue_mutex_lock(pthread_mutex_t* mutex) +{ + int errcode = pthread_mutex_lock(mutex); + if (errcode != 0) + { // エラーが発生する条件は、下記の何れかであり、本プログラムの設定では発生しない。 + // => 発生するのであれば、プログラム誤り。 + // EINVAL : mutex が適切に初期化されていない。 + // EDEADLK : mutex は既に呼び出しスレッドによりロックされている (「エラー検査を行う」mutexes のみ) + printf("###!!! [ERROR] pthred_mutex_lock !!!###\n"); + } +} + + +/** + * 指定された mutex をアンロックします。 + * + * @param mutex mutexオブジェクト + */ +static +void blockingqueue_mutex_unlock(pthread_mutex_t* mutex) +{ + int errcode = pthread_mutex_unlock(mutex); + if (errcode != 0) + { // エラーが発生する条件は、下記の何れかであり、本プログラムの設定では発生しない。 + // => 発生するのであれば、プログラム誤り。 + // EINVAL : mutex が適切に初期化されていない。 + // EDEADLK : mutex は既に呼び出しスレッドによりロックされている (「エラー検査を行う」mutexes のみ) + printf("###!!! [ERROR] pthred_mutex_lock !!!###\n"); + } +} + + +/** + * イベントキューにデータを追加します。 + * + * @param blockingqueue キューのインスタンス + * @param data キューに追加するエントリ(データ) + * @param size キューに追加するエントリ(データ)のサイズ + * @return true/false (追加成功/追加失敗) + */ +bool blockingqueue_push(struct blockingqueue* blockingqueue, void* data, size_t size) +{ + blockingqueue_mutex_lock(&blockingqueue->data_lock); + + // キューにデータがない間ブロックする。 + size_t data_count = queue_size(blockingqueue->queue); + if (data_count == 0) + { + pthread_cond_signal(&blockingqueue->pop_block); + } + + bool result = queue_push(blockingqueue->queue, data, size); + + blockingqueue_mutex_unlock(&blockingqueue->data_lock); + + return result; +} + + +/** + * キューの先頭よりデータを取り出します。 + * キューにデータがない場合、ブロックされます。 + * + * @param blockingqueue キューのインスタンス + * @param buf 取り出したデータ格納用バッファ + * @param buflen バッファのサイズ + * @return 取り出したデータのサイズ + */ +size_t blockingqueue_pop( struct blockingqueue* blockingqueue, void* buf, size_t buflen) +{ + blockingqueue_mutex_lock(&blockingqueue->data_lock); + + // キューにデータがない間ブロックする。 + size_t data_count = queue_size(blockingqueue->queue); + while (data_count == 0) + { + pthread_cond_wait(&blockingqueue->pop_block, &blockingqueue->data_lock); + data_count = queue_size(blockingqueue->queue); + } + + size_t data_size = queue_pop(blockingqueue->queue, buf, buflen); + blockingqueue_mutex_unlock(&blockingqueue->data_lock); + return data_size; +} + + +/** + * キューの先頭よりデータを取り出します。 + * データの取り出しに失敗した場合、0を返します。 + * blockingqueue_pop と異なり、キューからデータは削除されません。 + * + * @param blockingqueue キューのインスタンス + * @param buf 取り出したデータ格納用バッファ + * @param buflen バッファのサイズ + * @return 取り出したデータのサイズ + */ +size_t blockingqueue_peek( struct blockingqueue* blockingqueue, void* buf, size_t buflen) +{ + blockingqueue_mutex_lock(&blockingqueue->data_lock); + size_t size = queue_peek(blockingqueue->queue, buf, buflen); + blockingqueue_mutex_unlock(&blockingqueue->data_lock); + return size; +} + + + +/** + * キューのサイズ(キューに入っているデータの個数)を取得します。 + * + * @param blockingqueue キューのインスタンス + * @return キューのサイズ + */ +size_t blockingqueue_size(struct blockingqueue* blockingqueue) +{ + size_t size; + blockingqueue_mutex_lock(&blockingqueue->data_lock); + size = queue_size(blockingqueue->queue); + blockingqueue_mutex_unlock(&blockingqueue->data_lock); + return size; +} + + +#endif diff --git a/.bash.d/tmp/util/src/util_queue.c b/.bash.d/tmp/util/src/util_queue.c new file mode 100644 index 0000000..fb2e153 --- /dev/null +++ b/.bash.d/tmp/util/src/util_queue.c @@ -0,0 +1,341 @@ +#include +#include +#include + +#include "util_thread.h" +#include "util_queue.h" + + + +/* ============================================================================= + * 構造体定義 + * ============================================================================= + */ + +/** + * キューのエントリ構造体 + */ +struct queue_entry { + size_t size; /**< 実データサイズ */ + char* data; /**< データバッファ */ +}; + + +/** + * キューのインスタンス構造体 + */ +struct queue { + size_t capacity; /**< キューの容量 */ + size_t size; /**< キューのサイズ */ + size_t max_data_size; /**< データの最大サイズ */ + struct queue_entry* entries; /**< データのエントリ */ + struct queue_entry* head; /**< キューの取出位置 */ + struct queue_entry* tail; /**< キューの挿入位置 */ +}; + + + +/* ============================================================================= + * 内部関数プロトタイプ宣言 + * ============================================================================= + */ +static struct queue_entry* queue_next_entry(struct queue* queue, struct queue_entry* entry); +static bool queue_is_enabled(struct queue* queue, struct queue_entry* entry); + + + +/* ============================================================================= + * 公開関数 + * ============================================================================= + */ + +/** + * キューを生成します。 + * インスタンスを生成できない場合、NULL を返します。 + * + * [注意] + * このキューは、同期化されません。 + * 同期化が必要な場合は、blocking_queue を使用してください。 + * + * @param cap キューの容量(格納可能なデータの個数) + * @param size キューに格納する1つあたりのデータサイズ + * @return キューのインスタンス + */ +/* [実装メモ] + * キューのデータ管理構造 + * メモリ確保を 1 回の malloc でできるようにしている。 + * +-------------------+ + * | queue 構造体 | + * +-------------------+ + * | entry[0] | + * +-------------------+ + * : + * +-------------------+ + * | entry[cap-1] | + +-------------------+ + * | entry[0].data | + * | 用バッファ | + * +-------------------+ + * : + * +-------------------+ + * | entry[cap-1].data | + * | 用バッファ | + * +-------------------+ + */ +struct queue* queue_new(size_t cap, size_t size) +{ + size_t queue_size = sizeof(struct queue) + + ((sizeof(struct queue_entry) + size) * cap); + struct queue* queue = (struct queue*) malloc(queue_size); + if (queue != NULL) + { + queue->capacity = cap; + queue->size = 0; + queue->max_data_size = size; + queue->entries = (struct queue_entry*) (queue + 1); + queue->head = queue->entries; + queue->tail = queue->entries; + + + char* tmp_data_ptr = (char*) (queue->entries + queue->capacity); + struct queue_entry* tmp_entry = queue->entries; + for (size_t i = 0; i < queue->capacity; i++) + { + tmp_entry->data = tmp_data_ptr; + tmp_entry++; + tmp_data_ptr += queue->max_data_size; + } + } + return queue; +} + + + +/** + * 指定されたキューを破棄します。 + * + * @param queue キューのインスタンス + */ +void queue_destroy(struct queue* queue) +{ + free(queue); +} + + +/** + * キューにデータを追加します。 + * + * @param queue キューのインスタンス + * @param data キューに追加するエントリ(データ) + * @param size キューに追加するエントリ(データ)のサイズ + * @return true/false (追加成功/追加失敗) + */ +bool queue_push(struct queue* queue, void* data, size_t size) +{ + bool result = false; + + if (size < queue->max_data_size) + { + if (queue->size < queue->capacity) + { + memset(queue->tail->data, 0x00, sizeof(struct queue_entry)); + memcpy(queue->tail->data, data, size); + queue->tail->size = size; + queue->tail++; + if (queue->tail >= (queue->entries + queue->capacity)) + { + queue->tail = queue->entries; + } + queue->size++; + result = true; + } + } + return result; +} + + +/** + * キューの先頭よりデータを取り出します。 + * データの取り出しに失敗した場合、0を返します。 + * + * @param queue キューのインスタンス + * @param buf 取り出したデータ格納用バッファ + * @param buflen バッファのサイズ + * @return 取り出したデータのサイズ + */ +size_t queue_pop( struct queue* queue, void* buf, size_t buflen) +{ + size_t size = 0; + if (queue->size > 0) + { + if (queue->head->size < buflen) + { + size = queue->head->size; + memset(buf, 0x00, buflen); + memcpy(buf, queue->head->data, size); + + // データクリア (なくても動作上問題ない) + memset(queue->head->data, 0x00, queue->max_data_size); + queue->head->size = 0; + + queue->head++; + if (queue->head >= (queue->entries + queue->capacity)) + { + queue->head = queue->entries; + } + queue->size--; + } + } + return size; +} + + +/** + * キューの先頭よりデータを取り出します。 + * データの取り出しに失敗した場合、0を返します。 + * queue_pop と異なり、キューからデータは削除されません。 + * + * @param queue キューのインスタンス + * @param buf 取り出したデータ格納用バッファ + * @param buflen バッファのサイズ + * @return 取り出したデータのサイズ + */ +size_t queue_peek( struct queue* queue, void* buf, size_t buflen) +{ + size_t size = 0; + if (queue->size > 0) + { + if (queue->head->size < buflen) + { + size = queue->head->size; + memset(buf, 0x00, buflen); + memcpy(buf, queue->head->data, size); + } + } + return size; +} + + +/** + * キューのサイズ(キューに入っているデータの個数)を取得します。 + * + * @param queue キューのインスタンス + * @return キューのサイズ + */ +size_t queue_size(struct queue* queue) +{ + return queue->size; +} + + +/** + * キューに格納されている全エントリーを引数に、指定された handler を呼び出します。 + * + * handler の引数: + * - data : データ + * - size : データのサイズ + * + * @param queue キューのインスタンス + * @param handler ハンドラ + */ +void queue_entries(struct queue* queue, bool (*handler)(void* data, size_t size)) +{ + bool is_continue = true; + size_t counter = queue->size; + struct queue_entry* entry = queue->head; + while (is_continue && (counter > 0)) + { + is_continue = handler(entry->data, entry->size); + entry = queue_next_entry(queue, entry); + counter--; + } +} + + +/** + * キューインスタンスの全エントリを引数に、指定された handler を呼び出します。 + * この関数は、デバッグ用の関数です。 + * handler には、実際にデータが入っていないキューのエントリも渡されます。 + * + * handler の引数: + * - data : データ + * - size : データのサイズ + * - index : キューの管理上のインデックス + * - enabled : true/false (有効なデータ/無効なデータ) + * - arg : ユーザーデータ + * + * @param queue キューのインスタンス + * @param handler ハンドラ + * @param arg ハンドラに渡すユーザーデータ + */ +void queue_entries_full(struct queue* queue, + bool (*handler)(void* data, size_t size, int index, bool enabled, void* arg), + void* arg) +{ + bool is_enabled; + bool is_continue = true; + struct queue_entry* entry = queue->entries; + for (int i = 0; is_continue && (i < (int) queue->capacity); i++) + { + is_enabled = queue_is_enabled(queue, entry); + is_continue = handler(entry->data, entry->size, i, is_enabled, arg); + entry++; + } +} + + + +/* ============================================================================= + * 内部関数 + * ============================================================================= + */ + + +/** + * 指定されたエントリの次のエントリーを取得します。 + * エントリー配列の末尾に到達した場合、先頭のエントリを返します。 + * + * @param queue キューのインスタンス + * @param entry エントリ + * @return 次のエントリ + */ +static +struct queue_entry* queue_next_entry(struct queue* queue, struct queue_entry* entry) +{ + struct queue_entry* next_entry = entry; + next_entry++; + if (next_entry >= (queue->entries + queue->capacity)) + { + next_entry = queue->entries; + } + return next_entry; +} + + +/** + * 指定されたエントリが、有効か否かを返します。 + * + * @param queue キューのインスタンス + * @param entry エントリ + * @return true/false (有効/無効) + */ +static +bool queue_is_enabled(struct queue* queue, struct queue_entry* entry) +{ + bool is_enabled; + if ((queue->size != queue->capacity) && (queue->head <= queue->tail)) + { /* キューのエントリ配列が以下のような状態の場合 + * [- |- |HT-|- |- |- |- |- ] + * [- |- |Ho |o |o |T-|- |- ] (-:空/o:データ有/H:Head/T:Tail) + */ + is_enabled = ((queue->head <= entry) && (entry < queue->tail)); + } + else + { /* キューのエントリ配列が以下のような状態の場合 + * [o |o |HTo|o |o |o |o |o ] + * [o |o |T- |- |- |Ho|o |o ] (-:空/o:データ有/H:Head/T:Tail) + */ + is_enabled = ((entry < queue->tail) || (queue->head <= entry)); + } + return is_enabled; +} diff --git a/.bash.d/tmp/util/src/util_thread.c b/.bash.d/tmp/util/src/util_thread.c new file mode 100644 index 0000000..20c6fc6 --- /dev/null +++ b/.bash.d/tmp/util/src/util_thread.c @@ -0,0 +1,341 @@ +/** + * @file util_thread.c + * スレッドを扱うモジュール。 + */ +#include +#include +#include +#include + +#include "util_thread.h" + + + +/* ///////////////////////////////////////////////////////////////////////////// +// +// スレッド +// +*/ + + +/* ============================================================================ + * 構造体定義 + * ============================================================================ + */ + +/** + * スレッドインスタンス構造体。 + */ +struct thread { + void (*start_routine)(void*); /**< スレッドとして実行する関数 */ + void* arg; /**< スレッドに渡すデータ */ + pthread_t tid; /**< スレッドのID */ +}; + + + +/* ============================================================================ + * プロトタイプ宣言 (内部でのみ利用する関数) + * ============================================================================ + */ +static void* thread_run(void* args); + + + +/* ============================================================================ + * 公開関数 + * ============================================================================ + */ + +/** + * スレッドを生成します。 + * スレッドの生成に失敗した場合、NULL を返します。 + * + * @param start_routine スレッドで実行する関数 + * @param arg スレッドに渡すデータへのポインタ + * @return スレッドインスタンス + */ +struct thread* thread_new(void (*start_routine)(void*), void* arg) +{ + struct thread* thread = (struct thread*) malloc(sizeof(struct thread)); + if (thread != NULL) + { + thread->start_routine = start_routine; + thread->arg = arg; + } + return thread; +} + + +/** + * スレッドインスタンスのリソースを開放します。 + * スレッド自体が終了するわけではありません。 + * + * @param thread スレッドインスタンス + */ +void thread_delete(struct thread* thread) +{ + free(thread); +} + + +/** + * スレッドを開始します。 + * + * @param thread スレッドインスタンス + */ +void thread_start(struct thread* thread) +{ + pthread_create(&thread->tid, NULL, thread_run, thread); +} + + +/** + * 指定されたスレッドが終了するのを待ちます。 + * + * @param thread スレッドインスタンス + */ +bool thread_join(struct thread* thread) +{ + int ret = pthread_join(thread->tid, NULL); + return (ret == 0); +} + + +/** + * 現在のスレッドが指定されたスレッドと同一か否かを返します。 + * + * @param thread スレッドインスタンス + * @return true/false (一致/不一致) + */ +bool thread_equals(struct thread* thread) +{ + pthread_t tid = pthread_self(); + return (tid == thread->tid); +} + + + +/* ============================================================================ + * 内部関数 + * ============================================================================ + */ + +/** + * スレッドとして実行される関数。 + * この関数の中で、スレッド起動に指定された関数を実行します。 + * + * @param arg スレッドインスタンス + */ +static +void* thread_run(void* arg) +{ + struct thread* thread = (struct thread*) arg; + thread->start_routine(thread->arg); + return NULL; +} + + + +/* ///////////////////////////////////////////////////////////////////////////// +// +// mutex +// +*/ + + +/* ============================================================================ + * 構造体定義 + * ============================================================================ + */ + +/** + * mutex インスタンス構造体。 + */ +struct mutex { + pthread_mutex_t mutex; /**< mutex オブジェクト */ +}; + + + +/* ============================================================================ + * 公開関数 + * ============================================================================ + */ + +/** + * mutex を生成します。 + * mutex の生成に失敗した場合、NULL を返します。 + * + * @return mutex + */ + +struct mutex* mutex_new(void) +{ + struct mutex* mutex = (struct mutex*) malloc(sizeof(struct mutex)); + if (mutex != NULL) + { + pthread_mutex_init(&mutex->mutex, NULL); + } + return mutex; +} + + +/** + * mutex を破棄します。 + * mutex がロックされている場合は、破棄に失敗しエラーを返します。 + * + * @param mutex 破棄する mutex + * @return true/false (破棄成功/破棄失敗 [mutex がロックされている]) + */ +bool mutex_delete(struct mutex* mutex) +{ + if (mutex != NULL) + { + int res = pthread_mutex_destroy(&mutex->mutex); + if (res == 0) + { + free(mutex); + return true; + } + } + return false; +} + + +/** + * mutex をロックします。 + * + * @param mutex ロックする mutex + */ +void mutex_lock(struct mutex* mutex) +{ + // mutex は、デフォルトの種別で初期化済みのため、 + // EINVAL, EDEADLK のエラーは発生しない。 + pthread_mutex_lock(&mutex->mutex); +} + + +/** + * mutex をアンロックします。 + * + * @param mutex アンロックする mutex + */ +void mutex_unlock(struct mutex* mutex) +{ + // mutex は、デフォルトの種別で初期化済みのため、 + // EINVAL, EPERM のエラーは発生しない。 + pthread_mutex_unlock(&mutex->mutex); +} + + + +/* ///////////////////////////////////////////////////////////////////////////// +// +// cond +// +*/ + + +/* ============================================================================ + * 構造体定義 + * ============================================================================ + */ + +/** + * cond インスタンス構造体。 + */ +struct cond { + pthread_cond_t cond; /**< cond オブジェクト */ +}; + + + +/* ============================================================================ + * 公開関数 + * ============================================================================ + */ + +/** + * cond を生成します。 + * cond の生成に失敗した場合、NULL を返します。 + * + * @return cond + */ + +struct cond* cond_new(void) +{ + struct cond* cond = (struct cond*) malloc(sizeof(struct cond)); + if (cond != NULL) + { + pthread_cond_init(&cond->cond, NULL); + } + return cond; +} + + +/** + * cond を破棄します。 + * cond が条件変数を待っている場合、破棄に失敗しエラーを返します。 + * + * @param cond 破棄する cond + * @return true/false (破棄成功/破棄失敗 [cond が条件変数を待っている]) + */ +bool cond_delete(struct cond* cond) +{ + if (cond!= NULL) + { + int res = pthread_cond_destroy(&cond->cond); + if (res == 0) + { + free(cond); + return true; + } + } + return false; +} + + +/** + * 指定された mutex のアンロックと、条件変数 cond の送信に対する待機を + * アトミックに行います。条件変数が送信されるまで、スレッドの実行は停止され、 + * CPU時間を消費しません。 + * + * 本関数を実行する前に、 mutex はロックされている必要があります。 + * 本関数を呼び出しスレッドが、条件変数の待機完了により動作する際、 + * mutex は再びロックされます。 + * + * @param cond cond インスタンス + * @param mutex mutex ロック済みの mutex + */ +void cond_wait(struct cond* cond, struct mutex* mutex) +{ + pthread_cond_wait(&cond->cond, &mutex->mutex); +} + + +/** + * 条件変数 cond に備えて待機しているスレッドの一つの実行を再開させます。 + * cond を待機しているスレッドがなければ何もしません。 + * 複数のスレッドが cond を待機している場合、どのスレッドが再開されるかはわからない。 + * + * @param cond 再開させる cond + */ +void cond_signal(struct cond* cond) +{ + pthread_cond_signal(&cond->cond); +} + + +/** + * 条件変数 cond に備えて待機しているすべてのスレッドを再開させます。 + * cond を待機しているスレッドがなければ何もしません。 + * + * @param cond 再開させる cond + */ +void cond_broadcast(struct cond* cond) +{ + pthread_cond_broadcast(&cond->cond); +} + + diff --git a/.bash.d/tmp/util/test/Makefile b/.bash.d/tmp/util/test/Makefile new file mode 100644 index 0000000..395b46a --- /dev/null +++ b/.bash.d/tmp/util/test/Makefile @@ -0,0 +1,29 @@ +# ============================================================================== +# Makefile +# ============================================================================== +TOPDIR ?= ../../.. +TARGET = ut_libutil.exe +SUBDIRS = +VERSION = +TARGET_TYPE = target-exe +#TARGET_TYPE = target-a +#TARGET_TYPE = target-so +#TARGET_TYPE = target-dll + +EXCLUDES = ../src/ut_queue.c + +include $(TOPDIR)/mk/conf.mk +include $(TOPDIR)/mk/$(USE)/com.*.mk +include $(TOPDIR)/mk/$(USE)/conf.ut.*.mk + +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -lcunit -lpthread + +.PHONY: all +all: all-subdir $(TARGET) + + +include $(TOPDIR)/mk/$(USE)/rule.*.mk + diff --git a/.bash.d/tmp/util/test/src/test_main.c_ b/.bash.d/tmp/util/test/src/test_main.c_ new file mode 100644 index 0000000..ed7cf1d --- /dev/null +++ b/.bash.d/tmp/util/test/src/test_main.c_ @@ -0,0 +1,26 @@ +#include +#include +#include + +#include "test_thread.h" + + +int main(void) +{ + test_thread(); + return 0; +} +int t(void) +{ + CU_pSuite testSuite; + + CU_initialize_registry(); + testSuite = CU_add_suite("libutil.so",NULL,NULL); + + CU_add_test(testSuite, "thread", test_thread); + + CU_console_run_tests(); + CU_cleanup_registry(); + + return 0; +} diff --git a/.bash.d/tmp/util/test/src/test_thread.c b/.bash.d/tmp/util/test/src/test_thread.c new file mode 100644 index 0000000..682dcd4 --- /dev/null +++ b/.bash.d/tmp/util/test/src/test_thread.c @@ -0,0 +1,42 @@ +#include +#include + +#include "util_thread.h" +#include "test_thread.h" + + + +static void* thread_1_arg = NULL; +void thread_1(void* arg) +{ + thread_1_arg = arg; + for (int i = 0; i < 5; i++) + { + sleep(1); + printf("[%s] %d \n", (const char*)thread_1_arg, i); + } +} +static void* thread_2_arg = NULL; +void thread_2(void* arg) +{ + thread_2_arg = arg; + for (int i = 0; i < 5; i++) + { + printf("[%s] %d\n", (const char*)thread_2_arg, i); + sleep(1); + } +} + +void test_thread(void) +{ + struct thread* thread1 = thread_new(thread_1, "thread_1"); + struct thread* thread2 = thread_new(thread_2, "thread_2"); + + thread_start(thread1); + thread_start(thread2); + + thread_join(thread1); + thread_join(thread2); + +} + diff --git a/.bash.d/tmp/util/test/src/test_thread.h b/.bash.d/tmp/util/test/src/test_thread.h new file mode 100644 index 0000000..82e91db --- /dev/null +++ b/.bash.d/tmp/util/test/src/test_thread.h @@ -0,0 +1,7 @@ +#ifndef TEST_THREAD_H +#define TEST_THREAD_H + +void test_thread(void); + +#endif + diff --git a/.bash.d/tmp/util/test/src/ut_queue.c b/.bash.d/tmp/util/test/src/ut_queue.c new file mode 100644 index 0000000..ad268b5 --- /dev/null +++ b/.bash.d/tmp/util/test/src/ut_queue.c @@ -0,0 +1,106 @@ +#include +#include "util_queue.h" + +extern void queue_print_entries(struct queue* queue); + + +bool handler(void* data, size_t size, int index, bool enabled, void* arg) +{ + printf("[%02d][enabled=%d][size=%03ld] %s \n", index, enabled, size, (char*)data); + (void) arg; + return true; +} + +bool simple_handler(void* data, size_t size) +{ + printf("##DATA:[size=%03ld] %s\n", size, (char*) data); + return true; +} + + +int main(void) +{ + char tmp[256]; + size_t size; + bool is_success; + struct queue* queue = queue_new(4,256); + is_success = queue_push(queue, "ABC", 4); + printf("push ABC : %d\n", is_success); + + queue_entries_full(queue, handler, NULL); + queue_entries(queue, simple_handler); + + is_success = queue_push(queue, "DEF", 4); + printf("push DEF : %d\n", is_success); + + queue_entries_full(queue, handler, NULL); + queue_entries(queue, simple_handler); + + is_success = queue_push(queue, "XYZ111", 7); + printf("push XYZ111: %d\n", is_success); + + queue_entries_full(queue, handler, NULL); + queue_entries(queue, simple_handler); + + size = queue_peek(queue, tmp, sizeof(tmp)); + printf("peek : size=%ld [%s]\n", size, tmp); + + queue_entries_full(queue, handler, NULL); + queue_entries(queue, simple_handler); + + size = queue_peek(queue, tmp, sizeof(tmp)); + printf("peek : size=%ld [%s]\n", size, tmp); + + queue_entries_full(queue, handler, NULL); + queue_entries(queue, simple_handler); + + size = queue_pop(queue, tmp, sizeof(tmp)); + printf("pop : size=%ld [%s]\n", size, tmp); + + queue_entries_full(queue, handler, NULL); + queue_entries(queue, simple_handler); + + size = queue_pop(queue, tmp, sizeof(tmp)); + printf("pop : size=%ld [%s]\n", size, tmp); + queue_entries_full(queue, handler, NULL); + queue_entries(queue, simple_handler); + + size = queue_pop(queue, tmp, sizeof(tmp)); + printf("pop : size=%ld [%s]\n", size, tmp); + queue_entries_full(queue, handler, NULL); + queue_entries(queue, simple_handler); + + size = queue_pop(queue, tmp, sizeof(tmp)); + printf("pop : size=%ld [%s]\n", size, tmp); + queue_entries_full(queue, handler, NULL); + queue_entries(queue, simple_handler); + + is_success = queue_push(queue, "ABC", 4); + printf("push ABC : %d\n", is_success); + queue_entries_full(queue, handler, NULL); + queue_entries(queue, simple_handler); + + is_success = queue_push(queue, "DEF", 4); + printf("push DEF : %d\n", is_success); + queue_entries_full(queue, handler, NULL); + queue_entries(queue, simple_handler); + + is_success = queue_push(queue, "GHIJKLMN", 9); + printf("push GHIJKLMN: %d\n", is_success); + queue_entries_full(queue, handler, NULL); + queue_entries(queue, simple_handler); + + is_success = queue_push(queue, "O", 2); + printf("push O: %d\n", is_success); + queue_entries_full(queue, handler, NULL); + queue_entries(queue, simple_handler); + + is_success = queue_push(queue, "P", 2); + printf("push P: %d\n", is_success); + queue_entries_full(queue, handler, NULL); + queue_entries(queue, simple_handler); + + + return 0; +} +