We got nominated! Help us out and vote for GitHub as Best Bootstrapped Startup of 2008. (You can vote once a day.) [ hide ]

public
Description: Phusion Passenger (mod_rails)
Homepage: http://www.modrails.com/
Clone URL: git://github.com/FooBarWidget/passenger.git
Click here to lend your support to: passenger and make a donation at www.pledgie.com !
Finish the spawn manager.
FooBarWidget (author)
Fri Jan 25 12:24:52 -0800 2008
commit  48142791f9e95d63bab374387b228c1088047f8f
tree    6d65d44c15bb4cbbffd5d1b1f38e2adcaa3a24d9
parent  6c1ee04aa991d71c1648d1e484359866b2c0494e
...
15
16
17
 
 
 
18
19
20
...
79
80
81
82
83
 
 
 
 
 
 
84
85
86
...
163
164
165
166
 
167
168
 
 
 
 
 
 
 
 
 
 
 
 
169
170
171
...
15
16
17
18
19
20
21
22
23
...
82
83
84
 
 
85
86
87
88
89
90
91
92
93
...
170
171
172
 
173
174
 
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
0
@@ -15,6 +15,9 @@ class FrameworkSpawner < AbstractServer
0
   APP_SPAWNER_MAX_IDLE_TIME = 120
0
 
0
   include Utils
0
+
0
+ # An attribute, used internally. This should not be used outside mod_rails.
0
+ attr_accessor :time
0
 
0
   # Creates a new instance of FrameworkSpawner.
0
   #
0
@@ -79,8 +82,12 @@ class FrameworkSpawner < AbstractServer
0
     end
0
   end
0
   
0
- def reload(app_root)
0
- send_to_server("reload", normalize_path(app_root))
0
+ def reload(app_root = nil)
0
+ if app_root.nil?
0
+ send_to_server("reload")
0
+ else
0
+ send_to_server("reload", normalize_path(app_root))
0
+ end
0
   rescue Errno::EPIPE, Errno::EBADF, IOError, SocketError
0
     raise IOError, "Cannot send reload command to the framework spawner server."
0
   end
0
@@ -163,9 +170,20 @@ private
0
     end
0
   end
0
   
0
- def handle_reload(app_root)
0
+ def handle_reload(app_root = nil)
0
     @spawners_lock.synchronize do
0
- @spawners.delete(app_root)
0
+ if app_root.nil?
0
+ @spawners.each_value do |spawner|
0
+ spawner.stop
0
+ end
0
+ @spawners.clear
0
+ else
0
+ spawner = @spawners[app_root]
0
+ if spawner
0
+ spawner.stop
0
+ end
0
+ @spawners.delete(app_root)
0
+ end
0
     end
0
   end
0
   
...
 
1
2
3
...
20
21
22
 
 
 
23
24
25
...
88
89
90
91
 
 
 
 
 
92
93
94
...
1
2
3
4
...
21
22
23
24
25
26
27
28
29
...
92
93
94
 
95
96
97
98
99
100
101
102
0
@@ -1,3 +1,4 @@
0
+require 'mod_rails/native_support'
0
 module ModRails # :nodoc:
0
 
0
 # This class can be wrapped around an IO object to provide the ability
0
@@ -20,6 +21,9 @@ class MessageChannel
0
   DELIMITER = "\0"
0
   DELIMITER_NAME = "null byte"
0
   
0
+ include NativeSupport
0
+ private :send_fd
0
+
0
   # The wrapped IO object.
0
   attr_reader :io
0
 
0
@@ -88,7 +92,11 @@ class MessageChannel
0
   # connection.
0
   # Raises IOError if the IO stream is already closed on this side.
0
   def send_io(io)
0
- @io.send_io(io)
0
+ if io.respond_to?(:send_io)
0
+ @io.send_io(io)
0
+ else
0
+ send_fd(@io.fileno, io.fileno)
0
+ end
0
   end
0
   
0
   # Receive an IO object (a file descriptor) from the channel. The other
...
1
2
3
4
 
5
6
7
...
48
49
50
51
 
 
52
53
...
1
2
 
3
4
5
6
7
...
48
49
50
 
51
52
53
54
0
@@ -1,7 +1,7 @@
0
 # NOTE: we make use of pipes instead of Unix sockets, because
0
 # experimentation has shown that pipes are slightly faster.
0
-
0
 module ModRails # :nodoc:
0
+
0
 class RequestHandler
0
   def initialize(reader_pipe, writer_pipe)
0
     @reader = reader_pipe
0
@@ -48,5 +48,6 @@ private
0
       trap(signal, handler)
0
     end
0
   end
0
-end # class SCGIHandler
0
+end
0
+
0
 end # module ModRails
0
\ No newline at end of file
...
 
 
 
1
2
3
 
4
5
6
 
 
 
7
 
8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
10
11
12
13
14
15
16
17
 
 
 
 
 
 
 
18
 
19
20
21
22
 
23
24
25
26
27
28
29
 
 
 
 
 
 
 
 
 
 
 
 
 
30
31
32
33
34
35
36
 
 
37
38
39
40
41
42
43
44
 
 
 
 
 
 
 
 
 
45
46
47
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
49
50
...
52
53
54
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
56
57
58
 
 
 
 
59
60
61
62
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 
 
 
 
 
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
 
 
 
 
 
55
56
57
58
59
60
61
62
63
64
65
66
67
68
 
 
 
 
69
70
71
72
73
 
 
 
 
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
...
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
 
163
164
165
166
167
168
169
170
0
@@ -1,50 +1,120 @@
0
+if __FILE__ == $0
0
+ $LOAD_PATH << "#{File.dirname(__FILE__)}/.."
0
+end
0
 require 'mod_rails/framework_spawner'
0
 require 'mod_rails/application'
0
 require 'mod_rails/message_channel'
0
+require 'mod_rails/core_extensions'
0
 module ModRails # :nodoc:
0
 
0
 class SpawnManager
0
+ SPAWNER_CLEAN_INTERVAL = 125
0
+ SPAWNER_MAX_IDLE_TIME = 120
0
+
0
   def initialize
0
+ @previous_signal_handlers = {}
0
     @spawners = {}
0
+ @lock = Mutex.new
0
+ @cond = ConditionVariable.new
0
+ @cleaner_thread = Thread.new do
0
+ cleaner_thread_main
0
+ end
0
+ end
0
+
0
+ def cleanup
0
+ @lock.synchronize do
0
+ @cond.signal
0
+ end
0
+ @cleaner_thread.join
0
+ @lock.synchronize do
0
+ @spawners.each_value do |spawner|
0
+ spawner.stop
0
+ end
0
+ @spawners.clear
0
+ end
0
   end
0
 
0
   def spawn_application(app_root, username = nil)
0
     framework_version = Application.get_framework_version(app_root)
0
- spawner = @spawners[framework_version]
0
- if !spawner
0
- spawner = FrameworkSpawner.new(framework_version)
0
- @spawners[framework_version] = spawner
0
- spawner.start
0
+ @lock.synchronize do
0
+ spawner = @spawners[framework_version]
0
+ if !spawner
0
+ spawner = FrameworkSpawner.new(framework_version)
0
+ @spawners[framework_version] = spawner
0
+ spawner.start
0
+ end
0
     end
0
+ spawner.time = Time.now
0
     return spawner.spawn_application(app_root, username)
0
   end
0
   
0
   def server_main(unix_socket)
0
+ @done = false
0
     @channel = MessageChannel.new(unix_socket)
0
     install_signal_handlers
0
- @done = false
0
- while !@done
0
- begin
0
- name, *args = channel.read
0
- if name.nil?
0
+ begin
0
+ while !@done
0
+ begin
0
+ name, *args = @channel.read
0
+ if name.nil?
0
+ @done = true
0
+ elsif !MESSAGE_HANDLERS.has_key?(name)
0
+ raise StandardError, "Unknown message '#{name}' received."
0
+ else
0
+ name, *args = message
0
+ __send__(MESSAGE_HANDLERS[name], *args)
0
+ end
0
+ rescue ExitNow
0
           @done = true
0
- elsif name.???
0
- else
0
- name, *args = message
0
- __send__(MESSAGE_HANDLERS[name], *args)
0
         end
0
       end
0
+ ensure
0
+ revert_signal_handlers
0
     end
0
- @spawners.each_value do |spawner|
0
- spawner.stop
0
- end
0
- @spawners.clear
0
   end
0
 
0
 private
0
+ class ExitNow < RuntimeError
0
+ end
0
+
0
+ SIGNAL_HANDLERS = {
0
+ 'TERM' => :exit_now!,
0
+ 'INT' => :exit_now!,
0
+ 'USR1' => :exit_asap,
0
+ 'HUP' => :reload
0
+ }
0
   MESSAGE_HANDLERS = {
0
     'spawn_application' => :handle_spawn_application
0
   }
0
+
0
+ def install_signal_handlers
0
+ Signal.list.each_key do |signal|
0
+ begin
0
+ prev_handler = trap(signal, 'DEFAULT')
0
+ if prev_handler != 'DEFAULT'
0
+ @previous_signal_handlers[signal] = prev_handler
0
+ end
0
+ rescue ArgumentError
0
+ # Signal cannot be trapped; ignore it.
0
+ end
0
+ end
0
+ SIGNAL_HANDLERS.each_pair do |signal, handler|
0
+ if handler == :ignore
0
+ trap(signal, 'IGNORE')
0
+ else
0
+ trap(signal) do
0
+ __send__(handler)
0
+ end
0
+ end
0
+ end
0
+ end
0
+
0
+ def revert_signal_handlers
0
+ @previous_signal_handlers.each_pair do |signal, handler|
0
+ trap(signal, handler)
0
+ end
0
+ @previous_signal_handlers = {}
0
+ end
0
 
0
   def handle_spawn_application(app_root, username)
0
     app = spawn_application(app_root, username)
0
@@ -52,10 +122,48 @@ private
0
     @channel.send_io(app.socket)
0
     app.socket.close
0
   end
0
+
0
+ def cleaner_thread_main
0
+ @lock.synchronize do
0
+ while true
0
+ if @cond.timed_wait(@lock, SPAWNER_CLEAN_INTERVAL)
0
+ break
0
+ else
0
+ current_time = Time.now
0
+ @spawners.keys.each do |key|
0
+ spawner = @spawners[key]
0
+ if current_time - spawner.time > SPAWNER_MAX_IDLE_TIME
0
+ spawner.stop
0
+ @spawners.delete(key)
0
+ end
0
+ end
0
+ end
0
+ end
0
+ end
0
+ end
0
+
0
+ def exit_now!
0
+ raise ExitNow, "Exit requested"
0
+ end
0
+
0
+ def exit_asap
0
+ @done = true
0
+ end
0
+
0
+ def reload
0
+ @lock.synchronize do
0
+ @spawners.each_each do |spawner|
0
+ spawner.reload
0
+ end
0
+ end
0
+ end
0
 end
0
 
0
 if __FILE__ == $0
0
- exit(SpawnManager.new.server_main)
0
+ unix_socket = IO.new(ARGV[0].to_i, "a+")
0
+ spawn_manager = SpawnManager.new
0
+ spawn_manager.server_main(unix_socket)
0
+ spawn_manager.cleanup
0
 end
0
 
0
 end # module ModRails
0
\ No newline at end of file
...
 
1
2
3
...
1
2
3
4
0
@@ -1,3 +1,4 @@
0
+$LOAD_PATH << "#{File.dirname(__FILE__)}/../lib"
0
 require 'test/unit'
0
 require 'socket'
0
 require 'mod_rails/message_channel'

Comments

    No one has commented yet.