Import Geant4 11.0.0.beta source tree
This commit is contained in:
+30
-6
@@ -46,6 +46,22 @@
|
||||
::std::initializer_list<int>{ (__VA_ARGS__, 0)... })
|
||||
#endif
|
||||
|
||||
#if !defined(PTL_NO_SANITIZE_THREAD)
|
||||
// expect that sanitizer is from compiler which supports __has_attribute
|
||||
# if defined(__has_attribute)
|
||||
# if __has_attribute(no_sanitize)
|
||||
# define PTL_NO_SANITIZE_THREAD __attribute__((no_sanitize("thread")))
|
||||
# else
|
||||
# define PTL_NO_SANITIZE_THREAD
|
||||
# endif
|
||||
# elif defined(__clang__) || defined(__GNUC__)
|
||||
# define PTL_NO_SANITIZE_THREAD __attribute__((no_sanitize("thread")))
|
||||
# else
|
||||
// otherwise, make blank
|
||||
# define PTL_NO_SANITIZE_THREAD
|
||||
# endif
|
||||
#endif
|
||||
|
||||
namespace PTL
|
||||
{
|
||||
template <typename T>
|
||||
@@ -144,10 +160,17 @@ using index_type_t = decay_t<decltype(std::get<Idx>(std::declval<Tup>()))>;
|
||||
|
||||
template <typename FnT, typename TupleT, size_t... Idx>
|
||||
static inline auto
|
||||
apply(FnT&& __f, TupleT&& __t, impl::index_sequence<Idx...>)
|
||||
-> decltype(std::forward<FnT>(__f)(std::get<Idx>(__t)...))
|
||||
apply(FnT&& _func, TupleT _args, impl::index_sequence<Idx...>)
|
||||
-> decltype(std::forward<FnT>(_func)(std::get<Idx>(std::move(_args))...))
|
||||
{
|
||||
return std::forward<FnT>(__f)(std::get<Idx>(__t)...);
|
||||
// GCC 5.3 warns about unused variable _args when the index sequence is empty
|
||||
#if defined(__GNUC__) && (__GNUC__ < 6)
|
||||
if(sizeof...(Idx) == 0)
|
||||
{
|
||||
consume_parameters(_args);
|
||||
}
|
||||
#endif
|
||||
return std::forward<FnT>(_func)(std::get<Idx>(std::move(_args))...);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
@@ -170,10 +193,11 @@ using index_sequence_for = impl::make_index_sequence<sizeof...(Types)>;
|
||||
|
||||
template <typename FnT, typename TupleT>
|
||||
static inline void
|
||||
apply(FnT&& __f, TupleT&& __t)
|
||||
apply(FnT&& _func, TupleT&& _args)
|
||||
{
|
||||
constexpr auto N = std::tuple_size<TupleT>::value;
|
||||
impl::apply(std::forward<FnT>(__f), std::forward<TupleT>(__t),
|
||||
using tuple_type = typename std::decay<TupleT>::type;
|
||||
constexpr auto N = std::tuple_size<tuple_type>::value;
|
||||
impl::apply(std::forward<FnT>(_func), std::forward<TupleT>(_args),
|
||||
impl::make_index_sequence<N>{});
|
||||
}
|
||||
|
||||
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
//
|
||||
// MIT License
|
||||
// Copyright (c) 2020 Jonathan R. Madsen
|
||||
// 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
|
||||
// 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.
|
||||
//
|
||||
//
|
||||
// ---------------------------------------------------------------
|
||||
// Tasking class header file
|
||||
//
|
||||
// Class Description:
|
||||
//
|
||||
// This is the join function used by task groups
|
||||
//
|
||||
// ---------------------------------------------------------------
|
||||
// Author: Jonathan Madsen (Feb 13th 2018)
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "PTL/Types.hh"
|
||||
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
namespace PTL
|
||||
{
|
||||
template <typename JoinT, typename JoinArg>
|
||||
struct JoinFunction
|
||||
{
|
||||
public:
|
||||
using Type = std::function<JoinT(JoinT&, JoinArg&&)>;
|
||||
|
||||
public:
|
||||
PTL_DEFAULT_OBJECT(JoinFunction)
|
||||
|
||||
template <typename Func>
|
||||
JoinFunction(Func&& func)
|
||||
: m_func(std::forward<Func>(func))
|
||||
{}
|
||||
|
||||
template <typename... Args>
|
||||
JoinT& operator()(Args&&... args)
|
||||
{
|
||||
return std::move(m_func(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
private:
|
||||
Type m_func = [](JoinT& lhs, JoinArg&&) { return lhs; };
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
template <typename JoinArg>
|
||||
struct JoinFunction<void, JoinArg>
|
||||
{
|
||||
public:
|
||||
using Type = std::function<void(JoinArg)>;
|
||||
|
||||
public:
|
||||
PTL_DEFAULT_OBJECT(JoinFunction)
|
||||
|
||||
template <typename Func>
|
||||
JoinFunction(Func&& func)
|
||||
: m_func(std::forward<Func>(func))
|
||||
{}
|
||||
|
||||
template <typename... Args>
|
||||
void operator()(Args&&... args)
|
||||
{
|
||||
m_func(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
private:
|
||||
Type m_func = [](JoinArg) {};
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
template <>
|
||||
struct JoinFunction<void, void>
|
||||
{
|
||||
public:
|
||||
using Type = std::function<void()>;
|
||||
|
||||
public:
|
||||
PTL_DEFAULT_OBJECT(JoinFunction)
|
||||
|
||||
template <typename Func>
|
||||
JoinFunction(Func&& func)
|
||||
: m_func(std::forward<Func>(func))
|
||||
{}
|
||||
|
||||
void operator()() { m_func(); }
|
||||
|
||||
private:
|
||||
Type m_func = []() {};
|
||||
};
|
||||
|
||||
} // namespace PTL
|
||||
+1
-203
@@ -30,213 +30,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "PTL/TaskGroup.hh"
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#if defined(PTL_USE_TBB)
|
||||
# include <tbb/tbb.h>
|
||||
#endif
|
||||
|
||||
namespace PTL
|
||||
{
|
||||
class ThreadPool;
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
#if defined(PTL_USE_TBB)
|
||||
|
||||
class ThreadPool;
|
||||
namespace
|
||||
{
|
||||
typedef ::tbb::task_group tbb_task_group_t;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
template <typename Tp, typename Arg = Tp>
|
||||
class TBBTaskGroup : public TaskGroup<Tp, Arg>
|
||||
{
|
||||
public:
|
||||
//------------------------------------------------------------------------//
|
||||
typedef decay_t<Arg> ArgTp;
|
||||
typedef TBBTaskGroup<Tp, Arg> this_type;
|
||||
typedef TaskGroup<Tp, Arg> base_type;
|
||||
typedef typename base_type::result_type result_type;
|
||||
typedef typename base_type::packaged_task_type packaged_task_type;
|
||||
typedef typename base_type::future_type future_type;
|
||||
typedef typename base_type::promise_type promise_type;
|
||||
typedef typename JoinFunction<Tp, Arg>::Type join_type;
|
||||
typedef tbb::task_group tbb_task_group_t;
|
||||
//------------------------------------------------------------------------//
|
||||
template <typename... Args>
|
||||
using task_type = Task<ArgTp, decay_t<Args>...>;
|
||||
//------------------------------------------------------------------------//
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
template <typename Func>
|
||||
TBBTaskGroup(Func&& _join, ThreadPool* _tp = nullptr)
|
||||
: base_type(std::forward<Func>(_join), _tp)
|
||||
, m_tbb_task_group(new tbb_task_group_t)
|
||||
{}
|
||||
template <typename Up = Tp, enable_if_t<std::is_same<Up, void>::value, int> = 0>
|
||||
TBBTaskGroup(ThreadPool* _tp = nullptr)
|
||||
: base_type(_tp)
|
||||
, m_tbb_task_group(new tbb_task_group_t)
|
||||
{}
|
||||
|
||||
// Destructor
|
||||
virtual ~TBBTaskGroup()
|
||||
{
|
||||
delete m_tbb_task_group;
|
||||
this->clear();
|
||||
}
|
||||
|
||||
// delete copy-construct
|
||||
TBBTaskGroup(const this_type&) = delete;
|
||||
// define move-construct
|
||||
TBBTaskGroup(this_type&& rhs) = default;
|
||||
|
||||
// delete copy-assign
|
||||
this_type& operator=(const this_type& rhs) = delete;
|
||||
// define move-assign
|
||||
this_type& operator=(this_type&& rhs) = default;
|
||||
|
||||
public:
|
||||
//------------------------------------------------------------------------//
|
||||
template <typename Up>
|
||||
Up* operator+=(Up* _task)
|
||||
{
|
||||
// store in list
|
||||
vtask_list.push_back(_task);
|
||||
// thread-safe increment of tasks in task group
|
||||
operator++();
|
||||
// add the future
|
||||
m_task_set.push_back(std::move(_task->get_future()));
|
||||
// return
|
||||
return _task;
|
||||
}
|
||||
|
||||
public:
|
||||
//------------------------------------------------------------------------//
|
||||
template <typename Func, typename... Args>
|
||||
task_type<Args...>* wrap(Func&& func, Args... args)
|
||||
{
|
||||
return operator+=(
|
||||
new task_type<Args...>(this, std::forward<Func>(func), args...));
|
||||
}
|
||||
|
||||
public:
|
||||
//------------------------------------------------------------------------//
|
||||
template <typename Func, typename... Args>
|
||||
void run(Func&& func, Args... args)
|
||||
{
|
||||
auto _func = [=]() { return func(args...); };
|
||||
auto _task = wrap(std::move(_func));
|
||||
auto _lamb = [=]() { (*_task)(); };
|
||||
m_tbb_task_group->run(_lamb);
|
||||
}
|
||||
//------------------------------------------------------------------------//
|
||||
template <typename Func, typename... Args>
|
||||
void exec(Func&& func, Args... args)
|
||||
{
|
||||
auto _func = [=]() { return func(args...); };
|
||||
auto _task = wrap(std::move(_func));
|
||||
auto _lamb = [=]() { (*_task)(); };
|
||||
m_tbb_task_group->run(_lamb);
|
||||
}
|
||||
//------------------------------------------------------------------------//
|
||||
template <typename Func, typename... Args, typename Up = Tp,
|
||||
enable_if_t<std::is_same<Up, void>::value, int> = 0>
|
||||
void parallel_for(uintmax_t nitr, uintmax_t, Func&& func, Args... args)
|
||||
{
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, nitr),
|
||||
[&](const tbb::blocked_range<size_t>& range) {
|
||||
for(size_t i = range.begin(); i != range.end(); ++i)
|
||||
func(args...);
|
||||
});
|
||||
}
|
||||
|
||||
public:
|
||||
//------------------------------------------------------------------------//
|
||||
// this is not a native Tasking task group
|
||||
virtual bool is_native_task_group() const override { return false; }
|
||||
|
||||
//------------------------------------------------------------------------//
|
||||
// wait on tbb::task_group, not internal thread-pool
|
||||
virtual void wait() override
|
||||
{
|
||||
base_type::wait();
|
||||
m_tbb_task_group->wait();
|
||||
}
|
||||
|
||||
public:
|
||||
using base_type::begin;
|
||||
using base_type::cbegin;
|
||||
using base_type::cend;
|
||||
using base_type::clear;
|
||||
using base_type::end;
|
||||
using base_type::get_tasks;
|
||||
|
||||
//------------------------------------------------------------------------//
|
||||
// wait to finish
|
||||
template <typename Up = Tp, enable_if_t<!std::is_void<Up>::value, int> = 0>
|
||||
inline Up join(Up accum = {})
|
||||
{
|
||||
this->wait();
|
||||
for(auto& itr : m_task_set)
|
||||
{
|
||||
using RetT = decay_t<decltype(itr.get())>;
|
||||
accum = m_join(std::ref(accum), std::forward<RetT>(itr.get()));
|
||||
}
|
||||
this->clear();
|
||||
return accum;
|
||||
}
|
||||
//------------------------------------------------------------------------//
|
||||
// wait to finish
|
||||
template <typename Up = Tp, typename Rp = Arg,
|
||||
enable_if_t<std::is_void<Up>::value && std::is_void<Rp>::value, int> = 0>
|
||||
inline void join()
|
||||
{
|
||||
this->wait();
|
||||
for(auto& itr : m_task_set)
|
||||
itr.get();
|
||||
m_join();
|
||||
this->clear();
|
||||
}
|
||||
//------------------------------------------------------------------------//
|
||||
// wait to finish
|
||||
template <typename Up = Tp, typename Rp = Arg,
|
||||
enable_if_t<std::is_void<Up>::value && !std::is_void<Rp>::value, int> = 0>
|
||||
inline void join()
|
||||
{
|
||||
this->wait();
|
||||
for(auto& itr : m_task_set)
|
||||
{
|
||||
using RetT = decay_t<decltype(itr.get())>;
|
||||
m_join(std::forward<RetT>(itr.get()));
|
||||
}
|
||||
this->clear();
|
||||
}
|
||||
|
||||
protected:
|
||||
// Protected variables
|
||||
tbb_task_group_t* m_tbb_task_group;
|
||||
using base_type:: operator++;
|
||||
using base_type:: operator--;
|
||||
using base_type::m_join;
|
||||
using base_type::m_task_set;
|
||||
using base_type::vtask_list;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
#else
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
// in new version, TaskGroup handles TBB
|
||||
template <typename Tp, typename Arg = Tp>
|
||||
using TBBTaskGroup = TaskGroup<Tp, Arg>;
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
#endif
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
} // namespace PTL
|
||||
|
||||
+110
-106
@@ -31,7 +31,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "Globals.hh"
|
||||
#include "TaskAllocator.hh"
|
||||
#include "VTask.hh"
|
||||
|
||||
#include <cstdint>
|
||||
@@ -40,14 +39,46 @@
|
||||
|
||||
namespace PTL
|
||||
{
|
||||
class VTaskGroup;
|
||||
class ThreadPool;
|
||||
|
||||
//======================================================================================//
|
||||
|
||||
/// \brief The task class is supplied to thread_pool.
|
||||
template <typename RetT>
|
||||
class TaskFuture : public VTask
|
||||
{
|
||||
public:
|
||||
typedef std::promise<RetT> promise_type;
|
||||
typedef std::future<RetT> future_type;
|
||||
typedef RetT result_type;
|
||||
|
||||
public:
|
||||
// pass a free function pointer
|
||||
template <typename... Args>
|
||||
TaskFuture(Args&&... args)
|
||||
: VTask{ std::forward<Args>(args)... }
|
||||
{}
|
||||
|
||||
virtual ~TaskFuture() = default;
|
||||
|
||||
TaskFuture(const TaskFuture&) = delete;
|
||||
TaskFuture& operator=(const TaskFuture&) = delete;
|
||||
|
||||
TaskFuture(TaskFuture&&) = default;
|
||||
TaskFuture& operator=(TaskFuture&&) = default;
|
||||
|
||||
public:
|
||||
// execution operator
|
||||
virtual future_type get_future() = 0;
|
||||
virtual void wait() = 0;
|
||||
virtual RetT get() = 0;
|
||||
};
|
||||
|
||||
//======================================================================================//
|
||||
|
||||
/// \brief The task class is supplied to thread_pool.
|
||||
template <typename RetT, typename... Args>
|
||||
class PackagedTask : public VTask
|
||||
class PackagedTask : public TaskFuture<RetT>
|
||||
{
|
||||
public:
|
||||
typedef PackagedTask<RetT, Args...> this_type;
|
||||
@@ -60,36 +91,33 @@ public:
|
||||
public:
|
||||
// pass a free function pointer
|
||||
template <typename FuncT>
|
||||
PackagedTask(FuncT&& func, Args... args)
|
||||
: VTask()
|
||||
, m_ptask(std::forward<FuncT>(func))
|
||||
, m_args(args...)
|
||||
PackagedTask(FuncT func, Args... args)
|
||||
: TaskFuture<RetT>{ true, 0 }
|
||||
, m_ptask{ std::move(func) }
|
||||
, m_args{ args... }
|
||||
{}
|
||||
|
||||
template <typename FuncT>
|
||||
PackagedTask(VTaskGroup* tg, FuncT&& func, Args... args)
|
||||
: VTask(tg)
|
||||
, m_ptask(std::forward<FuncT>(func))
|
||||
, m_args(args...)
|
||||
PackagedTask(bool _is_native, intmax_t _depth, FuncT func, Args... args)
|
||||
: TaskFuture<RetT>{ _is_native, _depth }
|
||||
, m_ptask{ std::move(func) }
|
||||
, m_args{ args... }
|
||||
{}
|
||||
|
||||
template <typename FuncT>
|
||||
PackagedTask(ThreadPool* _pool, FuncT&& func, Args... args)
|
||||
: VTask(_pool)
|
||||
, m_ptask(std::forward<FuncT>(func))
|
||||
, m_args(args...)
|
||||
{}
|
||||
virtual ~PackagedTask() = default;
|
||||
|
||||
virtual ~PackagedTask() {}
|
||||
PackagedTask(const PackagedTask&) = delete;
|
||||
PackagedTask& operator=(const PackagedTask&) = delete;
|
||||
|
||||
PackagedTask(PackagedTask&&) = default;
|
||||
PackagedTask& operator=(PackagedTask&&) = default;
|
||||
|
||||
public:
|
||||
// execution operator
|
||||
virtual void operator()() override
|
||||
{
|
||||
mpl::apply(std::move(m_ptask), std::move(m_args));
|
||||
}
|
||||
future_type get_future() { return m_ptask.get_future(); }
|
||||
virtual bool is_native_task() const override { return true; }
|
||||
virtual void operator()() final { mpl::apply(std::move(m_ptask), std::move(m_args)); }
|
||||
virtual future_type get_future() final { return m_ptask.get_future(); }
|
||||
virtual void wait() final { return m_ptask.get_future().wait(); }
|
||||
virtual RetT get() final { return m_ptask.get_future().get(); }
|
||||
|
||||
private:
|
||||
packaged_task_type m_ptask;
|
||||
@@ -100,7 +128,7 @@ private:
|
||||
|
||||
/// \brief The task class is supplied to thread_pool.
|
||||
template <typename RetT, typename... Args>
|
||||
class Task : public VTask
|
||||
class Task : public TaskFuture<RetT>
|
||||
{
|
||||
public:
|
||||
typedef Task<RetT, Args...> this_type;
|
||||
@@ -112,54 +140,48 @@ public:
|
||||
|
||||
public:
|
||||
template <typename FuncT>
|
||||
Task(FuncT&& func, Args... args)
|
||||
: VTask()
|
||||
, m_ptask(std::forward<FuncT>(func))
|
||||
, m_args(args...)
|
||||
Task(FuncT func, Args... args)
|
||||
: TaskFuture<RetT>{}
|
||||
, m_ptask{ std::move(func) }
|
||||
, m_args{ args... }
|
||||
{}
|
||||
|
||||
template <typename FuncT>
|
||||
Task(VTaskGroup* tg, FuncT&& func, Args... args)
|
||||
: VTask(tg)
|
||||
, m_ptask(std::forward<FuncT>(func))
|
||||
, m_args(args...)
|
||||
Task(bool _is_native, intmax_t _depth, FuncT func, Args... args)
|
||||
: TaskFuture<RetT>{ _is_native, _depth }
|
||||
, m_ptask{ std::move(func) }
|
||||
, m_args{ args... }
|
||||
{}
|
||||
|
||||
template <typename FuncT>
|
||||
Task(ThreadPool* tp, FuncT&& func, Args... args)
|
||||
: VTask(tp)
|
||||
, m_ptask(std::forward<FuncT>(func))
|
||||
, m_args(args...)
|
||||
{}
|
||||
virtual ~Task() = default;
|
||||
|
||||
virtual ~Task() {}
|
||||
Task(const Task&) = delete;
|
||||
Task& operator=(const Task&) = delete;
|
||||
|
||||
Task(Task&&) = default;
|
||||
Task& operator=(Task&&) = default;
|
||||
|
||||
public:
|
||||
// execution operator
|
||||
virtual void operator()() final
|
||||
{
|
||||
mpl::apply(std::move(m_ptask), std::move(m_args));
|
||||
// decrements the task-group counter on active tasks
|
||||
// when the counter is < 2, if the thread owning the task group is
|
||||
// sleeping at the TaskGroup::wait(), it signals the thread to wake
|
||||
// up and check if all tasks are finished, proceeding if this
|
||||
// check returns as true
|
||||
this_type::operator--();
|
||||
if(m_ptask.valid())
|
||||
mpl::apply(std::move(m_ptask), std::move(m_args));
|
||||
}
|
||||
|
||||
virtual bool is_native_task() const override { return true; }
|
||||
future_type get_future() { return m_ptask.get_future(); }
|
||||
virtual future_type get_future() final { return m_ptask.get_future(); }
|
||||
virtual void wait() final { return m_ptask.get_future().wait(); }
|
||||
virtual RetT get() final { return m_ptask.get_future().get(); }
|
||||
|
||||
private:
|
||||
packaged_task_type m_ptask;
|
||||
tuple_type m_args;
|
||||
packaged_task_type m_ptask{};
|
||||
tuple_type m_args{};
|
||||
};
|
||||
|
||||
//======================================================================================//
|
||||
|
||||
/// \brief The task class is supplied to thread_pool.
|
||||
template <typename RetT>
|
||||
class Task<RetT, void> : public VTask
|
||||
class Task<RetT, void> : public TaskFuture<RetT>
|
||||
{
|
||||
public:
|
||||
typedef Task<RetT> this_type;
|
||||
@@ -170,50 +192,41 @@ public:
|
||||
|
||||
public:
|
||||
template <typename FuncT>
|
||||
Task(FuncT&& func)
|
||||
: VTask()
|
||||
, m_ptask(std::forward<FuncT>(func))
|
||||
Task(FuncT func)
|
||||
: TaskFuture<RetT>()
|
||||
, m_ptask{ std::move(func) }
|
||||
{}
|
||||
|
||||
template <typename FuncT>
|
||||
Task(VTaskGroup* tg, FuncT&& func)
|
||||
: VTask(tg)
|
||||
, m_ptask(std::forward<FuncT>(func))
|
||||
Task(bool _is_native, intmax_t _depth, FuncT func)
|
||||
: TaskFuture<RetT>{ _is_native, _depth }
|
||||
, m_ptask{ std::move(func) }
|
||||
{}
|
||||
|
||||
template <typename FuncT>
|
||||
Task(ThreadPool* tp, FuncT&& func)
|
||||
: VTask(tp)
|
||||
, m_ptask(std::forward<FuncT>(func))
|
||||
{}
|
||||
virtual ~Task() = default;
|
||||
|
||||
virtual ~Task() {}
|
||||
Task(const Task&) = delete;
|
||||
Task& operator=(const Task&) = delete;
|
||||
|
||||
Task(Task&&) = default;
|
||||
Task& operator=(Task&&) = default;
|
||||
|
||||
public:
|
||||
// execution operator
|
||||
virtual void operator()() final
|
||||
{
|
||||
m_ptask();
|
||||
// decrements the task-group counter on active tasks
|
||||
// when the counter is < 2, if the thread owning the task group is
|
||||
// sleeping at the TaskGroup::wait(), it signals the thread to wake
|
||||
// up and check if all tasks are finished, proceeding if this
|
||||
// check returns as true
|
||||
this_type::operator--();
|
||||
}
|
||||
|
||||
virtual bool is_native_task() const override { return true; }
|
||||
future_type get_future() { return m_ptask.get_future(); }
|
||||
virtual void operator()() final { m_ptask(); }
|
||||
virtual future_type get_future() final { return m_ptask.get_future(); }
|
||||
virtual void wait() final { return m_ptask.get_future().wait(); }
|
||||
virtual RetT get() final { return m_ptask.get_future().get(); }
|
||||
|
||||
private:
|
||||
packaged_task_type m_ptask;
|
||||
packaged_task_type m_ptask{};
|
||||
};
|
||||
|
||||
//======================================================================================//
|
||||
|
||||
/// \brief The task class is supplied to thread_pool.
|
||||
template <>
|
||||
class Task<void, void> : public VTask
|
||||
class Task<void, void> : public TaskFuture<void>
|
||||
{
|
||||
public:
|
||||
typedef void RetT;
|
||||
@@ -225,43 +238,34 @@ public:
|
||||
|
||||
public:
|
||||
template <typename FuncT>
|
||||
explicit Task(FuncT&& func)
|
||||
: VTask()
|
||||
, m_ptask(std::forward<FuncT>(func))
|
||||
explicit Task(FuncT func)
|
||||
: TaskFuture<RetT>{}
|
||||
, m_ptask{ std::move(func) }
|
||||
{}
|
||||
|
||||
template <typename FuncT>
|
||||
Task(VTaskGroup* tg, FuncT&& func)
|
||||
: VTask(tg)
|
||||
, m_ptask(std::forward<FuncT>(func))
|
||||
Task(bool _is_native, intmax_t _depth, FuncT func)
|
||||
: TaskFuture<RetT>{ _is_native, _depth }
|
||||
, m_ptask{ std::move(func) }
|
||||
{}
|
||||
|
||||
template <typename FuncT>
|
||||
Task(ThreadPool* tp, FuncT&& func)
|
||||
: VTask(tp)
|
||||
, m_ptask(std::forward<FuncT>(func))
|
||||
{}
|
||||
virtual ~Task() = default;
|
||||
|
||||
virtual ~Task() {}
|
||||
Task(const Task&) = delete;
|
||||
Task& operator=(const Task&) = delete;
|
||||
|
||||
Task(Task&&) = default;
|
||||
Task& operator=(Task&&) = default;
|
||||
|
||||
public:
|
||||
// execution operator
|
||||
virtual void operator()() final
|
||||
{
|
||||
m_ptask();
|
||||
// decrements the task-group counter on active tasks
|
||||
// when the counter is < 2, if the thread owning the task group is
|
||||
// sleeping at the TaskGroup::wait(), it signals the thread to wake
|
||||
// up and check if all tasks are finished, proceeding if this
|
||||
// check returns as true
|
||||
this_type::operator--();
|
||||
}
|
||||
|
||||
virtual bool is_native_task() const override { return true; }
|
||||
future_type get_future() { return m_ptask.get_future(); }
|
||||
virtual void operator()() final { m_ptask(); }
|
||||
virtual future_type get_future() final { return m_ptask.get_future(); }
|
||||
virtual void wait() final { return m_ptask.get_future().wait(); }
|
||||
virtual RetT get() final { return m_ptask.get_future().get(); }
|
||||
|
||||
private:
|
||||
packaged_task_type m_ptask;
|
||||
packaged_task_type m_ptask{};
|
||||
};
|
||||
|
||||
//======================================================================================//
|
||||
|
||||
-339
@@ -1,339 +0,0 @@
|
||||
//
|
||||
// MIT License
|
||||
// Copyright (c) 2020 Jonathan R. Madsen
|
||||
// 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
|
||||
// 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.
|
||||
//
|
||||
//
|
||||
// ------------------------------------------------------------
|
||||
// Tasking class header file
|
||||
//
|
||||
// Class Description:
|
||||
//
|
||||
// A class for fast allocation of objects to the heap through a pool of
|
||||
// chunks organised as linked list. It's meant to be used by associating
|
||||
// it to the object to be allocated and defining for it new and delete
|
||||
// operators via MallocSingle() and FreeSingle() methods.
|
||||
// ---------------- TaskAllocator ----------------
|
||||
//
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <typeinfo>
|
||||
|
||||
#include "PTL/TaskAllocatorPool.hh"
|
||||
#include "PTL/Threading.hh"
|
||||
|
||||
namespace PTL
|
||||
{
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
class TaskAllocatorBase
|
||||
{
|
||||
public:
|
||||
TaskAllocatorBase();
|
||||
virtual ~TaskAllocatorBase();
|
||||
virtual void ResetStorage() = 0;
|
||||
virtual size_t GetAllocatedSize() const = 0;
|
||||
virtual int GetNoPages() const = 0;
|
||||
virtual size_t GetPageSize() const = 0;
|
||||
virtual void IncreasePageSize(unsigned int sz) = 0;
|
||||
virtual const char* GetPoolType() const = 0;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
template <class Type>
|
||||
class TaskAllocatorImpl : public TaskAllocatorBase
|
||||
{
|
||||
public: // with description
|
||||
TaskAllocatorImpl();
|
||||
~TaskAllocatorImpl();
|
||||
// Constructor & destructor
|
||||
|
||||
inline Type* MallocSingle();
|
||||
inline void FreeSingle(Type* anElement);
|
||||
// Malloc and Free methods to be used when overloading
|
||||
// new and delete operators in the client <Type> object
|
||||
|
||||
inline void ResetStorage() override;
|
||||
// Returns allocated storage to the free store, resets allocator.
|
||||
// Note: contents in memory are lost using this call !
|
||||
|
||||
inline size_t GetAllocatedSize() const override;
|
||||
// Returns the size of the total memory allocated
|
||||
inline int GetNoPages() const override;
|
||||
// Returns the total number of allocated pages
|
||||
inline size_t GetPageSize() const override;
|
||||
// Returns the current size of a page
|
||||
inline void IncreasePageSize(unsigned int sz) override;
|
||||
// Resets allocator and increases default page size of a given factor
|
||||
inline const char* GetPoolType() const override;
|
||||
// Returns the type_info Id of the allocated type in the pool
|
||||
|
||||
public: // without description
|
||||
// This public section includes standard methods and types
|
||||
// required if the allocator is to be used as alternative
|
||||
// allocator for STL containers.
|
||||
// NOTE: the code below is a trivial implementation to make
|
||||
// this class an STL compliant allocator.
|
||||
// It is anyhow NOT recommended to use this class as
|
||||
// alternative allocator for STL containers !
|
||||
|
||||
typedef Type value_type;
|
||||
typedef size_t size_type;
|
||||
typedef ptrdiff_t difference_type;
|
||||
typedef Type* pointer;
|
||||
typedef const Type* const_pointer;
|
||||
typedef Type& reference;
|
||||
typedef const Type& const_reference;
|
||||
|
||||
template <class U>
|
||||
TaskAllocatorImpl(const TaskAllocatorImpl<U>& right) throw()
|
||||
: mem(right.mem)
|
||||
, tname(right.name())
|
||||
{}
|
||||
// Copy constructor
|
||||
|
||||
pointer address(reference r) const { return &r; }
|
||||
const_pointer address(const_reference r) const { return &r; }
|
||||
// Returns the address of values
|
||||
|
||||
pointer allocate(size_type n, void* = 0)
|
||||
{
|
||||
// Allocates space for n elements of type Type, but does not initialise
|
||||
//
|
||||
Type* mem_alloc = 0;
|
||||
if(n == 1)
|
||||
mem_alloc = MallocSingle();
|
||||
else
|
||||
mem_alloc = static_cast<Type*>(::operator new(n * sizeof(Type)));
|
||||
return mem_alloc;
|
||||
}
|
||||
void deallocate(pointer p, size_type n)
|
||||
{
|
||||
// Deallocates n elements of type Type, but doesn't destroy
|
||||
//
|
||||
if(n == 1)
|
||||
FreeSingle(p);
|
||||
else
|
||||
::operator delete((void*) p);
|
||||
return;
|
||||
}
|
||||
|
||||
void construct(pointer p, const Type& val) { new((void*) p) Type(val); }
|
||||
// Initialises *p by val
|
||||
void destroy(pointer p) { p->~Type(); }
|
||||
// Destroy *p but doesn't deallocate
|
||||
|
||||
size_type max_size() const throw()
|
||||
{
|
||||
// Returns the maximum number of elements that can be allocated
|
||||
//
|
||||
return 2147483647 / sizeof(Type);
|
||||
}
|
||||
|
||||
template <class U>
|
||||
struct rebind
|
||||
{
|
||||
typedef TaskAllocatorImpl<U> other;
|
||||
};
|
||||
// Rebind allocator to type U
|
||||
|
||||
TaskAllocatorPool mem;
|
||||
// Pool of elements of sizeof(Type)
|
||||
|
||||
private:
|
||||
const char* tname;
|
||||
// Type name identifier
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
//
|
||||
// Inherit from this class, e.g. MyClass : public TaskAllocator<MyClass>
|
||||
//
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
template <typename Type>
|
||||
class TaskAllocator : public TaskAllocatorImpl<Type>
|
||||
{
|
||||
public:
|
||||
typedef Type value_type;
|
||||
typedef size_t size_type;
|
||||
typedef ptrdiff_t difference_type;
|
||||
typedef Type* pointer;
|
||||
typedef const Type* const_pointer;
|
||||
typedef Type& reference;
|
||||
typedef const Type& const_reference;
|
||||
typedef TaskAllocatorImpl<Type> allocator_type;
|
||||
|
||||
public:
|
||||
// define the new operator
|
||||
void* operator new(size_type)
|
||||
{
|
||||
return static_cast<void*>(get_allocator()->MallocSingle());
|
||||
}
|
||||
// define the delete operator
|
||||
void operator delete(void* ptr)
|
||||
{
|
||||
get_allocator()->FreeSingle(static_cast<pointer>(ptr));
|
||||
}
|
||||
|
||||
private:
|
||||
// currently disabled due to memory leak found via -fsanitize=leak
|
||||
// static function to get allocator
|
||||
static allocator_type* get_allocator()
|
||||
{
|
||||
typedef std::unique_ptr<allocator_type> allocator_ptr;
|
||||
static thread_local allocator_ptr _allocator = allocator_ptr(new allocator_type);
|
||||
return _allocator.get();
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
// Inline implementation
|
||||
|
||||
template <class Type>
|
||||
TaskAllocatorImpl<Type>::TaskAllocatorImpl()
|
||||
: mem(sizeof(Type))
|
||||
, tname(typeid(Type).name())
|
||||
{}
|
||||
|
||||
// ************************************************************
|
||||
// TaskAllocatorImpl destructor
|
||||
// ************************************************************
|
||||
//
|
||||
template <class Type>
|
||||
TaskAllocatorImpl<Type>::~TaskAllocatorImpl()
|
||||
{}
|
||||
|
||||
// ************************************************************
|
||||
// MallocSingle
|
||||
// ************************************************************
|
||||
//
|
||||
template <class Type>
|
||||
Type*
|
||||
TaskAllocatorImpl<Type>::MallocSingle()
|
||||
{
|
||||
return static_cast<Type*>(mem.Alloc());
|
||||
}
|
||||
|
||||
// ************************************************************
|
||||
// FreeSingle
|
||||
// ************************************************************
|
||||
//
|
||||
template <class Type>
|
||||
void
|
||||
TaskAllocatorImpl<Type>::FreeSingle(Type* anElement)
|
||||
{
|
||||
mem.Free(anElement);
|
||||
return;
|
||||
}
|
||||
|
||||
// ************************************************************
|
||||
// ResetStorage
|
||||
// ************************************************************
|
||||
//
|
||||
template <class Type>
|
||||
void
|
||||
TaskAllocatorImpl<Type>::ResetStorage()
|
||||
{
|
||||
// Clear all allocated storage and return it to the free store
|
||||
//
|
||||
mem.Reset();
|
||||
return;
|
||||
}
|
||||
|
||||
// ************************************************************
|
||||
// GetAllocatedSize
|
||||
// ************************************************************
|
||||
//
|
||||
template <class Type>
|
||||
size_t
|
||||
TaskAllocatorImpl<Type>::GetAllocatedSize() const
|
||||
{
|
||||
return mem.Size();
|
||||
}
|
||||
|
||||
// ************************************************************
|
||||
// GetNoPages
|
||||
// ************************************************************
|
||||
//
|
||||
template <class Type>
|
||||
int
|
||||
TaskAllocatorImpl<Type>::GetNoPages() const
|
||||
{
|
||||
return mem.GetNoPages();
|
||||
}
|
||||
|
||||
// ************************************************************
|
||||
// GetPageSize
|
||||
// ************************************************************
|
||||
//
|
||||
template <class Type>
|
||||
size_t
|
||||
TaskAllocatorImpl<Type>::GetPageSize() const
|
||||
{
|
||||
return mem.GetPageSize();
|
||||
}
|
||||
|
||||
// ************************************************************
|
||||
// IncreasePageSize
|
||||
// ************************************************************
|
||||
//
|
||||
template <class Type>
|
||||
void
|
||||
TaskAllocatorImpl<Type>::IncreasePageSize(unsigned int sz)
|
||||
{
|
||||
ResetStorage();
|
||||
mem.GrowPageSize(sz);
|
||||
}
|
||||
|
||||
// ************************************************************
|
||||
// GetPoolType
|
||||
// ************************************************************
|
||||
//
|
||||
template <class Type>
|
||||
const char*
|
||||
TaskAllocatorImpl<Type>::GetPoolType() const
|
||||
{
|
||||
return tname;
|
||||
}
|
||||
|
||||
// ************************************************************
|
||||
// operator==
|
||||
// ************************************************************
|
||||
//
|
||||
template <class T1, class T2>
|
||||
bool
|
||||
operator==(const TaskAllocatorImpl<T1>&, const TaskAllocatorImpl<T2>&) throw()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// ************************************************************
|
||||
// operator!=
|
||||
// ************************************************************
|
||||
//
|
||||
template <class T1, class T2>
|
||||
bool
|
||||
operator!=(const TaskAllocatorImpl<T1>&, const TaskAllocatorImpl<T2>&) throw()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace PTL
|
||||
@@ -1,58 +0,0 @@
|
||||
//
|
||||
// MIT License
|
||||
// Copyright (c) 2020 Jonathan R. Madsen
|
||||
// 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
|
||||
// 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.
|
||||
//
|
||||
// ------------------------------------------------------------
|
||||
// Tasking class header file
|
||||
//
|
||||
// Class Description:
|
||||
//
|
||||
// A class to store all TaskAllocator objects in a thread for the sake
|
||||
// of cleanly deleting them.
|
||||
//
|
||||
// ------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "PTL/Globals.hh"
|
||||
#include <vector>
|
||||
|
||||
namespace PTL
|
||||
{
|
||||
class TaskAllocatorBase;
|
||||
|
||||
class TaskAllocatorList
|
||||
{
|
||||
public: // with description
|
||||
static TaskAllocatorList* GetAllocatorList();
|
||||
static TaskAllocatorList* GetAllocatorListIfExist();
|
||||
|
||||
public:
|
||||
~TaskAllocatorList();
|
||||
void Register(TaskAllocatorBase*);
|
||||
void Destroy(int nStat = 0, int verboseLevel = 0);
|
||||
int Size() const;
|
||||
|
||||
private:
|
||||
TaskAllocatorList();
|
||||
|
||||
private:
|
||||
static TaskAllocatorList*& fAllocatorList();
|
||||
std::vector<TaskAllocatorBase*> fList;
|
||||
};
|
||||
|
||||
} // namespace PTL
|
||||
@@ -1,173 +0,0 @@
|
||||
//
|
||||
// MIT License
|
||||
// Copyright (c) 2020 Jonathan R. Madsen
|
||||
// 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
|
||||
// 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.
|
||||
//
|
||||
//
|
||||
// -------------------------------------------------------------------
|
||||
// Tasking class header file
|
||||
//
|
||||
// Class description:
|
||||
//
|
||||
// Class implementing a memory pool for fast allocation and deallocation
|
||||
// of memory chunks. The size of the chunks for small allocated objects
|
||||
// is fixed to 1Kb and takes into account of memory alignment; for large
|
||||
// objects it is set to 10 times the object's size.
|
||||
// The implementation is derived from: B.Stroustrup, The C++ Programming
|
||||
// Language, Third Edition.
|
||||
|
||||
// -------------- TaskAllocatorPool ----------------
|
||||
//
|
||||
// Author: G.Cosmo (CERN), November 2000
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace PTL
|
||||
{
|
||||
class TaskAllocatorPool
|
||||
{
|
||||
public:
|
||||
explicit TaskAllocatorPool(unsigned int n = 0);
|
||||
// Create a pool of elements of size n
|
||||
~TaskAllocatorPool();
|
||||
// Destructor. Return storage to the free store
|
||||
|
||||
inline void* Alloc();
|
||||
// Allocate one element
|
||||
inline void Free(void* b);
|
||||
// Return an element back to the pool
|
||||
|
||||
inline unsigned int Size() const;
|
||||
// Return storage size
|
||||
void Reset();
|
||||
// Return storage to the free store
|
||||
|
||||
inline int GetNoPages() const;
|
||||
// Return the total number of allocated pages
|
||||
inline unsigned int GetPageSize() const;
|
||||
// Accessor for default page size
|
||||
inline void GrowPageSize(unsigned int factor);
|
||||
// Increase default page size by a given factor
|
||||
|
||||
private:
|
||||
TaskAllocatorPool(const TaskAllocatorPool& right);
|
||||
// Provate copy constructor
|
||||
TaskAllocatorPool& operator=(const TaskAllocatorPool& right);
|
||||
// Private equality operator
|
||||
|
||||
struct PoolLink
|
||||
{
|
||||
PoolLink* next;
|
||||
};
|
||||
|
||||
class PoolChunk
|
||||
{
|
||||
public:
|
||||
explicit PoolChunk(unsigned int sz)
|
||||
: size(sz)
|
||||
, mem(new char[size])
|
||||
, next(0)
|
||||
{
|
||||
;
|
||||
}
|
||||
~PoolChunk() { delete[] mem; }
|
||||
const unsigned int size;
|
||||
char* mem;
|
||||
PoolChunk* next;
|
||||
};
|
||||
|
||||
void Grow();
|
||||
// Make pool larger
|
||||
|
||||
private:
|
||||
const unsigned int esize;
|
||||
unsigned int csize;
|
||||
PoolChunk* chunks;
|
||||
PoolLink* head;
|
||||
int nchunks;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
// Inline implementation
|
||||
|
||||
// ************************************************************
|
||||
// Alloc
|
||||
// ************************************************************
|
||||
//
|
||||
inline void*
|
||||
TaskAllocatorPool::Alloc()
|
||||
{
|
||||
if(head == nullptr)
|
||||
Grow();
|
||||
PoolLink* p = head; // return first element
|
||||
head = p->next;
|
||||
return p;
|
||||
}
|
||||
|
||||
// ************************************************************
|
||||
// Free
|
||||
// ************************************************************
|
||||
//
|
||||
inline void
|
||||
TaskAllocatorPool::Free(void* b)
|
||||
{
|
||||
PoolLink* p = static_cast<PoolLink*>(b);
|
||||
p->next = head; // put b back as first element
|
||||
head = p;
|
||||
}
|
||||
|
||||
// ************************************************************
|
||||
// Size
|
||||
// ************************************************************
|
||||
//
|
||||
inline unsigned int
|
||||
TaskAllocatorPool::Size() const
|
||||
{
|
||||
return nchunks * csize;
|
||||
}
|
||||
|
||||
// ************************************************************
|
||||
// GetNoPages
|
||||
// ************************************************************
|
||||
//
|
||||
inline int
|
||||
TaskAllocatorPool::GetNoPages() const
|
||||
{
|
||||
return nchunks;
|
||||
}
|
||||
|
||||
// ************************************************************
|
||||
// GetPageSize
|
||||
// ************************************************************
|
||||
//
|
||||
inline unsigned int
|
||||
TaskAllocatorPool::GetPageSize() const
|
||||
{
|
||||
return csize;
|
||||
}
|
||||
|
||||
// ************************************************************
|
||||
// GrowPageSize
|
||||
// ************************************************************
|
||||
//
|
||||
inline void
|
||||
TaskAllocatorPool::GrowPageSize(unsigned int sz)
|
||||
{
|
||||
csize = (sz) ? sz * csize : csize;
|
||||
}
|
||||
|
||||
} // namespace PTL
|
||||
+152
-209
@@ -31,131 +31,65 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "PTL/JoinFunction.hh"
|
||||
#include "PTL/Task.hh"
|
||||
#include "PTL/ThreadData.hh"
|
||||
#include "PTL/ThreadPool.hh"
|
||||
#include "PTL/VTaskGroup.hh"
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <deque>
|
||||
#include <future>
|
||||
#include <list>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#ifdef PTL_USE_TBB
|
||||
#if defined(PTL_USE_TBB)
|
||||
# include <tbb/tbb.h>
|
||||
#endif
|
||||
|
||||
namespace PTL
|
||||
{
|
||||
class ThreadPool;
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
#if !defined(PTL_DEFAULT_OBJECT)
|
||||
# define PTL_DEFAULT_OBJECT(NAME) \
|
||||
NAME() = default; \
|
||||
~NAME() = default; \
|
||||
NAME(const NAME&) = default; \
|
||||
NAME(NAME&&) noexcept = default; \
|
||||
NAME& operator=(const NAME&) = default; \
|
||||
NAME& operator=(NAME&&) noexcept = default;
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
template <typename JoinT, typename JoinArg>
|
||||
struct JoinFunction
|
||||
namespace internal
|
||||
{
|
||||
public:
|
||||
using Type = std::function<JoinT(JoinT&, JoinArg&&)>;
|
||||
std::atomic_uintmax_t&
|
||||
task_group_counter();
|
||||
|
||||
public:
|
||||
PTL_DEFAULT_OBJECT(JoinFunction)
|
||||
ThreadPool*
|
||||
get_default_threadpool();
|
||||
|
||||
template <typename Func>
|
||||
JoinFunction(Func&& func)
|
||||
: m_func(std::forward<Func>(func))
|
||||
{}
|
||||
|
||||
template <typename... Args>
|
||||
JoinT& operator()(Args&&... args)
|
||||
{
|
||||
return std::move(m_func(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
private:
|
||||
Type m_func = [](JoinT& lhs, JoinArg&&) { return lhs; };
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
template <typename JoinArg>
|
||||
struct JoinFunction<void, JoinArg>
|
||||
{
|
||||
public:
|
||||
using Type = std::function<void(JoinArg)>;
|
||||
|
||||
public:
|
||||
PTL_DEFAULT_OBJECT(JoinFunction)
|
||||
|
||||
template <typename Func>
|
||||
JoinFunction(Func&& func)
|
||||
: m_func(std::forward<Func>(func))
|
||||
{}
|
||||
|
||||
template <typename... Args>
|
||||
void operator()(Args&&... args)
|
||||
{
|
||||
m_func(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
private:
|
||||
Type m_func = [](JoinArg) {};
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
template <>
|
||||
struct JoinFunction<void, void>
|
||||
{
|
||||
public:
|
||||
using Type = std::function<void()>;
|
||||
|
||||
public:
|
||||
PTL_DEFAULT_OBJECT(JoinFunction)
|
||||
|
||||
template <typename Func>
|
||||
JoinFunction(Func&& func)
|
||||
: m_func(std::forward<Func>(func))
|
||||
{}
|
||||
|
||||
void operator()() { m_func(); }
|
||||
|
||||
private:
|
||||
Type m_func = []() {};
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
template <typename Tp, typename Arg = Tp>
|
||||
intmax_t
|
||||
get_task_depth();
|
||||
} // namespace internal
|
||||
template <typename Tp, typename Arg = Tp, intmax_t MaxDepth = 0>
|
||||
class TaskGroup
|
||||
: public VTaskGroup
|
||||
, public TaskAllocator<TaskGroup<Tp, Arg>>
|
||||
{
|
||||
public:
|
||||
//------------------------------------------------------------------------//
|
||||
typedef decay_t<Arg> ArgTp;
|
||||
typedef Tp result_type;
|
||||
typedef TaskGroup<Tp, Arg> this_type;
|
||||
typedef std::promise<ArgTp> promise_type;
|
||||
typedef std::future<ArgTp> future_type;
|
||||
typedef std::packaged_task<ArgTp()> packaged_task_type;
|
||||
typedef list_type<future_type> task_list_t;
|
||||
typedef typename JoinFunction<Tp, Arg>::Type join_type;
|
||||
typedef typename task_list_t::iterator iterator;
|
||||
typedef typename task_list_t::reverse_iterator reverse_iterator;
|
||||
typedef typename task_list_t::const_iterator const_iterator;
|
||||
typedef typename task_list_t::const_reverse_iterator const_reverse_iterator;
|
||||
template <typename Up>
|
||||
using container_type = std::vector<Up>;
|
||||
|
||||
using tid_type = std::thread::id;
|
||||
using size_type = uintmax_t;
|
||||
using lock_t = Mutex;
|
||||
using atomic_int = std::atomic_intmax_t;
|
||||
using atomic_uint = std::atomic_uintmax_t;
|
||||
using condition_t = Condition;
|
||||
using ArgTp = decay_t<Arg>;
|
||||
using result_type = Tp;
|
||||
using task_pointer = std::shared_ptr<TaskFuture<ArgTp>>;
|
||||
using task_list_t = container_type<task_pointer>;
|
||||
using this_type = TaskGroup<Tp, Arg, MaxDepth>;
|
||||
using promise_type = std::promise<ArgTp>;
|
||||
using future_type = std::future<ArgTp>;
|
||||
using packaged_task_type = std::packaged_task<ArgTp()>;
|
||||
using future_list_t = container_type<future_type>;
|
||||
using join_type = typename JoinFunction<Tp, Arg>::Type;
|
||||
using iterator = typename future_list_t::iterator;
|
||||
using reverse_iterator = typename future_list_t::reverse_iterator;
|
||||
using const_iterator = typename future_list_t::const_iterator;
|
||||
using const_reverse_iterator = typename future_list_t::const_reverse_iterator;
|
||||
//------------------------------------------------------------------------//
|
||||
template <typename... Args>
|
||||
using task_type = Task<ArgTp, decay_t<Args>...>;
|
||||
@@ -164,17 +98,14 @@ public:
|
||||
public:
|
||||
// Constructor
|
||||
template <typename Func>
|
||||
TaskGroup(Func&& _join, ThreadPool* _tp = nullptr)
|
||||
: VTaskGroup(_tp)
|
||||
, m_join(std::forward<Func>(_join))
|
||||
{}
|
||||
template <typename Up = Tp, enable_if_t<std::is_same<Up, void>::value, int> = 0>
|
||||
explicit TaskGroup(ThreadPool* _tp = nullptr)
|
||||
: VTaskGroup(_tp)
|
||||
, m_join([]() {})
|
||||
{}
|
||||
TaskGroup(Func&& _join, ThreadPool* _tp = internal::get_default_threadpool());
|
||||
|
||||
template <typename Up = Tp>
|
||||
TaskGroup(ThreadPool* _tp = internal::get_default_threadpool(),
|
||||
enable_if_t<std::is_void<Up>::value, int> = 0);
|
||||
|
||||
// Destructor
|
||||
virtual ~TaskGroup() { this->clear(); }
|
||||
~TaskGroup();
|
||||
|
||||
// delete copy-construct
|
||||
TaskGroup(const this_type&) = delete;
|
||||
@@ -186,140 +117,152 @@ public:
|
||||
this_type& operator=(this_type&& rhs) = default;
|
||||
|
||||
public:
|
||||
//------------------------------------------------------------------------//
|
||||
template <typename Up>
|
||||
Up* operator+=(Up* _task)
|
||||
std::shared_ptr<Up> operator+=(std::shared_ptr<Up>&& _task);
|
||||
|
||||
// wait to finish
|
||||
void wait();
|
||||
|
||||
// increment (prefix)
|
||||
intmax_t operator++() { return ++(m_tot_task_count); }
|
||||
intmax_t operator++(int) { return (m_tot_task_count)++; }
|
||||
intmax_t operator--() { return --(m_tot_task_count); }
|
||||
intmax_t operator--(int) { return (m_tot_task_count)--; }
|
||||
|
||||
// size
|
||||
intmax_t size() const { return m_tot_task_count.load(); }
|
||||
|
||||
// get the locks/conditions
|
||||
lock_t& task_lock() { return m_task_lock; }
|
||||
condition_t& task_cond() { return m_task_cond; }
|
||||
|
||||
// identifier
|
||||
uintmax_t id() const { return m_id; }
|
||||
|
||||
// thread pool
|
||||
void set_pool(ThreadPool* tp) { m_pool = tp; }
|
||||
ThreadPool*& pool() { return m_pool; }
|
||||
ThreadPool* pool() const { return m_pool; }
|
||||
|
||||
bool is_native_task_group() const { return (m_tbb_task_group) ? false : true; }
|
||||
bool is_main() const { return this_tid() == m_main_tid; }
|
||||
|
||||
// check if any tasks are still pending
|
||||
intmax_t pending() { return m_tot_task_count.load(); }
|
||||
|
||||
static void set_verbose(int level) { f_verbose = level; }
|
||||
|
||||
ScopeDestructor get_scope_destructor();
|
||||
|
||||
void notify();
|
||||
void notify_all();
|
||||
void reserve(size_t _n)
|
||||
{
|
||||
// store in list
|
||||
vtask_list.push_back(_task);
|
||||
// thread-safe increment of tasks in task group
|
||||
operator++();
|
||||
// add the future
|
||||
m_task_set.push_back(std::move(_task->get_future()));
|
||||
// return
|
||||
return _task;
|
||||
m_task_list.reserve(_n);
|
||||
m_future_list.reserve(_n);
|
||||
}
|
||||
|
||||
public:
|
||||
//------------------------------------------------------------------------//
|
||||
template <typename Func, typename... Args>
|
||||
task_type<Args...>* wrap(Func&& func, Args... args)
|
||||
std::shared_ptr<task_type<Args...>> wrap(Func func, Args... args)
|
||||
{
|
||||
return operator+=(
|
||||
new task_type<Args...>(this, std::forward<Func>(func), args...));
|
||||
return operator+=(std::make_shared<task_type<Args...>>(
|
||||
is_native_task_group(), m_depth, std::move(func), std::move(args)...));
|
||||
}
|
||||
|
||||
public:
|
||||
//------------------------------------------------------------------------//
|
||||
template <typename Func, typename... Args, typename Up = Tp>
|
||||
enable_if_t<std::is_void<Up>::value, void> exec(Func func, Args... args);
|
||||
|
||||
template <typename Func, typename... Args, typename Up = Tp>
|
||||
enable_if_t<!std::is_void<Up>::value, void> exec(Func func, Args... args);
|
||||
|
||||
template <typename Func, typename... Args>
|
||||
void exec(Func&& func, Args... args)
|
||||
void run(Func func, Args... args)
|
||||
{
|
||||
m_pool->add_task(wrap(std::forward<Func>(func), args...));
|
||||
}
|
||||
//------------------------------------------------------------------------//
|
||||
template <typename Func, typename... Args>
|
||||
void run(Func&& func, Args... args)
|
||||
{
|
||||
m_pool->add_task(wrap(std::forward<Func>(func), args...));
|
||||
}
|
||||
//------------------------------------------------------------------------//
|
||||
template <typename Func, typename... Args>
|
||||
void parallel_for(const intmax_t& nitr, const intmax_t& chunks, Func&& func,
|
||||
Args... args)
|
||||
{
|
||||
auto nsplit = nitr / chunks;
|
||||
auto nmod = nitr % chunks;
|
||||
if(nsplit < 1)
|
||||
nsplit = 1;
|
||||
for(intmax_t n = 0; n < nsplit; ++n)
|
||||
{
|
||||
auto _beg = n * chunks;
|
||||
auto _end = (n + 1) * chunks + ((n + 1 == nsplit) ? nmod : 0);
|
||||
run(std::forward<Func>(func), std::move(_beg), std::move(_end), args...);
|
||||
}
|
||||
exec(std::move(func), std::move(args)...);
|
||||
}
|
||||
|
||||
protected:
|
||||
//------------------------------------------------------------------------//
|
||||
template <typename Up, typename Func, typename... Args>
|
||||
enable_if_t<std::is_void<Up>::value, void> local_exec(Func func, Args... args);
|
||||
|
||||
template <typename Up, typename Func, typename... Args>
|
||||
enable_if_t<!std::is_void<Up>::value, void> local_exec(Func func, Args... args);
|
||||
|
||||
// shorter typedefs
|
||||
typedef iterator itr_t;
|
||||
typedef const_iterator citr_t;
|
||||
typedef reverse_iterator ritr_t;
|
||||
typedef const_reverse_iterator critr_t;
|
||||
using itr_t = iterator;
|
||||
using citr_t = const_iterator;
|
||||
using ritr_t = reverse_iterator;
|
||||
using critr_t = const_reverse_iterator;
|
||||
|
||||
public:
|
||||
//------------------------------------------------------------------------//
|
||||
// Get tasks with non-void return types
|
||||
//
|
||||
task_list_t& get_tasks() { return m_task_set; }
|
||||
const task_list_t& get_tasks() const { return m_task_set; }
|
||||
future_list_t& get_tasks() { return m_future_list; }
|
||||
const future_list_t& get_tasks() const { return m_future_list; }
|
||||
|
||||
//------------------------------------------------------------------------//
|
||||
// iterate over tasks with return type
|
||||
//
|
||||
itr_t begin() { return m_task_set.begin(); }
|
||||
itr_t end() { return m_task_set.end(); }
|
||||
citr_t begin() const { return m_task_set.begin(); }
|
||||
citr_t end() const { return m_task_set.end(); }
|
||||
citr_t cbegin() const { return m_task_set.begin(); }
|
||||
citr_t cend() const { return m_task_set.end(); }
|
||||
ritr_t rbegin() { return m_task_set.rbegin(); }
|
||||
ritr_t rend() { return m_task_set.rend(); }
|
||||
critr_t rbegin() const { return m_task_set.rbegin(); }
|
||||
critr_t rend() const { return m_task_set.rend(); }
|
||||
itr_t begin() { return m_future_list.begin(); }
|
||||
itr_t end() { return m_future_list.end(); }
|
||||
citr_t begin() const { return m_future_list.begin(); }
|
||||
citr_t end() const { return m_future_list.end(); }
|
||||
citr_t cbegin() const { return m_future_list.begin(); }
|
||||
citr_t cend() const { return m_future_list.end(); }
|
||||
ritr_t rbegin() { return m_future_list.rbegin(); }
|
||||
ritr_t rend() { return m_future_list.rend(); }
|
||||
critr_t rbegin() const { return m_future_list.rbegin(); }
|
||||
critr_t rend() const { return m_future_list.rend(); }
|
||||
|
||||
//------------------------------------------------------------------------//
|
||||
// wait to finish
|
||||
template <typename Up = Tp, enable_if_t<!std::is_void<Up>::value, int> = 0>
|
||||
inline Up join(Up accum = {})
|
||||
{
|
||||
this->wait();
|
||||
for(auto& itr : m_task_set)
|
||||
{
|
||||
using RetT = decay_t<decltype(itr.get())>;
|
||||
accum = std::move(m_join(std::ref(accum), std::forward<RetT>(itr.get())));
|
||||
}
|
||||
this->clear();
|
||||
return accum;
|
||||
}
|
||||
inline Up join(Up accum = {});
|
||||
//------------------------------------------------------------------------//
|
||||
// wait to finish
|
||||
template <typename Up = Tp, typename Rp = Arg,
|
||||
enable_if_t<std::is_void<Up>::value && std::is_void<Rp>::value, int> = 0>
|
||||
inline void join()
|
||||
{
|
||||
this->wait();
|
||||
for(auto& itr : m_task_set)
|
||||
itr.get();
|
||||
m_join();
|
||||
this->clear();
|
||||
}
|
||||
inline void join();
|
||||
//------------------------------------------------------------------------//
|
||||
// wait to finish
|
||||
template <typename Up = Tp, typename Rp = Arg,
|
||||
enable_if_t<std::is_void<Up>::value && !std::is_void<Rp>::value, int> = 0>
|
||||
inline void join()
|
||||
{
|
||||
this->wait();
|
||||
for(auto& itr : m_task_set)
|
||||
{
|
||||
using RetT = decay_t<decltype(itr.get())>;
|
||||
m_join(std::forward<RetT>(itr.get()));
|
||||
}
|
||||
this->clear();
|
||||
}
|
||||
inline void join();
|
||||
//------------------------------------------------------------------------//
|
||||
// clear the task result history
|
||||
void clear()
|
||||
{
|
||||
m_task_set.clear();
|
||||
VTaskGroup::clear();
|
||||
}
|
||||
void clear();
|
||||
|
||||
protected:
|
||||
// Protected variables
|
||||
task_list_t m_task_set;
|
||||
join_type m_join;
|
||||
//------------------------------------------------------------------------//
|
||||
// get the thread id
|
||||
static tid_type this_tid() { return std::this_thread::get_id(); }
|
||||
|
||||
//------------------------------------------------------------------------//
|
||||
// get the task count
|
||||
atomic_int& task_count() { return m_tot_task_count; }
|
||||
const atomic_int& task_count() const { return m_tot_task_count; }
|
||||
|
||||
protected:
|
||||
static int f_verbose;
|
||||
// Private variables
|
||||
uintmax_t m_id = internal::task_group_counter()++;
|
||||
intmax_t m_depth = internal::get_task_depth();
|
||||
tid_type m_main_tid = std::this_thread::get_id();
|
||||
atomic_int m_tot_task_count{ 0 };
|
||||
lock_t m_task_lock = {};
|
||||
condition_t m_task_cond = {};
|
||||
join_type m_join{};
|
||||
ThreadPool* m_pool = internal::get_default_threadpool();
|
||||
tbb_task_group_t* m_tbb_task_group = nullptr;
|
||||
task_list_t m_task_list = {};
|
||||
future_list_t m_future_list = {};
|
||||
|
||||
private:
|
||||
void internal_update();
|
||||
};
|
||||
|
||||
} // namespace PTL
|
||||
|
||||
#include "PTL/TaskGroup.icc"
|
||||
|
||||
+485
@@ -0,0 +1,485 @@
|
||||
//
|
||||
// MIT License
|
||||
// Copyright (c) 2020 Jonathan R. Madsen
|
||||
// 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
|
||||
// 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.
|
||||
//
|
||||
//
|
||||
// ---------------------------------------------------------------
|
||||
// Tasking class header file
|
||||
//
|
||||
// Class Description:
|
||||
//
|
||||
// This file creates the a class for handling a group of tasks that
|
||||
// can be independently joined
|
||||
//
|
||||
// ---------------------------------------------------------------
|
||||
// Author: Jonathan Madsen (Feb 13th 2018)
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
#include "PTL/Types.hh"
|
||||
|
||||
namespace PTL
|
||||
{
|
||||
template <typename Tp, typename Arg, intmax_t MaxDepth>
|
||||
template <typename Func>
|
||||
TaskGroup<Tp, Arg, MaxDepth>::TaskGroup(Func&& _join, ThreadPool* _tp)
|
||||
: m_join{ std::forward<Func>(_join) }
|
||||
, m_pool{ _tp }
|
||||
{
|
||||
internal_update();
|
||||
}
|
||||
|
||||
template <typename Tp, typename Arg, intmax_t MaxDepth>
|
||||
template <typename Up>
|
||||
TaskGroup<Tp, Arg, MaxDepth>::TaskGroup(ThreadPool* _tp,
|
||||
enable_if_t<std::is_void<Up>::value, int>)
|
||||
: m_join{ []() {} }
|
||||
, m_pool{ _tp }
|
||||
{
|
||||
internal_update();
|
||||
}
|
||||
|
||||
// Destructor
|
||||
template <typename Tp, typename Arg, intmax_t MaxDepth>
|
||||
TaskGroup<Tp, Arg, MaxDepth>::~TaskGroup()
|
||||
{
|
||||
{
|
||||
// task will decrement counter and then acquire the lock to notify
|
||||
// condition variable so acquiring lock here will prevent the
|
||||
// task group from being destroyed before this is completed
|
||||
AutoLock _lk{ m_task_lock, std::defer_lock };
|
||||
if(!_lk.owns_lock())
|
||||
_lk.lock();
|
||||
}
|
||||
|
||||
if(m_tbb_task_group)
|
||||
{
|
||||
auto* _arena = m_pool->get_task_arena();
|
||||
_arena->execute([=]() { m_tbb_task_group->wait(); });
|
||||
}
|
||||
delete m_tbb_task_group;
|
||||
this->clear();
|
||||
}
|
||||
|
||||
template <typename Tp, typename Arg, intmax_t MaxDepth>
|
||||
template <typename Up>
|
||||
std::shared_ptr<Up>
|
||||
TaskGroup<Tp, Arg, MaxDepth>::operator+=(std::shared_ptr<Up>&& _task)
|
||||
{
|
||||
// thread-safe increment of tasks in task group
|
||||
operator++();
|
||||
// copy the shared pointer to abstract instance
|
||||
m_task_list.push_back(_task);
|
||||
// return the derived instance
|
||||
return std::move(_task);
|
||||
}
|
||||
|
||||
template <typename Tp, typename Arg, intmax_t MaxDepth>
|
||||
void
|
||||
TaskGroup<Tp, Arg, MaxDepth>::wait()
|
||||
{
|
||||
auto _dtor = ScopeDestructor{ [&]() {
|
||||
if(m_tbb_task_group)
|
||||
{
|
||||
auto* _arena = m_pool->get_task_arena();
|
||||
_arena->execute([=]() { m_tbb_task_group->wait(); });
|
||||
}
|
||||
} };
|
||||
|
||||
ThreadData* data = ThreadData::GetInstance();
|
||||
if(!data)
|
||||
return;
|
||||
// if no pool was initially present at creation
|
||||
if(!m_pool)
|
||||
{
|
||||
// check for master MT run-manager
|
||||
m_pool = internal::get_default_threadpool();
|
||||
|
||||
// if no thread pool created
|
||||
if(!m_pool)
|
||||
{
|
||||
if(f_verbose > 0)
|
||||
{
|
||||
fprintf(stderr, "%s @ %i :: Warning! nullptr to thread-pool (%p)\n",
|
||||
__FUNCTION__, __LINE__, static_cast<void*>(m_pool));
|
||||
std::cerr << __FUNCTION__ << "@" << __LINE__ << " :: Warning! "
|
||||
<< "nullptr to thread pool!" << std::endl;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ThreadPool* tpool = (m_pool) ? m_pool : data->thread_pool;
|
||||
VUserTaskQueue* taskq = (tpool) ? tpool->get_queue() : data->current_queue;
|
||||
|
||||
bool _is_main = data->is_main;
|
||||
bool _within_task = data->within_task;
|
||||
|
||||
auto is_active_state = [&]() {
|
||||
return (tpool->state()->load(std::memory_order_relaxed) !=
|
||||
thread_pool::state::STOPPED);
|
||||
};
|
||||
|
||||
auto execute_this_threads_tasks = [&]() {
|
||||
if(!taskq)
|
||||
return;
|
||||
|
||||
// only want to process if within a task
|
||||
if((!_is_main || tpool->size() < 2) && _within_task)
|
||||
{
|
||||
int bin = static_cast<int>(taskq->GetThreadBin());
|
||||
// const auto nitr = (tpool) ? tpool->size() :
|
||||
// Thread::hardware_concurrency();
|
||||
while(this->pending() > 0)
|
||||
{
|
||||
if(!taskq->empty())
|
||||
{
|
||||
auto _task = taskq->GetTask(bin);
|
||||
if(_task)
|
||||
(*_task)();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// checks for validity
|
||||
if(!is_native_task_group())
|
||||
{
|
||||
// for external threads
|
||||
if(!_is_main || tpool->size() < 2)
|
||||
return;
|
||||
}
|
||||
else if(f_verbose > 0)
|
||||
{
|
||||
if(!tpool || !taskq)
|
||||
{
|
||||
// something is wrong, didn't create thread-pool?
|
||||
fprintf(stderr,
|
||||
"%s @ %i :: Warning! nullptr to thread data (%p) or task-queue "
|
||||
"(%p)\n",
|
||||
__FUNCTION__, __LINE__, static_cast<void*>(tpool),
|
||||
static_cast<void*>(taskq));
|
||||
}
|
||||
// return if thread pool isn't built
|
||||
else if(is_native_task_group() && !tpool->is_alive())
|
||||
{
|
||||
fprintf(stderr, "%s @ %i :: Warning! thread-pool is not alive!\n",
|
||||
__FUNCTION__, __LINE__);
|
||||
}
|
||||
else if(!is_active_state())
|
||||
{
|
||||
fprintf(stderr, "%s @ %i :: Warning! thread-pool is not active!\n",
|
||||
__FUNCTION__, __LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
intmax_t wake_size = 2;
|
||||
AutoLock _lock(m_task_lock, std::defer_lock);
|
||||
|
||||
while(is_active_state())
|
||||
{
|
||||
execute_this_threads_tasks();
|
||||
|
||||
// while loop protects against spurious wake-ups
|
||||
while(_is_main && pending() > 0 && is_active_state())
|
||||
{
|
||||
// auto _wake = [&]() { return (wake_size > pending() ||
|
||||
// !is_active_state());
|
||||
// };
|
||||
|
||||
// lock before sleeping on condition
|
||||
if(!_lock.owns_lock())
|
||||
_lock.lock();
|
||||
|
||||
// Wait until signaled that a task has been competed
|
||||
// Unlock mutex while wait, then lock it back when signaled
|
||||
// when true, this wakes the thread
|
||||
if(pending() >= wake_size)
|
||||
{
|
||||
m_task_cond.wait(_lock);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_task_cond.wait_for(_lock, std::chrono::microseconds(100));
|
||||
}
|
||||
// unlock
|
||||
if(_lock.owns_lock())
|
||||
_lock.unlock();
|
||||
}
|
||||
|
||||
// if pending is not greater than zero, we are joined
|
||||
if(pending() <= 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if(_lock.owns_lock())
|
||||
_lock.unlock();
|
||||
|
||||
intmax_t ntask = this->task_count().load();
|
||||
if(ntask > 0)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "\nWarning! Join operation issue! " << ntask << " tasks still "
|
||||
<< "are running!" << std::endl;
|
||||
std::cerr << ss.str();
|
||||
this->wait();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Tp, typename Arg, intmax_t MaxDepth>
|
||||
ScopeDestructor
|
||||
TaskGroup<Tp, Arg, MaxDepth>::get_scope_destructor()
|
||||
{
|
||||
auto& _counter = m_tot_task_count;
|
||||
auto& _task_cond = task_cond();
|
||||
auto& _task_lock = task_lock();
|
||||
return ScopeDestructor{ [&_task_cond, &_task_lock, &_counter]() {
|
||||
auto _count = --(_counter);
|
||||
if(_count < 1)
|
||||
{
|
||||
AutoLock _lk{ _task_lock };
|
||||
_task_cond.notify_all();
|
||||
}
|
||||
} };
|
||||
}
|
||||
|
||||
template <typename Tp, typename Arg, intmax_t MaxDepth>
|
||||
void
|
||||
TaskGroup<Tp, Arg, MaxDepth>::notify()
|
||||
{
|
||||
AutoLock _lk{ m_task_lock };
|
||||
m_task_cond.notify_one();
|
||||
}
|
||||
|
||||
template <typename Tp, typename Arg, intmax_t MaxDepth>
|
||||
void
|
||||
TaskGroup<Tp, Arg, MaxDepth>::notify_all()
|
||||
{
|
||||
AutoLock _lk{ m_task_lock };
|
||||
m_task_cond.notify_all();
|
||||
}
|
||||
|
||||
template <typename Tp, typename Arg, intmax_t MaxDepth>
|
||||
template <typename Func, typename... Args, typename Up>
|
||||
enable_if_t<std::is_void<Up>::value, void>
|
||||
TaskGroup<Tp, Arg, MaxDepth>::exec(Func func, Args... args)
|
||||
{
|
||||
if(MaxDepth > 0 && !m_tbb_task_group && ThreadData::GetInstance() &&
|
||||
ThreadData::GetInstance()->task_depth > MaxDepth)
|
||||
{
|
||||
local_exec<Tp>(std::move(func), std::move(args)...);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto& _counter = m_tot_task_count;
|
||||
auto& _task_cond = task_cond();
|
||||
auto& _task_lock = task_lock();
|
||||
auto _task = wrap([&_task_cond, &_task_lock, &_counter, func, args...]() {
|
||||
auto _tdata = ThreadData::GetInstance();
|
||||
if(_tdata)
|
||||
++(_tdata->task_depth);
|
||||
func(args...);
|
||||
auto _count = --(_counter);
|
||||
if(_tdata)
|
||||
--(_tdata->task_depth);
|
||||
if(_count < 1)
|
||||
{
|
||||
AutoLock _lk{ _task_lock };
|
||||
_task_cond.notify_all();
|
||||
}
|
||||
});
|
||||
|
||||
if(m_tbb_task_group)
|
||||
{
|
||||
auto* _arena = m_pool->get_task_arena();
|
||||
auto* _tbb_task_group = m_tbb_task_group;
|
||||
auto* _ptask = _task.get();
|
||||
_arena->execute([_tbb_task_group, _ptask]() {
|
||||
_tbb_task_group->run([_ptask]() { (*_ptask)(); });
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pool->add_task(std::move(_task));
|
||||
}
|
||||
}
|
||||
}
|
||||
template <typename Tp, typename Arg, intmax_t MaxDepth>
|
||||
template <typename Func, typename... Args, typename Up>
|
||||
enable_if_t<!std::is_void<Up>::value, void>
|
||||
TaskGroup<Tp, Arg, MaxDepth>::exec(Func func, Args... args)
|
||||
{
|
||||
if(MaxDepth > 0 && !m_tbb_task_group && ThreadData::GetInstance() &&
|
||||
ThreadData::GetInstance()->task_depth > MaxDepth)
|
||||
{
|
||||
local_exec<Tp>(std::move(func), std::move(args)...);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto& _counter = m_tot_task_count;
|
||||
auto& _task_cond = task_cond();
|
||||
auto& _task_lock = task_lock();
|
||||
auto _task = wrap([&_task_cond, &_task_lock, &_counter, func, args...]() {
|
||||
auto _tdata = ThreadData::GetInstance();
|
||||
if(_tdata)
|
||||
++(_tdata->task_depth);
|
||||
auto&& _ret = func(args...);
|
||||
auto _count = --(_counter);
|
||||
if(_tdata)
|
||||
--(_tdata->task_depth);
|
||||
if(_count < 1)
|
||||
{
|
||||
AutoLock _lk{ _task_lock };
|
||||
_task_cond.notify_all();
|
||||
}
|
||||
return std::forward<decltype(_ret)>(_ret);
|
||||
});
|
||||
|
||||
if(m_tbb_task_group)
|
||||
{
|
||||
auto* _arena = m_pool->get_task_arena();
|
||||
auto* _tbb_task_group = m_tbb_task_group;
|
||||
auto* _ptask = _task.get();
|
||||
_arena->execute([_tbb_task_group, _ptask]() {
|
||||
_tbb_task_group->run([_ptask]() { (*_ptask)(); });
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pool->add_task(std::move(_task));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Tp, typename Arg, intmax_t MaxDepth>
|
||||
template <typename Up, typename Func, typename... Args>
|
||||
enable_if_t<std::is_void<Up>::value, void>
|
||||
TaskGroup<Tp, Arg, MaxDepth>::local_exec(Func func, Args... args)
|
||||
{
|
||||
auto _tdata = ThreadData::GetInstance();
|
||||
if(_tdata)
|
||||
++(_tdata->task_depth);
|
||||
promise_type _p{};
|
||||
m_future_list.emplace_back(_p.get_future());
|
||||
func(args...);
|
||||
_p.set_value();
|
||||
if(_tdata)
|
||||
--(_tdata->task_depth);
|
||||
}
|
||||
|
||||
template <typename Tp, typename Arg, intmax_t MaxDepth>
|
||||
template <typename Up, typename Func, typename... Args>
|
||||
enable_if_t<!std::is_void<Up>::value, void>
|
||||
TaskGroup<Tp, Arg, MaxDepth>::local_exec(Func func, Args... args)
|
||||
{
|
||||
auto _tdata = ThreadData::GetInstance();
|
||||
if(_tdata)
|
||||
++(_tdata->task_depth);
|
||||
promise_type _p{};
|
||||
m_future_list.emplace_back(_p.get_future());
|
||||
_p.set_value(func(args...));
|
||||
if(_tdata)
|
||||
--(_tdata->task_depth);
|
||||
}
|
||||
|
||||
template <typename Tp, typename Arg, intmax_t MaxDepth>
|
||||
template <typename Up, enable_if_t<!std::is_void<Up>::value, int>>
|
||||
inline Up
|
||||
TaskGroup<Tp, Arg, MaxDepth>::join(Up accum)
|
||||
{
|
||||
this->wait();
|
||||
for(auto& itr : m_task_list)
|
||||
{
|
||||
using RetT = decay_t<decltype(itr->get())>;
|
||||
accum = std::move(m_join(std::ref(accum), std::forward<RetT>(itr->get())));
|
||||
}
|
||||
for(auto& itr : m_future_list)
|
||||
{
|
||||
using RetT = decay_t<decltype(itr.get())>;
|
||||
accum = std::move(m_join(std::ref(accum), std::forward<RetT>(itr.get())));
|
||||
}
|
||||
this->clear();
|
||||
return accum;
|
||||
}
|
||||
|
||||
template <typename Tp, typename Arg, intmax_t MaxDepth>
|
||||
template <typename Up, typename Rp,
|
||||
enable_if_t<std::is_void<Up>::value && std::is_void<Rp>::value, int>>
|
||||
inline void
|
||||
TaskGroup<Tp, Arg, MaxDepth>::join()
|
||||
{
|
||||
this->wait();
|
||||
for(auto& itr : m_task_list)
|
||||
itr->get();
|
||||
for(auto& itr : m_future_list)
|
||||
itr.get();
|
||||
m_join();
|
||||
this->clear();
|
||||
}
|
||||
|
||||
template <typename Tp, typename Arg, intmax_t MaxDepth>
|
||||
template <typename Up, typename Rp,
|
||||
enable_if_t<std::is_void<Up>::value && !std::is_void<Rp>::value, int>>
|
||||
inline void
|
||||
TaskGroup<Tp, Arg, MaxDepth>::join()
|
||||
{
|
||||
this->wait();
|
||||
for(auto& itr : m_task_list)
|
||||
{
|
||||
using RetT = decay_t<decltype(itr->get())>;
|
||||
m_join(std::forward<RetT>(itr->get()));
|
||||
}
|
||||
for(auto& itr : m_future_list)
|
||||
{
|
||||
using RetT = decay_t<decltype(itr.get())>;
|
||||
m_join(std::forward<RetT>(itr.get()));
|
||||
}
|
||||
this->clear();
|
||||
}
|
||||
|
||||
template <typename Tp, typename Arg, intmax_t MaxDepth>
|
||||
void
|
||||
TaskGroup<Tp, Arg, MaxDepth>::clear()
|
||||
{
|
||||
m_future_list.clear();
|
||||
m_task_list.clear();
|
||||
}
|
||||
|
||||
template <typename Tp, typename Arg, intmax_t MaxDepth>
|
||||
void
|
||||
TaskGroup<Tp, Arg, MaxDepth>::internal_update()
|
||||
{
|
||||
if(!m_pool)
|
||||
m_pool = internal::get_default_threadpool();
|
||||
|
||||
if(!m_pool)
|
||||
{
|
||||
std::stringstream ss{};
|
||||
ss << "[TaskGroup]> " << __FUNCTION__ << "@" << __LINE__
|
||||
<< " :: nullptr to thread pool";
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
|
||||
if(m_pool->is_tbb_threadpool())
|
||||
{
|
||||
m_tbb_task_group = new tbb_task_group_t{};
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Tp, typename Arg, intmax_t MaxDepth>
|
||||
int TaskGroup<Tp, Arg, MaxDepth>::f_verbose = GetEnv<int>("PTL_VERBOSE", 0);
|
||||
|
||||
} // namespace PTL
|
||||
+15
-54
@@ -97,42 +97,37 @@ public:
|
||||
// direct insertion of a packaged_task
|
||||
//------------------------------------------------------------------------//
|
||||
template <typename RetT, typename FuncT, typename... Args>
|
||||
std::future<RetT> async(FuncT&& func, Args&&... args)
|
||||
std::shared_ptr<PackagedTask<RetT, Args...>> async(FuncT&& func, Args&&... args)
|
||||
{
|
||||
typedef PackagedTask<RetT, Args...> task_type;
|
||||
typedef task_type* task_pointer;
|
||||
typedef PackagedTask<RetT, Args...> task_type;
|
||||
|
||||
task_pointer _ptask =
|
||||
new task_type(std::forward<FuncT>(func), std::forward<Args>(args)...);
|
||||
std::future<RetT> _f = _ptask->get_future();
|
||||
auto _ptask = std::make_shared<task_type>(std::forward<FuncT>(func),
|
||||
std::forward<Args>(args)...);
|
||||
m_pool->add_task(_ptask);
|
||||
return _f;
|
||||
return _ptask;
|
||||
}
|
||||
//------------------------------------------------------------------------//
|
||||
template <typename RetT, typename FuncT>
|
||||
std::future<RetT> async(FuncT&& func)
|
||||
std::shared_ptr<PackagedTask<RetT>> async(FuncT&& func)
|
||||
{
|
||||
typedef PackagedTask<RetT> task_type;
|
||||
typedef task_type* task_pointer;
|
||||
|
||||
task_pointer _ptask = new task_type(std::forward<FuncT>(func));
|
||||
std::future<RetT> _f = _ptask->get_future();
|
||||
auto _ptask = std::make_shared<task_type>(std::forward<FuncT>(func));
|
||||
m_pool->add_task(_ptask);
|
||||
return _f;
|
||||
return _ptask;
|
||||
}
|
||||
//------------------------------------------------------------------------//
|
||||
template <typename FuncT, typename... Args>
|
||||
auto async(FuncT&& func, Args... args)
|
||||
-> std::future<decay_t<decltype(func(args...))>>
|
||||
-> std::shared_ptr<PackagedTask<decay_t<decltype(func(args...))>, Args...>>
|
||||
{
|
||||
using RetT = decay_t<decltype(func(args...))>;
|
||||
typedef PackagedTask<RetT, Args...> task_type;
|
||||
|
||||
auto _ptask =
|
||||
new task_type(std::forward<FuncT>(func), std::forward<Args>(args)...);
|
||||
auto _f = _ptask->get_future();
|
||||
auto _ptask = std::make_shared<task_type>(std::forward<FuncT>(func),
|
||||
std::forward<Args>(args)...);
|
||||
m_pool->add_task(_ptask);
|
||||
return _f;
|
||||
return _ptask;
|
||||
}
|
||||
//------------------------------------------------------------------------//
|
||||
|
||||
@@ -141,14 +136,14 @@ public:
|
||||
// public wrap functions
|
||||
//------------------------------------------------------------------------//
|
||||
template <typename RetT, typename ArgT, typename FuncT, typename... Args>
|
||||
Task<RetT, ArgT, Args...>* wrap(TaskGroup<RetT, ArgT>& tg, FuncT&& func,
|
||||
Args&&... args)
|
||||
std::shared_ptr<Task<RetT, ArgT, Args...>> wrap(TaskGroup<RetT, ArgT>& tg,
|
||||
FuncT&& func, Args&&... args)
|
||||
{
|
||||
return tg.wrap(std::forward<FuncT>(func), std::forward<Args>(args)...);
|
||||
}
|
||||
//------------------------------------------------------------------------//
|
||||
template <typename RetT, typename ArgT, typename FuncT>
|
||||
Task<RetT, ArgT>* wrap(TaskGroup<RetT, ArgT>& tg, FuncT&& func)
|
||||
std::shared_ptr<Task<RetT, ArgT>> wrap(TaskGroup<RetT, ArgT>& tg, FuncT&& func)
|
||||
{
|
||||
return tg.wrap(std::forward<FuncT>(func));
|
||||
}
|
||||
@@ -196,40 +191,6 @@ public:
|
||||
}
|
||||
//------------------------------------------------------------------------//
|
||||
|
||||
#if defined(PTL_USE_TBB)
|
||||
//------------------------------------------------------------------------//
|
||||
// public wrap functions using TBB tasks
|
||||
//------------------------------------------------------------------------//
|
||||
template <typename RetT, typename ArgT, typename FuncT, typename... Args>
|
||||
Task<RetT, ArgT, Args...>* wrap(TBBTaskGroup<RetT, ArgT>& tg, FuncT&& func,
|
||||
Args&&... args)
|
||||
{
|
||||
return tg.wrap(std::forward<FuncT>(func), std::forward<Args>(args)...);
|
||||
}
|
||||
//------------------------------------------------------------------------//
|
||||
template <typename RetT, typename ArgT, typename FuncT>
|
||||
Task<RetT, ArgT>* wrap(TBBTaskGroup<RetT, ArgT>& tg, FuncT&& func)
|
||||
{
|
||||
return tg.wrap(std::forward<FuncT>(func));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------//
|
||||
// public exec functions using TBB tasks
|
||||
//------------------------------------------------------------------------//
|
||||
template <typename RetT, typename ArgT, typename FuncT, typename... Args>
|
||||
void exec(TBBTaskGroup<RetT, ArgT>& tg, FuncT&& func, Args&&... args)
|
||||
{
|
||||
tg.exec(std::forward<FuncT>(func), std::forward<Args>(args)...);
|
||||
}
|
||||
//------------------------------------------------------------------------//
|
||||
template <typename RetT, typename ArgT, typename FuncT>
|
||||
void exec(TBBTaskGroup<RetT, ArgT>& tg, FuncT&& func)
|
||||
{
|
||||
tg.exec(std::forward<FuncT>(func));
|
||||
}
|
||||
//------------------------------------------------------------------------//
|
||||
#endif
|
||||
|
||||
protected:
|
||||
// Protected variables
|
||||
ThreadPool* m_pool = nullptr;
|
||||
|
||||
+2
-2
@@ -44,7 +44,7 @@ class TaskManager;
|
||||
class TaskRunManager
|
||||
{
|
||||
public:
|
||||
typedef TaskRunManager* pointer;
|
||||
typedef TaskRunManager* pointer;
|
||||
|
||||
public:
|
||||
// Parameters:
|
||||
@@ -52,7 +52,7 @@ public:
|
||||
// useTBB: only relevant if PTL_USE_TBB defined
|
||||
// grainsize: 0 = auto
|
||||
explicit TaskRunManager(bool useTBB = false);
|
||||
virtual ~TaskRunManager();
|
||||
virtual ~TaskRunManager() = default;
|
||||
|
||||
public:
|
||||
virtual int GetNumberOfThreads() const
|
||||
|
||||
+41
-8
@@ -28,11 +28,15 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <thread>
|
||||
|
||||
#if defined(PTL_USE_TBB)
|
||||
# if !defined(TBB_PREVIEW_GLOBAL_CONTROL)
|
||||
# define TBB_PREVIEW_GLOBAL_CONTROL 1
|
||||
# endif
|
||||
# include <tbb/global_control.h>
|
||||
# include <tbb/task_arena.h>
|
||||
# include <tbb/task_group.h>
|
||||
# include <tbb/task_scheduler_init.h>
|
||||
#endif
|
||||
|
||||
namespace PTL
|
||||
@@ -43,6 +47,8 @@ namespace PTL
|
||||
|
||||
using tbb_global_control_t = ::tbb::global_control;
|
||||
using tbb_task_group_t = ::tbb::task_group;
|
||||
using tbb_task_arena_t = ::tbb::task_arena;
|
||||
|
||||
#else
|
||||
|
||||
namespace tbb
|
||||
@@ -82,10 +88,37 @@ public:
|
||||
static size_t active_value(parameter param);
|
||||
};
|
||||
|
||||
class task_arena
|
||||
{
|
||||
public:
|
||||
enum parameter
|
||||
{
|
||||
not_initialized = -2,
|
||||
automatic = -1
|
||||
};
|
||||
|
||||
task_arena(int max_concurrency = automatic, unsigned reserved_for_masters = 1)
|
||||
{
|
||||
(void) max_concurrency;
|
||||
(void) reserved_for_masters;
|
||||
}
|
||||
|
||||
~task_arena() = default;
|
||||
|
||||
void initialize(int max_concurrency = automatic, unsigned reserved_for_masters = 1);
|
||||
|
||||
template <typename FuncT>
|
||||
auto execute(FuncT&& _func) -> decltype(_func())
|
||||
{
|
||||
return _func();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace tbb
|
||||
|
||||
using tbb_global_control_t = tbb::global_control;
|
||||
using tbb_task_group_t = tbb::task_group;
|
||||
using tbb_task_arena_t = tbb::task_arena;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -103,17 +136,17 @@ public:
|
||||
using TaskStack = std::deque<Tp>;
|
||||
|
||||
ThreadData(ThreadPool* tp);
|
||||
~ThreadData();
|
||||
~ThreadData() = default;
|
||||
|
||||
void update();
|
||||
|
||||
public:
|
||||
bool is_master;
|
||||
bool within_task;
|
||||
intmax_t task_depth;
|
||||
ThreadPool* thread_pool;
|
||||
VUserTaskQueue* current_queue;
|
||||
TaskStack<VUserTaskQueue*> queue_stack;
|
||||
bool is_main = false;
|
||||
bool within_task = false;
|
||||
intmax_t task_depth = 0;
|
||||
ThreadPool* thread_pool = nullptr;
|
||||
VUserTaskQueue* current_queue = nullptr;
|
||||
TaskStack<VUserTaskQueue*> queue_stack = {};
|
||||
|
||||
public:
|
||||
// Public functions
|
||||
|
||||
+264
-96
@@ -34,10 +34,15 @@
|
||||
#include "PTL/ThreadData.hh"
|
||||
#include "PTL/Threading.hh"
|
||||
#include "PTL/VTask.hh"
|
||||
#include "PTL/VTaskGroup.hh"
|
||||
#include "PTL/VUserTaskQueue.hh"
|
||||
|
||||
#ifdef PTL_USE_TBB
|
||||
#if defined(PTL_USE_TBB)
|
||||
# if !defined(TBB_SUPPRESS_DEPRECATED_MESSAGES)
|
||||
# define TBB_SUPPRESS_DEPRECATED_MESSAGES 1
|
||||
# endif
|
||||
# if !defined(TBB_PREVIEW_GLOBAL_CONTROL)
|
||||
# define TBB_PREVIEW_GLOBAL_CONTROL 1
|
||||
# endif
|
||||
# include <tbb/global_control.h>
|
||||
# include <tbb/tbb.h>
|
||||
#endif
|
||||
@@ -76,28 +81,28 @@ public:
|
||||
using task_type = VTask;
|
||||
using lock_t = std::shared_ptr<Mutex>;
|
||||
using condition_t = std::shared_ptr<Condition>;
|
||||
using task_pointer = task_type*;
|
||||
using task_pointer = std::shared_ptr<task_type>;
|
||||
using task_queue_t = VUserTaskQueue;
|
||||
// containers
|
||||
typedef std::deque<ThreadId> thread_list_t;
|
||||
typedef std::vector<bool> bool_list_t;
|
||||
typedef std::map<ThreadId, uintmax_t> thread_id_map_t;
|
||||
typedef std::map<uintmax_t, ThreadId> thread_index_map_t;
|
||||
using thread_vec_t = std::vector<Thread>;
|
||||
using thread_vec_t = std::vector<Thread>;
|
||||
using thread_data_t = std::vector<std::shared_ptr<ThreadData>>;
|
||||
// functions
|
||||
typedef std::function<void()> initialize_func_t;
|
||||
typedef std::function<intmax_t(intmax_t)> affinity_func_t;
|
||||
|
||||
public:
|
||||
// Constructor and Destructors
|
||||
ThreadPool(
|
||||
const size_type& pool_size, VUserTaskQueue* task_queue = nullptr,
|
||||
bool _use_affinity = GetEnv<bool>("PTL_CPU_AFFINITY", false),
|
||||
const affinity_func_t& = [](intmax_t) {
|
||||
static std::atomic<intmax_t> assigned;
|
||||
intmax_t _assign = assigned++;
|
||||
return _assign % Thread::hardware_concurrency();
|
||||
});
|
||||
ThreadPool(const size_type& pool_size, VUserTaskQueue* task_queue = nullptr,
|
||||
bool _use_affinity = GetEnv<bool>("PTL_CPU_AFFINITY", false),
|
||||
affinity_func_t = [](intmax_t) {
|
||||
static std::atomic<intmax_t> assigned;
|
||||
intmax_t _assign = assigned++;
|
||||
return _assign % Thread::hardware_concurrency();
|
||||
});
|
||||
// Virtual destructors are required by abstract classes
|
||||
// so add it by default, just in case
|
||||
virtual ~ThreadPool();
|
||||
@@ -115,6 +120,15 @@ public:
|
||||
template <typename FuncT>
|
||||
void execute_on_all_threads(FuncT&& _func);
|
||||
|
||||
template <typename FuncT>
|
||||
void execute_on_specific_threads(const std::set<std::thread::id>& _tid,
|
||||
FuncT&& _func);
|
||||
|
||||
task_queue_t* get_queue() const { return m_task_queue; }
|
||||
task_queue_t*& get_valid_queue(task_queue_t*&) const;
|
||||
|
||||
bool is_tbb_threadpool() const { return m_tbb_tp; }
|
||||
|
||||
public:
|
||||
// Public functions related to TBB
|
||||
static bool using_tbb();
|
||||
@@ -123,7 +137,7 @@ public:
|
||||
|
||||
public:
|
||||
// add tasks for threads to process
|
||||
size_type add_task(task_pointer task, int bin = -1);
|
||||
size_type add_task(task_pointer&& task, int bin = -1);
|
||||
// size_type add_thread_task(ThreadId id, task_pointer&& task);
|
||||
// add a generic container with iterator
|
||||
template <typename ListT>
|
||||
@@ -132,8 +146,6 @@ public:
|
||||
Thread* get_thread(size_type _n) const;
|
||||
Thread* get_thread(std::thread::id id) const;
|
||||
|
||||
task_queue_t* get_queue() const { return m_task_queue; }
|
||||
|
||||
// only relevant when compiled with PTL_USE_TBB
|
||||
static tbb_global_control_t*& tbb_global_control();
|
||||
|
||||
@@ -168,21 +180,37 @@ public:
|
||||
|
||||
void set_verbose(int n) { m_verbose = n; }
|
||||
int get_verbose() const { return m_verbose; }
|
||||
bool is_master() const { return ThisThread::get_id() == m_master_tid; }
|
||||
bool is_main() const { return ThisThread::get_id() == m_main_tid; }
|
||||
|
||||
tbb_task_arena_t* get_task_arena();
|
||||
|
||||
public:
|
||||
// read FORCE_NUM_THREADS environment variable
|
||||
static const thread_id_map_t& get_thread_ids();
|
||||
static uintmax_t get_this_thread_id();
|
||||
static uintmax_t add_thread_id()
|
||||
{
|
||||
AutoLock lock(TypeMutex<ThreadPool>(), std::defer_lock);
|
||||
if(!lock.owns_lock())
|
||||
lock.lock();
|
||||
auto _tid = ThisThread::get_id();
|
||||
if(f_thread_ids.find(_tid) == f_thread_ids.end())
|
||||
{
|
||||
auto _idx = f_thread_ids.size();
|
||||
f_thread_ids[_tid] = _idx;
|
||||
Threading::SetThreadId(_idx);
|
||||
}
|
||||
return f_thread_ids.at(_tid);
|
||||
}
|
||||
|
||||
protected:
|
||||
void execute_thread(VUserTaskQueue*); // function thread sits in
|
||||
int insert(const task_pointer&, int = -1);
|
||||
int run_on_this(task_pointer);
|
||||
int insert(task_pointer&&, int = -1);
|
||||
int run_on_this(task_pointer&&);
|
||||
|
||||
protected:
|
||||
// called in THREAD INIT
|
||||
static void start_thread(ThreadPool*, intmax_t = -1);
|
||||
static void start_thread(ThreadPool*, thread_data_t*, intmax_t = -1);
|
||||
|
||||
void record_entry()
|
||||
{
|
||||
@@ -199,15 +227,16 @@ protected:
|
||||
private:
|
||||
// Private variables
|
||||
// random
|
||||
bool m_use_affinity;
|
||||
bool m_tbb_tp;
|
||||
int m_verbose = 0;
|
||||
size_type m_pool_size = 0;
|
||||
ThreadId m_master_tid;
|
||||
atomic_bool_type m_alive_flag = std::make_shared<std::atomic_bool>(false);
|
||||
pool_state_type m_pool_state = std::make_shared<std::atomic_short>(0);
|
||||
atomic_int_type m_thread_awake = std::make_shared<std::atomic_uintmax_t>();
|
||||
atomic_int_type m_thread_active = std::make_shared<std::atomic_uintmax_t>();
|
||||
bool m_use_affinity = false;
|
||||
bool m_tbb_tp = false;
|
||||
bool m_delete_task_queue = false;
|
||||
int m_verbose = GetEnv<int>("PTL_VERBOSE", 0);
|
||||
size_type m_pool_size = 0;
|
||||
ThreadId m_main_tid = ThisThread::get_id();
|
||||
atomic_bool_type m_alive_flag = std::make_shared<std::atomic_bool>(false);
|
||||
pool_state_type m_pool_state = std::make_shared<std::atomic_short>(0);
|
||||
atomic_int_type m_thread_awake = std::make_shared<std::atomic_uintmax_t>();
|
||||
atomic_int_type m_thread_active = std::make_shared<std::atomic_uintmax_t>();
|
||||
|
||||
// locks
|
||||
lock_t m_task_lock = std::make_shared<Mutex>();
|
||||
@@ -215,18 +244,20 @@ private:
|
||||
condition_t m_task_cond = std::make_shared<Condition>();
|
||||
|
||||
// containers
|
||||
bool_list_t m_is_joined; // join list
|
||||
bool_list_t m_is_stopped; // lets thread know to stop
|
||||
thread_list_t m_main_threads; // storage for active threads
|
||||
thread_list_t m_stop_threads; // storage for stopped threads
|
||||
thread_vec_t m_threads;
|
||||
bool_list_t m_is_joined{}; // join list
|
||||
bool_list_t m_is_stopped{}; // lets thread know to stop
|
||||
thread_list_t m_main_threads{}; // storage for active threads
|
||||
thread_list_t m_stop_threads{}; // storage for stopped threads
|
||||
thread_vec_t m_threads{};
|
||||
thread_data_t m_thread_data{};
|
||||
|
||||
// task queue
|
||||
task_queue_t* m_task_queue;
|
||||
tbb_task_group_t* m_tbb_task_group;
|
||||
task_queue_t* m_task_queue = nullptr;
|
||||
tbb_task_arena_t* m_tbb_task_arena = nullptr;
|
||||
tbb_task_group_t* m_tbb_task_group = nullptr;
|
||||
|
||||
// functions
|
||||
initialize_func_t m_init_func;
|
||||
initialize_func_t m_init_func = []() {};
|
||||
affinity_func_t m_affinity_func;
|
||||
|
||||
private:
|
||||
@@ -271,7 +302,9 @@ ThreadPool::notify(size_type ntasks)
|
||||
m_task_cond->notify_one();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_task_cond->notify_all();
|
||||
}
|
||||
}
|
||||
}
|
||||
//--------------------------------------------------------------------------------------//
|
||||
@@ -283,6 +316,28 @@ ThreadPool::tbb_global_control()
|
||||
return _instance;
|
||||
}
|
||||
//--------------------------------------------------------------------------------------//
|
||||
// task arena
|
||||
inline tbb_task_arena_t*
|
||||
ThreadPool::get_task_arena()
|
||||
{
|
||||
#if defined(PTL_USE_TBB)
|
||||
// create a task arena
|
||||
if(!m_tbb_task_arena)
|
||||
{
|
||||
auto _sz = (tbb_global_control())
|
||||
? tbb_global_control()->active_value(
|
||||
tbb::global_control::max_allowed_parallelism)
|
||||
: size();
|
||||
m_tbb_task_arena = new tbb_task_arena_t(::tbb::task_arena::attach{});
|
||||
m_tbb_task_arena->initialize(_sz, 1);
|
||||
}
|
||||
#else
|
||||
if(!m_tbb_task_arena)
|
||||
m_tbb_task_arena = new tbb_task_arena_t{};
|
||||
#endif
|
||||
return m_tbb_task_arena;
|
||||
}
|
||||
//--------------------------------------------------------------------------------------//
|
||||
inline void
|
||||
ThreadPool::resize(size_type _n)
|
||||
{
|
||||
@@ -293,17 +348,14 @@ ThreadPool::resize(size_type _n)
|
||||
}
|
||||
//--------------------------------------------------------------------------------------//
|
||||
inline int
|
||||
ThreadPool::run_on_this(task_pointer task)
|
||||
ThreadPool::run_on_this(task_pointer&& _task)
|
||||
{
|
||||
auto _func = [=]() {
|
||||
(*task)();
|
||||
if(!task->group())
|
||||
delete task;
|
||||
};
|
||||
auto&& _func = [_task]() { (*_task)(); };
|
||||
|
||||
if(m_tbb_tp && m_tbb_task_group)
|
||||
{
|
||||
m_tbb_task_group->run(_func);
|
||||
auto _arena = get_task_arena();
|
||||
_arena->execute([=]() { m_tbb_task_group->run(_func); });
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -314,28 +366,24 @@ ThreadPool::run_on_this(task_pointer task)
|
||||
}
|
||||
//--------------------------------------------------------------------------------------//
|
||||
inline int
|
||||
ThreadPool::insert(const task_pointer& task, int bin)
|
||||
ThreadPool::insert(task_pointer&& task, int bin)
|
||||
{
|
||||
static thread_local ThreadData* _data = ThreadData::GetInstance();
|
||||
|
||||
// pass the task to the queue
|
||||
auto ibin = m_task_queue->InsertTask(task, _data, bin);
|
||||
auto ibin = get_valid_queue(m_task_queue)->InsertTask(std::move(task), _data, bin);
|
||||
notify();
|
||||
return ibin;
|
||||
}
|
||||
//--------------------------------------------------------------------------------------//
|
||||
inline ThreadPool::size_type
|
||||
ThreadPool::add_task(task_pointer task, int bin)
|
||||
ThreadPool::add_task(task_pointer&& task, int bin)
|
||||
{
|
||||
// if not native (i.e. TBB) then return
|
||||
if(!task->is_native_task())
|
||||
return 0;
|
||||
// if not native (i.e. TBB) or we haven't built thread-pool, just execute
|
||||
if(m_tbb_tp || !task->is_native_task() || !m_alive_flag->load())
|
||||
return static_cast<size_type>(run_on_this(std::move(task)));
|
||||
|
||||
// if we haven't built thread-pool, just execute
|
||||
if(!m_alive_flag->load())
|
||||
return static_cast<size_type>(run_on_this(task));
|
||||
|
||||
return static_cast<size_type>(insert(task, bin));
|
||||
return static_cast<size_type>(insert(std::move(task), bin));
|
||||
}
|
||||
//--------------------------------------------------------------------------------------//
|
||||
template <typename ListT>
|
||||
@@ -359,7 +407,7 @@ ThreadPool::add_tasks(ListT& c)
|
||||
else
|
||||
{
|
||||
//++(m_task_queue);
|
||||
m_task_queue->InsertTask(itr);
|
||||
get_valid_queue(m_task_queue)->InsertTask(itr);
|
||||
}
|
||||
}
|
||||
c.clear();
|
||||
@@ -377,87 +425,92 @@ ThreadPool::execute_on_all_threads(FuncT&& _func)
|
||||
if(m_tbb_tp && m_tbb_task_group)
|
||||
{
|
||||
#if defined(PTL_USE_TBB)
|
||||
// TBB lazily activates threads to process tasks and the master thread
|
||||
// TBB lazily activates threads to process tasks and the main thread
|
||||
// participates in processing the tasks so getting a specific
|
||||
// function to execute only on the worker threads requires some trickery
|
||||
//
|
||||
auto master_tid = ThisThread::get_id();
|
||||
std::set<std::thread::id> _first;
|
||||
Mutex _mutex;
|
||||
std::set<std::thread::id> _first{};
|
||||
Mutex _mutex{};
|
||||
// init function which executes function and returns 1 only once
|
||||
auto _init = [&]() {
|
||||
static thread_local int _once = 0;
|
||||
int _once = 0;
|
||||
_mutex.lock();
|
||||
if(_first.find(std::this_thread::get_id()) == _first.end())
|
||||
{
|
||||
// we need to reset this thread-local static for multiple invocations
|
||||
// of the same template instantiation
|
||||
_once = 0;
|
||||
_once = 1;
|
||||
_first.insert(std::this_thread::get_id());
|
||||
}
|
||||
_mutex.unlock();
|
||||
if(_once++ == 0)
|
||||
if(_once != 0)
|
||||
{
|
||||
_func();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
// consumes approximately N milliseconds of cpu time
|
||||
auto _consume = [](long n) {
|
||||
using stl_mutex_t = std::mutex;
|
||||
using unique_lock_t = std::unique_lock<stl_mutex_t>;
|
||||
// a mutex held by one lock
|
||||
stl_mutex_t mutex;
|
||||
// acquire lock
|
||||
unique_lock_t hold_lk(mutex);
|
||||
// associate but defer
|
||||
unique_lock_t try_lk(mutex, std::defer_lock);
|
||||
// get current time
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
// try until time point
|
||||
while(std::chrono::steady_clock::now() < (now + std::chrono::milliseconds(n)))
|
||||
try_lk.try_lock();
|
||||
};
|
||||
// this will collect the number of threads which have
|
||||
// executed the _init function above
|
||||
std::atomic<size_t> _total_init{ 0 };
|
||||
// max parallelism by TBB
|
||||
size_t _maxp = tbb_global_control()->active_value(
|
||||
tbb::global_control::max_allowed_parallelism);
|
||||
// create a task arean
|
||||
auto _arena = get_task_arena();
|
||||
// size of the thread-pool
|
||||
size_t _sz = size();
|
||||
// number of cores
|
||||
size_t _ncore = Threading::GetNumberOfCores();
|
||||
// maximum depth for recursion
|
||||
size_t _dmax = std::max<size_t>(_ncore, 4);
|
||||
// how many threads we need to initialize
|
||||
size_t _num = std::min(_maxp, std::min(_sz, _ncore));
|
||||
// this is the task passed to the task-group
|
||||
auto _init_task = [&]() {
|
||||
int _ret = 0;
|
||||
// don't let the master thread execute the function
|
||||
if(ThisThread::get_id() != master_tid)
|
||||
std::function<void()> _init_task;
|
||||
_init_task = [&]() {
|
||||
add_thread_id();
|
||||
static thread_local size_type _depth = 0;
|
||||
int _ret = 0;
|
||||
// don't let the main thread execute the function
|
||||
if(!is_main())
|
||||
{
|
||||
// execute the function
|
||||
_ret = _init();
|
||||
// add the result
|
||||
_total_init += _ret;
|
||||
}
|
||||
// if the function did not return anything, put it to sleep
|
||||
// so TBB will wake other threads to execute the remaining tasks
|
||||
if(_ret == 0)
|
||||
_consume(100);
|
||||
// if the function did not return anything, recursively execute
|
||||
// two more tasks
|
||||
++_depth;
|
||||
if(_ret == 0 && _depth < _dmax && _total_init.load() < _num)
|
||||
{
|
||||
tbb::task_group tg{};
|
||||
tg.run([&]() { _init_task(); });
|
||||
tg.run([&]() { _init_task(); });
|
||||
ThisThread::sleep_for(std::chrono::milliseconds{ 1 });
|
||||
tg.wait();
|
||||
}
|
||||
--_depth;
|
||||
};
|
||||
|
||||
// TBB won't oversubscribe so we need to limit by ncores - 1
|
||||
size_t nitr = 0;
|
||||
size_t _maxp = tbb_global_control()->active_value(
|
||||
tbb::global_control::max_allowed_parallelism);
|
||||
size_t _sz = size();
|
||||
size_t _ncore = Threading::GetNumberOfCores() - 1;
|
||||
size_t _num = std::min(_maxp, std::min(_sz, _ncore));
|
||||
size_t nitr = 0;
|
||||
auto _fname = __FUNCTION__;
|
||||
auto _write_info = [&]() {
|
||||
std::cerr << "[" << _fname << "]> Total initalized: " << _total_init
|
||||
std::cout << "[" << _fname << "]> Total initalized: " << _total_init
|
||||
<< ", expected: " << _num << ", max-parallel: " << _maxp
|
||||
<< ", size: " << _sz << ", ncore: " << _ncore << std::endl;
|
||||
};
|
||||
while(_total_init < _num)
|
||||
{
|
||||
auto _n = _num;
|
||||
auto _n = 2 * _num;
|
||||
while(--_n > 0)
|
||||
m_tbb_task_group->run(_init_task);
|
||||
m_tbb_task_group->wait();
|
||||
{
|
||||
_arena->execute(
|
||||
[&]() { m_tbb_task_group->run([&]() { _init_task(); }); });
|
||||
}
|
||||
_arena->execute([&]() { m_tbb_task_group->wait(); });
|
||||
// don't loop infinitely but use a strict condition
|
||||
if(nitr++ > 2 * (_num + 1) && (_total_init - 1) == _num)
|
||||
{
|
||||
@@ -480,6 +533,121 @@ ThreadPool::execute_on_all_threads(FuncT&& _func)
|
||||
get_queue()->ExecuteOnAllThreads(this, std::forward<FuncT>(_func));
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
template <typename FuncT>
|
||||
inline void
|
||||
ThreadPool::execute_on_specific_threads(const std::set<std::thread::id>& _tids,
|
||||
FuncT&& _func)
|
||||
{
|
||||
if(m_tbb_tp && m_tbb_task_group)
|
||||
{
|
||||
#if defined(PTL_USE_TBB)
|
||||
// TBB lazily activates threads to process tasks and the main thread
|
||||
// participates in processing the tasks so getting a specific
|
||||
// function to execute only on the worker threads requires some trickery
|
||||
//
|
||||
std::set<std::thread::id> _first{};
|
||||
Mutex _mutex{};
|
||||
// init function which executes function and returns 1 only once
|
||||
auto _exec = [&]() {
|
||||
int _once = 0;
|
||||
_mutex.lock();
|
||||
if(_first.find(std::this_thread::get_id()) == _first.end())
|
||||
{
|
||||
// we need to reset this thread-local static for multiple invocations
|
||||
// of the same template instantiation
|
||||
_once = 1;
|
||||
_first.insert(std::this_thread::get_id());
|
||||
}
|
||||
_mutex.unlock();
|
||||
if(_once != 0)
|
||||
{
|
||||
_func();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
// this will collect the number of threads which have
|
||||
// executed the _exec function above
|
||||
std::atomic<size_t> _total_exec{ 0 };
|
||||
// number of cores
|
||||
size_t _ncore = Threading::GetNumberOfCores();
|
||||
// maximum depth for recursion
|
||||
size_t _dmax = std::max<size_t>(_ncore, 4);
|
||||
// how many threads we need to initialize
|
||||
size_t _num = _tids.size();
|
||||
// create a task arena
|
||||
auto _arena = get_task_arena();
|
||||
// this is the task passed to the task-group
|
||||
std::function<void()> _exec_task;
|
||||
_exec_task = [&]() {
|
||||
add_thread_id();
|
||||
static thread_local size_type _depth = 0;
|
||||
int _ret = 0;
|
||||
auto _this_tid = std::this_thread::get_id();
|
||||
// don't let the main thread execute the function
|
||||
if(_tids.count(_this_tid) > 0)
|
||||
{
|
||||
// execute the function
|
||||
_ret = _exec();
|
||||
// add the result
|
||||
_total_exec += _ret;
|
||||
}
|
||||
// if the function did not return anything, recursively execute
|
||||
// two more tasks
|
||||
++_depth;
|
||||
if(_ret == 0 && _depth < _dmax && _total_exec.load() < _num)
|
||||
{
|
||||
tbb::task_group tg{};
|
||||
tg.run([&]() { _exec_task(); });
|
||||
tg.run([&]() { _exec_task(); });
|
||||
ThisThread::sleep_for(std::chrono::milliseconds{ 1 });
|
||||
tg.wait();
|
||||
}
|
||||
--_depth;
|
||||
};
|
||||
|
||||
// TBB won't oversubscribe so we need to limit by ncores - 1
|
||||
size_t nitr = 0;
|
||||
auto _fname = __FUNCTION__;
|
||||
auto _write_info = [&]() {
|
||||
std::cout << "[" << _fname << "]> Total executed: " << _total_exec
|
||||
<< ", expected: " << _num << ", size: " << size() << std::endl;
|
||||
};
|
||||
while(_total_exec < _num)
|
||||
{
|
||||
auto _n = 2 * _num;
|
||||
while(--_n > 0)
|
||||
{
|
||||
_arena->execute(
|
||||
[&]() { m_tbb_task_group->run([&]() { _exec_task(); }); });
|
||||
}
|
||||
_arena->execute([&]() { m_tbb_task_group->wait(); });
|
||||
// don't loop infinitely but use a strict condition
|
||||
if(nitr++ > 2 * (_num + 1) && (_total_exec - 1) == _num)
|
||||
{
|
||||
_write_info();
|
||||
break;
|
||||
}
|
||||
// at this point we need to exit
|
||||
if(nitr > 8 * (_num + 1))
|
||||
{
|
||||
_write_info();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(get_verbose() > 3)
|
||||
_write_info();
|
||||
#endif
|
||||
}
|
||||
else if(get_queue())
|
||||
{
|
||||
get_queue()->ExecuteOnSpecificThreads(_tids, this, std::forward<FuncT>(_func));
|
||||
}
|
||||
}
|
||||
|
||||
//======================================================================================//
|
||||
|
||||
} // namespace PTL
|
||||
|
||||
+12
-80
@@ -28,6 +28,7 @@
|
||||
#include "PTL/Globals.hh"
|
||||
#include "PTL/Types.hh"
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <future>
|
||||
@@ -113,42 +114,12 @@ typedef int (*thread_unlock)(Mutex*);
|
||||
// a template class "Cache<T>" that required a static
|
||||
// mutex for specific to type T:
|
||||
// AutoLock l(TypeMutex<Cache<T>>());
|
||||
template <typename Tp>
|
||||
Mutex&
|
||||
template <typename Tp, typename MutexTp = Mutex, size_t N = 4>
|
||||
MutexTp&
|
||||
TypeMutex(const unsigned int& _n = 0)
|
||||
{
|
||||
static Mutex* _mutex = new Mutex();
|
||||
if(_n == 0)
|
||||
return *_mutex;
|
||||
|
||||
static std::vector<Mutex*> _mutexes;
|
||||
if(_n > _mutexes.size())
|
||||
_mutexes.resize(_n, nullptr);
|
||||
if(!_mutexes[_n])
|
||||
_mutexes[_n] = new Mutex();
|
||||
return *(_mutexes[_n - 1]);
|
||||
}
|
||||
|
||||
// Helper function for getting a unique static recursive_mutex for a
|
||||
// specific class or type
|
||||
// Usage example:
|
||||
// a template class "Cache<T>" that required a static
|
||||
// recursive_mutex for specific to type T:
|
||||
// RecursiveAutoLock l(TypeRecursiveMutex<Cache<T>>());
|
||||
template <typename Tp>
|
||||
RecursiveMutex&
|
||||
TypeRecursiveMutex(const unsigned int& _n = 0)
|
||||
{
|
||||
static RecursiveMutex* _mutex = new RecursiveMutex();
|
||||
if(_n == 0)
|
||||
return *(_mutex);
|
||||
|
||||
static std::vector<RecursiveMutex*> _mutexes;
|
||||
if(_n > _mutexes.size())
|
||||
_mutexes.resize(_n, nullptr);
|
||||
if(!_mutexes[_n])
|
||||
_mutexes[_n] = new RecursiveMutex();
|
||||
return *(_mutexes[_n - 1]);
|
||||
static std::array<MutexTp, N> _mutex_array{};
|
||||
return _mutex_array[_n % N];
|
||||
}
|
||||
|
||||
//======================================================================================//
|
||||
@@ -157,47 +128,14 @@ TypeRecursiveMutex(const unsigned int& _n = 0)
|
||||
typedef std::thread Thread;
|
||||
typedef std::thread::native_handle_type NativeThread;
|
||||
|
||||
// mutex macros
|
||||
#define MUTEXLOCK(mutex) \
|
||||
{ \
|
||||
(mutex)->lock(); \
|
||||
}
|
||||
#define MUTEXUNLOCK(mutex) \
|
||||
{ \
|
||||
(mutex)->unlock(); \
|
||||
}
|
||||
|
||||
// Macro to join thread
|
||||
#define THREADJOIN(worker) (worker).join()
|
||||
|
||||
// std::thread::id does not cast to integer
|
||||
typedef std::thread::id Pid_t;
|
||||
|
||||
// Instead of previous macro taking one argument, define function taking
|
||||
// unlimited arguments
|
||||
template <typename WorkerT, typename FuncT, typename... Args>
|
||||
void
|
||||
THREADCREATE(WorkerT*& worker, FuncT func, Args... args)
|
||||
{
|
||||
*worker = Thread(func, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// Conditions
|
||||
//
|
||||
// See MTRunManager for example on how to use these
|
||||
//
|
||||
// Condition
|
||||
typedef std::condition_variable Condition;
|
||||
#define CONDITION_INITIALIZER \
|
||||
{}
|
||||
#define CONDITIONWAIT(cond, lock) (cond)->wait(*lock);
|
||||
#define CONDITIONWAITLAMBDA(cond, lock, lambda) (cond)->wait(*lock, lambda);
|
||||
#define CONDITIONNOTIFY(cond) (cond)->notify_one();
|
||||
#define CONDITIONBROADCAST(cond) (cond)->notify_all();
|
||||
//
|
||||
|
||||
//======================================================================================//
|
||||
|
||||
// Define here after Thread has been typedef
|
||||
// Thread identifier
|
||||
typedef Thread::id ThreadId;
|
||||
|
||||
//======================================================================================//
|
||||
@@ -214,24 +152,18 @@ enum
|
||||
|
||||
Pid_t
|
||||
GetPidId();
|
||||
|
||||
unsigned
|
||||
GetNumberOfCores();
|
||||
|
||||
int
|
||||
GetThreadId();
|
||||
bool
|
||||
IsWorkerThread();
|
||||
bool
|
||||
IsMasterThread();
|
||||
|
||||
void
|
||||
SetThreadId(int aNewValue);
|
||||
|
||||
bool
|
||||
SetPinAffinity(int idx, NativeThread& at);
|
||||
int
|
||||
WorkerThreadLeavesPool();
|
||||
int
|
||||
WorkerThreadJoinsPool();
|
||||
int
|
||||
GetNumberOfRunningWorkerThreads();
|
||||
} // namespace Threading
|
||||
|
||||
} // namespace Threading
|
||||
} // namespace PTL
|
||||
|
||||
+34
-80
@@ -52,10 +52,22 @@
|
||||
# define PTL_DLL
|
||||
#endif
|
||||
|
||||
#if !defined(PTL_DEFAULT_OBJECT)
|
||||
# define PTL_DEFAULT_OBJECT(NAME) \
|
||||
NAME() = default; \
|
||||
~NAME() = default; \
|
||||
NAME(const NAME&) = default; \
|
||||
NAME(NAME&&) = default; \
|
||||
NAME& operator=(const NAME&) = default; \
|
||||
NAME& operator=(NAME&&) = default;
|
||||
#endif
|
||||
|
||||
#include <atomic>
|
||||
#include <complex>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
namespace PTL
|
||||
{
|
||||
@@ -107,99 +119,41 @@ GetSharedPointerPairMasterInstance()
|
||||
return _inst;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
//======================================================================================//
|
||||
|
||||
template <typename CountedType>
|
||||
class CountedObject
|
||||
struct ScopeDestructor
|
||||
{
|
||||
public:
|
||||
typedef CountedObject<CountedType> this_type;
|
||||
typedef CountedObject<void> void_type;
|
||||
template <typename FuncT>
|
||||
ScopeDestructor(FuncT&& _func)
|
||||
: m_functor(std::forward<FuncT>(_func))
|
||||
{}
|
||||
|
||||
public:
|
||||
// return number of existing objects:
|
||||
static int64_t live() { return count(); }
|
||||
static constexpr int64_t zero() { return static_cast<int64_t>(0); }
|
||||
static int64_t max_depth() { return fmax_depth; }
|
||||
// delete copy operations
|
||||
ScopeDestructor(const ScopeDestructor&) = delete;
|
||||
ScopeDestructor& operator=(const ScopeDestructor&) = delete;
|
||||
|
||||
static void enable(const bool& val) { fenabled = val; }
|
||||
static void set_max_depth(const int64_t& val) { fmax_depth = val; }
|
||||
static bool is_enabled() { return fenabled; }
|
||||
|
||||
template <typename Tp = CountedType,
|
||||
typename std::enable_if<std::is_same<Tp, void>::value>::type* = nullptr>
|
||||
static bool enable()
|
||||
// allow move operations
|
||||
ScopeDestructor(ScopeDestructor&& rhs) noexcept
|
||||
: m_functor(std::move(rhs.m_functor))
|
||||
{
|
||||
return fenabled && fmax_depth > count();
|
||||
rhs.m_functor = []() {};
|
||||
}
|
||||
// the void type is consider the global setting
|
||||
template <typename Tp = CountedType,
|
||||
typename std::enable_if<!std::is_same<Tp, void>::value>::type* = nullptr>
|
||||
static bool enable()
|
||||
ScopeDestructor& operator=(ScopeDestructor&& rhs) noexcept
|
||||
{
|
||||
return void_type::is_enabled() && void_type::max_depth() > count() && fenabled &&
|
||||
fmax_depth > count();
|
||||
if(this != &rhs)
|
||||
{
|
||||
m_functor = std::move(rhs.m_functor);
|
||||
rhs.m_functor = []() {};
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
protected:
|
||||
// default constructor
|
||||
CountedObject() { ++count(); }
|
||||
~CountedObject() { --count(); }
|
||||
CountedObject(const this_type&) { ++count(); }
|
||||
explicit CountedObject(this_type&&) { ++count(); }
|
||||
~ScopeDestructor() { m_functor(); }
|
||||
|
||||
private:
|
||||
// number of existing objects
|
||||
static int64_t& thread_number();
|
||||
static int64_t& master_count();
|
||||
static int64_t& count();
|
||||
static int64_t fmax_depth;
|
||||
static bool fenabled;
|
||||
std::function<void()> m_functor = []() {};
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
template <typename CountedType>
|
||||
int64_t&
|
||||
CountedObject<CountedType>::thread_number()
|
||||
{
|
||||
static std::atomic<int64_t> _all_instance;
|
||||
static thread_local int64_t _instance = _all_instance++;
|
||||
return _instance;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
template <typename CountedType>
|
||||
int64_t&
|
||||
CountedObject<CountedType>::master_count()
|
||||
{
|
||||
static int64_t _instance = 0;
|
||||
return _instance;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
template <typename CountedType>
|
||||
int64_t&
|
||||
CountedObject<CountedType>::count()
|
||||
{
|
||||
if(thread_number() == 0)
|
||||
return master_count();
|
||||
static thread_local int64_t _instance = master_count();
|
||||
return _instance;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
template <typename CountedType>
|
||||
int64_t CountedObject<CountedType>::fmax_depth = std::numeric_limits<int64_t>::max();
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
template <typename CountedType>
|
||||
bool CountedObject<CountedType>::fenabled = true;
|
||||
|
||||
//======================================================================================//
|
||||
|
||||
} // namespace PTL
|
||||
|
||||
+4
-4
@@ -33,6 +33,7 @@
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <random>
|
||||
#include <set>
|
||||
@@ -41,13 +42,12 @@
|
||||
namespace PTL
|
||||
{
|
||||
class VTask;
|
||||
class VTaskGroup;
|
||||
class TaskSubQueue; // definition in UserTaskQueue.icc
|
||||
|
||||
class UserTaskQueue : public VUserTaskQueue
|
||||
{
|
||||
public:
|
||||
typedef VTask* task_pointer;
|
||||
typedef std::shared_ptr<VTask> task_pointer;
|
||||
typedef std::vector<TaskSubQueue*> TaskSubQueueContainer;
|
||||
typedef std::default_random_engine random_engine_t;
|
||||
typedef std::uniform_int_distribution<int> int_dist_t;
|
||||
@@ -63,8 +63,8 @@ public:
|
||||
// Virtual function for getting a task from the queue
|
||||
virtual task_pointer GetTask(intmax_t subq = -1, intmax_t nitr = -1) override;
|
||||
// Virtual function for inserting a task into the queue
|
||||
virtual intmax_t InsertTask(task_pointer, ThreadData* = nullptr,
|
||||
intmax_t subq = -1) override;
|
||||
virtual intmax_t InsertTask(task_pointer&&, ThreadData* = nullptr,
|
||||
intmax_t subq = -1) override PTL_NO_SANITIZE_THREAD;
|
||||
|
||||
// if executing only tasks in threads bin
|
||||
task_pointer GetThreadBinTask();
|
||||
|
||||
+23
-10
@@ -34,6 +34,7 @@
|
||||
#include <cassert>
|
||||
#include <deque>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <stack>
|
||||
|
||||
@@ -46,10 +47,10 @@ class VTask;
|
||||
class TaskSubQueue
|
||||
{
|
||||
public:
|
||||
template <typename _Tp>
|
||||
using container = std::deque<_Tp>;
|
||||
template <typename Tp>
|
||||
using container = std::list<Tp>;
|
||||
|
||||
typedef VTask* task_pointer;
|
||||
typedef std::shared_ptr<VTask> task_pointer;
|
||||
typedef container<task_pointer> container_type;
|
||||
typedef container_type::size_type size_type;
|
||||
|
||||
@@ -66,13 +67,17 @@ public:
|
||||
bool AcquireClaim();
|
||||
void ReleaseClaim();
|
||||
|
||||
void PushTask(task_pointer);
|
||||
task_pointer PopTask(bool front = true);
|
||||
void PushTask(task_pointer&&) PTL_NO_SANITIZE_THREAD;
|
||||
task_pointer PopTask(bool front = true) PTL_NO_SANITIZE_THREAD;
|
||||
|
||||
size_type size() const;
|
||||
bool empty() const;
|
||||
|
||||
private:
|
||||
// mutex
|
||||
#if defined(PTL_USE_LOCKS)
|
||||
Mutex m_mutex{};
|
||||
#endif
|
||||
// used internally to keep number of tasks
|
||||
std::atomic<size_type> m_ntasks;
|
||||
// for checking if being modified
|
||||
@@ -144,13 +149,15 @@ TaskSubQueue::empty() const
|
||||
//======================================================================================//
|
||||
|
||||
inline void
|
||||
TaskSubQueue::PushTask(task_pointer task)
|
||||
TaskSubQueue::PushTask(task_pointer&& task)
|
||||
{
|
||||
// no need to lock these if claim is acquired via atomic
|
||||
assert(m_available.load(std::memory_order_relaxed) == false);
|
||||
//++(*m_all_tasks); already incremented
|
||||
++m_ntasks;
|
||||
m_task_queue.push_front(task);
|
||||
#if defined(PTL_USE_LOCKS)
|
||||
AutoLock lk{ m_mutex };
|
||||
#endif
|
||||
m_task_queue.emplace_front(std::move(task));
|
||||
}
|
||||
|
||||
//======================================================================================//
|
||||
@@ -160,17 +167,23 @@ TaskSubQueue::PopTask(bool front)
|
||||
{
|
||||
// no need to lock -- claim is acquired via atomic
|
||||
assert(m_available.load(std::memory_order_relaxed) == false);
|
||||
if(m_task_queue.empty())
|
||||
if(m_ntasks.load() == 0)
|
||||
return nullptr;
|
||||
|
||||
task_pointer _task;
|
||||
task_pointer _task{ nullptr };
|
||||
if(front)
|
||||
{
|
||||
#if defined(PTL_USE_LOCKS)
|
||||
AutoLock lk{ m_mutex };
|
||||
#endif
|
||||
_task = std::move(m_task_queue.front());
|
||||
m_task_queue.pop_front();
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined(PTL_USE_LOCKS)
|
||||
AutoLock lk{ m_mutex };
|
||||
#endif
|
||||
_task = std::move(m_task_queue.back());
|
||||
m_task_queue.pop_back();
|
||||
}
|
||||
|
||||
+16
-36
@@ -30,7 +30,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "PTL/AutoLock.hh"
|
||||
#include "PTL/TaskAllocator.hh"
|
||||
#include "PTL/Globals.hh"
|
||||
#include "PTL/Threading.hh"
|
||||
|
||||
#include <atomic>
|
||||
@@ -45,7 +45,6 @@
|
||||
|
||||
namespace PTL
|
||||
{
|
||||
class VTaskGroup;
|
||||
class ThreadPool;
|
||||
|
||||
//======================================================================================//
|
||||
@@ -56,51 +55,32 @@ class VTask
|
||||
public:
|
||||
typedef std::thread::id tid_type;
|
||||
typedef size_t size_type;
|
||||
typedef VTask this_type;
|
||||
typedef std::atomic_uintmax_t count_t;
|
||||
typedef VTask* iterator;
|
||||
typedef const VTask* const_iterator;
|
||||
typedef std::function<void()> void_func_t;
|
||||
|
||||
public:
|
||||
VTask();
|
||||
explicit VTask(VTaskGroup* task_group);
|
||||
explicit VTask(ThreadPool* pool);
|
||||
virtual ~VTask();
|
||||
VTask(bool _is_native, intmax_t _depth);
|
||||
|
||||
VTask() = default;
|
||||
virtual ~VTask() = default;
|
||||
|
||||
VTask(const VTask&) = delete;
|
||||
VTask& operator=(const VTask&) = delete;
|
||||
|
||||
VTask(VTask&&) = default;
|
||||
VTask& operator=(VTask&&) = default;
|
||||
|
||||
public:
|
||||
// execution operator
|
||||
virtual void operator()() = 0;
|
||||
|
||||
public:
|
||||
// used by thread_pool
|
||||
void operator--();
|
||||
virtual bool is_native_task() const;
|
||||
virtual ThreadPool* pool() const;
|
||||
VTaskGroup* group() const { return m_group; }
|
||||
|
||||
public:
|
||||
// used by task tree
|
||||
iterator begin() { return this; }
|
||||
iterator end() { return this + 1; }
|
||||
|
||||
const_iterator begin() const { return this; }
|
||||
const_iterator end() const { return this + 1; }
|
||||
|
||||
const_iterator cbegin() const { return this; }
|
||||
const_iterator cend() const { return this + 1; }
|
||||
|
||||
intmax_t& depth() { return m_depth; }
|
||||
const intmax_t& depth() const { return m_depth; }
|
||||
bool is_native_task() const { return m_is_native; }
|
||||
intmax_t depth() const { return m_depth; }
|
||||
|
||||
protected:
|
||||
static tid_type this_tid() { return std::this_thread::get_id(); }
|
||||
|
||||
protected:
|
||||
intmax_t m_depth;
|
||||
VTaskGroup* m_group;
|
||||
ThreadPool* m_pool;
|
||||
void_func_t m_func = []() {};
|
||||
bool m_is_native = false;
|
||||
intmax_t m_depth = 0;
|
||||
void_func_t m_func = []() {};
|
||||
};
|
||||
|
||||
//======================================================================================//
|
||||
|
||||
-155
@@ -1,155 +0,0 @@
|
||||
//
|
||||
// MIT License
|
||||
// Copyright (c) 2020 Jonathan R. Madsen
|
||||
// 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
|
||||
// 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.
|
||||
//
|
||||
// ---------------------------------------------------------------
|
||||
// Tasking class header file
|
||||
//
|
||||
// Class Description:
|
||||
//
|
||||
// This file creates an abstract base class for the grouping the thread-pool
|
||||
// tasking system into independently joinable units
|
||||
//
|
||||
// ---------------------------------------------------------------
|
||||
// Author: Jonathan Madsen (Feb 13th 2018)
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "PTL/AutoLock.hh"
|
||||
#include "PTL/Threading.hh"
|
||||
#include "PTL/VTask.hh"
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
|
||||
#include <deque>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace PTL
|
||||
{
|
||||
class ThreadPool;
|
||||
|
||||
class VTaskGroup
|
||||
{
|
||||
public:
|
||||
template <typename Tp>
|
||||
using container_type = std::vector<Tp>;
|
||||
template <typename Tp>
|
||||
using list_type = std::vector<Tp>;
|
||||
|
||||
typedef VTaskGroup this_type;
|
||||
typedef std::thread::id tid_type;
|
||||
typedef VTask task_type;
|
||||
typedef uintmax_t size_type;
|
||||
typedef Mutex lock_t;
|
||||
typedef std::atomic_intmax_t atomic_int;
|
||||
typedef std::atomic_uintmax_t atomic_uint;
|
||||
typedef Condition condition_t;
|
||||
typedef task_type* task_pointer;
|
||||
typedef container_type<task_pointer> vtask_list_type;
|
||||
|
||||
public:
|
||||
// Constructor and Destructors
|
||||
explicit VTaskGroup(ThreadPool* tp = nullptr);
|
||||
// Virtual destructors are required by abstract classes
|
||||
// so add it by default, just in case
|
||||
virtual ~VTaskGroup();
|
||||
|
||||
VTaskGroup(const this_type&) = delete;
|
||||
VTaskGroup(this_type&& rhs) = default;
|
||||
|
||||
this_type& operator=(const this_type&) = delete;
|
||||
this_type& operator=(this_type&& rhs) = default;
|
||||
|
||||
public:
|
||||
//------------------------------------------------------------------------//
|
||||
// wait to finish
|
||||
virtual void wait();
|
||||
|
||||
//------------------------------------------------------------------------//
|
||||
// increment (prefix)
|
||||
intmax_t operator++() { return ++(*m_tot_task_count); }
|
||||
intmax_t operator++(int) { return (*m_tot_task_count)++; }
|
||||
//------------------------------------------------------------------------//
|
||||
// increment (prefix)
|
||||
intmax_t operator--() { return --(*m_tot_task_count); }
|
||||
intmax_t operator--(int) { return (*m_tot_task_count)--; }
|
||||
//------------------------------------------------------------------------//
|
||||
// size
|
||||
intmax_t size() const { return m_tot_task_count->load(); }
|
||||
|
||||
// get the locks/conditions
|
||||
std::shared_ptr<condition_t> task_cond() { return m_task_cond; }
|
||||
|
||||
// identifier
|
||||
const uintmax_t& id() const { return m_id; }
|
||||
|
||||
// thread pool
|
||||
void set_pool(ThreadPool* tp) { m_pool = tp; }
|
||||
ThreadPool*& pool() { return m_pool; }
|
||||
ThreadPool* pool() const { return m_pool; }
|
||||
|
||||
void clear();
|
||||
virtual bool is_native_task_group() const { return true; }
|
||||
virtual bool is_master() const { return this_tid() == m_main_tid; }
|
||||
|
||||
//------------------------------------------------------------------------//
|
||||
// check if any tasks are still pending
|
||||
virtual intmax_t pending() { return m_tot_task_count->load(); }
|
||||
|
||||
static void set_verbose(int level) { f_verbose = level; }
|
||||
|
||||
protected:
|
||||
//------------------------------------------------------------------------//
|
||||
// get the thread id
|
||||
static tid_type this_tid() { return std::this_thread::get_id(); }
|
||||
|
||||
//------------------------------------------------------------------------//
|
||||
// get the task count
|
||||
atomic_int& task_count() { return *m_tot_task_count; }
|
||||
const atomic_int& task_count() const { return *m_tot_task_count; }
|
||||
|
||||
protected:
|
||||
// Private variables
|
||||
uintmax_t m_id;
|
||||
ThreadPool* m_pool;
|
||||
std::shared_ptr<atomic_int> m_tot_task_count = std::make_shared<atomic_int>(0);
|
||||
std::shared_ptr<condition_t> m_task_cond = std::make_shared<condition_t>();
|
||||
std::shared_ptr<lock_t> m_task_lock = std::make_shared<lock_t>();
|
||||
tid_type m_main_tid;
|
||||
vtask_list_type vtask_list;
|
||||
static int f_verbose;
|
||||
};
|
||||
|
||||
inline void
|
||||
VTaskGroup::clear()
|
||||
{
|
||||
for(auto& itr : vtask_list)
|
||||
delete itr;
|
||||
vtask_list.clear();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
} // namespace PTL
|
||||
+9
-112
@@ -32,6 +32,7 @@
|
||||
#include "PTL/Types.hh"
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
@@ -40,25 +41,24 @@
|
||||
namespace PTL
|
||||
{
|
||||
class VTask;
|
||||
class VTaskGroup;
|
||||
class ThreadPool;
|
||||
class ThreadData;
|
||||
|
||||
class VUserTaskQueue
|
||||
{
|
||||
public:
|
||||
typedef VTask* task_pointer;
|
||||
typedef std::atomic<intmax_t> AtomicInt;
|
||||
typedef uintmax_t size_type;
|
||||
typedef std::function<void()> function_type;
|
||||
typedef std::set<ThreadId> ThreadIdSet;
|
||||
typedef std::shared_ptr<VTask> task_pointer;
|
||||
typedef std::atomic<intmax_t> AtomicInt;
|
||||
typedef uintmax_t size_type;
|
||||
typedef std::function<void()> function_type;
|
||||
typedef std::set<ThreadId> ThreadIdSet;
|
||||
|
||||
public:
|
||||
// Constructor - accepting the number of workers
|
||||
explicit VUserTaskQueue(intmax_t nworkers = -1);
|
||||
// Virtual destructors are required by abstract classes
|
||||
// so add it by default, just in case
|
||||
virtual ~VUserTaskQueue();
|
||||
virtual ~VUserTaskQueue() = default;
|
||||
|
||||
public:
|
||||
// Virtual function for getting a task from the queue
|
||||
@@ -75,8 +75,8 @@ public:
|
||||
// 2. int - sub-queue to inserting into
|
||||
// return:
|
||||
// int - subqueue inserted into
|
||||
virtual intmax_t InsertTask(task_pointer, ThreadData* = nullptr,
|
||||
intmax_t subq = -1) = 0;
|
||||
virtual intmax_t InsertTask(task_pointer&&, ThreadData* = nullptr,
|
||||
intmax_t subq = -1) PTL_NO_SANITIZE_THREAD = 0;
|
||||
|
||||
// Overload this function to hold threads
|
||||
virtual void Wait() = 0;
|
||||
@@ -105,109 +105,6 @@ public:
|
||||
|
||||
virtual VUserTaskQueue* clone() = 0;
|
||||
|
||||
// operator for number of tasks
|
||||
// prefix versions
|
||||
// virtual uintmax_t operator++() = 0;
|
||||
// virtual uintmax_t operator--() = 0;
|
||||
// postfix versions
|
||||
// virtual uintmax_t operator++(int) = 0;
|
||||
// virtual uintmax_t operator--(int) = 0;
|
||||
|
||||
public:
|
||||
template <typename ContainerT, size_t... Idx>
|
||||
static auto ContainerToTupleImpl(ContainerT&& container, mpl::index_sequence<Idx...>)
|
||||
-> decltype(std::make_tuple(std::forward<ContainerT>(container)[Idx]...))
|
||||
{
|
||||
return std::make_tuple(std::forward<ContainerT>(container)[Idx]...);
|
||||
}
|
||||
|
||||
template <std::size_t N, typename ContainerT>
|
||||
static auto ContainerToTuple(ContainerT&& container)
|
||||
-> decltype(ContainerToTupleImpl(std::forward<ContainerT>(container),
|
||||
mpl::make_index_sequence<N>{}))
|
||||
{
|
||||
return ContainerToTupleImpl(std::forward<ContainerT>(container),
|
||||
mpl::make_index_sequence<N>{});
|
||||
}
|
||||
|
||||
template <std::size_t N, std::size_t Nt, typename TupleT,
|
||||
enable_if_t<(N == Nt), int> = 0>
|
||||
static void TExecutor(TupleT&& _t)
|
||||
{
|
||||
if(std::get<N>(_t).get())
|
||||
(*(std::get<N>(_t)))();
|
||||
}
|
||||
|
||||
template <std::size_t N, std::size_t Nt, typename TupleT,
|
||||
enable_if_t<(N < Nt), int> = 0>
|
||||
static void TExecutor(TupleT&& _t)
|
||||
{
|
||||
if(std::get<N>(_t).get())
|
||||
(*(std::get<N>(_t)))();
|
||||
TExecutor<N + 1, Nt, TupleT>(std::forward<TupleT>(_t));
|
||||
}
|
||||
|
||||
template <typename TupleT, std::size_t N = std::tuple_size<decay_t<TupleT>>::value>
|
||||
static void Executor(TupleT&& __t)
|
||||
{
|
||||
TExecutor<0, N - 1, TupleT>(std::forward<TupleT>(__t));
|
||||
}
|
||||
|
||||
template <typename Container,
|
||||
typename std::enable_if<std::is_same<Container, task_pointer>::value,
|
||||
int>::type = 0>
|
||||
static void Execute(Container& obj)
|
||||
{
|
||||
if(obj.get())
|
||||
(*obj)();
|
||||
}
|
||||
|
||||
template <typename Container,
|
||||
typename std::enable_if<!std::is_same<Container, task_pointer>::value,
|
||||
int>::type = 0>
|
||||
static void Execute(Container& tasks)
|
||||
{
|
||||
/*
|
||||
for(auto& itr : tasks)
|
||||
{
|
||||
if(itr.get())
|
||||
(*itr)();
|
||||
}*/
|
||||
|
||||
size_type n = tasks.size();
|
||||
size_type max_n = 4;
|
||||
while(n > 0)
|
||||
{
|
||||
auto compute = (n > max_n) ? max_n : n;
|
||||
switch(compute)
|
||||
{
|
||||
case 4: {
|
||||
auto t = ContainerToTuple<4>(tasks);
|
||||
Executor(t);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
auto t = ContainerToTuple<3>(tasks);
|
||||
Executor(t);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
auto t = ContainerToTuple<2>(tasks);
|
||||
Executor(t);
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
auto t = ContainerToTuple<1>(tasks);
|
||||
Executor(t);
|
||||
break;
|
||||
}
|
||||
case 0: break;
|
||||
}
|
||||
// tasks.erase(tasks.begin(), tasks.begin() + compute);
|
||||
n -= compute;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
intmax_t m_workers = 0;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user