|
37 | 37 | #include <algorithm> |
38 | 38 | #include <cassert> |
39 | 39 | #include <chrono> |
| 40 | +#include <condition_variable> |
40 | 41 | #include <cstdarg> |
41 | 42 | #include <cstdio> |
42 | 43 | #include <fstream> |
@@ -256,7 +257,27 @@ void DAP::SendJSON(const llvm::json::Value &json) { |
256 | 257 | } |
257 | 258 | return; |
258 | 259 | } |
259 | | - auto status = transport.Write(log, M); |
| 260 | + Send(M); |
| 261 | +} |
| 262 | + |
| 263 | +void DAP::Send(const protocol::Message &M) { |
| 264 | + lldb_private::Status status; |
| 265 | + // If the debugger was interrupted while handling a response, mark the |
| 266 | + // response as cancelled since it may contain partial information. |
| 267 | + if (const auto *resp = std::get_if<protocol::Response>(&M); |
| 268 | + debugger.InterruptRequested()) { |
| 269 | + status = transport.Write( |
| 270 | + log, protocol::Response{ |
| 271 | + /*request_seq=*/resp->request_seq, |
| 272 | + /*command=*/resp->command, |
| 273 | + /*success=*/false, |
| 274 | + /*message=*/protocol::Response::Message::cancelled, |
| 275 | + /*rawBody=*/std::nullopt, |
| 276 | + }); |
| 277 | + } else { |
| 278 | + status = transport.Write(log, M); |
| 279 | + } |
| 280 | + |
260 | 281 | if (status.Fail() && log) |
261 | 282 | *log << llvm::formatv("failed to send {0}: {1}\n", llvm::json::Value(M), |
262 | 283 | status.AsCString()) |
@@ -798,22 +819,112 @@ lldb::SBError DAP::Disconnect(bool terminateDebuggee) { |
798 | 819 | return error; |
799 | 820 | } |
800 | 821 |
|
| 822 | +template <typename T> |
| 823 | +static std::optional<T> getArgumentsIfRequest(const protocol::Message &pm, |
| 824 | + llvm::StringLiteral command) { |
| 825 | + auto *const req = std::get_if<protocol::Request>(&pm); |
| 826 | + if (!req || req->command != command) |
| 827 | + return std::nullopt; |
| 828 | + |
| 829 | + T args; |
| 830 | + llvm::json::Path::Root root; |
| 831 | + if (!fromJSON(req->rawArguments, args, root)) { |
| 832 | + return std::nullopt; |
| 833 | + } |
| 834 | + |
| 835 | + return std::move(args); |
| 836 | +} |
| 837 | + |
801 | 838 | llvm::Error DAP::Loop() { |
802 | | - auto cleanup = llvm::make_scope_exit([this]() { StopEventHandlers(); }); |
| 839 | + std::deque<protocol::Message> queue; |
| 840 | + std::condition_variable queue_cv; |
| 841 | + std::mutex queue_mutex; |
| 842 | + std::thread queue_reader([&]() { |
| 843 | + auto cleanup = llvm::make_scope_exit([&]() { |
| 844 | + // Ensure we're marked as disconnecting when the reader exits. |
| 845 | + disconnecting = true; |
| 846 | + queue_cv.notify_all(); |
| 847 | + }); |
| 848 | + while (!disconnecting) { |
| 849 | + protocol::Message next; |
| 850 | + auto status = transport.Read(log, next); |
| 851 | + if (status.Fail()) { |
| 852 | + if (status.GetError() != Transport::kEOF && log) |
| 853 | + *log << "DAP transport failure: " << status.AsCString() << std::endl; |
| 854 | + break; |
| 855 | + } |
| 856 | + |
| 857 | + { |
| 858 | + std::lock_guard<std::mutex> lock(queue_mutex); |
| 859 | + |
| 860 | + // If a cancel is requested for the active request, make a best |
| 861 | + // effort attempt to interrupt. |
| 862 | + if (const auto args = getArgumentsIfRequest<protocol::CancelArguments>( |
| 863 | + next, "cancel"); |
| 864 | + args && active_seq == args->requestId) |
| 865 | + debugger.RequestInterrupt(); |
| 866 | + |
| 867 | + queue.push_back(std::move(next)); |
| 868 | + } |
| 869 | + queue_cv.notify_one(); |
| 870 | + } |
| 871 | + }); |
| 872 | + |
| 873 | + auto cleanup = llvm::make_scope_exit([&]() { |
| 874 | + StopEventHandlers(); |
| 875 | + queue_reader.join(); |
| 876 | + }); |
| 877 | + |
803 | 878 | while (!disconnecting) { |
804 | 879 | protocol::Message next; |
805 | | - auto status = transport.Read(log, next); |
806 | | - if (status.Fail()) { |
807 | | - // On EOF, simply break out of the loop. |
808 | | - if (status.GetError() == Transport::kEOF) |
| 880 | + { |
| 881 | + std::unique_lock<std::mutex> lock(queue_mutex); |
| 882 | + queue_cv.wait(lock, [&] { return disconnecting || !queue.empty(); }); |
| 883 | + |
| 884 | + if (queue.empty()) |
809 | 885 | break; |
810 | | - return status.takeError(); |
| 886 | + |
| 887 | + next = queue.front(); |
| 888 | + queue.pop_front(); |
| 889 | + |
| 890 | + if (protocol::Request *req = std::get_if<protocol::Request>(&next)) { |
| 891 | + active_seq = req->seq; |
| 892 | + |
| 893 | + // Check if we should preempt this request from a queued cancel. |
| 894 | + bool cancelled = false; |
| 895 | + for (const auto &message : queue) { |
| 896 | + if (const auto args = |
| 897 | + getArgumentsIfRequest<protocol::CancelArguments>(message, |
| 898 | + "cancel"); |
| 899 | + args && args->requestId == req->seq) { |
| 900 | + cancelled = true; |
| 901 | + break; |
| 902 | + } |
| 903 | + } |
| 904 | + |
| 905 | + // Preempt the request and immeidately respond with cancelled. |
| 906 | + if (cancelled) { |
| 907 | + Send(protocol::Response{ |
| 908 | + /*request_seq=*/req->seq, |
| 909 | + /*command=*/req->command, |
| 910 | + /*success=*/false, |
| 911 | + /*message=*/protocol::Response::Message::cancelled, |
| 912 | + /*rawBody=*/std::nullopt, |
| 913 | + }); |
| 914 | + continue; |
| 915 | + } |
| 916 | + } else |
| 917 | + active_seq = 0; |
811 | 918 | } |
812 | 919 |
|
813 | 920 | if (!HandleObject(next)) { |
814 | 921 | return llvm::createStringError(llvm::inconvertibleErrorCode(), |
815 | 922 | "unhandled packet"); |
816 | 923 | } |
| 924 | + |
| 925 | + // Clear interrupt marker prior to handling the next request. |
| 926 | + if (debugger.InterruptRequested()) |
| 927 | + debugger.CancelInterruptRequest(); |
817 | 928 | } |
818 | 929 |
|
819 | 930 | return llvm::Error::success(); |
|
0 commit comments