12
12
#define MRDOX_SUPPORT_EXECUTORGROUP_HPP
13
13
14
14
#include < mrdox/Platform.hpp>
15
+ #include < mrdox/Support/any_callable.hpp>
15
16
#include < mrdox/Support/ThreadPool.hpp>
16
- #include < mrdox/Support/unlock_guard.hpp>
17
- #include < condition_variable>
18
17
#include < deque>
18
+ #include < memory>
19
19
#include < mutex>
20
20
#include < vector>
21
21
@@ -25,48 +25,65 @@ namespace mrdox {
25
25
class MRDOX_DECL
26
26
ExecutorGroupBase
27
27
{
28
+ class scoped_agent ;
29
+
28
30
protected:
31
+ struct Impl ;
32
+
29
33
struct MRDOX_DECL
30
34
AnyAgent
31
35
{
32
36
virtual ~AnyAgent () = 0 ;
33
37
virtual void * get () noexcept = 0;
34
38
};
35
39
40
+ std::unique_ptr<Impl> impl_;
41
+ std::vector<std::unique_ptr<AnyAgent>> agents_;
42
+ std::deque<any_callable<void (void *)>> work_;
43
+
44
+ explicit ExecutorGroupBase (ThreadPool&);
45
+ void post (any_callable<void (void *)>);
46
+ void run (std::unique_lock<std::mutex>);
47
+
36
48
public:
49
+ template <class T >
50
+ using arg_t = ThreadPool::arg_t <T>;
51
+
52
+ ~ExecutorGroupBase ();
53
+ ExecutorGroupBase (ExecutorGroupBase&&) noexcept ;
54
+
55
+ /* * Block until all work has completed.
56
+ */
57
+ void
58
+ wait () noexcept ;
37
59
};
38
60
39
61
/* * A set of execution agents for performing concurrent work.
40
62
*/
41
63
template <class Agent >
42
64
class ExecutorGroup : public ExecutorGroupBase
43
65
{
44
- struct Impl
66
+ struct AgentImpl : AnyAgent
45
67
{
46
- std::mutex mutex_;
47
- std::condition_variable cv_;
48
- };
68
+ Agent agent_;
49
69
50
- ThreadPool& threadPool_;
51
- std::unique_ptr<Impl> impl_;
52
- std::vector<std::unique_ptr<Agent>> agents_;
53
- std::deque<any_callable<void (Agent&)>> work_;
54
- std::size_t busy_ = 0 ;
55
-
56
- public:
57
- template <class T >
58
- using arg_t = ThreadPool::arg_t <T>;
70
+ template <class ... Args>
71
+ AgentImpl (Args&&... args)
72
+ : agent_(std::forward<Args>(args)...)
73
+ {
74
+ }
59
75
60
- ExecutorGroup (ExecutorGroup const &) = delete ;
61
- ExecutorGroup& operator =(ExecutorGroup&&) = delete ;
62
- ExecutorGroup& operator =(ExecutorGroup const &) = delete ;
63
- ExecutorGroup (ExecutorGroup&&) = default ;
76
+ void * get () noexcept override
77
+ {
78
+ return &agent_;
79
+ }
80
+ };
64
81
82
+ public:
65
83
explicit
66
84
ExecutorGroup (
67
- ThreadPool& threadPool) noexcept
68
- : threadPool_(threadPool)
69
- , impl_(std::make_unique<Impl>())
85
+ ThreadPool& threadPool)
86
+ : ExecutorGroupBase(threadPool)
70
87
{
71
88
}
72
89
@@ -79,8 +96,9 @@ class ExecutorGroup : public ExecutorGroupBase
79
96
void
80
97
emplace (Args&&... args)
81
98
{
82
- agents_.emplace_back (std::make_unique<Agent>(
83
- std::forward<Args>(args)...));
99
+ agents_.emplace_back (
100
+ std::make_unique<AgentImpl>(
101
+ std::forward<Args>(args)...));
84
102
}
85
103
86
104
/* * Submit work to be executed.
@@ -96,86 +114,17 @@ class ExecutorGroup : public ExecutorGroupBase
96
114
async (F&& f, Args&&... args)
97
115
{
98
116
static_assert (std::is_invocable_v<F, Agent&, arg_t <Args>...>);
99
- std::unique_lock<std::mutex> lock (impl_->mutex_ );
100
- work_.emplace_back (
117
+ post (
101
118
[
102
119
f = std::forward<F>(f),
103
120
args = std::tuple<arg_t <Args>...>(args...)
104
- ](Agent& agent)
121
+ ](void * agent)
105
122
{
106
- std::apply (f, std::tuple_cat (
107
- std::tuple<Agent&>(agent),
123
+ std::apply (f,
124
+ std::tuple_cat (std::tuple<Agent&>(
125
+ *reinterpret_cast <Agent*>(agent)),
108
126
std::move (args)));
109
127
});
110
- if (agents_.empty ())
111
- return ;
112
- run (std::move (lock));
113
- }
114
-
115
- /* * Block until all work has completed.
116
- */
117
- void
118
- wait ()
119
- {
120
- std::unique_lock<std::mutex> lock (impl_->mutex_ );
121
- impl_->cv_ .wait (lock,
122
- [&]
123
- {
124
- return work_.empty () && busy_ == 0 ;
125
- });
126
- }
127
-
128
- private:
129
- class scoped_agent
130
- {
131
- ExecutorGroup& group_;
132
- std::unique_ptr<Agent> agent_;
133
-
134
- public:
135
- scoped_agent (
136
- ExecutorGroup& group,
137
- std::unique_ptr<Agent> agent) noexcept
138
- : group_(group)
139
- , agent_(std::move(agent))
140
- {
141
- }
142
-
143
- ~scoped_agent ()
144
- {
145
- --group_.busy_ ;
146
- group_.agents_ .emplace_back (std::move (agent_));
147
- group_.impl_ ->cv_ .notify_all ();
148
- }
149
-
150
- Agent& operator *() const noexcept
151
- {
152
- return *agent_;
153
- }
154
- };
155
-
156
- void
157
- run (std::unique_lock<std::mutex> lock)
158
- {
159
- std::unique_ptr<Agent> agent (std::move (agents_.back ()));
160
- agents_.pop_back ();
161
- ++busy_;
162
-
163
- threadPool_.async (
164
- [this , agent = std::move (agent)]() mutable
165
- {
166
- std::unique_lock<std::mutex> lock (impl_->mutex_ );
167
- scoped_agent scope (*this , std::move (agent));
168
- for (;;)
169
- {
170
- if (work_.empty ())
171
- break ;
172
- any_callable<void (Agent&)> work (
173
- std::move (work_.front ()));
174
- work_.pop_front ();
175
- unlock_guard unlock (impl_->mutex_ );
176
- work (*scope);
177
- }
178
- });
179
128
}
180
129
};
181
130
0 commit comments