serverengine 1.6.4 → 2.0.0pre1

Sign up to get free protection for your applications and to get access to all the features.
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