serverengine 1.6.4 → 2.0.0pre1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/Changelog +11 -0
  4. data/README.md +31 -3
  5. data/Rakefile +16 -3
  6. data/appveyor.yml +11 -5
  7. data/examples/server.rb +138 -0
  8. data/examples/spawn_worker_script.rb +38 -0
  9. data/lib/serverengine/blocking_flag.rb +2 -3
  10. data/lib/serverengine/command_sender.rb +89 -0
  11. data/lib/serverengine/config_loader.rb +2 -0
  12. data/lib/serverengine/daemon.rb +114 -86
  13. data/lib/serverengine/daemon_logger.rb +3 -139
  14. data/lib/serverengine/embedded_server.rb +2 -0
  15. data/lib/serverengine/multi_process_server.rb +28 -7
  16. data/lib/serverengine/multi_spawn_server.rb +17 -18
  17. data/lib/serverengine/multi_thread_server.rb +6 -0
  18. data/lib/serverengine/multi_worker_server.rb +14 -0
  19. data/lib/serverengine/privilege.rb +57 -0
  20. data/lib/serverengine/process_manager.rb +66 -48
  21. data/lib/serverengine/server.rb +45 -11
  22. data/lib/serverengine/signal_thread.rb +0 -2
  23. data/lib/serverengine/signals.rb +31 -0
  24. data/lib/serverengine/socket_manager.rb +3 -5
  25. data/lib/serverengine/socket_manager_unix.rb +1 -0
  26. data/lib/serverengine/socket_manager_win.rb +4 -2
  27. data/lib/serverengine/supervisor.rb +105 -25
  28. data/lib/serverengine/utils.rb +23 -0
  29. data/lib/serverengine/version.rb +1 -1
  30. data/lib/serverengine/worker.rb +10 -7
  31. data/lib/serverengine.rb +9 -27
  32. data/serverengine.gemspec +12 -1
  33. data/spec/daemon_logger_spec.rb +17 -12
  34. data/spec/daemon_spec.rb +147 -24
  35. data/spec/multi_process_server_spec.rb +59 -7
  36. data/spec/server_worker_context.rb +104 -0
  37. data/spec/signal_thread_spec.rb +61 -56
  38. data/spec/supervisor_spec.rb +113 -99
  39. metadata +40 -6
@@ -32,6 +32,29 @@ module ServerEngine
32
32
  }
33
33
  nil
34
34
  end
35
+
36
+ def format_signal_name(n)
37
+ Signal.list.each_pair {|k,v|
38
+ return "SIG#{k}" if n == v
39
+ }
40
+ return n
41
+ end
42
+
43
+ def format_join_status(code)
44
+ case code
45
+ when Process::Status
46
+ if code.signaled?
47
+ "signal #{format_signal_name(code.termsig)}"
48
+ else
49
+ "status #{code.exitstatus}"
50
+ end
51
+ when Exception
52
+ "exception #{code}"
53
+ when nil
54
+ "unknown reason"
55
+ end
56
+ end
57
+
35
58
  end
36
59
 
37
60
  extend ClassMethods
@@ -1,3 +1,3 @@
1
1
  module ServerEngine
2
- VERSION = "1.6.4"
2
+ VERSION = "2.0.0pre1"
3
3
  end
@@ -15,6 +15,9 @@
15
15
  # See the License for the specific language governing permissions and
16
16
  # limitations under the License.
17
17
  #
18
+ require 'serverengine/signals'
19
+ require 'serverengine/signal_thread'
20
+
18
21
  module ServerEngine
19
22
 
20
23
  class Worker
@@ -54,19 +57,19 @@ module ServerEngine
54
57
  def install_signal_handlers
55
58
  w = self
56
59
  SignalThread.new do |st|
57
- st.trap(Daemon::Signals::GRACEFUL_STOP) { w.stop }
58
- st.trap(Daemon::Signals::IMMEDIATE_STOP, 'SIG_DFL')
60
+ st.trap(Signals::GRACEFUL_STOP) { w.stop }
61
+ st.trap(Signals::IMMEDIATE_STOP, 'SIG_DFL')
59
62
 
60
- st.trap(Daemon::Signals::GRACEFUL_RESTART) { w.stop }
61
- st.trap(Daemon::Signals::IMMEDIATE_RESTART, 'SIG_DFL')
63
+ st.trap(Signals::GRACEFUL_RESTART) { w.stop }
64
+ st.trap(Signals::IMMEDIATE_RESTART, 'SIG_DFL')
62
65
 
63
- st.trap(Daemon::Signals::RELOAD) {
66
+ st.trap(Signals::RELOAD) {
64
67
  w.logger.reopen!
65
68
  w.reload
66
69
  }
67
- st.trap(Daemon::Signals::DETACH) { w.stop }
70
+ st.trap(Signals::DETACH) { w.stop }
68
71
 
69
- st.trap(Daemon::Signals::DUMP) { Sigdump.dump }
72
+ st.trap(Signals::DUMP) { Sigdump.dump }
70
73
  end
71
74
  end
72
75
 
data/lib/serverengine.rb CHANGED
@@ -19,34 +19,15 @@ module ServerEngine
19
19
 
20
20
  require 'sigdump'
21
21
 
22
- here = File.expand_path(File.dirname(__FILE__))
22
+ require 'serverengine/version'
23
23
 
24
- {
25
- :BlockingFlag => 'serverengine/blocking_flag',
26
- :SignalThread => 'serverengine/signal_thread',
27
- :DaemonLogger => 'serverengine/daemon_logger',
28
- :ConfigLoader => 'serverengine/config_loader',
29
- :Daemon => 'serverengine/daemon',
30
- :Supervisor => 'serverengine/supervisor',
31
- :Server => 'serverengine/server',
32
- :EmbeddedServer => 'serverengine/embedded_server',
33
- :MultiWorkerServer => 'serverengine/multi_worker_server',
34
- :MultiProcessServer => 'serverengine/multi_process_server',
35
- :MultiThreadServer => 'serverengine/multi_thread_server',
36
- :MultiSpawnServer => 'serverengine/multi_spawn_server',
37
- :ProcessManager => 'serverengine/process_manager',
38
- :SocketManager => 'serverengine/socket_manager',
39
- :Worker => 'serverengine/worker',
40
- :VERSION => 'serverengine/version',
41
- }.each_pair {|k,v|
42
- autoload k, File.expand_path(v, File.dirname(__FILE__))
43
- }
24
+ require 'serverengine/utils' # ServerEngine.windows? and other util methods
44
25
 
45
- [
46
- 'serverengine/utils',
47
- ].each {|v|
48
- require File.join(here, v)
49
- }
26
+ require 'serverengine/daemon'
27
+ require 'serverengine/supervisor'
28
+ require 'serverengine/server'
29
+ require 'serverengine/worker'
30
+ require 'serverengine/socket_manager'
50
31
 
51
32
  def self.create(server_module, worker_module, load_config_proc={}, &block)
52
33
  Daemon.new(server_module, worker_module, load_config_proc, &block)
@@ -54,8 +35,9 @@ module ServerEngine
54
35
 
55
36
  def self.ruby_bin_path
56
37
  if ServerEngine.windows?
38
+ require 'windows/library'
57
39
  ruby_path = "\0" * 256
58
- GetModuleFileName.call(0, ruby_path, 256)
40
+ Windows::Library::GetModuleFileName.call(0, ruby_path, 256)
59
41
  return ruby_path.rstrip.gsub(/\\/, '/')
60
42
  else
61
43
  return File.join(RbConfig::CONFIG["bindir"], RbConfig::CONFIG["RUBY_INSTALL_NAME"]) + RbConfig::CONFIG["EXEEXT"]
data/serverengine.gemspec CHANGED
@@ -17,10 +17,21 @@ Gem::Specification.new do |gem|
17
17
  gem.require_paths = ["lib"]
18
18
  gem.has_rdoc = false
19
19
 
20
- gem.required_ruby_version = ">= 1.9.3"
20
+ gem.required_ruby_version = ">= 2.1.0"
21
21
 
22
22
  gem.add_dependency "sigdump", ["~> 0.2.2"]
23
23
 
24
24
  gem.add_development_dependency "rake", [">= 0.9.2"]
25
25
  gem.add_development_dependency "rspec", ["~> 2.13.0"]
26
+
27
+ gem.add_development_dependency 'rake-compiler-dock', ['~> 0.5.0']
28
+ gem.add_development_dependency 'rake-compiler', ['~> 0.9.4']
29
+
30
+ # build gem for a certain platform. see also Rakefile
31
+ fake_platform = ENV['GEM_BUILD_FAKE_PLATFORM'].to_s
32
+ gem.platform = fake_platform unless fake_platform.empty?
33
+ if /mswin|mingw/ =~ fake_platform || (/mswin|mingw/ =~ RUBY_PLATFORM && fake_platform.empty?)
34
+ # windows dependencies
35
+ gem.add_runtime_dependency("windows-pr", ["~> 1.2.5"])
36
+ end
26
37
  end
@@ -1,10 +1,12 @@
1
1
  require 'stringio'
2
2
 
3
3
  describe ServerEngine::DaemonLogger do
4
+ before { FileUtils.rm_rf("tmp") }
4
5
  before { FileUtils.mkdir_p("tmp") }
5
6
  before { FileUtils.rm_f("tmp/se1.log") }
6
- before { FileUtils.rm_f Dir["tmp/se1.log.**"] }
7
7
  before { FileUtils.rm_f("tmp/se2.log") }
8
+ before { FileUtils.rm_f Dir["tmp/se3.log.**"] }
9
+ before { FileUtils.rm_f Dir["tmp/se4.log.**"] }
8
10
 
9
11
  subject { DaemonLogger.new("tmp/se1.log", level: 'trace') }
10
12
 
@@ -113,26 +115,27 @@ describe ServerEngine::DaemonLogger do
113
115
  end
114
116
 
115
117
  it 'rotation' do
116
- log = DaemonLogger.new("tmp/se1.log", level: 'trace', log_rotate_age: 3, log_rotate_size: 10000)
118
+ log = DaemonLogger.new("tmp/se3.log", level: 'trace', log_rotate_age: 3, log_rotate_size: 10000)
117
119
  # 100 bytes
118
120
  log.warn "test1"*20
119
- File.exist?("tmp/se1.log").should == true
120
- File.exist?("tmp/se1.log.0").should == false
121
+ File.exist?("tmp/se3.log").should == true
122
+ File.exist?("tmp/se3.log.0").should == false
121
123
 
122
124
  # 10000 bytes
123
125
  100.times { log.warn "test2"*20 }
124
- File.exist?("tmp/se1.log").should == true
125
- File.exist?("tmp/se1.log.0").should == true
126
- File.read("tmp/se1.log.0") =~ /test2$/
126
+ File.exist?("tmp/se3.log").should == true
127
+ File.exist?("tmp/se3.log.0").should == true
128
+ File.read("tmp/se3.log.0") =~ /test2$/
127
129
 
128
130
  # 10000 bytes
129
131
  100.times { log.warn "test3"*20 }
130
- File.exist?("tmp/se1.log").should == true
131
- File.exist?("tmp/se1.log.1").should == true
132
- File.exist?("tmp/se1.log.2").should == false
132
+ File.exist?("tmp/se3.log").should == true
133
+ File.exist?("tmp/se3.log.1").should == true
134
+ File.exist?("tmp/se3.log.2").should == false
133
135
 
134
136
  log.warn "test4"*20
135
- File.read("tmp/se1.log.0") =~ /test5$/
137
+ File.read("tmp/se3.log").should =~ /test4$/
138
+ File.read("tmp/se3.log.0").should =~ /test3$/
136
139
  end
137
140
 
138
141
  it 'IO logger' do
@@ -146,7 +149,9 @@ describe ServerEngine::DaemonLogger do
146
149
  end
147
150
 
148
151
  it 'inter-process locking on rotation' do
149
- log = DaemonLogger.new("tmp/se1.log", level: 'trace', log_rotate_age: 3, log_rotate_size: 10)
152
+ pending "fork is not implemented in Windows" if ServerEngine.windows?
153
+
154
+ log = DaemonLogger.new("tmp/se4.log", level: 'trace', log_rotate_age: 3, log_rotate_size: 10)
150
155
  r, w = IO.pipe
151
156
  $stderr = w # To capture #warn output in DaemonLogger
152
157
  pid1 = Process.fork do
data/spec/daemon_spec.rb CHANGED
@@ -2,45 +2,168 @@
2
2
  describe ServerEngine::Daemon do
3
3
  include_context 'test server and worker'
4
4
 
5
- it 'run and graceful stop' do
6
- dm = Daemon.new(TestServer, TestWorker, daemonize: true, pid_path: "tmp/pid")
5
+ it 'run and graceful stop by signal' do
6
+ pending "not supported signal base commands on Windows" if ServerEngine.windows?
7
+
8
+ dm = Daemon.new(TestServer, TestWorker, daemonize: true, pid_path: "tmp/pid", command_sender: "signal")
7
9
  dm.main
8
10
 
11
+ wait_for_fork
12
+
9
13
  test_state(:server_initialize).should == 1
10
14
 
11
- pid = File.read('tmp/pid').to_i
15
+ begin
16
+ dm.stop(true)
17
+ wait_for_stop
18
+
19
+ test_state(:server_stop_graceful).should == 1
20
+ test_state(:worker_stop).should == 1
21
+ test_state(:server_after_run).should == 1
22
+ ensure
23
+ dm.stop(false) rescue nil
24
+ end
25
+ end
26
+
27
+ it 'signals' do
28
+ pending "not supported signal base commands on Windows" if ServerEngine.windows?
29
+ dm = Daemon.new(TestServer, TestWorker, daemonize: true, pid_path: "tmp/pid", command_sender: "signal")
30
+ dm.main
31
+
12
32
  wait_for_fork
13
33
 
14
- Process.kill(:TERM, pid)
15
- wait_for_stop
34
+ begin
35
+ dm.reload
36
+ wait_for_stop
37
+ test_state(:server_reload).should == 1
16
38
 
17
- test_state(:server_stop_graceful).should == 1
18
- test_state(:worker_stop).should == 1
19
- test_state(:server_after_run).should == 1
39
+ dm.restart(true)
40
+ wait_for_stop
41
+ test_state(:server_restart_graceful).should == 1
42
+
43
+ dm.restart(false)
44
+ wait_for_stop
45
+ test_state(:server_restart_immediate).should == 1
46
+
47
+ dm.stop(false)
48
+ wait_for_stop
49
+ test_state(:server_stop_immediate).should == 1
50
+ ensure
51
+ dm.stop(true) rescue nil
52
+ end
20
53
  end
21
54
 
22
- it 'signals' do
23
- dm = Daemon.new(TestServer, TestWorker, daemonize: true, pid_path: "tmp/pid")
55
+ it 'run and graceful stop by pipe' do
56
+ dm = Daemon.new(TestServer, TestWorker, daemonize: true, pid_path: "tmp/pid", windows_daemon_cmdline: windows_daemon_cmdline, command_sender: "pipe")
57
+ dm.main
58
+
59
+ wait_for_fork
60
+
61
+ test_state(:server_initialize).should == 1
62
+
63
+ begin
64
+ dm.stop(true)
65
+ wait_for_stop
66
+
67
+ test_state(:server_stop_graceful).should == 1
68
+ test_state(:worker_stop).should == 1
69
+ test_state(:server_after_run).should == 1
70
+ ensure
71
+ dm.stop(false) rescue nil
72
+ end
73
+ end
74
+
75
+ it 'recieve commands from pipe' do
76
+ dm = Daemon.new(TestServer, TestWorker, daemonize: true, pid_path: "tmp/pid", windows_daemon_cmdline: windows_daemon_cmdline, command_sender: "pipe")
24
77
  dm.main
25
78
 
26
- pid = File.read('tmp/pid').to_i
27
79
  wait_for_fork
28
80
 
29
- Process.kill(:USR2, pid)
30
- wait_for_stop
31
- test_state(:server_reload).should == 1
81
+ begin
82
+ dm.reload
83
+ wait_for_stop
84
+ test_state(:server_reload).should == 1
32
85
 
33
- Process.kill(:USR1, pid)
34
- wait_for_stop
35
- test_state(:server_restart_graceful).should == 1
86
+ dm.restart(true)
87
+ wait_for_stop
88
+ test_state(:server_restart_graceful).should == 1
36
89
 
37
- Process.kill(:HUP, pid)
38
- wait_for_stop
39
- test_state(:server_restart_immediate).should == 1
90
+ dm.restart(false)
91
+ wait_for_stop
92
+ test_state(:server_restart_immediate).should == 1
40
93
 
41
- Process.kill(:QUIT, pid)
42
- wait_for_stop
43
- test_state(:server_stop_immediate).should == 1
94
+ dm.stop(false)
95
+ wait_for_stop
96
+ test_state(:server_stop_immediate).should == 1
97
+ ensure
98
+ dm.stop(true) rescue nil
99
+ end
44
100
  end
45
- end
46
101
 
102
+ it 'exits with status 0 when it was stopped normally' do
103
+ pending "worker type process(fork) cannot be used in Windows" if ServerEngine.windows?
104
+ dm = Daemon.new(
105
+ TestServer,
106
+ TestWorker,
107
+ daemonize: false,
108
+ supervisor: false,
109
+ pid_path: "tmp/pid",
110
+ log_stdout: false,
111
+ log_stderr: false,
112
+ unrecoverable_exit_codes: [3,4,5],
113
+ )
114
+ exit_code = nil
115
+ t = Thread.new { exit_code = dm.main }
116
+ sleep 0.1 until dm.instance_eval{ @pid }
117
+ dm.stop(true)
118
+
119
+ t.join
120
+
121
+ exit_code.should == 0
122
+ end
123
+
124
+ it 'exits with status of workers if worker exits with status specified in unrecoverable_exit_codes, without supervisor' do
125
+ pending "worker type process(fork) cannot be used in Windows" if ServerEngine.windows?
126
+
127
+ dm = Daemon.new(
128
+ TestServer,
129
+ TestExitWorker,
130
+ daemonize: false,
131
+ supervisor: false,
132
+ worker_type: 'process',
133
+ pid_path: "tmp/pid",
134
+ log_stdout: false,
135
+ log_stderr: false,
136
+ unrecoverable_exit_codes: [3,4,5],
137
+ )
138
+ exit_code = nil
139
+ t = Thread.new { exit_code = dm.main }
140
+ sleep 0.1 until dm.instance_eval{ @pid }
141
+
142
+ t.join
143
+
144
+ exit_code.should == 5
145
+ end
146
+
147
+ it 'exits with status of workers if worker exits with status specified in unrecoverable_exit_codes, with supervisor' do
148
+ pending "worker type process(fork) cannot be used in Windows" if ServerEngine.windows?
149
+
150
+ dm = Daemon.new(
151
+ TestServer,
152
+ TestExitWorker,
153
+ daemonize: false,
154
+ supervisor: true,
155
+ worker_type: 'process',
156
+ pid_path: "tmp/pid",
157
+ log_stdout: false,
158
+ log_stderr: false,
159
+ unrecoverable_exit_codes: [3,4,5],
160
+ )
161
+ exit_code = nil
162
+ t = Thread.new { exit_code = dm.main }
163
+ sleep 0.1 until dm.instance_eval{ @pid }
164
+
165
+ t.join
166
+
167
+ exit_code.should == 5
168
+ end
169
+ end
@@ -1,11 +1,13 @@
1
+ [ServerEngine::MultiThreadServer, ServerEngine::MultiProcessServer].each do |impl_class|
2
+ # MultiProcessServer uses fork(2) internally, then it doesn't support Windows.
1
3
 
2
- describe ServerEngine::MultiWorkerServer do
3
- include_context 'test server and worker'
4
-
5
- [MultiThreadServer, MultiProcessServer].each do |impl_class|
4
+ describe impl_class do
5
+ include_context 'test server and worker'
6
6
 
7
7
  it 'scale up' do
8
- config = {:workers => 2}
8
+ pending "Windows environment does not support fork" if ServerEngine.windows? && impl_class == ServerEngine::MultiProcessServer
9
+
10
+ config = {workers: 2, log_stdout: false, log_stderr: false}
9
11
 
10
12
  s = impl_class.new(TestWorker) { config.dup }
11
13
  t = Thread.new { s.main }
@@ -31,7 +33,9 @@ describe ServerEngine::MultiWorkerServer do
31
33
  end
32
34
 
33
35
  it 'scale down' do
34
- config = {:workers => 2}
36
+ pending "Windows environment does not support fork" if ServerEngine.windows? && impl_class == ServerEngine::MultiProcessServer
37
+
38
+ config = {workers: 2, log_stdout: false, log_stderr: false}
35
39
 
36
40
  s = impl_class.new(TestWorker) { config.dup }
37
41
  t = Thread.new { s.main }
@@ -56,6 +60,54 @@ describe ServerEngine::MultiWorkerServer do
56
60
  test_state(:worker_stop).should == 3
57
61
  end
58
62
 
63
+ it 'raises SystemExit when all workers exit with specified code by unrecoverable_exit_codes' do
64
+ pending "unrecoverable_exit_codes supported only for multi process workers" if impl_class == ServerEngine::MultiThreadServer
65
+ pending "Windows environment does not support fork" if ServerEngine.windows? && impl_class == ServerEngine::MultiProcessServer
66
+
67
+ config = {workers: 4, log_stdout: false, log_stderr: false, unrecoverable_exit_codes: [3, 4, 5]}
68
+
69
+ s = impl_class.new(TestExitWorker) { config.dup }
70
+ raised_error = nil
71
+ t = Thread.new do
72
+ begin
73
+ s.main
74
+ rescue SystemExit => e
75
+ raised_error = e
76
+ end
77
+ end
78
+
79
+ wait_for_fork
80
+ test_state(:worker_run).should == 4
81
+ t.join
82
+
83
+ test_state(:worker_stop).to_i.should == 0
84
+ raised_error.status.should == 3 # 4th process's exit status
85
+ end
86
+
87
+ it 'raises SystemExit immediately when a worker exits if stop_immediately_at_unrecoverable_exit specified' do
88
+ pending "unrecoverable_exit_codes supported only for multi process workers" if impl_class == ServerEngine::MultiThreadServer
89
+ pending "Windows environment does not support fork" if ServerEngine.windows? && impl_class == ServerEngine::MultiProcessServer
90
+
91
+ config = {workers: 4, log_stdout: false, log_stderr: false, unrecoverable_exit_codes: [3, 4, 5], stop_immediately_at_unrecoverable_exit: true}
92
+
93
+ s = impl_class.new(TestExitWorker) { config.dup }
94
+ raised_error = nil
95
+ t = Thread.new do
96
+ begin
97
+ s.main
98
+ rescue SystemExit => e
99
+ raised_error = e
100
+ end
101
+ end
102
+
103
+ wait_for_fork
104
+ test_state(:worker_run).should == 4
105
+ t.join
106
+
107
+ test_state(:worker_stop).to_i.should == 3
108
+ test_state(:worker_finished).to_i.should == 3
109
+ raised_error.should_not be_nil
110
+ raised_error.status.should == 5 # 1st process's exit status
111
+ end
59
112
  end
60
113
  end
61
-
@@ -7,6 +7,76 @@ def reset_test_state
7
7
  FileUtils.rm_f 'tmp/state.yml'
8
8
  FileUtils.touch 'tmp/state.yml'
9
9
  $state_file_mutex = Mutex.new
10
+ if ServerEngine.windows?
11
+ open("tmp/daemon.rb", "w") do |f|
12
+ f.puts <<-'end_of_script'
13
+ require "serverengine"
14
+ require "rspec"
15
+ $state_file_mutex = Mutex.new # TODO
16
+ require "server_worker_context"
17
+ include ServerEngine
18
+ command_pipe = STDIN.dup
19
+ STDIN.reopen(File::NULL)
20
+ Daemon.run_server(TestServer, TestWorker, command_pipe: command_pipe)
21
+ end_of_script
22
+ end
23
+
24
+ open("tmp/supervisor.rb", "w") do |f|
25
+ f.puts <<-'end_of_script'
26
+ require "serverengine"
27
+ require "rspec"
28
+ $state_file_mutex = Mutex.new # TODO
29
+ require "server_worker_context"
30
+ include ServerEngine
31
+ server = TestServer
32
+ worker = TestWorker
33
+ config = {command_pipe: STDIN.dup}
34
+ STDIN.reopen(File::NULL)
35
+ ARGV.each do |arg|
36
+ case arg
37
+ when /^server=(.*)$/
38
+ server = Object.const_get($1)
39
+ when /^worker=(.*)$/
40
+ worker = Object.const_get($1)
41
+ when /^(.*)=(\d+)$/
42
+ config[$1.to_sym] = $2.to_i
43
+ when /^(.*)=(.*)$/
44
+ config[$1.to_sym] = $2
45
+ else
46
+ raise "Unknown parameter: [#{arg}]"
47
+ end
48
+ end
49
+ sv = Supervisor.new(server, worker, config)
50
+ s = sv.create_server(nil)
51
+ s.install_signal_handlers
52
+ t = Thread.new{ s.main }
53
+ s.after_start
54
+ t.join
55
+ end_of_script
56
+ end
57
+ end
58
+ end
59
+
60
+ def windows_daemon_cmdline
61
+ if ServerEngine.windows?
62
+ [ServerEngine.ruby_bin_path, '-I', File.dirname(__FILE__), 'tmp/daemon.rb']
63
+ else
64
+ nil
65
+ end
66
+ end
67
+
68
+ def windows_supervisor_cmdline(server = nil, worker = nil, config = {})
69
+ if ServerEngine.windows?
70
+ cmd = [ServerEngine.ruby_bin_path, '-I', File.dirname(__FILE__), 'tmp/supervisor.rb']
71
+ cmd << "server=#{server}" if server
72
+ cmd << "worker=#{worker}" if worker
73
+ config.each_pair do |k, v|
74
+ cmd << "#{k}=#{v}"
75
+ end
76
+ cmd
77
+ else
78
+ nil
79
+ end
10
80
  end
11
81
 
12
82
  def incr_test_state(key)
@@ -20,6 +90,7 @@ def incr_test_state(key)
20
90
 
21
91
  f.pos = 0
22
92
  f.write YAML.dump(data)
93
+ data[key]
23
94
  end
24
95
  end
25
96
  end
@@ -111,6 +182,39 @@ module TestWorker
111
182
  end
112
183
  end
113
184
 
185
+ module RunErrorWorker
186
+ def run
187
+ incr_test_state :worker_run
188
+ raise StandardError, "error test"
189
+ end
190
+ end
191
+
192
+ module TestExitWorker
193
+ def initialize
194
+ @stop_flag = BlockingFlag.new
195
+ @worker_num = incr_test_state :worker_initialize
196
+ @exit_code = case @worker_num
197
+ when 1 then 5
198
+ when 4 then 3
199
+ else 4
200
+ end
201
+ end
202
+
203
+ def run
204
+ incr_test_state :worker_run
205
+ exit_at = Time.now + @worker_num * 2
206
+ until @stop_flag.wait(0.1)
207
+ exit!(@exit_code) if Time.now >= exit_at
208
+ end
209
+ incr_test_state :worker_finished
210
+ end
211
+
212
+ def stop
213
+ incr_test_state :worker_stop
214
+ @stop_flag.set!
215
+ end
216
+ end
217
+
114
218
  shared_context 'test server and worker' do
115
219
  before { reset_test_state }
116
220