Skip to content

Commit 458ce72

Browse files
committed
Reuse threads between connections. Either Switch or libnx doesn't seem to release allocated memory for spawned threads after they have been cleaned up, joined, and exited.
1 parent b4401b7 commit 458ce72

14 files changed

+467
-239
lines changed

include/commandHandler.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,11 @@ namespace CommandHandler {
7575
#pragma endregion Command registration.
7676
};
7777

78-
~Handler() override {}
78+
~Handler() override {
79+
m_cmd.clear();
80+
cqNotifyAll();
81+
cqJoinThread();
82+
};
7983

8084
public:
8185
std::vector<char> HandleCommand(const std::string& cmd, const std::vector<std::string>& params);

include/connection.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ namespace Connection {
1212

1313
public:
1414
virtual Result initialize(Result& res) = 0;
15+
virtual void initializeThreads() = 0;
16+
virtual void stopThreads() = 0;
1517
virtual bool connect() = 0;
1618
virtual void run() = 0;
1719
virtual void disconnect() = 0;

include/controllerCommands.h

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ namespace ControllerCommands {
1616
class Controller : protected virtual ModuleBase::BaseCommands {
1717
public:
1818
Controller() : BaseCommands(), m_ccQueue() {
19-
m_workMem = (u8*)aligned_alloc(0x1000, m_workMem_size);
2019
m_controllerHandle = { 0 };
2120
m_controllerDevice = { 0 };
2221
m_hiddbgHdlsState = { 0 };
@@ -29,12 +28,17 @@ namespace ControllerCommands {
2928
};
3029

3130
~Controller() override {
32-
std::lock_guard<std::mutex> lock(m_ccMutex);
33-
m_isEnabledPA = false;
31+
m_isEnabledPA = false;
3432
m_ccThreadRunning = false;
35-
detachController();
3633
m_ccCv.notify_all();
3734
if (m_ccThread.joinable()) m_ccThread.join();
35+
m_ccQueue.clear();
36+
detachController();
37+
hiddbgExit();
38+
if (m_workMem) {
39+
aligned_free(m_workMem);
40+
m_workMem = nullptr;
41+
}
3842
};
3943

4044
public:
@@ -94,7 +98,7 @@ namespace ControllerCommands {
9498
static int parseStringToButton(const std::string& arg);
9599
static int parseStringToStick(const std::string& arg);
96100

97-
void startControllerThread(LockFreeQueue<std::vector<char>>& senderQueue, std::condition_variable& senderCv, std::atomic_bool& error);
101+
void startControllerThread(LockFreeQueue<std::vector<char>>& senderQueue, std::condition_variable& senderCv, std::atomic_bool& stop, std::atomic_bool& error);
98102
void cqEnqueueCommand(const ControllerCommand& cmd);
99103
void cqReplaceOnNext();
100104
void cqCancel();
@@ -116,7 +120,7 @@ namespace ControllerCommands {
116120
void setControllerType(const std::vector<std::string>& params);
117121

118122
private:
119-
void commandLoopPA(LockFreeQueue<std::vector<char>>& senderQueue, std::condition_variable& senderCv, std::atomic_bool& error);
123+
void commandLoopPA(LockFreeQueue<std::vector<char>>& senderQueue, std::condition_variable& senderCv, std::atomic_bool& stop, std::atomic_bool& error);
120124
void cqControllerState(const ControllerCommand& cmd);
121125

122126
inline void* aligned_alloc(size_t alignment, size_t size) {

include/lockFreeQueue.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#include <atomic>
55

66
namespace LocklessQueue {
7-
template<typename T, size_t Capacity = 128>
7+
template<typename T, size_t Capacity = 256>
88
class LockFreeQueue {
99
static_assert(Capacity > 0, "Capacity must be greater than 0.");
1010

@@ -95,5 +95,11 @@ namespace LocklessQueue {
9595
size_t seq = cell.sequence.load(std::memory_order_acquire);
9696
return ((intptr_t)seq - (intptr_t)pos) < 0;
9797
}
98+
99+
size_t size() const {
100+
size_t enq = m_enqueuePos.load(std::memory_order_acquire);
101+
size_t deq = m_dequeuePos.load(std::memory_order_acquire);
102+
return enq - deq;
103+
}
98104
};
99105
}

include/moduleBase.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ namespace ModuleBase {
8989

9090
private:
9191
static std::string getCurrentSbbVersion() {
92-
return !g_enableBackwardsCompat ? "3.0\r\n" : "3.1\r\n";
92+
return !g_enableBackwardsCompat ? "3.01\r\n" : "3.11\r\n";
9393
}
9494

9595
void setButtonClickSleepTime(const std::vector<std::string>& params);

include/socketConnection.h

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,19 @@ namespace SocketConnection {
1212
public:
1313
SocketConnection() : ConnectionHandler(), m_tcp(), m_senderQueue(), m_commandQueue() {
1414
m_error = false;
15+
m_stop = false;
1516
m_handler = std::make_unique<CommandHandler::Handler>();
1617
};
1718

1819
~SocketConnection() override {
19-
m_error = true;
20-
notifyAll();
21-
22-
m_persistentBuffer.clear();
23-
m_senderQueue.clear();
24-
m_commandQueue.clear();
25-
26-
if (m_senderThread.joinable()) m_senderThread.join();
27-
if (m_commandThread.joinable()) m_commandThread.join();
28-
if (m_handler) m_handler->cqJoinThread();
29-
if (m_handler) m_handler.reset();
20+
disconnect();
21+
stopThreads();
3022
};
3123

3224
public:
3325
Result initialize(Result& res) override;
26+
void initializeThreads() override;
27+
void stopThreads() override;
3428
bool connect() override;
3529
void run() override;
3630
void disconnect() override;
@@ -47,13 +41,21 @@ namespace SocketConnection {
4741
TcpConnection m_tcp;
4842

4943
int setupServerSocket();
44+
void closeSocket();
5045
void notifyAll() {
5146
m_commandCv.notify_all();
5247
m_senderCv.notify_all();
5348
if (m_handler) m_handler->cqNotifyAll();
5449
}
5550

51+
bool getThreadsInitialized() const {
52+
return m_senderInitialized.load(std::memory_order_relaxed)
53+
&& m_commandInitialized.load(std::memory_order_relaxed);
54+
}
55+
5656
std::string m_persistentBuffer;
57+
std::atomic_bool m_senderInitialized { false };
58+
std::atomic_bool m_commandInitialized { false };
5759

5860
std::thread m_senderThread;
5961
LocklessQueue::LockFreeQueue<std::vector<char>> m_senderQueue;
@@ -66,6 +68,7 @@ namespace SocketConnection {
6668
std::condition_variable m_commandCv;
6769

6870
std::atomic_bool m_error { false };
71+
std::atomic_bool m_stop { false };
6972
std::unique_ptr<CommandHandler::Handler> m_handler;
7073
};
7174
}

include/usbConnection.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ namespace UsbConnection {
1212
public:
1313
UsbConnection() : ConnectionHandler() {
1414
m_error = false;
15+
m_stop = false;
1516
m_handler = std::make_unique<CommandHandler::Handler>();
1617
};
1718

@@ -30,6 +31,8 @@ namespace UsbConnection {
3031

3132
public:
3233
Result initialize(Result& res) override;
34+
void initializeThreads() override;
35+
void stopThreads() override;
3336
bool connect() override;
3437
void run() override;
3538
void disconnect() override;
@@ -43,7 +46,14 @@ namespace UsbConnection {
4346
if (m_handler) m_handler->cqNotifyAll();
4447
}
4548

49+
bool getThreadsInitialized() const {
50+
return m_senderInitialized.load(std::memory_order_relaxed)
51+
&& m_commandInitialized.load(std::memory_order_relaxed);
52+
}
53+
4654
std::string m_persistentBuffer;
55+
std::atomic_bool m_senderInitialized { false };
56+
std::atomic_bool m_commandInitialized { false };
4757

4858
std::thread m_senderThread;
4959
LocklessQueue::LockFreeQueue<std::vector<char>> m_senderQueue;
@@ -56,6 +66,7 @@ namespace UsbConnection {
5666
std::condition_variable m_commandCv;
5767

5868
std::atomic_bool m_error { false };
69+
std::atomic_bool m_stop{ false };
5970
std::unique_ptr<CommandHandler::Handler> m_handler;
6071

6172
struct USBResponse {

source/commandHandler.cpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,7 @@ namespace CommandHandler {
3838
Logger::instance().log(log);
3939
u64 pid = 0;
4040
Result rc = pmdmntGetApplicationProcessId(&pid);
41-
if (R_FAILED(rc)) {
42-
Logger::instance().log("initMetaData() pmdmntGetApplicationProcessId() failed: pid=" + std::to_string(pid), std::to_string(R_DESCRIPTION(rc)));
43-
}
44-
45-
if (m_metaData.pid == 0 || m_metaData.pid != pid) {
41+
if (R_SUCCEEDED(rc) && (m_metaData.pid == 0 || m_metaData.pid != pid)) {
4642
m_metaData.pid = pid;
4743
initMetaData();
4844
}

source/controllerCommands.cpp

Lines changed: 56 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,16 @@ namespace ControllerCommands {
2525
}
2626

2727
if (!m_workMem) {
28-
m_workMem = (u8*)aligned_alloc(0x1000, m_workMem_size);
29-
if (!m_workMem) {
30-
Logger::instance().log("Failed to initialize virtual controller.", "initController() aligned_alloc() failed.");
28+
try {
29+
m_workMem = (u8*)aligned_alloc(0x1000, m_workMem_size);
30+
if (!m_workMem) {
31+
Logger::instance().log("Failed to initialize virtual controller.", "initController() aligned_alloc() failed.");
32+
hiddbgExit();
33+
return;
34+
}
35+
} catch (...) {
36+
Logger::instance().log("Exception during m_workMem allocation.");
37+
hiddbgExit();
3138
return;
3239
}
3340
}
@@ -225,14 +232,30 @@ namespace ControllerCommands {
225232
* @param Condition variable for the sender queue.
226233
* @param Atomic boolean for error handling, passed from the command thread.
227234
*/
228-
void Controller::startControllerThread(LockFreeQueue<std::vector<char>>& senderQueue, std::condition_variable& senderCv, std::atomic_bool& error) {
235+
void Controller::startControllerThread(LockFreeQueue<std::vector<char>>& senderQueue, std::condition_variable& senderCv, std::atomic_bool& stop, std::atomic_bool& error) {
229236
if (m_ccThreadRunning) {
230237
Logger::instance().log("Controller thread already running.");
231238
return;
232239
}
233240

234241
Logger::instance().log("Starting commandLoopPA thread.");
235-
m_ccThread = std::thread(&Controller::commandLoopPA, this, std::ref(senderQueue), std::ref(senderCv), std::ref(error));
242+
try {
243+
m_ccThread = std::thread(&Controller::commandLoopPA, this, std::ref(senderQueue), std::ref(senderCv), std::ref(stop), std::ref(error));
244+
m_ccThreadRunning = true;
245+
Logger::instance().log("commandLoopPA thread created successfully.");
246+
} catch (const std::exception& e) {
247+
Logger::instance().log("Failed to create commandLoopPA thread: ", e.what());
248+
m_ccThreadRunning = false;
249+
stop = true;
250+
error = true;
251+
throw;
252+
} catch (...) {
253+
Logger::instance().log("Unknown exception creating commandLoopPA thread.");
254+
m_ccThreadRunning = false;
255+
stop = true;
256+
error = true;
257+
throw;
258+
}
236259
}
237260

238261
/**
@@ -241,14 +264,13 @@ namespace ControllerCommands {
241264
* @param Condition variable for the sender queue.
242265
* @param Atomic boolean for error handling, passed from the command thread.
243266
*/
244-
void Controller::commandLoopPA(LockFreeQueue<std::vector<char>>& senderQueue, std::condition_variable& senderCv, std::atomic_bool& error) {
267+
void Controller::commandLoopPA(LockFreeQueue<std::vector<char>>& senderQueue, std::condition_variable& senderCv, std::atomic_bool& stop, std::atomic_bool& error) {
245268
const std::chrono::microseconds earlyWake(1000);
246269
m_nextStateChange = WallClock::max();
247-
m_ccThreadRunning = true;
248270
Logger::instance().log("commandLoopPA() started.");
249271

250272
std::unique_lock<std::mutex> lock(m_ccMutex);
251-
while (!error) {
273+
while (!stop) {
252274
WallClock now = std::chrono::steady_clock::now();
253275
ControllerCommand cmd;
254276
if (now >= m_nextStateChange) {
@@ -270,17 +292,29 @@ namespace ControllerCommands {
270292
if (m_ccCurrentCommand.seqnum != 0){
271293
Logger::instance().log("cqSendState() command finished with seqnum: " + std::to_string(m_ccCurrentCommand.seqnum));
272294
std::string res = "cqCommandFinished " + std::to_string(m_ccCurrentCommand.seqnum) + "\r\n";
273-
senderQueue.push(std::vector<char>(res.begin(), res.end()));
274-
senderCv.notify_one();
295+
if (!senderQueue.full()) {
296+
senderQueue.push(std::vector<char>(res.begin(), res.end()));
297+
senderCv.notify_one();
298+
} else {
299+
Logger::instance().log("Sender queue full, dropping command finished message.");
300+
}
275301
}
276302

277303
m_ccCurrentCommand = cmd;
278-
m_ccCv.wait_until(lock, m_nextStateChange - earlyWake, [&] { return error || now + earlyWake >= m_nextStateChange; });
304+
m_ccCv.wait_until(lock, m_nextStateChange - earlyWake, [&] { return stop || error || now + earlyWake >= m_nextStateChange; });
305+
if (error) {
306+
m_ccQueue.clear();
307+
cqControllerState(ControllerCommand{});
308+
m_nextStateChange = WallClock::max();
309+
}
279310
}
280311

281312
m_ccQueue.clear();
282313
cqControllerState(ControllerCommand{});
283314
detachController();
315+
m_ccThreadRunning = false;
316+
m_isEnabledPA = false;
317+
stop = true;
284318
Logger::instance().log("commandLoopPA() exiting thread...");
285319
}
286320

@@ -290,7 +324,15 @@ namespace ControllerCommands {
290324
*/
291325
void Controller::cqControllerState(const ControllerCommand& cmd) {
292326
Logger::instance().log("cqControllerState() called with seqnum: " + std::to_string(cmd.seqnum));
293-
initController();
327+
try {
328+
initController();
329+
} catch (const std::exception& e) {
330+
Logger::instance().log("cqControllerState() initController() failed: ", e.what());
331+
return;
332+
} catch (...) {
333+
Logger::instance().log("cqControllerState() initController() unknown exception.");
334+
return;
335+
}
294336

295337
m_hiddbgHdlsState.buttons = cmd.state.buttons;
296338
m_hiddbgHdlsState.analog_stick_l.x = cmd.state.left_joystick_x;
@@ -362,11 +404,8 @@ namespace ControllerCommands {
362404
* @brief Join the PA controller thread if it is running.
363405
*/
364406
void Controller::cqJoinThread() {
365-
if (m_ccThreadRunning && m_ccThread.joinable()) {
366-
m_ccThread.join();
367-
m_ccThreadRunning = false;
368-
Logger::instance().log("commandLoopPA thread finished.");
369-
}
407+
m_ccCv.notify_all();
408+
if (m_ccThread.joinable()) m_ccThread.join();
370409
}
371410

372411
/**

0 commit comments

Comments
 (0)