Skip to content

Commit f7d20f9

Browse files
committed
Non-trivial fix for a keyboard control shutdown deadlock.
1 parent 9fc9e7a commit f7d20f9

37 files changed

+274
-246
lines changed

Common/Cpp/CancellableScope.cpp

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <mutex>
1111
#include <condition_variable>
1212
#include "Exceptions.h"
13+
#include "ListenerSet.h"
1314
#include "Containers/Pimpl.tpp"
1415
#include "Concurrency/SpinLock.h"
1516
#include "CancellableScope.h"
@@ -28,9 +29,17 @@ struct CancellableData{
2829
std::atomic<bool> cancelled;
2930
mutable SpinLock lock;
3031
std::exception_ptr exception;
32+
ListenerSet<Cancellable::CancelListener> m_listeners;
3133
};
3234

3335

36+
void Cancellable::add_cancel_listener(CancelListener& listener){
37+
m_impl->m_listeners.add(listener);
38+
}
39+
void Cancellable::remove_cancel_listener(CancelListener& listener){
40+
m_impl->m_listeners.remove(listener);
41+
}
42+
3443

3544
Cancellable::Cancellable()
3645
: m_impl(CONSTRUCT_TOKEN)
@@ -56,10 +65,15 @@ bool Cancellable::cancel(std::exception_ptr exception) noexcept{
5665
if (exception && !data.exception){
5766
data.exception = std::move(exception);
5867
}
59-
if (data.cancelled.load(std::memory_order_acquire)){
68+
if (data.cancelled.load(std::memory_order_relaxed)){
6069
return true;
6170
}
62-
return data.cancelled.exchange(true, std::memory_order_relaxed);
71+
data.cancelled.store(true, std::memory_order_release);
72+
// if (data.cancelled.exchange(true, std::memory_order_relaxed)){
73+
// return true;
74+
// }
75+
m_impl->m_listeners.run_method_with_duplicates(&CancelListener::on_cancellable_cancel);
76+
return false;
6377
}
6478
void Cancellable::throw_if_cancelled() const{
6579
auto scope_check = m_sanitizer.check_scope();

Common/Cpp/CancellableScope.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,14 @@ struct CancellableData;
4545
class Cancellable{
4646
Cancellable(const Cancellable&) = delete;
4747
void operator=(const Cancellable&) = delete;
48+
public:
49+
struct CancelListener{
50+
virtual void on_cancellable_cancel() = 0;
51+
};
52+
void add_cancel_listener(CancelListener& listener);
53+
void remove_cancel_listener(CancelListener& listener);
54+
55+
4856
public:
4957
virtual ~Cancellable();
5058

Common/Cpp/ListenerSet.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ void ListenerSet<ListenerType>::run_method_with_duplicates(Function function, Ar
202202
ListenerType& listener = *item.first;
203203
size_t count = item.second;
204204
do{
205-
(listener->*function)(std::forward<Args>(args)...);
205+
(listener.*function)(std::forward<Args>(args)...);
206206
}while (--count);
207207
}
208208
}

SerialPrograms/Source/CommonFramework/Globals.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ namespace PokemonAutomation{
2626
const bool IS_BETA_VERSION = true;
2727
const int PROGRAM_VERSION_MAJOR = 0;
2828
const int PROGRAM_VERSION_MINOR = 60;
29-
const int PROGRAM_VERSION_PATCH = 5;
29+
const int PROGRAM_VERSION_PATCH = 6;
3030

3131
const std::string PROGRAM_VERSION_BASE =
3232
"v" + std::to_string(PROGRAM_VERSION_MAJOR) +

SerialPrograms/Source/Controllers/Controller.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -180,16 +180,16 @@ class AbstractController{
180180
// empty out rather than entering itself into the queue.
181181
// If a cancellation happens inside this function, it will immediately
182182
// throw an OperationCancelledException.
183-
virtual void wait_for_all(const Cancellable* cancellable) = 0;
183+
virtual void wait_for_all(Cancellable* cancellable) = 0;
184184

185185
// Tell the scheduler to wait for all pending commands to finish
186186
// (including cooldowns) before executing further instructions.
187187
// This is used to prevent hanging commands from overlapping with new
188188
// commands issued after this barrier.
189-
virtual void issue_barrier(const Cancellable* cancellable) = 0;
189+
virtual void issue_barrier(Cancellable* cancellable) = 0;
190190

191191
// Do nothing for this much time.
192-
virtual void issue_nop(const Cancellable* cancellable, Milliseconds duration) = 0;
192+
virtual void issue_nop(Cancellable* cancellable, Milliseconds duration) = 0;
193193

194194

195195
public:
@@ -248,7 +248,7 @@ class ControllerContext final : public CancellableScope{
248248

249249

250250
public:
251-
void wait_for_all_requests() const{
251+
void wait_for_all_requests(){
252252
auto scope = m_lifetime_sanitizer.check_scope();
253253
m_controller.wait_for_all(this);
254254
}

SerialPrograms/Source/Controllers/KeyboardInput/KeyboardInput.cpp

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ namespace PokemonAutomation{
2424
KeyboardInputController::~KeyboardInputController() = default;
2525
KeyboardInputController::KeyboardInputController(Logger& logger, bool enabled)
2626
: m_logger(logger)
27-
, m_stop(false)
2827
{}
2928

3029
void KeyboardInputController::start(){
@@ -36,9 +35,7 @@ void KeyboardInputController::start(){
3635
});
3736
}
3837
void KeyboardInputController::stop() noexcept{
39-
bool expected = false;
40-
if (!m_stop.compare_exchange_strong(expected, true)){
41-
// Already stopped.
38+
if (cancel(nullptr)){
4239
return;
4340
}
4441
{
@@ -95,7 +92,7 @@ void KeyboardInputController::thread_loop(){
9592
bool last_neutral = true;
9693
WallClock last_press = current_time();
9794
while (true){
98-
if (m_stop.load(std::memory_order_acquire)){
95+
if (cancelled()){
9996
return;
10097
}
10198

@@ -170,7 +167,7 @@ void KeyboardInputController::thread_loop(){
170167

171168
// Wait for next event.
172169
std::unique_lock<std::mutex> lg(m_sleep_lock);
173-
if (m_stop.load(std::memory_order_acquire)){
170+
if (cancelled()){
174171
return;
175172
}
176173

SerialPrograms/Source/Controllers/KeyboardInput/KeyboardInput.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ namespace PokemonAutomation{
2525

2626

2727

28-
class KeyboardInputController{
28+
class KeyboardInputController : public Cancellable{
2929
public:
3030
KeyboardInputController(Logger& logger, bool enabled);
3131
virtual ~KeyboardInputController();
@@ -59,7 +59,7 @@ class KeyboardInputController{
5959
SpinLock m_state_lock;
6060
KeyboardStateTracker m_state_tracker;
6161

62-
std::atomic<bool> m_stop;
62+
// std::atomic<bool> m_stop;
6363

6464
std::mutex m_sleep_lock;
6565
std::condition_variable m_cv;
@@ -77,14 +77,14 @@ class KeyboardManager : public KeyboardInputController, public KeyboardEventHand
7777
, m_controller(&controller)
7878
{}
7979
void stop() noexcept{
80+
KeyboardInputController::stop();
8081
{
8182
WriteSpinLock lg(m_lock);
8283
if (m_controller == nullptr){
8384
return;
8485
}
8586
m_controller = nullptr;
8687
}
87-
KeyboardInputController::stop();
8888
}
8989

9090
virtual std::unique_ptr<ControllerState> make_state() const override{

SerialPrograms/Source/Controllers/Schedulers/ControllerWithScheduler.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class ControllerWithScheduler{
3434
public:
3535
// Superscalar Commands (the "ssf" framework)
3636

37-
void issue_barrier(const Cancellable* cancellable){
37+
void issue_barrier(Cancellable* cancellable){
3838
SuperscalarScheduler::Schedule schedule;
3939
std::lock_guard<std::mutex> lg0(m_issue_lock);
4040
{
@@ -46,7 +46,7 @@ class ControllerWithScheduler{
4646
m_logger.log("issue_barrier()", COLOR_DARKGREEN);
4747
}
4848
}
49-
void issue_nop(const Cancellable* cancellable, Milliseconds duration){
49+
void issue_nop(Cancellable* cancellable, Milliseconds duration){
5050
SuperscalarScheduler::Schedule schedule;
5151
std::lock_guard<std::mutex> lg0(m_issue_lock);
5252
{
@@ -68,11 +68,11 @@ class ControllerWithScheduler{
6868

6969
protected:
7070
virtual void execute_state(
71-
const Cancellable* cancellable,
71+
Cancellable* cancellable,
7272
const SuperscalarScheduler::ScheduleEntry& entry
7373
) = 0;
7474
virtual void execute_schedule(
75-
const Cancellable* cancellable,
75+
Cancellable* cancellable,
7676
const SuperscalarScheduler::Schedule& schedule
7777
){
7878
for (const SuperscalarScheduler::ScheduleEntry& entry : schedule){

SerialPrograms/Source/Controllers/SerialPABotBase/Connection/BotBase.h

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class BotBaseControllerContext;
1919

2020

2121

22-
class BotBaseController{
22+
class BotBaseController : public Cancellable::CancelListener{
2323
public:
2424
using ContextType = BotBaseControllerContext;
2525

@@ -38,10 +38,8 @@ class BotBaseController{
3838
virtual State state() const = 0;
3939
virtual size_t queue_limit() const = 0;
4040

41-
virtual void notify_all() = 0;
42-
4341
// Waits for all pending requests to finish.
44-
virtual void wait_for_all_requests(const Cancellable* cancelled = nullptr) = 0;
42+
virtual void wait_for_all_requests(Cancellable* cancelled = nullptr) = 0;
4543

4644
// Stop all pending commands. This wipes the command queue on both sides
4745
// and stops any currently executing command.
@@ -59,15 +57,15 @@ class BotBaseController{
5957
public:
6058
virtual bool try_issue_request(
6159
const BotBaseRequest& request,
62-
const Cancellable* cancelled = nullptr
60+
Cancellable* cancelled = nullptr
6361
) = 0;
6462
virtual void issue_request(
6563
const BotBaseRequest& request,
66-
const Cancellable* cancelled = nullptr
64+
Cancellable* cancelled = nullptr
6765
) = 0;
6866
virtual BotBaseMessage issue_request_and_wait(
6967
const BotBaseRequest& request,
70-
const Cancellable* cancelled = nullptr
68+
Cancellable* cancelled = nullptr
7169
) = 0;
7270

7371
};

0 commit comments

Comments
 (0)