-
Notifications
You must be signed in to change notification settings - Fork 24
Expand file tree
/
Copy pathflow_control.rb
More file actions
161 lines (142 loc) · 6.68 KB
/
Copy pathflow_control.rb
File metadata and controls
161 lines (142 loc) · 6.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# frozen_string_literal: true
module PuppetDebugServer
module DebugSession
# Manages the flags used to control the flow of puppet and Debugger during a debug session.
class FlowControl
# What mode the debug session is running in
# @see PuppetDebugServer::DebugSession::PuppetSessionRunMode
# @return [PuppetDebugServer::DebugSession::PuppetSessionRunMode]
attr_reader :run_mode
# @param debug_session [PuppetDebugServer::PuppetDebugSession] The debug session to manage the flow for.
def initialize(debug_session)
@debug_session = debug_session
@flag_mutex = Mutex.new
@flags = {
start_puppet: false,
puppet_started: false,
session_paused: false,
client_completed_configuration: false,
session_setup: false,
terminate: false,
suppress_log_messages: false
}
@run_mode = PuppetDebugServer::DebugSession::PuppetSessionRunMode.new
end
# Returns which flags are set.
#
# Available flags
# :start_puppet Indicates the main thread can start running Puppet and begin the debug session
# :puppet_started Indicates the main thread has started running puppet and debug session is now active
# :session_paused Indicates that the debug session has hit a breakpoint and is currently paused
# :client_completed_configuration The debug client has completed it's configuration
# :session_setup This debug session has been setup ready to start
# :terminate Indicates that all threads and wait processes should terminate
# :suppress_log_messages Indicates that Puppet log messages should not be sent to the client
#
# @param flag_name [Symbol] The name of the flag
# @return [Boolean] Whether the flag is set
def flag?(flag_name)
result = false
@flag_mutex.synchronize do
result = @flags[flag_name]
end
result.nil? ? false : result
end
# Asserts a flag is set
#
# @param flag_name [Symbol] The name of the flag
def assert_flag(flag_name)
@flag_mutex.synchronize do
@flags[flag_name] = true
PuppetDebugServer.log_message(:debug, "Asserting flag #{flag_name} is true")
# Any custom logic for when flags are asserted
# rubocop:disable Style/MultipleComparison, Style/SoleNestedConditional -- This is faster and doesn't require creation of an array
if flag_name == :client_completed_configuration || flag_name == :session_setup
# If the client_completed_configuration and session_setup flag are asserted but the session isn't active yet
# assert the flag start_puppet so puppet can start in the main thread.
if !@flags[:puppet_started] && @flags[:client_completed_configuration] && @flags[:session_setup]
PuppetDebugServer.log_message(:debug, 'Asserting flag start_puppet is true')
@flags[:start_puppet] = true
end
end
@terminate_flag = true if flag_name == :terminate
end
end
# rubocop:enable Style/MultipleComparison, Style/SoleNestedConditional
# Removes/unasserts a flag
#
# @param flag_name [Symbol] The name of the flag
def unassert_flag(flag_name)
return if flag_name == :terminate # Can never unset the terminate flag
@flag_mutex.synchronize do
@flags[flag_name] = false
PuppetDebugServer.log_message(:debug, "Unasserting flag #{flag_name} is true")
end
end
# The terminate flag will be queried quite often during spin-wait cycles and it's basically immutable (i.e. Once set it can not be unset).
# So to help speed up access just treat it as a normal boolean. This can also stop any deadlocks.
#
# @return [Boolean] Whether the debug session should be terminating.
def terminate?
@terminate_flag
end
# Whether the debug session has started.
#
# @return [Boolean] Returns true if the debug session has started.
def session_active?
flag?(:puppet_started)
end
# Whether the debug session is paused due to breakpoints.
#
# @return [Boolean] Returns true if the debug session is paused.
def session_paused?
flag?(:session_paused)
end
# Raises a stopped event to the Debug Client and waits for the debug session to continue.
#
# @param reason [String] The reason for the event. Values: 'step', 'breakpoint', 'exception', 'pause', 'entry', 'goto', 'function breakpoint', 'data breakpoint'.
# @param description [String] The full reason for the event, e.g. 'Paused on exception'. This string is shown in the UI as is and must be translated.
# @param text [String] Additional information. E.g. if reason is 'exception', text contains the exception name. This string is shown in the UI.
# @param session_state [Hash] Additional information about the puppet state when the event is raised. See PuppetSessionState::Saved.
def raise_stopped_event_and_wait(reason, description, text, session_state)
# Signal a stop event
assert_flag(:session_paused)
# Save the state so when the client queries us, we can respond.
@debug_session.puppet_session_state.saved.update!(session_state)
@debug_session.send_stopped_event(
reason,
'description' => description,
'text' => text,
'threadId' => @debug_session.puppet_thread_id
)
# Spin-wait for the session to be unpaused...
# TODO - Could be better. Semaphore maybe?
sleep(0.5) while flag?(:session_paused) && !terminate?
end
# Continues a paused debug session
def continue!
run_mode.run!
@debug_session.puppet_session_state.saved.clear!
unassert_flag(:session_paused)
end
# Next steps through a paused debug session
def next!
run_mode.next!(@debug_session.puppet_session_state.saved.pops_depth_level)
@debug_session.puppet_session_state.saved.clear!
unassert_flag(:session_paused)
end
# Steps into a paused debug session
def step_in!
run_mode.step_in!
@debug_session.puppet_session_state.saved.clear!
unassert_flag(:session_paused)
end
# Steps out of a paused debug session
def step_out!
run_mode.step_out!(@debug_session.puppet_session_state.saved.pops_depth_level)
@debug_session.puppet_session_state.saved.clear!
unassert_flag(:session_paused)
end
end
end
end