passenger 5.0.0.beta3 → 5.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of passenger might be problematic. Click here for more details.
- checksums.yaml +8 -8
- checksums.yaml.gz.asc +7 -7
- data.tar.gz.asc +7 -7
- data/.editorconfig +11 -5
- data/CHANGELOG +38 -0
- data/CONTRIBUTING.md +1 -4
- data/Gemfile +0 -1
- data/Gemfile.lock +0 -2
- data/Rakefile +33 -33
- data/bin/passenger +1 -1
- data/bin/passenger-config +1 -1
- data/bin/passenger-install-apache2-module +800 -800
- data/bin/passenger-install-nginx-module +592 -592
- data/bin/passenger-memory-stats +127 -127
- data/bin/passenger-status +216 -216
- data/build/agents.rb +127 -127
- data/build/apache2.rb +87 -87
- data/build/basics.rb +60 -60
- data/build/common_library.rb +165 -165
- data/build/cplusplus_support.rb +51 -51
- data/build/cxx_tests.rb +268 -268
- data/build/debian.rb +143 -143
- data/build/documentation.rb +58 -58
- data/build/integration_tests.rb +81 -81
- data/build/misc.rb +132 -132
- data/build/nginx.rb +20 -20
- data/build/node_tests.rb +7 -7
- data/build/oxt_tests.rb +14 -14
- data/build/packaging.rb +570 -570
- data/build/preprocessor.rb +260 -260
- data/build/rake_extensions.rb +71 -71
- data/build/ruby_extension.rb +29 -29
- data/build/ruby_tests.rb +6 -6
- data/build/test_basics.rb +37 -37
- data/debian.template/control.template +3 -5
- data/dev/copy_boost_headers +134 -134
- data/dev/install_scripts_bootstrap_code.rb +25 -25
- data/dev/list_tests +20 -20
- data/dev/ruby_server.rb +223 -223
- data/dev/runner +18 -18
- data/doc/ServerOptimizationGuide.txt.md +55 -2
- data/doc/Users guide Nginx.txt +0 -26
- data/doc/Users guide Standalone.txt +5 -1
- data/doc/users_guide_snippets/tips.txt +9 -0
- data/ext/common/ApplicationPool2/Group.h +23 -11
- data/ext/common/ApplicationPool2/Implementation.cpp +32 -7
- data/ext/common/ApplicationPool2/Pool.h +22 -17
- data/ext/common/ApplicationPool2/SmartSpawner.h +4 -1
- data/ext/common/ApplicationPool2/Spawner.h +1 -1
- data/ext/common/Constants.h +1 -1
- data/ext/common/agents/Base.cpp +35 -20
- data/ext/common/agents/HelperAgent/Main.cpp +8 -1
- data/ext/common/agents/HelperAgent/OptionParser.h +18 -4
- data/ext/common/agents/HelperAgent/RequestHandler.h +2 -83
- data/ext/common/agents/HelperAgent/RequestHandler/ForwardResponse.cpp +54 -1
- data/ext/common/agents/HelperAgent/RequestHandler/InitRequest.cpp +7 -4
- data/ext/common/agents/Main.cpp +1 -1
- data/ext/common/agents/Watchdog/Main.cpp +54 -19
- data/ext/nginx/Configuration.c +7 -0
- data/ext/nginx/ContentHandler.c +9 -1
- data/helper-scripts/backtrace-sanitizer.rb +106 -87
- data/helper-scripts/crash-watch.rb +32 -0
- data/helper-scripts/download_binaries/extconf.rb +38 -38
- data/helper-scripts/meteor-loader.rb +107 -107
- data/helper-scripts/prespawn +101 -101
- data/helper-scripts/rack-loader.rb +96 -96
- data/helper-scripts/rack-preloader.rb +137 -137
- data/lib/phusion_passenger.rb +292 -292
- data/lib/phusion_passenger/abstract_installer.rb +438 -438
- data/lib/phusion_passenger/active_support3_extensions/init.rb +168 -170
- data/lib/phusion_passenger/admin_tools.rb +20 -20
- data/lib/phusion_passenger/admin_tools/instance.rb +178 -178
- data/lib/phusion_passenger/admin_tools/instance_registry.rb +61 -61
- data/lib/phusion_passenger/admin_tools/memory_stats.rb +267 -267
- data/lib/phusion_passenger/apache2/config_options.rb +182 -182
- data/lib/phusion_passenger/common_library.rb +479 -485
- data/lib/phusion_passenger/config/about_command.rb +161 -161
- data/lib/phusion_passenger/config/admin_command_command.rb +129 -129
- data/lib/phusion_passenger/config/agent_compiler.rb +121 -121
- data/lib/phusion_passenger/config/build_native_support_command.rb +43 -43
- data/lib/phusion_passenger/config/command.rb +25 -25
- data/lib/phusion_passenger/config/compile_agent_command.rb +62 -62
- data/lib/phusion_passenger/config/compile_nginx_engine_command.rb +88 -73
- data/lib/phusion_passenger/config/detach_process_command.rb +72 -72
- data/lib/phusion_passenger/config/download_agent_command.rb +246 -227
- data/lib/phusion_passenger/config/download_nginx_engine_command.rb +245 -224
- data/lib/phusion_passenger/config/install_agent_command.rb +144 -132
- data/lib/phusion_passenger/config/install_standalone_runtime_command.rb +205 -185
- data/lib/phusion_passenger/config/installation_utils.rb +204 -204
- data/lib/phusion_passenger/config/list_instances_command.rb +64 -64
- data/lib/phusion_passenger/config/main.rb +152 -152
- data/lib/phusion_passenger/config/nginx_engine_compiler.rb +319 -300
- data/lib/phusion_passenger/config/reopen_logs_command.rb +67 -67
- data/lib/phusion_passenger/config/restart_app_command.rb +155 -155
- data/lib/phusion_passenger/config/system_metrics_command.rb +13 -13
- data/lib/phusion_passenger/config/utils.rb +95 -95
- data/lib/phusion_passenger/config/validate_install_command.rb +198 -198
- data/lib/phusion_passenger/console_text_template.rb +25 -25
- data/lib/phusion_passenger/constants.rb +90 -90
- data/lib/phusion_passenger/debug_logging.rb +106 -106
- data/lib/phusion_passenger/loader_shared_helpers.rb +447 -432
- data/lib/phusion_passenger/message_channel.rb +312 -312
- data/lib/phusion_passenger/message_client.rb +176 -176
- data/lib/phusion_passenger/native_support.rb +369 -369
- data/lib/phusion_passenger/nginx/config_options.rb +297 -297
- data/lib/phusion_passenger/packaging.rb +131 -131
- data/lib/phusion_passenger/platform_info.rb +360 -360
- data/lib/phusion_passenger/platform_info/apache.rb +767 -767
- data/lib/phusion_passenger/platform_info/apache_detector.rb +199 -199
- data/lib/phusion_passenger/platform_info/binary_compatibility.rb +107 -107
- data/lib/phusion_passenger/platform_info/compiler.rb +570 -570
- data/lib/phusion_passenger/platform_info/curl.rb +32 -32
- data/lib/phusion_passenger/platform_info/cxx_portability.rb +188 -188
- data/lib/phusion_passenger/platform_info/depcheck.rb +372 -372
- data/lib/phusion_passenger/platform_info/depcheck_specs/apache2.rb +109 -109
- data/lib/phusion_passenger/platform_info/depcheck_specs/compiler_toolchain.rb +4 -4
- data/lib/phusion_passenger/platform_info/depcheck_specs/gems.rb +10 -34
- data/lib/phusion_passenger/platform_info/depcheck_specs/libs.rb +101 -101
- data/lib/phusion_passenger/platform_info/depcheck_specs/ruby.rb +5 -5
- data/lib/phusion_passenger/platform_info/depcheck_specs/utilities.rb +13 -13
- data/lib/phusion_passenger/platform_info/linux.rb +55 -55
- data/lib/phusion_passenger/platform_info/operating_system.rb +149 -149
- data/lib/phusion_passenger/platform_info/ruby.rb +468 -448
- data/lib/phusion_passenger/platform_info/zlib.rb +9 -9
- data/lib/phusion_passenger/plugin.rb +66 -66
- data/lib/phusion_passenger/preloader_shared_helpers.rb +126 -126
- data/lib/phusion_passenger/public_api.rb +191 -191
- data/lib/phusion_passenger/rack/out_of_band_gc.rb +93 -94
- data/lib/phusion_passenger/rack/thread_handler_extension.rb +231 -227
- data/lib/phusion_passenger/request_handler.rb +567 -577
- data/lib/phusion_passenger/request_handler/thread_handler.rb +379 -381
- data/lib/phusion_passenger/ruby_core_enhancements.rb +86 -86
- data/lib/phusion_passenger/ruby_core_io_enhancements.rb +74 -74
- data/lib/phusion_passenger/simple_benchmarking.rb +25 -25
- data/lib/phusion_passenger/standalone/app_finder.rb +153 -150
- data/lib/phusion_passenger/standalone/command.rb +44 -40
- data/lib/phusion_passenger/standalone/config_utils.rb +53 -53
- data/lib/phusion_passenger/standalone/control_utils.rb +38 -59
- data/lib/phusion_passenger/standalone/main.rb +73 -73
- data/lib/phusion_passenger/standalone/start_command.rb +697 -685
- data/lib/phusion_passenger/standalone/start_command/builtin_engine.rb +193 -155
- data/lib/phusion_passenger/standalone/start_command/nginx_engine.rb +162 -133
- data/lib/phusion_passenger/standalone/status_command.rb +64 -64
- data/lib/phusion_passenger/standalone/stop_command.rb +72 -72
- data/lib/phusion_passenger/standalone/version_command.rb +9 -9
- data/lib/phusion_passenger/union_station/connection.rb +32 -32
- data/lib/phusion_passenger/union_station/core.rb +251 -251
- data/lib/phusion_passenger/union_station/transaction.rb +126 -126
- data/lib/phusion_passenger/utils.rb +199 -167
- data/lib/phusion_passenger/utils/ansi_colors.rb +128 -128
- data/lib/phusion_passenger/utils/download.rb +196 -196
- data/lib/phusion_passenger/utils/file_system_watcher.rb +158 -158
- data/lib/phusion_passenger/utils/hosts_file_parser.rb +101 -101
- data/lib/phusion_passenger/utils/lock.rb +31 -31
- data/lib/phusion_passenger/utils/native_support_utils.rb +31 -31
- data/lib/phusion_passenger/utils/progress_bar.rb +26 -26
- data/lib/phusion_passenger/utils/shellwords.rb +20 -20
- data/lib/phusion_passenger/utils/terminal_choice_menu.rb +206 -206
- data/lib/phusion_passenger/utils/unseekable_socket.rb +272 -272
- data/lib/phusion_passenger/vendor/crash_watch/app.rb +129 -0
- data/lib/phusion_passenger/vendor/crash_watch/gdb_controller.rb +341 -0
- data/lib/phusion_passenger/vendor/crash_watch/version.rb +24 -0
- data/lib/phusion_passenger/vendor/daemon_controller.rb +877 -0
- data/lib/phusion_passenger/vendor/daemon_controller/lock_file.rb +127 -0
- data/lib/phusion_passenger/vendor/daemon_controller/spawn.rb +26 -0
- data/lib/phusion_passenger/vendor/daemon_controller/version.rb +29 -0
- data/packaging/rpm/passenger_spec/passenger.spec.template +0 -1
- data/passenger.gemspec +0 -1
- data/resources/templates/config/nginx_engine_compiler/possible_solutions_for_download_and_extraction_problems.txt.erb +27 -0
- data/resources/templates/standalone/config.erb +19 -15
- data/test/integration_tests/apache2_tests.rb +566 -566
- data/test/integration_tests/downloaded_binaries_tests.rb +126 -125
- data/test/integration_tests/native_packaging_spec.rb +296 -296
- data/test/integration_tests/nginx_tests.rb +393 -393
- data/test/integration_tests/shared/example_webapp_tests.rb +282 -280
- data/test/integration_tests/source_packaging_test.rb +138 -138
- data/test/integration_tests/spec_helper.rb +5 -5
- data/test/integration_tests/standalone_tests.rb +367 -367
- data/test/ruby/debug_logging_spec.rb +133 -133
- data/test/ruby/message_channel_spec.rb +186 -186
- data/test/ruby/rack/loader_spec.rb +28 -28
- data/test/ruby/rack/preloader_spec.rb +34 -34
- data/test/ruby/rails3.0/loader_spec.rb +12 -12
- data/test/ruby/rails3.0/preloader_spec.rb +18 -18
- data/test/ruby/rails3.1/loader_spec.rb +12 -12
- data/test/ruby/rails3.1/preloader_spec.rb +18 -18
- data/test/ruby/rails3.2/loader_spec.rb +12 -12
- data/test/ruby/rails3.2/preloader_spec.rb +18 -18
- data/test/ruby/rails4.0/loader_spec.rb +12 -12
- data/test/ruby/rails4.0/preloader_spec.rb +18 -18
- data/test/ruby/rails4.1/loader_spec.rb +12 -12
- data/test/ruby/rails4.1/preloader_spec.rb +18 -18
- data/test/ruby/request_handler_spec.rb +730 -730
- data/test/ruby/shared/loader_sharedspec.rb +224 -224
- data/test/ruby/shared/rails/union_station_extensions_sharedspec.rb +327 -327
- data/test/ruby/shared/ruby_loader_sharedspec.rb +47 -47
- data/test/ruby/spec_helper.rb +65 -65
- data/test/ruby/standalone/runtime_installer_spec.rb +384 -384
- data/test/ruby/union_station_spec.rb +276 -276
- data/test/ruby/utils/file_system_watcher_spec.rb +220 -220
- data/test/ruby/utils/hosts_file_parser.rb +248 -248
- data/test/ruby/utils/tee_input_spec.rb +215 -215
- data/test/ruby/utils/unseekable_socket_spec.rb +57 -57
- data/test/ruby/utils_spec.rb +21 -21
- data/test/stub/rack/config.ru +87 -87
- data/test/stub/rack/library.rb +8 -8
- data/test/stub/rack/start.rb +30 -30
- data/test/support/apache2_controller.rb +191 -191
- data/test/support/nginx_controller.rb +90 -99
- data/test/support/placebo-preloader.rb +57 -57
- data/test/support/test_helper.rb +435 -435
- metadata +11 -21
- metadata.gz.asc +7 -7
- data/lib/phusion_passenger/standalone/command2.rb +0 -292
- data/lib/phusion_passenger/standalone/start2_command.rb +0 -799
- data/resources/templates/standalone/download_tool_missing.txt.erb +0 -18
- data/resources/templates/standalone/possible_solutions_for_download_and_extraction_problems.txt.erb +0 -17
- data/resources/templates/standalone/run_installer_as_root.txt.erb +0 -8
@@ -36,582 +36,572 @@ PhusionPassenger.require_passenger_lib 'request_handler/thread_handler'
|
|
36
36
|
|
37
37
|
module PhusionPassenger
|
38
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
|
-
|
121
|
-
|
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
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
iterations = new_iterations
|
607
|
-
start_time = Time.now
|
608
|
-
sleep 0.01
|
609
|
-
end
|
610
|
-
end
|
611
|
-
end
|
612
|
-
|
613
|
-
debug("All threads are now idle")
|
614
|
-
end
|
615
|
-
end
|
39
|
+
class RequestHandler
|
40
|
+
include DebugLogging
|
41
|
+
include Utils
|
42
|
+
|
43
|
+
# Signal which will cause the application to exit immediately.
|
44
|
+
HARD_TERMINATION_SIGNAL = "SIGTERM"
|
45
|
+
BACKLOG_SIZE = 500
|
46
|
+
|
47
|
+
# String constants which exist to relieve Ruby's garbage collector.
|
48
|
+
IGNORE = 'IGNORE' # :nodoc:
|
49
|
+
DEFAULT = 'DEFAULT' # :nodoc:
|
50
|
+
|
51
|
+
# A hash containing all server sockets that this request handler listens on.
|
52
|
+
# The hash is in the form of:
|
53
|
+
#
|
54
|
+
# {
|
55
|
+
# name1 => [socket_address1, socket_type1, socket1],
|
56
|
+
# name2 => [socket_address2, socket_type2, socket2],
|
57
|
+
# ...
|
58
|
+
# }
|
59
|
+
#
|
60
|
+
# +name+ is a Symbol. +socket_addressx+ is the address of the socket,
|
61
|
+
# +socket_typex+ is the socket's type (either 'unix' or 'tcp') and
|
62
|
+
# +socketx+ is the actual socket IO objec.
|
63
|
+
# There's guaranteed to be at least one server socket, namely one with the
|
64
|
+
# name +:main+.
|
65
|
+
attr_reader :server_sockets
|
66
|
+
|
67
|
+
attr_reader :concurrency
|
68
|
+
|
69
|
+
# A password with which clients must authenticate. Default is unauthenticated.
|
70
|
+
attr_accessor :connect_password
|
71
|
+
|
72
|
+
# Create a new RequestHandler with the given owner pipe.
|
73
|
+
# +owner_pipe+ must be the readable part of a pipe IO object.
|
74
|
+
#
|
75
|
+
# Additionally, the following options may be given:
|
76
|
+
# - connect_password
|
77
|
+
def initialize(owner_pipe, options = {})
|
78
|
+
require_option(options, "app_group_name")
|
79
|
+
install_options_as_ivars(self, options,
|
80
|
+
"app",
|
81
|
+
"app_group_name",
|
82
|
+
"connect_password",
|
83
|
+
"union_station_core"
|
84
|
+
)
|
85
|
+
|
86
|
+
@keepalive = options.fetch("keepalive", true).to_s == "true"
|
87
|
+
@force_http_session = ENV["_PASSENGER_FORCE_HTTP_SESSION"] == "true"
|
88
|
+
if @force_http_session
|
89
|
+
@connect_password = nil
|
90
|
+
end
|
91
|
+
@thread_handler = options["thread_handler"] || ThreadHandler
|
92
|
+
@concurrency = 1
|
93
|
+
|
94
|
+
#############
|
95
|
+
#############
|
96
|
+
|
97
|
+
@server_sockets = {}
|
98
|
+
|
99
|
+
if should_use_unix_sockets?
|
100
|
+
@main_socket_address, @main_socket = create_unix_socket_on_filesystem(options)
|
101
|
+
else
|
102
|
+
@main_socket_address, @main_socket = create_tcp_socket
|
103
|
+
end
|
104
|
+
@server_sockets[:main] = {
|
105
|
+
:address => @main_socket_address,
|
106
|
+
:socket => @main_socket,
|
107
|
+
:protocol => @force_http_session ? :http_session : :session,
|
108
|
+
:concurrency => @concurrency
|
109
|
+
}
|
110
|
+
|
111
|
+
@http_socket_address, @http_socket = create_tcp_socket
|
112
|
+
@server_sockets[:http] = {
|
113
|
+
:address => @http_socket_address,
|
114
|
+
:socket => @http_socket,
|
115
|
+
:protocol => :http,
|
116
|
+
:concurrency => 1
|
117
|
+
}
|
118
|
+
|
119
|
+
@owner_pipe = owner_pipe
|
120
|
+
@options = options
|
121
|
+
@previous_signal_handlers = {}
|
122
|
+
@main_loop_generation = 0
|
123
|
+
@main_loop_thread_lock = Mutex.new
|
124
|
+
@main_loop_thread_cond = ConditionVariable.new
|
125
|
+
@threads = []
|
126
|
+
@threads_mutex = Mutex.new
|
127
|
+
@main_loop_running = false
|
128
|
+
|
129
|
+
#############
|
130
|
+
end
|
131
|
+
|
132
|
+
# Clean up temporary stuff created by the request handler.
|
133
|
+
#
|
134
|
+
# If the main loop was started by #main_loop, then this method may only
|
135
|
+
# be called after the main loop has exited.
|
136
|
+
#
|
137
|
+
# If the main loop was started by #start_main_loop_thread, then this method
|
138
|
+
# may be called at any time, and it will stop the main loop thread.
|
139
|
+
def cleanup
|
140
|
+
if @main_loop_thread
|
141
|
+
@main_loop_thread_lock.synchronize do
|
142
|
+
@graceful_termination_pipe[1].close rescue nil
|
143
|
+
end
|
144
|
+
@main_loop_thread.join
|
145
|
+
end
|
146
|
+
@server_sockets.each_value do |info|
|
147
|
+
socket = info[:socket]
|
148
|
+
type = get_socket_address_type(info[:address])
|
149
|
+
|
150
|
+
begin
|
151
|
+
socket.close if !socket.closed?
|
152
|
+
rescue Exception => e
|
153
|
+
# Ignore "stream closed" error, which occurs in some unit tests.
|
154
|
+
# We catch Exception here instead of IOError because of a Ruby 1.8.7 bug.
|
155
|
+
if e.to_s !~ /stream closed/ && e.message.to_s !~ /stream closed/
|
156
|
+
raise e
|
157
|
+
end
|
158
|
+
end
|
159
|
+
if type == :unix
|
160
|
+
filename = info[:address].sub(/^unix:/, '')
|
161
|
+
File.unlink(filename) rescue nil
|
162
|
+
end
|
163
|
+
end
|
164
|
+
@owner_pipe.close rescue nil
|
165
|
+
end
|
166
|
+
|
167
|
+
# Check whether the main loop's currently running.
|
168
|
+
def main_loop_running?
|
169
|
+
@main_loop_thread_lock.synchronize do
|
170
|
+
return @main_loop_running
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
# Enter the request handler's main loop.
|
175
|
+
def main_loop
|
176
|
+
debug("Entering request handler main loop")
|
177
|
+
reset_signal_handlers
|
178
|
+
begin
|
179
|
+
@graceful_termination_pipe = IO.pipe
|
180
|
+
@graceful_termination_pipe[0].close_on_exec!
|
181
|
+
@graceful_termination_pipe[1].close_on_exec!
|
182
|
+
|
183
|
+
@main_loop_thread_lock.synchronize do
|
184
|
+
@main_loop_generation += 1
|
185
|
+
@main_loop_running = true
|
186
|
+
@main_loop_thread_cond.broadcast
|
187
|
+
|
188
|
+
@select_timeout = nil
|
189
|
+
|
190
|
+
@selectable_sockets = []
|
191
|
+
@server_sockets.each_value do |value|
|
192
|
+
socket = value[2]
|
193
|
+
@selectable_sockets << socket if socket
|
194
|
+
end
|
195
|
+
@selectable_sockets << @owner_pipe
|
196
|
+
@selectable_sockets << @graceful_termination_pipe[0]
|
197
|
+
end
|
198
|
+
|
199
|
+
install_useful_signal_handlers
|
200
|
+
start_threads
|
201
|
+
wait_until_termination_requested
|
202
|
+
wait_until_all_threads_are_idle
|
203
|
+
terminate_threads
|
204
|
+
debug("Request handler main loop exited normally")
|
205
|
+
|
206
|
+
rescue EOFError
|
207
|
+
# Exit main loop.
|
208
|
+
trace(2, "Request handler main loop interrupted by EOFError exception")
|
209
|
+
rescue Interrupt
|
210
|
+
# Exit main loop.
|
211
|
+
trace(2, "Request handler main loop interrupted by Interrupt exception")
|
212
|
+
rescue SignalException => signal
|
213
|
+
trace(2, "Request handler main loop interrupted by SignalException")
|
214
|
+
if signal.message != HARD_TERMINATION_SIGNAL
|
215
|
+
raise
|
216
|
+
end
|
217
|
+
rescue Exception => e
|
218
|
+
trace(2, "Request handler main loop interrupted by #{e.class} exception")
|
219
|
+
raise
|
220
|
+
ensure
|
221
|
+
debug("Exiting request handler main loop")
|
222
|
+
revert_signal_handlers
|
223
|
+
@main_loop_thread_lock.synchronize do
|
224
|
+
@graceful_termination_pipe[1].close rescue nil
|
225
|
+
@graceful_termination_pipe[0].close rescue nil
|
226
|
+
@selectable_sockets = []
|
227
|
+
@main_loop_generation += 1
|
228
|
+
@main_loop_running = false
|
229
|
+
@main_loop_thread_cond.broadcast
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
# Start the main loop in a new thread. This thread will be stopped by #cleanup.
|
235
|
+
def start_main_loop_thread
|
236
|
+
current_generation = @main_loop_generation
|
237
|
+
@main_loop_thread = create_thread_and_abort_on_exception do
|
238
|
+
main_loop
|
239
|
+
end
|
240
|
+
@main_loop_thread_lock.synchronize do
|
241
|
+
while @main_loop_generation == current_generation
|
242
|
+
@main_loop_thread_cond.wait(@main_loop_thread_lock)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
private
|
248
|
+
def should_use_unix_sockets?
|
249
|
+
# Historical note:
|
250
|
+
# There seems to be a bug in MacOS X Leopard w.r.t. Unix server
|
251
|
+
# sockets file descriptors that are passed to another process.
|
252
|
+
# Usually Unix server sockets work fine, but when they're passed
|
253
|
+
# to another process, then clients that connect to the socket
|
254
|
+
# can incorrectly determine that the client socket is closed,
|
255
|
+
# even though that's not actually the case. More specifically:
|
256
|
+
# recv()/read() calls on these client sockets can return 0 even
|
257
|
+
# when we know EOF is not reached.
|
258
|
+
#
|
259
|
+
# The ApplicationPool infrastructure used to connect to a backend
|
260
|
+
# process's Unix socket in the helper server process, and then
|
261
|
+
# pass the connection file descriptor to the web server, which
|
262
|
+
# triggers this kernel bug. We used to work around this by using
|
263
|
+
# TCP sockets instead of Unix sockets; TCP sockets can still fail
|
264
|
+
# with this fake-EOF bug once in a while, but not nearly as often
|
265
|
+
# as with Unix sockets.
|
266
|
+
#
|
267
|
+
# This problem no longer applies today. The web server now passes
|
268
|
+
# all I/O through the HelperAgent, and the bug is no longer
|
269
|
+
# triggered. Nevertheless, we keep this function intact so that
|
270
|
+
# if something like this ever happens again, we know why, and we
|
271
|
+
# can easily reactivate the workaround. Or maybe if we just need
|
272
|
+
# TCP sockets for some other reason.
|
273
|
+
|
274
|
+
#return RUBY_PLATFORM !~ /darwin/
|
275
|
+
|
276
|
+
ruby_engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : "ruby"
|
277
|
+
# Unix domain socket implementation on JRuby
|
278
|
+
# is still bugged as of version 1.7.0. They can
|
279
|
+
# cause unexplicable freezes when used in combination
|
280
|
+
# with threading.
|
281
|
+
return !@force_http_session && ruby_engine != "jruby"
|
282
|
+
end
|
283
|
+
|
284
|
+
def create_unix_socket_on_filesystem(options)
|
285
|
+
if defined?(NativeSupport)
|
286
|
+
unix_path_max = NativeSupport::UNIX_PATH_MAX
|
287
|
+
else
|
288
|
+
unix_path_max = options.fetch('UNIX_PATH_MAX', 100).to_i
|
289
|
+
end
|
290
|
+
if options['socket_dir']
|
291
|
+
socket_dir = options['socket_dir']
|
292
|
+
socket_prefix = "ruby"
|
293
|
+
else
|
294
|
+
socket_dir = Dir.tmpdir
|
295
|
+
socket_prefix = "PsgRubyApp"
|
296
|
+
end
|
297
|
+
|
298
|
+
retry_at_most(128, Errno::EADDRINUSE) do
|
299
|
+
socket_address = "#{socket_dir}/#{socket_prefix}.#{generate_random_id(:base64)}"
|
300
|
+
socket_address = socket_address.slice(0, unix_path_max - 10)
|
301
|
+
socket = UNIXServer.new(socket_address)
|
302
|
+
socket.listen(BACKLOG_SIZE)
|
303
|
+
socket.binmode
|
304
|
+
socket.sync = true
|
305
|
+
socket.close_on_exec!
|
306
|
+
File.chmod(0600, socket_address)
|
307
|
+
["unix:#{socket_address}", socket]
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
def create_tcp_socket
|
312
|
+
# We use "127.0.0.1" as address in order to force
|
313
|
+
# TCPv4 instead of TCPv6.
|
314
|
+
socket = TCPServer.new('127.0.0.1', 0)
|
315
|
+
socket.listen(BACKLOG_SIZE)
|
316
|
+
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
317
|
+
socket.binmode
|
318
|
+
socket.sync = true
|
319
|
+
socket.close_on_exec!
|
320
|
+
socket_address = "tcp://127.0.0.1:#{socket.addr[1]}"
|
321
|
+
return [socket_address, socket]
|
322
|
+
end
|
323
|
+
|
324
|
+
# Reset signal handlers to their default handler, and install some
|
325
|
+
# special handlers for a few signals. The previous signal handlers
|
326
|
+
# will be put back by calling revert_signal_handlers.
|
327
|
+
def reset_signal_handlers
|
328
|
+
Signal.list_trappable.each_key do |signal|
|
329
|
+
begin
|
330
|
+
prev_handler = trap(signal, DEFAULT)
|
331
|
+
if prev_handler != DEFAULT
|
332
|
+
@previous_signal_handlers[signal] = prev_handler
|
333
|
+
end
|
334
|
+
rescue ArgumentError
|
335
|
+
# Signal cannot be trapped; ignore it.
|
336
|
+
end
|
337
|
+
end
|
338
|
+
trap('HUP', IGNORE)
|
339
|
+
PhusionPassenger.call_event(:after_installing_signal_handlers)
|
340
|
+
end
|
341
|
+
|
342
|
+
def install_useful_signal_handlers
|
343
|
+
trappable_signals = Signal.list_trappable
|
344
|
+
|
345
|
+
trap('ABRT') do
|
346
|
+
print_status_report
|
347
|
+
end if trappable_signals.has_key?('ABRT')
|
348
|
+
trap('QUIT') do
|
349
|
+
print_status_report
|
350
|
+
end if trappable_signals.has_key?('QUIT')
|
351
|
+
end
|
352
|
+
|
353
|
+
def revert_signal_handlers
|
354
|
+
@previous_signal_handlers.each_pair do |signal, handler|
|
355
|
+
trap(signal, handler)
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
def print_status_report
|
360
|
+
warn(Utils.global_backtrace_report)
|
361
|
+
warn("Threads: #{@threads.inspect}")
|
362
|
+
end
|
363
|
+
|
364
|
+
def start_threads
|
365
|
+
common_options = {
|
366
|
+
:app => @app,
|
367
|
+
:app_group_name => @app_group_name,
|
368
|
+
:connect_password => @connect_password,
|
369
|
+
:union_station_core => @union_station_core,
|
370
|
+
:keepalive_enabled => @keepalive
|
371
|
+
}
|
372
|
+
main_socket_options = common_options.merge(
|
373
|
+
:server_socket => @main_socket,
|
374
|
+
:socket_name => "main socket",
|
375
|
+
:protocol => @server_sockets[:main][:protocol] == :session ?
|
376
|
+
:session :
|
377
|
+
:http
|
378
|
+
)
|
379
|
+
http_socket_options = common_options.merge(
|
380
|
+
:server_socket => @http_socket,
|
381
|
+
:socket_name => "HTTP socket",
|
382
|
+
:protocol => :http
|
383
|
+
)
|
384
|
+
|
385
|
+
# Used for marking threads that have finished initializing,
|
386
|
+
# or failed during initialization. Threads that are not yet done
|
387
|
+
# are not in `initialization_state`. Threads that have succeeded
|
388
|
+
# set their own state to true. Threads that have failed set their
|
389
|
+
# own state to false.
|
390
|
+
initialization_state_mutex = Mutex.new
|
391
|
+
initialization_state_cond = ConditionVariable.new
|
392
|
+
initialization_state = {}
|
393
|
+
set_initialization_state = lambda do |value|
|
394
|
+
initialization_state_mutex.synchronize do
|
395
|
+
initialization_state[Thread.current] = value
|
396
|
+
initialization_state_cond.signal
|
397
|
+
end
|
398
|
+
end
|
399
|
+
set_initialization_state_to_true = lambda do
|
400
|
+
set_initialization_state.call(true)
|
401
|
+
end
|
402
|
+
|
403
|
+
# Actually start all the threads.
|
404
|
+
thread_handler = @thread_handler
|
405
|
+
expected_nthreads = 0
|
406
|
+
|
407
|
+
@threads_mutex.synchronize do
|
408
|
+
@concurrency.times do |i|
|
409
|
+
thread = create_thread_and_abort_on_exception(i) do |number|
|
410
|
+
begin
|
411
|
+
Thread.current[:name] = "Worker #{number + 1}"
|
412
|
+
handler = thread_handler.new(self, main_socket_options)
|
413
|
+
handler.install
|
414
|
+
handler.main_loop(set_initialization_state_to_true)
|
415
|
+
ensure
|
416
|
+
set_initialization_state.call(false)
|
417
|
+
unregister_current_thread
|
418
|
+
end
|
419
|
+
end
|
420
|
+
@threads << thread
|
421
|
+
expected_nthreads += 1
|
422
|
+
end
|
423
|
+
|
424
|
+
thread = create_thread_and_abort_on_exception do
|
425
|
+
begin
|
426
|
+
Thread.current[:name] = "HTTP helper worker"
|
427
|
+
handler = thread_handler.new(self, http_socket_options)
|
428
|
+
handler.install
|
429
|
+
handler.main_loop(set_initialization_state_to_true)
|
430
|
+
ensure
|
431
|
+
set_initialization_state.call(false)
|
432
|
+
unregister_current_thread
|
433
|
+
end
|
434
|
+
end
|
435
|
+
@threads << thread
|
436
|
+
expected_nthreads += 1
|
437
|
+
end
|
438
|
+
|
439
|
+
# Wait until all threads have finished starting.
|
440
|
+
initialization_state_mutex.synchronize do
|
441
|
+
while initialization_state.size != expected_nthreads
|
442
|
+
initialization_state_cond.wait(initialization_state_mutex)
|
443
|
+
end
|
444
|
+
end
|
445
|
+
end
|
446
|
+
|
447
|
+
def unregister_current_thread
|
448
|
+
@threads_mutex.synchronize do
|
449
|
+
@threads.delete(Thread.current)
|
450
|
+
end
|
451
|
+
end
|
452
|
+
|
453
|
+
def wait_until_termination_requested
|
454
|
+
ruby_engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : "ruby"
|
455
|
+
if ruby_engine == "jruby"
|
456
|
+
# On JRuby, selecting on an input TTY always returns, so
|
457
|
+
# we use threads to do the job.
|
458
|
+
owner_pipe_watcher = IO.pipe
|
459
|
+
owner_pipe_watcher_thread = create_thread_and_abort_on_exception do
|
460
|
+
Thread.current[:name] = "Owner pipe waiter"
|
461
|
+
begin
|
462
|
+
@owner_pipe.read(1)
|
463
|
+
ensure
|
464
|
+
owner_pipe_watcher[1].write('x')
|
465
|
+
end
|
466
|
+
end
|
467
|
+
begin
|
468
|
+
ios = select([owner_pipe_watcher[0], @graceful_termination_pipe[0]])[0]
|
469
|
+
if ios.include?(owner_pipe_watcher[0])
|
470
|
+
trace(2, "Owner pipe closed")
|
471
|
+
else
|
472
|
+
trace(2, "Graceful termination pipe closed")
|
473
|
+
end
|
474
|
+
ensure
|
475
|
+
owner_pipe_watcher_thread.kill
|
476
|
+
owner_pipe_watcher_thread.join
|
477
|
+
owner_pipe_watcher[0].close if !owner_pipe_watcher[0].closed?
|
478
|
+
owner_pipe_watcher[1].close if !owner_pipe_watcher[1].closed?
|
479
|
+
end
|
480
|
+
else
|
481
|
+
ios = select([@owner_pipe, @graceful_termination_pipe[0]])[0]
|
482
|
+
if ios.include?(@owner_pipe)
|
483
|
+
trace(2, "Owner pipe closed")
|
484
|
+
else
|
485
|
+
trace(2, "Graceful termination pipe closed")
|
486
|
+
end
|
487
|
+
end
|
488
|
+
end
|
489
|
+
|
490
|
+
def wakeup_all_threads
|
491
|
+
threads = []
|
492
|
+
if get_socket_address_type(@server_sockets[:main][:address]) == :unix &&
|
493
|
+
!File.exist?(@server_sockets[:main][:address].sub(/^unix:/, ''))
|
494
|
+
# It looks like someone deleted the Unix domain socket we listen on.
|
495
|
+
# This makes it impossible to wake up the worker threads gracefully,
|
496
|
+
# so we hard kill them.
|
497
|
+
warn("Unix domain socket gone; force aborting all threads")
|
498
|
+
@threads_mutex.synchronize do
|
499
|
+
@threads.each do |thread|
|
500
|
+
thread.raise(RuntimeError.new("Force abort"))
|
501
|
+
end
|
502
|
+
end
|
503
|
+
else
|
504
|
+
@concurrency.times do
|
505
|
+
threads << create_thread_and_abort_on_exception(@server_sockets[:main][:address]) do |address|
|
506
|
+
begin
|
507
|
+
debug("Shutting down worker thread by connecting to #{address}")
|
508
|
+
connect_to_server(address).close
|
509
|
+
rescue Errno::ECONNREFUSED
|
510
|
+
debug("Worker thread listening on #{address} already exited")
|
511
|
+
rescue SystemCallError, IOError => e
|
512
|
+
debug("Error shutting down worker thread (#{address}): #{e} (#{e.class})")
|
513
|
+
end
|
514
|
+
end
|
515
|
+
end
|
516
|
+
end
|
517
|
+
threads << create_thread_and_abort_on_exception(@server_sockets[:http][:address]) do |address|
|
518
|
+
begin
|
519
|
+
debug("Shutting down HTTP thread by connecting to #{address}")
|
520
|
+
connect_to_server(address).close
|
521
|
+
rescue Errno::ECONNREFUSED
|
522
|
+
debug("Worker thread listening on #{address} already exited")
|
523
|
+
rescue SystemCallError, IOError => e
|
524
|
+
debug("Error shutting down HTTP thread (#{address}): #{e} (#{e.class})")
|
525
|
+
end
|
526
|
+
end
|
527
|
+
return threads
|
528
|
+
end
|
529
|
+
|
530
|
+
def terminate_threads
|
531
|
+
debug("Stopping all threads")
|
532
|
+
threads = @threads_mutex.synchronize do
|
533
|
+
@threads.dup
|
534
|
+
end
|
535
|
+
threads.each do |thr|
|
536
|
+
thr.raise(ThreadHandler::Interrupted.new)
|
537
|
+
end
|
538
|
+
threads.each do |thr|
|
539
|
+
thr.join
|
540
|
+
end
|
541
|
+
debug("All threads stopped")
|
542
|
+
end
|
543
|
+
|
544
|
+
def wait_until_all_threads_are_idle
|
545
|
+
debug("Waiting until all threads have become idle...")
|
546
|
+
|
547
|
+
# We wait until 100 ms have passed since all handlers have become
|
548
|
+
# interruptable and remained in the same iterations.
|
549
|
+
|
550
|
+
done = false
|
551
|
+
|
552
|
+
while !done
|
553
|
+
handlers = @threads_mutex.synchronize do
|
554
|
+
@threads.map do |thr|
|
555
|
+
thr[:passenger_thread_handler]
|
556
|
+
end
|
557
|
+
end
|
558
|
+
debug("There are currently #{handlers.size} threads")
|
559
|
+
if handlers.empty?
|
560
|
+
# There are no threads, so we're done.
|
561
|
+
done = true
|
562
|
+
break
|
563
|
+
end
|
564
|
+
|
565
|
+
# Record initial state.
|
566
|
+
handlers.each { |h| h.stats_mutex.lock }
|
567
|
+
iterations = handlers.map { |h| h.iteration }
|
568
|
+
handlers.each { |h| h.stats_mutex.unlock }
|
569
|
+
|
570
|
+
start_time = Time.now
|
571
|
+
sleep 0.01
|
572
|
+
|
573
|
+
while true
|
574
|
+
if handlers.size != @threads_mutex.synchronize { @threads.size }
|
575
|
+
debug("The number of threads changed. Restarting waiting algorithm")
|
576
|
+
break
|
577
|
+
end
|
578
|
+
|
579
|
+
# Record current state.
|
580
|
+
handlers.each { |h| h.stats_mutex.lock }
|
581
|
+
all_interruptable = handlers.all? { |h| h.interruptable }
|
582
|
+
new_iterations = handlers.map { |h| h.iteration }
|
583
|
+
|
584
|
+
# Are all threads interruptable and has there been no activity
|
585
|
+
# since last time we checked?
|
586
|
+
if all_interruptable && new_iterations == iterations
|
587
|
+
# Yes. If enough time has passed then we're done.
|
588
|
+
handlers.each { |h| h.stats_mutex.unlock }
|
589
|
+
if Time.now >= start_time + 0.1
|
590
|
+
done = true
|
591
|
+
break
|
592
|
+
end
|
593
|
+
else
|
594
|
+
# No. We reset the timer and check again later.
|
595
|
+
handlers.each { |h| h.stats_mutex.unlock }
|
596
|
+
iterations = new_iterations
|
597
|
+
start_time = Time.now
|
598
|
+
sleep 0.01
|
599
|
+
end
|
600
|
+
end
|
601
|
+
end
|
602
|
+
|
603
|
+
debug("All threads are now idle")
|
604
|
+
end
|
605
|
+
end
|
616
606
|
|
617
607
|
end # module PhusionPassenger
|