Skip to content

Commit 7f4d4bf

Browse files
authored
[rb] Handle graceful webdriver shutdown (#14430)
When shutting down `Selenium::WebDriver::ServiceManager#stop_server` issues a `/shutdown` request against the webdriver server and the server exits cleanly, however the mechanism to assert if the child process is up or not cannot distinguish between busy or not found. `Selenium::WebDriver::ChildProcess#exited?` returns `false` when the process is still alive because `Selenium::WebDriver::ChildProcess#waitpid2` returns `nil`. However, by catching `Errno::ECHILD` and `Errno::ESRCH` and returning `nil` `#waitpid2` masks a not running pid as "running" delaying the shutdown procedure when the server was already gone. This patch moves the exception handling away from the `Process` wrappers so its closer to the decision points in `Selenium::WebDriver::ChildProcess#exited?` and `Selenium::WebDriver::ChildProcess#stop`. Addresses a similar inconsistency in #14032
1 parent b0464e1 commit 7f4d4bf

File tree

2 files changed

+21
-14
lines changed

2 files changed

+21
-14
lines changed

rb/lib/selenium/webdriver/common/child_process.rb

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -64,16 +64,10 @@ def stop(timeout = 3)
6464
return unless @pid
6565
return if exited?
6666

67-
WebDriver.logger.debug("Sending TERM to process: #{@pid}", id: :process)
68-
terminate(@pid)
69-
poll_for_exit(timeout)
70-
71-
WebDriver.logger.debug(" -> stopped #{@pid}", id: :process)
72-
rescue TimeoutError, Errno::EINVAL
73-
WebDriver.logger.debug(" -> sending KILL to process: #{@pid}", id: :process)
74-
kill(@pid)
75-
wait
76-
WebDriver.logger.debug(" -> killed #{@pid}", id: :process)
67+
terminate_and_wait_else_kill(timeout)
68+
rescue Errno::ECHILD, Errno::ESRCH => e
69+
# Process exited earlier than terminate/kill could catch
70+
WebDriver.logger.debug(" -> process: #{@pid} does not exist (#{e.class.name})", id: :process)
7771
end
7872

7973
def alive?
@@ -91,6 +85,9 @@ def exited?
9185
WebDriver.logger.debug(" -> exit code is #{exit_code.inspect}", id: :process)
9286

9387
!!exit_code
88+
rescue Errno::ECHILD, Errno::ESRCH
89+
WebDriver.logger.debug(" -> process: #{@pid} already finished", id: :process)
90+
true
9491
end
9592

9693
def poll_for_exit(timeout)
@@ -110,20 +107,29 @@ def wait
110107

111108
private
112109

110+
def terminate_and_wait_else_kill(timeout)
111+
WebDriver.logger.debug("Sending TERM to process: #{@pid}", id: :process)
112+
terminate(@pid)
113+
poll_for_exit(timeout)
114+
115+
WebDriver.logger.debug(" -> stopped #{@pid}", id: :process)
116+
rescue TimeoutError, Errno::EINVAL
117+
WebDriver.logger.debug(" -> sending KILL to process: #{@pid}", id: :process)
118+
kill(@pid)
119+
wait
120+
WebDriver.logger.debug(" -> killed #{@pid}", id: :process)
121+
end
122+
113123
def terminate(pid)
114124
Process.kill(SIGTERM, pid)
115125
end
116126

117127
def kill(pid)
118128
Process.kill(SIGKILL, pid)
119-
rescue Errno::ECHILD, Errno::ESRCH
120-
# already dead
121129
end
122130

123131
def waitpid2(pid, flags = 0)
124132
Process.waitpid2(pid, flags)
125-
rescue Errno::ECHILD
126-
# already dead
127133
end
128134
end # ChildProcess
129135
end # WebDriver

rb/lib/selenium/webdriver/common/service_manager.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ def stop_process
113113
def stop_server
114114
connect_to_server do |http|
115115
headers = WebDriver::Remote::Http::Common::DEFAULT_HEADERS.dup
116+
WebDriver.logger.debug('Sending shutdown request to server', id: :driver_service)
116117
http.get('/shutdown', headers)
117118
end
118119
end

0 commit comments

Comments
 (0)