From 763269674d4e321c2f2fd4ede6180d18a172126e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Wed, 13 Nov 2024 00:05:31 -0300 Subject: [PATCH 01/63] ci: removing the trigger of 'pull_request' workflow for push events on main branch --- .github/workflows/pull_request.yaml | 3 --- README.md | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/pull_request.yaml b/.github/workflows/pull_request.yaml index 066674b..2ff4ac5 100644 --- a/.github/workflows/pull_request.yaml +++ b/.github/workflows/pull_request.yaml @@ -5,9 +5,6 @@ on: push: branches: - develop - push: - branches: - - main env: COVERAGE_REPORT_DIR: build/coverage diff --git a/README.md b/README.md index 4194e77..f9ee320 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,6 @@ Optional build flags: | BUILD_SAMPLES | Build the library samples | | ENABLE_LOGGING | Enable library logs | -#### Library support +#### Support - linux From 9973cab299722354714611aedbd68d3cfeb1b79f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Wed, 13 Nov 2024 00:29:26 -0300 Subject: [PATCH 02/63] ci: renaming the pull_request workflow --- .github/workflows/{pull_request.yaml => ci.yaml} | 2 +- README.md | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) rename .github/workflows/{pull_request.yaml => ci.yaml} (99%) diff --git a/.github/workflows/pull_request.yaml b/.github/workflows/ci.yaml similarity index 99% rename from .github/workflows/pull_request.yaml rename to .github/workflows/ci.yaml index 2ff4ac5..f2d83ad 100644 --- a/.github/workflows/pull_request.yaml +++ b/.github/workflows/ci.yaml @@ -1,4 +1,4 @@ -name: Pull request +name: CI on: pull_request: diff --git a/README.md b/README.md index f9ee320..1dbaf52 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,11 @@ ## pool-day +[![CI](https://github.com/andrelcmoreira/pool-day/actions/workflows/pull_request.yaml/badge.svg)](https://github.com/andrelcmoreira/pool-day/actions/workflows/pull_request.yaml) +[![License: LGPL v3](https://img.shields.io/badge/License-LGPL_v3-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0) + #### Overview -`pool-day` is a thread pool library for C/C++ applications. +`pool-day` is a simple thread pool library for C/C++ applications. #### Build @@ -16,7 +19,7 @@ Optional build flags: | Flag | Description | |---------------------|----------------------------------------| | BUILD_SAMPLES | Build the library samples | -| ENABLE_LOGGING | Enable library logs | +| ENABLE_LOGGING | Enable the library logging feature | #### Support From f0a3250ea2125ab8b31fdae19d2d4dfd3c379d9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Wed, 13 Nov 2024 23:19:08 -0300 Subject: [PATCH 03/63] fixing CI badge on README file --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1dbaf52..5a94c72 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ## pool-day -[![CI](https://github.com/andrelcmoreira/pool-day/actions/workflows/pull_request.yaml/badge.svg)](https://github.com/andrelcmoreira/pool-day/actions/workflows/pull_request.yaml) +[![CI](https://github.com/andrelcmoreira/pool-day/actions/workflows/ci.yaml/badge.svg)](https://github.com/andrelcmoreira/pool-day/actions/workflows/ci.yaml) [![License: LGPL v3](https://img.shields.io/badge/License-LGPL_v3-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0) #### Overview From 7c3bae65a911a2acf72b5c1686425f6eb9a3e68b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Wed, 13 Nov 2024 23:51:42 -0300 Subject: [PATCH 04/63] improving the README file --- README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5a94c72..2aefe18 100644 --- a/README.md +++ b/README.md @@ -5,16 +5,20 @@ #### Overview -`pool-day` is a simple thread pool library for C/C++ applications. +`pool-day` is a simple thread pool library for C/C++ applications and yes, the +name was chosen as a homage to my favorite counter-strike's map: `fy_pool_day`. -#### Build +#### Building + +The library relies on `cmake` tool to be built: ```cmake $ cmake -S . -B build $ cmake --build build ``` -Optional build flags: +Additional flags can be supplied as parameter to cmake according to the table +below: | Flag | Description | |---------------------|----------------------------------------| From af111109bf5115079854732ad86970dcbce6224e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Wed, 20 Nov 2024 01:20:33 -0300 Subject: [PATCH 05/63] using the README.md file as main page for the auto-generated library documentation --- doc/Doxyfile.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index a4140cc..7b945f1 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -121,7 +121,8 @@ WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- -INPUT = @CMAKE_CURRENT_SOURCE_DIR@/include @CMAKE_CURRENT_SOURCE_DIR@/src +INPUT = @CMAKE_CURRENT_SOURCE_DIR@/include @CMAKE_CURRENT_SOURCE_DIR@/src @CMAKE_CURRENT_SOURCE_DIR@/README.md +USE_MDFILE_AS_MAINPAGE = README.md INPUT_ENCODING = UTF-8 INPUT_FILE_ENCODING = FILE_PATTERNS = *.c \ From c02946e63448e64660860aad55a48ffa0638f698 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Thu, 21 Nov 2024 23:58:43 -0300 Subject: [PATCH 06/63] ci: adding the clang build on 'build' step --- .github/workflows/ci.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index f2d83ad..2d65ae9 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -15,6 +15,9 @@ jobs: build: name: Build library runs-on: ubuntu-latest + strategy: + matrix: + cc: [gcc, clang] container: image: andrelcmoreira/pool-day:v2 volumes: @@ -26,7 +29,7 @@ jobs: - uses: actions/checkout@v2 - name: Build library run: | - cmake -DCMAKE_BUILD_TYPE=Debug -S . -B build + cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=${{ matrix.cc }} -S . -B build cmake --build build build_samples: From 5a423aeb45a2c3d4dcdff708f74a4aedb8413361 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Sat, 5 Apr 2025 14:36:27 -0300 Subject: [PATCH 07/63] adding the generation of compile_commands file --- .clangd | 2 ++ CMakeLists.txt | 1 + 2 files changed, 3 insertions(+) create mode 100644 .clangd diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..5ff6833 --- /dev/null +++ b/.clangd @@ -0,0 +1,2 @@ +CompileFlags: + CompilationDatabase: "build" diff --git a/CMakeLists.txt b/CMakeLists.txt index 902ecd7..e696ca9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,7 @@ option(ENABLE_LOGGING "enable library logs" OFF) set(PROJECT_NAME "pool-day") set(SOURCES src/task.c src/queue.c src/pool_day.c) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror -Wextra -Wpedantic") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS} -g3") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS} -O3 -s") From 6df4050fb75b926dc4d596f5c46180394a025b87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?andr=C3=A9?= Date: Fri, 11 Jul 2025 00:14:39 -0300 Subject: [PATCH 08/63] updating README file --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2aefe18..7b2fd11 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,8 @@ #### Overview -`pool-day` is a simple thread pool library for C/C++ applications and yes, the -name was chosen as a homage to my favorite counter-strike's map: `fy_pool_day`. +`pool-day` is a simple thread pool library for C/C++ applications built around POSIX threads. And yes, the +name of the library was chosen as a homage to my favorite counter-strike's map: `fy_pool_day`. #### Building From 2557d310787bee869a1d35269b05d8e8acedc609 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?andr=C3=A9?= Date: Fri, 11 Jul 2025 00:26:51 -0300 Subject: [PATCH 09/63] updating README file --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7b2fd11..faaa6d9 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,8 @@ below: | Flag | Description | |---------------------|----------------------------------------| -| BUILD_SAMPLES | Build the library samples | -| ENABLE_LOGGING | Enable the library logging feature | +| BUILD_SAMPLES | Build the library's samples | +| ENABLE_LOGGING | Enable the library's logging feature | #### Support From fff51c7b7156164aa740b343df1019da1705c358 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Sun, 16 Nov 2025 13:32:45 -0300 Subject: [PATCH 10/63] ci: adding cppcheck job --- .github/workflows/ci.yaml | 16 ++++++++++++++++ README.md | 12 ++++++------ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2d65ae9..7afacf0 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -71,6 +71,22 @@ jobs: - name: Run unit tests run: ${{ env.LD_PRELOAD }} build/test/pool-day-tests + static_analysis: + name: Run static analysis + runs-on: ubuntu-latest + container: + image: andrelcmoreira/pool-day:v2 + volumes: + - ${{ github.workspace }}:/pool-day + credentials: + username: ${{ secrets.DOCKERHUB_USER }} + password: ${{ secrets.DOCKERHUB_PASS }} + steps: + - uses: actions/checkout@v2 + + - name: Run cppcheck + run: cppcheck --enable=all -Iinclude --error-exitcode=1 --suppress=missingIncludeSystem src/* + dynamic_analysis: name: Run dynamic analysis runs-on: ubuntu-latest diff --git a/README.md b/README.md index faaa6d9..98f0d0d 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,7 @@ #### Overview -`pool-day` is a simple thread pool library for C/C++ applications built around POSIX threads. And yes, the -name of the library was chosen as a homage to my favorite counter-strike's map: `fy_pool_day`. +`pool-day` is a simple thread pool library for C/C++ applications built around POSIX threads. #### Building @@ -15,15 +14,16 @@ The library relies on `cmake` tool to be built: ```cmake $ cmake -S . -B build $ cmake --build build +$ sudo cmake --install build ``` Additional flags can be supplied as parameter to cmake according to the table below: -| Flag | Description | -|---------------------|----------------------------------------| -| BUILD_SAMPLES | Build the library's samples | -| ENABLE_LOGGING | Enable the library's logging feature | +| Flag | Description | +|----------------|--------------------------------------| +| BUILD_SAMPLES | Build the library's samples | +| ENABLE_LOGGING | Enable the library's logging feature | #### Support From 77033577028742caa7135785f5076a2c32cedcf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Sun, 16 Nov 2025 13:35:49 -0300 Subject: [PATCH 11/63] ci: upgrading version of upload-artifact action --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 7afacf0..566daa1 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -155,7 +155,7 @@ jobs: - name: Upload coverage report if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: coverage-report path: ${{ env.COVERAGE_REPORT_DIR }} From d5a79be68eb4be45e412a4f2e84d95eb98ceae34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Sun, 16 Nov 2025 20:52:25 -0300 Subject: [PATCH 12/63] ci: fixing cppcheck issues --- .github/workflows/ci.yaml | 2 +- src/logger.c | 6 +++--- src/pool_day.c | 10 +++++++++- src/task.c | 3 ++- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 566daa1..ea97eac 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -85,7 +85,7 @@ jobs: - uses: actions/checkout@v2 - name: Run cppcheck - run: cppcheck --enable=all -Iinclude --error-exitcode=1 --suppress=missingIncludeSystem src/* + run: cppcheck --enable=all --inline-suppr -Iinclude --error-exitcode=1 --suppress=missingIncludeSystem src/* dynamic_analysis: name: Run dynamic analysis diff --git a/src/logger.c b/src/logger.c index b7a9213..92ef974 100644 --- a/src/logger.c +++ b/src/logger.c @@ -18,10 +18,10 @@ static pthread_mutex_t log_mutex; //!< Mutex for log operations. void __log_msg(pool_day_log_severity_t sev, const char *file_name, const char *func_name, int line_no, const char *fmt, ...) { - char log_buffer[LOG_BUFFER_SIZE] = {0}; - va_list args; - THREAD_SAFE_ZONE(&log_mutex, { + char log_buffer[LOG_BUFFER_SIZE] = {0}; + va_list args; + va_start(args, fmt); vsnprintf(log_buffer, LOG_BUFFER_SIZE, fmt, args); diff --git a/src/pool_day.c b/src/pool_day.c index 03e21e6..41b8c64 100644 --- a/src/pool_day.c +++ b/src/pool_day.c @@ -51,6 +51,7 @@ __static void *thread_func(void *param) { return NULL; } +// cppcheck-suppress unusedFunction pool_day_retcode_t enqueue_task(pool_day_t pool, task_t *task) { if (!pool || !task) { POOL_DAY_ERROR("null parameter"); @@ -65,6 +66,7 @@ pool_day_retcode_t enqueue_task(pool_day_t pool, task_t *task) { return POOL_DAY_SUCCESS; } +// cppcheck-suppress unusedFunction pool_day_t create_pool(uint32_t pool_size) { pool_day_t pool; @@ -104,6 +106,7 @@ pool_day_t create_pool(uint32_t pool_size) { return pool; } +// cppcheck-suppress unusedFunction pool_day_retcode_t destroy_pool(pool_day_t *pool) { if (!pool || !(*pool)) { POOL_DAY_ERROR("null pool handle"); @@ -138,6 +141,7 @@ pool_day_retcode_t destroy_pool(pool_day_t *pool) { return POOL_DAY_SUCCESS; } +// cppcheck-suppress unusedFunction pool_day_retcode_t abort_tasks(pool_day_t pool) { if (!pool) { POOL_DAY_ERROR("null pool handle"); @@ -151,11 +155,13 @@ pool_day_retcode_t abort_tasks(pool_day_t pool) { return POOL_DAY_SUCCESS; } +// cppcheck-suppress unusedFunction uint32_t queued_tasks(pool_day_t pool) { return pool ? queue_size(pool->tasks) : 0; } -void *wait_task_finish(pool_day_t pool, task_t *task) { +// cppcheck-suppress unusedFunction +void *wait_task_finish(const pool_day_t pool, task_t *task) { void *ret; if (!pool || !task) { @@ -163,6 +169,8 @@ void *wait_task_finish(pool_day_t pool, task_t *task) { return NULL; } + // TODO: should we check if the task belongs to the pool? + POOL_DAY_INFO("waiting for the finish of the task"); sem_wait(&task->ready); POOL_DAY_INFO("task finished"); diff --git a/src/task.c b/src/task.c index da24ad2..c9a318c 100644 --- a/src/task.c +++ b/src/task.c @@ -2,6 +2,7 @@ #include +// cppcheck-suppress unusedFunction task_t *create_task(void *(*task)(void *), void *param) { task_t *node; @@ -9,7 +10,7 @@ task_t *create_task(void *(*task)(void *), void *param) { if (node) { node->next = node->prev = NULL; node->task = task; - node->param = param; + node->param = param; // TODO: should we deep copy the param? node->ret_val = NULL; sem_init(&node->ready, 0, 0); From 9d0b4866e143a8d7722c381a9aeaa8a434a601e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Sat, 22 Nov 2025 14:37:53 -0300 Subject: [PATCH 13/63] lib: adding task callbacks --- include/internal/task.h | 21 +++-- include/pool_day.h | 2 +- samples/c/main.c | 2 +- samples/cpp/main.cc | 4 +- src/pool_day.c | 8 ++ src/queue.c | 1 + src/task.c | 7 +- test/src/mock/callback_mock.cc | 8 ++ test/src/mock/callback_mock.h | 4 + test/src/pool_day_test.cc | 141 ++++++++++++++++++++++++++++----- test/src/queue_test.cc | 36 ++++----- 11 files changed, 182 insertions(+), 52 deletions(-) diff --git a/include/internal/task.h b/include/internal/task.h index 93e72df..36454ff 100644 --- a/include/internal/task.h +++ b/include/internal/task.h @@ -49,12 +49,14 @@ * @brief Pool task definition. */ struct task { - struct task *next; //!< Next element of the current instance. - struct task *prev; //!< Previous element of the current instance. - void *(*task)(void *); //!< Task callback. - void *param; //!< Parameter of the task callback. - void *ret_val; //!< Task return value. - sem_t ready; //!< Task's semaphore. + struct task *next; //!< Next element of the current instance. + struct task *prev; //!< Previous element of the current instance. + void *(*task)(void *); //!< Task callback. + void *param; //!< Parameter of the task callback. + void *ret_val; //!< Task return value. + void (*on_task_start)(void); //!< Callback executed when the task starts. + void (*on_task_end)(void *); //!< Callback executed when the task ends. + sem_t ready; //!< Task's semaphore. }; typedef struct task task_t; //!< Structure representing an item on the task @@ -63,13 +65,16 @@ typedef struct task task_t; //!< Structure representing an item on the task * @brief Create a new task. * * @note The created task doesn't require a manual release once it's bound to a - * pool. Otherwise it can be released using the function destroy_task. + * pool. * * @param[in] task Task callback. * @param[in] param Task parameter. + * @param[in] start_cb Callback executed when the task starts. + * @param[in] end_cb Callback executed when the task ends. * * @return Pointer to the new task. */ -task_t *create_task(void *(*task)(void *), void *param); +task_t *create_task(void *(*task)(void *), void *param, void (*start_cb)(void), + void (*end_cb)(void *)); #endif // TASK_H_ diff --git a/include/pool_day.h b/include/pool_day.h index 8763022..7f118a8 100644 --- a/include/pool_day.h +++ b/include/pool_day.h @@ -81,7 +81,7 @@ uint32_t queued_tasks(pool_day_t pool); * * pool = create_pool(size); * if (pool) { - * task_t *t = create_task(callback, (void *)param); + * task_t *t = create_task(callback, (void *)param, start_cb, end_cb); * * enqueue_task(pool, t); * diff --git a/samples/c/main.c b/samples/c/main.c index 54435dd..67716aa 100644 --- a/samples/c/main.c +++ b/samples/c/main.c @@ -30,7 +30,7 @@ int main(void) { exit(EXIT_FAILURE); } - task = create_task(func, (void *)"foo"); + task = create_task(func, (void *)"foo", NULL, NULL); assert(enqueue_task(pool, task) == POOL_DAY_SUCCESS); char *ret = (char *)wait_task_finish(pool, task); diff --git a/samples/cpp/main.cc b/samples/cpp/main.cc index a6bfc0c..bc95ccf 100644 --- a/samples/cpp/main.cc +++ b/samples/cpp/main.cc @@ -7,8 +7,8 @@ class Foo { public: Foo() : pool_{create_pool(2)}, - t1_{create_task(Foo::Cb1, (void *)"hello")}, - t2_{create_task(Foo::Cb2, (void *)"hi")} { + t1_{create_task(Foo::Cb1, (void *)"hello", nullptr, nullptr)}, + t2_{create_task(Foo::Cb2, (void *)"hi", nullptr, nullptr)} { } void RunTasks(void) { diff --git a/src/pool_day.c b/src/pool_day.c index 41b8c64..85ce688 100644 --- a/src/pool_day.c +++ b/src/pool_day.c @@ -38,7 +38,15 @@ __static void *thread_func(void *param) { task_t *entry = dequeue(pool->tasks); if (entry) { POOL_DAY_INFO("thread '0x%x' running the task", pthread_self()); + if (entry->on_task_start) { + entry->on_task_start(); + } + void *ret = entry->task(entry->param); + + if (entry->on_task_end) { + entry->on_task_end(ret); + } POOL_DAY_INFO("thread '0x%x' finished the task", pthread_self()); entry->ret_val = ret; diff --git a/src/queue.c b/src/queue.c index 264efe7..05a670d 100644 --- a/src/queue.c +++ b/src/queue.c @@ -88,6 +88,7 @@ void destroy_queue(task_queue_t *queue) { for_each_task_safe(curr, queue) { task_t *node = __dequeue(queue); + sem_destroy(&node->ready); free(node); } }) diff --git a/src/task.c b/src/task.c index c9a318c..49c10dd 100644 --- a/src/task.c +++ b/src/task.c @@ -3,15 +3,16 @@ #include // cppcheck-suppress unusedFunction -task_t *create_task(void *(*task)(void *), void *param) { +task_t *create_task(void *(*task)(void *), void *param, void (*start_cb)(void), + void (*end_cb)(void *)) { task_t *node; node = (task_t *)calloc(1, sizeof(task_t)); if (node) { - node->next = node->prev = NULL; node->task = task; node->param = param; // TODO: should we deep copy the param? - node->ret_val = NULL; + node->on_task_start = start_cb; + node->on_task_end = end_cb; sem_init(&node->ready, 0, 0); } diff --git a/test/src/mock/callback_mock.cc b/test/src/mock/callback_mock.cc index 9deed48..24fd86b 100644 --- a/test/src/mock/callback_mock.cc +++ b/test/src/mock/callback_mock.cc @@ -16,6 +16,14 @@ void *CbWrapper::TaskCb(void *param) { return mock_->TaskCb(param); } +void CbWrapper::OnTaskStartCb() { + mock_->OnTaskStartCb(); +} + +void CbWrapper::OnTaskEndCb(void *param) { + mock_->OnTaskEndCb(param); +} + TaskMock &CbWrapper::mock() { return *CbWrapper::mock_; } diff --git a/test/src/mock/callback_mock.h b/test/src/mock/callback_mock.h index 2572191..ce524f1 100644 --- a/test/src/mock/callback_mock.h +++ b/test/src/mock/callback_mock.h @@ -6,6 +6,8 @@ class TaskMock { public: MOCK_METHOD(void *, TaskCb, (void *)); + MOCK_METHOD(void, OnTaskStartCb, ()); + MOCK_METHOD(void, OnTaskEndCb, (void *)); }; class CbWrapper { @@ -14,6 +16,8 @@ class CbWrapper { ~CbWrapper(); static void *TaskCb(void *param); + static void OnTaskStartCb(); + static void OnTaskEndCb(void *param); static TaskMock &mock(); private: diff --git a/test/src/pool_day_test.cc b/test/src/pool_day_test.cc index 2438365..d777b8c 100644 --- a/test/src/pool_day_test.cc +++ b/test/src/pool_day_test.cc @@ -48,7 +48,7 @@ TEST_F(PoolDayTest, CreatePollWithInvalidSize) { * it, then it must be added to the pool's task queue. */ TEST_F(PoolDayTest, EnqueueSingleTaskWithPoolEmpty) { - auto task = create_task(nullptr, nullptr); + auto task = create_task(nullptr, nullptr, nullptr, nullptr); EXPECT_EQ(queued_tasks(pool_), 0); EXPECT_EQ(enqueue_task(pool_, task), POOL_DAY_SUCCESS); @@ -60,11 +60,11 @@ TEST_F(PoolDayTest, EnqueueSingleTaskWithPoolEmpty) { * it, then they must be added to the pool's task queue. */ TEST_F(PoolDayTest, EnqueueTaskWithPoolNotEmpty) { - auto t1 = create_task(nullptr, nullptr); - auto t2 = create_task(nullptr, nullptr); - auto t3 = create_task(nullptr, nullptr); - auto t4 = create_task(nullptr, nullptr); - auto t5 = create_task(nullptr, nullptr); + auto t1 = create_task(nullptr, nullptr, nullptr, nullptr); + auto t2 = create_task(nullptr, nullptr, nullptr, nullptr); + auto t3 = create_task(nullptr, nullptr, nullptr, nullptr); + auto t4 = create_task(nullptr, nullptr, nullptr, nullptr); + auto t5 = create_task(nullptr, nullptr, nullptr, nullptr); EXPECT_EQ(queued_tasks(pool_), 0); @@ -82,7 +82,7 @@ TEST_F(PoolDayTest, EnqueueTaskWithPoolNotEmpty) { * then nothing must happen and the suitable error code must be returned. */ TEST_F(PoolDayTest, EnqueueTaskWithNullPool) { - auto task = create_task(nullptr, nullptr); + auto task = create_task(nullptr, nullptr, nullptr, nullptr); EXPECT_EQ(enqueue_task(nullptr, task), POOL_DAY_ERROR_NULL_PARAM); free(task); @@ -93,7 +93,7 @@ TEST_F(PoolDayTest, EnqueueTaskWithNullPool) { * then nothing must happen and the suitable error code must be returned. */ TEST_F(PoolDayTest, EnqueueTaskWithNullTask) { - auto task = create_task(nullptr, nullptr); + auto task = create_task(nullptr, nullptr, nullptr, nullptr); EXPECT_EQ(enqueue_task(pool_, nullptr), POOL_DAY_ERROR_NULL_PARAM); free(task); @@ -113,7 +113,7 @@ TEST_F(PoolDayTest, GetQueuedTasksCountWithNoTasks) { */ TEST_F(PoolDayTest, GetQueuedTasksCountWithSingleTask) { { - auto task = create_task(nullptr, nullptr); + auto task = create_task(nullptr, nullptr, nullptr, nullptr); enqueue_task(pool_, task); } @@ -128,11 +128,11 @@ TEST_F(PoolDayTest, GetQueuedTasksCountWithSingleTask) { */ TEST_F(PoolDayTest, GetQueuedTasksCountWithSeveralTasks) { { - auto t1 = create_task(nullptr, nullptr); - auto t2 = create_task(nullptr, nullptr); - auto t3 = create_task(nullptr, nullptr); - auto t4 = create_task(nullptr, nullptr); - auto t5 = create_task(nullptr, nullptr); + auto t1 = create_task(nullptr, nullptr, nullptr, nullptr); + auto t2 = create_task(nullptr, nullptr, nullptr, nullptr); + auto t3 = create_task(nullptr, nullptr, nullptr, nullptr); + auto t4 = create_task(nullptr, nullptr, nullptr, nullptr); + auto t5 = create_task(nullptr, nullptr, nullptr, nullptr); enqueue_task(pool_, t1); enqueue_task(pool_, t2); @@ -167,7 +167,7 @@ TEST_F(PoolDayTest, DestroyPollWithNullHandle) { * scheduled for execution, then the task's callback must be called. */ TEST_F(PoolDayTest, ExecuteTaskWithNullParameterWithSuccess) { - auto task = create_task(CbWrapper::TaskCb, nullptr); + auto task = create_task(CbWrapper::TaskCb, nullptr, nullptr, nullptr); char ret_val[]{ "hello, i'm the return of the task" }; enqueue_task(pool_, task); @@ -189,6 +189,109 @@ TEST_F(PoolDayTest, ExecuteTaskWithNullParameterWithSuccess) { free(task); } +/** + * @brief Given the pool has one task with callbacks, when the task is + * scheduled for execution, then the task's callbacks must be called at the + * suitable times. + */ +TEST_F(PoolDayTest, ExecuteTaskWithCallbacks) { + auto task = create_task(CbWrapper::TaskCb, nullptr, CbWrapper::OnTaskStartCb, + CbWrapper::OnTaskEndCb); + char ret_val[]{ "hello, i'm the return of the task" }; + + enqueue_task(pool_, task); + + EXPECT_CALL(CbWrapper::mock(), OnTaskStartCb()) + .Times(1); + + EXPECT_CALL(CbWrapper::mock(), TaskCb(nullptr)) + .Times(1) + .WillOnce( + InvokeWithoutArgs([&]() { + abort_tasks(pool_); // to break the thread loop + return ret_val; + }) + ); + + EXPECT_CALL(CbWrapper::mock(), OnTaskEndCb(ret_val)) + .Times(1); + + auto ret = thread_func(pool_); + EXPECT_EQ(ret, nullptr); + EXPECT_EQ(task->ret_val, ret_val); + + // cleanup + free(task); +} + +/** + * @brief Given the pool has one task with only start callback, when the task is + * scheduled for execution, then the task's start callback must be called. + */ +TEST_F(PoolDayTest, ExecuteTaskWithStartCallbackOnly) { + auto task = create_task(CbWrapper::TaskCb, nullptr, CbWrapper::OnTaskStartCb, + nullptr); + char ret_val[]{ "hello, i'm the return of the task" }; + + enqueue_task(pool_, task); + + EXPECT_CALL(CbWrapper::mock(), OnTaskStartCb()) + .Times(1); + + EXPECT_CALL(CbWrapper::mock(), TaskCb(nullptr)) + .Times(1) + .WillOnce( + InvokeWithoutArgs([&]() { + abort_tasks(pool_); // to break the thread loop + return ret_val; + }) + ); + + EXPECT_CALL(CbWrapper::mock(), OnTaskEndCb(_)) + .Times(0); + + auto ret = thread_func(pool_); + EXPECT_EQ(ret, nullptr); + EXPECT_EQ(task->ret_val, ret_val); + + // cleanup + free(task); +} + +/** + * @brief Given the pool has one task with only end callback, when the task is + * scheduled for execution, then the task's end callback must be called. + */ +TEST_F(PoolDayTest, ExecuteTaskWithEndCallbackOnly) { + auto task = create_task(CbWrapper::TaskCb, nullptr, nullptr, + CbWrapper::OnTaskEndCb); + char ret_val[]{ "hello, i'm the return of the task" }; + + enqueue_task(pool_, task); + + EXPECT_CALL(CbWrapper::mock(), OnTaskStartCb()) + .Times(0); + + EXPECT_CALL(CbWrapper::mock(), TaskCb(nullptr)) + .Times(1) + .WillOnce( + InvokeWithoutArgs([&]() { + abort_tasks(pool_); // to break the thread loop + return ret_val; + }) + ); + + EXPECT_CALL(CbWrapper::mock(), OnTaskEndCb(ret_val)) + .Times(1); + + auto ret = thread_func(pool_); + EXPECT_EQ(ret, nullptr); + EXPECT_EQ(task->ret_val, ret_val); + + // cleanup + free(task); +} + /** * @brief Given the pool has one task with a parameter, when the task is * scheduled for execution, then the task's callback must be called with the @@ -197,7 +300,7 @@ TEST_F(PoolDayTest, ExecuteTaskWithNullParameterWithSuccess) { TEST_F(PoolDayTest, ExecuteTaskWithParameterWithSuccess) { char param[]{ "param" }; char ret_val[]{ "hello, i'm the return of the task" }; - auto task = create_task(CbWrapper::TaskCb, param); + auto task = create_task(CbWrapper::TaskCb, param, nullptr, nullptr); enqueue_task(pool_, task); @@ -224,7 +327,7 @@ TEST_F(PoolDayTest, ExecuteTaskWithParameterWithSuccess) { */ TEST_F(PoolDayTest, ExecuteTaskWithMustStopSet) { { - auto task = create_task(CbWrapper::TaskCb, nullptr); + auto task = create_task(CbWrapper::TaskCb, nullptr, nullptr, nullptr); enqueue_task(pool_, task); } @@ -251,7 +354,7 @@ TEST_F(PoolDayTest, AbortTasksWithNullPoolHandle) { TEST_F(PoolDayTest, WaitTaskFinish) { int ret_val{1234}; char param[]{ "param" }; - auto task = create_task(CbWrapper::TaskCb, param); + auto task = create_task(CbWrapper::TaskCb, param, nullptr, nullptr); { enqueue_task(pool_, task); @@ -277,7 +380,7 @@ TEST_F(PoolDayTest, WaitTaskFinish) { * the finish of the task, then null must be returned. */ TEST_F(PoolDayTest, WaitTaskFinishWithNullPoolHandle) { - auto task = create_task(nullptr, nullptr); + auto task = create_task(nullptr, nullptr, nullptr, nullptr); EXPECT_EQ(wait_task_finish(nullptr, task), nullptr); diff --git a/test/src/queue_test.cc b/test/src/queue_test.cc index d859f69..ad3a8bd 100644 --- a/test/src/queue_test.cc +++ b/test/src/queue_test.cc @@ -33,11 +33,11 @@ TEST_F(QueueTest, GetQueueSizeWithEmptyQueue) { */ TEST_F(QueueTest, GetQueueSizeWithNotEmptyQueue) { { - auto t1 = create_task(nullptr, nullptr); - auto t2 = create_task(nullptr, nullptr); - auto t3 = create_task(nullptr, nullptr); - auto t4 = create_task(nullptr, nullptr); - auto t5 = create_task(nullptr, nullptr); + auto t1 = create_task(nullptr, nullptr, nullptr, nullptr); + auto t2 = create_task(nullptr, nullptr, nullptr, nullptr); + auto t3 = create_task(nullptr, nullptr, nullptr, nullptr); + auto t4 = create_task(nullptr, nullptr, nullptr, nullptr); + auto t5 = create_task(nullptr, nullptr, nullptr, nullptr); enqueue(queue_, t1); enqueue(queue_, t2); @@ -63,8 +63,8 @@ TEST_F(QueueTest, GetQueueSizeWithNullQueue) { */ TEST_F(QueueTest, EnqueueElementWithNotEmptyQueue) { { - auto t1 = create_task(nullptr, nullptr); - auto t2 = create_task(nullptr, nullptr); + auto t1 = create_task(nullptr, nullptr, nullptr, nullptr); + auto t2 = create_task(nullptr, nullptr, nullptr, nullptr); enqueue(queue_, t1); enqueue(queue_, t2); @@ -72,7 +72,7 @@ TEST_F(QueueTest, EnqueueElementWithNotEmptyQueue) { EXPECT_EQ(queue_size(queue_), 2); } - auto t3 = create_task(nullptr, nullptr); + auto t3 = create_task(nullptr, nullptr, nullptr, nullptr); enqueue(queue_, t3); @@ -95,8 +95,8 @@ TEST_F(QueueTest, EnqueueNullElementWithEmptyQueue) { */ TEST_F(QueueTest, EnqueueNullElementWithNotEmptyQueue) { { - auto t1 = create_task(nullptr, nullptr); - auto t2 = create_task(nullptr, nullptr); + auto t1 = create_task(nullptr, nullptr, nullptr, nullptr); + auto t2 = create_task(nullptr, nullptr, nullptr, nullptr); enqueue(queue_, t1); enqueue(queue_, t2); @@ -112,7 +112,7 @@ TEST_F(QueueTest, EnqueueNullElementWithNotEmptyQueue) { * then nothing must happen. */ TEST_F(QueueTest, EnqueueElementWithNullQueue) { - auto t1 = create_task(nullptr, nullptr); + auto t1 = create_task(nullptr, nullptr, nullptr, nullptr); enqueue(nullptr, t1); free(t1); @@ -138,9 +138,9 @@ TEST_F(QueueTest, DequeueElementWithEmptyQueue) { * size of the queue must be decreased by 1. */ TEST_F(QueueTest, DequeueSingleElementWithNotEmptyQueue) { - auto t1 = create_task(nullptr, nullptr); - auto t2 = create_task(nullptr, nullptr); - auto t3 = create_task(nullptr, nullptr); + auto t1 = create_task(nullptr, nullptr, nullptr, nullptr); + auto t2 = create_task(nullptr, nullptr, nullptr, nullptr); + auto t3 = create_task(nullptr, nullptr, nullptr, nullptr); enqueue(queue_, t1); enqueue(queue_, t2); @@ -163,10 +163,10 @@ TEST_F(QueueTest, DequeueSingleElementWithNotEmptyQueue) { * the operations. */ TEST_F(QueueTest, DequeueMultipleElementsWithNotEmptyQueue) { - auto t1 = create_task(nullptr, nullptr); - auto t2 = create_task(nullptr, nullptr); - auto t3 = create_task(nullptr, nullptr); - auto t4 = create_task(nullptr, nullptr); + auto t1 = create_task(nullptr, nullptr, nullptr, nullptr); + auto t2 = create_task(nullptr, nullptr, nullptr, nullptr); + auto t3 = create_task(nullptr, nullptr, nullptr, nullptr); + auto t4 = create_task(nullptr, nullptr, nullptr, nullptr); task_t *ret; enqueue(queue_, t1); From 055f14dd139b7de18320a4c762cdeca21b105d87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Sat, 22 Nov 2025 14:59:46 -0300 Subject: [PATCH 14/63] samples: updating C sample --- include/internal/task.h | 23 +++++++++++++---------- samples/c/main.c | 11 ++++++++++- src/pool_day.c | 10 +++++----- src/task.c | 5 +++-- test/src/mock/callback_mock.cc | 8 ++++---- test/src/mock/callback_mock.h | 8 ++++---- test/src/pool_day_test.cc | 26 ++++++++++++++------------ 7 files changed, 53 insertions(+), 38 deletions(-) diff --git a/include/internal/task.h b/include/internal/task.h index 36454ff..f8cf891 100644 --- a/include/internal/task.h +++ b/include/internal/task.h @@ -25,6 +25,7 @@ #define TASK_H_ #include +#include /** * @brief For-each macro implementation. @@ -49,14 +50,15 @@ * @brief Pool task definition. */ struct task { - struct task *next; //!< Next element of the current instance. - struct task *prev; //!< Previous element of the current instance. - void *(*task)(void *); //!< Task callback. - void *param; //!< Parameter of the task callback. - void *ret_val; //!< Task return value. - void (*on_task_start)(void); //!< Callback executed when the task starts. - void (*on_task_end)(void *); //!< Callback executed when the task ends. - sem_t ready; //!< Task's semaphore. + // TODO: ID? + struct task *next; //!< Next element of the current instance. + struct task *prev; //!< Previous element of the current instance. + void *(*task)(void *); //!< Task callback. + void *param; //!< Parameter of the task callback. + void *ret_val; //!< Task return value. + void (*on_task_start)(uint32_t); //!< Callback executed when the task starts. + void (*on_task_end)(uint32_t, void *); //!< Callback executed when the task ends. + sem_t ready; //!< Task's semaphore. }; typedef struct task task_t; //!< Structure representing an item on the task @@ -74,7 +76,8 @@ typedef struct task task_t; //!< Structure representing an item on the task * * @return Pointer to the new task. */ -task_t *create_task(void *(*task)(void *), void *param, void (*start_cb)(void), - void (*end_cb)(void *)); +task_t *create_task(void *(*task)(void *), void *param, + void (*start_cb)(uint32_t), + void (*end_cb)(uint32_t, void *)); #endif // TASK_H_ diff --git a/samples/c/main.c b/samples/c/main.c index 67716aa..8b0fdfc 100644 --- a/samples/c/main.c +++ b/samples/c/main.c @@ -6,6 +6,14 @@ #include "pool_day.h" +void task_start_callback(uint32_t tid) { + printf("task '%u' is starting...\n", tid); +} + +void task_end_callback(uint32_t tid, void *ret_val) { + printf("task '%u' ended with return value: %s\n", tid, (char *)ret_val); +} + void *func(void *param) { char *str = (char *)param; char *ret = (char *)calloc(10, sizeof(char)); @@ -30,7 +38,8 @@ int main(void) { exit(EXIT_FAILURE); } - task = create_task(func, (void *)"foo", NULL, NULL); + task = create_task(func, (void *)"foo", task_start_callback, + task_end_callback); assert(enqueue_task(pool, task) == POOL_DAY_SUCCESS); char *ret = (char *)wait_task_finish(pool, task); diff --git a/src/pool_day.c b/src/pool_day.c index 85ce688..8f99ea9 100644 --- a/src/pool_day.c +++ b/src/pool_day.c @@ -37,19 +37,19 @@ __static void *thread_func(void *param) { task_t *entry = dequeue(pool->tasks); if (entry) { - POOL_DAY_INFO("thread '0x%x' running the task", pthread_self()); if (entry->on_task_start) { - entry->on_task_start(); + entry->on_task_start(0); } + POOL_DAY_INFO("thread '0x%x' running the task", pthread_self()); void *ret = entry->task(entry->param); + POOL_DAY_INFO("thread '0x%x' finished the task", pthread_self()); + entry->ret_val = ret; if (entry->on_task_end) { - entry->on_task_end(ret); + entry->on_task_end(0, entry->ret_val); } - POOL_DAY_INFO("thread '0x%x' finished the task", pthread_self()); - entry->ret_val = ret; sem_post(&entry->ready); } } diff --git a/src/task.c b/src/task.c index 49c10dd..3a87dd9 100644 --- a/src/task.c +++ b/src/task.c @@ -3,8 +3,9 @@ #include // cppcheck-suppress unusedFunction -task_t *create_task(void *(*task)(void *), void *param, void (*start_cb)(void), - void (*end_cb)(void *)) { +task_t *create_task(void *(*task)(void *), void *param, + void (*start_cb)(uint32_t), + void (*end_cb)(uint32_t, void *)) { task_t *node; node = (task_t *)calloc(1, sizeof(task_t)); diff --git a/test/src/mock/callback_mock.cc b/test/src/mock/callback_mock.cc index 24fd86b..19c98bc 100644 --- a/test/src/mock/callback_mock.cc +++ b/test/src/mock/callback_mock.cc @@ -16,12 +16,12 @@ void *CbWrapper::TaskCb(void *param) { return mock_->TaskCb(param); } -void CbWrapper::OnTaskStartCb() { - mock_->OnTaskStartCb(); +void CbWrapper::OnTaskStartCb(uint32_t tid) { + mock_->OnTaskStartCb(tid); } -void CbWrapper::OnTaskEndCb(void *param) { - mock_->OnTaskEndCb(param); +void CbWrapper::OnTaskEndCb(uint32_t tid, void *param) { + mock_->OnTaskEndCb(tid, param); } TaskMock &CbWrapper::mock() { diff --git a/test/src/mock/callback_mock.h b/test/src/mock/callback_mock.h index ce524f1..edd1c6a 100644 --- a/test/src/mock/callback_mock.h +++ b/test/src/mock/callback_mock.h @@ -6,8 +6,8 @@ class TaskMock { public: MOCK_METHOD(void *, TaskCb, (void *)); - MOCK_METHOD(void, OnTaskStartCb, ()); - MOCK_METHOD(void, OnTaskEndCb, (void *)); + MOCK_METHOD(void, OnTaskStartCb, (uint32_t)); + MOCK_METHOD(void, OnTaskEndCb, (uint32_t, void *)); }; class CbWrapper { @@ -16,8 +16,8 @@ class CbWrapper { ~CbWrapper(); static void *TaskCb(void *param); - static void OnTaskStartCb(); - static void OnTaskEndCb(void *param); + static void OnTaskStartCb(uint32_t tid); + static void OnTaskEndCb(uint32_t tid, void *param); static TaskMock &mock(); private: diff --git a/test/src/pool_day_test.cc b/test/src/pool_day_test.cc index d777b8c..97d37c1 100644 --- a/test/src/pool_day_test.cc +++ b/test/src/pool_day_test.cc @@ -172,6 +172,8 @@ TEST_F(PoolDayTest, ExecuteTaskWithNullParameterWithSuccess) { enqueue_task(pool_, task); + EXPECT_CALL(CbWrapper::mock(), OnTaskStartCb(_)) + .Times(0); EXPECT_CALL(CbWrapper::mock(), TaskCb(nullptr)) .Times(1) .WillOnce( @@ -180,6 +182,8 @@ TEST_F(PoolDayTest, ExecuteTaskWithNullParameterWithSuccess) { return ret_val; }) ); + EXPECT_CALL(CbWrapper::mock(), OnTaskEndCb(_, _)) + .Times(0); auto ret = thread_func(pool_); EXPECT_EQ(ret, nullptr); @@ -201,9 +205,8 @@ TEST_F(PoolDayTest, ExecuteTaskWithCallbacks) { enqueue_task(pool_, task); - EXPECT_CALL(CbWrapper::mock(), OnTaskStartCb()) + EXPECT_CALL(CbWrapper::mock(), OnTaskStartCb(0)) // TODO: check tid .Times(1); - EXPECT_CALL(CbWrapper::mock(), TaskCb(nullptr)) .Times(1) .WillOnce( @@ -212,8 +215,7 @@ TEST_F(PoolDayTest, ExecuteTaskWithCallbacks) { return ret_val; }) ); - - EXPECT_CALL(CbWrapper::mock(), OnTaskEndCb(ret_val)) + EXPECT_CALL(CbWrapper::mock(), OnTaskEndCb(0, ret_val)) // TODO: check tid .Times(1); auto ret = thread_func(pool_); @@ -235,9 +237,8 @@ TEST_F(PoolDayTest, ExecuteTaskWithStartCallbackOnly) { enqueue_task(pool_, task); - EXPECT_CALL(CbWrapper::mock(), OnTaskStartCb()) + EXPECT_CALL(CbWrapper::mock(), OnTaskStartCb(0)) // TODO: check tid .Times(1); - EXPECT_CALL(CbWrapper::mock(), TaskCb(nullptr)) .Times(1) .WillOnce( @@ -246,8 +247,7 @@ TEST_F(PoolDayTest, ExecuteTaskWithStartCallbackOnly) { return ret_val; }) ); - - EXPECT_CALL(CbWrapper::mock(), OnTaskEndCb(_)) + EXPECT_CALL(CbWrapper::mock(), OnTaskEndCb(_, _)) .Times(0); auto ret = thread_func(pool_); @@ -269,9 +269,8 @@ TEST_F(PoolDayTest, ExecuteTaskWithEndCallbackOnly) { enqueue_task(pool_, task); - EXPECT_CALL(CbWrapper::mock(), OnTaskStartCb()) + EXPECT_CALL(CbWrapper::mock(), OnTaskStartCb(0)) // TODO: check tid .Times(0); - EXPECT_CALL(CbWrapper::mock(), TaskCb(nullptr)) .Times(1) .WillOnce( @@ -280,8 +279,7 @@ TEST_F(PoolDayTest, ExecuteTaskWithEndCallbackOnly) { return ret_val; }) ); - - EXPECT_CALL(CbWrapper::mock(), OnTaskEndCb(ret_val)) + EXPECT_CALL(CbWrapper::mock(), OnTaskEndCb(0, ret_val)) // TODO: check tid .Times(1); auto ret = thread_func(pool_); @@ -304,6 +302,8 @@ TEST_F(PoolDayTest, ExecuteTaskWithParameterWithSuccess) { enqueue_task(pool_, task); + EXPECT_CALL(CbWrapper::mock(), OnTaskStartCb(_)) + .Times(0); EXPECT_CALL(CbWrapper::mock(), TaskCb(param)) .Times(1) .WillOnce( @@ -312,6 +312,8 @@ TEST_F(PoolDayTest, ExecuteTaskWithParameterWithSuccess) { return ret_val; }) ); + EXPECT_CALL(CbWrapper::mock(), OnTaskEndCb(_, _)) + .Times(0); auto ret = thread_func(pool_); EXPECT_EQ(ret, nullptr); From 6162129b1c8170464a80e066b2932771625ca93e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Sat, 22 Nov 2025 15:28:52 -0300 Subject: [PATCH 15/63] lib: adding 'id' field to task struct --- include/internal/task.h | 5 ++-- include/pool_day.h | 2 +- samples/c/main.c | 4 +-- samples/cpp/main.cc | 4 +-- src/pool_day.c | 4 +-- src/task.c | 3 +- test/src/pool_day_test.cc | 62 +++++++++++++++++++++------------------ test/src/queue_test.cc | 36 +++++++++++------------ 8 files changed, 63 insertions(+), 57 deletions(-) diff --git a/include/internal/task.h b/include/internal/task.h index f8cf891..d8db719 100644 --- a/include/internal/task.h +++ b/include/internal/task.h @@ -50,7 +50,7 @@ * @brief Pool task definition. */ struct task { - // TODO: ID? + uint32_t id; //!< Task identifier. struct task *next; //!< Next element of the current instance. struct task *prev; //!< Previous element of the current instance. void *(*task)(void *); //!< Task callback. @@ -69,6 +69,7 @@ typedef struct task task_t; //!< Structure representing an item on the task * @note The created task doesn't require a manual release once it's bound to a * pool. * + * @param[in] id Task identifier. * @param[in] task Task callback. * @param[in] param Task parameter. * @param[in] start_cb Callback executed when the task starts. @@ -76,7 +77,7 @@ typedef struct task task_t; //!< Structure representing an item on the task * * @return Pointer to the new task. */ -task_t *create_task(void *(*task)(void *), void *param, +task_t *create_task(uint32_t id, void *(*task)(void *), void *param, void (*start_cb)(uint32_t), void (*end_cb)(uint32_t, void *)); diff --git a/include/pool_day.h b/include/pool_day.h index 7f118a8..86cb8cc 100644 --- a/include/pool_day.h +++ b/include/pool_day.h @@ -81,7 +81,7 @@ uint32_t queued_tasks(pool_day_t pool); * * pool = create_pool(size); * if (pool) { - * task_t *t = create_task(callback, (void *)param, start_cb, end_cb); + * task_t *t = create_task(id, callback, (void *)param, start_cb, end_cb); * * enqueue_task(pool, t); * diff --git a/samples/c/main.c b/samples/c/main.c index 8b0fdfc..d5904f8 100644 --- a/samples/c/main.c +++ b/samples/c/main.c @@ -7,7 +7,7 @@ #include "pool_day.h" void task_start_callback(uint32_t tid) { - printf("task '%u' is starting...\n", tid); + printf("task '%u' starting...\n", tid); } void task_end_callback(uint32_t tid, void *ret_val) { @@ -38,7 +38,7 @@ int main(void) { exit(EXIT_FAILURE); } - task = create_task(func, (void *)"foo", task_start_callback, + task = create_task(123, func, (void *)"foo", task_start_callback, task_end_callback); assert(enqueue_task(pool, task) == POOL_DAY_SUCCESS); diff --git a/samples/cpp/main.cc b/samples/cpp/main.cc index bc95ccf..ef955d7 100644 --- a/samples/cpp/main.cc +++ b/samples/cpp/main.cc @@ -7,8 +7,8 @@ class Foo { public: Foo() : pool_{create_pool(2)}, - t1_{create_task(Foo::Cb1, (void *)"hello", nullptr, nullptr)}, - t2_{create_task(Foo::Cb2, (void *)"hi", nullptr, nullptr)} { + t1_{create_task(0, Foo::Cb1, (void *)"hello", nullptr, nullptr)}, + t2_{create_task(1, Foo::Cb2, (void *)"hi", nullptr, nullptr)} { } void RunTasks(void) { diff --git a/src/pool_day.c b/src/pool_day.c index 8f99ea9..e38980a 100644 --- a/src/pool_day.c +++ b/src/pool_day.c @@ -38,7 +38,7 @@ __static void *thread_func(void *param) { task_t *entry = dequeue(pool->tasks); if (entry) { if (entry->on_task_start) { - entry->on_task_start(0); + entry->on_task_start(entry->id); } POOL_DAY_INFO("thread '0x%x' running the task", pthread_self()); @@ -47,7 +47,7 @@ __static void *thread_func(void *param) { entry->ret_val = ret; if (entry->on_task_end) { - entry->on_task_end(0, entry->ret_val); + entry->on_task_end(entry->id, entry->ret_val); } sem_post(&entry->ready); diff --git a/src/task.c b/src/task.c index 3a87dd9..6e15180 100644 --- a/src/task.c +++ b/src/task.c @@ -3,13 +3,14 @@ #include // cppcheck-suppress unusedFunction -task_t *create_task(void *(*task)(void *), void *param, +task_t *create_task(uint32_t id, void *(*task)(void *), void *param, void (*start_cb)(uint32_t), void (*end_cb)(uint32_t, void *)) { task_t *node; node = (task_t *)calloc(1, sizeof(task_t)); if (node) { + node->id = id; node->task = task; node->param = param; // TODO: should we deep copy the param? node->on_task_start = start_cb; diff --git a/test/src/pool_day_test.cc b/test/src/pool_day_test.cc index 97d37c1..52b65a0 100644 --- a/test/src/pool_day_test.cc +++ b/test/src/pool_day_test.cc @@ -48,7 +48,7 @@ TEST_F(PoolDayTest, CreatePollWithInvalidSize) { * it, then it must be added to the pool's task queue. */ TEST_F(PoolDayTest, EnqueueSingleTaskWithPoolEmpty) { - auto task = create_task(nullptr, nullptr, nullptr, nullptr); + auto task = create_task(0, nullptr, nullptr, nullptr, nullptr); EXPECT_EQ(queued_tasks(pool_), 0); EXPECT_EQ(enqueue_task(pool_, task), POOL_DAY_SUCCESS); @@ -60,11 +60,11 @@ TEST_F(PoolDayTest, EnqueueSingleTaskWithPoolEmpty) { * it, then they must be added to the pool's task queue. */ TEST_F(PoolDayTest, EnqueueTaskWithPoolNotEmpty) { - auto t1 = create_task(nullptr, nullptr, nullptr, nullptr); - auto t2 = create_task(nullptr, nullptr, nullptr, nullptr); - auto t3 = create_task(nullptr, nullptr, nullptr, nullptr); - auto t4 = create_task(nullptr, nullptr, nullptr, nullptr); - auto t5 = create_task(nullptr, nullptr, nullptr, nullptr); + auto t1 = create_task(0, nullptr, nullptr, nullptr, nullptr); + auto t2 = create_task(1, nullptr, nullptr, nullptr, nullptr); + auto t3 = create_task(2, nullptr, nullptr, nullptr, nullptr); + auto t4 = create_task(3, nullptr, nullptr, nullptr, nullptr); + auto t5 = create_task(4, nullptr, nullptr, nullptr, nullptr); EXPECT_EQ(queued_tasks(pool_), 0); @@ -82,7 +82,7 @@ TEST_F(PoolDayTest, EnqueueTaskWithPoolNotEmpty) { * then nothing must happen and the suitable error code must be returned. */ TEST_F(PoolDayTest, EnqueueTaskWithNullPool) { - auto task = create_task(nullptr, nullptr, nullptr, nullptr); + auto task = create_task(0, nullptr, nullptr, nullptr, nullptr); EXPECT_EQ(enqueue_task(nullptr, task), POOL_DAY_ERROR_NULL_PARAM); free(task); @@ -93,7 +93,7 @@ TEST_F(PoolDayTest, EnqueueTaskWithNullPool) { * then nothing must happen and the suitable error code must be returned. */ TEST_F(PoolDayTest, EnqueueTaskWithNullTask) { - auto task = create_task(nullptr, nullptr, nullptr, nullptr); + auto task = create_task(0, nullptr, nullptr, nullptr, nullptr); EXPECT_EQ(enqueue_task(pool_, nullptr), POOL_DAY_ERROR_NULL_PARAM); free(task); @@ -113,7 +113,7 @@ TEST_F(PoolDayTest, GetQueuedTasksCountWithNoTasks) { */ TEST_F(PoolDayTest, GetQueuedTasksCountWithSingleTask) { { - auto task = create_task(nullptr, nullptr, nullptr, nullptr); + auto task = create_task(0, nullptr, nullptr, nullptr, nullptr); enqueue_task(pool_, task); } @@ -128,11 +128,11 @@ TEST_F(PoolDayTest, GetQueuedTasksCountWithSingleTask) { */ TEST_F(PoolDayTest, GetQueuedTasksCountWithSeveralTasks) { { - auto t1 = create_task(nullptr, nullptr, nullptr, nullptr); - auto t2 = create_task(nullptr, nullptr, nullptr, nullptr); - auto t3 = create_task(nullptr, nullptr, nullptr, nullptr); - auto t4 = create_task(nullptr, nullptr, nullptr, nullptr); - auto t5 = create_task(nullptr, nullptr, nullptr, nullptr); + auto t1 = create_task(0, nullptr, nullptr, nullptr, nullptr); + auto t2 = create_task(1, nullptr, nullptr, nullptr, nullptr); + auto t3 = create_task(2, nullptr, nullptr, nullptr, nullptr); + auto t4 = create_task(3, nullptr, nullptr, nullptr, nullptr); + auto t5 = create_task(4, nullptr, nullptr, nullptr, nullptr); enqueue_task(pool_, t1); enqueue_task(pool_, t2); @@ -167,7 +167,7 @@ TEST_F(PoolDayTest, DestroyPollWithNullHandle) { * scheduled for execution, then the task's callback must be called. */ TEST_F(PoolDayTest, ExecuteTaskWithNullParameterWithSuccess) { - auto task = create_task(CbWrapper::TaskCb, nullptr, nullptr, nullptr); + auto task = create_task(0, CbWrapper::TaskCb, nullptr, nullptr, nullptr); char ret_val[]{ "hello, i'm the return of the task" }; enqueue_task(pool_, task); @@ -199,13 +199,13 @@ TEST_F(PoolDayTest, ExecuteTaskWithNullParameterWithSuccess) { * suitable times. */ TEST_F(PoolDayTest, ExecuteTaskWithCallbacks) { - auto task = create_task(CbWrapper::TaskCb, nullptr, CbWrapper::OnTaskStartCb, - CbWrapper::OnTaskEndCb); + auto task = create_task(0, CbWrapper::TaskCb, nullptr, + CbWrapper::OnTaskStartCb, CbWrapper::OnTaskEndCb); char ret_val[]{ "hello, i'm the return of the task" }; enqueue_task(pool_, task); - EXPECT_CALL(CbWrapper::mock(), OnTaskStartCb(0)) // TODO: check tid + EXPECT_CALL(CbWrapper::mock(), OnTaskStartCb(task->id)) .Times(1); EXPECT_CALL(CbWrapper::mock(), TaskCb(nullptr)) .Times(1) @@ -215,7 +215,7 @@ TEST_F(PoolDayTest, ExecuteTaskWithCallbacks) { return ret_val; }) ); - EXPECT_CALL(CbWrapper::mock(), OnTaskEndCb(0, ret_val)) // TODO: check tid + EXPECT_CALL(CbWrapper::mock(), OnTaskEndCb(task->id, ret_val)) .Times(1); auto ret = thread_func(pool_); @@ -231,13 +231,13 @@ TEST_F(PoolDayTest, ExecuteTaskWithCallbacks) { * scheduled for execution, then the task's start callback must be called. */ TEST_F(PoolDayTest, ExecuteTaskWithStartCallbackOnly) { - auto task = create_task(CbWrapper::TaskCb, nullptr, CbWrapper::OnTaskStartCb, - nullptr); + auto task = create_task(0, CbWrapper::TaskCb, nullptr, + CbWrapper::OnTaskStartCb, nullptr); char ret_val[]{ "hello, i'm the return of the task" }; enqueue_task(pool_, task); - EXPECT_CALL(CbWrapper::mock(), OnTaskStartCb(0)) // TODO: check tid + EXPECT_CALL(CbWrapper::mock(), OnTaskStartCb(task->id)) .Times(1); EXPECT_CALL(CbWrapper::mock(), TaskCb(nullptr)) .Times(1) @@ -263,13 +263,13 @@ TEST_F(PoolDayTest, ExecuteTaskWithStartCallbackOnly) { * scheduled for execution, then the task's end callback must be called. */ TEST_F(PoolDayTest, ExecuteTaskWithEndCallbackOnly) { - auto task = create_task(CbWrapper::TaskCb, nullptr, nullptr, + auto task = create_task(0, CbWrapper::TaskCb, nullptr, nullptr, CbWrapper::OnTaskEndCb); char ret_val[]{ "hello, i'm the return of the task" }; enqueue_task(pool_, task); - EXPECT_CALL(CbWrapper::mock(), OnTaskStartCb(0)) // TODO: check tid + EXPECT_CALL(CbWrapper::mock(), OnTaskStartCb(_)) .Times(0); EXPECT_CALL(CbWrapper::mock(), TaskCb(nullptr)) .Times(1) @@ -279,7 +279,7 @@ TEST_F(PoolDayTest, ExecuteTaskWithEndCallbackOnly) { return ret_val; }) ); - EXPECT_CALL(CbWrapper::mock(), OnTaskEndCb(0, ret_val)) // TODO: check tid + EXPECT_CALL(CbWrapper::mock(), OnTaskEndCb(task->id, ret_val)) .Times(1); auto ret = thread_func(pool_); @@ -298,7 +298,7 @@ TEST_F(PoolDayTest, ExecuteTaskWithEndCallbackOnly) { TEST_F(PoolDayTest, ExecuteTaskWithParameterWithSuccess) { char param[]{ "param" }; char ret_val[]{ "hello, i'm the return of the task" }; - auto task = create_task(CbWrapper::TaskCb, param, nullptr, nullptr); + auto task = create_task(0, CbWrapper::TaskCb, param, nullptr, nullptr); enqueue_task(pool_, task); @@ -329,7 +329,7 @@ TEST_F(PoolDayTest, ExecuteTaskWithParameterWithSuccess) { */ TEST_F(PoolDayTest, ExecuteTaskWithMustStopSet) { { - auto task = create_task(CbWrapper::TaskCb, nullptr, nullptr, nullptr); + auto task = create_task(0, CbWrapper::TaskCb, nullptr, nullptr, nullptr); enqueue_task(pool_, task); } @@ -356,11 +356,13 @@ TEST_F(PoolDayTest, AbortTasksWithNullPoolHandle) { TEST_F(PoolDayTest, WaitTaskFinish) { int ret_val{1234}; char param[]{ "param" }; - auto task = create_task(CbWrapper::TaskCb, param, nullptr, nullptr); + auto task = create_task(0, CbWrapper::TaskCb, param, nullptr, nullptr); { enqueue_task(pool_, task); + EXPECT_CALL(CbWrapper::mock(), OnTaskStartCb(_)) + .Times(0); EXPECT_CALL(CbWrapper::mock(), TaskCb(param)) .Times(1) .WillOnce( @@ -369,6 +371,8 @@ TEST_F(PoolDayTest, WaitTaskFinish) { return &ret_val; }) ); + EXPECT_CALL(CbWrapper::mock(), OnTaskEndCb(_, _)) + .Times(0); thread_func(pool_); } @@ -382,7 +386,7 @@ TEST_F(PoolDayTest, WaitTaskFinish) { * the finish of the task, then null must be returned. */ TEST_F(PoolDayTest, WaitTaskFinishWithNullPoolHandle) { - auto task = create_task(nullptr, nullptr, nullptr, nullptr); + auto task = create_task(0, nullptr, nullptr, nullptr, nullptr); EXPECT_EQ(wait_task_finish(nullptr, task), nullptr); diff --git a/test/src/queue_test.cc b/test/src/queue_test.cc index ad3a8bd..5b0976b 100644 --- a/test/src/queue_test.cc +++ b/test/src/queue_test.cc @@ -33,11 +33,11 @@ TEST_F(QueueTest, GetQueueSizeWithEmptyQueue) { */ TEST_F(QueueTest, GetQueueSizeWithNotEmptyQueue) { { - auto t1 = create_task(nullptr, nullptr, nullptr, nullptr); - auto t2 = create_task(nullptr, nullptr, nullptr, nullptr); - auto t3 = create_task(nullptr, nullptr, nullptr, nullptr); - auto t4 = create_task(nullptr, nullptr, nullptr, nullptr); - auto t5 = create_task(nullptr, nullptr, nullptr, nullptr); + auto t1 = create_task(0, nullptr, nullptr, nullptr, nullptr); + auto t2 = create_task(1, nullptr, nullptr, nullptr, nullptr); + auto t3 = create_task(2, nullptr, nullptr, nullptr, nullptr); + auto t4 = create_task(3, nullptr, nullptr, nullptr, nullptr); + auto t5 = create_task(4, nullptr, nullptr, nullptr, nullptr); enqueue(queue_, t1); enqueue(queue_, t2); @@ -63,8 +63,8 @@ TEST_F(QueueTest, GetQueueSizeWithNullQueue) { */ TEST_F(QueueTest, EnqueueElementWithNotEmptyQueue) { { - auto t1 = create_task(nullptr, nullptr, nullptr, nullptr); - auto t2 = create_task(nullptr, nullptr, nullptr, nullptr); + auto t1 = create_task(0, nullptr, nullptr, nullptr, nullptr); + auto t2 = create_task(1, nullptr, nullptr, nullptr, nullptr); enqueue(queue_, t1); enqueue(queue_, t2); @@ -72,7 +72,7 @@ TEST_F(QueueTest, EnqueueElementWithNotEmptyQueue) { EXPECT_EQ(queue_size(queue_), 2); } - auto t3 = create_task(nullptr, nullptr, nullptr, nullptr); + auto t3 = create_task(3, nullptr, nullptr, nullptr, nullptr); enqueue(queue_, t3); @@ -95,8 +95,8 @@ TEST_F(QueueTest, EnqueueNullElementWithEmptyQueue) { */ TEST_F(QueueTest, EnqueueNullElementWithNotEmptyQueue) { { - auto t1 = create_task(nullptr, nullptr, nullptr, nullptr); - auto t2 = create_task(nullptr, nullptr, nullptr, nullptr); + auto t1 = create_task(0, nullptr, nullptr, nullptr, nullptr); + auto t2 = create_task(1, nullptr, nullptr, nullptr, nullptr); enqueue(queue_, t1); enqueue(queue_, t2); @@ -112,7 +112,7 @@ TEST_F(QueueTest, EnqueueNullElementWithNotEmptyQueue) { * then nothing must happen. */ TEST_F(QueueTest, EnqueueElementWithNullQueue) { - auto t1 = create_task(nullptr, nullptr, nullptr, nullptr); + auto t1 = create_task(0, nullptr, nullptr, nullptr, nullptr); enqueue(nullptr, t1); free(t1); @@ -138,9 +138,9 @@ TEST_F(QueueTest, DequeueElementWithEmptyQueue) { * size of the queue must be decreased by 1. */ TEST_F(QueueTest, DequeueSingleElementWithNotEmptyQueue) { - auto t1 = create_task(nullptr, nullptr, nullptr, nullptr); - auto t2 = create_task(nullptr, nullptr, nullptr, nullptr); - auto t3 = create_task(nullptr, nullptr, nullptr, nullptr); + auto t1 = create_task(0, nullptr, nullptr, nullptr, nullptr); + auto t2 = create_task(1, nullptr, nullptr, nullptr, nullptr); + auto t3 = create_task(2, nullptr, nullptr, nullptr, nullptr); enqueue(queue_, t1); enqueue(queue_, t2); @@ -163,10 +163,10 @@ TEST_F(QueueTest, DequeueSingleElementWithNotEmptyQueue) { * the operations. */ TEST_F(QueueTest, DequeueMultipleElementsWithNotEmptyQueue) { - auto t1 = create_task(nullptr, nullptr, nullptr, nullptr); - auto t2 = create_task(nullptr, nullptr, nullptr, nullptr); - auto t3 = create_task(nullptr, nullptr, nullptr, nullptr); - auto t4 = create_task(nullptr, nullptr, nullptr, nullptr); + auto t1 = create_task(0, nullptr, nullptr, nullptr, nullptr); + auto t2 = create_task(1, nullptr, nullptr, nullptr, nullptr); + auto t3 = create_task(2, nullptr, nullptr, nullptr, nullptr); + auto t4 = create_task(3, nullptr, nullptr, nullptr, nullptr); task_t *ret; enqueue(queue_, t1); From 5f5ebe3e3e70cf0db3baa726262f06ab9c7c466c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Thu, 20 Nov 2025 16:13:46 -0300 Subject: [PATCH 16/63] lib: deep copying the task parameter and adding 'param_size' parameter to create_task function --- include/internal/task.h | 3 +- include/pool_day.h | 3 +- samples/c/main.c | 6 ++-- samples/cpp/main.cc | 9 ++++-- src/pool_day.c | 4 +++ src/queue.c | 3 ++ src/task.c | 9 ++++-- test/src/pool_day_test.cc | 58 +++++++++++++++++++++++---------------- test/src/queue_test.cc | 36 ++++++++++++------------ 9 files changed, 80 insertions(+), 51 deletions(-) diff --git a/include/internal/task.h b/include/internal/task.h index d8db719..d5e97b5 100644 --- a/include/internal/task.h +++ b/include/internal/task.h @@ -72,13 +72,14 @@ typedef struct task task_t; //!< Structure representing an item on the task * @param[in] id Task identifier. * @param[in] task Task callback. * @param[in] param Task parameter. + * @param[in] param_size Size of the task parameter. * @param[in] start_cb Callback executed when the task starts. * @param[in] end_cb Callback executed when the task ends. * * @return Pointer to the new task. */ task_t *create_task(uint32_t id, void *(*task)(void *), void *param, - void (*start_cb)(uint32_t), + size_t param_size, void (*start_cb)(uint32_t), void (*end_cb)(uint32_t, void *)); #endif // TASK_H_ diff --git a/include/pool_day.h b/include/pool_day.h index 86cb8cc..73eb72a 100644 --- a/include/pool_day.h +++ b/include/pool_day.h @@ -81,7 +81,8 @@ uint32_t queued_tasks(pool_day_t pool); * * pool = create_pool(size); * if (pool) { - * task_t *t = create_task(id, callback, (void *)param, start_cb, end_cb); + * task_t *t = create_task(id, callback, (void *)param, param_size, + * start_cb, end_cb); * * enqueue_task(pool, t); * diff --git a/samples/c/main.c b/samples/c/main.c index d5904f8..3b3cc02 100644 --- a/samples/c/main.c +++ b/samples/c/main.c @@ -30,7 +30,6 @@ void *func(void *param) { int main(void) { pool_day_t pool; - task_t *task; pool = create_pool(1); if (!pool) { @@ -38,8 +37,9 @@ int main(void) { exit(EXIT_FAILURE); } - task = create_task(123, func, (void *)"foo", task_start_callback, - task_end_callback); + char str[] = "foo"; + task_t *task = create_task(func, (void *)str, sizeof(char) * strlen(str) + 1, + task_start_callback, task_end_callback); assert(enqueue_task(pool, task) == POOL_DAY_SUCCESS); char *ret = (char *)wait_task_finish(pool, task); diff --git a/samples/cpp/main.cc b/samples/cpp/main.cc index ef955d7..fbe583c 100644 --- a/samples/cpp/main.cc +++ b/samples/cpp/main.cc @@ -1,4 +1,5 @@ #include +#include #include #include "pool_day.h" @@ -7,8 +8,12 @@ class Foo { public: Foo() : pool_{create_pool(2)}, - t1_{create_task(0, Foo::Cb1, (void *)"hello", nullptr, nullptr)}, - t2_{create_task(1, Foo::Cb2, (void *)"hi", nullptr, nullptr)} { + t1_{create_task(Foo::Cb1, (void *)"hello", + sizeof(char) * std::strlen("hello") + 1), nullptr, + nullptr}, + t2_{create_task(Foo::Cb2, (void *)"hi", + sizeof(char) * std::strlen("hi") + 1, nullptr, + nullptr)} { } void RunTasks(void) { diff --git a/src/pool_day.c b/src/pool_day.c index e38980a..de102c3 100644 --- a/src/pool_day.c +++ b/src/pool_day.c @@ -184,6 +184,10 @@ void *wait_task_finish(const pool_day_t pool, task_t *task) { POOL_DAY_INFO("task finished"); ret = task->ret_val; + + if (task->param) { + free(task->param); + } free(task); return ret; diff --git a/src/queue.c b/src/queue.c index 05a670d..089f989 100644 --- a/src/queue.c +++ b/src/queue.c @@ -89,6 +89,9 @@ void destroy_queue(task_queue_t *queue) { task_t *node = __dequeue(queue); sem_destroy(&node->ready); + if (node->param) { + free(node->param); + } free(node); } }) diff --git a/src/task.c b/src/task.c index 6e15180..8c77a97 100644 --- a/src/task.c +++ b/src/task.c @@ -1,10 +1,11 @@ #include "internal/task.h" #include +#include // cppcheck-suppress unusedFunction task_t *create_task(uint32_t id, void *(*task)(void *), void *param, - void (*start_cb)(uint32_t), + size_t param_size, void (*start_cb)(uint32_t), void (*end_cb)(uint32_t, void *)) { task_t *node; @@ -12,10 +13,14 @@ task_t *create_task(uint32_t id, void *(*task)(void *), void *param, if (node) { node->id = id; node->task = task; - node->param = param; // TODO: should we deep copy the param? node->on_task_start = start_cb; node->on_task_end = end_cb; + if (param) { + node->param = malloc(param_size); + memcpy(node->param, param, param_size); + } + sem_init(&node->ready, 0, 0); } diff --git a/test/src/pool_day_test.cc b/test/src/pool_day_test.cc index 52b65a0..6712063 100644 --- a/test/src/pool_day_test.cc +++ b/test/src/pool_day_test.cc @@ -36,6 +36,13 @@ class PoolDayTest : public Test { CbWrapper wrapper_; //!< Callback instance. }; +/** + * @brief Custom matcher to compare void pointers containing strings. + */ +MATCHER_P(StrEqVoidPointer, expected_string, "") { + return std::string(static_cast(arg)) == expected_string; +} + /** * @brief When we try to create a pool with size 0, then null must be returned. */ @@ -48,7 +55,7 @@ TEST_F(PoolDayTest, CreatePollWithInvalidSize) { * it, then it must be added to the pool's task queue. */ TEST_F(PoolDayTest, EnqueueSingleTaskWithPoolEmpty) { - auto task = create_task(0, nullptr, nullptr, nullptr, nullptr); + auto task = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); EXPECT_EQ(queued_tasks(pool_), 0); EXPECT_EQ(enqueue_task(pool_, task), POOL_DAY_SUCCESS); @@ -60,11 +67,11 @@ TEST_F(PoolDayTest, EnqueueSingleTaskWithPoolEmpty) { * it, then they must be added to the pool's task queue. */ TEST_F(PoolDayTest, EnqueueTaskWithPoolNotEmpty) { - auto t1 = create_task(0, nullptr, nullptr, nullptr, nullptr); - auto t2 = create_task(1, nullptr, nullptr, nullptr, nullptr); - auto t3 = create_task(2, nullptr, nullptr, nullptr, nullptr); - auto t4 = create_task(3, nullptr, nullptr, nullptr, nullptr); - auto t5 = create_task(4, nullptr, nullptr, nullptr, nullptr); + auto t1 = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); + auto t2 = create_task(1, nullptr, nullptr, 0, nullptr, nullptr); + auto t3 = create_task(2, nullptr, nullptr, 0, nullptr, nullptr); + auto t4 = create_task(3, nullptr, nullptr, 0, nullptr, nullptr); + auto t5 = create_task(4, nullptr, nullptr, 0, nullptr, nullptr); EXPECT_EQ(queued_tasks(pool_), 0); @@ -82,7 +89,7 @@ TEST_F(PoolDayTest, EnqueueTaskWithPoolNotEmpty) { * then nothing must happen and the suitable error code must be returned. */ TEST_F(PoolDayTest, EnqueueTaskWithNullPool) { - auto task = create_task(0, nullptr, nullptr, nullptr, nullptr); + auto task = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); EXPECT_EQ(enqueue_task(nullptr, task), POOL_DAY_ERROR_NULL_PARAM); free(task); @@ -93,7 +100,7 @@ TEST_F(PoolDayTest, EnqueueTaskWithNullPool) { * then nothing must happen and the suitable error code must be returned. */ TEST_F(PoolDayTest, EnqueueTaskWithNullTask) { - auto task = create_task(0, nullptr, nullptr, nullptr, nullptr); + auto task = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); EXPECT_EQ(enqueue_task(pool_, nullptr), POOL_DAY_ERROR_NULL_PARAM); free(task); @@ -113,7 +120,7 @@ TEST_F(PoolDayTest, GetQueuedTasksCountWithNoTasks) { */ TEST_F(PoolDayTest, GetQueuedTasksCountWithSingleTask) { { - auto task = create_task(0, nullptr, nullptr, nullptr, nullptr); + auto task = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); enqueue_task(pool_, task); } @@ -128,11 +135,11 @@ TEST_F(PoolDayTest, GetQueuedTasksCountWithSingleTask) { */ TEST_F(PoolDayTest, GetQueuedTasksCountWithSeveralTasks) { { - auto t1 = create_task(0, nullptr, nullptr, nullptr, nullptr); - auto t2 = create_task(1, nullptr, nullptr, nullptr, nullptr); - auto t3 = create_task(2, nullptr, nullptr, nullptr, nullptr); - auto t4 = create_task(3, nullptr, nullptr, nullptr, nullptr); - auto t5 = create_task(4, nullptr, nullptr, nullptr, nullptr); + auto t1 = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); + auto t2 = create_task(1, nullptr, nullptr, 0, nullptr, nullptr); + auto t3 = create_task(2, nullptr, nullptr, 0, nullptr, nullptr); + auto t4 = create_task(3, nullptr, nullptr, 0, nullptr, nullptr); + auto t5 = create_task(4, nullptr, nullptr, 0, nullptr, nullptr); enqueue_task(pool_, t1); enqueue_task(pool_, t2); @@ -167,7 +174,7 @@ TEST_F(PoolDayTest, DestroyPollWithNullHandle) { * scheduled for execution, then the task's callback must be called. */ TEST_F(PoolDayTest, ExecuteTaskWithNullParameterWithSuccess) { - auto task = create_task(0, CbWrapper::TaskCb, nullptr, nullptr, nullptr); + auto task = create_task(0, CbWrapper::TaskCb, nullptr, 0, nullptr, nullptr); char ret_val[]{ "hello, i'm the return of the task" }; enqueue_task(pool_, task); @@ -199,7 +206,7 @@ TEST_F(PoolDayTest, ExecuteTaskWithNullParameterWithSuccess) { * suitable times. */ TEST_F(PoolDayTest, ExecuteTaskWithCallbacks) { - auto task = create_task(0, CbWrapper::TaskCb, nullptr, + auto task = create_task(0, CbWrapper::TaskCb, nullptr, 0, CbWrapper::OnTaskStartCb, CbWrapper::OnTaskEndCb); char ret_val[]{ "hello, i'm the return of the task" }; @@ -231,7 +238,7 @@ TEST_F(PoolDayTest, ExecuteTaskWithCallbacks) { * scheduled for execution, then the task's start callback must be called. */ TEST_F(PoolDayTest, ExecuteTaskWithStartCallbackOnly) { - auto task = create_task(0, CbWrapper::TaskCb, nullptr, + auto task = create_task(0, CbWrapper::TaskCb, nullptr, 0, CbWrapper::OnTaskStartCb, nullptr); char ret_val[]{ "hello, i'm the return of the task" }; @@ -263,7 +270,7 @@ TEST_F(PoolDayTest, ExecuteTaskWithStartCallbackOnly) { * scheduled for execution, then the task's end callback must be called. */ TEST_F(PoolDayTest, ExecuteTaskWithEndCallbackOnly) { - auto task = create_task(0, CbWrapper::TaskCb, nullptr, nullptr, + auto task = create_task(0, CbWrapper::TaskCb, nullptr, 0, nullptr, CbWrapper::OnTaskEndCb); char ret_val[]{ "hello, i'm the return of the task" }; @@ -298,13 +305,14 @@ TEST_F(PoolDayTest, ExecuteTaskWithEndCallbackOnly) { TEST_F(PoolDayTest, ExecuteTaskWithParameterWithSuccess) { char param[]{ "param" }; char ret_val[]{ "hello, i'm the return of the task" }; - auto task = create_task(0, CbWrapper::TaskCb, param, nullptr, nullptr); + auto task = create_task(CbWrapper::TaskCb, param, + sizeof(char) * strlen(param) + 1, nullptr, nullptr); enqueue_task(pool_, task); EXPECT_CALL(CbWrapper::mock(), OnTaskStartCb(_)) .Times(0); - EXPECT_CALL(CbWrapper::mock(), TaskCb(param)) + EXPECT_CALL(CbWrapper::mock(), TaskCb(StrEqVoidPointer(param))) .Times(1) .WillOnce( InvokeWithoutArgs([&]() { @@ -320,6 +328,7 @@ TEST_F(PoolDayTest, ExecuteTaskWithParameterWithSuccess) { EXPECT_EQ(task->ret_val, ret_val); // cleanup + free(task->param); free(task); } @@ -329,7 +338,7 @@ TEST_F(PoolDayTest, ExecuteTaskWithParameterWithSuccess) { */ TEST_F(PoolDayTest, ExecuteTaskWithMustStopSet) { { - auto task = create_task(0, CbWrapper::TaskCb, nullptr, nullptr, nullptr); + auto task = create_task(0, CbWrapper::TaskCb, nullptr, 0, nullptr, nullptr); enqueue_task(pool_, task); } @@ -356,14 +365,15 @@ TEST_F(PoolDayTest, AbortTasksWithNullPoolHandle) { TEST_F(PoolDayTest, WaitTaskFinish) { int ret_val{1234}; char param[]{ "param" }; - auto task = create_task(0, CbWrapper::TaskCb, param, nullptr, nullptr); + auto task = create_task(0, CbWrapper::TaskCb, param, + sizeof(char) * strlen(param) + 1, nullptr, nullptr); { enqueue_task(pool_, task); EXPECT_CALL(CbWrapper::mock(), OnTaskStartCb(_)) .Times(0); - EXPECT_CALL(CbWrapper::mock(), TaskCb(param)) + EXPECT_CALL(CbWrapper::mock(), TaskCb(StrEqVoidPointer(param))) .Times(1) .WillOnce( Invoke([&]() { @@ -386,7 +396,7 @@ TEST_F(PoolDayTest, WaitTaskFinish) { * the finish of the task, then null must be returned. */ TEST_F(PoolDayTest, WaitTaskFinishWithNullPoolHandle) { - auto task = create_task(0, nullptr, nullptr, nullptr, nullptr); + auto task = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); EXPECT_EQ(wait_task_finish(nullptr, task), nullptr); diff --git a/test/src/queue_test.cc b/test/src/queue_test.cc index 5b0976b..a1ea283 100644 --- a/test/src/queue_test.cc +++ b/test/src/queue_test.cc @@ -33,11 +33,11 @@ TEST_F(QueueTest, GetQueueSizeWithEmptyQueue) { */ TEST_F(QueueTest, GetQueueSizeWithNotEmptyQueue) { { - auto t1 = create_task(0, nullptr, nullptr, nullptr, nullptr); - auto t2 = create_task(1, nullptr, nullptr, nullptr, nullptr); - auto t3 = create_task(2, nullptr, nullptr, nullptr, nullptr); - auto t4 = create_task(3, nullptr, nullptr, nullptr, nullptr); - auto t5 = create_task(4, nullptr, nullptr, nullptr, nullptr); + auto t1 = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); + auto t2 = create_task(1, nullptr, nullptr, 0, nullptr, nullptr); + auto t3 = create_task(2, nullptr, nullptr, 0, nullptr, nullptr); + auto t4 = create_task(3, nullptr, nullptr, 0, nullptr, nullptr); + auto t5 = create_task(4, nullptr, nullptr, 0, nullptr, nullptr); enqueue(queue_, t1); enqueue(queue_, t2); @@ -63,8 +63,8 @@ TEST_F(QueueTest, GetQueueSizeWithNullQueue) { */ TEST_F(QueueTest, EnqueueElementWithNotEmptyQueue) { { - auto t1 = create_task(0, nullptr, nullptr, nullptr, nullptr); - auto t2 = create_task(1, nullptr, nullptr, nullptr, nullptr); + auto t1 = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); + auto t2 = create_task(1, nullptr, nullptr, 0, nullptr, nullptr); enqueue(queue_, t1); enqueue(queue_, t2); @@ -72,7 +72,7 @@ TEST_F(QueueTest, EnqueueElementWithNotEmptyQueue) { EXPECT_EQ(queue_size(queue_), 2); } - auto t3 = create_task(3, nullptr, nullptr, nullptr, nullptr); + auto t3 = create_task(3, nullptr, nullptr, 0, nullptr, nullptr); enqueue(queue_, t3); @@ -95,8 +95,8 @@ TEST_F(QueueTest, EnqueueNullElementWithEmptyQueue) { */ TEST_F(QueueTest, EnqueueNullElementWithNotEmptyQueue) { { - auto t1 = create_task(0, nullptr, nullptr, nullptr, nullptr); - auto t2 = create_task(1, nullptr, nullptr, nullptr, nullptr); + auto t1 = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); + auto t2 = create_task(1, nullptr, nullptr, 0, nullptr, nullptr); enqueue(queue_, t1); enqueue(queue_, t2); @@ -112,7 +112,7 @@ TEST_F(QueueTest, EnqueueNullElementWithNotEmptyQueue) { * then nothing must happen. */ TEST_F(QueueTest, EnqueueElementWithNullQueue) { - auto t1 = create_task(0, nullptr, nullptr, nullptr, nullptr); + auto t1 = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); enqueue(nullptr, t1); free(t1); @@ -138,9 +138,9 @@ TEST_F(QueueTest, DequeueElementWithEmptyQueue) { * size of the queue must be decreased by 1. */ TEST_F(QueueTest, DequeueSingleElementWithNotEmptyQueue) { - auto t1 = create_task(0, nullptr, nullptr, nullptr, nullptr); - auto t2 = create_task(1, nullptr, nullptr, nullptr, nullptr); - auto t3 = create_task(2, nullptr, nullptr, nullptr, nullptr); + auto t1 = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); + auto t2 = create_task(1, nullptr, nullptr, 0, nullptr, nullptr); + auto t3 = create_task(2, nullptr, nullptr, 0, nullptr, nullptr); enqueue(queue_, t1); enqueue(queue_, t2); @@ -163,10 +163,10 @@ TEST_F(QueueTest, DequeueSingleElementWithNotEmptyQueue) { * the operations. */ TEST_F(QueueTest, DequeueMultipleElementsWithNotEmptyQueue) { - auto t1 = create_task(0, nullptr, nullptr, nullptr, nullptr); - auto t2 = create_task(1, nullptr, nullptr, nullptr, nullptr); - auto t3 = create_task(2, nullptr, nullptr, nullptr, nullptr); - auto t4 = create_task(3, nullptr, nullptr, nullptr, nullptr); + auto t1 = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); + auto t2 = create_task(1, nullptr, nullptr, 0, nullptr, nullptr); + auto t3 = create_task(2, nullptr, nullptr, 0, nullptr, nullptr); + auto t4 = create_task(3, nullptr, nullptr, 0, nullptr, nullptr); task_t *ret; enqueue(queue_, t1); From 5f06c98dde477ef8f115382c7b3bd6de8aaa4bd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Sun, 30 Nov 2025 13:00:36 -0300 Subject: [PATCH 17/63] lib: fixing compiling errors --- samples/c/main.c | 5 ++++- samples/cpp/main.cc | 8 ++++---- src/pool_day.c | 1 + test/src/pool_day_test.cc | 2 +- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/samples/c/main.c b/samples/c/main.c index 3b3cc02..92deb2f 100644 --- a/samples/c/main.c +++ b/samples/c/main.c @@ -6,6 +6,8 @@ #include "pool_day.h" +#define DUMMY_TASK_ID 0 + void task_start_callback(uint32_t tid) { printf("task '%u' starting...\n", tid); } @@ -38,7 +40,8 @@ int main(void) { } char str[] = "foo"; - task_t *task = create_task(func, (void *)str, sizeof(char) * strlen(str) + 1, + task_t *task = create_task(DUMMY_TASK_ID, func, (void *)str, + sizeof(char) * strlen(str) + 1, task_start_callback, task_end_callback); assert(enqueue_task(pool, task) == POOL_DAY_SUCCESS); diff --git a/samples/cpp/main.cc b/samples/cpp/main.cc index fbe583c..43f8d04 100644 --- a/samples/cpp/main.cc +++ b/samples/cpp/main.cc @@ -8,10 +8,10 @@ class Foo { public: Foo() : pool_{create_pool(2)}, - t1_{create_task(Foo::Cb1, (void *)"hello", - sizeof(char) * std::strlen("hello") + 1), nullptr, - nullptr}, - t2_{create_task(Foo::Cb2, (void *)"hi", + t1_{create_task(0, Foo::Cb1, (void *)"hello", + sizeof(char) * std::strlen("hello") + 1, nullptr, + nullptr)}, + t2_{create_task(1, Foo::Cb2, (void *)"hi", sizeof(char) * std::strlen("hi") + 1, nullptr, nullptr)} { } diff --git a/src/pool_day.c b/src/pool_day.c index de102c3..36902a3 100644 --- a/src/pool_day.c +++ b/src/pool_day.c @@ -185,6 +185,7 @@ void *wait_task_finish(const pool_day_t pool, task_t *task) { ret = task->ret_val; + sem_destroy(&task->ready); if (task->param) { free(task->param); } diff --git a/test/src/pool_day_test.cc b/test/src/pool_day_test.cc index 6712063..1a70f83 100644 --- a/test/src/pool_day_test.cc +++ b/test/src/pool_day_test.cc @@ -305,7 +305,7 @@ TEST_F(PoolDayTest, ExecuteTaskWithEndCallbackOnly) { TEST_F(PoolDayTest, ExecuteTaskWithParameterWithSuccess) { char param[]{ "param" }; char ret_val[]{ "hello, i'm the return of the task" }; - auto task = create_task(CbWrapper::TaskCb, param, + auto task = create_task(0, CbWrapper::TaskCb, param, sizeof(char) * strlen(param) + 1, nullptr, nullptr); enqueue_task(pool_, task); From 3754fbf76d4b9dcfcfff9d48a243ed8096ac3c33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Fri, 21 Nov 2025 20:21:55 -0300 Subject: [PATCH 18/63] lib: checking if the provided task if bounded to the provided pool on wait_task_finish function --- include/internal/errors.h | 3 ++- include/internal/task.h | 8 ++++++ include/pool_day.h | 7 +++--- samples/c/main.c | 7 +++--- samples/cpp/main.cc | 7 ++++-- src/pool_day.c | 27 ++++++++++++--------- src/queue.c | 2 ++ src/task.c | 12 +++++++++ test/src/pool_day_test.cc | 51 ++++++++++++++++++++++++++++++++++----- 9 files changed, 97 insertions(+), 27 deletions(-) diff --git a/include/internal/errors.h b/include/internal/errors.h index 1f48c5c..aa24daf 100644 --- a/include/internal/errors.h +++ b/include/internal/errors.h @@ -28,7 +28,8 @@ */ typedef enum { POOL_DAY_SUCCESS, - POOL_DAY_ERROR_NULL_PARAM + POOL_DAY_ERROR_NULL_PARAM, + POOL_DAY_ERROR_TASK_ALREADY_BOUND } pool_day_retcode_t; #endif // ERRORS_H_ diff --git a/include/internal/task.h b/include/internal/task.h index d5e97b5..3d1325d 100644 --- a/include/internal/task.h +++ b/include/internal/task.h @@ -56,6 +56,7 @@ struct task { void *(*task)(void *); //!< Task callback. void *param; //!< Parameter of the task callback. void *ret_val; //!< Task return value. + void *bound_pool; //!< Pointer to the pool which the task is bound to. void (*on_task_start)(uint32_t); //!< Callback executed when the task starts. void (*on_task_end)(uint32_t, void *); //!< Callback executed when the task ends. sem_t ready; //!< Task's semaphore. @@ -82,4 +83,11 @@ task_t *create_task(uint32_t id, void *(*task)(void *), void *param, size_t param_size, void (*start_cb)(uint32_t), void (*end_cb)(uint32_t, void *)); +/** + * @brief Destroy a task. + * + * @param[in] task Pointer to the task to be destroyed. + */ +void destroy_task(task_t *task); + #endif // TASK_H_ diff --git a/include/pool_day.h b/include/pool_day.h index 73eb72a..6545144 100644 --- a/include/pool_day.h +++ b/include/pool_day.h @@ -86,20 +86,21 @@ uint32_t queued_tasks(pool_day_t pool); * * enqueue_task(pool, t); * - * void *ret = wait_task_finish(pool, t); + * void *ret = get_task_result(pool, t); + * + * destroy_task(t); * destroy_pool(&pool); * } * @endcode * * @note This function blocks the current thread. - * @note The task is released after the execution of this function. * * @param[in] pool Handle to the thread pool. * @param[in] task Task handler. * * @return The return value of the task. */ -void *wait_task_finish(pool_day_t pool, task_t *task); +void *get_task_result(pool_day_t pool, task_t *task); /** * @brief Abort the execution of incoming tasks. diff --git a/samples/c/main.c b/samples/c/main.c index 92deb2f..9cef281 100644 --- a/samples/c/main.c +++ b/samples/c/main.c @@ -45,11 +45,12 @@ int main(void) { task_start_callback, task_end_callback); assert(enqueue_task(pool, task) == POOL_DAY_SUCCESS); - char *ret = (char *)wait_task_finish(pool, task); - - destroy_pool(&pool); + char *ret = (char *)get_task_result(pool, task); printf("result = %s\n", ret); + + destroy_pool(&pool); + destroy_task(task); free(ret); exit(EXIT_SUCCESS); diff --git a/samples/cpp/main.cc b/samples/cpp/main.cc index 43f8d04..373b68c 100644 --- a/samples/cpp/main.cc +++ b/samples/cpp/main.cc @@ -20,8 +20,11 @@ class Foo { enqueue_task(pool_, t1_); enqueue_task(pool_, t2_); - wait_task_finish(pool_, t1_); - wait_task_finish(pool_, t2_); + get_task_result(pool_, t1_); + get_task_result(pool_, t2_); + + destroy_task(t1_); + destroy_task(t2_); } ~Foo() { diff --git a/src/pool_day.c b/src/pool_day.c index 36902a3..7670053 100644 --- a/src/pool_day.c +++ b/src/pool_day.c @@ -66,6 +66,14 @@ pool_day_retcode_t enqueue_task(pool_day_t pool, task_t *task) { return POOL_DAY_ERROR_NULL_PARAM; } + if (task->bound_pool) { + POOL_DAY_ERROR("task is already bounded to an existing pool"); + return POOL_DAY_ERROR_TASK_ALREADY_BOUND; + } + + task->bound_pool = (void *)pool; + task->ret_val = NULL; + enqueue(pool->tasks, task); sem_post(&pool->lock); @@ -169,27 +177,22 @@ uint32_t queued_tasks(pool_day_t pool) { } // cppcheck-suppress unusedFunction -void *wait_task_finish(const pool_day_t pool, task_t *task) { - void *ret; - +void *get_task_result(const pool_day_t pool, task_t *task) { if (!pool || !task) { POOL_DAY_ERROR("null pool handle or task"); return NULL; } - // TODO: should we check if the task belongs to the pool? + if (task->bound_pool != pool) { + POOL_DAY_ERROR("task is not bounded to the pool"); + return NULL; + } POOL_DAY_INFO("waiting for the finish of the task"); sem_wait(&task->ready); POOL_DAY_INFO("task finished"); - ret = task->ret_val; - - sem_destroy(&task->ready); - if (task->param) { - free(task->param); - } - free(task); + task->bound_pool = NULL; - return ret; + return task->ret_val; } diff --git a/src/queue.c b/src/queue.c index 089f989..ed72d31 100644 --- a/src/queue.c +++ b/src/queue.c @@ -88,7 +88,9 @@ void destroy_queue(task_queue_t *queue) { for_each_task_safe(curr, queue) { task_t *node = __dequeue(queue); + // TODO: call destroy_task? sem_destroy(&node->ready); + if (node->param) { free(node->param); } diff --git a/src/task.c b/src/task.c index 8c77a97..0e0d6d3 100644 --- a/src/task.c +++ b/src/task.c @@ -26,3 +26,15 @@ task_t *create_task(uint32_t id, void *(*task)(void *), void *param, return node; } + +// cppcheck-suppress unusedFunction +void destroy_task(task_t *task) { + if (task) { + sem_destroy(&task->ready); + + if (task->param) { + free(task->param); + } + free(task); + } +} diff --git a/test/src/pool_day_test.cc b/test/src/pool_day_test.cc index 1a70f83..ecb57c5 100644 --- a/test/src/pool_day_test.cc +++ b/test/src/pool_day_test.cc @@ -60,6 +60,7 @@ TEST_F(PoolDayTest, EnqueueSingleTaskWithPoolEmpty) { EXPECT_EQ(queued_tasks(pool_), 0); EXPECT_EQ(enqueue_task(pool_, task), POOL_DAY_SUCCESS); EXPECT_EQ(queued_tasks(pool_), 1); + EXPECT_EQ(task->bound_pool, (void *)pool_); } /** @@ -82,6 +83,12 @@ TEST_F(PoolDayTest, EnqueueTaskWithPoolNotEmpty) { EXPECT_EQ(enqueue_task(pool_, t5), POOL_DAY_SUCCESS); EXPECT_EQ(queued_tasks(pool_), 5); + + EXPECT_EQ(t1->bound_pool, (void *)pool_); + EXPECT_EQ(t2->bound_pool, (void *)pool_); + EXPECT_EQ(t3->bound_pool, (void *)pool_); + EXPECT_EQ(t4->bound_pool, (void *)pool_); + EXPECT_EQ(t5->bound_pool, (void *)pool_); } /** @@ -106,6 +113,21 @@ TEST_F(PoolDayTest, EnqueueTaskWithNullTask) { free(task); } +/** + * @brief Given we have a valid pool and a task already bound to a pool, when we + * try to enqueue it, then nothing must happen and the suitable error code + * must be returned. + */ +TEST_F(PoolDayTest, EnqueueTaskAlreadyBound) { + auto another_pool = create_pool(1); + auto task = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); + + EXPECT_EQ(enqueue_task(pool_, task), POOL_DAY_SUCCESS); + EXPECT_EQ(enqueue_task(another_pool, task), POOL_DAY_ERROR_TASK_ALREADY_BOUND); + + destroy_pool(&another_pool); +} + /** * @brief Given we have a pool with no tasks, when we try to get the number of * queued tasks of the pool, then 0 must be returned. @@ -362,7 +384,7 @@ TEST_F(PoolDayTest, AbortTasksWithNullPoolHandle) { * @brief Given we have an enqueued task, when we wait for the finish of the * task, then the correct return value must be returned with success. */ -TEST_F(PoolDayTest, WaitTaskFinish) { +TEST_F(PoolDayTest, GetTaskResultWithSuccess) { int ret_val{1234}; char param[]{ "param" }; auto task = create_task(0, CbWrapper::TaskCb, param, @@ -387,18 +409,22 @@ TEST_F(PoolDayTest, WaitTaskFinish) { thread_func(pool_); } - auto ret = reinterpret_cast(wait_task_finish(pool_, task)); + auto ret = reinterpret_cast(get_task_result(pool_, task)); EXPECT_EQ(*ret, ret_val); + EXPECT_EQ(task->bound_pool, nullptr); + + // cleanup + free(task); } /** * @brief Given we have a task and a null pool handle, when we wait for * the finish of the task, then null must be returned. */ -TEST_F(PoolDayTest, WaitTaskFinishWithNullPoolHandle) { +TEST_F(PoolDayTest, GetTaskResultWithNullPoolHandle) { auto task = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); - EXPECT_EQ(wait_task_finish(nullptr, task), nullptr); + EXPECT_EQ(get_task_result(nullptr, task), nullptr); // cleanup free(task); @@ -408,6 +434,19 @@ TEST_F(PoolDayTest, WaitTaskFinishWithNullPoolHandle) { * @brief Given we have a null task and a valid pool handle, when we wait for * the finish of the task, then null must be returned. */ -TEST_F(PoolDayTest, WaitTaskFinishWithNullPoolTask) { - EXPECT_EQ(wait_task_finish(pool_, nullptr), nullptr); +TEST_F(PoolDayTest, GetTaskResultWithNullPoolTask) { + EXPECT_EQ(get_task_result(pool_, nullptr), nullptr); +} + +/** + * @brief Given we have a task not bound to the pool, when we wait for + * the finish of the task, then null must be returned. + */ +TEST_F(PoolDayTest, GetTaskResultWithWithUnboundTask) { + auto task = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); + + EXPECT_EQ(get_task_result(pool_, task), nullptr); + + // cleanup + free(task); } From 11f63f0dbcf4f27eaebef0bdb89f8b3638a89e70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Sun, 30 Nov 2025 17:27:06 -0300 Subject: [PATCH 19/63] lib: fixing valgrind issues on unit tests --- src/pool_day.c | 6 +++--- test/src/pool_day_test.cc | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/pool_day.c b/src/pool_day.c index 7670053..7530241 100644 --- a/src/pool_day.c +++ b/src/pool_day.c @@ -67,7 +67,7 @@ pool_day_retcode_t enqueue_task(pool_day_t pool, task_t *task) { } if (task->bound_pool) { - POOL_DAY_ERROR("task is already bounded to an existing pool"); + POOL_DAY_ERROR("task is already bound to an existing pool"); return POOL_DAY_ERROR_TASK_ALREADY_BOUND; } @@ -184,7 +184,7 @@ void *get_task_result(const pool_day_t pool, task_t *task) { } if (task->bound_pool != pool) { - POOL_DAY_ERROR("task is not bounded to the pool"); + POOL_DAY_ERROR("task is not bound to the pool"); return NULL; } @@ -192,7 +192,7 @@ void *get_task_result(const pool_day_t pool, task_t *task) { sem_wait(&task->ready); POOL_DAY_INFO("task finished"); - task->bound_pool = NULL; + task->bound_pool = NULL; // TODO: this must occur on async flow as well?decorator? return task->ret_val; } diff --git a/test/src/pool_day_test.cc b/test/src/pool_day_test.cc index ecb57c5..a2e8724 100644 --- a/test/src/pool_day_test.cc +++ b/test/src/pool_day_test.cc @@ -414,6 +414,7 @@ TEST_F(PoolDayTest, GetTaskResultWithSuccess) { EXPECT_EQ(task->bound_pool, nullptr); // cleanup + free(task->param); free(task); } From e1225a0d92b5139631f049739a386fd45facc3a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Sun, 30 Nov 2025 20:01:37 -0300 Subject: [PATCH 20/63] lib: removing the pool parameter on get_task_result function --- include/internal/errors.h | 3 +-- include/internal/task.h | 1 - include/pool_day.h | 5 ++--- samples/c/main.c | 2 +- samples/cpp/main.cc | 4 ++-- src/pool_day.c | 20 ++++---------------- test/src/pool_day_test.cc | 28 +++------------------------- 7 files changed, 13 insertions(+), 50 deletions(-) diff --git a/include/internal/errors.h b/include/internal/errors.h index aa24daf..1f48c5c 100644 --- a/include/internal/errors.h +++ b/include/internal/errors.h @@ -28,8 +28,7 @@ */ typedef enum { POOL_DAY_SUCCESS, - POOL_DAY_ERROR_NULL_PARAM, - POOL_DAY_ERROR_TASK_ALREADY_BOUND + POOL_DAY_ERROR_NULL_PARAM } pool_day_retcode_t; #endif // ERRORS_H_ diff --git a/include/internal/task.h b/include/internal/task.h index 3d1325d..eed0d2e 100644 --- a/include/internal/task.h +++ b/include/internal/task.h @@ -56,7 +56,6 @@ struct task { void *(*task)(void *); //!< Task callback. void *param; //!< Parameter of the task callback. void *ret_val; //!< Task return value. - void *bound_pool; //!< Pointer to the pool which the task is bound to. void (*on_task_start)(uint32_t); //!< Callback executed when the task starts. void (*on_task_end)(uint32_t, void *); //!< Callback executed when the task ends. sem_t ready; //!< Task's semaphore. diff --git a/include/pool_day.h b/include/pool_day.h index 6545144..6055399 100644 --- a/include/pool_day.h +++ b/include/pool_day.h @@ -86,7 +86,7 @@ uint32_t queued_tasks(pool_day_t pool); * * enqueue_task(pool, t); * - * void *ret = get_task_result(pool, t); + * void *ret = get_task_result(t); * * destroy_task(t); * destroy_pool(&pool); @@ -95,12 +95,11 @@ uint32_t queued_tasks(pool_day_t pool); * * @note This function blocks the current thread. * - * @param[in] pool Handle to the thread pool. * @param[in] task Task handler. * * @return The return value of the task. */ -void *get_task_result(pool_day_t pool, task_t *task); +void *get_task_result(task_t *task); /** * @brief Abort the execution of incoming tasks. diff --git a/samples/c/main.c b/samples/c/main.c index 9cef281..1d9afae 100644 --- a/samples/c/main.c +++ b/samples/c/main.c @@ -45,7 +45,7 @@ int main(void) { task_start_callback, task_end_callback); assert(enqueue_task(pool, task) == POOL_DAY_SUCCESS); - char *ret = (char *)get_task_result(pool, task); + char *ret = (char *)get_task_result(task); printf("result = %s\n", ret); diff --git a/samples/cpp/main.cc b/samples/cpp/main.cc index 373b68c..734c04a 100644 --- a/samples/cpp/main.cc +++ b/samples/cpp/main.cc @@ -20,8 +20,8 @@ class Foo { enqueue_task(pool_, t1_); enqueue_task(pool_, t2_); - get_task_result(pool_, t1_); - get_task_result(pool_, t2_); + get_task_result(t1_); + get_task_result(t2_); destroy_task(t1_); destroy_task(t2_); diff --git a/src/pool_day.c b/src/pool_day.c index 7530241..c641bd5 100644 --- a/src/pool_day.c +++ b/src/pool_day.c @@ -66,13 +66,8 @@ pool_day_retcode_t enqueue_task(pool_day_t pool, task_t *task) { return POOL_DAY_ERROR_NULL_PARAM; } - if (task->bound_pool) { - POOL_DAY_ERROR("task is already bound to an existing pool"); - return POOL_DAY_ERROR_TASK_ALREADY_BOUND; - } - - task->bound_pool = (void *)pool; task->ret_val = NULL; + sem_init(&task->ready, 0, 0); // TODO: necessary? enqueue(pool->tasks, task); sem_post(&pool->lock); @@ -177,14 +172,9 @@ uint32_t queued_tasks(pool_day_t pool) { } // cppcheck-suppress unusedFunction -void *get_task_result(const pool_day_t pool, task_t *task) { - if (!pool || !task) { - POOL_DAY_ERROR("null pool handle or task"); - return NULL; - } - - if (task->bound_pool != pool) { - POOL_DAY_ERROR("task is not bound to the pool"); +void *get_task_result(task_t *task) { + if (!task) { + POOL_DAY_ERROR("null task provided"); return NULL; } @@ -192,7 +182,5 @@ void *get_task_result(const pool_day_t pool, task_t *task) { sem_wait(&task->ready); POOL_DAY_INFO("task finished"); - task->bound_pool = NULL; // TODO: this must occur on async flow as well?decorator? - return task->ret_val; } diff --git a/test/src/pool_day_test.cc b/test/src/pool_day_test.cc index a2e8724..061e910 100644 --- a/test/src/pool_day_test.cc +++ b/test/src/pool_day_test.cc @@ -60,7 +60,6 @@ TEST_F(PoolDayTest, EnqueueSingleTaskWithPoolEmpty) { EXPECT_EQ(queued_tasks(pool_), 0); EXPECT_EQ(enqueue_task(pool_, task), POOL_DAY_SUCCESS); EXPECT_EQ(queued_tasks(pool_), 1); - EXPECT_EQ(task->bound_pool, (void *)pool_); } /** @@ -83,12 +82,6 @@ TEST_F(PoolDayTest, EnqueueTaskWithPoolNotEmpty) { EXPECT_EQ(enqueue_task(pool_, t5), POOL_DAY_SUCCESS); EXPECT_EQ(queued_tasks(pool_), 5); - - EXPECT_EQ(t1->bound_pool, (void *)pool_); - EXPECT_EQ(t2->bound_pool, (void *)pool_); - EXPECT_EQ(t3->bound_pool, (void *)pool_); - EXPECT_EQ(t4->bound_pool, (void *)pool_); - EXPECT_EQ(t5->bound_pool, (void *)pool_); } /** @@ -123,7 +116,6 @@ TEST_F(PoolDayTest, EnqueueTaskAlreadyBound) { auto task = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); EXPECT_EQ(enqueue_task(pool_, task), POOL_DAY_SUCCESS); - EXPECT_EQ(enqueue_task(another_pool, task), POOL_DAY_ERROR_TASK_ALREADY_BOUND); destroy_pool(&another_pool); } @@ -409,34 +401,20 @@ TEST_F(PoolDayTest, GetTaskResultWithSuccess) { thread_func(pool_); } - auto ret = reinterpret_cast(get_task_result(pool_, task)); + auto ret = reinterpret_cast(get_task_result(task)); EXPECT_EQ(*ret, ret_val); - EXPECT_EQ(task->bound_pool, nullptr); // cleanup free(task->param); free(task); } -/** - * @brief Given we have a task and a null pool handle, when we wait for - * the finish of the task, then null must be returned. - */ -TEST_F(PoolDayTest, GetTaskResultWithNullPoolHandle) { - auto task = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); - - EXPECT_EQ(get_task_result(nullptr, task), nullptr); - - // cleanup - free(task); -} - /** * @brief Given we have a null task and a valid pool handle, when we wait for * the finish of the task, then null must be returned. */ TEST_F(PoolDayTest, GetTaskResultWithNullPoolTask) { - EXPECT_EQ(get_task_result(pool_, nullptr), nullptr); + EXPECT_EQ(get_task_result(nullptr), nullptr); } /** @@ -446,7 +424,7 @@ TEST_F(PoolDayTest, GetTaskResultWithNullPoolTask) { TEST_F(PoolDayTest, GetTaskResultWithWithUnboundTask) { auto task = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); - EXPECT_EQ(get_task_result(pool_, task), nullptr); + EXPECT_EQ(get_task_result(task), nullptr); // cleanup free(task); From 5da0002c9d63a99c3e7d5cdf2d46583ff4ea0b28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Fri, 5 Dec 2025 21:55:49 -0300 Subject: [PATCH 21/63] lib: moving for_each_task and for_each_task_safe macros to queue module --- include/internal/task.h | 22 ++-------------------- src/queue.c | 29 +++++++++++++++++++++-------- 2 files changed, 23 insertions(+), 28 deletions(-) diff --git a/include/internal/task.h b/include/internal/task.h index eed0d2e..fcd6a67 100644 --- a/include/internal/task.h +++ b/include/internal/task.h @@ -25,27 +25,9 @@ #define TASK_H_ #include +#include #include -/** - * @brief For-each macro implementation. - * - * @details It can be used to iterate over the list. - */ -#define for_each_task(curr, list) \ - for (struct task *curr = list->head; curr; curr = curr->prev) - -/** - * @brief Safe implementation of for-each macro. - * - * @details It can be used to delete elements of the list while iterating over - * it. - */ -#define for_each_task_safe(curr, list) \ - for (struct task *curr = list->head, *tmp = curr ? curr->prev : NULL; \ - curr; \ - curr = tmp, tmp = tmp ? tmp->prev : NULL) - /** * @brief Pool task definition. */ @@ -62,7 +44,7 @@ struct task { }; typedef struct task task_t; //!< Structure representing an item on the task - // list. + // queue. /** * @brief Create a new task. * diff --git a/src/queue.c b/src/queue.c index ed72d31..2c542b8 100644 --- a/src/queue.c +++ b/src/queue.c @@ -5,6 +5,25 @@ #include "internal/task.h" #include "internal/utils.h" +/** + * @brief For-each macro implementation. + * + * @details It can be used to iterate over the queue. + */ +#define for_each_task(curr, queue) \ + for (struct task *curr = queue->head; curr; curr = curr->prev) + +/** + * @brief Safe implementation of for-each macro. + * + * @details It can be used to delete elements of the queue while iterating over + * it. + */ +#define for_each_task_safe(curr, queue) \ + for (struct task *curr = queue->head, *tmp = curr ? curr->prev : NULL; \ + curr; \ + curr = tmp, tmp = tmp ? tmp->prev : NULL) + /** * @brief Task queue definition. */ @@ -46,7 +65,7 @@ void enqueue(task_queue_t *queue, task_t *elem) { static task_t *__dequeue(task_queue_t *queue) { task_t *to_del = queue->head; - // if the list is not empty + // if the queue is not empty if (to_del) { queue->head = to_del->prev; // if the queue has more than one element @@ -88,13 +107,7 @@ void destroy_queue(task_queue_t *queue) { for_each_task_safe(curr, queue) { task_t *node = __dequeue(queue); - // TODO: call destroy_task? - sem_destroy(&node->ready); - - if (node->param) { - free(node->param); - } - free(node); + destroy_task(node); } }) From d9ba28d7b527b7d5a107832fb6e3584b38b9fee7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Sat, 6 Dec 2025 00:07:03 -0300 Subject: [PATCH 22/63] lib: preventing double free issue on task destruction --- include/internal/task.h | 5 +++-- samples/c/main.c | 2 +- src/pool_day.c | 5 +++-- src/task.c | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/include/internal/task.h b/include/internal/task.h index fcd6a67..064ed73 100644 --- a/include/internal/task.h +++ b/include/internal/task.h @@ -33,6 +33,7 @@ */ struct task { uint32_t id; //!< Task identifier. + bool executed; //!< Flag indicating whether the task has been executed. struct task *next; //!< Next element of the current instance. struct task *prev; //!< Previous element of the current instance. void *(*task)(void *); //!< Task callback. @@ -43,8 +44,8 @@ struct task { sem_t ready; //!< Task's semaphore. }; -typedef struct task task_t; //!< Structure representing an item on the task - // queue. +typedef struct task task_t; //!< Structure representing an item on the task queue. + /** * @brief Create a new task. * diff --git a/samples/c/main.c b/samples/c/main.c index 1d9afae..b421e93 100644 --- a/samples/c/main.c +++ b/samples/c/main.c @@ -49,8 +49,8 @@ int main(void) { printf("result = %s\n", ret); - destroy_pool(&pool); destroy_task(task); + destroy_pool(&pool); free(ret); exit(EXIT_SUCCESS); diff --git a/src/pool_day.c b/src/pool_day.c index c641bd5..d5aca9c 100644 --- a/src/pool_day.c +++ b/src/pool_day.c @@ -46,6 +46,7 @@ __static void *thread_func(void *param) { POOL_DAY_INFO("thread '0x%x' finished the task", pthread_self()); entry->ret_val = ret; + entry->executed = true; if (entry->on_task_end) { entry->on_task_end(entry->id, entry->ret_val); } @@ -62,12 +63,12 @@ __static void *thread_func(void *param) { // cppcheck-suppress unusedFunction pool_day_retcode_t enqueue_task(pool_day_t pool, task_t *task) { if (!pool || !task) { - POOL_DAY_ERROR("null parameter"); + POOL_DAY_ERROR("null parameter supplied"); return POOL_DAY_ERROR_NULL_PARAM; } task->ret_val = NULL; - sem_init(&task->ready, 0, 0); // TODO: necessary? + task->executed = false; enqueue(pool->tasks, task); sem_post(&pool->lock); diff --git a/src/task.c b/src/task.c index 0e0d6d3..e714517 100644 --- a/src/task.c +++ b/src/task.c @@ -29,7 +29,7 @@ task_t *create_task(uint32_t id, void *(*task)(void *), void *param, // cppcheck-suppress unusedFunction void destroy_task(task_t *task) { - if (task) { + if (task && task->executed) { sem_destroy(&task->ready); if (task->param) { From 01c5e071b1dbe5088ec9ac776bb678c1a77edbfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Sat, 6 Dec 2025 12:33:00 -0300 Subject: [PATCH 23/63] lib: fixing valgrind issues --- include/internal/task.h | 2 +- src/pool_day.c | 9 +++--- src/task.c | 5 +++- test/src/pool_day_test.cc | 63 ++++++++++++++++++++++++++------------- 4 files changed, 52 insertions(+), 27 deletions(-) diff --git a/include/internal/task.h b/include/internal/task.h index 064ed73..d3eb929 100644 --- a/include/internal/task.h +++ b/include/internal/task.h @@ -33,7 +33,7 @@ */ struct task { uint32_t id; //!< Task identifier. - bool executed; //!< Flag indicating whether the task has been executed. + bool is_orphan; //!< Flag indicating whether the task is orphaned. struct task *next; //!< Next element of the current instance. struct task *prev; //!< Previous element of the current instance. void *(*task)(void *); //!< Task callback. diff --git a/src/pool_day.c b/src/pool_day.c index d5aca9c..3e08fe7 100644 --- a/src/pool_day.c +++ b/src/pool_day.c @@ -15,8 +15,7 @@ */ struct pool_day { uint32_t size; //!< Size of the pool. - bool must_stop; //!< Flag indicating wheter all threads must - // stop its execution. + bool must_stop; //!< Flag indicating wheter all threads must stop its execution. sem_t lock; //!< Pool's semaphore. pthread_t *threads; //!< Threads whose makes part of the pool. task_queue_t *tasks; //!< Pool's queued tasks. @@ -37,6 +36,8 @@ __static void *thread_func(void *param) { task_t *entry = dequeue(pool->tasks); if (entry) { + entry->is_orphan = true; + if (entry->on_task_start) { entry->on_task_start(entry->id); } @@ -46,7 +47,7 @@ __static void *thread_func(void *param) { POOL_DAY_INFO("thread '0x%x' finished the task", pthread_self()); entry->ret_val = ret; - entry->executed = true; + if (entry->on_task_end) { entry->on_task_end(entry->id, entry->ret_val); } @@ -68,7 +69,7 @@ pool_day_retcode_t enqueue_task(pool_day_t pool, task_t *task) { } task->ret_val = NULL; - task->executed = false; + task->is_orphan = false; enqueue(pool->tasks, task); sem_post(&pool->lock); diff --git a/src/task.c b/src/task.c index e714517..d8c2cb2 100644 --- a/src/task.c +++ b/src/task.c @@ -15,6 +15,7 @@ task_t *create_task(uint32_t id, void *(*task)(void *), void *param, node->task = task; node->on_task_start = start_cb; node->on_task_end = end_cb; + node->is_orphan = true; if (param) { node->param = malloc(param_size); @@ -29,7 +30,9 @@ task_t *create_task(uint32_t id, void *(*task)(void *), void *param, // cppcheck-suppress unusedFunction void destroy_task(task_t *task) { - if (task && task->executed) { + // if the task is orphaned, it means that the user will take care of its release, + // otherwise it is managed by the pool + if (task && task->is_orphan) { sem_destroy(&task->ready); if (task->param) { diff --git a/test/src/pool_day_test.cc b/test/src/pool_day_test.cc index 061e910..577278f 100644 --- a/test/src/pool_day_test.cc +++ b/test/src/pool_day_test.cc @@ -60,6 +60,9 @@ TEST_F(PoolDayTest, EnqueueSingleTaskWithPoolEmpty) { EXPECT_EQ(queued_tasks(pool_), 0); EXPECT_EQ(enqueue_task(pool_, task), POOL_DAY_SUCCESS); EXPECT_EQ(queued_tasks(pool_), 1); + + // to force the task destruction on destroy_pool call + task->is_orphan = true; } /** @@ -82,6 +85,13 @@ TEST_F(PoolDayTest, EnqueueTaskWithPoolNotEmpty) { EXPECT_EQ(enqueue_task(pool_, t5), POOL_DAY_SUCCESS); EXPECT_EQ(queued_tasks(pool_), 5); + + // to force the task destruction on destroy_pool call + t1->is_orphan = true; + t2->is_orphan = true; + t3->is_orphan = true; + t4->is_orphan = true; + t5->is_orphan = true; } /** @@ -118,6 +128,9 @@ TEST_F(PoolDayTest, EnqueueTaskAlreadyBound) { EXPECT_EQ(enqueue_task(pool_, task), POOL_DAY_SUCCESS); destroy_pool(&another_pool); + + // to force the task destruction on destroy_pool call + task->is_orphan = true; } /** @@ -133,13 +146,14 @@ TEST_F(PoolDayTest, GetQueuedTasksCountWithNoTasks) { * the number of queued tasks in the pool, then 1 must be returned. */ TEST_F(PoolDayTest, GetQueuedTasksCountWithSingleTask) { - { - auto task = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); + auto task = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); - enqueue_task(pool_, task); - } + enqueue_task(pool_, task); EXPECT_EQ(queued_tasks(pool_), 1); + + // to force the task destruction on destroy_pool call + task->is_orphan = true; } /** @@ -148,21 +162,26 @@ TEST_F(PoolDayTest, GetQueuedTasksCountWithSingleTask) { * returned. */ TEST_F(PoolDayTest, GetQueuedTasksCountWithSeveralTasks) { - { - auto t1 = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); - auto t2 = create_task(1, nullptr, nullptr, 0, nullptr, nullptr); - auto t3 = create_task(2, nullptr, nullptr, 0, nullptr, nullptr); - auto t4 = create_task(3, nullptr, nullptr, 0, nullptr, nullptr); - auto t5 = create_task(4, nullptr, nullptr, 0, nullptr, nullptr); - - enqueue_task(pool_, t1); - enqueue_task(pool_, t2); - enqueue_task(pool_, t3); - enqueue_task(pool_, t4); - enqueue_task(pool_, t5); - } + auto t1 = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); + auto t2 = create_task(1, nullptr, nullptr, 0, nullptr, nullptr); + auto t3 = create_task(2, nullptr, nullptr, 0, nullptr, nullptr); + auto t4 = create_task(3, nullptr, nullptr, 0, nullptr, nullptr); + auto t5 = create_task(4, nullptr, nullptr, 0, nullptr, nullptr); + + enqueue_task(pool_, t1); + enqueue_task(pool_, t2); + enqueue_task(pool_, t3); + enqueue_task(pool_, t4); + enqueue_task(pool_, t5); EXPECT_EQ(queued_tasks(pool_), 5); + + // to force the task destruction on destroy_pool call + t1->is_orphan = true; + t2->is_orphan = true; + t3->is_orphan = true; + t4->is_orphan = true; + t5->is_orphan = true; } /** @@ -351,10 +370,9 @@ TEST_F(PoolDayTest, ExecuteTaskWithParameterWithSuccess) { * task is scheduled for execution, then the task's callback must not be called. */ TEST_F(PoolDayTest, ExecuteTaskWithMustStopSet) { - { - auto task = create_task(0, CbWrapper::TaskCb, nullptr, 0, nullptr, nullptr); - enqueue_task(pool_, task); - } + auto task = create_task(0, CbWrapper::TaskCb, nullptr, 0, nullptr, nullptr); + + enqueue_task(pool_, task); EXPECT_CALL(CbWrapper::mock(), TaskCb(_)).Times(0); @@ -362,6 +380,9 @@ TEST_F(PoolDayTest, ExecuteTaskWithMustStopSet) { auto ret = thread_func(pool_); EXPECT_EQ(ret, nullptr); + + // to force the task destruction on destroy_pool call + task->is_orphan = true; } /** From ded56dfe05335714eaa8c9adebabde6bcc4c1b10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Sat, 6 Dec 2025 12:34:33 -0300 Subject: [PATCH 24/63] lib: fixing cppcheck issues --- src/task.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/task.c b/src/task.c index d8c2cb2..9192446 100644 --- a/src/task.c +++ b/src/task.c @@ -28,7 +28,6 @@ task_t *create_task(uint32_t id, void *(*task)(void *), void *param, return node; } -// cppcheck-suppress unusedFunction void destroy_task(task_t *task) { // if the task is orphaned, it means that the user will take care of its release, // otherwise it is managed by the pool From 43db6fa85a04e9c282264138c1e0ddb8de0653ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Mon, 8 Dec 2025 15:36:23 -0300 Subject: [PATCH 25/63] lib: releasing the task after its execution on async flow --- src/pool_day.c | 6 +++--- test/src/pool_day_test.cc | 20 +++++--------------- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/src/pool_day.c b/src/pool_day.c index 3e08fe7..77ddc24 100644 --- a/src/pool_day.c +++ b/src/pool_day.c @@ -47,12 +47,12 @@ __static void *thread_func(void *param) { POOL_DAY_INFO("thread '0x%x' finished the task", pthread_self()); entry->ret_val = ret; - if (entry->on_task_end) { entry->on_task_end(entry->id, entry->ret_val); + destroy_task(entry); + } else { + sem_post(&entry->ready); } - - sem_post(&entry->ready); } } diff --git a/test/src/pool_day_test.cc b/test/src/pool_day_test.cc index 577278f..a802e8f 100644 --- a/test/src/pool_day_test.cc +++ b/test/src/pool_day_test.cc @@ -230,7 +230,7 @@ TEST_F(PoolDayTest, ExecuteTaskWithNullParameterWithSuccess) { EXPECT_EQ(task->ret_val, ret_val); // cleanup - free(task); + destroy_task(task); } /** @@ -260,10 +260,6 @@ TEST_F(PoolDayTest, ExecuteTaskWithCallbacks) { auto ret = thread_func(pool_); EXPECT_EQ(ret, nullptr); - EXPECT_EQ(task->ret_val, ret_val); - - // cleanup - free(task); } /** @@ -295,7 +291,7 @@ TEST_F(PoolDayTest, ExecuteTaskWithStartCallbackOnly) { EXPECT_EQ(task->ret_val, ret_val); // cleanup - free(task); + destroy_task(task); } /** @@ -324,10 +320,6 @@ TEST_F(PoolDayTest, ExecuteTaskWithEndCallbackOnly) { auto ret = thread_func(pool_); EXPECT_EQ(ret, nullptr); - EXPECT_EQ(task->ret_val, ret_val); - - // cleanup - free(task); } /** @@ -361,8 +353,7 @@ TEST_F(PoolDayTest, ExecuteTaskWithParameterWithSuccess) { EXPECT_EQ(task->ret_val, ret_val); // cleanup - free(task->param); - free(task); + destroy_task(task); } /** @@ -426,8 +417,7 @@ TEST_F(PoolDayTest, GetTaskResultWithSuccess) { EXPECT_EQ(*ret, ret_val); // cleanup - free(task->param); - free(task); + destroy_task(task); } /** @@ -448,5 +438,5 @@ TEST_F(PoolDayTest, GetTaskResultWithWithUnboundTask) { EXPECT_EQ(get_task_result(task), nullptr); // cleanup - free(task); + destroy_task(task); } From 9e2a7f62a9c1154c5de608fba51a6bdfc4a81c32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Mon, 8 Dec 2025 17:02:45 -0300 Subject: [PATCH 26/63] lib: adding useful logs --- include/internal/logger.h | 12 +++++------ src/logger.c | 4 ++-- src/pool_day.c | 42 ++++++++++++++++++++++----------------- 3 files changed, 32 insertions(+), 26 deletions(-) diff --git a/include/internal/logger.h b/include/internal/logger.h index cf7c69e..8f30a78 100644 --- a/include/internal/logger.h +++ b/include/internal/logger.h @@ -27,17 +27,17 @@ * @brief Log severity. */ typedef enum { - POOL_DAY_INFO_INFO, - POOL_DAY_INFO_ERROR + POOL_DAY_DEBUG_SEVERITY, + POOL_DAY_ERROR_SEVERITY } pool_day_log_severity_t; #ifdef LIB_LOGGING -#define POOL_DAY_INFO(...) \ - __log_msg(POOL_DAY_INFO_INFO, __FILE__, __func__, __LINE__, __VA_ARGS__) +#define POOL_DAY_DEBUG(...) \ + __log_msg(POOL_DAY_DEBUG_SEVERITY, __FILE__, __func__, __LINE__, __VA_ARGS__) #define POOL_DAY_ERROR(...) \ - __log_msg(POOL_DAY_INFO_ERROR, __FILE__, __func__, __LINE__, __VA_ARGS__) + __log_msg(POOL_DAY_ERROR_SEVERITY, __FILE__, __func__, __LINE__, __VA_ARGS__) #else -#define POOL_DAY_INFO(...) +#define POOL_DAY_DEBUG(...) #define POOL_DAY_ERROR(...) #endif // LIB_LOGGING diff --git a/src/logger.c b/src/logger.c index 92ef974..79de3fd 100644 --- a/src/logger.c +++ b/src/logger.c @@ -7,8 +7,8 @@ #include "internal/utils.h" -#define GET_LOG_FILE(sev) (sev == POOL_DAY_INFO_INFO) ? stdout : stderr -#define GET_LOG_TAG(sev) (sev == POOL_DAY_INFO_INFO) ? "INFO" : "ERROR" +#define GET_LOG_FILE(sev) (sev == POOL_DAY_DEBUG_SEVERITY) ? stdout : stderr +#define GET_LOG_TAG(sev) (sev == POOL_DAY_DEBUG_SEVERITY) ? "DEBUG" : "ERROR" #define GET_FILE_NAME(path) strrchr(path, '/') + 1 #define LOG_FMT "%s:%d\t| %s\t| %s:" diff --git a/src/pool_day.c b/src/pool_day.c index 77ddc24..c3c70c6 100644 --- a/src/pool_day.c +++ b/src/pool_day.c @@ -27,10 +27,10 @@ __static void *thread_func(void *param) { while (!pool->must_stop) { sem_wait(&pool->lock); - POOL_DAY_INFO("thread '0x%x' woke up", pthread_self()); + POOL_DAY_DEBUG("thread '0x%x' woke up", pthread_self()); if (pool->must_stop) { - POOL_DAY_INFO("thread '0x%x' aborting...", pthread_self()); + POOL_DAY_DEBUG("thread '0x%x' aborting...", pthread_self()); break; } @@ -42,21 +42,27 @@ __static void *thread_func(void *param) { entry->on_task_start(entry->id); } - POOL_DAY_INFO("thread '0x%x' running the task", pthread_self()); + POOL_DAY_DEBUG("thread '0x%x' running the task '0x%x'", pthread_self(), + entry->id); void *ret = entry->task(entry->param); - POOL_DAY_INFO("thread '0x%x' finished the task", pthread_self()); + POOL_DAY_DEBUG("thread '0x%x' finished the task '0x%x'", pthread_self(), + entry->id); entry->ret_val = ret; if (entry->on_task_end) { + POOL_DAY_DEBUG("thread '0x%x' running async teardown for task '0x%x'", + pthread_self(), entry->id); entry->on_task_end(entry->id, entry->ret_val); destroy_task(entry); } else { + POOL_DAY_DEBUG("thread '0x%x' running sync teardown for task '0x%x'", + pthread_self(), entry->id); sem_post(&entry->ready); } } } - POOL_DAY_INFO("thread '0%x' finishing...", pthread_self()); + POOL_DAY_DEBUG("thread '0%x' finishing...", pthread_self()); return NULL; } @@ -74,7 +80,7 @@ pool_day_retcode_t enqueue_task(pool_day_t pool, task_t *task) { enqueue(pool->tasks, task); sem_post(&pool->lock); - POOL_DAY_INFO("task enqueued with success"); + POOL_DAY_DEBUG("task enqueued with success"); return POOL_DAY_SUCCESS; } @@ -83,7 +89,7 @@ pool_day_retcode_t enqueue_task(pool_day_t pool, task_t *task) { pool_day_t create_pool(uint32_t pool_size) { pool_day_t pool; - POOL_DAY_INFO("pool size: %u", pool_size); + POOL_DAY_DEBUG("pool size: %u", pool_size); if (!pool_size) { POOL_DAY_ERROR("bad pool size"); @@ -111,10 +117,10 @@ pool_day_t create_pool(uint32_t pool_size) { for (uint32_t i = 0; i < pool_size; i++) { pthread_create(&pool->threads[i], NULL, thread_func, pool); - POOL_DAY_INFO("thread '%u' created", i); + POOL_DAY_DEBUG("thread '%u' created", i); } - POOL_DAY_INFO("pool created with success"); + POOL_DAY_DEBUG("pool created with success"); return pool; } @@ -126,20 +132,20 @@ pool_day_retcode_t destroy_pool(pool_day_t *pool) { return POOL_DAY_ERROR_NULL_PARAM; } - POOL_DAY_INFO("waking up all sleeping threads"); + POOL_DAY_DEBUG("waking up all sleeping threads"); (*pool)->must_stop = true; for (uint32_t i = 0; i < (*pool)->size; i++) { - POOL_DAY_INFO("waking up thread '%u'", i); + POOL_DAY_DEBUG("waking up thread '%u'", i); sem_post(&(*pool)->lock); } - POOL_DAY_INFO("joining all threads of the pool"); + POOL_DAY_DEBUG("joining all threads of the pool"); for (uint32_t i = 0; i < (*pool)->size; i++) { - POOL_DAY_INFO("finishing thread '%u'", i); + POOL_DAY_DEBUG("finishing thread '%u'", i); pthread_join((*pool)->threads[i], NULL); - POOL_DAY_INFO("thread '%u' finished", i); + POOL_DAY_DEBUG("thread '%u' finished", i); } free((*pool)->threads); @@ -149,7 +155,7 @@ pool_day_retcode_t destroy_pool(pool_day_t *pool) { free(*pool); *pool = NULL; - POOL_DAY_INFO("pool destroyed with success"); + POOL_DAY_DEBUG("pool destroyed with success"); return POOL_DAY_SUCCESS; } @@ -163,7 +169,7 @@ pool_day_retcode_t abort_tasks(pool_day_t pool) { pool->must_stop = true; - POOL_DAY_INFO("stopping all threads"); + POOL_DAY_DEBUG("stopping all threads"); return POOL_DAY_SUCCESS; } @@ -180,9 +186,9 @@ void *get_task_result(task_t *task) { return NULL; } - POOL_DAY_INFO("waiting for the finish of the task"); + POOL_DAY_DEBUG("waiting for the finish of the task"); sem_wait(&task->ready); - POOL_DAY_INFO("task finished"); + POOL_DAY_DEBUG("task finished"); return task->ret_val; } From 2477e5b8f1b8cd09a2ed22c97668056c01fca458 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Mon, 8 Dec 2025 17:29:16 -0300 Subject: [PATCH 27/63] lib: improving log format --- CMakeLists.txt | 2 +- include/internal/logger.h | 4 ++-- src/logger.c | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e696ca9..16e2de4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,7 +41,7 @@ if(BUILD_DOCUMENTATION) endif(BUILD_DOCUMENTATION) if(ENABLE_LOGGING) - add_definitions(-DLIB_LOGGING) + add_definitions(-DENABLE_LOGGING) list(APPEND SOURCES "src/logger.c") endif(ENABLE_LOGGING) diff --git a/include/internal/logger.h b/include/internal/logger.h index 8f30a78..affd511 100644 --- a/include/internal/logger.h +++ b/include/internal/logger.h @@ -31,7 +31,7 @@ typedef enum { POOL_DAY_ERROR_SEVERITY } pool_day_log_severity_t; -#ifdef LIB_LOGGING +#ifdef ENABLE_LOGGING #define POOL_DAY_DEBUG(...) \ __log_msg(POOL_DAY_DEBUG_SEVERITY, __FILE__, __func__, __LINE__, __VA_ARGS__) #define POOL_DAY_ERROR(...) \ @@ -39,7 +39,7 @@ typedef enum { #else #define POOL_DAY_DEBUG(...) #define POOL_DAY_ERROR(...) -#endif // LIB_LOGGING +#endif // ENABLE_LOGGING /** * @brief Log a message to the screen. diff --git a/src/logger.c b/src/logger.c index 79de3fd..8b86e44 100644 --- a/src/logger.c +++ b/src/logger.c @@ -11,7 +11,7 @@ #define GET_LOG_TAG(sev) (sev == POOL_DAY_DEBUG_SEVERITY) ? "DEBUG" : "ERROR" #define GET_FILE_NAME(path) strrchr(path, '/') + 1 -#define LOG_FMT "%s:%d\t| %s\t| %s:" +#define LOG_FMT "%s | %s:%d | %s | %s\n" #define LOG_BUFFER_SIZE 4096 static pthread_mutex_t log_mutex; //!< Mutex for log operations. @@ -25,8 +25,8 @@ void __log_msg(pool_day_log_severity_t sev, const char *file_name, va_start(args, fmt); vsnprintf(log_buffer, LOG_BUFFER_SIZE, fmt, args); - fprintf(GET_LOG_FILE(sev), LOG_FMT " %s\n", GET_FILE_NAME(file_name), - line_no, func_name, GET_LOG_TAG(sev), log_buffer); + fprintf(GET_LOG_FILE(sev), LOG_FMT, GET_LOG_TAG(sev), + GET_FILE_NAME(file_name), line_no, func_name, log_buffer); va_end(args); }); From c51d86a76238f16f5f18535ac83a3bb4b34c9ccd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Mon, 15 Dec 2025 13:41:24 -0300 Subject: [PATCH 28/63] lib: adding 'param' to task callbacks --- include/internal/task.h | 24 ++++++++++++------------ samples/c/main.c | 6 ++++-- src/pool_day.c | 4 ++-- src/task.c | 4 ++-- test/src/mock/callback_mock.cc | 8 ++++---- test/src/mock/callback_mock.h | 8 ++++---- test/src/pool_day_test.cc | 24 ++++++++++++------------ 7 files changed, 40 insertions(+), 38 deletions(-) diff --git a/include/internal/task.h b/include/internal/task.h index d3eb929..d5f0e1a 100644 --- a/include/internal/task.h +++ b/include/internal/task.h @@ -32,16 +32,16 @@ * @brief Pool task definition. */ struct task { - uint32_t id; //!< Task identifier. - bool is_orphan; //!< Flag indicating whether the task is orphaned. - struct task *next; //!< Next element of the current instance. - struct task *prev; //!< Previous element of the current instance. - void *(*task)(void *); //!< Task callback. - void *param; //!< Parameter of the task callback. - void *ret_val; //!< Task return value. - void (*on_task_start)(uint32_t); //!< Callback executed when the task starts. - void (*on_task_end)(uint32_t, void *); //!< Callback executed when the task ends. - sem_t ready; //!< Task's semaphore. + uint32_t id; //!< Task identifier. + bool is_orphan; //!< Flag indicating whether the task is orphaned. + struct task *next; //!< Next element of the current instance. + struct task *prev; //!< Previous element of the current instance. + void *(*task)(void *); //!< Task callback. + void *param; //!< Parameter of the task callback. + void *ret_val; //!< Task return value. + void (*on_task_start)(uint32_t, void *); //!< Callback executed when the task starts. + void (*on_task_end)(uint32_t, void *, void *); //!< Callback executed when the task ends. + sem_t ready; //!< Task's semaphore. }; typedef struct task task_t; //!< Structure representing an item on the task queue. @@ -62,8 +62,8 @@ typedef struct task task_t; //!< Structure representing an item on the task queu * @return Pointer to the new task. */ task_t *create_task(uint32_t id, void *(*task)(void *), void *param, - size_t param_size, void (*start_cb)(uint32_t), - void (*end_cb)(uint32_t, void *)); + size_t param_size, void (*start_cb)(uint32_t, void *), + void (*end_cb)(uint32_t, void *, void *)); /** * @brief Destroy a task. diff --git a/samples/c/main.c b/samples/c/main.c index b421e93..a9f47e8 100644 --- a/samples/c/main.c +++ b/samples/c/main.c @@ -8,11 +8,13 @@ #define DUMMY_TASK_ID 0 -void task_start_callback(uint32_t tid) { +void task_start_callback(uint32_t tid, void *param) { + (void)param; printf("task '%u' starting...\n", tid); } -void task_end_callback(uint32_t tid, void *ret_val) { +void task_end_callback(uint32_t tid, void *param, void *ret_val) { + (void)param; printf("task '%u' ended with return value: %s\n", tid, (char *)ret_val); } diff --git a/src/pool_day.c b/src/pool_day.c index c3c70c6..4f6a309 100644 --- a/src/pool_day.c +++ b/src/pool_day.c @@ -39,7 +39,7 @@ __static void *thread_func(void *param) { entry->is_orphan = true; if (entry->on_task_start) { - entry->on_task_start(entry->id); + entry->on_task_start(entry->id, entry->param); } POOL_DAY_DEBUG("thread '0x%x' running the task '0x%x'", pthread_self(), @@ -52,7 +52,7 @@ __static void *thread_func(void *param) { if (entry->on_task_end) { POOL_DAY_DEBUG("thread '0x%x' running async teardown for task '0x%x'", pthread_self(), entry->id); - entry->on_task_end(entry->id, entry->ret_val); + entry->on_task_end(entry->id, entry->param, entry->ret_val); destroy_task(entry); } else { POOL_DAY_DEBUG("thread '0x%x' running sync teardown for task '0x%x'", diff --git a/src/task.c b/src/task.c index 9192446..a9c57f0 100644 --- a/src/task.c +++ b/src/task.c @@ -5,8 +5,8 @@ // cppcheck-suppress unusedFunction task_t *create_task(uint32_t id, void *(*task)(void *), void *param, - size_t param_size, void (*start_cb)(uint32_t), - void (*end_cb)(uint32_t, void *)) { + size_t param_size, void (*start_cb)(uint32_t, void *), + void (*end_cb)(uint32_t, void *, void *)) { task_t *node; node = (task_t *)calloc(1, sizeof(task_t)); diff --git a/test/src/mock/callback_mock.cc b/test/src/mock/callback_mock.cc index 19c98bc..a1a2dee 100644 --- a/test/src/mock/callback_mock.cc +++ b/test/src/mock/callback_mock.cc @@ -16,12 +16,12 @@ void *CbWrapper::TaskCb(void *param) { return mock_->TaskCb(param); } -void CbWrapper::OnTaskStartCb(uint32_t tid) { - mock_->OnTaskStartCb(tid); +void CbWrapper::OnTaskStartCb(uint32_t tid, void *param) { + mock_->OnTaskStartCb(tid, param); } -void CbWrapper::OnTaskEndCb(uint32_t tid, void *param) { - mock_->OnTaskEndCb(tid, param); +void CbWrapper::OnTaskEndCb(uint32_t tid, void *param, void *ret_val) { + mock_->OnTaskEndCb(tid, param, ret_val); } TaskMock &CbWrapper::mock() { diff --git a/test/src/mock/callback_mock.h b/test/src/mock/callback_mock.h index edd1c6a..3bd8b00 100644 --- a/test/src/mock/callback_mock.h +++ b/test/src/mock/callback_mock.h @@ -6,8 +6,8 @@ class TaskMock { public: MOCK_METHOD(void *, TaskCb, (void *)); - MOCK_METHOD(void, OnTaskStartCb, (uint32_t)); - MOCK_METHOD(void, OnTaskEndCb, (uint32_t, void *)); + MOCK_METHOD(void, OnTaskStartCb, (uint32_t, void *)); + MOCK_METHOD(void, OnTaskEndCb, (uint32_t, void *, void *)); }; class CbWrapper { @@ -16,8 +16,8 @@ class CbWrapper { ~CbWrapper(); static void *TaskCb(void *param); - static void OnTaskStartCb(uint32_t tid); - static void OnTaskEndCb(uint32_t tid, void *param); + static void OnTaskStartCb(uint32_t tid, void *param); + static void OnTaskEndCb(uint32_t tid, void *param, void *ret_val); static TaskMock &mock(); private: diff --git a/test/src/pool_day_test.cc b/test/src/pool_day_test.cc index a802e8f..386750e 100644 --- a/test/src/pool_day_test.cc +++ b/test/src/pool_day_test.cc @@ -212,7 +212,7 @@ TEST_F(PoolDayTest, ExecuteTaskWithNullParameterWithSuccess) { enqueue_task(pool_, task); - EXPECT_CALL(CbWrapper::mock(), OnTaskStartCb(_)) + EXPECT_CALL(CbWrapper::mock(), OnTaskStartCb(_, _)) .Times(0); EXPECT_CALL(CbWrapper::mock(), TaskCb(nullptr)) .Times(1) @@ -222,7 +222,7 @@ TEST_F(PoolDayTest, ExecuteTaskWithNullParameterWithSuccess) { return ret_val; }) ); - EXPECT_CALL(CbWrapper::mock(), OnTaskEndCb(_, _)) + EXPECT_CALL(CbWrapper::mock(), OnTaskEndCb(_, _, _)) .Times(0); auto ret = thread_func(pool_); @@ -245,7 +245,7 @@ TEST_F(PoolDayTest, ExecuteTaskWithCallbacks) { enqueue_task(pool_, task); - EXPECT_CALL(CbWrapper::mock(), OnTaskStartCb(task->id)) + EXPECT_CALL(CbWrapper::mock(), OnTaskStartCb(task->id, task->param)) .Times(1); EXPECT_CALL(CbWrapper::mock(), TaskCb(nullptr)) .Times(1) @@ -255,7 +255,7 @@ TEST_F(PoolDayTest, ExecuteTaskWithCallbacks) { return ret_val; }) ); - EXPECT_CALL(CbWrapper::mock(), OnTaskEndCb(task->id, ret_val)) + EXPECT_CALL(CbWrapper::mock(), OnTaskEndCb(task->id, task->param, ret_val)) .Times(1); auto ret = thread_func(pool_); @@ -273,7 +273,7 @@ TEST_F(PoolDayTest, ExecuteTaskWithStartCallbackOnly) { enqueue_task(pool_, task); - EXPECT_CALL(CbWrapper::mock(), OnTaskStartCb(task->id)) + EXPECT_CALL(CbWrapper::mock(), OnTaskStartCb(task->id, task->param)) .Times(1); EXPECT_CALL(CbWrapper::mock(), TaskCb(nullptr)) .Times(1) @@ -283,7 +283,7 @@ TEST_F(PoolDayTest, ExecuteTaskWithStartCallbackOnly) { return ret_val; }) ); - EXPECT_CALL(CbWrapper::mock(), OnTaskEndCb(_, _)) + EXPECT_CALL(CbWrapper::mock(), OnTaskEndCb(_, _, _)) .Times(0); auto ret = thread_func(pool_); @@ -305,7 +305,7 @@ TEST_F(PoolDayTest, ExecuteTaskWithEndCallbackOnly) { enqueue_task(pool_, task); - EXPECT_CALL(CbWrapper::mock(), OnTaskStartCb(_)) + EXPECT_CALL(CbWrapper::mock(), OnTaskStartCb(_, _)) .Times(0); EXPECT_CALL(CbWrapper::mock(), TaskCb(nullptr)) .Times(1) @@ -315,7 +315,7 @@ TEST_F(PoolDayTest, ExecuteTaskWithEndCallbackOnly) { return ret_val; }) ); - EXPECT_CALL(CbWrapper::mock(), OnTaskEndCb(task->id, ret_val)) + EXPECT_CALL(CbWrapper::mock(), OnTaskEndCb(task->id, task->param, ret_val)) .Times(1); auto ret = thread_func(pool_); @@ -335,7 +335,7 @@ TEST_F(PoolDayTest, ExecuteTaskWithParameterWithSuccess) { enqueue_task(pool_, task); - EXPECT_CALL(CbWrapper::mock(), OnTaskStartCb(_)) + EXPECT_CALL(CbWrapper::mock(), OnTaskStartCb(_, _)) .Times(0); EXPECT_CALL(CbWrapper::mock(), TaskCb(StrEqVoidPointer(param))) .Times(1) @@ -345,7 +345,7 @@ TEST_F(PoolDayTest, ExecuteTaskWithParameterWithSuccess) { return ret_val; }) ); - EXPECT_CALL(CbWrapper::mock(), OnTaskEndCb(_, _)) + EXPECT_CALL(CbWrapper::mock(), OnTaskEndCb(_, _, _)) .Times(0); auto ret = thread_func(pool_); @@ -397,7 +397,7 @@ TEST_F(PoolDayTest, GetTaskResultWithSuccess) { { enqueue_task(pool_, task); - EXPECT_CALL(CbWrapper::mock(), OnTaskStartCb(_)) + EXPECT_CALL(CbWrapper::mock(), OnTaskStartCb(_, _)) .Times(0); EXPECT_CALL(CbWrapper::mock(), TaskCb(StrEqVoidPointer(param))) .Times(1) @@ -407,7 +407,7 @@ TEST_F(PoolDayTest, GetTaskResultWithSuccess) { return &ret_val; }) ); - EXPECT_CALL(CbWrapper::mock(), OnTaskEndCb(_, _)) + EXPECT_CALL(CbWrapper::mock(), OnTaskEndCb(_, _, _)) .Times(0); thread_func(pool_); From 809f4683f7c532a4b445e9a41f5e5279c318e424 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Tue, 16 Dec 2025 22:42:24 -0300 Subject: [PATCH 29/63] lib: improving library API --- CMakeLists.txt | 2 +- include/internal/queue.h | 8 +-- include/internal/{task.h => task_def.h} | 40 +++++---------- include/pool_day.h | 9 ++-- include/task.h | 66 +++++++++++++++++++++++++ samples/c/main.c | 16 ++---- samples/cpp/main.cc | 5 +- src/pool_day.c | 9 ++-- src/queue.c | 19 +++---- src/task.c | 16 +++--- test/src/pool_day_test.cc | 2 + test/src/queue_test.cc | 4 +- 12 files changed, 120 insertions(+), 76 deletions(-) rename include/internal/{task.h => task_def.h} (67%) create mode 100644 include/task.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 16e2de4..5d890a7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,5 +50,5 @@ target_link_libraries(${PROJECT_NAME} pthread) install(TARGETS ${PROJECT_NAME} DESTINATION /usr/lib) install(FILES include/pool_day.h DESTINATION /usr/include/pool_day) +install(FILES include/task.h DESTINATION /usr/include/pool_day) install(FILES include/internal/errors.h DESTINATION /usr/include/pool_day/internal) -install(FILES include/internal/task.h DESTINATION /usr/include/pool_day/internal) diff --git a/include/internal/queue.h b/include/internal/queue.h index ea29bd4..d49f9e0 100644 --- a/include/internal/queue.h +++ b/include/internal/queue.h @@ -26,10 +26,10 @@ #include #include -typedef struct task task_t; //!< Structure representing an item on the task - // queue. typedef struct task_queue task_queue_t; //!< Structure representing the queue // itself. +typedef struct task *task_t; //!< Structure representing an item on the task + // queue. /** * @brief Get the queue size. @@ -50,7 +50,7 @@ uint32_t queue_size(task_queue_t *queue); * @param[in,out] queue Pointer to the queue. * @param[in] task Pointer to the task to be enqueued. */ -void enqueue(task_queue_t *queue, task_t *task); +void enqueue(task_queue_t *queue, task_t task); /** * @brief Dequeue a task from the queue. @@ -61,7 +61,7 @@ void enqueue(task_queue_t *queue, task_t *task); * * @return The head task. */ -task_t *dequeue(task_queue_t *queue); +task_t dequeue(task_queue_t *queue); /** * @brief Initialize the queue. diff --git a/include/internal/task.h b/include/internal/task_def.h similarity index 67% rename from include/internal/task.h rename to include/internal/task_def.h index d5f0e1a..0e3b027 100644 --- a/include/internal/task.h +++ b/include/internal/task_def.h @@ -21,13 +21,17 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ -#ifndef TASK_H_ -#define TASK_H_ +#ifndef TASK_DEF_H_ +#define TASK_DEF_H_ #include #include #include +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + /** * @brief Pool task definition. */ @@ -44,32 +48,10 @@ struct task { sem_t ready; //!< Task's semaphore. }; -typedef struct task task_t; //!< Structure representing an item on the task queue. - -/** - * @brief Create a new task. - * - * @note The created task doesn't require a manual release once it's bound to a - * pool. - * - * @param[in] id Task identifier. - * @param[in] task Task callback. - * @param[in] param Task parameter. - * @param[in] param_size Size of the task parameter. - * @param[in] start_cb Callback executed when the task starts. - * @param[in] end_cb Callback executed when the task ends. - * - * @return Pointer to the new task. - */ -task_t *create_task(uint32_t id, void *(*task)(void *), void *param, - size_t param_size, void (*start_cb)(uint32_t, void *), - void (*end_cb)(uint32_t, void *, void *)); +typedef struct task *task_t; //!< Structure representing an item on the task queue. -/** - * @brief Destroy a task. - * - * @param[in] task Pointer to the task to be destroyed. - */ -void destroy_task(task_t *task); +#ifdef __cplusplus +} +#endif // __cplusplus -#endif // TASK_H_ +#endif // TASK_DEF_H_ diff --git a/include/pool_day.h b/include/pool_day.h index 6055399..928e3ff 100644 --- a/include/pool_day.h +++ b/include/pool_day.h @@ -31,9 +31,9 @@ extern "C" { #endif // __cplusplus #include "internal/errors.h" -#include "internal/task.h" typedef struct pool_day *pool_day_t; //!< Handle to the pool. +typedef struct task *task_t; //!< Handle to the task. /** * @brief Enqueue a new task into the pool. @@ -43,7 +43,7 @@ typedef struct pool_day *pool_day_t; //!< Handle to the pool. * * @return POOL_DAY_SUCCESS on success; otherwise the suitable error code. */ -pool_day_retcode_t enqueue_task(pool_day_t pool, task_t *task); +pool_day_retcode_t enqueue_task(pool_day_t pool, task_t task); /** * @brief Create a new pool according to the specified size. @@ -81,8 +81,7 @@ uint32_t queued_tasks(pool_day_t pool); * * pool = create_pool(size); * if (pool) { - * task_t *t = create_task(id, callback, (void *)param, param_size, - * start_cb, end_cb); + * task_t t = create_task(id, callback, (void *)param, param_size, NULL, NULL); * * enqueue_task(pool, t); * @@ -99,7 +98,7 @@ uint32_t queued_tasks(pool_day_t pool); * * @return The return value of the task. */ -void *get_task_result(task_t *task); +void *get_task_result(task_t task); /** * @brief Abort the execution of incoming tasks. diff --git a/include/task.h b/include/task.h new file mode 100644 index 0000000..91a05b1 --- /dev/null +++ b/include/task.h @@ -0,0 +1,66 @@ +/** + * @file + * + * @brief Contains the definitions of task structure and helper methods and + * macros to handle it. + * + * @authors + * Copyright (C) 2023 André L. C. Moreira + * + * @copyright + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ +#ifndef TASK_H_ +#define TASK_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef struct task *task_t; //!< Handle to the task. + +/** + * @brief Create a new task. + * + * @note The created task doesn't require a manual release once it's bound to a + * pool. + * + * @param[in] id Task identifier. + * @param[in] task Task callback. + * @param[in] param Task parameter. + * @param[in] param_size Size of the task parameter. + * @param[in] start_cb Callback executed when the task starts. + * @param[in] end_cb Callback executed when the task ends. + * + * @return Pointer to the new task. + */ +task_t create_task(uint32_t id, void *(*task)(void *), void *param, + size_t param_size, void (*start_cb)(uint32_t, void *), + void (*end_cb)(uint32_t, void *, void *)); + +/** + * @brief Destroy a task. + * + * @param[in] task Pointer to the task to be destroyed. + */ +void destroy_task(task_t task); + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // TASK_H_ diff --git a/samples/c/main.c b/samples/c/main.c index a9f47e8..d17f16c 100644 --- a/samples/c/main.c +++ b/samples/c/main.c @@ -5,19 +5,10 @@ #include #include "pool_day.h" +#include "task.h" #define DUMMY_TASK_ID 0 -void task_start_callback(uint32_t tid, void *param) { - (void)param; - printf("task '%u' starting...\n", tid); -} - -void task_end_callback(uint32_t tid, void *param, void *ret_val) { - (void)param; - printf("task '%u' ended with return value: %s\n", tid, (char *)ret_val); -} - void *func(void *param) { char *str = (char *)param; char *ret = (char *)calloc(10, sizeof(char)); @@ -42,9 +33,8 @@ int main(void) { } char str[] = "foo"; - task_t *task = create_task(DUMMY_TASK_ID, func, (void *)str, - sizeof(char) * strlen(str) + 1, - task_start_callback, task_end_callback); + task_t task = create_task(DUMMY_TASK_ID, func, (void *)str, + sizeof(char) * strlen(str) + 1, NULL, NULL); assert(enqueue_task(pool, task) == POOL_DAY_SUCCESS); char *ret = (char *)get_task_result(task); diff --git a/samples/cpp/main.cc b/samples/cpp/main.cc index 734c04a..cfa77d9 100644 --- a/samples/cpp/main.cc +++ b/samples/cpp/main.cc @@ -3,6 +3,7 @@ #include #include "pool_day.h" +#include "task.h" class Foo { public: @@ -57,8 +58,8 @@ class Foo { } pool_day_t pool_; - task_t *t1_; - task_t *t2_; + task_t t1_; + task_t t2_; }; int main(void) { diff --git a/src/pool_day.c b/src/pool_day.c index 4f6a309..d7a806f 100644 --- a/src/pool_day.c +++ b/src/pool_day.c @@ -4,11 +4,12 @@ #include #include #include -#include #include "internal/logger.h" #include "internal/queue.h" +#include "internal/task_def.h" #include "internal/utils.h" +#include "task.h" /** * @brief Main structure of the library, it defines a handle to the pool. @@ -34,7 +35,7 @@ __static void *thread_func(void *param) { break; } - task_t *entry = dequeue(pool->tasks); + task_t entry = dequeue(pool->tasks); if (entry) { entry->is_orphan = true; @@ -68,7 +69,7 @@ __static void *thread_func(void *param) { } // cppcheck-suppress unusedFunction -pool_day_retcode_t enqueue_task(pool_day_t pool, task_t *task) { +pool_day_retcode_t enqueue_task(pool_day_t pool, task_t task) { if (!pool || !task) { POOL_DAY_ERROR("null parameter supplied"); return POOL_DAY_ERROR_NULL_PARAM; @@ -180,7 +181,7 @@ uint32_t queued_tasks(pool_day_t pool) { } // cppcheck-suppress unusedFunction -void *get_task_result(task_t *task) { +void *get_task_result(task_t task) { if (!task) { POOL_DAY_ERROR("null task provided"); return NULL; diff --git a/src/queue.c b/src/queue.c index 2c542b8..18586df 100644 --- a/src/queue.c +++ b/src/queue.c @@ -2,8 +2,9 @@ #include #include "internal/queue.h" -#include "internal/task.h" +#include "internal/task_def.h" #include "internal/utils.h" +#include "task.h" /** * @brief For-each macro implementation. @@ -28,8 +29,8 @@ * @brief Task queue definition. */ struct task_queue { - task_t *tail; //!< Tail of the queue. - task_t *head; //!< Head of the queue. + task_t tail; //!< Tail of the queue. + task_t head; //!< Head of the queue. pthread_mutex_t mutex; //!< Mutex of the queue. }; @@ -47,7 +48,7 @@ uint32_t queue_size(task_queue_t *queue) { return size; } -void enqueue(task_queue_t *queue, task_t *elem) { +void enqueue(task_queue_t *queue, task_t elem) { if (queue && elem) { THREAD_SAFE_ZONE(&queue->mutex, { // if the queue is empty @@ -62,8 +63,8 @@ void enqueue(task_queue_t *queue, task_t *elem) { } } -static task_t *__dequeue(task_queue_t *queue) { - task_t *to_del = queue->head; +static task_t __dequeue(task_queue_t *queue) { + task_t to_del = queue->head; // if the queue is not empty if (to_del) { @@ -80,8 +81,8 @@ static task_t *__dequeue(task_queue_t *queue) { return to_del; } -task_t *dequeue(task_queue_t *queue) { - task_t *to_del = NULL; +task_t dequeue(task_queue_t *queue) { + task_t to_del = NULL; if (queue) { THREAD_SAFE_ZONE(&queue->mutex, { @@ -105,7 +106,7 @@ void destroy_queue(task_queue_t *queue) { if (queue) { THREAD_SAFE_ZONE(&queue->mutex, { for_each_task_safe(curr, queue) { - task_t *node = __dequeue(queue); + task_t node = __dequeue(queue); destroy_task(node); } diff --git a/src/task.c b/src/task.c index a9c57f0..b65f74e 100644 --- a/src/task.c +++ b/src/task.c @@ -1,15 +1,17 @@ -#include "internal/task.h" +#include "task.h" #include #include +#include "internal/task_def.h" + // cppcheck-suppress unusedFunction -task_t *create_task(uint32_t id, void *(*task)(void *), void *param, - size_t param_size, void (*start_cb)(uint32_t, void *), - void (*end_cb)(uint32_t, void *, void *)) { - task_t *node; +task_t create_task(uint32_t id, void *(*task)(void *), void *param, + size_t param_size, void (*start_cb)(uint32_t, void *), + void (*end_cb)(uint32_t, void *, void *)) { + task_t node; - node = (task_t *)calloc(1, sizeof(task_t)); + node = (task_t)calloc(1, sizeof(*node)); if (node) { node->id = id; node->task = task; @@ -28,7 +30,7 @@ task_t *create_task(uint32_t id, void *(*task)(void *), void *param, return node; } -void destroy_task(task_t *task) { +void destroy_task(task_t task) { // if the task is orphaned, it means that the user will take care of its release, // otherwise it is managed by the pool if (task && task->is_orphan) { diff --git a/test/src/pool_day_test.cc b/test/src/pool_day_test.cc index 386750e..6a2c535 100644 --- a/test/src/pool_day_test.cc +++ b/test/src/pool_day_test.cc @@ -3,7 +3,9 @@ #include "mock/callback_mock.h" extern "C" { +#include "internal/task_def.h" #include "pool_day.h" +#include "task.h" extern void *thread_func(void *param); } diff --git a/test/src/queue_test.cc b/test/src/queue_test.cc index a1ea283..a7b5ef7 100644 --- a/test/src/queue_test.cc +++ b/test/src/queue_test.cc @@ -2,7 +2,7 @@ extern "C" { #include "internal/queue.h" -#include "internal/task.h" +#include "task.h" } class QueueTest : public ::testing::Test { @@ -167,7 +167,7 @@ TEST_F(QueueTest, DequeueMultipleElementsWithNotEmptyQueue) { auto t2 = create_task(1, nullptr, nullptr, 0, nullptr, nullptr); auto t3 = create_task(2, nullptr, nullptr, 0, nullptr, nullptr); auto t4 = create_task(3, nullptr, nullptr, 0, nullptr, nullptr); - task_t *ret; + task_t ret; enqueue(queue_, t1); enqueue(queue_, t2); From 8e18ce2214afd5f7f789901fc7805a16aefcb15a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Tue, 16 Dec 2025 23:07:06 -0300 Subject: [PATCH 30/63] lib: updating doc --- include/internal/queue.h | 2 +- include/pool_day.h | 2 +- include/task.h | 4 ++-- src/queue.c | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/internal/queue.h b/include/internal/queue.h index d49f9e0..4ff33c9 100644 --- a/include/internal/queue.h +++ b/include/internal/queue.h @@ -48,7 +48,7 @@ uint32_t queue_size(task_queue_t *queue); * @note This function is thread-safe. * * @param[in,out] queue Pointer to the queue. - * @param[in] task Pointer to the task to be enqueued. + * @param[in] task Handle to the task to be enqueued. */ void enqueue(task_queue_t *queue, task_t task); diff --git a/include/pool_day.h b/include/pool_day.h index 928e3ff..7947dcd 100644 --- a/include/pool_day.h +++ b/include/pool_day.h @@ -94,7 +94,7 @@ uint32_t queued_tasks(pool_day_t pool); * * @note This function blocks the current thread. * - * @param[in] task Task handler. + * @param[in] task Task handle. * * @return The return value of the task. */ diff --git a/include/task.h b/include/task.h index 91a05b1..4bdde85 100644 --- a/include/task.h +++ b/include/task.h @@ -46,7 +46,7 @@ typedef struct task *task_t; //!< Handle to the task. * @param[in] start_cb Callback executed when the task starts. * @param[in] end_cb Callback executed when the task ends. * - * @return Pointer to the new task. + * @return Handle to the new task. */ task_t create_task(uint32_t id, void *(*task)(void *), void *param, size_t param_size, void (*start_cb)(uint32_t, void *), @@ -55,7 +55,7 @@ task_t create_task(uint32_t id, void *(*task)(void *), void *param, /** * @brief Destroy a task. * - * @param[in] task Pointer to the task to be destroyed. + * @param[in] task Handle to the task to be destroyed. */ void destroy_task(task_t task); diff --git a/src/queue.c b/src/queue.c index 18586df..7cb3c1b 100644 --- a/src/queue.c +++ b/src/queue.c @@ -29,8 +29,8 @@ * @brief Task queue definition. */ struct task_queue { - task_t tail; //!< Tail of the queue. - task_t head; //!< Head of the queue. + task_t tail; //!< Tail of the queue. + task_t head; //!< Head of the queue. pthread_mutex_t mutex; //!< Mutex of the queue. }; From 9f6693de7f285c797404ee776c18350f4c99fc3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Mon, 22 Dec 2025 18:39:51 -0300 Subject: [PATCH 31/63] lib: improving task releasing on async flow --- include/pool_day.h | 9 +++++++++ src/pool_day.c | 13 +++++++------ test/src/pool_day_test.cc | 6 ++++++ 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/include/pool_day.h b/include/pool_day.h index 7947dcd..40adb6d 100644 --- a/include/pool_day.h +++ b/include/pool_day.h @@ -100,6 +100,15 @@ uint32_t queued_tasks(pool_day_t pool); */ void *get_task_result(task_t task); +/** + * @brief Wait for the finish of a given task without retrieving its result. + * + * @note This function blocks the current thread. + * + * @param[in] task Task handle. + */ +void wait_task_finish(task_t task); + /** * @brief Abort the execution of incoming tasks. * diff --git a/src/pool_day.c b/src/pool_day.c index d7a806f..39bd434 100644 --- a/src/pool_day.c +++ b/src/pool_day.c @@ -51,15 +51,12 @@ __static void *thread_func(void *param) { entry->ret_val = ret; if (entry->on_task_end) { - POOL_DAY_DEBUG("thread '0x%x' running async teardown for task '0x%x'", + POOL_DAY_DEBUG("thread '0x%x' executing end callback for task '0x%x'", pthread_self(), entry->id); entry->on_task_end(entry->id, entry->param, entry->ret_val); - destroy_task(entry); - } else { - POOL_DAY_DEBUG("thread '0x%x' running sync teardown for task '0x%x'", - pthread_self(), entry->id); - sem_post(&entry->ready); } + + sem_post(&entry->ready); } } @@ -193,3 +190,7 @@ void *get_task_result(task_t task) { return task->ret_val; } + +void wait_task_finish(task_t task) { + (void)get_task_result(task); +} diff --git a/test/src/pool_day_test.cc b/test/src/pool_day_test.cc index 6a2c535..f69932f 100644 --- a/test/src/pool_day_test.cc +++ b/test/src/pool_day_test.cc @@ -262,6 +262,9 @@ TEST_F(PoolDayTest, ExecuteTaskWithCallbacks) { auto ret = thread_func(pool_); EXPECT_EQ(ret, nullptr); + + // cleanup + destroy_task(task); } /** @@ -322,6 +325,9 @@ TEST_F(PoolDayTest, ExecuteTaskWithEndCallbackOnly) { auto ret = thread_func(pool_); EXPECT_EQ(ret, nullptr); + + // cleanup + destroy_task(task); } /** From e4645495fccf187dba19b42d46fab73feb4ca4f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Mon, 22 Dec 2025 18:46:57 -0300 Subject: [PATCH 32/63] ci: fixing cppcheck issues --- src/pool_day.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pool_day.c b/src/pool_day.c index 39bd434..ad628cb 100644 --- a/src/pool_day.c +++ b/src/pool_day.c @@ -177,7 +177,6 @@ uint32_t queued_tasks(pool_day_t pool) { return pool ? queue_size(pool->tasks) : 0; } -// cppcheck-suppress unusedFunction void *get_task_result(task_t task) { if (!task) { POOL_DAY_ERROR("null task provided"); @@ -191,6 +190,7 @@ void *get_task_result(task_t task) { return task->ret_val; } +// cppcheck-suppress unusedFunction void wait_task_finish(task_t task) { (void)get_task_result(task); } From 13e4177bb6d282ef97109a5d7e5f53b34cdb46ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Mon, 22 Dec 2025 19:07:54 -0300 Subject: [PATCH 33/63] lib: adding 'auto_release' parameter on create_task function to control the task auto releasing --- include/internal/task_def.h | 1 + include/task.h | 6 +++++- src/pool_day.c | 3 +++ src/task.c | 4 +++- 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/include/internal/task_def.h b/include/internal/task_def.h index 0e3b027..858b49e 100644 --- a/include/internal/task_def.h +++ b/include/internal/task_def.h @@ -38,6 +38,7 @@ extern "C" { struct task { uint32_t id; //!< Task identifier. bool is_orphan; //!< Flag indicating whether the task is orphaned. + bool auto_release; //!< Flag indicating whether the task must be released after its execution. struct task *next; //!< Next element of the current instance. struct task *prev; //!< Previous element of the current instance. void *(*task)(void *); //!< Task callback. diff --git a/include/task.h b/include/task.h index 4bdde85..65d7600 100644 --- a/include/task.h +++ b/include/task.h @@ -24,6 +24,7 @@ #ifndef TASK_H_ #define TASK_H_ +#include #include #include @@ -43,13 +44,16 @@ typedef struct task *task_t; //!< Handle to the task. * @param[in] task Task callback. * @param[in] param Task parameter. * @param[in] param_size Size of the task parameter. + * @param[in] auto_release Flag indicating whether the task must be + * released after its execution. * @param[in] start_cb Callback executed when the task starts. * @param[in] end_cb Callback executed when the task ends. * * @return Handle to the new task. */ task_t create_task(uint32_t id, void *(*task)(void *), void *param, - size_t param_size, void (*start_cb)(uint32_t, void *), + size_t param_size, bool auto_release, + void (*start_cb)(uint32_t, void *), void (*end_cb)(uint32_t, void *, void *)); /** diff --git a/src/pool_day.c b/src/pool_day.c index ad628cb..b8e144f 100644 --- a/src/pool_day.c +++ b/src/pool_day.c @@ -57,6 +57,9 @@ __static void *thread_func(void *param) { } sem_post(&entry->ready); + if (entry->auto_release) { + destroy_task(entry); + } } } diff --git a/src/task.c b/src/task.c index b65f74e..a7e127e 100644 --- a/src/task.c +++ b/src/task.c @@ -7,7 +7,8 @@ // cppcheck-suppress unusedFunction task_t create_task(uint32_t id, void *(*task)(void *), void *param, - size_t param_size, void (*start_cb)(uint32_t, void *), + size_t param_size, bool auto_release, + void (*start_cb)(uint32_t, void *), void (*end_cb)(uint32_t, void *, void *)) { task_t node; @@ -18,6 +19,7 @@ task_t create_task(uint32_t id, void *(*task)(void *), void *param, node->on_task_start = start_cb; node->on_task_end = end_cb; node->is_orphan = true; + node->auto_release = auto_release; if (param) { node->param = malloc(param_size); From b4962baf62f0c4cf5837118ad8f83d694cd4b9c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Tue, 23 Dec 2025 22:55:34 -0300 Subject: [PATCH 34/63] lib: splitting the 'create_task' function into 'create_sync_task' and 'create_async_task' --- include/pool_day.h | 2 +- include/task.h | 29 ++++++++++++---- samples/c/main.c | 4 +-- samples/cpp/main.cc | 10 +++--- src/task.c | 13 ++++--- test/src/pool_day_test.cc | 73 ++++++++++++++++----------------------- test/src/queue_test.cc | 36 +++++++++---------- 7 files changed, 86 insertions(+), 81 deletions(-) diff --git a/include/pool_day.h b/include/pool_day.h index 40adb6d..f7f030d 100644 --- a/include/pool_day.h +++ b/include/pool_day.h @@ -81,7 +81,7 @@ uint32_t queued_tasks(pool_day_t pool); * * pool = create_pool(size); * if (pool) { - * task_t t = create_task(id, callback, (void *)param, param_size, NULL, NULL); + * task_t t = create_sync_task(id, callback, (void *)param, param_size); * * enqueue_task(pool, t); * diff --git a/include/task.h b/include/task.h index 65d7600..86e6616 100644 --- a/include/task.h +++ b/include/task.h @@ -35,9 +35,9 @@ extern "C" { typedef struct task *task_t; //!< Handle to the task. /** - * @brief Create a new task. + * @brief Create a new synchronous task. * - * @note The created task doesn't require a manual release once it's bound to a + * @note The created task requires a manual release once it's bound to a * pool. * * @param[in] id Task identifier. @@ -51,10 +51,27 @@ typedef struct task *task_t; //!< Handle to the task. * * @return Handle to the new task. */ -task_t create_task(uint32_t id, void *(*task)(void *), void *param, - size_t param_size, bool auto_release, - void (*start_cb)(uint32_t, void *), - void (*end_cb)(uint32_t, void *, void *)); +task_t create_sync_task(uint32_t id, void *(*task)(void *), void *param, + size_t param_size); + +/** + * @brief Create a new asynchronous task. + * + * @param[in] id Task identifier. + * @param[in] task Task callback. + * @param[in] param Task parameter. + * @param[in] param_size Size of the task parameter. + * @param[in] auto_release Flag indicating whether the task must be + * released after its execution. + * @param[in] start_cb Callback executed when the task starts. + * @param[in] end_cb Callback executed when the task ends. + * + * @return Handle to the new task. + */ +task_t create_async_task(uint32_t id, void *(*task)(void *), void *param, + size_t param_size, bool auto_release, + void (*start_cb)(uint32_t, void *), + void (*end_cb)(uint32_t, void *, void *)); /** * @brief Destroy a task. diff --git a/samples/c/main.c b/samples/c/main.c index d17f16c..a98f8cb 100644 --- a/samples/c/main.c +++ b/samples/c/main.c @@ -33,8 +33,8 @@ int main(void) { } char str[] = "foo"; - task_t task = create_task(DUMMY_TASK_ID, func, (void *)str, - sizeof(char) * strlen(str) + 1, NULL, NULL); + task_t task = create_sync_task(DUMMY_TASK_ID, func, (void *)str, + sizeof(char) * strlen(str) + 1); assert(enqueue_task(pool, task) == POOL_DAY_SUCCESS); char *ret = (char *)get_task_result(task); diff --git a/samples/cpp/main.cc b/samples/cpp/main.cc index cfa77d9..1502250 100644 --- a/samples/cpp/main.cc +++ b/samples/cpp/main.cc @@ -9,12 +9,10 @@ class Foo { public: Foo() : pool_{create_pool(2)}, - t1_{create_task(0, Foo::Cb1, (void *)"hello", - sizeof(char) * std::strlen("hello") + 1, nullptr, - nullptr)}, - t2_{create_task(1, Foo::Cb2, (void *)"hi", - sizeof(char) * std::strlen("hi") + 1, nullptr, - nullptr)} { + t1_{create_sync_task(0, Foo::Cb1, (void *)"hello", + sizeof(char) * std::strlen("hello") + 1)}, + t2_{create_sync_task(1, Foo::Cb2, (void *)"hi", + sizeof(char) * std::strlen("hi") + 1)} { } void RunTasks(void) { diff --git a/src/task.c b/src/task.c index a7e127e..bc5801a 100644 --- a/src/task.c +++ b/src/task.c @@ -5,11 +5,16 @@ #include "internal/task_def.h" +task_t create_sync_task(uint32_t id, void *(*task)(void *), void *param, + size_t param_size) { + return create_async_task(id, task, param, param_size, false, NULL, NULL); +} + // cppcheck-suppress unusedFunction -task_t create_task(uint32_t id, void *(*task)(void *), void *param, - size_t param_size, bool auto_release, - void (*start_cb)(uint32_t, void *), - void (*end_cb)(uint32_t, void *, void *)) { +task_t create_async_task(uint32_t id, void *(*task)(void *), void *param, + size_t param_size, bool auto_release, + void (*start_cb)(uint32_t, void *), + void (*end_cb)(uint32_t, void *, void *)) { task_t node; node = (task_t)calloc(1, sizeof(*node)); diff --git a/test/src/pool_day_test.cc b/test/src/pool_day_test.cc index f69932f..00dc540 100644 --- a/test/src/pool_day_test.cc +++ b/test/src/pool_day_test.cc @@ -57,7 +57,7 @@ TEST_F(PoolDayTest, CreatePollWithInvalidSize) { * it, then it must be added to the pool's task queue. */ TEST_F(PoolDayTest, EnqueueSingleTaskWithPoolEmpty) { - auto task = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); + auto task = create_sync_task(0, nullptr, nullptr, 0); EXPECT_EQ(queued_tasks(pool_), 0); EXPECT_EQ(enqueue_task(pool_, task), POOL_DAY_SUCCESS); @@ -72,11 +72,11 @@ TEST_F(PoolDayTest, EnqueueSingleTaskWithPoolEmpty) { * it, then they must be added to the pool's task queue. */ TEST_F(PoolDayTest, EnqueueTaskWithPoolNotEmpty) { - auto t1 = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); - auto t2 = create_task(1, nullptr, nullptr, 0, nullptr, nullptr); - auto t3 = create_task(2, nullptr, nullptr, 0, nullptr, nullptr); - auto t4 = create_task(3, nullptr, nullptr, 0, nullptr, nullptr); - auto t5 = create_task(4, nullptr, nullptr, 0, nullptr, nullptr); + auto t1 = create_sync_task(0, nullptr, nullptr, 0); + auto t2 = create_sync_task(1, nullptr, nullptr, 0); + auto t3 = create_sync_task(2, nullptr, nullptr, 0); + auto t4 = create_sync_task(3, nullptr, nullptr, 0); + auto t5 = create_sync_task(4, nullptr, nullptr, 0); EXPECT_EQ(queued_tasks(pool_), 0); @@ -101,7 +101,7 @@ TEST_F(PoolDayTest, EnqueueTaskWithPoolNotEmpty) { * then nothing must happen and the suitable error code must be returned. */ TEST_F(PoolDayTest, EnqueueTaskWithNullPool) { - auto task = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); + auto task = create_sync_task(0, nullptr, nullptr, 0); EXPECT_EQ(enqueue_task(nullptr, task), POOL_DAY_ERROR_NULL_PARAM); free(task); @@ -112,29 +112,12 @@ TEST_F(PoolDayTest, EnqueueTaskWithNullPool) { * then nothing must happen and the suitable error code must be returned. */ TEST_F(PoolDayTest, EnqueueTaskWithNullTask) { - auto task = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); + auto task = create_sync_task(0, nullptr, nullptr, 0); EXPECT_EQ(enqueue_task(pool_, nullptr), POOL_DAY_ERROR_NULL_PARAM); free(task); } -/** - * @brief Given we have a valid pool and a task already bound to a pool, when we - * try to enqueue it, then nothing must happen and the suitable error code - * must be returned. - */ -TEST_F(PoolDayTest, EnqueueTaskAlreadyBound) { - auto another_pool = create_pool(1); - auto task = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); - - EXPECT_EQ(enqueue_task(pool_, task), POOL_DAY_SUCCESS); - - destroy_pool(&another_pool); - - // to force the task destruction on destroy_pool call - task->is_orphan = true; -} - /** * @brief Given we have a pool with no tasks, when we try to get the number of * queued tasks of the pool, then 0 must be returned. @@ -148,7 +131,7 @@ TEST_F(PoolDayTest, GetQueuedTasksCountWithNoTasks) { * the number of queued tasks in the pool, then 1 must be returned. */ TEST_F(PoolDayTest, GetQueuedTasksCountWithSingleTask) { - auto task = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); + auto task = create_sync_task(0, nullptr, nullptr, 0); enqueue_task(pool_, task); @@ -164,11 +147,11 @@ TEST_F(PoolDayTest, GetQueuedTasksCountWithSingleTask) { * returned. */ TEST_F(PoolDayTest, GetQueuedTasksCountWithSeveralTasks) { - auto t1 = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); - auto t2 = create_task(1, nullptr, nullptr, 0, nullptr, nullptr); - auto t3 = create_task(2, nullptr, nullptr, 0, nullptr, nullptr); - auto t4 = create_task(3, nullptr, nullptr, 0, nullptr, nullptr); - auto t5 = create_task(4, nullptr, nullptr, 0, nullptr, nullptr); + auto t1 = create_sync_task(0, nullptr, nullptr, 0); + auto t2 = create_sync_task(1, nullptr, nullptr, 0); + auto t3 = create_sync_task(2, nullptr, nullptr, 0); + auto t4 = create_sync_task(3, nullptr, nullptr, 0); + auto t5 = create_sync_task(4, nullptr, nullptr, 0); enqueue_task(pool_, t1); enqueue_task(pool_, t2); @@ -209,7 +192,7 @@ TEST_F(PoolDayTest, DestroyPollWithNullHandle) { * scheduled for execution, then the task's callback must be called. */ TEST_F(PoolDayTest, ExecuteTaskWithNullParameterWithSuccess) { - auto task = create_task(0, CbWrapper::TaskCb, nullptr, 0, nullptr, nullptr); + auto task = create_sync_task(0, CbWrapper::TaskCb, nullptr, 0); char ret_val[]{ "hello, i'm the return of the task" }; enqueue_task(pool_, task); @@ -241,8 +224,9 @@ TEST_F(PoolDayTest, ExecuteTaskWithNullParameterWithSuccess) { * suitable times. */ TEST_F(PoolDayTest, ExecuteTaskWithCallbacks) { - auto task = create_task(0, CbWrapper::TaskCb, nullptr, 0, - CbWrapper::OnTaskStartCb, CbWrapper::OnTaskEndCb); + auto task = create_async_task(0, CbWrapper::TaskCb, nullptr, 0, false, + CbWrapper::OnTaskStartCb, + CbWrapper::OnTaskEndCb); char ret_val[]{ "hello, i'm the return of the task" }; enqueue_task(pool_, task); @@ -272,8 +256,8 @@ TEST_F(PoolDayTest, ExecuteTaskWithCallbacks) { * scheduled for execution, then the task's start callback must be called. */ TEST_F(PoolDayTest, ExecuteTaskWithStartCallbackOnly) { - auto task = create_task(0, CbWrapper::TaskCb, nullptr, 0, - CbWrapper::OnTaskStartCb, nullptr); + auto task = create_async_task(0, CbWrapper::TaskCb, nullptr, 0, false, + CbWrapper::OnTaskStartCb, nullptr); char ret_val[]{ "hello, i'm the return of the task" }; enqueue_task(pool_, task); @@ -304,8 +288,8 @@ TEST_F(PoolDayTest, ExecuteTaskWithStartCallbackOnly) { * scheduled for execution, then the task's end callback must be called. */ TEST_F(PoolDayTest, ExecuteTaskWithEndCallbackOnly) { - auto task = create_task(0, CbWrapper::TaskCb, nullptr, 0, nullptr, - CbWrapper::OnTaskEndCb); + auto task = create_async_task(0, CbWrapper::TaskCb, nullptr, 0, false, + nullptr, CbWrapper::OnTaskEndCb); char ret_val[]{ "hello, i'm the return of the task" }; enqueue_task(pool_, task); @@ -338,8 +322,9 @@ TEST_F(PoolDayTest, ExecuteTaskWithEndCallbackOnly) { TEST_F(PoolDayTest, ExecuteTaskWithParameterWithSuccess) { char param[]{ "param" }; char ret_val[]{ "hello, i'm the return of the task" }; - auto task = create_task(0, CbWrapper::TaskCb, param, - sizeof(char) * strlen(param) + 1, nullptr, nullptr); + auto task = create_async_task(0, CbWrapper::TaskCb, param, + sizeof(char) * strlen(param) + 1, false, + nullptr, nullptr); enqueue_task(pool_, task); @@ -369,7 +354,7 @@ TEST_F(PoolDayTest, ExecuteTaskWithParameterWithSuccess) { * task is scheduled for execution, then the task's callback must not be called. */ TEST_F(PoolDayTest, ExecuteTaskWithMustStopSet) { - auto task = create_task(0, CbWrapper::TaskCb, nullptr, 0, nullptr, nullptr); + auto task = create_sync_task(0, CbWrapper::TaskCb, nullptr, 0); enqueue_task(pool_, task); @@ -399,8 +384,8 @@ TEST_F(PoolDayTest, AbortTasksWithNullPoolHandle) { TEST_F(PoolDayTest, GetTaskResultWithSuccess) { int ret_val{1234}; char param[]{ "param" }; - auto task = create_task(0, CbWrapper::TaskCb, param, - sizeof(char) * strlen(param) + 1, nullptr, nullptr); + auto task = create_sync_task(0, CbWrapper::TaskCb, param, + sizeof(char) * strlen(param) + 1); { enqueue_task(pool_, task); @@ -441,7 +426,7 @@ TEST_F(PoolDayTest, GetTaskResultWithNullPoolTask) { * the finish of the task, then null must be returned. */ TEST_F(PoolDayTest, GetTaskResultWithWithUnboundTask) { - auto task = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); + auto task = create_sync_task(0, nullptr, nullptr, 0); EXPECT_EQ(get_task_result(task), nullptr); diff --git a/test/src/queue_test.cc b/test/src/queue_test.cc index a7b5ef7..cc4221b 100644 --- a/test/src/queue_test.cc +++ b/test/src/queue_test.cc @@ -33,11 +33,11 @@ TEST_F(QueueTest, GetQueueSizeWithEmptyQueue) { */ TEST_F(QueueTest, GetQueueSizeWithNotEmptyQueue) { { - auto t1 = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); - auto t2 = create_task(1, nullptr, nullptr, 0, nullptr, nullptr); - auto t3 = create_task(2, nullptr, nullptr, 0, nullptr, nullptr); - auto t4 = create_task(3, nullptr, nullptr, 0, nullptr, nullptr); - auto t5 = create_task(4, nullptr, nullptr, 0, nullptr, nullptr); + auto t1 = create_sync_task(0, nullptr, nullptr, 0); + auto t2 = create_sync_task(1, nullptr, nullptr, 0); + auto t3 = create_sync_task(2, nullptr, nullptr, 0); + auto t4 = create_sync_task(3, nullptr, nullptr, 0); + auto t5 = create_sync_task(4, nullptr, nullptr, 0); enqueue(queue_, t1); enqueue(queue_, t2); @@ -63,8 +63,8 @@ TEST_F(QueueTest, GetQueueSizeWithNullQueue) { */ TEST_F(QueueTest, EnqueueElementWithNotEmptyQueue) { { - auto t1 = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); - auto t2 = create_task(1, nullptr, nullptr, 0, nullptr, nullptr); + auto t1 = create_sync_task(0, nullptr, nullptr, 0); + auto t2 = create_sync_task(1, nullptr, nullptr, 0); enqueue(queue_, t1); enqueue(queue_, t2); @@ -72,7 +72,7 @@ TEST_F(QueueTest, EnqueueElementWithNotEmptyQueue) { EXPECT_EQ(queue_size(queue_), 2); } - auto t3 = create_task(3, nullptr, nullptr, 0, nullptr, nullptr); + auto t3 = create_sync_task(3, nullptr, nullptr, 0); enqueue(queue_, t3); @@ -95,8 +95,8 @@ TEST_F(QueueTest, EnqueueNullElementWithEmptyQueue) { */ TEST_F(QueueTest, EnqueueNullElementWithNotEmptyQueue) { { - auto t1 = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); - auto t2 = create_task(1, nullptr, nullptr, 0, nullptr, nullptr); + auto t1 = create_sync_task(0, nullptr, nullptr, 0); + auto t2 = create_sync_task(1, nullptr, nullptr, 0); enqueue(queue_, t1); enqueue(queue_, t2); @@ -112,7 +112,7 @@ TEST_F(QueueTest, EnqueueNullElementWithNotEmptyQueue) { * then nothing must happen. */ TEST_F(QueueTest, EnqueueElementWithNullQueue) { - auto t1 = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); + auto t1 = create_sync_task(0, nullptr, nullptr, 0); enqueue(nullptr, t1); free(t1); @@ -138,9 +138,9 @@ TEST_F(QueueTest, DequeueElementWithEmptyQueue) { * size of the queue must be decreased by 1. */ TEST_F(QueueTest, DequeueSingleElementWithNotEmptyQueue) { - auto t1 = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); - auto t2 = create_task(1, nullptr, nullptr, 0, nullptr, nullptr); - auto t3 = create_task(2, nullptr, nullptr, 0, nullptr, nullptr); + auto t1 = create_sync_task(0, nullptr, nullptr, 0); + auto t2 = create_sync_task(1, nullptr, nullptr, 0); + auto t3 = create_sync_task(2, nullptr, nullptr, 0); enqueue(queue_, t1); enqueue(queue_, t2); @@ -163,10 +163,10 @@ TEST_F(QueueTest, DequeueSingleElementWithNotEmptyQueue) { * the operations. */ TEST_F(QueueTest, DequeueMultipleElementsWithNotEmptyQueue) { - auto t1 = create_task(0, nullptr, nullptr, 0, nullptr, nullptr); - auto t2 = create_task(1, nullptr, nullptr, 0, nullptr, nullptr); - auto t3 = create_task(2, nullptr, nullptr, 0, nullptr, nullptr); - auto t4 = create_task(3, nullptr, nullptr, 0, nullptr, nullptr); + auto t1 = create_sync_task(0, nullptr, nullptr, 0); + auto t2 = create_sync_task(1, nullptr, nullptr, 0); + auto t3 = create_sync_task(2, nullptr, nullptr, 0); + auto t4 = create_sync_task(3, nullptr, nullptr, 0); task_t ret; enqueue(queue_, t1); From 9eb789fb82be329aeb8cfb762ea084556cff0fe6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Tue, 23 Dec 2025 23:29:04 -0300 Subject: [PATCH 35/63] lib: fixing cppcheck and doxygen issues --- include/task.h | 4 ---- src/task.c | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/include/task.h b/include/task.h index 86e6616..bce2486 100644 --- a/include/task.h +++ b/include/task.h @@ -44,10 +44,6 @@ typedef struct task *task_t; //!< Handle to the task. * @param[in] task Task callback. * @param[in] param Task parameter. * @param[in] param_size Size of the task parameter. - * @param[in] auto_release Flag indicating whether the task must be - * released after its execution. - * @param[in] start_cb Callback executed when the task starts. - * @param[in] end_cb Callback executed when the task ends. * * @return Handle to the new task. */ diff --git a/src/task.c b/src/task.c index bc5801a..788e3ce 100644 --- a/src/task.c +++ b/src/task.c @@ -5,12 +5,12 @@ #include "internal/task_def.h" +// cppcheck-suppress unusedFunction task_t create_sync_task(uint32_t id, void *(*task)(void *), void *param, size_t param_size) { return create_async_task(id, task, param, param_size, false, NULL, NULL); } -// cppcheck-suppress unusedFunction task_t create_async_task(uint32_t id, void *(*task)(void *), void *param, size_t param_size, bool auto_release, void (*start_cb)(uint32_t, void *), From 5e94f20fb0de1720eab8dad6af05f97c1700f300 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Tue, 23 Dec 2025 23:35:48 -0300 Subject: [PATCH 36/63] lib: adding unit tests to check the 'auto release' feature --- src/pool_day.c | 53 +++++++++++++++++++++------------------ test/src/pool_day_test.cc | 30 ++++++++++++++++++++++ 2 files changed, 59 insertions(+), 24 deletions(-) diff --git a/src/pool_day.c b/src/pool_day.c index b8e144f..d18240f 100644 --- a/src/pool_day.c +++ b/src/pool_day.c @@ -36,30 +36,35 @@ __static void *thread_func(void *param) { } task_t entry = dequeue(pool->tasks); - if (entry) { - entry->is_orphan = true; - - if (entry->on_task_start) { - entry->on_task_start(entry->id, entry->param); - } - - POOL_DAY_DEBUG("thread '0x%x' running the task '0x%x'", pthread_self(), - entry->id); - void *ret = entry->task(entry->param); - POOL_DAY_DEBUG("thread '0x%x' finished the task '0x%x'", pthread_self(), - entry->id); - - entry->ret_val = ret; - if (entry->on_task_end) { - POOL_DAY_DEBUG("thread '0x%x' executing end callback for task '0x%x'", - pthread_self(), entry->id); - entry->on_task_end(entry->id, entry->param, entry->ret_val); - } - - sem_post(&entry->ready); - if (entry->auto_release) { - destroy_task(entry); - } + if (!entry) { + POOL_DAY_DEBUG("thread '0x%x' found no task to execute", pthread_self()); + continue; + } + + entry->is_orphan = true; + + if (entry->on_task_start) { + POOL_DAY_DEBUG("thread '0x%x' executing start callback for task '0x%x'", + pthread_self(), entry->id); + entry->on_task_start(entry->id, entry->param); + } + + POOL_DAY_DEBUG("thread '0x%x' running the task '0x%x'", pthread_self(), + entry->id); + void *ret = entry->task(entry->param); + POOL_DAY_DEBUG("thread '0x%x' finished the task '0x%x'", pthread_self(), + entry->id); + + entry->ret_val = ret; + if (entry->on_task_end) { + POOL_DAY_DEBUG("thread '0x%x' executing end callback for task '0x%x'", + pthread_self(), entry->id); + entry->on_task_end(entry->id, entry->param, entry->ret_val); + } + + sem_post(&entry->ready); + if (entry->auto_release) { + destroy_task(entry); } } diff --git a/test/src/pool_day_test.cc b/test/src/pool_day_test.cc index 00dc540..416df83 100644 --- a/test/src/pool_day_test.cc +++ b/test/src/pool_day_test.cc @@ -251,6 +251,36 @@ TEST_F(PoolDayTest, ExecuteTaskWithCallbacks) { destroy_task(task); } +/** + * @brief Given the pool has one task with callbacks with 'auto release' set, + * when the task is scheduled for execution, then the task's callbacks must be + * called at the suitable times and the task must be destroyed. + */ +TEST_F(PoolDayTest, ExecuteTaskWithCallbacksAndAutoRelease) { + auto task = create_async_task(0, CbWrapper::TaskCb, nullptr, 0, true, + CbWrapper::OnTaskStartCb, + CbWrapper::OnTaskEndCb); + char ret_val[]{ "hello, i'm the return of the task" }; + + enqueue_task(pool_, task); + + EXPECT_CALL(CbWrapper::mock(), OnTaskStartCb(task->id, task->param)) + .Times(1); + EXPECT_CALL(CbWrapper::mock(), TaskCb(nullptr)) + .Times(1) + .WillOnce( + InvokeWithoutArgs([&]() { + abort_tasks(pool_); // to break the thread loop + return ret_val; + }) + ); + EXPECT_CALL(CbWrapper::mock(), OnTaskEndCb(task->id, task->param, ret_val)) + .Times(1); + + auto ret = thread_func(pool_); + EXPECT_EQ(ret, nullptr); +} + /** * @brief Given the pool has one task with only start callback, when the task is * scheduled for execution, then the task's start callback must be called. From d8641b918b4f318ee953e2461804cad5aecc7871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Sat, 27 Dec 2025 14:17:56 -0300 Subject: [PATCH 37/63] lib: refactoring thread_func function --- include/task.h | 7 +++++-- src/pool_day.c | 11 ++++++----- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/include/task.h b/include/task.h index bce2486..40c3e53 100644 --- a/include/task.h +++ b/include/task.h @@ -53,12 +53,15 @@ task_t create_sync_task(uint32_t id, void *(*task)(void *), void *param, /** * @brief Create a new asynchronous task. * + * @note When the 'auto_release' parameter is set to false, the created task + * requires a manual release once it's bound to a pool. + * * @param[in] id Task identifier. * @param[in] task Task callback. * @param[in] param Task parameter. * @param[in] param_size Size of the task parameter. - * @param[in] auto_release Flag indicating whether the task must be - * released after its execution. + * @param[in] auto_release Flag indicating whether the task must be released + * after its execution. * @param[in] start_cb Callback executed when the task starts. * @param[in] end_cb Callback executed when the task ends. * diff --git a/src/pool_day.c b/src/pool_day.c index d18240f..fb6ca69 100644 --- a/src/pool_day.c +++ b/src/pool_day.c @@ -26,8 +26,8 @@ __static void *thread_func(void *param) { pool_day_t pool = (pool_day_t)param; while (!pool->must_stop) { + POOL_DAY_DEBUG("thread '0x%x' waiting...", pthread_self()); sem_wait(&pool->lock); - POOL_DAY_DEBUG("thread '0x%x' woke up", pthread_self()); if (pool->must_stop) { @@ -55,15 +55,16 @@ __static void *thread_func(void *param) { POOL_DAY_DEBUG("thread '0x%x' finished the task '0x%x'", pthread_self(), entry->id); - entry->ret_val = ret; if (entry->on_task_end) { POOL_DAY_DEBUG("thread '0x%x' executing end callback for task '0x%x'", pthread_self(), entry->id); - entry->on_task_end(entry->id, entry->param, entry->ret_val); + entry->on_task_end(entry->id, entry->param, ret); } - sem_post(&entry->ready); - if (entry->auto_release) { + if (!entry->auto_release) { + entry->ret_val = ret; + sem_post(&entry->ready); + } else { destroy_task(entry); } } From a0b4200827439753f45c543ad7d8d41bccb17fea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Sat, 6 Dec 2025 23:01:17 -0300 Subject: [PATCH 38/63] samples: removing the C++ sample and moving the C sample to 'hello-world' folder --- samples/CMakeLists.txt | 3 +-- samples/cpp/CMakeLists.txt | 16 ---------------- samples/{c => hello-world}/CMakeLists.txt | 4 ++-- samples/{c => hello-world}/main.c | 8 ++++++-- src/task.c | 2 +- 5 files changed, 10 insertions(+), 23 deletions(-) delete mode 100644 samples/cpp/CMakeLists.txt rename samples/{c => hello-world}/CMakeLists.txt (79%) rename samples/{c => hello-world}/main.c (83%) diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index 26e351e..b6e0099 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -1,4 +1,3 @@ cmake_minimum_required(VERSION 3.18) -add_subdirectory(c) -add_subdirectory(cpp) +add_subdirectory(hello-world) diff --git a/samples/cpp/CMakeLists.txt b/samples/cpp/CMakeLists.txt deleted file mode 100644 index a7c40b5..0000000 --- a/samples/cpp/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -cmake_minimum_required(VERSION 3.18) - -project(pool-day-sample-cpp) - -set(PROJECT_NAME "pool-day-sample-cpp") -set(SOURCES main.cc) - -set(CMAKE_CXX_FLAGS "-Wall -Werror -Wextra") - -include_directories( - ${CMAKE_CURRENT_SOURCE_DIR}/../../include -) -link_directories(${CMAKE_CURRENT_LIST_DIR}/../../../build) - -add_executable(${PROJECT_NAME} ${SOURCES}) -target_link_libraries(${PROJECT_NAME} pool-day) diff --git a/samples/c/CMakeLists.txt b/samples/hello-world/CMakeLists.txt similarity index 79% rename from samples/c/CMakeLists.txt rename to samples/hello-world/CMakeLists.txt index 089a49d..ee394bc 100644 --- a/samples/c/CMakeLists.txt +++ b/samples/hello-world/CMakeLists.txt @@ -1,8 +1,8 @@ cmake_minimum_required(VERSION 3.18) -project(pool-day-sample-c) +project(pool-day-sample-hello-world) -set(PROJECT_NAME "pool-day-sample-c") +set(PROJECT_NAME "pool-day-sample-hello-world") set(SOURCES main.c) set(CMAKE_C_FLAGS "-Wall -Werror -Wextra") diff --git a/samples/c/main.c b/samples/hello-world/main.c similarity index 83% rename from samples/c/main.c rename to samples/hello-world/main.c index a98f8cb..0ad4641 100644 --- a/samples/c/main.c +++ b/samples/hello-world/main.c @@ -7,7 +7,7 @@ #include "pool_day.h" #include "task.h" -#define DUMMY_TASK_ID 0 +#define HELLO_WORLD_TASK_ID 0 void *func(void *param) { char *str = (char *)param; @@ -33,8 +33,12 @@ int main(void) { } char str[] = "foo"; - task_t task = create_sync_task(DUMMY_TASK_ID, func, (void *)str, + task_t task = create_sync_task(HELLO_WORLD_TASK_ID, func, (void *)str, sizeof(char) * strlen(str) + 1); + if (!task) { + // handle error + exit(EXIT_FAILURE); + } assert(enqueue_task(pool, task) == POOL_DAY_SUCCESS); char *ret = (char *)get_task_result(task); diff --git a/src/task.c b/src/task.c index 788e3ce..59822e6 100644 --- a/src/task.c +++ b/src/task.c @@ -39,7 +39,7 @@ task_t create_async_task(uint32_t id, void *(*task)(void *), void *param, void destroy_task(task_t task) { // if the task is orphaned, it means that the user will take care of its release, - // otherwise it is managed by the pool + // otherwise its destruction is managed by the pool if (task && task->is_orphan) { sem_destroy(&task->ready); From 24bf43f0996e80472a886a8fd5f2a4db38dab03e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Sat, 6 Dec 2025 23:09:43 -0300 Subject: [PATCH 39/63] samples: splitting 'hello-world' sample into 'hello-world-sync' and 'hello-world-async' --- samples/CMakeLists.txt | 3 +- samples/hello-world-async/CMakeLists.txt | 16 +++++ samples/hello-world-async/main.c | 67 +++++++++++++++++++ .../CMakeLists.txt | 4 +- .../{hello-world => hello-world-sync}/main.c | 8 +-- 5 files changed, 91 insertions(+), 7 deletions(-) create mode 100644 samples/hello-world-async/CMakeLists.txt create mode 100644 samples/hello-world-async/main.c rename samples/{hello-world => hello-world-sync}/CMakeLists.txt (77%) rename samples/{hello-world => hello-world-sync}/main.c (81%) diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index b6e0099..0c4d60d 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -1,3 +1,4 @@ cmake_minimum_required(VERSION 3.18) -add_subdirectory(hello-world) +add_subdirectory(hello-world-async) +add_subdirectory(hello-world-sync) diff --git a/samples/hello-world-async/CMakeLists.txt b/samples/hello-world-async/CMakeLists.txt new file mode 100644 index 0000000..6236854 --- /dev/null +++ b/samples/hello-world-async/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.18) + +project(pool-day-sample-hello-world-async) + +set(PROJECT_NAME "pool-day-sample-hello-world-async") +set(SOURCES main.c) + +set(CMAKE_C_FLAGS "-Wall -Werror -Wextra") + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/../../include +) +link_directories(${CMAKE_CURRENT_LIST_DIR}/../../../build) + +add_executable(${PROJECT_NAME} ${SOURCES}) +target_link_libraries(${PROJECT_NAME} pool-day) diff --git a/samples/hello-world-async/main.c b/samples/hello-world-async/main.c new file mode 100644 index 0000000..54ed8a4 --- /dev/null +++ b/samples/hello-world-async/main.c @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include + +#include "pool_day.h" +#include "task.h" + +#define HELLO_WORLD_TASK_ID 0 + +volatile bool task_finished = false; + +void task_start_callback(uint32_t tid, void *param) { + (void)param; + printf("task '%u' starting...\n", tid); +} + +void task_end_callback(uint32_t tid, void *param, void *ret_val) { + (void)param; + + task_finished = true; + printf("task '%u' ended with return value: %s\n", tid, (char *)ret_val); +} + +void *func(void *param) { + char *str = (char *)param; + char *ret = (char *)calloc(10, sizeof(char)); + + for (int i = 0; i < 10; i++) { + printf("%s %d\n", str, i); + usleep(1000000); + } + + strcat(ret, "success!"); + + return ret; +} + +int main(void) { + pool_day_t pool; + + pool = create_pool(1); + if (!pool) { + // handle error + exit(EXIT_FAILURE); + } + + char str[] = "hello, world!"; + task_t task = create_async_task(HELLO_WORLD_TASK_ID, func, (void *)str, + sizeof(char) * strlen(str) + 1, false, + task_start_callback, task_end_callback); + + if (!task) { + // handle error + exit(EXIT_FAILURE); + } + + assert(enqueue_task(pool, task) == POOL_DAY_SUCCESS); + + while(!task_finished); + + destroy_task(task); + destroy_pool(&pool); + + exit(EXIT_SUCCESS); +} diff --git a/samples/hello-world/CMakeLists.txt b/samples/hello-world-sync/CMakeLists.txt similarity index 77% rename from samples/hello-world/CMakeLists.txt rename to samples/hello-world-sync/CMakeLists.txt index ee394bc..330e891 100644 --- a/samples/hello-world/CMakeLists.txt +++ b/samples/hello-world-sync/CMakeLists.txt @@ -1,8 +1,8 @@ cmake_minimum_required(VERSION 3.18) -project(pool-day-sample-hello-world) +project(pool-day-sample-hello-world-sync) -set(PROJECT_NAME "pool-day-sample-hello-world") +set(PROJECT_NAME "pool-day-sample-hello-world-sync") set(SOURCES main.c) set(CMAKE_C_FLAGS "-Wall -Werror -Wextra") diff --git a/samples/hello-world/main.c b/samples/hello-world-sync/main.c similarity index 81% rename from samples/hello-world/main.c rename to samples/hello-world-sync/main.c index 0ad4641..59f24fd 100644 --- a/samples/hello-world/main.c +++ b/samples/hello-world-sync/main.c @@ -5,7 +5,6 @@ #include #include "pool_day.h" -#include "task.h" #define HELLO_WORLD_TASK_ID 0 @@ -32,9 +31,10 @@ int main(void) { exit(EXIT_FAILURE); } - char str[] = "foo"; - task_t task = create_sync_task(HELLO_WORLD_TASK_ID, func, (void *)str, - sizeof(char) * strlen(str) + 1); + char str[] = "hello, world!"; + task_t *task = create_task(HELLO_WORLD_TASK_ID, func, (void *)str, + sizeof(char) * strlen(str) + 1, NULL, NULL); + if (!task) { // handle error exit(EXIT_FAILURE); From 6a34899ed4c08f1e0b360bbc189ba22a4c7144af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Sat, 6 Dec 2025 23:12:44 -0300 Subject: [PATCH 40/63] samples: fixing memory leak on hello-world-async sample --- samples/hello-world-async/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/samples/hello-world-async/main.c b/samples/hello-world-async/main.c index 54ed8a4..132c378 100644 --- a/samples/hello-world-async/main.c +++ b/samples/hello-world-async/main.c @@ -21,6 +21,7 @@ void task_end_callback(uint32_t tid, void *param, void *ret_val) { task_finished = true; printf("task '%u' ended with return value: %s\n", tid, (char *)ret_val); + free(ret_val); } void *func(void *param) { From 3c8f2ea841d21ae853946206a84210d577d11ad2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Sat, 6 Dec 2025 23:43:36 -0300 Subject: [PATCH 41/63] samples: adding webserver sample --- samples/CMakeLists.txt | 1 + samples/webserver/CMakeLists.txt | 16 +++ samples/webserver/main.c | 227 +++++++++++++++++++++++++++++++ src/logger.c | 2 +- 4 files changed, 245 insertions(+), 1 deletion(-) create mode 100644 samples/webserver/CMakeLists.txt create mode 100644 samples/webserver/main.c diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index 0c4d60d..7a91fdb 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -2,3 +2,4 @@ cmake_minimum_required(VERSION 3.18) add_subdirectory(hello-world-async) add_subdirectory(hello-world-sync) +add_subdirectory(webserver) diff --git a/samples/webserver/CMakeLists.txt b/samples/webserver/CMakeLists.txt new file mode 100644 index 0000000..60cc25f --- /dev/null +++ b/samples/webserver/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.18) + +project(pool-day-sample-webserver) + +set(PROJECT_NAME "pool-day-sample-webserver") +set(SOURCES main.c) + +set(CMAKE_C_FLAGS "-Wall -Werror -Wextra") + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/../../include +) +link_directories(${CMAKE_CURRENT_LIST_DIR}/../../../build) + +add_executable(${PROJECT_NAME} ${SOURCES}) +target_link_libraries(${PROJECT_NAME} pool-day) diff --git a/samples/webserver/main.c b/samples/webserver/main.c new file mode 100644 index 0000000..699803a --- /dev/null +++ b/samples/webserver/main.c @@ -0,0 +1,227 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pool_day.h" + +#define HANDLE_REQUEST_TASK_ID 0 +#define MAX_VERB_SIZE 7 +#define MAX_BUFFER_SIZE 4096 + +#define DEFAULT_ROOT_DIR "./www" +#define DEFAULT_PORT 8080 +#define DEFAULT_MAX_CLIENTS 10 + +#define STR(x) #x +#define MAKE_ERROR_BODY(status_code, msg) \ + "

" STR(status_code) " " #msg "

" + +typedef struct { + uint32_t max_clients; + uint16_t port; + char root_dir[PATH_MAX]; +} server_cfg_t; + +typedef struct { + char verb[MAX_VERB_SIZE]; + char resource[PATH_MAX]; +} request_t; + +static server_cfg_t cfg; + +// TODO: what parameters should be useful here? +void task_start_callback(uint32_t tid) { + printf("[+] task '%u' starting...\n", tid); +} + +void task_end_callback(uint32_t tid, void *ret_val) { + printf("[+] task '%u' ended with return value: %s\n", tid, (char *)ret_val); +} + +void parse_request(const char *request_buffer, request_t *req) { + // Simple parsing logic for demonstration purposes + sscanf(request_buffer, "%s %s", req->verb, req->resource); +} + +void assemble_reply(char *buffer, int status_code, const char *status_str, + const char *body) { + const char *reply_fmt = + "HTTP/1.1 %d %s\r\n" + "Content-Type: text/html\r\n" + "\r\n" + "%s\r\n"; + + sprintf(buffer, reply_fmt, status_code, status_str, body); +} + +char *get_resource(const char *res) { + char resource[PATH_MAX * 2] = {0}; // FIXME + struct stat st; + + snprintf(resource, sizeof(resource), "%s/%s", cfg.root_dir, res); + + FILE *file = fopen(resource, "r"); + if (!file) { + return NULL; + } + + stat(resource, &st); + + char *content = calloc(1, st.st_size + 1); + if (!content) { + fclose(file); + return NULL; + } + + fread(content, 1, st.st_size, file); + fclose(file); + + return content; +} + +void *handle_client(void *param) { + char buffer[MAX_BUFFER_SIZE] = {0}; + int client_fd = *(int *)(param); + request_t req; + + memset(&req, 0, sizeof(request_t)); + + ssize_t received = recv(client_fd, buffer, sizeof(buffer) - 1, 0); + if (received > 0) { + parse_request(buffer, &req); + + printf("[+] received request: %s %s\n", req.verb, req.resource); + + if (!strcmp(req.verb, "GET")) { + char *res = get_resource(req.resource); + + memset(buffer, 0, sizeof(buffer)); + if (res) { + assemble_reply(buffer, 200, "OK", res); + free(res); + } else { + assemble_reply(buffer, 404, "Not Found", + MAKE_ERROR_BODY(404, Not Found)); + } + + send(client_fd, buffer, strlen(buffer), 0); + } + } + + close(client_fd); + free(param); + + return NULL; +} + +void parse_args(int argc, char **argv) { + int opt; + + // Set default values + cfg.max_clients = DEFAULT_MAX_CLIENTS; + cfg.port = DEFAULT_PORT; + memcpy(cfg.root_dir, DEFAULT_ROOT_DIR, strlen(DEFAULT_ROOT_DIR) + 1); + + while ((opt = getopt(argc, argv, "m:p:r:")) != -1) { + switch (opt) { + case 'm': + cfg.max_clients = (uint32_t)atoi(optarg); + break; + case 'p': + cfg.port = (uint16_t)atoi(optarg); + break; + case 'r': + memcpy(cfg.root_dir, optarg, strlen(optarg) + 1); + break; + } + } +} + +int create_socket(int *sock_fd) { + struct sockaddr_in server_addr; + + *sock_fd = socket(AF_INET, SOCK_STREAM, 0); + if (*sock_fd < 0) { + return 1; + } + + memset(&server_addr, 0, sizeof(server_addr)); + + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = INADDR_ANY; + server_addr.sin_port = htons(cfg.port); + + if (bind(*sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { + close(*sock_fd); + return 1; + } + + if (listen(*sock_fd, cfg.max_clients) < 0) { + close(*sock_fd); + return 1; + } + + return 0; +} + +int run_server(void) { + int server_fd; + pool_day_t pool; + + if (create_socket(&server_fd) != 0) { + printf("[-] failed to setup server socket\n"); + return 1; + } + + if (!(pool = create_pool(cfg.max_clients))) { + close(server_fd); + return 1; + } + + printf("[+] starting server with max_clients=%u, port=%u, root_dir=%s\n", + cfg.max_clients, cfg.port, cfg.root_dir); + + while (1) { + int client_fd = accept(server_fd, NULL, NULL); + + if (client_fd > 0) { + int *fd_ptr = malloc(sizeof(int)); + + *fd_ptr = client_fd; + task_t *task = create_task(HANDLE_REQUEST_TASK_ID, handle_client, + (void *)fd_ptr, sizeof(request_t), + task_start_callback, task_end_callback); + + if (!task) { + printf("[-] failed to create the request task\n"); + close(client_fd); + free(fd_ptr); + continue; + } + + if (enqueue_task(pool, task) != POOL_DAY_SUCCESS) { + printf("[-] failed to enqueue the request task\n"); + destroy_task(task); + close(client_fd); + free(fd_ptr); + } + } + } + + destroy_pool(&pool); + + return 0; +} + +int main(int argc, char **argv) { + parse_args(argc, argv); + + return run_server(); +} diff --git a/src/logger.c b/src/logger.c index 8b86e44..576b5c7 100644 --- a/src/logger.c +++ b/src/logger.c @@ -1,9 +1,9 @@ #include "internal/logger.h" +#include #include #include #include -#include #include "internal/utils.h" From 5dff6003ba477160754b4dbe47c28cf6910255d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Fri, 12 Dec 2025 11:19:09 -0300 Subject: [PATCH 42/63] samples: fixing valgrind issue --- samples/webserver/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/webserver/main.c b/samples/webserver/main.c index 699803a..edba0a8 100644 --- a/samples/webserver/main.c +++ b/samples/webserver/main.c @@ -196,7 +196,7 @@ int run_server(void) { *fd_ptr = client_fd; task_t *task = create_task(HANDLE_REQUEST_TASK_ID, handle_client, - (void *)fd_ptr, sizeof(request_t), + (void *)fd_ptr, sizeof(int), task_start_callback, task_end_callback); if (!task) { From 07d754952e7e4d4b5cbcbed0753406ae0389e3a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Fri, 12 Dec 2025 11:23:16 -0300 Subject: [PATCH 43/63] samples: moving the handle of GET request to handle_get_request function --- samples/webserver/main.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/samples/webserver/main.c b/samples/webserver/main.c index edba0a8..ddc58a8 100644 --- a/samples/webserver/main.c +++ b/samples/webserver/main.c @@ -86,6 +86,18 @@ char *get_resource(const char *res) { return content; } +void handle_get_request(char *reply_buffer, const char *resource) { + char *res = get_resource(resource); + + if (res) { + assemble_reply(reply_buffer, 200, "OK", res); + free(res); + } else { + assemble_reply(reply_buffer, 404, "Not Found", + MAKE_ERROR_BODY(404, Not Found)); + } +} + void *handle_client(void *param) { char buffer[MAX_BUFFER_SIZE] = {0}; int client_fd = *(int *)(param); @@ -99,17 +111,9 @@ void *handle_client(void *param) { printf("[+] received request: %s %s\n", req.verb, req.resource); + memset(buffer, 0, sizeof(buffer)); if (!strcmp(req.verb, "GET")) { - char *res = get_resource(req.resource); - - memset(buffer, 0, sizeof(buffer)); - if (res) { - assemble_reply(buffer, 200, "OK", res); - free(res); - } else { - assemble_reply(buffer, 404, "Not Found", - MAKE_ERROR_BODY(404, Not Found)); - } + handle_get_request(buffer, req.resource); send(client_fd, buffer, strlen(buffer), 0); } From 5f8215fa7b40f39dfd7b5409eb94fdbab3e5cc88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Fri, 12 Dec 2025 11:55:55 -0300 Subject: [PATCH 44/63] samples: renaming samples --- samples/hello-world-async/CMakeLists.txt | 4 +- samples/hello-world-sync/CMakeLists.txt | 4 +- samples/webserver/CMakeLists.txt | 4 +- samples/webserver/main.c | 47 +++++++++++++++--------- 4 files changed, 35 insertions(+), 24 deletions(-) diff --git a/samples/hello-world-async/CMakeLists.txt b/samples/hello-world-async/CMakeLists.txt index 6236854..bf559d6 100644 --- a/samples/hello-world-async/CMakeLists.txt +++ b/samples/hello-world-async/CMakeLists.txt @@ -1,8 +1,8 @@ cmake_minimum_required(VERSION 3.18) -project(pool-day-sample-hello-world-async) +project(hello-world-async) -set(PROJECT_NAME "pool-day-sample-hello-world-async") +set(PROJECT_NAME "hello-world-async") set(SOURCES main.c) set(CMAKE_C_FLAGS "-Wall -Werror -Wextra") diff --git a/samples/hello-world-sync/CMakeLists.txt b/samples/hello-world-sync/CMakeLists.txt index 330e891..79dd6e5 100644 --- a/samples/hello-world-sync/CMakeLists.txt +++ b/samples/hello-world-sync/CMakeLists.txt @@ -1,8 +1,8 @@ cmake_minimum_required(VERSION 3.18) -project(pool-day-sample-hello-world-sync) +project(hello-world-sync) -set(PROJECT_NAME "pool-day-sample-hello-world-sync") +set(PROJECT_NAME "hello-world-sync") set(SOURCES main.c) set(CMAKE_C_FLAGS "-Wall -Werror -Wextra") diff --git a/samples/webserver/CMakeLists.txt b/samples/webserver/CMakeLists.txt index 60cc25f..3203fff 100644 --- a/samples/webserver/CMakeLists.txt +++ b/samples/webserver/CMakeLists.txt @@ -1,8 +1,8 @@ cmake_minimum_required(VERSION 3.18) -project(pool-day-sample-webserver) +project(webserver) -set(PROJECT_NAME "pool-day-sample-webserver") +set(PROJECT_NAME "webserver") set(SOURCES main.c) set(CMAKE_C_FLAGS "-Wall -Werror -Wextra") diff --git a/samples/webserver/main.c b/samples/webserver/main.c index ddc58a8..f3ecc01 100644 --- a/samples/webserver/main.c +++ b/samples/webserver/main.c @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -7,6 +6,7 @@ #include #include #include +#include #include #include "pool_day.h" @@ -50,29 +50,29 @@ void parse_request(const char *request_buffer, request_t *req) { sscanf(request_buffer, "%s %s", req->verb, req->resource); } -void assemble_reply(char *buffer, int status_code, const char *status_str, - const char *body) { +void assemble_reply(char *buffer, size_t buffer_size, int status_code, + const char *status_str, const char *body) { const char *reply_fmt = "HTTP/1.1 %d %s\r\n" "Content-Type: text/html\r\n" "\r\n" "%s\r\n"; - sprintf(buffer, reply_fmt, status_code, status_str, body); + snprintf(buffer, buffer_size, reply_fmt, status_code, status_str, body); } -char *get_resource(const char *res) { - char resource[PATH_MAX * 2] = {0}; // FIXME +char *get_resource(const char *res_name) { + char res_path[PATH_MAX * 2] = {0}; // FIXME struct stat st; - snprintf(resource, sizeof(resource), "%s/%s", cfg.root_dir, res); + snprintf(res_path, sizeof(res_path), "%s/%s", cfg.root_dir, res_name); - FILE *file = fopen(resource, "r"); + FILE *file = fopen(res_path, "r"); if (!file) { return NULL; } - stat(resource, &st); + stat(res_path, &st); char *content = calloc(1, st.st_size + 1); if (!content) { @@ -80,20 +80,26 @@ char *get_resource(const char *res) { return NULL; } - fread(content, 1, st.st_size, file); + if (!fread(content, 1, st.st_size, file)) { + free(content); + fclose(file); + return NULL; + } + fclose(file); return content; } -void handle_get_request(char *reply_buffer, const char *resource) { +void handle_get_request(char *reply_buffer, size_t buffer_size, + const char *resource) { char *res = get_resource(resource); if (res) { - assemble_reply(reply_buffer, 200, "OK", res); + assemble_reply(reply_buffer, buffer_size, 200, "OK", res); free(res); } else { - assemble_reply(reply_buffer, 404, "Not Found", + assemble_reply(reply_buffer, buffer_size, 404, "Not Found", MAKE_ERROR_BODY(404, Not Found)); } } @@ -113,7 +119,7 @@ void *handle_client(void *param) { memset(buffer, 0, sizeof(buffer)); if (!strcmp(req.verb, "GET")) { - handle_get_request(buffer, req.resource); + handle_get_request(buffer, MAX_BUFFER_SIZE, req.resource); send(client_fd, buffer, strlen(buffer), 0); } @@ -128,10 +134,14 @@ void *handle_client(void *param) { void parse_args(int argc, char **argv) { int opt; - // Set default values - cfg.max_clients = DEFAULT_MAX_CLIENTS; - cfg.port = DEFAULT_PORT; - memcpy(cfg.root_dir, DEFAULT_ROOT_DIR, strlen(DEFAULT_ROOT_DIR) + 1); + if (argc == 1) { + // Set default values + cfg.max_clients = DEFAULT_MAX_CLIENTS; + cfg.port = DEFAULT_PORT; + memcpy(cfg.root_dir, DEFAULT_ROOT_DIR, strlen(DEFAULT_ROOT_DIR) + 1); + + return; + } while ((opt = getopt(argc, argv, "m:p:r:")) != -1) { switch (opt) { @@ -219,6 +229,7 @@ int run_server(void) { } } + close(server_fd); destroy_pool(&pool); return 0; From 328e5bae7e3f7c742016855ec44d348176eae0c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Fri, 12 Dec 2025 19:06:08 -0300 Subject: [PATCH 45/63] samples: fixing double free issue --- samples/webserver/main.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/samples/webserver/main.c b/samples/webserver/main.c index f3ecc01..d04c244 100644 --- a/samples/webserver/main.c +++ b/samples/webserver/main.c @@ -9,15 +9,14 @@ #include #include -#include "pool_day.h" +#include -#define HANDLE_REQUEST_TASK_ID 0 #define MAX_VERB_SIZE 7 #define MAX_BUFFER_SIZE 4096 #define DEFAULT_ROOT_DIR "./www" #define DEFAULT_PORT 8080 -#define DEFAULT_MAX_CLIENTS 10 +#define DEFAULT_MAX_CLIENTS 100 #define STR(x) #x #define MAKE_ERROR_BODY(status_code, msg) \ @@ -55,8 +54,7 @@ void assemble_reply(char *buffer, size_t buffer_size, int status_code, const char *reply_fmt = "HTTP/1.1 %d %s\r\n" "Content-Type: text/html\r\n" - "\r\n" - "%s\r\n"; + "\r\n%s"; snprintf(buffer, buffer_size, reply_fmt, status_code, status_str, body); } @@ -106,7 +104,7 @@ void handle_get_request(char *reply_buffer, size_t buffer_size, void *handle_client(void *param) { char buffer[MAX_BUFFER_SIZE] = {0}; - int client_fd = *(int *)(param); + uint32_t client_fd = *(uint32_t *)(param); request_t req; memset(&req, 0, sizeof(request_t)); @@ -126,7 +124,6 @@ void *handle_client(void *param) { } close(client_fd); - free(param); return NULL; } @@ -135,7 +132,7 @@ void parse_args(int argc, char **argv) { int opt; if (argc == 1) { - // Set default values + // default config cfg.max_clients = DEFAULT_MAX_CLIENTS; cfg.port = DEFAULT_PORT; memcpy(cfg.root_dir, DEFAULT_ROOT_DIR, strlen(DEFAULT_ROOT_DIR) + 1); @@ -206,12 +203,12 @@ int run_server(void) { int client_fd = accept(server_fd, NULL, NULL); if (client_fd > 0) { - int *fd_ptr = malloc(sizeof(int)); + uint32_t *fd_ptr = malloc(sizeof(uint32_t)); - *fd_ptr = client_fd; - task_t *task = create_task(HANDLE_REQUEST_TASK_ID, handle_client, - (void *)fd_ptr, sizeof(int), - task_start_callback, task_end_callback); + *fd_ptr = (uint32_t)client_fd; + task_t *task = create_task(client_fd, handle_client, (void *)fd_ptr, + sizeof(uint32_t), task_start_callback, + task_end_callback); if (!task) { printf("[-] failed to create the request task\n"); From 1e168a9da99925985af9585eac7e7585e114c4e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Sun, 14 Dec 2025 23:24:18 -0300 Subject: [PATCH 46/63] lib: passing the task 'param' field to task callbacks --- samples/webserver/main.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/samples/webserver/main.c b/samples/webserver/main.c index d04c244..8cc7913 100644 --- a/samples/webserver/main.c +++ b/samples/webserver/main.c @@ -36,21 +36,23 @@ typedef struct { static server_cfg_t cfg; // TODO: what parameters should be useful here? -void task_start_callback(uint32_t tid) { +static void task_start_callback(uint32_t tid, void *param) { + (void)param; printf("[+] task '%u' starting...\n", tid); } -void task_end_callback(uint32_t tid, void *ret_val) { +static void task_end_callback(uint32_t tid, void *param, void *ret_val) { + (void)param; printf("[+] task '%u' ended with return value: %s\n", tid, (char *)ret_val); } -void parse_request(const char *request_buffer, request_t *req) { +static void parse_request(const char *request_buffer, request_t *req) { // Simple parsing logic for demonstration purposes sscanf(request_buffer, "%s %s", req->verb, req->resource); } -void assemble_reply(char *buffer, size_t buffer_size, int status_code, - const char *status_str, const char *body) { +static void assemble_reply(char *buffer, size_t buffer_size, int status_code, + const char *status_str, const char *body) { const char *reply_fmt = "HTTP/1.1 %d %s\r\n" "Content-Type: text/html\r\n" @@ -59,7 +61,7 @@ void assemble_reply(char *buffer, size_t buffer_size, int status_code, snprintf(buffer, buffer_size, reply_fmt, status_code, status_str, body); } -char *get_resource(const char *res_name) { +static char *get_resource(const char *res_name) { char res_path[PATH_MAX * 2] = {0}; // FIXME struct stat st; @@ -89,8 +91,8 @@ char *get_resource(const char *res_name) { return content; } -void handle_get_request(char *reply_buffer, size_t buffer_size, - const char *resource) { +static void handle_get_request(char *reply_buffer, size_t buffer_size, + const char *resource) { char *res = get_resource(resource); if (res) { @@ -102,7 +104,7 @@ void handle_get_request(char *reply_buffer, size_t buffer_size, } } -void *handle_client(void *param) { +static void *handle_client(void *param) { char buffer[MAX_BUFFER_SIZE] = {0}; uint32_t client_fd = *(uint32_t *)(param); request_t req; @@ -128,7 +130,7 @@ void *handle_client(void *param) { return NULL; } -void parse_args(int argc, char **argv) { +static void parse_args(int argc, char **argv) { int opt; if (argc == 1) { @@ -155,7 +157,7 @@ void parse_args(int argc, char **argv) { } } -int create_socket(int *sock_fd) { +static int create_socket(int *sock_fd) { struct sockaddr_in server_addr; *sock_fd = socket(AF_INET, SOCK_STREAM, 0); @@ -182,7 +184,7 @@ int create_socket(int *sock_fd) { return 0; } -int run_server(void) { +static int run_server(void) { int server_fd; pool_day_t pool; From 629cdbd79d5a09bf6d6f6def7eec242abb2c9822 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Sun, 14 Dec 2025 23:38:15 -0300 Subject: [PATCH 47/63] samples: updating the webserver sample --- samples/webserver/main.c | 116 +++++++++++++++++++++------------------ 1 file changed, 63 insertions(+), 53 deletions(-) diff --git a/samples/webserver/main.c b/samples/webserver/main.c index 8cc7913..6ea4a57 100644 --- a/samples/webserver/main.c +++ b/samples/webserver/main.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -33,21 +34,27 @@ typedef struct { char resource[PATH_MAX]; } request_t; -static server_cfg_t cfg; +typedef struct { + uint32_t fd; + struct in_addr addr; + const char *root_dir; +} client_t; + +static void on_client_connected(uint32_t tid, void *param) { + client_t *cli = (client_t *)param; -// TODO: what parameters should be useful here? -static void task_start_callback(uint32_t tid, void *param) { - (void)param; - printf("[+] task '%u' starting...\n", tid); + printf("[+] task[%u]: client connected from %s\n", tid, inet_ntoa(cli->addr)); } -static void task_end_callback(uint32_t tid, void *param, void *ret_val) { - (void)param; - printf("[+] task '%u' ended with return value: %s\n", tid, (char *)ret_val); +static void on_client_disconnected(uint32_t tid, void *param, void *ret_val) { + client_t *cli = (client_t *)param; + + printf("[+] task[%u]: client disconnected from %s, result = %s\n", tid, + inet_ntoa(cli->addr), (char *)ret_val); } static void parse_request(const char *request_buffer, request_t *req) { - // Simple parsing logic for demonstration purposes + // simple parsing logic for demonstration purposes sscanf(request_buffer, "%s %s", req->verb, req->resource); } @@ -61,11 +68,11 @@ static void assemble_reply(char *buffer, size_t buffer_size, int status_code, snprintf(buffer, buffer_size, reply_fmt, status_code, status_str, body); } -static char *get_resource(const char *res_name) { +static char *get_resource(const char *res_name, const char *root_dir) { char res_path[PATH_MAX * 2] = {0}; // FIXME struct stat st; - snprintf(res_path, sizeof(res_path), "%s/%s", cfg.root_dir, res_name); + snprintf(res_path, sizeof(res_path), "%s/%s", root_dir, res_name); FILE *file = fopen(res_path, "r"); if (!file) { @@ -92,8 +99,8 @@ static char *get_resource(const char *res_name) { } static void handle_get_request(char *reply_buffer, size_t buffer_size, - const char *resource) { - char *res = get_resource(resource); + const char *resource, const char *root_dir) { + char *res = get_resource(resource, root_dir); if (res) { assemble_reply(reply_buffer, buffer_size, 200, "OK", res); @@ -106,12 +113,12 @@ static void handle_get_request(char *reply_buffer, size_t buffer_size, static void *handle_client(void *param) { char buffer[MAX_BUFFER_SIZE] = {0}; - uint32_t client_fd = *(uint32_t *)(param); + client_t *cli = (client_t *)param; request_t req; memset(&req, 0, sizeof(request_t)); - ssize_t received = recv(client_fd, buffer, sizeof(buffer) - 1, 0); + ssize_t received = recv(cli->fd, buffer, sizeof(buffer) - 1, 0); if (received > 0) { parse_request(buffer, &req); @@ -119,45 +126,48 @@ static void *handle_client(void *param) { memset(buffer, 0, sizeof(buffer)); if (!strcmp(req.verb, "GET")) { - handle_get_request(buffer, MAX_BUFFER_SIZE, req.resource); + handle_get_request(buffer, MAX_BUFFER_SIZE, req.resource, cli->root_dir); - send(client_fd, buffer, strlen(buffer), 0); + send(cli->fd, buffer, strlen(buffer), 0); } } - close(client_fd); + close(cli->fd); return NULL; } -static void parse_args(int argc, char **argv) { +static void parse_args(int argc, char **argv, server_cfg_t *cfg) { int opt; - if (argc == 1) { - // default config - cfg.max_clients = DEFAULT_MAX_CLIENTS; - cfg.port = DEFAULT_PORT; - memcpy(cfg.root_dir, DEFAULT_ROOT_DIR, strlen(DEFAULT_ROOT_DIR) + 1); - - return; - } - while ((opt = getopt(argc, argv, "m:p:r:")) != -1) { switch (opt) { case 'm': - cfg.max_clients = (uint32_t)atoi(optarg); + cfg->max_clients = (uint32_t)atoi(optarg); break; case 'p': - cfg.port = (uint16_t)atoi(optarg); + cfg->port = (uint16_t)atoi(optarg); break; case 'r': - memcpy(cfg.root_dir, optarg, strlen(optarg) + 1); + memcpy(cfg->root_dir, optarg, strlen(optarg) + 1); break; } } + + if (!cfg->max_clients) { + cfg->max_clients = DEFAULT_MAX_CLIENTS; + } + + if (!cfg->port) { + cfg->port = DEFAULT_PORT; + } + + if (cfg->root_dir[0] == '\0') { + memcpy(cfg->root_dir, DEFAULT_ROOT_DIR, strlen(DEFAULT_ROOT_DIR) + 1); + } } -static int create_socket(int *sock_fd) { +static int setup_socket(int *sock_fd, const server_cfg_t *cfg) { struct sockaddr_in server_addr; *sock_fd = socket(AF_INET, SOCK_STREAM, 0); @@ -169,14 +179,14 @@ static int create_socket(int *sock_fd) { server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = INADDR_ANY; - server_addr.sin_port = htons(cfg.port); + server_addr.sin_port = htons(cfg->port); if (bind(*sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { close(*sock_fd); return 1; } - if (listen(*sock_fd, cfg.max_clients) < 0) { + if (listen(*sock_fd, cfg->max_clients) < 0) { close(*sock_fd); return 1; } @@ -184,46 +194,44 @@ static int create_socket(int *sock_fd) { return 0; } -static int run_server(void) { +static int run_server(const server_cfg_t *cfg) { int server_fd; pool_day_t pool; + struct sockaddr_in cli_addr; + socklen_t cli_len = sizeof(cli_addr); - if (create_socket(&server_fd) != 0) { + if (setup_socket(&server_fd, cfg) != 0) { printf("[-] failed to setup server socket\n"); return 1; } - if (!(pool = create_pool(cfg.max_clients))) { + if (!(pool = create_pool(cfg->max_clients))) { close(server_fd); return 1; } printf("[+] starting server with max_clients=%u, port=%u, root_dir=%s\n", - cfg.max_clients, cfg.port, cfg.root_dir); + cfg->max_clients, cfg->port, cfg->root_dir); while (1) { - int client_fd = accept(server_fd, NULL, NULL); + int client_fd = accept(server_fd, (struct sockaddr *)&cli_addr, &cli_len); if (client_fd > 0) { - uint32_t *fd_ptr = malloc(sizeof(uint32_t)); + client_t *cli_ptr = calloc(1, sizeof(client_t)); - *fd_ptr = (uint32_t)client_fd; - task_t *task = create_task(client_fd, handle_client, (void *)fd_ptr, - sizeof(uint32_t), task_start_callback, - task_end_callback); + cli_ptr->fd = (uint32_t)client_fd; + cli_ptr->root_dir = cfg->root_dir; + memcpy(&cli_ptr->addr, &cli_addr.sin_addr, sizeof(struct in_addr)); - if (!task) { - printf("[-] failed to create the request task\n"); - close(client_fd); - free(fd_ptr); - continue; - } + task_t *task = create_task(client_fd, handle_client, (void *)cli_ptr, + sizeof(client_t), on_client_connected, + on_client_disconnected); if (enqueue_task(pool, task) != POOL_DAY_SUCCESS) { printf("[-] failed to enqueue the request task\n"); destroy_task(task); close(client_fd); - free(fd_ptr); + free(cli_ptr); } } } @@ -235,7 +243,9 @@ static int run_server(void) { } int main(int argc, char **argv) { - parse_args(argc, argv); + server_cfg_t cfg; - return run_server(); + memset(&cfg, 0, sizeof(server_cfg_t)); + parse_args(argc, argv, &cfg); + return run_server(&cfg); } From 23250e0f39d6d4892b4f58f05b4067e959f8b3d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Mon, 15 Dec 2025 21:18:39 -0300 Subject: [PATCH 48/63] lib: making 'param' argument constant on task callbacks --- include/internal/task_def.h | 22 ++++++------- samples/hello-world-async/main.c | 4 +-- samples/webserver/main.c | 54 +++++++++++++++++--------------- test/src/mock/callback_mock.cc | 4 +-- test/src/mock/callback_mock.h | 8 ++--- 5 files changed, 48 insertions(+), 44 deletions(-) diff --git a/include/internal/task_def.h b/include/internal/task_def.h index 858b49e..ddfdb97 100644 --- a/include/internal/task_def.h +++ b/include/internal/task_def.h @@ -36,17 +36,17 @@ extern "C" { * @brief Pool task definition. */ struct task { - uint32_t id; //!< Task identifier. - bool is_orphan; //!< Flag indicating whether the task is orphaned. - bool auto_release; //!< Flag indicating whether the task must be released after its execution. - struct task *next; //!< Next element of the current instance. - struct task *prev; //!< Previous element of the current instance. - void *(*task)(void *); //!< Task callback. - void *param; //!< Parameter of the task callback. - void *ret_val; //!< Task return value. - void (*on_task_start)(uint32_t, void *); //!< Callback executed when the task starts. - void (*on_task_end)(uint32_t, void *, void *); //!< Callback executed when the task ends. - sem_t ready; //!< Task's semaphore. + uint32_t id; //!< Task identifier. + bool is_orphan; //!< Flag indicating whether the task is orphaned. + bool auto_release; //!< Flag indicating whether the task must be released after its execution. + struct task *next; //!< Next element of the current instance. + struct task *prev; //!< Previous element of the current instance. + void *(*task)(void *); //!< Task callback. + void *param; //!< Parameter of the task callback. + void *ret_val; //!< Task return value. + void (*on_task_start)(uint32_t, const void *); //!< Callback executed when the task starts. + void (*on_task_end)(uint32_t, const void *, void *); //!< Callback executed when the task ends. + sem_t ready; //!< Task's semaphore. }; typedef struct task *task_t; //!< Structure representing an item on the task queue. diff --git a/samples/hello-world-async/main.c b/samples/hello-world-async/main.c index 132c378..199a9de 100644 --- a/samples/hello-world-async/main.c +++ b/samples/hello-world-async/main.c @@ -11,12 +11,12 @@ volatile bool task_finished = false; -void task_start_callback(uint32_t tid, void *param) { +void task_start_callback(uint32_t tid, const void *param) { (void)param; printf("task '%u' starting...\n", tid); } -void task_end_callback(uint32_t tid, void *param, void *ret_val) { +void task_end_callback(uint32_t tid, const void *param, void *ret_val) { (void)param; task_finished = true; diff --git a/samples/webserver/main.c b/samples/webserver/main.c index 6ea4a57..a0ecd1a 100644 --- a/samples/webserver/main.c +++ b/samples/webserver/main.c @@ -1,4 +1,3 @@ -#include #include #include #include @@ -37,25 +36,26 @@ typedef struct { typedef struct { uint32_t fd; struct in_addr addr; - const char *root_dir; } client_t; -static void on_client_connected(uint32_t tid, void *param) { - client_t *cli = (client_t *)param; +static void on_client_connected(uint32_t tid, const void *param) { + const client_t *cli = (client_t *)param; printf("[+] task[%u]: client connected from %s\n", tid, inet_ntoa(cli->addr)); } -static void on_client_disconnected(uint32_t tid, void *param, void *ret_val) { - client_t *cli = (client_t *)param; +static void on_client_disconnected(uint32_t tid, const void *param, + void *ret_val) { + const client_t *cli = (client_t *)param; - printf("[+] task[%u]: client disconnected from %s, result = %s\n", tid, - inet_ntoa(cli->addr), (char *)ret_val); + printf("[+] task[%u]: client disconnected from %s, result=%u\n", tid, + inet_ntoa(cli->addr), *((uint16_t *)ret_val)); + free(ret_val); } -static void parse_request(const char *request_buffer, request_t *req) { +static void parse_request(const char *buffer, request_t *req) { // simple parsing logic for demonstration purposes - sscanf(request_buffer, "%s %s", req->verb, req->resource); + sscanf(buffer, "%s %s", req->verb, req->resource); } static void assemble_reply(char *buffer, size_t buffer_size, int status_code, @@ -68,18 +68,15 @@ static void assemble_reply(char *buffer, size_t buffer_size, int status_code, snprintf(buffer, buffer_size, reply_fmt, status_code, status_str, body); } -static char *get_resource(const char *res_name, const char *root_dir) { - char res_path[PATH_MAX * 2] = {0}; // FIXME +static char *get_resource(const char *res_name) { struct stat st; - snprintf(res_path, sizeof(res_path), "%s/%s", root_dir, res_name); - - FILE *file = fopen(res_path, "r"); + FILE *file = fopen(res_name, "r"); if (!file) { return NULL; } - stat(res_path, &st); + stat(res_name, &st); char *content = calloc(1, st.st_size + 1); if (!content) { @@ -98,22 +95,28 @@ static char *get_resource(const char *res_name, const char *root_dir) { return content; } -static void handle_get_request(char *reply_buffer, size_t buffer_size, - const char *resource, const char *root_dir) { - char *res = get_resource(resource, root_dir); +static int handle_get_request(char *reply_buffer, size_t buffer_size, + const char *resource) { + char *res = get_resource(resource); + int status_code; if (res) { - assemble_reply(reply_buffer, buffer_size, 200, "OK", res); + status_code = 200; + assemble_reply(reply_buffer, buffer_size, status_code, "OK", res); free(res); } else { - assemble_reply(reply_buffer, buffer_size, 404, "Not Found", - MAKE_ERROR_BODY(404, Not Found)); + status_code = 404; + assemble_reply(reply_buffer, buffer_size, status_code, "Not Found", + MAKE_ERROR_BODY(status_code, Not Found)); } + + return status_code; } static void *handle_client(void *param) { char buffer[MAX_BUFFER_SIZE] = {0}; client_t *cli = (client_t *)param; + int *ret = calloc(1, sizeof(int)); request_t req; memset(&req, 0, sizeof(request_t)); @@ -126,7 +129,7 @@ static void *handle_client(void *param) { memset(buffer, 0, sizeof(buffer)); if (!strcmp(req.verb, "GET")) { - handle_get_request(buffer, MAX_BUFFER_SIZE, req.resource, cli->root_dir); + *ret = handle_get_request(buffer, MAX_BUFFER_SIZE, &req.resource[1]); send(cli->fd, buffer, strlen(buffer), 0); } @@ -134,7 +137,7 @@ static void *handle_client(void *param) { close(cli->fd); - return NULL; + return (void *)ret; } static void parse_args(int argc, char **argv, server_cfg_t *cfg) { @@ -213,6 +216,8 @@ static int run_server(const server_cfg_t *cfg) { printf("[+] starting server with max_clients=%u, port=%u, root_dir=%s\n", cfg->max_clients, cfg->port, cfg->root_dir); + chdir(cfg->root_dir); + while (1) { int client_fd = accept(server_fd, (struct sockaddr *)&cli_addr, &cli_len); @@ -220,7 +225,6 @@ static int run_server(const server_cfg_t *cfg) { client_t *cli_ptr = calloc(1, sizeof(client_t)); cli_ptr->fd = (uint32_t)client_fd; - cli_ptr->root_dir = cfg->root_dir; memcpy(&cli_ptr->addr, &cli_addr.sin_addr, sizeof(struct in_addr)); task_t *task = create_task(client_fd, handle_client, (void *)cli_ptr, diff --git a/test/src/mock/callback_mock.cc b/test/src/mock/callback_mock.cc index a1a2dee..ce71b3a 100644 --- a/test/src/mock/callback_mock.cc +++ b/test/src/mock/callback_mock.cc @@ -16,11 +16,11 @@ void *CbWrapper::TaskCb(void *param) { return mock_->TaskCb(param); } -void CbWrapper::OnTaskStartCb(uint32_t tid, void *param) { +void CbWrapper::OnTaskStartCb(uint32_t tid, const void *param) { mock_->OnTaskStartCb(tid, param); } -void CbWrapper::OnTaskEndCb(uint32_t tid, void *param, void *ret_val) { +void CbWrapper::OnTaskEndCb(uint32_t tid, const void *param, void *ret_val) { mock_->OnTaskEndCb(tid, param, ret_val); } diff --git a/test/src/mock/callback_mock.h b/test/src/mock/callback_mock.h index 3bd8b00..8190454 100644 --- a/test/src/mock/callback_mock.h +++ b/test/src/mock/callback_mock.h @@ -6,8 +6,8 @@ class TaskMock { public: MOCK_METHOD(void *, TaskCb, (void *)); - MOCK_METHOD(void, OnTaskStartCb, (uint32_t, void *)); - MOCK_METHOD(void, OnTaskEndCb, (uint32_t, void *, void *)); + MOCK_METHOD(void, OnTaskStartCb, (uint32_t, const void *)); + MOCK_METHOD(void, OnTaskEndCb, (uint32_t, const void *, void *)); }; class CbWrapper { @@ -16,8 +16,8 @@ class CbWrapper { ~CbWrapper(); static void *TaskCb(void *param); - static void OnTaskStartCb(uint32_t tid, void *param); - static void OnTaskEndCb(uint32_t tid, void *param, void *ret_val); + static void OnTaskStartCb(uint32_t tid, const void *param); + static void OnTaskEndCb(uint32_t tid, const void *param, void *ret_val); static TaskMock &mock(); private: From 9787e01ca21e38c001423d748d4385a7c9dec747 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Mon, 15 Dec 2025 21:45:35 -0300 Subject: [PATCH 49/63] samples: adding README file to the samples --- samples/hello-world-async/README.md | 3 +++ samples/hello-world-sync/README.md | 3 +++ samples/webserver/README.md | 7 +++++++ samples/webserver/main.c | 16 ++++++++++++---- 4 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 samples/hello-world-async/README.md create mode 100644 samples/hello-world-sync/README.md create mode 100644 samples/webserver/README.md diff --git a/samples/hello-world-async/README.md b/samples/hello-world-async/README.md new file mode 100644 index 0000000..60a0853 --- /dev/null +++ b/samples/hello-world-async/README.md @@ -0,0 +1,3 @@ +## hello-world-async + +This is a simple asynchronous "Hello, World!" program written using the library. diff --git a/samples/hello-world-sync/README.md b/samples/hello-world-sync/README.md new file mode 100644 index 0000000..1688005 --- /dev/null +++ b/samples/hello-world-sync/README.md @@ -0,0 +1,3 @@ +## hello-world-sync + +This is a simple synchronous "Hello, World!" program written using the library. diff --git a/samples/webserver/README.md b/samples/webserver/README.md new file mode 100644 index 0000000..28ecf7e --- /dev/null +++ b/samples/webserver/README.md @@ -0,0 +1,7 @@ +## webserver + +This is an extremely simple web server built with the library. For demonstration purposes, it only handles GET requests. Supported command line options: + +- `-m`: Maximum number of concurrent clients (default: 100) +- `-p`: Port number to listen on (default: 8080) +- `-r`: Root directory to serve files from (default: ./www) diff --git a/samples/webserver/main.c b/samples/webserver/main.c index a0ecd1a..773df19 100644 --- a/samples/webserver/main.c +++ b/samples/webserver/main.c @@ -38,6 +38,14 @@ typedef struct { struct in_addr addr; } client_t; +// TODO: +// - select +// - error handling +// - const and restrict +// - better request parsing? +// - logging? +// - graceful server exit + static void on_client_connected(uint32_t tid, const void *param) { const client_t *cli = (client_t *)param; @@ -96,7 +104,7 @@ static char *get_resource(const char *res_name) { } static int handle_get_request(char *reply_buffer, size_t buffer_size, - const char *resource) { + const char *resource) { char *res = get_resource(resource); int status_code; @@ -107,7 +115,7 @@ static int handle_get_request(char *reply_buffer, size_t buffer_size, } else { status_code = 404; assemble_reply(reply_buffer, buffer_size, status_code, "Not Found", - MAKE_ERROR_BODY(status_code, Not Found)); + MAKE_ERROR_BODY(404, Not Found)); } return status_code; @@ -140,7 +148,7 @@ static void *handle_client(void *param) { return (void *)ret; } -static void parse_args(int argc, char **argv, server_cfg_t *cfg) { +static void parse_args(int argc, char **argv, server_cfg_t *restrict cfg) { int opt; while ((opt = getopt(argc, argv, "m:p:r:")) != -1) { @@ -170,7 +178,7 @@ static void parse_args(int argc, char **argv, server_cfg_t *cfg) { } } -static int setup_socket(int *sock_fd, const server_cfg_t *cfg) { +static int setup_socket(int *restrict sock_fd, const server_cfg_t *cfg) { struct sockaddr_in server_addr; *sock_fd = socket(AF_INET, SOCK_STREAM, 0); From 51fdd71593f044dab007a46ab9d3020e6530627e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Thu, 18 Dec 2025 00:30:54 -0300 Subject: [PATCH 50/63] samples: fixing compilation errors --- include/task.h | 4 ++-- samples/hello-world-sync/main.c | 5 +++-- samples/webserver/main.c | 9 +++++---- src/task.c | 4 ++-- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/include/task.h b/include/task.h index 40c3e53..69e94bc 100644 --- a/include/task.h +++ b/include/task.h @@ -69,8 +69,8 @@ task_t create_sync_task(uint32_t id, void *(*task)(void *), void *param, */ task_t create_async_task(uint32_t id, void *(*task)(void *), void *param, size_t param_size, bool auto_release, - void (*start_cb)(uint32_t, void *), - void (*end_cb)(uint32_t, void *, void *)); + void (*start_cb)(uint32_t, const void *), + void (*end_cb)(uint32_t, const void *, void *)); /** * @brief Destroy a task. diff --git a/samples/hello-world-sync/main.c b/samples/hello-world-sync/main.c index 59f24fd..eb040fd 100644 --- a/samples/hello-world-sync/main.c +++ b/samples/hello-world-sync/main.c @@ -5,6 +5,7 @@ #include #include "pool_day.h" +#include "task.h" #define HELLO_WORLD_TASK_ID 0 @@ -32,8 +33,8 @@ int main(void) { } char str[] = "hello, world!"; - task_t *task = create_task(HELLO_WORLD_TASK_ID, func, (void *)str, - sizeof(char) * strlen(str) + 1, NULL, NULL); + task_t task = create_task(HELLO_WORLD_TASK_ID, func, (void *)str, + sizeof(char) * strlen(str) + 1, NULL, NULL); if (!task) { // handle error diff --git a/samples/webserver/main.c b/samples/webserver/main.c index 773df19..091ab60 100644 --- a/samples/webserver/main.c +++ b/samples/webserver/main.c @@ -9,7 +9,8 @@ #include #include -#include +#include "pool_day.h" +#include "task.h" #define MAX_VERB_SIZE 7 #define MAX_BUFFER_SIZE 4096 @@ -235,9 +236,9 @@ static int run_server(const server_cfg_t *cfg) { cli_ptr->fd = (uint32_t)client_fd; memcpy(&cli_ptr->addr, &cli_addr.sin_addr, sizeof(struct in_addr)); - task_t *task = create_task(client_fd, handle_client, (void *)cli_ptr, - sizeof(client_t), on_client_connected, - on_client_disconnected); + task_t task = create_task(client_fd, handle_client, (void *)cli_ptr, + sizeof(client_t), on_client_connected, + on_client_disconnected); if (enqueue_task(pool, task) != POOL_DAY_SUCCESS) { printf("[-] failed to enqueue the request task\n"); diff --git a/src/task.c b/src/task.c index 59822e6..8dcfa3c 100644 --- a/src/task.c +++ b/src/task.c @@ -13,8 +13,8 @@ task_t create_sync_task(uint32_t id, void *(*task)(void *), void *param, task_t create_async_task(uint32_t id, void *(*task)(void *), void *param, size_t param_size, bool auto_release, - void (*start_cb)(uint32_t, void *), - void (*end_cb)(uint32_t, void *, void *)) { + void (*start_cb)(uint32_t, const void *), + void (*end_cb)(uint32_t, const void *, void *)) { task_t node; node = (task_t)calloc(1, sizeof(*node)); From 0e249bdaae18fadfd117bce1abffd40a89e4028b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Sat, 27 Dec 2025 19:37:45 -0300 Subject: [PATCH 51/63] lib: adding debugging logs to enqueue_task function --- src/pool_day.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/pool_day.c b/src/pool_day.c index fb6ca69..760551f 100644 --- a/src/pool_day.c +++ b/src/pool_day.c @@ -81,6 +81,10 @@ pool_day_retcode_t enqueue_task(pool_day_t pool, task_t task) { return POOL_DAY_ERROR_NULL_PARAM; } + POOL_DAY_DEBUG("task ID: %u", task->id); + POOL_DAY_DEBUG("task callback: %p", task->task); + POOL_DAY_DEBUG("task parameter: %p", task->param); + task->ret_val = NULL; task->is_orphan = false; From 39bf4f088d10d64354802988f432e30f031059fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Sat, 27 Dec 2025 19:53:24 -0300 Subject: [PATCH 52/63] samples: fixing compile errors --- samples/hello-world-async/main.c | 5 +---- samples/hello-world-sync/main.c | 4 ++-- samples/webserver/main.c | 7 ++++--- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/samples/hello-world-async/main.c b/samples/hello-world-async/main.c index 199a9de..f294b2a 100644 --- a/samples/hello-world-async/main.c +++ b/samples/hello-world-async/main.c @@ -9,8 +9,6 @@ #define HELLO_WORLD_TASK_ID 0 -volatile bool task_finished = false; - void task_start_callback(uint32_t tid, const void *param) { (void)param; printf("task '%u' starting...\n", tid); @@ -19,7 +17,6 @@ void task_start_callback(uint32_t tid, const void *param) { void task_end_callback(uint32_t tid, const void *param, void *ret_val) { (void)param; - task_finished = true; printf("task '%u' ended with return value: %s\n", tid, (char *)ret_val); free(ret_val); } @@ -59,7 +56,7 @@ int main(void) { assert(enqueue_task(pool, task) == POOL_DAY_SUCCESS); - while(!task_finished); + wait_task_finish(task); destroy_task(task); destroy_pool(&pool); diff --git a/samples/hello-world-sync/main.c b/samples/hello-world-sync/main.c index eb040fd..d69b479 100644 --- a/samples/hello-world-sync/main.c +++ b/samples/hello-world-sync/main.c @@ -33,8 +33,8 @@ int main(void) { } char str[] = "hello, world!"; - task_t task = create_task(HELLO_WORLD_TASK_ID, func, (void *)str, - sizeof(char) * strlen(str) + 1, NULL, NULL); + task_t task = create_sync_task(HELLO_WORLD_TASK_ID, func, (void *)str, + sizeof(char) * strlen(str) + 1); if (!task) { // handle error diff --git a/samples/webserver/main.c b/samples/webserver/main.c index 091ab60..3e24a63 100644 --- a/samples/webserver/main.c +++ b/samples/webserver/main.c @@ -236,9 +236,10 @@ static int run_server(const server_cfg_t *cfg) { cli_ptr->fd = (uint32_t)client_fd; memcpy(&cli_ptr->addr, &cli_addr.sin_addr, sizeof(struct in_addr)); - task_t task = create_task(client_fd, handle_client, (void *)cli_ptr, - sizeof(client_t), on_client_connected, - on_client_disconnected); + task_t task = create_async_task(client_fd, handle_client, (void *)cli_ptr, + sizeof(client_t), true, + on_client_connected, + on_client_disconnected); if (enqueue_task(pool, task) != POOL_DAY_SUCCESS) { printf("[-] failed to enqueue the request task\n"); From f1b83bc54b06e91e25287c17b80e7f16da30699f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Sat, 27 Dec 2025 23:48:56 -0300 Subject: [PATCH 53/63] lib: checking for the 'auto_release' option on get_task_result function --- src/pool_day.c | 6 ++++++ test/src/pool_day_test.cc | 15 +++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/pool_day.c b/src/pool_day.c index 760551f..ddb3028 100644 --- a/src/pool_day.c +++ b/src/pool_day.c @@ -196,6 +196,12 @@ void *get_task_result(task_t task) { return NULL; } + if (task->auto_release) { + POOL_DAY_ERROR( + "the result of a task with 'auto_release' option set can't be retrieved"); + return NULL; + } + POOL_DAY_DEBUG("waiting for the finish of the task"); sem_wait(&task->ready); POOL_DAY_DEBUG("task finished"); diff --git a/test/src/pool_day_test.cc b/test/src/pool_day_test.cc index 416df83..fcc1310 100644 --- a/test/src/pool_day_test.cc +++ b/test/src/pool_day_test.cc @@ -463,3 +463,18 @@ TEST_F(PoolDayTest, GetTaskResultWithWithUnboundTask) { // cleanup destroy_task(task); } + +/** + * @brief Given we have a task with the 'auto_release' option set, when we wait + * for the finish of the task, then null must be returned. + */ +TEST_F(PoolDayTest, GetTaskResultWithAutoReleaseSet) { + auto task = create_async_task(0, nullptr, nullptr, 0, true, + CbWrapper::OnTaskStartCb, + CbWrapper::OnTaskEndCb); + + EXPECT_EQ(get_task_result(task), nullptr); + + // cleanup + destroy_task(task); +} From c7aaa220e8516456e403a915833a5f7216672a6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Sun, 28 Dec 2025 00:28:02 -0300 Subject: [PATCH 54/63] lib: updating the documentation of get_task_result and wait_task_finish functions --- include/pool_day.h | 25 +++++++++++++++-- samples/cpp/main.cc | 68 --------------------------------------------- 2 files changed, 23 insertions(+), 70 deletions(-) delete mode 100644 samples/cpp/main.cc diff --git a/include/pool_day.h b/include/pool_day.h index f7f030d..82169e0 100644 --- a/include/pool_day.h +++ b/include/pool_day.h @@ -87,12 +87,15 @@ uint32_t queued_tasks(pool_day_t pool); * * void *ret = get_task_result(t); * + * handle_ret(ret); + * * destroy_task(t); * destroy_pool(&pool); * } * @endcode * - * @note This function blocks the current thread. + * @note This function blocks the current thread and its usage is not safe for + * tasks with 'auto_release' option set. * * @param[in] task Task handle. * @@ -103,7 +106,25 @@ void *get_task_result(task_t task); /** * @brief Wait for the finish of a given task without retrieving its result. * - * @note This function blocks the current thread. + * Sample: + * @code{.c} + * pool_day_t pool; + * + * pool = create_pool(size); + * if (pool) { + * task_t t = create_sync_task(id, callback, (void *)param, param_size); + * + * enqueue_task(pool, t); + * + * wait_task_finish(t); + * + * destroy_task(t); + * destroy_pool(&pool); + * } + * @endcode + * + * @note This function blocks the current thread and its usage is not safe for + * tasks with 'auto_release' option set. * * @param[in] task Task handle. */ diff --git a/samples/cpp/main.cc b/samples/cpp/main.cc deleted file mode 100644 index 1502250..0000000 --- a/samples/cpp/main.cc +++ /dev/null @@ -1,68 +0,0 @@ -#include -#include -#include - -#include "pool_day.h" -#include "task.h" - -class Foo { - public: - Foo() - : pool_{create_pool(2)}, - t1_{create_sync_task(0, Foo::Cb1, (void *)"hello", - sizeof(char) * std::strlen("hello") + 1)}, - t2_{create_sync_task(1, Foo::Cb2, (void *)"hi", - sizeof(char) * std::strlen("hi") + 1)} { - } - - void RunTasks(void) { - enqueue_task(pool_, t1_); - enqueue_task(pool_, t2_); - - get_task_result(t1_); - get_task_result(t2_); - - destroy_task(t1_); - destroy_task(t2_); - } - - ~Foo() { - destroy_pool(&pool_); - } - - private: - static void *Cb1(void *param) { - using namespace std::chrono_literals; - - char *msg = reinterpret_cast(param); - for (int i = 0; i < 10; i++) { - std::cout << msg << std::endl; - std::this_thread::sleep_for(2000ms); - } - - return nullptr; - } - - static void *Cb2(void *param) { - using namespace std::chrono_literals; - - char *msg = reinterpret_cast(param); - for (int i = 0; i < 10; i++) { - std::cout << msg << std::endl; - std::this_thread::sleep_for(1000ms); - } - - return nullptr; - } - - pool_day_t pool_; - task_t t1_; - task_t t2_; -}; - -int main(void) { - Foo d; - - d.RunTasks(); - return 0; -} From 0b3acb717cd388550612fe38ea7eed1542b92558 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Thu, 1 Jan 2026 15:44:45 -0300 Subject: [PATCH 55/63] samples: updating http-server sample's README file --- samples/CMakeLists.txt | 2 +- samples/{webserver => http-server}/CMakeLists.txt | 4 ++-- samples/http-server/README.md | 7 +++++++ samples/{webserver => http-server}/main.c | 8 +++----- samples/webserver/README.md | 7 ------- 5 files changed, 13 insertions(+), 15 deletions(-) rename samples/{webserver => http-server}/CMakeLists.txt (85%) create mode 100644 samples/http-server/README.md rename samples/{webserver => http-server}/main.c (96%) delete mode 100644 samples/webserver/README.md diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index 7a91fdb..7cb4e0b 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -2,4 +2,4 @@ cmake_minimum_required(VERSION 3.18) add_subdirectory(hello-world-async) add_subdirectory(hello-world-sync) -add_subdirectory(webserver) +add_subdirectory(http-server) diff --git a/samples/webserver/CMakeLists.txt b/samples/http-server/CMakeLists.txt similarity index 85% rename from samples/webserver/CMakeLists.txt rename to samples/http-server/CMakeLists.txt index 3203fff..128233b 100644 --- a/samples/webserver/CMakeLists.txt +++ b/samples/http-server/CMakeLists.txt @@ -1,8 +1,8 @@ cmake_minimum_required(VERSION 3.18) -project(webserver) +project(http-server) -set(PROJECT_NAME "webserver") +set(PROJECT_NAME "http-server") set(SOURCES main.c) set(CMAKE_C_FLAGS "-Wall -Werror -Wextra") diff --git a/samples/http-server/README.md b/samples/http-server/README.md new file mode 100644 index 0000000..ecee787 --- /dev/null +++ b/samples/http-server/README.md @@ -0,0 +1,7 @@ +## http-server + +This is an extremely simple static http server built with the library. For demonstration purposes, it only handles GET requests. Supported command line options: + +- `-m`: Maximum number of concurrent clients (default: 100) +- `-p`: Port number to listen on (default: 8080) +- `-r`: Root directory to serve files from (default: ./www) diff --git a/samples/webserver/main.c b/samples/http-server/main.c similarity index 96% rename from samples/webserver/main.c rename to samples/http-server/main.c index 3e24a63..2848b2a 100644 --- a/samples/webserver/main.c +++ b/samples/http-server/main.c @@ -41,23 +41,21 @@ typedef struct { // TODO: // - select +// - graceful server exit // - error handling // - const and restrict -// - better request parsing? -// - logging? -// - graceful server exit static void on_client_connected(uint32_t tid, const void *param) { const client_t *cli = (client_t *)param; - printf("[+] task[%u]: client connected from %s\n", tid, inet_ntoa(cli->addr)); + printf("[+] task[%u]: client connected, ip=%s\n", tid, inet_ntoa(cli->addr)); } static void on_client_disconnected(uint32_t tid, const void *param, void *ret_val) { const client_t *cli = (client_t *)param; - printf("[+] task[%u]: client disconnected from %s, result=%u\n", tid, + printf("[+] task[%u]: client disconnected ip=%s, result=%u\n", tid, inet_ntoa(cli->addr), *((uint16_t *)ret_val)); free(ret_val); } diff --git a/samples/webserver/README.md b/samples/webserver/README.md deleted file mode 100644 index 28ecf7e..0000000 --- a/samples/webserver/README.md +++ /dev/null @@ -1,7 +0,0 @@ -## webserver - -This is an extremely simple web server built with the library. For demonstration purposes, it only handles GET requests. Supported command line options: - -- `-m`: Maximum number of concurrent clients (default: 100) -- `-p`: Port number to listen on (default: 8080) -- `-r`: Root directory to serve files from (default: ./www) From d42598918318d6c3842de72f7e833b609b451087 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Thu, 1 Jan 2026 16:13:08 -0300 Subject: [PATCH 56/63] samples: improving the error handling on http-server sample --- samples/http-server/main.c | 62 +++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/samples/http-server/main.c b/samples/http-server/main.c index 2848b2a..257cd8a 100644 --- a/samples/http-server/main.c +++ b/samples/http-server/main.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -42,7 +43,6 @@ typedef struct { // TODO: // - select // - graceful server exit -// - error handling // - const and restrict static void on_client_connected(uint32_t tid, const void *param) { @@ -60,7 +60,8 @@ static void on_client_disconnected(uint32_t tid, const void *param, free(ret_val); } -static void parse_request(const char *buffer, request_t *req) { +static void parse_request(const char *restrict buffer, + request_t *restrict req) { // simple parsing logic for demonstration purposes sscanf(buffer, "%s %s", req->verb, req->resource); } @@ -80,6 +81,7 @@ static char *get_resource(const char *res_name) { FILE *file = fopen(res_name, "r"); if (!file) { + printf("[-] file to open the requested resource: %s\n", strerror(errno)); return NULL; } @@ -87,11 +89,14 @@ static char *get_resource(const char *res_name) { char *content = calloc(1, st.st_size + 1); if (!content) { + printf( + "[-] no memory available to put the requested resource's content on\n"); fclose(file); return NULL; } if (!fread(content, 1, st.st_size, file)) { + printf("[-] file to read the requested resource\n"); free(content); fclose(file); return NULL; @@ -178,25 +183,28 @@ static void parse_args(int argc, char **argv, server_cfg_t *restrict cfg) { } static int setup_socket(int *restrict sock_fd, const server_cfg_t *cfg) { - struct sockaddr_in server_addr; + struct sockaddr_in addr; *sock_fd = socket(AF_INET, SOCK_STREAM, 0); if (*sock_fd < 0) { + printf("[-] fail to create the server socket: %s\n", strerror(errno)); return 1; } - memset(&server_addr, 0, sizeof(server_addr)); + memset(&addr, 0, sizeof(addr)); - server_addr.sin_family = AF_INET; - server_addr.sin_addr.s_addr = INADDR_ANY; - server_addr.sin_port = htons(cfg->port); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_port = htons(cfg->port); - if (bind(*sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { + if (bind(*sock_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + printf("[-] fail to create the server socket: %s\n", strerror(errno)); close(*sock_fd); return 1; } if (listen(*sock_fd, cfg->max_clients) < 0) { + printf("[-] fail to create the server socket: %s\n", strerror(errno)); close(*sock_fd); return 1; } @@ -204,7 +212,7 @@ static int setup_socket(int *restrict sock_fd, const server_cfg_t *cfg) { return 0; } -static int run_server(const server_cfg_t *cfg) { +static int run_server(const server_cfg_t *restrict cfg) { int server_fd; pool_day_t pool; struct sockaddr_in cli_addr; @@ -216,6 +224,7 @@ static int run_server(const server_cfg_t *cfg) { } if (!(pool = create_pool(cfg->max_clients))) { + printf("[-] fail to setup the server pool\n"); close(server_fd); return 1; } @@ -223,28 +232,33 @@ static int run_server(const server_cfg_t *cfg) { printf("[+] starting server with max_clients=%u, port=%u, root_dir=%s\n", cfg->max_clients, cfg->port, cfg->root_dir); - chdir(cfg->root_dir); + if (chdir(cfg->root_dir)) { + printf("[-] fail to run the server: %s\n", strerror(errno)); + return 1; + } while (1) { int client_fd = accept(server_fd, (struct sockaddr *)&cli_addr, &cli_len); - if (client_fd > 0) { - client_t *cli_ptr = calloc(1, sizeof(client_t)); + if (client_fd == -1) { + printf("[-] failed to accept the incoming client: %s\n", strerror(errno)); + continue; + } + + client_t *cli_ptr = calloc(1, sizeof(client_t)); - cli_ptr->fd = (uint32_t)client_fd; - memcpy(&cli_ptr->addr, &cli_addr.sin_addr, sizeof(struct in_addr)); + cli_ptr->fd = (uint32_t)client_fd; + memcpy(&cli_ptr->addr, &cli_addr.sin_addr, sizeof(struct in_addr)); - task_t task = create_async_task(client_fd, handle_client, (void *)cli_ptr, - sizeof(client_t), true, - on_client_connected, - on_client_disconnected); + task_t task = create_async_task(client_fd, handle_client, (void *)cli_ptr, + sizeof(client_t), true, on_client_connected, + on_client_disconnected); - if (enqueue_task(pool, task) != POOL_DAY_SUCCESS) { - printf("[-] failed to enqueue the request task\n"); - destroy_task(task); - close(client_fd); - free(cli_ptr); - } + if (enqueue_task(pool, task) != POOL_DAY_SUCCESS) { + printf("[-] failed to enqueue the request task\n"); + destroy_task(task); + close(client_fd); + free(cli_ptr); } } From c40b28c0e269f7e5c66a044a856b47a1ca08c8f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Fri, 2 Jan 2026 01:17:43 -0300 Subject: [PATCH 57/63] samples: improving http-server mainloop --- samples/http-server/main.c | 58 +++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/samples/http-server/main.c b/samples/http-server/main.c index 257cd8a..f15ed13 100644 --- a/samples/http-server/main.c +++ b/samples/http-server/main.c @@ -6,9 +6,11 @@ #include #include #include +#include #include #include #include +#include #include "pool_day.h" #include "task.h" @@ -40,10 +42,9 @@ typedef struct { struct in_addr addr; } client_t; -// TODO: -// - select -// - graceful server exit -// - const and restrict +void sig_handler(int signum) { + (void)signum; +} static void on_client_connected(uint32_t tid, const void *param) { const client_t *cli = (client_t *)param; @@ -213,10 +214,11 @@ static int setup_socket(int *restrict sock_fd, const server_cfg_t *cfg) { } static int run_server(const server_cfg_t *restrict cfg) { - int server_fd; + int ret, server_fd; pool_day_t pool; struct sockaddr_in cli_addr; socklen_t cli_len = sizeof(cli_addr); + fd_set set; if (setup_socket(&server_fd, cfg) != 0) { printf("[-] failed to setup server socket\n"); @@ -238,27 +240,40 @@ static int run_server(const server_cfg_t *restrict cfg) { } while (1) { - int client_fd = accept(server_fd, (struct sockaddr *)&cli_addr, &cli_len); + FD_ZERO(&set); + FD_SET(server_fd, &set); - if (client_fd == -1) { - printf("[-] failed to accept the incoming client: %s\n", strerror(errno)); - continue; + ret = select(server_fd + 1, &set, NULL, NULL, NULL); + if ((ret == -1) && (errno == EINTR)) { + printf("[-] exiting server...\n"); + break; } - client_t *cli_ptr = calloc(1, sizeof(client_t)); + if (FD_ISSET(server_fd, &set)) { + int client_fd = accept(server_fd, (struct sockaddr *)&cli_addr, &cli_len); + + if (client_fd == -1) { + printf("[-] failed to accept the incoming client: %s\n", + strerror(errno)); + continue; + } - cli_ptr->fd = (uint32_t)client_fd; - memcpy(&cli_ptr->addr, &cli_addr.sin_addr, sizeof(struct in_addr)); + client_t *cli_ptr = calloc(1, sizeof(client_t)); - task_t task = create_async_task(client_fd, handle_client, (void *)cli_ptr, - sizeof(client_t), true, on_client_connected, - on_client_disconnected); + cli_ptr->fd = (uint32_t)client_fd; + memcpy(&cli_ptr->addr, &cli_addr.sin_addr, sizeof(struct in_addr)); - if (enqueue_task(pool, task) != POOL_DAY_SUCCESS) { - printf("[-] failed to enqueue the request task\n"); - destroy_task(task); - close(client_fd); - free(cli_ptr); + task_t task = create_async_task(client_fd, handle_client, (void *)cli_ptr, + sizeof(client_t), true, + on_client_connected, + on_client_disconnected); + + if (enqueue_task(pool, task) != POOL_DAY_SUCCESS) { + printf("[-] failed to enqueue the request task\n"); + destroy_task(task); + close(client_fd); + free(cli_ptr); + } } } @@ -272,6 +287,9 @@ int main(int argc, char **argv) { server_cfg_t cfg; memset(&cfg, 0, sizeof(server_cfg_t)); + + signal(SIGINT, sig_handler); + parse_args(argc, argv, &cfg); return run_server(&cfg); } From 5c6ec50d8e1dceeb394a61321913c1db66180594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Fri, 2 Jan 2026 01:24:37 -0300 Subject: [PATCH 58/63] samples: removing memory leak on http-server sample --- samples/http-server/main.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/samples/http-server/main.c b/samples/http-server/main.c index f15ed13..644c8e3 100644 --- a/samples/http-server/main.c +++ b/samples/http-server/main.c @@ -42,7 +42,7 @@ typedef struct { struct in_addr addr; } client_t; -void sig_handler(int signum) { +static void sig_handler(int signum) { (void)signum; } @@ -61,8 +61,8 @@ static void on_client_disconnected(uint32_t tid, const void *param, free(ret_val); } -static void parse_request(const char *restrict buffer, - request_t *restrict req) { +static void parse_request(const char *buffer, + request_t *req) { // simple parsing logic for demonstration purposes sscanf(buffer, "%s %s", req->verb, req->resource); } @@ -153,7 +153,7 @@ static void *handle_client(void *param) { return (void *)ret; } -static void parse_args(int argc, char **argv, server_cfg_t *restrict cfg) { +static void parse_args(int argc, char **argv, server_cfg_t *cfg) { int opt; while ((opt = getopt(argc, argv, "m:p:r:")) != -1) { @@ -183,7 +183,7 @@ static void parse_args(int argc, char **argv, server_cfg_t *restrict cfg) { } } -static int setup_socket(int *restrict sock_fd, const server_cfg_t *cfg) { +static int setup_socket(int *sock_fd, const server_cfg_t *cfg) { struct sockaddr_in addr; *sock_fd = socket(AF_INET, SOCK_STREAM, 0); @@ -213,7 +213,7 @@ static int setup_socket(int *restrict sock_fd, const server_cfg_t *cfg) { return 0; } -static int run_server(const server_cfg_t *restrict cfg) { +static int run_server(const server_cfg_t *cfg) { int ret, server_fd; pool_day_t pool; struct sockaddr_in cli_addr; @@ -258,12 +258,10 @@ static int run_server(const server_cfg_t *restrict cfg) { continue; } - client_t *cli_ptr = calloc(1, sizeof(client_t)); - - cli_ptr->fd = (uint32_t)client_fd; - memcpy(&cli_ptr->addr, &cli_addr.sin_addr, sizeof(struct in_addr)); - - task_t task = create_async_task(client_fd, handle_client, (void *)cli_ptr, + task_t task = create_async_task(client_fd, handle_client, + (void *)&((client_t) { + .fd = (uint32_t)client_fd, + .addr = cli_addr.sin_addr }), sizeof(client_t), true, on_client_connected, on_client_disconnected); @@ -272,7 +270,6 @@ static int run_server(const server_cfg_t *restrict cfg) { printf("[-] failed to enqueue the request task\n"); destroy_task(task); close(client_fd); - free(cli_ptr); } } } @@ -289,6 +286,7 @@ int main(int argc, char **argv) { memset(&cfg, 0, sizeof(server_cfg_t)); signal(SIGINT, sig_handler); + signal(SIGTERM, sig_handler); parse_args(argc, argv, &cfg); return run_server(&cfg); From 8ecac26f6f22a68852a4a463470fd5de98e43052 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Sat, 21 Mar 2026 14:54:13 -0300 Subject: [PATCH 59/63] doc: updating e-mail --- include/internal/errors.h | 2 +- include/internal/logger.h | 2 +- include/internal/queue.h | 2 +- include/internal/task_def.h | 2 +- include/internal/utils.h | 2 +- include/pool_day.h | 2 +- include/task.h | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/internal/errors.h b/include/internal/errors.h index 1f48c5c..c482919 100644 --- a/include/internal/errors.h +++ b/include/internal/errors.h @@ -4,7 +4,7 @@ * @brief Contains the definition of the error codes of the library. * * @authors - * Copyright (C) 2023 André L. C. Moreira + * Copyright (C) 2023 André L. C. Moreira * * @copyright * This program is free software: you can redistribute it and/or modify diff --git a/include/internal/logger.h b/include/internal/logger.h index affd511..975e8c5 100644 --- a/include/internal/logger.h +++ b/include/internal/logger.h @@ -4,7 +4,7 @@ * @brief Contains the logging functions of the library. * * @authors - * Copyright (C) 2023 André L. C. Moreira + * Copyright (C) 2023 André L. C. Moreira * * @copyright * This program is free software: you can redistribute it and/or modify diff --git a/include/internal/queue.h b/include/internal/queue.h index 4ff33c9..94ed8ef 100644 --- a/include/internal/queue.h +++ b/include/internal/queue.h @@ -4,7 +4,7 @@ * @brief Contains the basic definitions and data structures of the task queue. * * @authors - * Copyright (C) 2023 André L. C. Moreira + * Copyright (C) 2023 André L. C. Moreira * * @copyright * This program is free software: you can redistribute it and/or modify diff --git a/include/internal/task_def.h b/include/internal/task_def.h index ddfdb97..8cb50be 100644 --- a/include/internal/task_def.h +++ b/include/internal/task_def.h @@ -5,7 +5,7 @@ * macros to handle it. * * @authors - * Copyright (C) 2023 André L. C. Moreira + * Copyright (C) 2023 André L. C. Moreira * * @copyright * This program is free software: you can redistribute it and/or modify diff --git a/include/internal/utils.h b/include/internal/utils.h index 146eb1d..ed3e432 100644 --- a/include/internal/utils.h +++ b/include/internal/utils.h @@ -4,7 +4,7 @@ * @brief Contains a set of utilities available for use by the library. * * @authors - * Copyright (C) 2023 André L. C. Moreira + * Copyright (C) 2023 André L. C. Moreira * * @copyright * This program is free software: you can redistribute it and/or modify diff --git a/include/pool_day.h b/include/pool_day.h index 82169e0..89f2932 100644 --- a/include/pool_day.h +++ b/include/pool_day.h @@ -4,7 +4,7 @@ * @brief Contains the basic definitions and data structures of the library. * * @authors - * Copyright (C) 2023 André L. C. Moreira + * Copyright (C) 2023 André L. C. Moreira * * @copyright * This program is free software: you can redistribute it and/or modify diff --git a/include/task.h b/include/task.h index 69e94bc..7d56ffa 100644 --- a/include/task.h +++ b/include/task.h @@ -5,7 +5,7 @@ * macros to handle it. * * @authors - * Copyright (C) 2023 André L. C. Moreira + * Copyright (C) 2023 André L. C. Moreira * * @copyright * This program is free software: you can redistribute it and/or modify From 294ba348ebf3c850c7c3056b4a25632426ecc351 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Mon, 30 Mar 2026 22:27:20 -0300 Subject: [PATCH 60/63] samples: fixing typo on http-server sample --- samples/http-server/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/http-server/main.c b/samples/http-server/main.c index 644c8e3..e80c4c6 100644 --- a/samples/http-server/main.c +++ b/samples/http-server/main.c @@ -82,7 +82,7 @@ static char *get_resource(const char *res_name) { FILE *file = fopen(res_name, "r"); if (!file) { - printf("[-] file to open the requested resource: %s\n", strerror(errno)); + printf("[-] fail to open the requested resource: %s\n", strerror(errno)); return NULL; } @@ -97,7 +97,7 @@ static char *get_resource(const char *res_name) { } if (!fread(content, 1, st.st_size, file)) { - printf("[-] file to read the requested resource\n"); + printf("[-] fail to read the requested resource\n"); free(content); fclose(file); return NULL; From 74fc6d8366025210cac4401d302697b041d2a117 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Sat, 3 Jan 2026 01:16:12 -0300 Subject: [PATCH 61/63] samples: replacing the usage of printf by fprintf --- samples/http-server/main.c | 46 +++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/samples/http-server/main.c b/samples/http-server/main.c index e80c4c6..fa5370c 100644 --- a/samples/http-server/main.c +++ b/samples/http-server/main.c @@ -16,7 +16,7 @@ #include "task.h" #define MAX_VERB_SIZE 7 -#define MAX_BUFFER_SIZE 4096 +#define MAX_BUFFER_SIZE 4096 * 50 #define DEFAULT_ROOT_DIR "./www" #define DEFAULT_PORT 8080 @@ -49,15 +49,16 @@ static void sig_handler(int signum) { static void on_client_connected(uint32_t tid, const void *param) { const client_t *cli = (client_t *)param; - printf("[+] task[%u]: client connected, ip=%s\n", tid, inet_ntoa(cli->addr)); + fprintf(stdout, "[+] task[%u]: client connected, ip=%s\n", tid, + inet_ntoa(cli->addr)); } static void on_client_disconnected(uint32_t tid, const void *param, void *ret_val) { const client_t *cli = (client_t *)param; - printf("[+] task[%u]: client disconnected ip=%s, result=%u\n", tid, - inet_ntoa(cli->addr), *((uint16_t *)ret_val)); + fprintf(stdout, "[+] task[%u]: client disconnected ip=%s, result=%u\n", tid, + inet_ntoa(cli->addr), *((uint16_t *)ret_val)); free(ret_val); } @@ -82,7 +83,8 @@ static char *get_resource(const char *res_name) { FILE *file = fopen(res_name, "r"); if (!file) { - printf("[-] fail to open the requested resource: %s\n", strerror(errno)); + fprintf(stderr, "[-] fail to open the requested resource: %s\n", + strerror(errno)); return NULL; } @@ -90,14 +92,14 @@ static char *get_resource(const char *res_name) { char *content = calloc(1, st.st_size + 1); if (!content) { - printf( + fprintf(stderr, "[-] no memory available to put the requested resource's content on\n"); fclose(file); return NULL; } if (!fread(content, 1, st.st_size, file)) { - printf("[-] fail to read the requested resource\n"); + fprintf(stderr, "[-] fail to read the requested resource\n"); free(content); fclose(file); return NULL; @@ -138,7 +140,7 @@ static void *handle_client(void *param) { if (received > 0) { parse_request(buffer, &req); - printf("[+] received request: %s %s\n", req.verb, req.resource); + fprintf(stdout, "[+] received request: %s %s\n", req.verb, req.resource); memset(buffer, 0, sizeof(buffer)); if (!strcmp(req.verb, "GET")) { @@ -188,7 +190,8 @@ static int setup_socket(int *sock_fd, const server_cfg_t *cfg) { *sock_fd = socket(AF_INET, SOCK_STREAM, 0); if (*sock_fd < 0) { - printf("[-] fail to create the server socket: %s\n", strerror(errno)); + fprintf(stderr, "[-] fail to create the server socket: %s\n", + strerror(errno)); return 1; } @@ -199,13 +202,15 @@ static int setup_socket(int *sock_fd, const server_cfg_t *cfg) { addr.sin_port = htons(cfg->port); if (bind(*sock_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - printf("[-] fail to create the server socket: %s\n", strerror(errno)); + fprintf(stderr, "[-] fail to create the server socket: %s\n", + strerror(errno)); close(*sock_fd); return 1; } if (listen(*sock_fd, cfg->max_clients) < 0) { - printf("[-] fail to create the server socket: %s\n", strerror(errno)); + fprintf(stderr, "[-] fail to create the server socket: %s\n", + strerror(errno)); close(*sock_fd); return 1; } @@ -221,21 +226,22 @@ static int run_server(const server_cfg_t *cfg) { fd_set set; if (setup_socket(&server_fd, cfg) != 0) { - printf("[-] failed to setup server socket\n"); + fprintf(stderr, "[-] failed to setup server socket\n"); return 1; } if (!(pool = create_pool(cfg->max_clients))) { - printf("[-] fail to setup the server pool\n"); + fprintf(stderr, "[-] fail to setup the server pool\n"); close(server_fd); return 1; } - printf("[+] starting server with max_clients=%u, port=%u, root_dir=%s\n", - cfg->max_clients, cfg->port, cfg->root_dir); + fprintf(stdout, + "[+] starting server with max_clients=%u, port=%u, root_dir=%s\n", + cfg->max_clients, cfg->port, cfg->root_dir); if (chdir(cfg->root_dir)) { - printf("[-] fail to run the server: %s\n", strerror(errno)); + fprintf(stderr, "[-] fail to run the server: %s\n", strerror(errno)); return 1; } @@ -245,7 +251,7 @@ static int run_server(const server_cfg_t *cfg) { ret = select(server_fd + 1, &set, NULL, NULL, NULL); if ((ret == -1) && (errno == EINTR)) { - printf("[-] exiting server...\n"); + fprintf(stdout, "[+] exiting server...\n"); break; } @@ -253,8 +259,8 @@ static int run_server(const server_cfg_t *cfg) { int client_fd = accept(server_fd, (struct sockaddr *)&cli_addr, &cli_len); if (client_fd == -1) { - printf("[-] failed to accept the incoming client: %s\n", - strerror(errno)); + fprintf(stderr, "[-] failed to accept the incoming client: %s\n", + strerror(errno)); continue; } @@ -267,7 +273,7 @@ static int run_server(const server_cfg_t *cfg) { on_client_disconnected); if (enqueue_task(pool, task) != POOL_DAY_SUCCESS) { - printf("[-] failed to enqueue the request task\n"); + fprintf(stderr, "[-] failed to enqueue the request task\n"); destroy_task(task); close(client_fd); } From 0bf4fa84c1ee5a97ef2895dd6c1ec03bf95c099a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Sat, 3 Jan 2026 15:45:15 -0300 Subject: [PATCH 62/63] samples: funcionando essa porra --- samples/http-server/README.md | 2 +- samples/http-server/main.c | 245 ++++++++++++++++++++++++---------- src/pool_day.c | 7 +- 3 files changed, 182 insertions(+), 72 deletions(-) diff --git a/samples/http-server/README.md b/samples/http-server/README.md index ecee787..52fd953 100644 --- a/samples/http-server/README.md +++ b/samples/http-server/README.md @@ -1,6 +1,6 @@ ## http-server -This is an extremely simple static http server built with the library. For demonstration purposes, it only handles GET requests. Supported command line options: +This is an extremely simple static http server built with the library. Supported command line options: - `-m`: Maximum number of concurrent clients (default: 100) - `-p`: Port number to listen on (default: 8080) diff --git a/samples/http-server/main.c b/samples/http-server/main.c index fa5370c..da9cb3d 100644 --- a/samples/http-server/main.c +++ b/samples/http-server/main.c @@ -15,8 +15,8 @@ #include "pool_day.h" #include "task.h" -#define MAX_VERB_SIZE 7 -#define MAX_BUFFER_SIZE 4096 * 50 +#define MAX_METHOD_SIZE 7 +#define MAX_BUFFER_SIZE 10 * 1024 #define DEFAULT_ROOT_DIR "./www" #define DEFAULT_PORT 8080 @@ -33,7 +33,7 @@ typedef struct { } server_cfg_t; typedef struct { - char verb[MAX_VERB_SIZE]; + char method[MAX_METHOD_SIZE]; char resource[PATH_MAX]; } request_t; @@ -42,6 +42,27 @@ typedef struct { struct in_addr addr; } client_t; +typedef enum { + SUCCESS, + NOT_FOUND, + INTERNAL_ERROR +} http_status_id_t; + +typedef struct { + http_status_id_t id; + int code; + char *msg; +} http_status_t; + +//static http_status_t status_table[] = { +// { SUCCESS, 200, "OK" }, +// { NOT_FOUND, 404, "Not Found" }, +// { INTERNAL_ERROR, 505, "Internal Error" } +//}; + +// TODO: lookup table for http errors +// TODO: 501 error? + static void sig_handler(int signum) { (void)signum; } @@ -56,32 +77,69 @@ static void on_client_connected(uint32_t tid, const void *param) { static void on_client_disconnected(uint32_t tid, const void *param, void *ret_val) { const client_t *cli = (client_t *)param; + int ret = ret_val ? *((uint16_t *)ret_val) : -1; fprintf(stdout, "[+] task[%u]: client disconnected ip=%s, result=%u\n", tid, - inet_ntoa(cli->addr), *((uint16_t *)ret_val)); - free(ret_val); + inet_ntoa(cli->addr), ret); + + if (ret_val) { + free(ret_val); + } } -static void parse_request(const char *buffer, - request_t *req) { +static void parse_request(const char *buffer, request_t *req) { // simple parsing logic for demonstration purposes - sscanf(buffer, "%s %s", req->verb, req->resource); + sscanf(buffer, "%s %s", req->method, req->resource); } -static void assemble_reply(char *buffer, size_t buffer_size, int status_code, - const char *status_str, const char *body) { - const char *reply_fmt = - "HTTP/1.1 %d %s\r\n" - "Content-Type: text/html\r\n" - "\r\n%s"; +static char *build_http_header(int status_code, const char *status_str, + size_t *header_len) { + const char *crlf = "\r\n"; + const char *version = "HTTP/1.1"; + const char *content_type = "Content-Type: text/html"; // TODO: and how about images? + const char *header_fmt = + "%s %d %s\r\n" + "%s\r\n" + "\r\n"; + + //const char *reply_fmt = + // "HTTP/1.1 %d %s\r\n" + // "Content-Type: text/html\r\n" + // "\r\n%s"; + + *header_len = strlen(version) + sizeof(int) + strlen(status_str) + 2 + + 3 * strlen(crlf) + strlen(content_type); + + char *header = calloc(1, *header_len); + if (!header) { + return NULL; + } - snprintf(buffer, buffer_size, reply_fmt, status_code, status_str, body); + snprintf(header, *header_len, header_fmt, version, status_code, status_str, + content_type); + + return header; +} + +static int assemble_reply(char **buffer, char *header, size_t header_len, + const char *body, size_t body_len) { + size_t buffer_len = header_len + body_len + 1; + + *buffer = calloc(1, buffer_len); + if (!(*buffer)) { + return 0; + } + + memcpy(*buffer, header, header_len); + memcpy(*buffer + header_len - 1, body, body_len); + + return buffer_len; } -static char *get_resource(const char *res_name) { +static char *get_resource(const char *res_name, size_t *resource_len) { struct stat st; - FILE *file = fopen(res_name, "r"); + FILE *file = fopen(res_name, "r"); // TODO: and how about the image files? if (!file) { fprintf(stderr, "[-] fail to open the requested resource: %s\n", strerror(errno)); @@ -92,13 +150,13 @@ static char *get_resource(const char *res_name) { char *content = calloc(1, st.st_size + 1); if (!content) { - fprintf(stderr, - "[-] no memory available to put the requested resource's content on\n"); + fprintf(stderr, "[-] fail to allocate memory for the requested resource\n"); fclose(file); return NULL; } - if (!fread(content, 1, st.st_size, file)) { + *resource_len = fread(content, 1, st.st_size, file); + if (!(*resource_len)) { fprintf(stderr, "[-] fail to read the requested resource\n"); free(content); fclose(file); @@ -110,52 +168,92 @@ static char *get_resource(const char *res_name) { return content; } -static int handle_get_request(char *reply_buffer, size_t buffer_size, +static int handle_get_request(char **reply_buffer, size_t *reply_buffer_size, const char *resource) { - char *res = get_resource(resource); int status_code; + size_t res_len; + char *header; + size_t header_len; + char *res = get_resource(resource, &res_len); if (res) { status_code = 200; - assemble_reply(reply_buffer, buffer_size, status_code, "OK", res); + header = build_http_header(status_code, "OK", &header_len); + *reply_buffer_size = assemble_reply(reply_buffer, header, header_len, res, + res_len); free(res); } else { status_code = 404; - assemble_reply(reply_buffer, buffer_size, status_code, "Not Found", - MAKE_ERROR_BODY(404, Not Found)); + header = build_http_header(status_code, "Not Found", &header_len); + char *body = MAKE_ERROR_BODY(404, Not Found); + *reply_buffer_size = assemble_reply(reply_buffer, header, header_len, + body, strlen(body)); } + free(header); + return status_code; } -static void *handle_client(void *param) { - char buffer[MAX_BUFFER_SIZE] = {0}; - client_t *cli = (client_t *)param; - int *ret = calloc(1, sizeof(int)); +static int handle_request(const char *req_buffer, char **reply_buffer, + size_t *reply_buffer_len) { request_t req; memset(&req, 0, sizeof(request_t)); - ssize_t received = recv(cli->fd, buffer, sizeof(buffer) - 1, 0); + parse_request(req_buffer, &req); + + fprintf(stdout, "[+] received request: %s %s\n", req.method, req.resource); + if (strcmp(req.method, "GET")) { + fprintf(stderr, "[-] unsupported request\n"); + // TODO: assemble 501 reply + return 501; + } + + return handle_get_request(reply_buffer, reply_buffer_len, &req.resource[1]); +} + +static void *handle_new_connection(void *param) { + client_t *cli = (client_t *)param; + int *ret = NULL; + char *req_buffer; + char *reply_buffer; + size_t reply_buffer_len; + + req_buffer = calloc(1, MAX_BUFFER_SIZE); + if (!req_buffer) { + fprintf(stderr, "[-] fail to allocate memory for request buffer\n"); + close(cli->fd); + return NULL; + } + + ssize_t received = recv(cli->fd, req_buffer, MAX_BUFFER_SIZE - 1, 0); if (received > 0) { - parse_request(buffer, &req); + ret = calloc(1, sizeof(int)); + if (ret) { + *ret = handle_request(req_buffer, &reply_buffer, &reply_buffer_len); - fprintf(stdout, "[+] received request: %s %s\n", req.verb, req.resource); + printf("reply_len = %lu\n", reply_buffer_len); + printf("reply = %s\n", reply_buffer); - memset(buffer, 0, sizeof(buffer)); - if (!strcmp(req.verb, "GET")) { - *ret = handle_get_request(buffer, MAX_BUFFER_SIZE, &req.resource[1]); + //for (size_t i = 0; i < reply_buffer_len; i++) { + // printf("%c\n", reply_buffer[i]); + //} - send(cli->fd, buffer, strlen(buffer), 0); + if (reply_buffer_len) { + send(cli->fd, reply_buffer, reply_buffer_len - 2, 0); // FIXME: tem coisa amais sendo copiado: GDB!!!!! + free(reply_buffer); + } } } close(cli->fd); + free(req_buffer); return (void *)ret; } -static void parse_args(int argc, char **argv, server_cfg_t *cfg) { +static void fill_cfg(int argc, char **argv, server_cfg_t *cfg) { int opt; while ((opt = getopt(argc, argv, "m:p:r:")) != -1) { @@ -190,8 +288,6 @@ static int setup_socket(int *sock_fd, const server_cfg_t *cfg) { *sock_fd = socket(AF_INET, SOCK_STREAM, 0); if (*sock_fd < 0) { - fprintf(stderr, "[-] fail to create the server socket: %s\n", - strerror(errno)); return 1; } @@ -202,15 +298,11 @@ static int setup_socket(int *sock_fd, const server_cfg_t *cfg) { addr.sin_port = htons(cfg->port); if (bind(*sock_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - fprintf(stderr, "[-] fail to create the server socket: %s\n", - strerror(errno)); close(*sock_fd); return 1; } if (listen(*sock_fd, cfg->max_clients) < 0) { - fprintf(stderr, "[-] fail to create the server socket: %s\n", - strerror(errno)); close(*sock_fd); return 1; } @@ -218,32 +310,28 @@ static int setup_socket(int *sock_fd, const server_cfg_t *cfg) { return 0; } -static int run_server(const server_cfg_t *cfg) { - int ret, server_fd; - pool_day_t pool; - struct sockaddr_in cli_addr; - socklen_t cli_len = sizeof(cli_addr); - fd_set set; - - if (setup_socket(&server_fd, cfg) != 0) { - fprintf(stderr, "[-] failed to setup server socket\n"); +static int setup_server(const server_cfg_t *cfg, int *server_fd, + pool_day_t *pool) { + if (setup_socket(server_fd, cfg) != 0) { + fprintf(stderr, "[-] fail to setup the server socket: %s\n", + strerror(errno)); return 1; } - if (!(pool = create_pool(cfg->max_clients))) { + if (!(*pool = create_pool(cfg->max_clients))) { fprintf(stderr, "[-] fail to setup the server pool\n"); - close(server_fd); + close(*server_fd); return 1; } - fprintf(stdout, - "[+] starting server with max_clients=%u, port=%u, root_dir=%s\n", - cfg->max_clients, cfg->port, cfg->root_dir); + return 0; +} - if (chdir(cfg->root_dir)) { - fprintf(stderr, "[-] fail to run the server: %s\n", strerror(errno)); - return 1; - } +static int server_mainloop(int server_fd, pool_day_t pool) { + int ret; + fd_set set; + struct sockaddr_in cli_addr; + socklen_t cli_len = sizeof(cli_addr); while (1) { FD_ZERO(&set); @@ -259,12 +347,12 @@ static int run_server(const server_cfg_t *cfg) { int client_fd = accept(server_fd, (struct sockaddr *)&cli_addr, &cli_len); if (client_fd == -1) { - fprintf(stderr, "[-] failed to accept the incoming client: %s\n", + fprintf(stderr, "[-] fail to accept the incoming client: %s\n", strerror(errno)); continue; } - task_t task = create_async_task(client_fd, handle_client, + task_t task = create_async_task(client_fd, handle_new_connection, (void *)&((client_t) { .fd = (uint32_t)client_fd, .addr = cli_addr.sin_addr }), @@ -273,7 +361,7 @@ static int run_server(const server_cfg_t *cfg) { on_client_disconnected); if (enqueue_task(pool, task) != POOL_DAY_SUCCESS) { - fprintf(stderr, "[-] failed to enqueue the request task\n"); + fprintf(stderr, "[-] fail to enqueue the request task\n"); destroy_task(task); close(client_fd); } @@ -283,7 +371,28 @@ static int run_server(const server_cfg_t *cfg) { close(server_fd); destroy_pool(&pool); - return 0; + return EXIT_SUCCESS; +} + +static int run_server(const server_cfg_t *cfg) { + int server_fd; + pool_day_t pool; + + if (setup_server(cfg, &server_fd, &pool)) { + fprintf(stderr, "[-] fail to setup the server\n"); + return EXIT_FAILURE; + } + + if (chdir(cfg->root_dir)) { + fprintf(stderr, "[-] fail to run the server: %s\n", strerror(errno)); + return EXIT_FAILURE; + } + + fprintf(stdout, + "[+] starting server with max_clients=%u, port=%u, root_dir=%s\n", + cfg->max_clients, cfg->port, cfg->root_dir); + + return server_mainloop(server_fd, pool); } int main(int argc, char **argv) { @@ -294,6 +403,8 @@ int main(int argc, char **argv) { signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); - parse_args(argc, argv, &cfg); - return run_server(&cfg); + fill_cfg(argc, argv, &cfg); + int ret = run_server(&cfg); + + exit(ret); } diff --git a/src/pool_day.c b/src/pool_day.c index ddb3028..b547246 100644 --- a/src/pool_day.c +++ b/src/pool_day.c @@ -81,9 +81,8 @@ pool_day_retcode_t enqueue_task(pool_day_t pool, task_t task) { return POOL_DAY_ERROR_NULL_PARAM; } - POOL_DAY_DEBUG("task ID: %u", task->id); - POOL_DAY_DEBUG("task callback: %p", task->task); - POOL_DAY_DEBUG("task parameter: %p", task->param); + POOL_DAY_DEBUG("task id=%u, callback=%p, parameter=%p", task->id, task->task, + task->param); task->ret_val = NULL; task->is_orphan = false; @@ -100,7 +99,7 @@ pool_day_retcode_t enqueue_task(pool_day_t pool, task_t task) { pool_day_t create_pool(uint32_t pool_size) { pool_day_t pool; - POOL_DAY_DEBUG("pool size: %u", pool_size); + POOL_DAY_DEBUG("pool_size=%u", pool_size); if (!pool_size) { POOL_DAY_ERROR("bad pool size"); From 04965bd4133ea02a3cf5bb8fde481479269226a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Fri, 3 Apr 2026 19:31:44 -0300 Subject: [PATCH 63/63] samples: adding the content-type verification to the http-server sample --- samples/http-server/main.c | 288 ++++++++++++++++++++----------------- 1 file changed, 154 insertions(+), 134 deletions(-) diff --git a/samples/http-server/main.c b/samples/http-server/main.c index da9cb3d..1b29d0f 100644 --- a/samples/http-server/main.c +++ b/samples/http-server/main.c @@ -22,9 +22,16 @@ #define DEFAULT_PORT 8080 #define DEFAULT_MAX_CLIENTS 100 -#define STR(x) #x -#define MAKE_ERROR_BODY(status_code, msg) \ - "

" STR(status_code) " " #msg "

" +#define CONTENT_TYPE_TEXT_HTML "text/html" +#define CONTENT_TYPE_IMAGE_PNG "image/png" +#define CONTENT_TYPE_IMAGE_JPEG "image/jpeg" +#define CONTENT_TYPE_IMAGE_GIF "image/gif" +#define CONTENT_TYPE_TEXT_CSS "text/css" +#define CONTENT_TYPE_APP_JS "application/javascript" +#define CONTENT_TYPE_APP_OCTET "application/octet-stream" + +#define MAKE_ERROR_BODY(code, msg) \ + "

" #code " " #msg "

" typedef struct { uint32_t max_clients; @@ -42,27 +49,6 @@ typedef struct { struct in_addr addr; } client_t; -typedef enum { - SUCCESS, - NOT_FOUND, - INTERNAL_ERROR -} http_status_id_t; - -typedef struct { - http_status_id_t id; - int code; - char *msg; -} http_status_t; - -//static http_status_t status_table[] = { -// { SUCCESS, 200, "OK" }, -// { NOT_FOUND, 404, "Not Found" }, -// { INTERNAL_ERROR, 505, "Internal Error" } -//}; - -// TODO: lookup table for http errors -// TODO: 501 error? - static void sig_handler(int signum) { (void)signum; } @@ -87,168 +73,216 @@ static void on_client_disconnected(uint32_t tid, const void *param, } } +static const char *get_content_type(const char *path) { + const char *ext = strrchr(path, '.'); + + if (!ext) { + return CONTENT_TYPE_APP_OCTET; + } + + if (!strcmp(ext, ".html") || !strcmp(ext, ".htm")) { + return CONTENT_TYPE_TEXT_HTML; + } + + if (!strcmp(ext, ".png")) { + return CONTENT_TYPE_IMAGE_PNG; + } + + if (!strcmp(ext, ".jpg") || !strcmp(ext, ".jpeg")) { + return CONTENT_TYPE_IMAGE_JPEG; + } + + if (!strcmp(ext, ".gif")) { + return CONTENT_TYPE_IMAGE_GIF; + } + + if (!strcmp(ext, ".css")) { + return CONTENT_TYPE_TEXT_CSS; + } + + if (!strcmp(ext, ".js")) { + return CONTENT_TYPE_APP_JS; + } + + return CONTENT_TYPE_APP_OCTET; +} + static void parse_request(const char *buffer, request_t *req) { - // simple parsing logic for demonstration purposes sscanf(buffer, "%s %s", req->method, req->resource); } static char *build_http_header(int status_code, const char *status_str, + const char *content_type, size_t content_length, size_t *header_len) { - const char *crlf = "\r\n"; - const char *version = "HTTP/1.1"; - const char *content_type = "Content-Type: text/html"; // TODO: and how about images? - const char *header_fmt = - "%s %d %s\r\n" - "%s\r\n" - "\r\n"; - //const char *reply_fmt = - // "HTTP/1.1 %d %s\r\n" - // "Content-Type: text/html\r\n" - // "\r\n%s"; + const char *fmt = + "HTTP/1.1 %d %s\r\n" + "Content-Type: %s\r\n" + "Content-Length: %zu\r\n" + "Connection: close\r\n" + "\r\n"; - *header_len = strlen(version) + sizeof(int) + strlen(status_str) + 2 - + 3 * strlen(crlf) + strlen(content_type); + *header_len = snprintf(NULL, 0, fmt, status_code, status_str, content_type, + content_length); - char *header = calloc(1, *header_len); + char *header = malloc(*header_len + 1); if (!header) { return NULL; } - snprintf(header, *header_len, header_fmt, version, status_code, status_str, - content_type); + snprintf(header, *header_len + 1, fmt, status_code, status_str, content_type, + content_length); return header; } -static int assemble_reply(char **buffer, char *header, size_t header_len, +static int assemble_reply(char **buffer, const char *header, size_t header_len, const char *body, size_t body_len) { - size_t buffer_len = header_len + body_len + 1; + size_t total = header_len + body_len; - *buffer = calloc(1, buffer_len); + *buffer = malloc(total); if (!(*buffer)) { return 0; } memcpy(*buffer, header, header_len); - memcpy(*buffer + header_len - 1, body, body_len); + memcpy(*buffer + header_len, body, body_len); - return buffer_len; + return total; } -static char *get_resource(const char *res_name, size_t *resource_len) { +static char *get_resource(const char *path, size_t *len) { struct stat st; - FILE *file = fopen(res_name, "r"); // TODO: and how about the image files? + FILE *file = fopen(path, "rb"); if (!file) { - fprintf(stderr, "[-] fail to open the requested resource: %s\n", - strerror(errno)); return NULL; } - stat(res_name, &st); - - char *content = calloc(1, st.st_size + 1); - if (!content) { - fprintf(stderr, "[-] fail to allocate memory for the requested resource\n"); + if (stat(path, &st) < 0) { fclose(file); return NULL; } - *resource_len = fread(content, 1, st.st_size, file); - if (!(*resource_len)) { - fprintf(stderr, "[-] fail to read the requested resource\n"); - free(content); + char *data = malloc(st.st_size); + if (!data) { fclose(file); return NULL; } + size_t read_bytes = fread(data, 1, st.st_size, file); fclose(file); - return content; + if (read_bytes != (size_t)st.st_size) { + free(data); + return NULL; + } + + *len = read_bytes; + return data; } static int handle_get_request(char **reply_buffer, size_t *reply_buffer_size, const char *resource) { - int status_code; - size_t res_len; - char *header; size_t header_len; + + if (strstr(resource, "..")) { + return 400; + } + + size_t res_len; char *res = get_resource(resource, &res_len); if (res) { - status_code = 200; - header = build_http_header(status_code, "OK", &header_len); + const char *content_type = get_content_type(resource); + char *header = build_http_header(200, "OK", content_type, res_len, + &header_len); + *reply_buffer_size = assemble_reply(reply_buffer, header, header_len, res, res_len); + + free(header); free(res); - } else { - status_code = 404; - header = build_http_header(status_code, "Not Found", &header_len); - char *body = MAKE_ERROR_BODY(404, Not Found); - *reply_buffer_size = assemble_reply(reply_buffer, header, header_len, - body, strlen(body)); + + return 200; } + const char *body = MAKE_ERROR_BODY(404, Not Found); + size_t body_len = strlen(body); + char *header = build_http_header(404, "Not Found", CONTENT_TYPE_TEXT_HTML, + body_len, &header_len); + + *reply_buffer_size = assemble_reply(reply_buffer, header, header_len, body, + body_len); + free(header); - return status_code; + return 404; } -static int handle_request(const char *req_buffer, char **reply_buffer, +static int handle_request(const char *req_buffer, + char **reply_buffer, size_t *reply_buffer_len) { + request_t req; memset(&req, 0, sizeof(request_t)); parse_request(req_buffer, &req); - - fprintf(stdout, "[+] received request: %s %s\n", req.method, req.resource); - if (strcmp(req.method, "GET")) { - fprintf(stderr, "[-] unsupported request\n"); - // TODO: assemble 501 reply + if (strcmp(req.method, "GET") != 0) { return 501; } - return handle_get_request(reply_buffer, reply_buffer_len, &req.resource[1]); + fprintf(stdout, "[+] %s %s\n", req.method, req.resource); + + const char *resource = req.resource[0] == '/' ? + req.resource + 1 : + req.resource; + + return handle_get_request(reply_buffer, reply_buffer_len, resource); +} + +static void send_all(int fd, const char *buffer, size_t len) { + size_t total = 0; + + while (total < len) { + ssize_t sent = send(fd, buffer + total, len - total, 0); + + if (sent <= 0) { + break; + } + + total += sent; + } } static void *handle_new_connection(void *param) { client_t *cli = (client_t *)param; int *ret = NULL; - char *req_buffer; - char *reply_buffer; - size_t reply_buffer_len; - req_buffer = calloc(1, MAX_BUFFER_SIZE); + char *req_buffer = calloc(1, MAX_BUFFER_SIZE); if (!req_buffer) { - fprintf(stderr, "[-] fail to allocate memory for request buffer\n"); close(cli->fd); return NULL; } ssize_t received = recv(cli->fd, req_buffer, MAX_BUFFER_SIZE - 1, 0); - if (received > 0) { - ret = calloc(1, sizeof(int)); - if (ret) { - *ret = handle_request(req_buffer, &reply_buffer, &reply_buffer_len); - printf("reply_len = %lu\n", reply_buffer_len); - printf("reply = %s\n", reply_buffer); + if (received > 0) { + char *reply_buffer = NULL; + size_t reply_len = 0; - //for (size_t i = 0; i < reply_buffer_len; i++) { - // printf("%c\n", reply_buffer[i]); - //} + ret = calloc(1, sizeof(int)); + *ret = handle_request(req_buffer, &reply_buffer, &reply_len); - if (reply_buffer_len) { - send(cli->fd, reply_buffer, reply_buffer_len - 2, 0); // FIXME: tem coisa amais sendo copiado: GDB!!!!! - free(reply_buffer); - } + if (reply_buffer && reply_len > 0) { + send_all(cli->fd, reply_buffer, reply_len); + free(reply_buffer); } } - close(cli->fd); free(req_buffer); + close(cli->fd); return (void *)ret; } @@ -259,13 +293,13 @@ static void fill_cfg(int argc, char **argv, server_cfg_t *cfg) { while ((opt = getopt(argc, argv, "m:p:r:")) != -1) { switch (opt) { case 'm': - cfg->max_clients = (uint32_t)atoi(optarg); + cfg->max_clients = atoi(optarg); break; case 'p': - cfg->port = (uint16_t)atoi(optarg); + cfg->port = atoi(optarg); break; case 'r': - memcpy(cfg->root_dir, optarg, strlen(optarg) + 1); + strcpy(cfg->root_dir, optarg); break; } } @@ -278,8 +312,8 @@ static void fill_cfg(int argc, char **argv, server_cfg_t *cfg) { cfg->port = DEFAULT_PORT; } - if (cfg->root_dir[0] == '\0') { - memcpy(cfg->root_dir, DEFAULT_ROOT_DIR, strlen(DEFAULT_ROOT_DIR) + 1); + if (!cfg->root_dir[0]) { + strcpy(cfg->root_dir, DEFAULT_ROOT_DIR); } } @@ -310,17 +344,14 @@ static int setup_socket(int *sock_fd, const server_cfg_t *cfg) { return 0; } -static int setup_server(const server_cfg_t *cfg, int *server_fd, +static int setup_server(int *sock_fd, const server_cfg_t *cfg, pool_day_t *pool) { - if (setup_socket(server_fd, cfg) != 0) { - fprintf(stderr, "[-] fail to setup the server socket: %s\n", - strerror(errno)); + if (setup_socket(sock_fd, cfg) != 0) { return 1; } if (!(*pool = create_pool(cfg->max_clients))) { - fprintf(stderr, "[-] fail to setup the server pool\n"); - close(*server_fd); + close(*sock_fd); return 1; } @@ -328,27 +359,22 @@ static int setup_server(const server_cfg_t *cfg, int *server_fd, } static int server_mainloop(int server_fd, pool_day_t pool) { - int ret; fd_set set; - struct sockaddr_in cli_addr; - socklen_t cli_len = sizeof(cli_addr); while (1) { FD_ZERO(&set); FD_SET(server_fd, &set); - ret = select(server_fd + 1, &set, NULL, NULL, NULL); - if ((ret == -1) && (errno == EINTR)) { - fprintf(stdout, "[+] exiting server...\n"); + if (select(server_fd + 1, &set, NULL, NULL, NULL) < 0) { break; } if (FD_ISSET(server_fd, &set)) { - int client_fd = accept(server_fd, (struct sockaddr *)&cli_addr, &cli_len); + struct sockaddr_in cli_addr; + socklen_t len = sizeof(cli_addr); - if (client_fd == -1) { - fprintf(stderr, "[-] fail to accept the incoming client: %s\n", - strerror(errno)); + int client_fd = accept(server_fd, (struct sockaddr *)&cli_addr, &len); + if (client_fd < 0) { continue; } @@ -360,32 +386,29 @@ static int server_mainloop(int server_fd, pool_day_t pool) { on_client_connected, on_client_disconnected); - if (enqueue_task(pool, task) != POOL_DAY_SUCCESS) { - fprintf(stderr, "[-] fail to enqueue the request task\n"); - destroy_task(task); - close(client_fd); - } + enqueue_task(pool, task); } } close(server_fd); destroy_pool(&pool); - return EXIT_SUCCESS; + return 0; } static int run_server(const server_cfg_t *cfg) { int server_fd; pool_day_t pool; - if (setup_server(cfg, &server_fd, &pool)) { + if (setup_server(&server_fd, cfg, &pool)) { fprintf(stderr, "[-] fail to setup the server\n"); - return EXIT_FAILURE; + return 1; } if (chdir(cfg->root_dir)) { - fprintf(stderr, "[-] fail to run the server: %s\n", strerror(errno)); - return EXIT_FAILURE; + fprintf(stderr, "[-] fail to run the server on '%s': %s\n", cfg->root_dir, + strerror(errno)); + return 1; } fprintf(stdout, @@ -398,13 +421,10 @@ static int run_server(const server_cfg_t *cfg) { int main(int argc, char **argv) { server_cfg_t cfg; - memset(&cfg, 0, sizeof(server_cfg_t)); - signal(SIGINT, sig_handler); - signal(SIGTERM, sig_handler); + memset(&cfg, 0, sizeof(server_cfg_t)); fill_cfg(argc, argv, &cfg); - int ret = run_server(&cfg); - exit(ret); + return run_server(&cfg); }