Skip to content

Commit

Permalink
fixes #12
Browse files Browse the repository at this point in the history
  • Loading branch information
Colin S committed Dec 21, 2024
1 parent 027ba9f commit a03a0aa
Show file tree
Hide file tree
Showing 3 changed files with 243 additions and 26 deletions.
62 changes: 36 additions & 26 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,29 +1,39 @@
add_executable(
unit
unit.cpp
${SIGFN_SOURCES})
# add_executable(
# unit
# unit.cpp
# ${SIGFN_SOURCES})

target_include_directories(unit PRIVATE ${SIGFN_INCLUDE} ${CMAKE_CURRENT_SOURCE_DIR}/../src)
# target_include_directories(unit PRIVATE ${SIGFN_INCLUDE} ${CMAKE_CURRENT_SOURCE_DIR}/../src)

if(SIGFN_COVER)
if(WIN32)
message("skipping code coverage for windows")
else()
target_compile_options(unit PRIVATE -fprofile-arcs -ftest-coverage -g -O0)
target_link_libraries(unit PRIVATE gcov "--coverage")
add_custom_target(
cover
DEPENDS unit)
add_custom_command(
TARGET cover
COMMAND gcovr -r ${CMAKE_CURRENT_SOURCE_DIR}/.. -e ${CMAKE_CURRENT_SOURCE_DIR})
endif()
endif()
# if(SIGFN_COVER)
# if(WIN32)
# message("skipping code coverage for windows")
# else()
# target_compile_options(unit PRIVATE -fprofile-arcs -ftest-coverage -g -O0)
# target_link_libraries(unit PRIVATE gcov "--coverage")
# add_custom_target(
# cover
# DEPENDS unit)
# add_custom_command(
# TARGET cover
# COMMAND gcovr -r ${CMAKE_CURRENT_SOURCE_DIR}/.. -e ${CMAKE_CURRENT_SOURCE_DIR})
# endif()
# endif()

add_test(NAME sigfn_handle COMMAND unit sigfn_handle )
add_test(NAME sigfn_ignore COMMAND unit sigfn_ignore)
add_test(NAME sigfn_reset COMMAND unit sigfn_reset)
add_test(NAME sigfn_error COMMAND unit sigfn_error)
add_test(NAME sigfn::handle COMMAND unit sigfn::handle)
add_test(NAME sigfn::ignore COMMAND unit sigfn::ignore)
add_test(NAME sigfn::reset COMMAND unit sigfn::reset)
# add_test(NAME sigfn_handle COMMAND unit sigfn_handle )
# add_test(NAME sigfn_ignore COMMAND unit sigfn_ignore)
# add_test(NAME sigfn_reset COMMAND unit sigfn_reset)
# add_test(NAME sigfn_error COMMAND unit sigfn_error)
# add_test(NAME sigfn::handle COMMAND unit sigfn::handle)
# add_test(NAME sigfn::ignore COMMAND unit sigfn::ignore)
# add_test(NAME sigfn::reset COMMAND unit sigfn::reset)

add_library(threadpool_a STATIC threadpool.cpp)
target_include_directories(threadpool_a PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

add_library(threadpool SHARED threadpool.cpp)
target_include_directories(threadpool PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

add_library(threadpool INTERFACE)

target_include_directories(threadpool INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
83 changes: 83 additions & 0 deletions tests/Threadpool.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#include <iostream>
#include "threadpool.hpp"

maxtek::threadpool::threadpool(size_t threads)
{
if (threads == 0)
{
throw std::runtime_error("failed to construct threadpool with zero threads");
}

_active = true;

_workers.reserve(threads);

while (_workers.size() < _workers.capacity())
{
_workers.push_back(std::thread([&]()
{
std::function<void()> task;
while (pop_task(task))
{
task();
} }));
}
}

maxtek::threadpool::~threadpool()
{
if (_active)
{
shutdown();
}
}

bool maxtek::threadpool::active() const
{
return _active;
}

void maxtek::threadpool::shutdown()
{
if (!_active)
{
throw std::runtime_error("failed to shut down inactive threadpool");
}
_active = false;
_condition.notify_all();
for (std::thread &worker : _workers)
{
worker.join();
}
}

void maxtek::threadpool::push_task(std::function<void()> &&task)
{
std::unique_lock<std::mutex> lock(_mutex);
if (!_active)
{
throw std::runtime_error("failed to submit to inactive threadpool");
}
_tasks.push(std::move(task));
lock.unlock();
_condition.notify_one();
}

bool maxtek::threadpool::pop_task(std::function<void()> &task)
{
std::unique_lock<std::mutex> lock(_mutex);
bool result(false);
_condition.wait(
lock,
[&]()
{
return (!_active || !_tasks.empty());
});
if (_active)
{
task = _tasks.front();
_tasks.pop();
result = true;
}
return result;
}
124 changes: 124 additions & 0 deletions tests/Threadpool.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
** Copyright 2024 Maxtek Consulting
**
** Permission is hereby granted, free of charge, to any person obtaining a copy
** of this software and associated documentation files (the "Software"), to deal
** in the Software without restriction, including without limitation the rights
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
** copies of the Software, and to permit persons to whom the Software is
** furnished to do so, subject to the following conditions:
**
** The above copyright notice and this permission notice shall be included in all
** copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
** SOFTWARE.
*/

#ifndef THREADPOOL_HPP
#define THREADPOOL_HPP

/**
* @file threadpool.hpp
* @brief Maxtek threadpool
* @author Max Guerrero and John R. Patek Sr.
*/

#include <algorithm>
#include <functional>
#include <future>
#include <memory>
#include <queue>
#include <thread>
#include <typeinfo>
#include <vector>

#ifdef _WIN32
#define DLL_EXPORT __declspec(dllexport)
#else
#define DLL_EXPORT
#endif

namespace maxtek
{
/**
* @brief threadpool
*
* @class allows tasks to be submitted and executed asynchronously across multiple threads.
*/
class DLL_EXPORT threadpool
{
public:

/**
* @brief constructs a new threadpool
*
* @param threads number of threads to use for constructing the threadpool
* @exception std::runtime_error if threads is set to zero
*/
threadpool(size_t threads = std::thread::hardware_concurrency());

/**
* @brief destroys threadpool after calling shutdown if necessary
*/
~threadpool();

/**
* @brief submits a function with its arguments to the threadpool
* @tparam F function signature
* @tparam Args function argument types
* @param function function signature
* @param args arguments to pass to the function
* @returns a future holding the asynchronous function result
* @exception std::runtime_error if the thread pool has been shut down
*/
template <class F, class... Args>
std::future<std::result_of_t<F(Args...)>> submit(F &&function, Args &&...args)
{
std::shared_ptr<std::packaged_task<std::result_of_t<F(Args...)>()>> packaged_task;
std::function<void()> work;
std::future<std::result_of_t<F(Args...)>> result;

packaged_task = std::make_shared<std::packaged_task<std::result_of_t<F(Args...)>()>>(std::bind(std::forward<F>(function), std::forward<Args>(args)...));
result = packaged_task->get_future();

work = [packaged_task]()
{
(*packaged_task)();
};

push_task(std::move(work));

return result;
}

/**
* @brief check if the threadpool is active
* @returns true if the threadpool is active, false if it has been shut down
*/
bool active() const;

/**
* @brief shut down threadpool by joining threads and rejecting submissions
* @exception std::runtime_error if the thread pool has already been shut down
*/
void shutdown();

private:
void push_task(std::function<void()> &&task);
bool pop_task(std::function<void()> &task);

size_t num_threads;
bool _active;
std::vector<std::thread> _workers;
std::queue<std::function<void()>> _tasks;
std::mutex _mutex;
std::condition_variable _condition;
};
}
#endif

0 comments on commit a03a0aa

Please sign in to comment.