timemory 3.3.0
Modular C++ Toolkit for Performance Analysis and Logging. Profiling API and Tools for C, C++, CUDA, Fortran, and Python. The C++ template API is essentially a framework to creating tools: it is designed to provide a unifying interface for recording various performance measurements alongside data logging and interfaces to other tools.
async.hpp
Go to the documentation of this file.
1// MIT License
2//
3// Copyright (c) 2020, The Regents of the University of California,
4// through Lawrence Berkeley National Laboratory (subject to receipt of any
5// required approvals from the U.S. Dept. of Energy). All rights reserved.
6//
7// Permission is hereby granted, free of charge, to any person obtaining a copy
8// of this software and associated documentation files (the "Software"), to deal
9// in the Software without restriction, including without limitation the rights
10// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11// copies of the Software, and to permit persons to whom the Software is
12// furnished to do so, subject to the following conditions:
13//
14// The above copyright notice and this permission notice shall be included in all
15// copies or substantial portions of the Software.
16//
17// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23// SOFTWARE.
24
25#pragma once
26
30
31#include <tuple>
32
33namespace tim
34{
35namespace operation
36{
37//
38//--------------------------------------------------------------------------------------//
39//
40template <typename Tp>
41struct async
42{
43 using type = Tp;
44 using task_type = std::packaged_task<void()>;
46 using lock_type = std::unique_lock<std::mutex>;
47
48 explicit async(type& obj)
49 : m_data{ &obj }
50 , m_thread{ execute_thread, this }
51 {}
52
54 {
55 {
56 m_pool_state.store(false);
57 lock_type _task_lock{ m_task_lock, std::defer_lock };
58 if(!_task_lock.owns_lock())
59 _task_lock.lock();
60 m_task_cond.notify_one();
61 }
62 m_thread.join();
63 }
64
65 async(const async&) = delete;
66 async& operator=(const async&) = delete;
67
68 async(async&&) = default;
69 async& operator=(async&&) = default;
70
71 template <typename FuncT, typename... Args>
72 auto operator()(FuncT&& func, Args... args)
73 {
74 lock_type _task_lock{ m_task_lock };
75 enqueue(std::forward<FuncT>(func), 0, std::move(args)...);
76 ++m_task_size;
77 m_task_cond.notify_one();
78 }
79
80 void wait()
81 {
82 std::deque<std::future<void>> _wait{};
83 {
84 lock_type _task_lock{ m_task_lock, std::defer_lock };
85 std::swap(m_task_wait, _wait);
86 if(!_task_lock.owns_lock())
87 _task_lock.lock();
88 m_task_cond.notify_all();
89 }
90 for(auto& itr : _wait)
91 itr.wait();
92 }
93
94private:
95 static void execute_thread(this_type* _this)
96 {
97 while(_this->m_pool_state.load() || (_this->m_task_size.load() == 0))
98 {
99 // synchronization
100 lock_type _task_lock(_this->m_task_lock, std::defer_lock);
101
102 auto leave_pool = [&]() {
103 return !_this->m_pool_state.load() && (_this->m_task_size.load() == 0);
104 };
105
106 while(_this->m_task_size.load() == 0 && _this->m_pool_state.load())
107 {
108 // lock before sleeping on condition
109 if(!_task_lock.owns_lock())
110 _task_lock.lock();
111
112 // break out of loop
113 if(_this->m_task_size.load() > 0)
114 break;
115
116 // return from function
117 if(!_this->m_pool_state.load())
118 return;
119
120 // Wait until there is a task in the queue
121 // Unlocks mutex while waiting, then locks it back when signaled
122 // use lambda to control waking
123 _this->m_task_cond.wait(_task_lock, [&]() {
124 return (_this->m_task_size.load() > 0 || !_this->m_pool_state.load());
125 });
126 }
127
128 // leave pool if conditions dictate it
129 if(leave_pool())
130 return;
131
132 // acquire the lock
133 if(!_task_lock.owns_lock())
134 _task_lock.lock();
135
136 std::packaged_task<void()> _task{};
137 if(!_this->m_task_pool.empty() && _this->m_task_size.load() > 0)
138 {
139 --(_this->m_task_size);
140 _task = std::move(_this->m_task_pool.front());
141 _this->m_task_pool.pop_front();
142 }
143
144 // release the lock
145 if(_task_lock.owns_lock())
146 _task_lock.unlock();
147
148 // execute the task(s)
149 if(_task.valid())
150 {
151 _task();
152 }
153 }
154 }
155
156 template <typename FuncT, typename... Args>
157 auto enqueue(FuncT&& func, int, Args... args)
158 -> decltype(func(std::declval<type&>(), args...), void())
159 {
160 auto&& _fut =
161 m_task_pool
162 .emplace(m_task_pool.end(),
163 [=]() { std::move(func)(*m_data, std::move(args)...); })
164 ->get_future();
165
166 m_task_wait.emplace_back(std::move(_fut));
167 }
168
169 template <typename FuncT, typename... Args>
170 auto enqueue(FuncT&& func, long, Args... args)
171 -> decltype(func(std::declval<type*>(), args...), void())
172 {
173 auto&& _fut = m_task_pool
174 .emplace(m_task_pool.end(),
175 [=]() { std::move(func)(m_data, std::move(args)...); })
176 ->get_future();
177
178 m_task_wait.emplace_back(std::move(_fut));
179 }
180
181 template <typename FuncT, typename... Args>
182 auto enqueue(FuncT&& func, long long, Args... args) -> decltype(func(args...), void())
183 {
184 auto&& _fut = m_task_pool
185 .emplace(m_task_pool.end(),
186 [=]() { std::move(func)(std::move(args)...); })
187 ->get_future();
188 m_task_wait.emplace_back(std::move(_fut));
189 }
190
191private:
192 type* m_data = nullptr;
193 std::atomic<bool> m_pool_state{ true };
194 std::atomic<size_t> m_task_size{ 0 };
195 std::mutex m_task_lock{};
196 std::condition_variable m_task_cond{};
197 std::deque<std::packaged_task<void()>> m_task_pool = {};
198 std::deque<std::future<void>> m_task_wait = {};
199 std::thread m_thread;
200};
201//
202//--------------------------------------------------------------------------------------//
203//
204} // namespace operation
205} // namespace tim
Definition: kokkosp.cpp:39
The declaration for the types for operations without definitions.
Include the macros for operations.
Declare the operations types.
auto operator()(FuncT &&func, Args... args)
Definition: async.hpp:72
async(async &&)=default
async & operator=(async &&)=default
async(type &obj)
Definition: async.hpp:48
std::unique_lock< std::mutex > lock_type
Definition: async.hpp:46
async(const async &)=delete
async< Tp > this_type
Definition: async.hpp:45
async & operator=(const async &)=delete
std::packaged_task< void()> task_type
Definition: async.hpp:44