Skip to content

Commit 9acb3ad

Browse files
committed
Cleaner SBB scheduling.
1 parent 05163d1 commit 9acb3ad

File tree

3 files changed

+77
-86
lines changed

3 files changed

+77
-86
lines changed

SerialPrograms/Source/NintendoSwitch/Controllers/SysbotBase/SysbotBase_ProController.cpp

Lines changed: 31 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ ProController_SysbotBase::ProController_SysbotBase(
2929
, m_stopping(false)
3030
, m_replace_on_next(false)
3131
, m_command_queue(QUEUE_SIZE)
32-
, m_is_active(false)
32+
, m_next_state_change(WallClock::max())
3333
{
3434
if (!connection.is_ready()){
3535
return;
@@ -74,11 +74,9 @@ void ProController_SysbotBase::cancel_all_commands(){
7474
// cout << "ProController_SysbotBase::cancel_all_commands()" << endl;
7575
std::lock_guard<std::mutex> lg(m_state_lock);
7676
size_t queue_size = m_command_queue.size();
77-
if (queue_size > 0){
78-
m_command_queue.clear();
79-
m_is_active = false;
80-
m_cv.notify_all();
81-
}
77+
m_next_state_change = WallClock::min();
78+
m_command_queue.clear();
79+
m_cv.notify_all();
8280
this->clear_on_next();
8381
m_logger.log("cancel_all_commands(): Command Queue Size = " + std::to_string(queue_size), COLOR_DARKGREEN);
8482
}
@@ -99,7 +97,7 @@ void ProController_SysbotBase::wait_for_all(const Cancellable* cancellable){
9997
m_logger.log("wait_for_all(): Command Queue Size = " + std::to_string(m_command_queue.size()), COLOR_DARKGREEN);
10098
this->issue_wait_for_all(cancellable);
10199
m_cv.wait(lg1, [this]{
102-
return m_command_queue.empty() || m_replace_on_next;
100+
return m_next_state_change == WallClock::max() || m_replace_on_next;
103101
});
104102
if (cancellable){
105103
cancellable->throw_if_cancelled();
@@ -151,12 +149,16 @@ void ProController_SysbotBase::push_state(const Cancellable* cancellable, WallDu
151149
}
152150

153151
if (m_replace_on_next){
154-
m_command_queue.clear();
155-
m_is_active = false;
152+
// cout << "executing replace" << endl;
156153
m_replace_on_next = false;
154+
m_command_queue.clear();
155+
m_next_state_change = WallClock::min();
156+
m_cv.notify_all();
157157
}
158158

159-
if (m_command_queue.empty()){
159+
// Enqueuing into empty+idle queue.
160+
if (m_next_state_change == WallClock::max()){
161+
m_next_state_change = WallClock::min();
160162
m_cv.notify_all();
161163
}
162164

@@ -298,52 +300,34 @@ void ProController_SysbotBase::thread_body(){
298300

299301
std::unique_lock<std::mutex> lg(m_state_lock);
300302
while (!m_stopping.load(std::memory_order_relaxed)){
301-
if (m_command_queue.empty()){
302-
m_is_active = false;
303-
if (current_state.is_neutral()){
304-
m_cv.wait(lg);
305-
continue;
306-
}
307-
308-
send_diff(current_state, ProControllerState());
309-
current_state.clear();
310-
continue;
311-
}
312-
313303
WallClock now = current_time();
314304

315-
// Check the next item in the schedule.
316-
Command& command = m_command_queue.front();
317-
318-
// Waking up from idle.
319-
if (!m_is_active){
320-
m_is_active = true;
321-
m_queue_start_time = now;
322-
323-
send_diff(current_state, command.state);
324-
current_state = command.state;
325-
326-
WallClock expiration = m_queue_start_time + command.duration;
327-
m_cv.wait_until(lg, expiration - EARLY_WAKE);
328-
pause();
305+
// State change.
306+
if (now >= m_next_state_change){
307+
if (m_command_queue.empty()){
308+
send_diff(current_state, ProControllerState());
309+
current_state.clear();
310+
m_next_state_change = WallClock::max();
311+
}else{
312+
Command& command = m_command_queue.front();
313+
send_diff(current_state, command.state);
314+
current_state = command.state;
315+
if (m_next_state_change == WallClock::min()){
316+
m_next_state_change = now;
317+
}
318+
m_next_state_change += command.duration;
319+
m_command_queue.pop_front();
320+
}
321+
m_cv.notify_all();
329322
continue;
330323
}
331324

332-
// Already running.
333-
WallClock expiration = m_queue_start_time + command.duration;
334-
335-
// Current command hasn't expired yet.
336-
if (now < expiration){
337-
m_cv.wait_until(lg, expiration - EARLY_WAKE);
325+
if (now + EARLY_WAKE >= m_next_state_change){
338326
pause();
339327
continue;
340328
}
341329

342-
// Command has expired. We can pop it.
343-
m_is_active = false;
344-
m_queue_start_time = expiration;
345-
m_command_queue.pop_front();
346-
m_cv.notify_all();
330+
m_cv.wait_until(lg, m_next_state_change - EARLY_WAKE);
347331

348332
// Check how much we shot passed the expiration time.
349333
// WallDuration delay = now - expiration;

SerialPrograms/Source/NintendoSwitch/Controllers/SysbotBase/SysbotBase_ProController.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,8 +236,10 @@ class ProController_SysbotBase final :
236236
Milliseconds duration;
237237
};
238238
CircularBuffer<Command> m_command_queue;
239-
WallClock m_queue_start_time;
240-
bool m_is_active;
239+
240+
// WallClock::max() means the queue is empty.
241+
// WallClock::min() means the state has suddently changed.
242+
WallClock m_next_state_change;
241243

242244
std::condition_variable m_cv;
243245
std::thread m_dispatch_thread;

SerialPrograms/Source/NintendoSwitch/DevPrograms/TestProgramComputer.cpp

Lines changed: 42 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,6 @@ class WatchdogTest1 : public WatchdogCallback{
225225

226226

227227

228-
229228
template <typename Type>
230229
class CheckedObject{
231230
public:
@@ -282,41 +281,6 @@ struct RequestManagerConfig{
282281

283282

284283

285-
struct Command{
286-
uint64_t seqnum;
287-
uint64_t milliseconds;
288-
uint64_t buttons;
289-
uint16_t left_joystick_x;
290-
uint16_t left_joystick_y;
291-
uint16_t right_joystick_x;
292-
uint16_t right_joystick_y;
293-
294-
void write_to_hex(char str[64]) const{
295-
const char HEX_DIGITS[] = "0123456789abcdef";
296-
const char* ptr = (const char*)this;
297-
for (size_t c = 0; c < 64; c += 2){
298-
uint8_t hi = (uint8_t)ptr[0] >> 4;
299-
uint8_t lo = (uint8_t)ptr[0] & 0x0f;
300-
str[c + 0] = HEX_DIGITS[hi];
301-
str[c + 1] = HEX_DIGITS[lo];
302-
ptr++;
303-
}
304-
}
305-
void parse_from_hex(const char str[64]){
306-
char* ptr = (char*)this;
307-
for (size_t c = 0; c < 64; c += 2){
308-
char hi = str[c + 0];
309-
char lo = str[c + 1];
310-
hi = hi < 'a' ? hi - '0' : hi - 'a' + 10;
311-
lo = lo < 'a' ? lo - '0' : lo - 'a' + 10;
312-
ptr[0] = hi << 4 | lo;
313-
ptr++;
314-
}
315-
}
316-
};
317-
318-
319-
320284

321285

322286

@@ -330,6 +294,47 @@ void TestProgramComputer::program(ProgramEnvironment& env, CancellableScope& sco
330294

331295
using namespace std::chrono_literals;
332296

297+
298+
#if 0
299+
{
300+
CommandQueue queue;
301+
302+
queue.enqueue_command(ControllerCommand{
303+
.seqnum = 10,
304+
.milliseconds = 1000,
305+
.state = ControllerState{
306+
.buttons = 123,
307+
}
308+
});
309+
queue.enqueue_command(ControllerCommand{
310+
.seqnum = 11,
311+
.milliseconds = 2000,
312+
.state = ControllerState{
313+
.buttons = 456,
314+
}
315+
});
316+
317+
scope.wait_for(60s);
318+
}
319+
#endif
320+
321+
#if 0
322+
HeapCircularBuffer<std::string> buffer(10);
323+
324+
buffer.try_push_back("asdf");
325+
buffer.try_push_back("qwer");
326+
buffer.try_push_back("zxcv");
327+
328+
cout << buffer[0] << endl;
329+
cout << buffer[1] << endl;
330+
cout << buffer[2] << endl;
331+
cout << "--------------------" << endl;
332+
buffer.pop_front();
333+
cout << buffer[0] << endl;
334+
cout << buffer[1] << endl;
335+
#endif
336+
337+
#if 0
333338
Command command{
334339
123,
335340
100,
@@ -351,7 +356,7 @@ void TestProgramComputer::program(ProgramEnvironment& env, CancellableScope& sco
351356
cout << command.left_joystick_y << endl;
352357
cout << command.right_joystick_x << endl;
353358
cout << command.right_joystick_y << endl;
354-
359+
#endif
355360

356361
#if 0
357362
ImageRGB32 image("20250503-121259857603.png");

0 commit comments

Comments
 (0)