riser 0.1.0

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.
@@ -0,0 +1,869 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'io/wait'
4
+ require 'socket'
5
+ require 'uri'
6
+
7
+ module Riser
8
+ class TimeoutSizedQueue
9
+ def initialize(size, name: nil)
10
+ @size = size
11
+ @queue = []
12
+ @closed = false
13
+ @mutex = Thread::Mutex.new
14
+ @push_cond = Thread::ConditionVariable.new
15
+ @pop_cond = Thread::ConditionVariable.new
16
+ @name = name && name.dup.freeze
17
+ @stat_enable = false
18
+ end
19
+
20
+ def size
21
+ @mutex.synchronize{ @queue.size }
22
+ end
23
+
24
+ alias length size
25
+
26
+ def empty?
27
+ @mutex.synchronize{ @queue.empty? }
28
+ end
29
+
30
+ def closed?
31
+ @mutex.synchronize{ @closed }
32
+ end
33
+
34
+ def close
35
+ @mutex.synchronize{
36
+ @closed = true
37
+ @pop_cond.broadcast
38
+ }
39
+ nil
40
+ end
41
+
42
+ def at_end_of_queue?
43
+ @mutex.synchronize{ @closed && @queue.empty? }
44
+ end
45
+
46
+ def push(value, timeout_seconds)
47
+ @mutex.synchronize{
48
+ @closed and raise 'closed'
49
+ if (@stat_enable) then
50
+ @stat_push_count += 1
51
+ @stat_push_average_queue_size = (@stat_push_average_queue_size * (@stat_push_count - 1) + @queue.size) / @stat_push_count
52
+ end
53
+ unless (@queue.size < @size) then
54
+ @stat_push_wait_count += 1 if @stat_enable
55
+ @push_cond.wait(@mutex, timeout_seconds)
56
+ unless (@queue.size < @size) then
57
+ @stat_push_timeout_count += 1 if @stat_enable
58
+ return
59
+ end
60
+ end
61
+ @pop_cond.signal
62
+ @queue.push(value)
63
+ self
64
+ }
65
+ end
66
+
67
+ def pop
68
+ @mutex.synchronize{
69
+ if (@stat_enable) then
70
+ @stat_pop_count += 1
71
+ @stat_pop_average_queue_size = (@stat_pop_average_queue_size * (@stat_pop_count - 1) + @queue.size) / @stat_pop_count
72
+ end
73
+ while (@queue.empty?)
74
+ @closed and return
75
+ @stat_pop_wait_count += 1 if @stat_enable
76
+ @pop_cond.wait(@mutex)
77
+ end
78
+ @push_cond.signal
79
+ @queue.shift
80
+ }
81
+ end
82
+
83
+ def stat_reset_no_lock
84
+ @stat_start_time = Time.now
85
+ @stat_push_average_queue_size = 0.0
86
+ @stat_push_count = 0
87
+ @stat_push_wait_count = 0
88
+ @stat_push_timeout_count = 0
89
+ @stat_pop_average_queue_size = 0.0
90
+ @stat_pop_count = 0
91
+ @stat_pop_wait_count = 0
92
+ end
93
+ private :stat_reset_no_lock
94
+
95
+ def stat_start
96
+ @mutex.synchronize{
97
+ unless (@stat_enable) then
98
+ stat_reset_no_lock
99
+ @stat_enable = true
100
+ end
101
+ }
102
+ nil
103
+ end
104
+
105
+ def stat_stop
106
+ @mutex.synchronize{
107
+ @stat_enable = false
108
+ }
109
+ nil
110
+ end
111
+
112
+ def stat_get(reset: true)
113
+ if (@stat_enable) then
114
+ info = nil
115
+ @mutex.synchronize{
116
+ info = {
117
+ queue_name: @name,
118
+ queue_size: @size,
119
+ closed: @closed,
120
+ start_time: @stat_start_time,
121
+ push_average_queue_size: @stat_push_average_queue_size,
122
+ push_count: @stat_push_count,
123
+ push_wait_count: @stat_push_wait_count,
124
+ push_timeout_count: @stat_push_timeout_count,
125
+ pop_average_queue_size: @stat_pop_average_queue_size,
126
+ pop_count: @stat_pop_count,
127
+ pop_wait_count: @stat_pop_wait_count
128
+ }
129
+
130
+ if (reset) then
131
+ stat_reset_no_lock
132
+ end
133
+ }
134
+
135
+ info[:get_time] = Time.now
136
+ info[:elapsed_seconds] = info[:get_time] - info[:start_time]
137
+ info[:push_wait_ratio] = info[:push_wait_count].to_f / info[:push_count]
138
+ info[:push_timeout_ratio] = info[:push_timeout_count].to_f / info[:push_count]
139
+ info[:pop_wait_ratio] = info[:pop_wait_count].to_f / info[:pop_count]
140
+
141
+ # sort
142
+ [ :queue_name,
143
+ :queue_size,
144
+ :closed,
145
+ :start_time,
146
+ :get_time,
147
+ :elapsed_seconds,
148
+ :push_average_queue_size,
149
+ :push_count,
150
+ :push_wait_count,
151
+ :push_wait_ratio,
152
+ :push_timeout_count,
153
+ :push_timeout_ratio,
154
+ :pop_average_queue_size,
155
+ :pop_count,
156
+ :pop_wait_count,
157
+ :pop_wait_ratio
158
+ ].each do |name|
159
+ info[name] = info.delete(name)
160
+ end
161
+
162
+ info
163
+ end
164
+ end
165
+ end
166
+
167
+ class SocketAddress
168
+ def initialize(type)
169
+ @type = type
170
+ end
171
+
172
+ attr_reader :type
173
+
174
+ def to_a
175
+ [ @type ]
176
+ end
177
+
178
+ def to_s
179
+ to_a.map{|s|
180
+ if (s.to_s.include? ':') then
181
+ "[#{s}]"
182
+ else
183
+ s
184
+ end
185
+ }.join(':')
186
+ end
187
+
188
+ def ==(other)
189
+ if (other.is_a? SocketAddress) then
190
+ self.to_a == other.to_a
191
+ else
192
+ false
193
+ end
194
+ end
195
+
196
+ def eql?(other)
197
+ self == other
198
+ end
199
+
200
+ def hash
201
+ to_a.hash ^ self.class.hash
202
+ end
203
+
204
+ def self.parse(config)
205
+ unsquare = proc{|s| s.sub(/\A \[/x, '').sub(/\] \z/x, '') }
206
+ case (config)
207
+ when String
208
+ case (config)
209
+ when /\A tcp:/x
210
+ uri = URI(config)
211
+ if (uri.host && uri.port) then
212
+ return TCPSocketAddress.new(unsquare.call(uri.host), uri.port)
213
+ end
214
+ when /\A unix:/x
215
+ uri = URI(config)
216
+ if (uri.path && ! uri.path.empty?) then
217
+ return UNIXSocketAddress.new(uri.path)
218
+ end
219
+ when %r"\A [A-Za-z]+:/"x
220
+ # unknown URI scheme
221
+ when /\A (\S+):(\d+) \z/x
222
+ host = $1
223
+ port = $2.to_i
224
+ return TCPSocketAddress.new(unsquare.call(host), port)
225
+ end
226
+ when Hash
227
+ if (type = config[:type] || config['type']) then
228
+ case (type.to_s)
229
+ when 'tcp'
230
+ host = config[:host] || config['host']
231
+ port = config[:port] || config['port']
232
+ if (host && (host.is_a? String) && port && (port.is_a? Integer)) then
233
+ return TCPSocketAddress.new(unsquare.call(host), port)
234
+ end
235
+ when 'unix'
236
+ path = config[:path] || config['path']
237
+ if (path && (path.is_a? String) && ! path.empty?) then
238
+ return UNIXSocketAddress.new(path)
239
+ end
240
+ end
241
+ end
242
+ end
243
+
244
+ return
245
+ end
246
+ end
247
+
248
+ class TCPSocketAddress < SocketAddress
249
+ def initialize(host, port)
250
+ super(:tcp)
251
+ @host = host
252
+ @port = port
253
+ end
254
+
255
+ attr_reader :host
256
+ attr_reader :port
257
+
258
+ def to_a
259
+ super << @host << @port
260
+ end
261
+
262
+ def open_server
263
+ TCPServer.new(@host, @port)
264
+ end
265
+ end
266
+
267
+ class UNIXSocketAddress < SocketAddress
268
+ def initialize(path)
269
+ super(:unix)
270
+ @path = path
271
+ end
272
+
273
+ attr_reader :path
274
+
275
+ def to_a
276
+ super << @path
277
+ end
278
+
279
+ def open_server
280
+ UNIXServer.new(@path)
281
+ end
282
+ end
283
+
284
+ module ServerSignal
285
+ SIGNAL_STOP_GRACEFUL = :TERM
286
+ SIGNAL_STOP_FORCED = :INT
287
+ SIGNAL_STAT_GET_AND_RESET = :USR1
288
+ SIGNAL_STAT_GET_NO_RESET = :USR2
289
+ SIGNAL_STAT_STOP = :WINCH
290
+ SIGNAL_RESTART_GRACEFUL = :HUP
291
+ SIGNAL_RESTART_FORCED = :QUIT
292
+ end
293
+
294
+ class SocketThreadDispatcher
295
+ def initialize(thread_queue_name)
296
+ @thread_num = nil
297
+ @thread_queue_name = thread_queue_name
298
+ @thread_queue_size = nil
299
+ @thread_queue_polling_timeout_seconds = nil
300
+ @at_stop = nil
301
+ @at_stat = nil
302
+ @at_stat_get = nil
303
+ @at_stat_stop = nil
304
+ @preprocess = nil
305
+ @postprocess = nil
306
+ @accept = nil
307
+ @accept_return = nil
308
+ @dispatch = nil
309
+ @stop_state = nil
310
+ @stat_operation_queue = []
311
+ end
312
+
313
+ attr_accessor :thread_num
314
+ attr_accessor :thread_queue_size
315
+ attr_accessor :thread_queue_polling_timeout_seconds
316
+
317
+ def at_stop(&block) # :yields: stop_state
318
+ @at_stop = block
319
+ nil
320
+ end
321
+
322
+ def at_stat(&block) # :yields: stat_info
323
+ @at_stat = block
324
+ nil
325
+ end
326
+
327
+ def at_stat_get(&block) # :yields: reset
328
+ @at_stat_get = block
329
+ nil
330
+ end
331
+
332
+ def at_stat_stop(&block) # :yields:
333
+ @at_stat_stop = block
334
+ nil
335
+ end
336
+
337
+ def preprocess(&block) # :yields:
338
+ @preprocess = block
339
+ nil
340
+ end
341
+
342
+ def postprocess(&block) # :yields:
343
+ @postprocess = block
344
+ nil
345
+ end
346
+
347
+ def accept(&block) # :yields:
348
+ @accept = block
349
+ nil
350
+ end
351
+
352
+ def accept_return(&block) # :yields:
353
+ @accept_return = block
354
+ nil
355
+ end
356
+
357
+ def dispatch(&block) # :yields: socket
358
+ @dispatch = block
359
+ nil
360
+ end
361
+
362
+ # should be called from signal(2) handler
363
+ def signal_stop_graceful
364
+ @stop_state ||= :graceful
365
+ nil
366
+ end
367
+
368
+ # should be called from signal(2) handler
369
+ def signal_stop_forced
370
+ @stop_state ||= :forced
371
+ nil
372
+ end
373
+
374
+ # should be called from signal(2) handler
375
+ def signal_stat_get(reset: true)
376
+ if (reset) then
377
+ @stat_operation_queue << :get_and_reset
378
+ else
379
+ @stat_operation_queue << :get
380
+ end
381
+
382
+ nil
383
+ end
384
+
385
+ # should be called from signal(2) handler
386
+ def signal_stat_stop
387
+ @stat_operation_queue << :stop
388
+ nil
389
+ end
390
+
391
+ def apply_signal_stat(queue)
392
+ unless (@stat_operation_queue.empty?) then
393
+ while (stat_ope = @stat_operation_queue.shift)
394
+ case (stat_ope)
395
+ when :get_and_reset
396
+ queue.stat_start
397
+ @at_stat.call(queue.stat_get(reset: true))
398
+ @at_stat_get.call(true)
399
+ when :get
400
+ queue.stat_start
401
+ @at_stat.call(queue.stat_get(reset: false))
402
+ @at_stat_get.call(false)
403
+ when :stop
404
+ queue.stat_stop
405
+ @at_stat_stop.call
406
+ else
407
+ raise "internal error: unknown stat operation <#{stat_ope.inspect}>"
408
+ end
409
+ end
410
+ end
411
+ end
412
+ private :apply_signal_stat
413
+
414
+ # should be executed on the main thread sharing the stack with
415
+ # signal(2) handlers
416
+ #
417
+ # _server_socket is a dummy argument to call like
418
+ # SocketProcessDispatcher#start.
419
+ def start(_server_socket=nil)
420
+ @preprocess.call
421
+ begin
422
+ queue = TimeoutSizedQueue.new(@thread_queue_size, name: @thread_queue_name)
423
+ begin
424
+ thread_list = []
425
+ @thread_num.times{|i|
426
+ thread_list << Thread.new{
427
+ Thread.current[:number] = i
428
+ while (socket = queue.pop)
429
+ begin
430
+ @dispatch.call(socket)
431
+ ensure
432
+ socket.close unless socket.closed?
433
+ end
434
+ end
435
+ }
436
+ }
437
+
438
+ catch (:end_of_server) {
439
+ while (true)
440
+ begin
441
+ @stop_state and throw(:end_of_server)
442
+ apply_signal_stat(queue)
443
+ socket = @accept.call
444
+ end until (socket)
445
+
446
+ until (queue.push(socket, @thread_queue_polling_timeout_seconds))
447
+ if (@stop_state == :forced) then
448
+ socket.close
449
+ @accept_return.call
450
+ throw(:end_of_server)
451
+ end
452
+ apply_signal_stat(queue)
453
+ end
454
+ @accept_return.call
455
+ end
456
+ }
457
+ ensure
458
+ queue.close
459
+ end
460
+
461
+ @at_stop.call(@stop_state)
462
+ case (@stop_state)
463
+ when :graceful
464
+ for thread in thread_list
465
+ thread.join
466
+ end
467
+ when :forced
468
+ for thread in thread_list
469
+ thread.kill
470
+ end
471
+ else
472
+ raise "internal error: unknown stop state <#{@stop_state.inspect}>"
473
+ end
474
+ ensure
475
+ @postprocess.call
476
+ end
477
+
478
+ nil
479
+ end
480
+ end
481
+
482
+ SocketProcess = Struct.new(:pid, :io) # :nodoc:
483
+
484
+ class SocketProcessDispatcher
485
+ include ServerSignal
486
+
487
+ NO_CALL = proc{} # :nodoc:
488
+
489
+ def initialize(process_queue_name, thread_queue_name)
490
+ @accept_polling_timeout_seconds = nil
491
+ @process_num = nil
492
+ @process_queue_name = process_queue_name
493
+ @process_queue_size = nil
494
+ @process_queue_polling_timeout_seconds = nil
495
+ @process_send_io_polling_timeout_seconds = nil
496
+ @thread_num = nil
497
+ @thread_queue_name = thread_queue_name
498
+ @thread_queue_size = nil
499
+ @thread_queue_polling_timeout_seconds = nil
500
+ @at_fork= nil
501
+ @at_stop = nil
502
+ @at_stat = nil
503
+ @preprocess = nil
504
+ @postprocess = nil
505
+ @dispatch = nil
506
+ @process_dispatcher = nil
507
+ end
508
+
509
+ attr_accessor :accept_polling_timeout_seconds
510
+ attr_accessor :process_num
511
+ attr_accessor :process_queue_size
512
+ attr_accessor :process_queue_polling_timeout_seconds
513
+ attr_accessor :process_send_io_polling_timeout_seconds
514
+ attr_accessor :thread_num
515
+ attr_accessor :thread_queue_size
516
+ attr_accessor :thread_queue_polling_timeout_seconds
517
+
518
+ def at_fork(&block) # :yields:
519
+ @at_fork = block
520
+ nil
521
+ end
522
+
523
+ def at_stop(&block) # :yields: stop_state
524
+ @at_stop = block
525
+ nil
526
+ end
527
+
528
+ def at_stat(&block) # :yields: stat_info
529
+ @at_stat = block
530
+ nil
531
+ end
532
+
533
+ def preprocess(&block) # :yields:
534
+ @preprocess = block
535
+ nil
536
+ end
537
+
538
+ def postprocess(&block) # :yields:
539
+ @postprocess = block
540
+ nil
541
+ end
542
+
543
+ def dispatch(&block) # :yields: accept_object
544
+ @dispatch = block
545
+ nil
546
+ end
547
+
548
+ # should be called from signal(2) handler
549
+ def signal_stop_graceful
550
+ @process_dispatcher.signal_stop_graceful if @process_dispatcher
551
+ nil
552
+ end
553
+
554
+ # should be called from signal(2) handler
555
+ def signal_stop_forced
556
+ @process_dispatcher.signal_stop_forced if @process_dispatcher
557
+ nil
558
+ end
559
+
560
+ # should be called from signal(2) handler
561
+ def signal_stat_get(reset: true)
562
+ @process_dispatcher.signal_stat_get(reset: reset) if @process_dispatcher
563
+ nil
564
+ end
565
+
566
+ # should be called from signal(2) handler
567
+ def signal_stat_stop
568
+ @process_dispatcher.signal_stat_stop if @process_dispatcher
569
+ nil
570
+ end
571
+
572
+ # after this method call is completed, the object will be ready to
573
+ # accept `signal_...' methods.
574
+ def setup
575
+ @process_dispatcher = SocketThreadDispatcher.new(@process_queue_name)
576
+ nil
577
+ end
578
+
579
+ def start(server_socket)
580
+ case (server_socket)
581
+ when TCPServer, UNIXServer
582
+ socket_class = server_socket.class.superclass
583
+ else
584
+ socket_class = IO
585
+ end
586
+
587
+ process_list = []
588
+ @process_num.times do |pos|
589
+ child_io, parent_io = UNIXSocket.socketpair
590
+ pid = Process.fork{
591
+ parent_io.close
592
+ pos.times do |i|
593
+ process_list[i].io.close
594
+ end
595
+
596
+ thread_dispatcher = SocketThreadDispatcher.new("#{@thread_queue_name}-#{pos}")
597
+ thread_dispatcher.thread_num = @thread_num
598
+ thread_dispatcher.thread_queue_size = @thread_queue_size
599
+ thread_dispatcher.thread_queue_polling_timeout_seconds = @thread_queue_polling_timeout_seconds
600
+
601
+ thread_dispatcher.at_stop(&@at_stop)
602
+ thread_dispatcher.at_stat(&@at_stat)
603
+ thread_dispatcher.at_stat_get(&NO_CALL)
604
+ thread_dispatcher.at_stat_stop(&NO_CALL)
605
+ thread_dispatcher.preprocess(&@preprocess)
606
+ thread_dispatcher.postprocess(&@postprocess)
607
+
608
+ thread_dispatcher.accept{
609
+ if (child_io.wait_readable(@process_send_io_polling_timeout_seconds) != nil) then
610
+ command = child_io.read(5)
611
+ command == "SEND\n" or raise "internal error: unknown command <#{command.inspect}>"
612
+ child_io.recv_io(socket_class)
613
+ end
614
+ }
615
+ thread_dispatcher.accept_return{ child_io.write("RADY\n") }
616
+ thread_dispatcher.dispatch(&@dispatch)
617
+
618
+ Signal.trap(SIGNAL_STOP_GRACEFUL) { thread_dispatcher.signal_stop_graceful }
619
+ Signal.trap(SIGNAL_STOP_FORCED) { thread_dispatcher.signal_stop_forced }
620
+ Signal.trap(SIGNAL_STAT_GET_AND_RESET) { thread_dispatcher.signal_stat_get(reset: true) }
621
+ Signal.trap(SIGNAL_STAT_GET_NO_RESET) { thread_dispatcher.signal_stat_get(reset: false) }
622
+ Signal.trap(SIGNAL_STAT_STOP) { thread_dispatcher.signal_stat_stop }
623
+
624
+ begin
625
+ child_io.write("RADY\n")
626
+ @at_fork.call
627
+ thread_dispatcher.start
628
+ ensure
629
+ child_io.close
630
+ end
631
+ }
632
+ child_io.close
633
+
634
+ process_list << SocketProcess.new(pid, parent_io)
635
+ end
636
+
637
+ for process in process_list
638
+ response = process.io.read(5)
639
+ response == "RADY\n" or raise "internal error: unknown response <#{response.inspect}>"
640
+ end
641
+
642
+ setup unless @process_dispatcher
643
+ @process_dispatcher.thread_num = @process_num
644
+ @process_dispatcher.thread_queue_size = @process_queue_size
645
+ @process_dispatcher.thread_queue_polling_timeout_seconds = @process_queue_polling_timeout_seconds
646
+
647
+ @process_dispatcher.at_stop{|state|
648
+ case (state)
649
+ when :graceful
650
+ for process in process_list
651
+ Process.kill(SIGNAL_STOP_GRACEFUL, process.pid)
652
+ end
653
+ when :forced
654
+ for process in process_list
655
+ Process.kill(SIGNAL_STOP_FORCED, process.pid)
656
+ end
657
+ end
658
+ }
659
+ @process_dispatcher.at_stat(&@at_stat)
660
+ @process_dispatcher.at_stat_get{|reset|
661
+ if (reset) then
662
+ for process in process_list
663
+ Process.kill(SIGNAL_STAT_GET_AND_RESET, process.pid)
664
+ end
665
+ else
666
+ for process in process_list
667
+ Process.kill(SIGNAL_STAT_GET_NO_RESET, process.pid)
668
+ end
669
+ end
670
+ }
671
+ @process_dispatcher.at_stat_stop{
672
+ for process in process_list
673
+ Process.kill(SIGNAL_STAT_STOP, process.pid)
674
+ end
675
+ }
676
+ @process_dispatcher.preprocess(&NO_CALL)
677
+ @process_dispatcher.postprocess(&NO_CALL)
678
+
679
+ @process_dispatcher.accept{
680
+ if (server_socket.wait_readable(@accept_polling_timeout_seconds) != nil) then
681
+ server_socket.accept
682
+ end
683
+ }
684
+ @process_dispatcher.accept_return(&NO_CALL)
685
+ @process_dispatcher.dispatch{|socket|
686
+ process = process_list[Thread.current[:number]]
687
+ process.io.write("SEND\n")
688
+ process.io.send_io(socket)
689
+ response = process.io.read(5)
690
+ response == "RADY\n" or raise "internal error: unknown response <#{response.inspect}>"
691
+ }
692
+ @process_dispatcher.start
693
+
694
+ for process in process_list
695
+ Process.wait(process.pid)
696
+ process.io.close
697
+ end
698
+
699
+ nil
700
+ end
701
+ end
702
+
703
+ class SocketServer
704
+ NO_CALL = proc{} # :nodoc:
705
+
706
+ def initialize
707
+ @accept_polling_timeout_seconds = 0.1
708
+ @process_num = 0
709
+ @process_queue_size = 20
710
+ @process_queue_polling_timeout_seconds = 0.1
711
+ @process_send_io_polling_timeout_seconds = 0.1
712
+ @thread_num = 4
713
+ @thread_queue_size = 20
714
+ @thread_queue_polling_timeout_seconds = 0.1
715
+ @before_start = NO_CALL
716
+ @at_fork = NO_CALL
717
+ @at_stop = NO_CALL
718
+ @at_stat = NO_CALL
719
+ @preprocess = NO_CALL
720
+ @postprocess = NO_CALL
721
+ @after_stop = NO_CALL
722
+ @dispatch = nil
723
+ @dispatcher = nil
724
+ end
725
+
726
+ attr_accessor :accept_polling_timeout_seconds
727
+ attr_accessor :process_num
728
+ attr_accessor :process_queue_size
729
+ attr_accessor :process_queue_polling_timeout_seconds
730
+ attr_accessor :process_send_io_polling_timeout_seconds
731
+ attr_accessor :thread_num
732
+ attr_accessor :thread_queue_size
733
+ attr_accessor :thread_queue_polling_timeout_seconds
734
+
735
+ def before_start(&block) # :yields: server_socket
736
+ @before_start = block
737
+ nil
738
+ end
739
+
740
+ def at_fork(&block) # :yields:
741
+ @at_fork = block
742
+ nil
743
+ end
744
+
745
+ def at_stop(&block) # :yields: stop_state
746
+ @at_stop = block
747
+ nil
748
+ end
749
+
750
+ def at_stat(&block) # :yields: stat_info
751
+ @at_stat = block
752
+ nil
753
+ end
754
+
755
+ def preprocess(&block) # :yields:
756
+ @preprocess = block
757
+ nil
758
+ end
759
+
760
+ def postprocess(&block) # :yields:
761
+ @postprocess = block
762
+ nil
763
+ end
764
+
765
+ def after_stop(&block) # :yields:
766
+ @after_stop = block
767
+ nil
768
+ end
769
+
770
+ def dispatch(&block) # :yields: socket
771
+ @dispatch = block
772
+ nil
773
+ end
774
+
775
+ # should be called from signal(2) handler
776
+ def signal_stop_graceful
777
+ @dispatcher.signal_stop_graceful if @dispatcher
778
+ nil
779
+ end
780
+
781
+ # should be called from signal(2) handler
782
+ def signal_stop_forced
783
+ @dispatcher.signal_stop_forced if @dispatcher
784
+ nil
785
+ end
786
+
787
+ # should be called from signal(2) handler
788
+ def signal_stat_get(reset: true)
789
+ @dispatcher.signal_stat_get(reset: reset) if @dispatcher
790
+ nil
791
+ end
792
+
793
+ # should be called from signal(2) handler
794
+ def signal_stat_stop
795
+ @dispatcher.signal_stat_stop if @dispatcher
796
+ nil
797
+ end
798
+
799
+ # after this method call is completed, the object will be ready to
800
+ # accept `signal_...' methods.
801
+ def setup(server_socket)
802
+ if (@process_num > 0) then
803
+ @dispatcher = SocketProcessDispatcher.new('process_queue', 'thread_queue')
804
+ @dispatcher.accept_polling_timeout_seconds = @accept_polling_timeout_seconds
805
+ @dispatcher.process_num = @process_num
806
+ @dispatcher.process_queue_size = @process_queue_size
807
+ @dispatcher.process_queue_polling_timeout_seconds = @process_queue_polling_timeout_seconds
808
+ @dispatcher.process_send_io_polling_timeout_seconds = @process_send_io_polling_timeout_seconds
809
+ @dispatcher.thread_num = @thread_num
810
+ @dispatcher.thread_queue_size = @thread_queue_size
811
+ @dispatcher.thread_queue_polling_timeout_seconds = @thread_queue_polling_timeout_seconds
812
+
813
+ @dispatcher.at_fork{
814
+ server_socket.close
815
+ @at_fork.call
816
+ }
817
+ @dispatcher.at_stop(&@at_stop)
818
+ @dispatcher.at_stat(&@at_stat)
819
+ @dispatcher.preprocess(&@preprocess)
820
+ @dispatcher.postprocess(&@postprocess)
821
+ @dispatcher.dispatch(&@dispatch)
822
+ @dispatcher.setup
823
+ else
824
+ @dispatcher = SocketThreadDispatcher.new('thread_queue')
825
+ @dispatcher.thread_num = @thread_num
826
+ @dispatcher.thread_queue_size = @thread_queue_size
827
+ @dispatcher.thread_queue_polling_timeout_seconds = @thread_queue_polling_timeout_seconds
828
+
829
+ @dispatcher.at_stop(&@at_stop)
830
+ @dispatcher.at_stat(&@at_stat)
831
+ @dispatcher.at_stat_get(&NO_CALL)
832
+ @dispatcher.at_stat_stop(&NO_CALL)
833
+ @dispatcher.preprocess(&@preprocess)
834
+ @dispatcher.postprocess(&@postprocess)
835
+ @dispatcher.accept{
836
+ if (server_socket.wait_readable(@accept_polling_timeout_seconds) != nil) then
837
+ server_socket.accept
838
+ end
839
+ }
840
+ @dispatcher.accept_return(&NO_CALL)
841
+ @dispatcher.dispatch(&@dispatch)
842
+ end
843
+
844
+ nil
845
+ end
846
+
847
+ # should be executed on the main thread sharing the stack with
848
+ # signal(2) handlers
849
+ def start(server_socket)
850
+ unless (@dispatcher) then
851
+ setup(server_socket)
852
+ end
853
+
854
+ @before_start.call(server_socket)
855
+ begin
856
+ @dispatcher.start(server_socket)
857
+ ensure
858
+ @after_stop.call
859
+ end
860
+
861
+ nil
862
+ end
863
+ end
864
+ end
865
+
866
+ # Local Variables:
867
+ # mode: Ruby
868
+ # indent-tabs-mode: nil
869
+ # End: