Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package rubygem-serverengine for 
openSUSE:Factory checked in at 2022-08-09 15:26:53
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/rubygem-serverengine (Old)
 and      /work/SRC/openSUSE:Factory/.rubygem-serverengine.new.1521 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "rubygem-serverengine"

Tue Aug  9 15:26:53 2022 rev:11 rq:993521 version:2.3.0

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/rubygem-serverengine/rubygem-serverengine.changes    
    2022-02-02 22:45:02.678055215 +0100
+++ 
/work/SRC/openSUSE:Factory/.rubygem-serverengine.new.1521/rubygem-serverengine.changes
      2022-08-09 15:27:10.541423460 +0200
@@ -1,0 +2,19 @@
+Thu Aug  4 13:29:16 UTC 2022 - Stephan Kulow <[email protected]>
+
+updated to version 2.3.0
+ see installed Changelog
+
+  2022-06-13 version 2.3.0
+  
+  * Add restart_worker_interval option to prevent workers restart immediately
+    after kill
+  * Reopen log file when rotation done by external tool is detected
+  * Fix unexpected behavior of start_worker_delay option
+  * Remove windows-pr dependency
+  * Fix a potential crash that command_sender_pipe of ProcessManager::Monitor
+    raises error on shutdown
+  * Allow to load serverengine/socket_manager without servernegine/utils
+  * Fix unstable tests
+  
+
+-------------------------------------------------------------------

Old:
----
  serverengine-2.2.5.gem

New:
----
  serverengine-2.3.0.gem

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ rubygem-serverengine.spec ++++++
--- /var/tmp/diff_new_pack.aDmOWr/_old  2022-08-09 15:27:10.997424763 +0200
+++ /var/tmp/diff_new_pack.aDmOWr/_new  2022-08-09 15:27:11.001424774 +0200
@@ -24,7 +24,7 @@
 #
 
 Name:           rubygem-serverengine
-Version:        2.2.5
+Version:        2.3.0
 Release:        0
 %define mod_name serverengine
 %define mod_full_name %{mod_name}-%{version}

++++++ serverengine-2.2.5.gem -> serverengine-2.3.0.gem ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/.github/workflows/linux.yml 
new/.github/workflows/linux.yml
--- old/.github/workflows/linux.yml     2022-01-13 10:47:58.000000000 +0100
+++ new/.github/workflows/linux.yml     2022-06-13 11:49:05.000000000 +0200
@@ -11,7 +11,7 @@
     strategy:
       fail-fast: false
       matrix:
-        ruby: [ '3.1', '3.0', '2.7', '2.6' ]
+        ruby: [ '3.1', '3.0', '2.7' ]
         os:
           - ubuntu-latest
     name: Unit testing with Ruby ${{ matrix.ruby }} on ${{ matrix.os }}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/.github/workflows/windows.yml 
new/.github/workflows/windows.yml
--- old/.github/workflows/windows.yml   2022-01-13 10:47:58.000000000 +0100
+++ new/.github/workflows/windows.yml   2022-06-13 11:49:05.000000000 +0200
@@ -11,7 +11,7 @@
     strategy:
       fail-fast: false
       matrix:
-        ruby: [ '3.1', '2.7', '2.6' ]
+        ruby: [ '3.1', '2.7' ]
         os:
           - windows-latest
         include:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Changelog new/Changelog
--- old/Changelog       2022-01-13 10:47:58.000000000 +0100
+++ new/Changelog       2022-06-13 11:49:05.000000000 +0200
@@ -1,3 +1,15 @@
+2022-06-13 version 2.3.0
+
+* Add restart_worker_interval option to prevent workers restart immediately
+  after kill
+* Reopen log file when rotation done by external tool is detected
+* Fix unexpected behavior of start_worker_delay option
+* Remove windows-pr dependency
+* Fix a potential crash that command_sender_pipe of ProcessManager::Monitor
+  raises error on shutdown
+* Allow to load serverengine/socket_manager without servernegine/utils
+* Fix unstable tests
+
 2022-01-13 version 2.2.5:
 
 * Fix DLL load error on Ruby 3.1 on Windows
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/README.md new/README.md
--- old/README.md       2022-01-13 10:47:58.000000000 +0100
+++ new/README.md       2022-06-13 11:49:05.000000000 +0200
@@ -478,10 +478,11 @@
   - **disable_reload** disables USR2 signal (default: false)
   - **server_restart_wait** sets wait time before restarting server after last 
restarting (default: 1.0) [dynamic reloadable]
   - **server_detach_wait** sets wait time before starting live restart 
(default: 10.0) [dynamic reloadable]
-- Multithread server and multiprocess server: available only when 
`worker_type` is thread or process
+- Multithread server and multiprocess server: available only when 
`worker_type` is "thread" or "process" or "spawn"
   - **workers** sets number of workers (default: 1) [dynamic reloadable]
-  - **start_worker_delay** sets wait time before starting a new worker 
(default: 0) [dynamic reloadable]
+  - **start_worker_delay** sets the delay between each worker-start when 
starting/restarting multiple workers at once (default: 0) [dynamic reloadable]
   - **start_worker_delay_rand** randomizes start_worker_delay at this ratio 
(default: 0.2) [dynamic reloadable]
+  - **restart_worker_interval** sets wait time before restarting a stopped 
worker (default: 0) [dynamic reloadable]
 - Multiprocess server: available only when `worker_type` is "process"
   - **worker_process_name** changes process name ($0) of workers [dynamic 
reloadable]
   - **worker_heartbeat_interval** sets interval of heartbeats in seconds 
(default: 1.0) [dynamic reloadable]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Rakefile new/Rakefile
--- old/Rakefile        2022-01-13 10:47:58.000000000 +0100
+++ new/Rakefile        2022-06-13 11:49:05.000000000 +0200
@@ -8,19 +8,3 @@
 
 RSpec::Core::RakeTask.new(:spec)
 task :default => [:spec, :build]
-
-# 1. update Changelog and lib/serverengine/version.rb
-# 2. bundle && bundle exec rake build:all
-# 3. release 3 packages built on pkg/ directory
-namespace :build do
-  desc 'Build gems for all platforms'
-  task :all do
-    Bundler.with_clean_env do
-      %w[ruby x86-mingw32 x64-mingw32].each do |name|
-        ENV['GEM_BUILD_FAKE_PLATFORM'] = name
-        Rake::Task["build"].execute
-      end
-    end
-  end
-end
-
Binary files old/checksums.yaml.gz and new/checksums.yaml.gz differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/serverengine/daemon_logger.rb 
new/lib/serverengine/daemon_logger.rb
--- old/lib/serverengine/daemon_logger.rb       2022-01-13 10:47:58.000000000 
+0100
+++ new/lib/serverengine/daemon_logger.rb       2022-06-13 11:49:05.000000000 
+0200
@@ -55,6 +55,9 @@
         # update path string
         old_file_dev = @file_dev
         @file_dev = LogDevice.new(logdev, shift_age: @rotate_age, shift_size: 
@rotate_size)
+        # Enable to detect rotation done by external tools.
+        # Otherwise it continues writing logs to old file unexpectedly.
+        @file_dev.extend(RotationAware)
         old_file_dev.close if old_file_dev
         @logdev = @file_dev
       end
@@ -130,6 +133,40 @@
       nil
     end
 
+    module RotationAware
+      def self.extended(obj)
+        obj.update_ino
+      end
+
+      def update_ino
+        (@ino_mutex ||= Mutex.new).synchronize do
+          @ino = File.stat(filename).ino rescue nil
+          @last_ino_time = Time.now
+        end
+      end
+
+      def reopen(log = nil)
+        super(log)
+        update_ino
+      end
+
+      def reopen!
+        super
+        update_ino
+      end
+
+      def write(message)
+        reopen_needed = false
+        @ino_mutex.synchronize do
+          if (Time.now - @last_ino_time).abs > 1
+            ino = File.stat(filename).ino rescue nil
+            reopen_needed = true if ino && ino != @ino
+          end
+        end
+        reopen! if reopen_needed
+        super(message)
+      end
+    end
   end
 
 end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/serverengine/multi_process_server.rb 
new/lib/serverengine/multi_process_server.rb
--- old/lib/serverengine/multi_process_server.rb        2022-01-13 
10:47:58.000000000 +0100
+++ new/lib/serverengine/multi_process_server.rb        2022-06-13 
11:49:05.000000000 +0200
@@ -105,9 +105,11 @@
         @unrecoverable_exit_codes = unrecoverable_exit_codes
         @unrecoverable_exit = false
         @exitstatus = nil
+        @restart_at = nil
       end
 
       attr_reader :exitstatus
+      attr_accessor :restart_at
 
       def send_stop(stop_graceful)
         @stop = true
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/serverengine/multi_thread_server.rb 
new/lib/serverengine/multi_thread_server.rb
--- old/lib/serverengine/multi_thread_server.rb 2022-01-13 10:47:58.000000000 
+0100
+++ new/lib/serverengine/multi_thread_server.rb 2022-06-13 11:49:05.000000000 
+0200
@@ -39,8 +39,11 @@
       def initialize(worker, thread)
         @worker = worker
         @thread = thread
+        @restart_at = nil
       end
 
+      attr_accessor :restart_at
+
       def send_stop(stop_graceful)
         Thread.new do
           begin
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/serverengine/multi_worker_server.rb 
new/lib/serverengine/multi_worker_server.rb
--- old/lib/serverengine/multi_worker_server.rb 2022-01-13 10:47:58.000000000 
+0100
+++ new/lib/serverengine/multi_worker_server.rb 2022-06-13 11:49:05.000000000 
+0200
@@ -55,8 +55,8 @@
 
     def run
       while true
-        num_alive = keepalive_workers
-        break if num_alive == 0
+        num_alive_or_restarting = keepalive_workers
+        break if num_alive_or_restarting == 0
         wait_tick
       end
     end
@@ -85,6 +85,7 @@
 
       @start_worker_delay = @config[:start_worker_delay] || 0
       @start_worker_delay_rand = @config[:start_worker_delay_rand] || 0.2
+      @restart_worker_interval = @config[:restart_worker_interval] || 0
 
       scale_workers(@config[:workers] || 1)
 
@@ -96,12 +97,12 @@
     end
 
     def keepalive_workers
-      num_alive = 0
+      num_alive_or_restarting = 0
 
       @monitors.each_with_index do |m,wid|
         if m && m.alive?
           # alive
-          num_alive += 1
+          num_alive_or_restarting += 1
 
         elsif m && m.respond_to?(:recoverable?) && !m.recoverable?
           # exited, with unrecoverable exit code
@@ -116,8 +117,12 @@
         elsif wid < @num_workers
           # scale up or reboot
           unless @stop
-            @monitors[wid] = delayed_start_worker(wid)
-            num_alive += 1
+            if m
+              restart_worker(wid)
+            else
+              start_new_worker(wid)
+            end
+            num_alive_or_restarting += 1
           end
 
         elsif m
@@ -126,7 +131,27 @@
         end
       end
 
-      return num_alive
+      return num_alive_or_restarting
+    end
+
+    def start_new_worker(wid)
+      delayed_start_worker(wid)
+    end
+
+    def restart_worker(wid)
+      m = @monitors[wid]
+
+      is_already_restarting = !m.restart_at.nil?
+      if is_already_restarting
+        delayed_start_worker(wid) if m.restart_at <= Time.now()
+        return
+      end
+
+      if @restart_worker_interval > 0
+        m.restart_at = Time.now() + @restart_worker_interval
+      else
+        delayed_start_worker(wid)
+      end
     end
 
     def delayed_start_worker(wid)
@@ -135,15 +160,13 @@
           Kernel.rand * @start_worker_delay * @start_worker_delay_rand -
           @start_worker_delay * @start_worker_delay_rand / 2
 
-        now = Time.now.to_f
-
-        wait = delay - (now - @last_start_worker_time)
+        wait = delay - (Time.now.to_f - @last_start_worker_time)
         sleep wait if wait > 0
 
-        @last_start_worker_time = now
+        @last_start_worker_time = Time.now.to_f
       end
 
-      start_worker(wid)
+      @monitors[wid] = start_worker(wid)
     end
   end
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/serverengine/process_manager.rb 
new/lib/serverengine/process_manager.rb
--- old/lib/serverengine/process_manager.rb     2022-01-13 10:47:58.000000000 
+0100
+++ new/lib/serverengine/process_manager.rb     2022-06-13 11:49:05.000000000 
+0200
@@ -333,7 +333,15 @@
       end
 
       def send_command(command)
-        @command_sender_pipe.write(command) if @command_sender_pipe
+        pid = @pid
+        return false unless pid
+
+        begin
+          @command_sender_pipe.write(command)
+          return true
+        rescue #Errno::EPIPE
+          return false
+        end
       end
 
       def try_join
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/serverengine/server.rb 
new/lib/serverengine/server.rb
--- old/lib/serverengine/server.rb      2022-01-13 10:47:58.000000000 +0100
+++ new/lib/serverengine/server.rb      2022-06-13 11:49:05.000000000 +0200
@@ -80,7 +80,7 @@
       if @command_pipe
         Thread.new do
           until @command_pipe.closed?
-            case @command_pipe.gets.chomp
+            case @command_pipe.gets&.chomp
             when "GRACEFUL_STOP"
               s.stop(true)
             when "IMMEDIATE_STOP"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/serverengine/socket_manager.rb 
new/lib/serverengine/socket_manager.rb
--- old/lib/serverengine/socket_manager.rb      2022-01-13 10:47:58.000000000 
+0100
+++ new/lib/serverengine/socket_manager.rb      2022-06-13 11:49:05.000000000 
+0200
@@ -22,6 +22,8 @@
 require 'json'
 require 'base64'
 
+require_relative 'utils' # for ServerEngine.windows?
+
 module ServerEngine
   module SocketManager
     # This token is used for communication between peers. If token is 
mismatched, messages will be discarded
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/serverengine/version.rb 
new/lib/serverengine/version.rb
--- old/lib/serverengine/version.rb     2022-01-13 10:47:58.000000000 +0100
+++ new/lib/serverengine/version.rb     2022-06-13 11:49:05.000000000 +0200
@@ -1,3 +1,3 @@
 module ServerEngine
-  VERSION = "2.2.5"
+  VERSION = "2.3.0"
 end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/serverengine/winsock.rb 
new/lib/serverengine/winsock.rb
--- old/lib/serverengine/winsock.rb     2022-01-13 10:47:58.000000000 +0100
+++ new/lib/serverengine/winsock.rb     2022-06-13 11:49:05.000000000 +0200
@@ -99,7 +99,6 @@
     extend Fiddle::Importer
 
     dlload "kernel32"
-    extern "int GetModuleFileNameA(int, char *, int)"
     extern "int CloseHandle(int)"
 
     dlload RbConfig::CONFIG['LIBRUBY_SO']
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/serverengine.rb new/lib/serverengine.rb
--- old/lib/serverengine.rb     2022-01-13 10:47:58.000000000 +0100
+++ new/lib/serverengine.rb     2022-06-13 11:49:05.000000000 +0200
@@ -35,12 +35,27 @@
 
   def self.ruby_bin_path
     if ServerEngine.windows?
-      require 'windows/library'
-      ruby_path = "\0" * 256
-      Windows::Library::GetModuleFileName.call(0, ruby_path, 256)
-      return ruby_path.rstrip.gsub(/\\/, '/')
+      ServerEngine::Win32.ruby_bin_path
     else
-      return File.join(RbConfig::CONFIG["bindir"], 
RbConfig::CONFIG["RUBY_INSTALL_NAME"]) + RbConfig::CONFIG["EXEEXT"]
+      File.join(RbConfig::CONFIG["bindir"], 
RbConfig::CONFIG["RUBY_INSTALL_NAME"]) + RbConfig::CONFIG["EXEEXT"]
+    end
+  end
+
+  if ServerEngine.windows?
+    module Win32
+      require 'fiddle/import'
+
+      extend Fiddle::Importer
+
+      dlload "kernel32"
+      extern "int GetModuleFileNameW(int, void *, int)"
+
+      def self.ruby_bin_path
+        ruby_bin_path_buf = Fiddle::Pointer.malloc(1024)
+        len = GetModuleFileNameW(0, ruby_bin_path_buf, ruby_bin_path_buf.size 
/ 2)
+        path_bytes = ruby_bin_path_buf[0, len * 2]
+        path_bytes.encode('UTF-8', 'UTF-16LE').gsub(/\\/, '/')
+      end
     end
   end
 end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/metadata new/metadata
--- old/metadata        2022-01-13 10:47:58.000000000 +0100
+++ new/metadata        2022-06-13 11:49:05.000000000 +0200
@@ -1,14 +1,14 @@
 --- !ruby/object:Gem::Specification
 name: serverengine
 version: !ruby/object:Gem::Version
-  version: 2.2.5
+  version: 2.3.0
 platform: ruby
 authors:
 - Sadayuki Furuhashi
 autorequire: 
 bindir: bin
 cert_chain: []
-date: 2022-01-13 00:00:00.000000000 Z
+date: 2022-06-13 00:00:00.000000000 Z
 dependencies:
 - !ruby/object:Gem::Dependency
   name: sigdump
@@ -80,6 +80,20 @@
     - - "~>"
       - !ruby/object:Gem::Version
         version: 0.9.4
+- !ruby/object:Gem::Dependency
+  name: timecop
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - "~>"
+      - !ruby/object:Gem::Version
+        version: 0.9.5
+  type: :development
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - "~>"
+      - !ruby/object:Gem::Version
+        version: 0.9.5
 description: A framework to implement robust multiprocess servers like Unicorn
 email:
 - [email protected]
@@ -154,7 +168,7 @@
     - !ruby/object:Gem::Version
       version: '0'
 requirements: []
-rubygems_version: 3.2.5
+rubygems_version: 3.3.7
 signing_key: 
 specification_version: 4
 summary: ServerEngine - multiprocess server framework
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/serverengine.gemspec new/serverengine.gemspec
--- old/serverengine.gemspec    2022-01-13 10:47:58.000000000 +0100
+++ new/serverengine.gemspec    2022-06-13 11:49:05.000000000 +0200
@@ -27,11 +27,5 @@
   gem.add_development_dependency 'rake-compiler-dock', ['~> 0.5.0']
   gem.add_development_dependency 'rake-compiler', ['~> 0.9.4']
 
-  # build gem for a certain platform. see also Rakefile
-  fake_platform = ENV['GEM_BUILD_FAKE_PLATFORM'].to_s
-  gem.platform = fake_platform unless fake_platform.empty?
-  if /mswin|mingw/ =~ fake_platform || (/mswin|mingw/ =~ RUBY_PLATFORM && 
fake_platform.empty?)
-    # windows dependencies
-    gem.add_runtime_dependency("windows-pr", ["~> 1.2.5"])
-  end
+  gem.add_development_dependency "timecop", ["~> 0.9.5"]
 end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spec/daemon_logger_spec.rb 
new/spec/daemon_logger_spec.rb
--- old/spec/daemon_logger_spec.rb      2022-01-13 10:47:58.000000000 +0100
+++ new/spec/daemon_logger_spec.rb      2022-06-13 11:49:05.000000000 +0200
@@ -1,4 +1,5 @@
 require 'stringio'
+require 'timecop'
 
 describe ServerEngine::DaemonLogger do
   before { FileUtils.rm_rf("tmp") }
@@ -172,4 +173,28 @@
     $stderr = STDERR
     stderr.should_not =~ /(log shifting failed|log writing failed|log rotation 
inter-process lock failed)/
   end
+
+  it 'reopen log when path is renamed' do
+    pending "rename isn't supported on windows" if ServerEngine.windows?
+
+    log = DaemonLogger.new("tmp/rotate.log", { level: 'info', log_rotate_age: 
0  })
+
+    log.info '11111'
+    File.read("tmp/rotate.log").should include('11111')
+    File.rename("tmp/rotate.log", "tmp/rotate.log.1")
+
+    Timecop.travel(Time.now + 1)
+
+    log.info '22222'
+    contents = File.read("tmp/rotate.log.1")
+    contents.should include('11111')
+    contents.should include('22222')
+
+    FileUtils.touch("tmp/rotate.log")
+    Timecop.travel(Time.now + 1)
+
+    log.info '33333'
+    File.read("tmp/rotate.log").should include('33333')
+    File.read("tmp/rotate.log.1").should_not include('33333')
+  end
 end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spec/multi_spawn_server_spec.rb 
new/spec/multi_spawn_server_spec.rb
--- old/spec/multi_spawn_server_spec.rb 2022-01-13 10:47:58.000000000 +0100
+++ new/spec/multi_spawn_server_spec.rb 2022-06-13 11:49:05.000000000 +0200
@@ -1,25 +1,208 @@
 require 'timeout'
+require 'timecop'
 
 describe ServerEngine::MultiSpawnServer do
   include_context 'test server and worker'
 
-  context 'with command_sender=pipe' do
-    it 'starts worker processes' do
-      config = {workers: 2, command_sender: 'pipe', log_stdout: false, 
log_stderr: false}
+  describe 'starts worker processes' do
+    context 'with command_sender=pipe' do
+      it do
+        config = {workers: 2, command_sender: 'pipe', log_stdout: false, 
log_stderr: false}
 
-      s = ServerEngine::MultiSpawnServer.new(TestWorker) { config.dup }
-      t = Thread.new { s.main }
+        s = ServerEngine::MultiSpawnServer.new(TestWorker) { config.dup }
+        t = Thread.new { s.main }
 
-      begin
-        wait_for_fork
+        begin
+          wait_for_fork
 
-        Timeout.timeout(5) do
-          sleep(0.5) until test_state(:worker_run) == 2
+          Timeout.timeout(5) do
+            sleep(0.5) until test_state(:worker_run) == 2
+          end
+          test_state(:worker_run).should == 2
+        ensure
+          s.stop(true)
+          t.join
+        end
+      end
+    end
+  end
+
+  describe 'keepalive_workers' do
+    let(:config) {
+      {
+        workers: workers,
+        command_sender: 'pipe',
+        log_stdout: false,
+        log_stderr: false,
+        start_worker_delay: start_worker_delay,
+        start_worker_delay_rand: 0,
+        restart_worker_interval: restart_worker_interval,
+      }
+    }
+    let(:workers) { 3 }
+    let(:server) { ServerEngine::MultiSpawnServer.new(TestWorker) { config.dup 
} }
+    let(:monitors) { server.instance_variable_get(:@monitors) }
+
+    context 'default' do
+      let(:start_worker_delay) { 0 }
+      let(:restart_worker_interval) { 0 }
+
+      it do
+        t = Thread.new { server.main }
+
+        begin
+          wait_for_fork
+
+          Timeout.timeout(5) do
+            sleep(0.5) until monitors.count { |m| m && m.alive? } == workers
+          end
+
+          monitors.each do |m|
+            m.send_stop(true)
+          end
+
+          # To prevent the judge before stopping once
+          wait_for_stop
+
+          -> {
+            Timeout.timeout(5) do
+              sleep(0.5) until monitors.count { |m| m.alive? } == workers
+            end
+          }.should_not raise_error, "Not all workers restarted correctly."
+        ensure
+          server.stop(true)
+          t.join
+        end
+      end
+    end
+
+    context 'with only restart_worker_interval' do
+      let(:start_worker_delay) { 0 }
+      let(:restart_worker_interval) { 10 }
+
+      it do
+        t = Thread.new { server.main }
+
+        begin
+          wait_for_fork
+
+          # Wait for initial starting
+          Timeout.timeout(5) do
+            sleep(0.5) until monitors.count { |m| m && m.alive? } == workers
+          end
+
+          monitors.each do |m|
+            m.send_stop(true)
+          end
+
+          # Wait for all workers to stop and to be set restarting time
+          Timeout.timeout(5) do
+            sleep(0.5) until monitors.count { |m| m.alive? || 
m.restart_at.nil? } == 0
+          end
+
+          Timecop.freeze
+
+          mergin_time = 3
+
+          Timecop.freeze(Time.now + restart_worker_interval - mergin_time)
+          sleep(1.5)
+          monitors.count { |m| m.alive? }.should == 0
+
+          Timecop.freeze(Time.now + 2 * mergin_time)
+          -> {
+            Timeout.timeout(5) do
+              sleep(0.5) until monitors.count { |m| m.alive? } == workers
+            end
+          }.should_not raise_error, "Not all workers restarted correctly."
+        ensure
+          server.stop(true)
+          t.join
+        end
+      end
+    end
+
+    context 'with only start_worker_delay' do
+      let(:start_worker_delay) { 3 }
+      let(:restart_worker_interval) { 0 }
+
+      it do
+        t = Thread.new { server.main }
+
+        begin
+          wait_for_fork
+
+          # Initial starts are delayed too, so set longer timeout.
+          # (`start_worker_delay` uses `sleep` inside, so Timecop can't skip 
this wait.)
+          Timeout.timeout(start_worker_delay * workers) do
+            sleep(0.5) until monitors.count { |m| m && m.alive? } == workers
+          end
+
+          # Skip time to avoid getting a delay for the initial starts.
+          Timecop.travel(Time.now + start_worker_delay)
+
+          monitors.each do |m|
+            m.send_stop(true)
+          end
+
+          sleep(3)
+
+          # The first worker should restart immediately.
+          monitors.count { |m| m.alive? }.should satisfy { |c| 0 < c && c < 
workers }
+
+          # `start_worker_delay` uses `sleep` inside, so Timecop can't skip 
this wait.
+          sleep(start_worker_delay * workers)
+          monitors.count { |m| m.alive? }.should == workers
+        ensure
+          server.stop(true)
+          t.join
+        end
+      end
+    end
+
+    context 'with both options' do
+      let(:start_worker_delay) { 3 }
+      let(:restart_worker_interval) { 10 }
+
+      it do
+        t = Thread.new { server.main }
+
+        begin
+          wait_for_fork
+
+          # Initial starts are delayed too, so set longer timeout.
+          # (`start_worker_delay` uses `sleep` inside, so Timecop can't skip 
this wait.)
+          Timeout.timeout(start_worker_delay * workers) do
+            sleep(0.5) until monitors.count { |m| m && m.alive? } == workers
+          end
+
+          monitors.each do |m|
+            m.send_stop(true)
+          end
+
+          # Wait for all workers to stop and to be set restarting time
+          Timeout.timeout(5) do
+            sleep(0.5) until monitors.count { |m| m.alive? || 
m.restart_at.nil? } == 0
+          end
+
+          Timecop.freeze
+
+          mergin_time = 3
+
+          Timecop.freeze(Time.now + restart_worker_interval - mergin_time)
+          sleep(1.5)
+          monitors.count { |m| m.alive? }.should == 0
+
+          Timecop.travel(Time.now + 2 * mergin_time)
+          sleep(1.5)
+          monitors.count { |m| m.alive? }.should satisfy { |c| 0 < c && c < 
workers }
+
+          # `start_worker_delay` uses `sleep` inside, so Timecop can't skip 
this wait.
+          sleep(start_worker_delay * workers)
+          monitors.count { |m| m.alive? }.should == workers
+        ensure
+          server.stop(true)
+          t.join
         end
-        test_state(:worker_run).should == 2
-      ensure
-        s.stop(true)
-        t.join
       end
     end
   end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spec/server_worker_context.rb 
new/spec/server_worker_context.rb
--- old/spec/server_worker_context.rb   2022-01-13 10:47:58.000000000 +0100
+++ new/spec/server_worker_context.rb   2022-06-13 11:49:05.000000000 +0200
@@ -1,6 +1,7 @@
 
 require 'thread'
 require 'yaml'
+require 'timecop'
 
 def reset_test_state
   FileUtils.mkdir_p 'tmp'
@@ -165,8 +166,9 @@
 
   def run
     incr_test_state :worker_run
-    5.times do
-      # repeats 5 times because signal handlers
+    # This means this worker will automatically finish after 50 seconds.
+    10.times do
+      # repeats multiple times because signal handlers
       # interrupts wait
       @stop_flag.wait(5.0)
     end
@@ -252,16 +254,25 @@
 
 shared_context 'test server and worker' do
   before { reset_test_state }
+  after { Timecop.return }
+
+  unless self.const_defined?(:WAIT_RATIO)
+    if ServerEngine.windows?
+      WAIT_RATIO = 2
+    else
+      WAIT_RATIO = 1
+    end
+  end
 
   def wait_for_fork
-    sleep 1.5
+    sleep 1.5 * WAIT_RATIO
   end
 
   def wait_for_stop
-    sleep 0.8
+    sleep 0.8 * WAIT_RATIO
   end
 
   def wait_for_restart
-    sleep 1.5
+    sleep 1.5 * WAIT_RATIO
   end
 end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spec/spec_helper.rb new/spec/spec_helper.rb
--- old/spec/spec_helper.rb     2022-01-13 10:47:58.000000000 +0100
+++ new/spec/spec_helper.rb     2022-06-13 11:49:05.000000000 +0200
@@ -1,4 +1,9 @@
 require 'bundler'
+require 'rspec'
+
+RSpec.configure do |config|
+  config.color_enabled = true
+end
 
 begin
   Bundler.setup(:default, :test)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spec/winsock_spec.rb new/spec/winsock_spec.rb
--- old/spec/winsock_spec.rb    2022-01-13 10:47:58.000000000 +0100
+++ new/spec/winsock_spec.rb    2022-06-13 11:49:05.000000000 +0200
@@ -1,5 +1,3 @@
-require 'windows/error' if ServerEngine.windows?
-
 describe ServerEngine::WinSock do
   # On Ruby 3.0, you need to use fiddle 1.0.8 or later to retrieve a correct
   # error code. In addition, you need to specify the path of fiddle by RUBYLIB
@@ -12,7 +10,8 @@
   context 'last_error' do
     it 'bind error' do
       expect(WinSock.bind(0, nil, 0)).to be -1
-      expect(WinSock.last_error).to be Windows::Error::WSAENOTSOCK
+      WSAENOTSOCK = 10038
+      expect(WinSock.last_error).to be WSAENOTSOCK
     end
   end
 end if ServerEngine.windows?

Reply via email to