Improve hello_debugger
Make it so that the debugger is actually looping through all lines while
in running, so that pause stops on whatever current line it's on, or
setting a breakpoint will eventually hit.
Also fixed breakpoint verified state for the last line, as line numbers
are 1-based.
diff --git a/examples/hello_debugger.cpp b/examples/hello_debugger.cpp
index 342e862..e406ff8 100644
--- a/examples/hello_debugger.cpp
+++ b/examples/hello_debugger.cpp
@@ -19,9 +19,11 @@
#include "dap/protocol.h"
#include "dap/session.h"
+#include <atomic>
#include <condition_variable>
#include <cstdio>
#include <mutex>
+#include <thread>
#include <unordered_set>
#ifdef _MSC_VER
@@ -40,6 +42,33 @@
namespace {
+// Event provides a basic wait and signal synchronization primitive.
+class Event {
+ public:
+ // wait() blocks until the event is fired.
+ void wait();
+
+ // fire() sets signals the event, and unblocks any calls to wait().
+ void fire();
+
+ private:
+ std::mutex mutex;
+ std::condition_variable cv;
+ bool fired = false;
+};
+
+void Event::wait() {
+ std::unique_lock<std::mutex> lock(mutex);
+ fired = false;
+ cv.wait(lock, [&] { return fired; });
+}
+
+void Event::fire() {
+ std::unique_lock<std::mutex> lock(mutex);
+ fired = true;
+ cv.notify_all();
+}
+
// sourceContent holds the synthetic file source.
constexpr char sourceContent[] = R"(// Hello Debugger!
@@ -61,9 +90,15 @@
Debugger(const EventHandler&);
- // run() instructs the debugger to continue execution.
+ // runs the debugger's main loop
void run();
+ // stop the debugger and terminate
+ void stop();
+
+ // resume() instructs the debugger to continue execution.
+ void resume();
+
// pause() instructs the debugger to pause execution.
void pause();
@@ -84,24 +119,45 @@
std::mutex mutex;
int line = 1;
std::unordered_set<int> breakpoints;
+ std::atomic<bool> terminate{false};
+ std::atomic<bool> running{true};
+ ::Event canRun;
};
Debugger::Debugger(const EventHandler& onEvent) : onEvent(onEvent) {}
void Debugger::run() {
- std::unique_lock<std::mutex> lock(mutex);
- for (int i = 0; i < numSourceLines; i++) {
- auto l = ((line + i) % numSourceLines) + 1;
- if (breakpoints.count(l)) {
- line = l;
+ while (!terminate) {
+ // If not running, wait
+ if (!running) {
+ canRun.wait();
+ }
+
+ // Go to next line
+ line = (line % numSourceLines) + 1;
+
+ // Check for breakpoints
+ std::unique_lock<std::mutex> lock(mutex);
+ if (breakpoints.count(line)) {
lock.unlock();
+ running = false;
onEvent(Event::BreakpointHit);
- return;
}
}
}
+void Debugger::stop() {
+ terminate = true;
+ resume();
+}
+
+void Debugger::resume() {
+ running = true;
+ canRun.fire();
+}
+
void Debugger::pause() {
+ running = false;
onEvent(Event::Paused);
}
@@ -127,32 +183,6 @@
this->breakpoints.emplace(l);
}
-// Event provides a basic wait and signal synchronization primitive.
-class Event {
- public:
- // wait() blocks until the event is fired.
- void wait();
-
- // fire() sets signals the event, and unblocks any calls to wait().
- void fire();
-
- private:
- std::mutex mutex;
- std::condition_variable cv;
- bool fired = false;
-};
-
-void Event::wait() {
- std::unique_lock<std::mutex> lock(mutex);
- cv.wait(lock, [&] { return fired; });
-}
-
-void Event::fire() {
- std::unique_lock<std::mutex> lock(mutex);
- fired = true;
- cv.notify_all();
-}
-
} // anonymous namespace
// main() entry point to the DAP server.
@@ -183,7 +213,6 @@
// Signal events
Event configured;
- Event terminate;
// Event handlers from the Debugger.
auto onDebuggerEvent = [&](Debugger::Event onEvent) {
@@ -225,7 +254,7 @@
dap::writef(log, "dap::Session error: %s\n", msg);
log->close();
}
- terminate.fire();
+ debugger.stop();
});
// The Initialize request is the first message sent from the client and
@@ -339,7 +368,7 @@
// all threads.
// https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Continue
session->registerHandler([&](const dap::ContinueRequest&) {
- debugger.run();
+ debugger.resume();
return dap::ContinueResponse();
});
@@ -380,7 +409,8 @@
response.breakpoints.resize(breakpoints.size());
for (size_t i = 0; i < breakpoints.size(); i++) {
debugger.addBreakpoint(breakpoints[i].line);
- response.breakpoints[i].verified = breakpoints[i].line < numSourceLines;
+ response.breakpoints[i].verified =
+ breakpoints[i].line >= 1 && breakpoints[i].line <= numSourceLines;
}
} else {
response.breakpoints.resize(breakpoints.size());
@@ -422,7 +452,7 @@
// Handler for disconnect requests
session->registerHandler([&](const dap::DisconnectRequest& request) {
if (request.terminateDebuggee.value(false)) {
- terminate.fire();
+ debugger.stop();
}
return dap::DisconnectResponse();
});
@@ -461,9 +491,7 @@
// This sends a stopped event to the client.
debugger.pause();
- // Block until we receive a 'terminateDebuggee' request or encounter a session
- // error.
- terminate.wait();
+ debugger.run();
return 0;
}