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; }