Class: MilkTea::DAP::Backends::LLDBDAP

Inherits:
Object
  • Object
show all
Defined in:
lib/milk_tea/dap/backends/lldb_dap.rb

Overview

Thin bridge to an lldb-dap compatible adapter process.

Instance Method Summary collapse

Constructor Details

#initialize(adapter_command: ["lldb-dap"], on_event: nil, on_request: nil) ⇒ LLDBDAP

Returns a new instance of LLDBDAP.



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/milk_tea/dap/backends/lldb_dap.rb', line 12

def initialize(adapter_command: ["lldb-dap"], on_event: nil, on_request: nil)
  @adapter_command = adapter_command
  @on_event = on_event
  @on_request = on_request
  @protocol = nil
  @stdin = nil
  @stdout = nil
  @stderr = nil
  @wait_thread = nil
  @reader_thread = nil
  @stderr_thread = nil
  @write_mutex = Mutex.new
  @pending_mutex = Mutex.new
  @pending = {}
  @next_seq = 1
end

Instance Method Details

#drain_stderrObject



129
130
131
132
133
134
135
136
137
# File 'lib/milk_tea/dap/backends/lldb_dap.rb', line 129

def drain_stderr
  return unless @stderr

  @stderr.each_line do |_line|
    # Intentionally ignored in this bridge layer.
  end
rescue StandardError
  nil
end

#handle_adapter_request(message) ⇒ Object



139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/milk_tea/dap/backends/lldb_dap.rb', line 139

def handle_adapter_request(message)
  response = @on_request&.call(message)
  response ||= {
    "type" => "response",
    "request_seq" => message["seq"],
    "success" => false,
    "command" => message["command"],
    "message" => "unsupported reverse request: #{message['command']}"
  }

  @write_mutex.synchronize do
    @protocol.write_message(response)
  end
end

#next_seqObject



94
95
96
97
98
# File 'lib/milk_tea/dap/backends/lldb_dap.rb', line 94

def next_seq
  seq = @next_seq
  @next_seq += 1
  seq
end

#read_loopObject



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/milk_tea/dap/backends/lldb_dap.rb', line 100

def read_loop
  loop do
    message = @protocol.read_message
    break if message.nil?

    if message["type"] == "response"
      queue = @pending_mutex.synchronize { @pending[message["request_seq"]] }
      queue&.push(message)
    elsif message["type"] == "event"
      @on_event&.call(message)
    elsif message["type"] == "request"
      handle_adapter_request(message)
    end
  end
rescue StandardError
  nil
ensure
  @pending_mutex.synchronize do
    @pending.each_value do |queue|
      queue.push({
        "type" => "response",
        "success" => false,
        "message" => "backend closed"
      })
    end
    @pending.clear
  end
end

#request(command, arguments = {}, timeout: 5) ⇒ Object



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/milk_tea/dap/backends/lldb_dap.rb', line 43

def request(command, arguments = {}, timeout: 5)
  start! unless running?

  seq = next_seq
  queue = Queue.new
  @pending_mutex.synchronize do
    @pending[seq] = queue
  end

  @write_mutex.synchronize do
    @protocol.write_message({
      seq: seq,
      type: "request",
      command: command,
      arguments: arguments
    })
  end

  Timeout.timeout(timeout) { queue.pop }
rescue Timeout::Error
  {
    "type" => "response",
    "request_seq" => seq,
    "success" => false,
    "message" => "backend request timed out: #{command}"
  }
ensure
  @pending_mutex.synchronize { @pending.delete(seq) }
end

#running?Boolean

Returns:

  • (Boolean)


39
40
41
# File 'lib/milk_tea/dap/backends/lldb_dap.rb', line 39

def running?
  !@wait_thread.nil? && @wait_thread.alive?
end

#start!Object



29
30
31
32
33
34
35
36
37
# File 'lib/milk_tea/dap/backends/lldb_dap.rb', line 29

def start!
  return if running?

  @stdin, @stdout, @stderr, @wait_thread = Open3.popen3(*@adapter_command)
  @protocol = Protocol.new(input: @stdout, output: @stdin)

  @reader_thread = Thread.new { read_loop }
  @stderr_thread = Thread.new { drain_stderr }
end

#stop!Object



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/milk_tea/dap/backends/lldb_dap.rb', line 73

def stop!
  @stdin&.close
  @stdout&.close
  @stderr&.close
  if @wait_thread&.alive?
    Process.kill("TERM", @wait_thread.pid)
  end
rescue StandardError
  nil
ensure
  @wait_thread&.join(0.2)
  @reader_thread&.join(0.2)
  @stderr_thread&.join(0.2)
  @stdin = nil
  @stdout = nil
  @stderr = nil
  @wait_thread = nil
  @reader_thread = nil
  @stderr_thread = nil
end