pax_global_header00006660000000000000000000000064134737502000014513gustar00rootroot0000000000000052 comment=048da9485390ee8b8a7ecedccc598a06ca4429e2 thread_pool-2.0.1/000077500000000000000000000000001347375020000140135ustar00rootroot00000000000000thread_pool-2.0.1/.gitignore000066400000000000000000000000371347375020000160030ustar00rootroot00000000000000# Compiled Object files build/ thread_pool-2.0.1/.gitmodules000066400000000000000000000001461347375020000161710ustar00rootroot00000000000000[submodule "vendor/googletest"] path = vendor/googletest url = https://github.com/google/googletest thread_pool-2.0.1/.travis.yml000066400000000000000000000017041347375020000161260ustar00rootroot00000000000000language: cpp compiler: - clang - gcc before_install: # cmake 3.2 - sudo add-apt-repository ppa:george-edison55/cmake-3.x -y # g++4.8.1 - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test # clang 3.4 - if [ "$CXX" == "clang++" ]; then sudo add-apt-repository -y ppa:h-rayflood/llvm; fi - sudo apt-get update -qq install: # cmake 3.2 - sudo apt-get install cmake cmake-data # g++4.8.1 - sudo apt-get install -qq g++-4.8 - if [ "$CXX" == "g++" ]; then export CXX="g++-4.8"; fi # clang 3.4 - if [ "$CXX" == "clang++" ]; then sudo apt-get install --allow-unauthenticated -qq clang-3.4; fi - if [ "$CXX" == "clang++" ]; then export CXX="clang++-3.4"; fi script: - mkdir build - cd build - cmake -DCMAKE_BUILD_TYPE=Release -Dthread_pool_build_tests=ON .. - make - ./bin/thread_pool_test notifications: email: on_success: change on_failure: always thread_pool-2.0.1/CMakeLists.txt000066400000000000000000000024071347375020000165560ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.2) project(thread_pool) include(GNUInstallDirs) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -march=native") set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) option(thread_pool_build_tests "Build thread_pool unit tests" OFF) add_library(thread_pool STATIC src/thread_pool.cpp) target_link_libraries(thread_pool Threads::Threads) target_include_directories(thread_pool PUBLIC $ $) install(TARGETS thread_pool DESTINATION ${CMAKE_INSTALL_LIBDIR}) install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/thread_pool DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) if (thread_pool_build_tests) add_executable(thread_pool_test test/thread_pool_test.cpp) if (NOT TARGET gtest_main) add_subdirectory(vendor/googletest/googletest EXCLUDE_FROM_ALL) endif() target_link_libraries(thread_pool_test thread_pool gtest_main) endif() thread_pool-2.0.1/LICENSE000066400000000000000000000020551347375020000150220ustar00rootroot00000000000000MIT License Copyright (c) 2017 Robert Vaser 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. thread_pool-2.0.1/README.md000066400000000000000000000046211347375020000152750ustar00rootroot00000000000000# Thread pool [![Latest GitHub release](https://img.shields.io/github/release/rvaser/thread_pool.svg)](https://github.com/rvaser/thread_pool/releases/latest) ![image](https://travis-ci.org/rvaser/thread_pool.svg?branch=master) A c++ thread pool implementation inspired by https://github.com/progschj/ThreadPool. ## Dependencies ### Linux Application uses following software: 1. gcc 4.8+ or clang 3.4+ 2. cmake 3.2+ ## Instalation By running the following commands: ```bash git clone https://github.com/rvaser/thread_pool.git thread_pool cd thread_pool mkdir build cd build cmake -DCMAKE_BUILD_TYPE=Release .. make ``` a library named `libthread_pool.a` will appear in `build/lib` directory. Optionally, you can run `sudo make install` to install thread_pool library to your machine. Alternatively, add the project to your `CMakeLists.txt` file with `add_subdirectory(vendor/thread_pool EXCLUDE_FROM_ALL)` and `target_link_libraries(your_exe thread_pool)` commands. To build unit tests run `git submodule update --init` and add `-Dthread_pool_build_tests=ON` while running `cmake`. After installation, an executable named `thread_pool_test` will be created in `build/bin`. ## Usage For details on how to use the thread pool, please look at the example bellow: ```cpp // define some functions you want to execute in parallel int function1(std::vector& data, int index, ...) { ... } int function2(float a, float b) { ... } void function3(void) { ... } // create thread pool std::shared_ptr thread_pool = thread_pool::createThreadPool(); // or pass number of threads you desire // or std::unique_ptr ... // create storage for return values of function1 and function2 std::vector> thread_futures; for (std::uint32_t i = 0; i < num_tasks; ++i) { // be sure to use std::ref() when passing references! thread_futures.emplace_back(thread_pool->submit(function1, std::ref(data), index, ...)); thread_futures.emplace_back(thread_pool->submit(function2, a, b)); } // wait for threads to finish for (auto& it: thread_futures) { it.wait(); // get return value with it.get(); } // new set of tasks running function3 std::vector> thread_futures2; for (std::uint32_t i = 0; i < num_tasks2; ++i) { thread_futures2.emplace_back(thread_pool->submit(function3)); } for (auto& it2: thread_futures2) { it.wait(); } ``` thread_pool-2.0.1/include/000077500000000000000000000000001347375020000154365ustar00rootroot00000000000000thread_pool-2.0.1/include/thread_pool/000077500000000000000000000000001347375020000177365ustar00rootroot00000000000000thread_pool-2.0.1/include/thread_pool/thread_pool.hpp000066400000000000000000000047121347375020000227530ustar00rootroot00000000000000/*! * @file thread_pool.hpp * * @brief ThreadPool class header file */ #pragma once #include #include #include #include #include #include #include #include #include #include #include namespace thread_pool { static const std::string version = "v2.0.1"; class Semaphore; std::unique_ptr createSemaphore(std::uint32_t value); class ThreadPool; std::unique_ptr createThreadPool(std::uint32_t num_threads = std::thread::hardware_concurrency() / 2); class Semaphore { public: ~Semaphore() = default; std::uint32_t value() const { return value_; } void wait(); void post(); friend std::unique_ptr createSemaphore(std::uint32_t value); private: Semaphore(std::uint32_t value); Semaphore(const Semaphore&) = delete; const Semaphore& operator=(const Semaphore&) = delete; std::mutex mutex_; std::condition_variable condition_; std::uint32_t value_; }; class ThreadPool { public: ~ThreadPool(); std::uint32_t num_threads() const { return threads_.size(); } const std::vector& thread_identifiers() const { return thread_identifiers_; } template auto submit(T&& routine, Ts&&... params) -> std::future::type> { auto task = std::make_shared::type()>>( std::bind(std::forward(routine), std::forward(params)...) ); auto task_result = task->get_future(); auto task_wrapper = [task]() { (*task)(); }; queue_sem_->wait(); task_queue_.emplace(task_wrapper); queue_sem_->post(); active_sem_->post(); return task_result; } friend std::unique_ptr createThreadPool(std::uint32_t num_threads); private: ThreadPool(std::uint32_t num_threads); ThreadPool(const ThreadPool&) = delete; const ThreadPool& operator=(const ThreadPool&) = delete; static void worker_thread(ThreadPool* thread_pool); std::vector threads_; std::vector thread_identifiers_; std::queue> task_queue_; std::unique_ptr queue_sem_; std::unique_ptr active_sem_; std::atomic terminate_; }; } thread_pool-2.0.1/src/000077500000000000000000000000001347375020000146025ustar00rootroot00000000000000thread_pool-2.0.1/src/thread_pool.cpp000066400000000000000000000036401347375020000176110ustar00rootroot00000000000000/*! * @file thread_pool.cpp * * @brief ThreadPool class source file */ #include #include "thread_pool/thread_pool.hpp" namespace thread_pool { std::unique_ptr createSemaphore(std::uint32_t value) { return std::unique_ptr(new Semaphore(value)); } std::unique_ptr createThreadPool(std::uint32_t num_threads) { if (num_threads == 0) { throw std::invalid_argument("[thread_pool::createThreadPool] error: " "invalid number of threads!"); } return std::unique_ptr(new ThreadPool(num_threads)); } Semaphore::Semaphore(std::uint32_t value) : value_(value) { } void Semaphore::post() { std::unique_lock lock(mutex_); ++value_; condition_.notify_one(); } void Semaphore::wait() { std::unique_lock lock(mutex_); condition_.wait(lock, [&](){ return value_; }); --value_; } ThreadPool::ThreadPool(std::uint32_t num_threads) { queue_sem_ = createSemaphore(1); active_sem_ = createSemaphore(0); terminate_ = false; for (std::uint32_t i = 0; i < num_threads; ++i) { threads_.emplace_back(ThreadPool::worker_thread, this); thread_identifiers_.emplace_back(threads_.back().get_id()); } } ThreadPool::~ThreadPool() { terminate_ = true; for (std::uint32_t i = 0; i < threads_.size(); ++i) { active_sem_->post(); } for (auto& it: threads_) { it.join(); } } void ThreadPool::worker_thread(ThreadPool* thread_pool) { while (true) { thread_pool->active_sem_->wait(); if (thread_pool->terminate_) { break; } thread_pool->queue_sem_->wait(); auto task = std::move(thread_pool->task_queue_.front()); thread_pool->task_queue_.pop(); thread_pool->queue_sem_->post(); if (thread_pool->terminate_) { break; } task(); } } } thread_pool-2.0.1/test/000077500000000000000000000000001347375020000147725ustar00rootroot00000000000000thread_pool-2.0.1/test/thread_pool_test.cpp000066400000000000000000000057321347375020000210440ustar00rootroot00000000000000/*! * @file thread_pool_test.cpp * * @brief Thread_pool unit test source file */ #include #include #include "thread_pool/thread_pool.hpp" #include "gtest/gtest.h" class ThreadPoolTest: public ::testing::Test { public: void SetUp() { thread_pool = thread_pool::createThreadPool(); } void TearDown() {} std::unique_ptr thread_pool; }; TEST(ThreadPoolTest_, CreateThreadPoolError) { try { auto thread_pool = thread_pool::createThreadPool(0); } catch (std::invalid_argument& exception) { EXPECT_STREQ(exception.what(), "[thread_pool::createThreadPool] error: " "invalid number of threads!"); } } TEST_F(ThreadPoolTest, ParallelCalculation) { std::vector> data(10); for (auto& it: data) { it.reserve(100000); for (std::uint32_t i = 0; i < 100000; ++i) { it.push_back(i); } } auto do_some_calculation = [](std::vector& src) -> void { for (std::uint32_t i = 0; i < src.size() - 1; ++i) { src[i] = (src[i] * src[i + 1]) / (src[i] - src[i + 1] * 3); } }; std::vector> thread_futures; for (std::uint32_t i = 0; i < data.size(); ++i) { thread_futures.emplace_back(thread_pool->submit(do_some_calculation, std::ref(data[i]))); } for (const auto& it: thread_futures) { it.wait(); } } TEST_F(ThreadPoolTest, ThreadIdentifiers) { const auto& identifiers = thread_pool->thread_identifiers(); std::unordered_map thread_map; std::uint32_t thread_id = 0; for (const auto& it: identifiers) { thread_map[it] = thread_id++; } EXPECT_EQ(thread_id, thread_map.size()); auto barrier = thread_pool::createSemaphore(0); auto checkpoint = thread_pool::createSemaphore(0); auto check_thread_id = [&barrier, &checkpoint]( std::unordered_map& thread_map) -> std::int32_t { checkpoint->post(); barrier->wait(); if (thread_map.count(std::this_thread::get_id()) != 0) { return thread_map[std::this_thread::get_id()]; } return -1; }; std::vector> thread_futures; for (std::uint32_t i = 0; i < thread_id; ++i) { thread_futures.emplace_back(thread_pool->submit(check_thread_id, std::ref(thread_map))); } for (std::uint32_t i = 0; i < thread_id; ++i) { checkpoint->wait(); } for (std::uint32_t i = 0; i < thread_id; ++i) { barrier->post(); } std::unordered_set thread_identifiers; for (auto& it: thread_futures) { it.wait(); thread_identifiers.emplace(it.get()); } EXPECT_EQ(thread_id, thread_identifiers.size()); for (std::uint32_t i = 0; i < thread_id; ++i) { EXPECT_EQ(1U, thread_identifiers.count(i)); } } thread_pool-2.0.1/vendor/000077500000000000000000000000001347375020000153105ustar00rootroot00000000000000thread_pool-2.0.1/vendor/googletest/000077500000000000000000000000001347375020000174645ustar00rootroot00000000000000