Skip to content

Commit e408bea

Browse files
committed
daemon: Fix slow start with Ruby 3
This does not change any specification. This just changes to use a Ruby thread instead of `CreateThread`. It is very very fast to use a Ruby thread from the beginning. Service startup is noticeably slower with Ruby 3. (Please see #84). Windows service needs to finish `StartServiceCtrlDispatcher` in 30 seconds by default. With Ruby 3 or later, this problem can cause timeout errors. The more CPUs, the slower it tends to be. Maybe some implementation about threading has changed with Ruby 3 series. The current implementation switches between multiple Ruby threads and non-Ruby threads. This complex process may be unsuitable for Ruby 3. `CreateThread` makes a non-Ruby thread, and it calls Ruby Proc, and it makes a Ruby thread, and ... Signed-off-by: Daijiro Fukuda <fukuda@clear-code.com>
1 parent 5e0bad6 commit e408bea

File tree

2 files changed

+16
-34
lines changed

2 files changed

+16
-34
lines changed

lib/win32/daemon.rb

Lines changed: 16 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -186,25 +186,6 @@ class Daemon
186186

187187
end
188188

189-
ThreadProc = FFI::Function.new(:ulong, [:pointer]) do |lpParameter|
190-
ste = FFI::MemoryPointer.new(SERVICE_TABLE_ENTRY, 2)
191-
192-
s = SERVICE_TABLE_ENTRY.new(ste[0])
193-
s[:lpServiceName] = FFI::MemoryPointer.from_string("")
194-
s[:lpServiceProc] = lpParameter
195-
196-
s = SERVICE_TABLE_ENTRY.new(ste[1])
197-
s[:lpServiceName] = nil
198-
s[:lpServiceProc] = nil
199-
200-
# No service to step, no service handle, no ruby exceptions, just terminate the thread..
201-
unless StartServiceCtrlDispatcher(ste)
202-
return 1
203-
end
204-
205-
return 0
206-
end
207-
208189
# This is a shortcut for Daemon.new + Daemon#mainloop.
209190
#
210191
def self.mainloop
@@ -254,26 +235,29 @@ def mainloop
254235
raise SystemCallError.new("CreateEvent", FFI.errno)
255236
end
256237

257-
hThread = CreateThread(nil, 0, ThreadProc, Service_Main, 0, nil)
238+
hThread = Thread.new(Service_Main) do |lp_proc|
239+
ste = FFI::MemoryPointer.new(SERVICE_TABLE_ENTRY, 2)
258240

259-
if hThread == 0
260-
raise SystemCallError.new("CreateThread", FFI.errno)
261-
end
241+
s = SERVICE_TABLE_ENTRY.new(ste[0])
242+
s[:lpServiceName] = FFI::MemoryPointer.from_string("")
243+
s[:lpServiceProc] = lp_proc
262244

263-
events = FFI::MemoryPointer.new(:pointer, 2)
264-
events.put_pointer(0, FFI::Pointer.new(hThread))
265-
events.put_pointer(FFI::Pointer.size, FFI::Pointer.new(@@hStartEvent))
245+
s = SERVICE_TABLE_ENTRY.new(ste[1])
246+
s[:lpServiceName] = nil
247+
s[:lpServiceProc] = nil
266248

267-
while (index = WaitForMultipleObjects(2, events, 0, 1000)) == WAIT_TIMEOUT
249+
# When returning 'false', there is no service to stop, no service handle,
250+
# no ruby exceptions, just terminate the thread.
251+
StartServiceCtrlDispatcher(ste)
268252
end
269253

270-
if index == WAIT_FAILED
271-
raise SystemCallError.new("WaitForMultipleObjects", FFI.errno)
254+
while (index = WaitForSingleObject(@@hStartEvent, 1000)) == WAIT_TIMEOUT
255+
# The thread exited, so the show is off.
256+
raise "Service_Main thread exited abnormally" unless hThread.alive?
272257
end
273258

274-
# The thread exited, so the show is off.
275-
if index == WAIT_OBJECT_0
276-
raise "Service_Main thread exited abnormally"
259+
if index == WAIT_FAILED
260+
raise SystemCallError.new("WaitForSingleObject", FFI.errno)
277261
end
278262

279263
thr = Thread.new do

lib/win32/windows/functions.rb

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,13 @@ def attach_pfunc(*args)
2323

2424
attach_pfunc :CloseHandle, [:handle], :bool
2525
attach_pfunc :CreateEvent, :CreateEventA, %i{ptr int int str}, :handle
26-
attach_pfunc :CreateThread, %i{ptr size_t ptr ptr dword ptr}, :handle, blocking: true
2726
attach_pfunc :EnterCriticalSection, [:ptr], :void
2827
attach_pfunc :FormatMessage, :FormatMessageA, %i{ulong ptr ulong ulong str ulong ptr}, :ulong
2928
attach_pfunc :GetCurrentProcess, [], :handle
3029
attach_pfunc :InitializeCriticalSection, [:ptr], :void
3130
attach_pfunc :LeaveCriticalSection, [:ptr], :void
3231
attach_pfunc :SetEvent, [:handle], :bool
3332
attach_pfunc :WaitForSingleObject, %i{handle dword}, :dword, blocking: true
34-
attach_pfunc :WaitForMultipleObjects, %i{dword ptr int dword}, :dword
3533

3634
ffi_lib :advapi32
3735

0 commit comments

Comments
 (0)