Skip to content

Commit 58fcb2f

Browse files
authored
Merge branch 'PokemonAutomation:main' into main
2 parents d13f531 + 3d41596 commit 58fcb2f

67 files changed

Lines changed: 465 additions & 292 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Common/Cpp/Concurrency/AsyncTask.cpp

Lines changed: 46 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,14 @@
44
*
55
*/
66

7+
#include <atomic>
8+
#include <exception>
9+
#include "Common/Cpp/Containers/Pimpl.tpp"
710
#include "SpinPause.h"
11+
#include "Mutex.h"
12+
#include "ConditionVariable.h"
813
#include "AsyncTask.h"
14+
#include "AsyncTaskCore.h"
915

1016
//#include <iostream>
1117
//using std::cout;
@@ -15,54 +21,55 @@ namespace PokemonAutomation{
1521

1622

1723

24+
25+
AsyncTask::AsyncTask(AsyncTask&&) = default;
26+
AsyncTask& AsyncTask::operator=(AsyncTask&&) = default;
27+
28+
29+
AsyncTask::AsyncTask()
30+
: m_sanitizer("AsyncTask")
31+
{}
32+
AsyncTask::AsyncTask(std::function<void()> task)
33+
: m_core(CONSTRUCT_TOKEN, std::move(task))
34+
, m_sanitizer("AsyncTask")
35+
{}
36+
1837
AsyncTask::~AsyncTask(){
19-
State state = m_state.load(std::memory_order_acquire);
20-
if (state == State::NOT_STARTED || state == State::SAFE_TO_DESTRUCT){
21-
// cout << "Already Done: " << (int)state << endl;
22-
return;
23-
}
24-
25-
{
26-
std::unique_lock<Mutex> lg(m_lock);
27-
m_cv.wait(lg, [this]{
28-
return m_state.load(std::memory_order_relaxed) != State::RUNNING;
29-
});
30-
}
31-
32-
while (m_state.load(std::memory_order_acquire) != State::SAFE_TO_DESTRUCT){
33-
pause();
34-
}
35-
36-
// cout << "Late Finish" << endl;
38+
wait_and_ignore_exceptions();
3739
}
3840

3941

40-
void AsyncTask::report_cancelled() noexcept{
41-
{
42+
bool AsyncTask::is_finished() const noexcept{
4243
#ifdef PA_SANITIZE_AsyncTask
43-
auto scope = m_sanitizer.check_scope();
44+
auto scope = m_sanitizer.check_scope();
4445
#endif
45-
m_state.store(State::FINISHED, std::memory_order_release);
46-
{
47-
std::lock_guard<Mutex> lg(m_lock);
48-
}
49-
m_cv.notify_all();
50-
}
51-
m_state.store(State::SAFE_TO_DESTRUCT, std::memory_order_release);
46+
return m_core->is_finished();
5247
}
53-
void AsyncTask::run() noexcept{
54-
{
48+
49+
void AsyncTask::wait_and_ignore_exceptions() noexcept{
50+
m_core.clear();
51+
}
52+
void AsyncTask::wait_and_rethrow_exceptions(){
5553
#ifdef PA_SANITIZE_AsyncTask
56-
auto scope = m_sanitizer.check_scope();
54+
auto scope = m_sanitizer.check_scope();
5755
#endif
58-
try{
59-
m_task();
60-
}catch (...){
61-
std::lock_guard<Mutex> lg(m_lock);
62-
m_exception = std::current_exception();
63-
}
64-
}
65-
report_cancelled();
56+
m_core->wait_and_rethrow_exceptions();
57+
}
58+
59+
60+
void AsyncTask::report_started(){
61+
#ifdef PA_SANITIZE_AsyncTask
62+
auto scope = m_sanitizer.check_scope();
63+
#endif
64+
m_core->report_started();
65+
}
66+
void AsyncTask::report_cancelled() noexcept{
67+
m_sanitizer.check_usage();
68+
m_core->report_cancelled();
69+
}
70+
void AsyncTask::run() noexcept{
71+
m_sanitizer.check_usage();
72+
m_core->run();
6673
}
6774

6875

Common/Cpp/Concurrency/AsyncTask.h

Lines changed: 21 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,7 @@
88
#define PokemonAutomation_AsyncTask_H
99

1010
#include <functional>
11-
#include <atomic>
12-
#include <exception>
13-
#include "Mutex.h"
14-
#include "ConditionVariable.h"
11+
#include "Common/Cpp/Containers/Pimpl.h"
1512

1613
#define PA_SANITIZE_AsyncTask
1714

@@ -21,72 +18,50 @@
2118

2219
namespace PokemonAutomation{
2320

21+
class AsyncTaskCore;
22+
2423

2524
class AsyncTask{
2625
public:
27-
enum class State{
28-
NOT_STARTED,
29-
RUNNING,
30-
FINISHED,
31-
SAFE_TO_DESTRUCT,
32-
};
33-
26+
AsyncTask(const AsyncTask&) = delete;
27+
void operator=(const AsyncTask&) = delete;
28+
AsyncTask(AsyncTask&&);
29+
AsyncTask& operator=(AsyncTask&&);
3430

35-
public:
3631
// If the task has already started, this will wait for it to finish.
3732
// This will not rethrow exceptions.
3833
~AsyncTask();
3934

4035

4136
public:
42-
template <class... Args>
43-
AsyncTask(Args&&... args)
44-
: m_task(std::forward<Args>(args)...)
45-
, m_state(State::NOT_STARTED)
46-
{}
37+
AsyncTask();
38+
AsyncTask(std::function<void()> task);
4739

48-
bool is_finished() const noexcept{
49-
#ifdef PA_SANITIZE_AsyncTask
50-
auto scope = m_sanitizer.check_scope();
51-
#endif
52-
State state = m_state.load(std::memory_order_acquire);
53-
return state == State::FINISHED || state == State::SAFE_TO_DESTRUCT;
40+
operator bool() const{
41+
return m_core;
42+
}
43+
AsyncTaskCore* core(){
44+
return m_core.get();
5445
}
46+
bool is_finished() const noexcept;
47+
48+
// Wait for the task to finish. Will ignore any exceptions.
49+
void wait_and_ignore_exceptions() noexcept;
5550

5651
// Wait for the task to finish. Will rethrow any exceptions.
57-
void wait_and_rethrow_exceptions(){
58-
#ifdef PA_SANITIZE_AsyncTask
59-
auto scope = m_sanitizer.check_scope();
60-
#endif
61-
if (!is_finished()){
62-
std::unique_lock<Mutex> lg(m_lock);
63-
m_cv.wait(lg, [this]{ return is_finished(); });
64-
}
65-
if (m_exception){
66-
std::rethrow_exception(m_exception);
67-
}
68-
}
52+
void wait_and_rethrow_exceptions();
6953

7054

7155
public:
7256
// These should only be called inside a parallel framework.
7357
// These are not thread-safe with each other.
74-
void report_started(){
75-
#ifdef PA_SANITIZE_AsyncTask
76-
auto scope = m_sanitizer.check_scope();
77-
#endif
78-
m_state.store(State::RUNNING, std::memory_order_release);
79-
}
58+
void report_started();
8059
void report_cancelled() noexcept;
8160
void run() noexcept;
8261

8362

8463
private:
85-
std::function<void()> m_task;
86-
std::atomic<State> m_state;
87-
std::exception_ptr m_exception;
88-
mutable Mutex m_lock;
89-
ConditionVariable m_cv;
64+
Pimpl<AsyncTaskCore> m_core;
9065

9166
#ifdef PA_SANITIZE_AsyncTask
9267
LifetimeSanitizer m_sanitizer;
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/* Async Task
2+
*
3+
* From: https://github.com/PokemonAutomation/
4+
*
5+
*/
6+
7+
#ifndef PokemonAutomation_AsyncTaskCore_H
8+
#define PokemonAutomation_AsyncTaskCore_H
9+
10+
#include <functional>
11+
#include <atomic>
12+
#include <exception>
13+
#include "SpinPause.h"
14+
#include "Mutex.h"
15+
#include "ConditionVariable.h"
16+
17+
namespace PokemonAutomation{
18+
19+
20+
21+
class AsyncTaskCore{
22+
public:
23+
enum class State{
24+
NOT_STARTED,
25+
RUNNING,
26+
FINISHED,
27+
SAFE_TO_DESTRUCT,
28+
};
29+
30+
31+
public:
32+
// If the task has already started, this will wait for it to finish.
33+
// This will not rethrow exceptions.
34+
~AsyncTaskCore(){
35+
State state = m_state.load(std::memory_order_acquire);
36+
if (state == State::NOT_STARTED || state == State::SAFE_TO_DESTRUCT){
37+
// cout << "Already Done: " << (int)state << endl;
38+
return;
39+
}
40+
41+
{
42+
std::unique_lock<Mutex> lg(m_lock);
43+
m_cv.wait(lg, [this]{
44+
return m_state.load(std::memory_order_relaxed) != State::RUNNING;
45+
});
46+
}
47+
48+
while (m_state.load(std::memory_order_acquire) != State::SAFE_TO_DESTRUCT){
49+
pause();
50+
}
51+
}
52+
AsyncTaskCore(std::function<void()> task)
53+
: m_task(std::move(task))
54+
, m_state(State::NOT_STARTED)
55+
{}
56+
57+
bool is_finished() const noexcept{
58+
State state = m_state.load(std::memory_order_acquire);
59+
return state == State::FINISHED || state == State::SAFE_TO_DESTRUCT;
60+
}
61+
62+
// Wait for the task to finish. Will rethrow any exceptions.
63+
void wait_and_rethrow_exceptions(){
64+
if (!is_finished()){
65+
std::unique_lock<Mutex> lg(m_lock);
66+
m_cv.wait(lg, [this]{ return is_finished(); });
67+
}
68+
if (m_exception){
69+
std::rethrow_exception(m_exception);
70+
}
71+
}
72+
73+
74+
public:
75+
// These should only be called inside a parallel framework.
76+
// These are not thread-safe with each other.
77+
void report_started(){
78+
m_state.store(State::RUNNING, std::memory_order_release);
79+
}
80+
void report_cancelled() noexcept{
81+
{
82+
m_state.store(State::FINISHED, std::memory_order_release);
83+
{
84+
std::lock_guard<Mutex> lg(m_lock);
85+
}
86+
m_cv.notify_all();
87+
}
88+
m_state.store(State::SAFE_TO_DESTRUCT, std::memory_order_release);
89+
}
90+
void run() noexcept{
91+
{
92+
try{
93+
m_task();
94+
}catch (...){
95+
std::lock_guard<Mutex> lg(m_lock);
96+
m_exception = std::current_exception();
97+
}
98+
}
99+
report_cancelled();
100+
}
101+
102+
103+
private:
104+
std::function<void()> m_task;
105+
std::atomic<State> m_state;
106+
107+
std::exception_ptr m_exception;
108+
109+
mutable Mutex m_lock;
110+
ConditionVariable m_cv;
111+
};
112+
113+
114+
115+
}
116+
#endif

Common/Cpp/Concurrency/PeriodicScheduler.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,9 +196,9 @@ void PeriodicRunner::thread_loop(){
196196
idle_since_last_check += end - start;
197197
}
198198
}
199-
void PeriodicRunner::stop_thread(){
199+
void PeriodicRunner::stop_thread() noexcept{
200200
PeriodicRunner::cancel(nullptr);
201-
m_runner.reset();
201+
m_runner.wait_and_ignore_exceptions();
202202
}
203203

204204
double PeriodicRunner::current_utilization() const{

Common/Cpp/Concurrency/PeriodicScheduler.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ class PeriodicRunner : public Cancellable{
8888
private:
8989
void thread_loop();
9090
protected:
91-
void stop_thread();
91+
void stop_thread() noexcept;
9292

9393
private:
9494
ThreadPool& m_thread_pool;
@@ -102,7 +102,7 @@ class PeriodicRunner : public Cancellable{
102102

103103
PeriodicScheduler m_scheduler;
104104

105-
std::unique_ptr<AsyncTask> m_runner;
105+
AsyncTask m_runner;
106106
};
107107

108108

Common/Cpp/Concurrency/ScheduledTaskRunner.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ ScheduledTaskRunner::ScheduledTaskRunner(ThreadPool& thread_pool)
2525
ScheduledTaskRunner::~ScheduledTaskRunner(){
2626
stop();
2727
}
28-
void ScheduledTaskRunner::stop(){
28+
void ScheduledTaskRunner::stop() noexcept{
2929
if (!m_runner){
3030
return;
3131
}
@@ -36,7 +36,7 @@ void ScheduledTaskRunner::stop(){
3636
m_stopped = true;
3737
}
3838
m_cv.notify_all();
39-
m_runner.reset();
39+
m_runner.wait_and_ignore_exceptions();
4040
// cout << "ScheduledTaskRunner: (Destructor - end): " << this << endl;
4141
}
4242

0 commit comments

Comments
 (0)