deep_test_pre 2.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.
- data/CHANGELOG +47 -0
- data/README.rdoc +199 -0
- data/Rakefile +137 -0
- data/lib/deep_test.rb +78 -0
- data/lib/deep_test/agent.rb +108 -0
- data/lib/deep_test/central_command.rb +165 -0
- data/lib/deep_test/cpu_info.rb +22 -0
- data/lib/deep_test/database/mysql_setup_listener.rb +112 -0
- data/lib/deep_test/database/setup_listener.rb +116 -0
- data/lib/deep_test/deadlock_detector.rb +7 -0
- data/lib/deep_test/demon.rb +25 -0
- data/lib/deep_test/distributed/beachhead.rb +104 -0
- data/lib/deep_test/distributed/dispatch_controller.rb +60 -0
- data/lib/deep_test/distributed/establish_beachhead.rb +19 -0
- data/lib/deep_test/distributed/filename_resolver.rb +40 -0
- data/lib/deep_test/distributed/landing_fleet.rb +30 -0
- data/lib/deep_test/distributed/landing_ship.rb +60 -0
- data/lib/deep_test/distributed/remote_deployment.rb +56 -0
- data/lib/deep_test/distributed/rsync.rb +50 -0
- data/lib/deep_test/distributed/shell_environment.rb +50 -0
- data/lib/deep_test/distributed/ssh_client_connection_info.rb +14 -0
- data/lib/deep_test/extensions/object_extension.rb +40 -0
- data/lib/deep_test/failure_message.rb +19 -0
- data/lib/deep_test/lib_root.rb +4 -0
- data/lib/deep_test/listener_list.rb +17 -0
- data/lib/deep_test/local_deployment.rb +46 -0
- data/lib/deep_test/logger.rb +32 -0
- data/lib/deep_test/main.rb +41 -0
- data/lib/deep_test/marshallable_exception_wrapper.rb +44 -0
- data/lib/deep_test/metrics/data.rb +34 -0
- data/lib/deep_test/metrics/measurement.rb +39 -0
- data/lib/deep_test/null_listener.rb +62 -0
- data/lib/deep_test/options.rb +113 -0
- data/lib/deep_test/proxy_io.rb +77 -0
- data/lib/deep_test/rake_tasks.rb +13 -0
- data/lib/deep_test/result_reader.rb +40 -0
- data/lib/deep_test/rspec_detector.rb +21 -0
- data/lib/deep_test/spec.rb +17 -0
- data/lib/deep_test/spec/extensions/example_group_methods.rb +64 -0
- data/lib/deep_test/spec/extensions/example_methods.rb +52 -0
- data/lib/deep_test/spec/extensions/options.rb +43 -0
- data/lib/deep_test/spec/extensions/spec_task.rb +21 -0
- data/lib/deep_test/spec/runner.rb +72 -0
- data/lib/deep_test/spec/work_result.rb +35 -0
- data/lib/deep_test/spec/work_unit.rb +59 -0
- data/lib/deep_test/test.rb +10 -0
- data/lib/deep_test/test/extensions/error.rb +14 -0
- data/lib/deep_test/test/run_test_suite.rb +5 -0
- data/lib/deep_test/test/runner.rb +24 -0
- data/lib/deep_test/test/supervised_test_suite.rb +48 -0
- data/lib/deep_test/test/work_result.rb +35 -0
- data/lib/deep_test/test/work_unit.rb +40 -0
- data/lib/deep_test/test_task.rb +47 -0
- data/lib/deep_test/ui/console.rb +74 -0
- data/lib/deep_test/ui/null.rb +17 -0
- data/lib/deep_test/warlock.rb +146 -0
- data/lib/telegraph.rb +29 -0
- data/lib/telegraph/ack_sequence.rb +14 -0
- data/lib/telegraph/logging.rb +20 -0
- data/lib/telegraph/message.rb +39 -0
- data/lib/telegraph/operator.rb +47 -0
- data/lib/telegraph/switchboard.rb +57 -0
- data/lib/telegraph/wire.rb +73 -0
- data/test/deep_test/agent_test.rb +175 -0
- data/test/deep_test/central_command_test.rb +147 -0
- data/test/deep_test/cpu_info_test.rb +33 -0
- data/test/deep_test/database/mysql_setup_listener_test.rb +18 -0
- data/test/deep_test/demon_test.rb +23 -0
- data/test/deep_test/distributed/beachhead_test.rb +67 -0
- data/test/deep_test/distributed/dispatch_controller_test.rb +162 -0
- data/test/deep_test/distributed/filename_resolver_test.rb +56 -0
- data/test/deep_test/distributed/landing_fleet_test.rb +55 -0
- data/test/deep_test/distributed/landing_ship_test.rb +48 -0
- data/test/deep_test/distributed/remote_deployment_test.rb +134 -0
- data/test/deep_test/distributed/rsync_test.rb +47 -0
- data/test/deep_test/distributed/shell_environment_test.rb +108 -0
- data/test/deep_test/distributed/ssh_client_connection_info_test.rb +34 -0
- data/test/deep_test/extensions/object_extension_test.rb +37 -0
- data/test/deep_test/listener_list_test.rb +22 -0
- data/test/deep_test/local_deployment_test.rb +19 -0
- data/test/deep_test/logger_test.rb +38 -0
- data/test/deep_test/main_test.rb +12 -0
- data/test/deep_test/marshallable_exception_wrapper_test.rb +46 -0
- data/test/deep_test/metrics/data_test.rb +22 -0
- data/test/deep_test/metrics/measurement_test.rb +18 -0
- data/test/deep_test/proxy_io_test.rb +104 -0
- data/test/deep_test/result_reader_test.rb +128 -0
- data/test/deep_test/test/extensions/error_test.rb +42 -0
- data/test/deep_test/test/runner_test.rb +11 -0
- data/test/deep_test/test/supervised_test_suite_test.rb +107 -0
- data/test/deep_test/test/work_result_test.rb +85 -0
- data/test/deep_test/test/work_unit_test.rb +63 -0
- data/test/deep_test/test_task_test.rb +15 -0
- data/test/deep_test/ui/console_test.rb +13 -0
- data/test/deep_test/warlock_test.rb +40 -0
- data/test/test_helper.rb +30 -0
- data/test/test_task_test.rb +75 -0
- metadata +156 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module DeepTest
|
|
2
|
+
module UI
|
|
3
|
+
class Null
|
|
4
|
+
def initialize(options)
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def distributed_failover_to_local(method, exception)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def dispatch_starting(method_name)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def dispatch_finished(method_name)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
module DeepTest
|
|
2
|
+
class Warlock
|
|
3
|
+
def initialize(options)
|
|
4
|
+
@options = options
|
|
5
|
+
@demons_semaphore = Mutex.new
|
|
6
|
+
@demons = []
|
|
7
|
+
@reapers = []
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def start(name, demon, *demon_args)
|
|
11
|
+
# Not synchronizing for the fork seems to cause
|
|
12
|
+
# random errors (Bus Error, Segfault, and GC non-object)
|
|
13
|
+
# in Beachhead processes.
|
|
14
|
+
#
|
|
15
|
+
begin
|
|
16
|
+
pid = nil
|
|
17
|
+
@demons_semaphore.synchronize do
|
|
18
|
+
pid = fork do
|
|
19
|
+
# Fork leaves the semaphore locked and we'll never make it
|
|
20
|
+
# to end of synchronize block.
|
|
21
|
+
#
|
|
22
|
+
# The Ruby 1.8.6 C mutex implementation automatically treats
|
|
23
|
+
# a mutex locked by a dead thread as unlocked and will raise
|
|
24
|
+
# an error if we try to unlock it from this thread.
|
|
25
|
+
#
|
|
26
|
+
@demons_semaphore.unlock if @demons_semaphore.locked?
|
|
27
|
+
|
|
28
|
+
close_open_network_connections
|
|
29
|
+
demon.forked name, @options, demon_args
|
|
30
|
+
|
|
31
|
+
exit
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
raise "fatal: fork returned nil" if pid.nil?
|
|
35
|
+
add_demon name, pid
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
launch_reaper_thread name, pid
|
|
39
|
+
|
|
40
|
+
rescue => e
|
|
41
|
+
puts "exception starting #{name}: #{e}"
|
|
42
|
+
puts "\t" + e.backtrace.join("\n\t")
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def close_open_network_connections
|
|
47
|
+
ObjectSpace.each_object(BasicSocket) do |sock|
|
|
48
|
+
begin
|
|
49
|
+
sock.close
|
|
50
|
+
rescue IOError
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def demon_count
|
|
56
|
+
@demons_semaphore.synchronize do
|
|
57
|
+
@demons.size
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def stop_demons
|
|
62
|
+
DeepTest.logger.debug { "stopping all demons" }
|
|
63
|
+
receivers = @demons_semaphore.synchronize do
|
|
64
|
+
@demons.reverse
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
receivers.reverse.each do |demon|
|
|
68
|
+
name, pid = demon
|
|
69
|
+
if running?(pid)
|
|
70
|
+
DeepTest.logger.debug { "Sending SIGTERM to #{name}, #{pid}" }
|
|
71
|
+
Process.kill("TERM", pid)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
DeepTest.logger.debug { "Warlock: Stopped all receivers" }
|
|
75
|
+
|
|
76
|
+
DeepTest.logger.debug { "waiting for reapers" }
|
|
77
|
+
@reapers.each {|r| r.join}
|
|
78
|
+
|
|
79
|
+
DeepTest.logger.debug { "Warlock: done reaping processes" }
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def exit_when_none_running
|
|
83
|
+
Thread.new do
|
|
84
|
+
wait_for_all_to_finish
|
|
85
|
+
DeepTest.logger.debug { "exiting #{Process.pid} with all demons finished" }
|
|
86
|
+
exit(0)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def wait_for_all_to_finish
|
|
91
|
+
loop do
|
|
92
|
+
Thread.pass
|
|
93
|
+
return unless any_running?
|
|
94
|
+
sleep(0.01)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def any_running?
|
|
99
|
+
@demons_semaphore.synchronize do
|
|
100
|
+
@demons.any? {|name, pid| running?(pid)}
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
#stolen from daemons
|
|
105
|
+
def running?(pid)
|
|
106
|
+
# Check if process is in existence
|
|
107
|
+
# The simplest way to do this is to send signal '0'
|
|
108
|
+
# (which is a single system call) that doesn't actually
|
|
109
|
+
# send a signal
|
|
110
|
+
begin
|
|
111
|
+
Process.kill(0, pid)
|
|
112
|
+
return true
|
|
113
|
+
rescue Errno::ESRCH
|
|
114
|
+
return false
|
|
115
|
+
rescue ::Exception # for example on EPERM (process exists but does not belong to us)
|
|
116
|
+
return true
|
|
117
|
+
#rescue Errno::EPERM
|
|
118
|
+
# return false
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
protected
|
|
123
|
+
|
|
124
|
+
def add_demon(name, pid)
|
|
125
|
+
DeepTest.logger.debug { "Started: #{name} (#{pid})" }
|
|
126
|
+
@demons << [name, pid]
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def remove_demon(name, pid)
|
|
130
|
+
@demons.delete [name, pid]
|
|
131
|
+
DeepTest.logger.debug { "Stopped: #{name} (#{pid})" }
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def launch_reaper_thread(name, pid)
|
|
136
|
+
@reapers << Thread.new do
|
|
137
|
+
Process.detach(pid).join
|
|
138
|
+
DeepTest.logger.debug { "#{name} (#{pid}) reaped" }
|
|
139
|
+
@demons_semaphore.synchronize do
|
|
140
|
+
DeepTest.logger.debug { "Warlock Reaper: removing #{name} (#{pid}) from demon list" }
|
|
141
|
+
remove_demon name, pid
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
data/lib/telegraph.rb
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require 'socket'
|
|
2
|
+
require File.dirname(__FILE__) + "/telegraph/logging"
|
|
3
|
+
require File.dirname(__FILE__) + "/telegraph/ack_sequence"
|
|
4
|
+
require File.dirname(__FILE__) + "/telegraph/message"
|
|
5
|
+
require File.dirname(__FILE__) + "/telegraph/wire"
|
|
6
|
+
require File.dirname(__FILE__) + "/telegraph/operator"
|
|
7
|
+
require File.dirname(__FILE__) + "/telegraph/switchboard"
|
|
8
|
+
|
|
9
|
+
module Telegraph
|
|
10
|
+
class Ping
|
|
11
|
+
attr_reader :value
|
|
12
|
+
|
|
13
|
+
def initialize(value)
|
|
14
|
+
@value = value
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class Pong
|
|
19
|
+
attr_reader :value
|
|
20
|
+
|
|
21
|
+
def initialize(value)
|
|
22
|
+
@value = value
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class NoMessageAvailable < StandardError; end
|
|
27
|
+
class LineDead < StandardError; end
|
|
28
|
+
end
|
|
29
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require 'logger'
|
|
2
|
+
|
|
3
|
+
module Telegraph
|
|
4
|
+
module Logging
|
|
5
|
+
def self.logger
|
|
6
|
+
@logger ||= begin
|
|
7
|
+
l = Logger.new($stdout)
|
|
8
|
+
l.level = Logger.const_get((ENV['TELEGRAPH_LOG_LEVEL'] || 'info').upcase)
|
|
9
|
+
l.formatter = proc do |sev, time, progmane, msg|
|
|
10
|
+
"[#{time.strftime "%T"}] (pid #{Process.pid}) #{msg}\n"
|
|
11
|
+
end
|
|
12
|
+
l
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def debug
|
|
17
|
+
Logging.logger.debug { "#{self.class}: #{yield}" }
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module Telegraph
|
|
2
|
+
class Message
|
|
3
|
+
include Logging
|
|
4
|
+
|
|
5
|
+
attr_reader :body, :sequence_number, :sequence_ack
|
|
6
|
+
|
|
7
|
+
def initialize(body, sequence_number, sequence_ack)
|
|
8
|
+
@body = body
|
|
9
|
+
@sequence_number = sequence_number
|
|
10
|
+
@sequence_ack = sequence_ack
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def write(stream)
|
|
14
|
+
message_data = Marshal.dump(body)
|
|
15
|
+
debug { "send #{message_data[4..20]}... (#{message_data.length} bytes)" }
|
|
16
|
+
stream.write [message_data.size, @sequence_number, @sequence_ack || 0].pack("NNN") + message_data
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class <<self
|
|
20
|
+
include Logging
|
|
21
|
+
|
|
22
|
+
def read(stream)
|
|
23
|
+
header_data = read_data(stream, 12, "header")
|
|
24
|
+
size, sequence_number, sequence_ack = header_data.unpack("NNN")
|
|
25
|
+
|
|
26
|
+
message_data = read_data(stream, size, "message")
|
|
27
|
+
debug { "read #{message_data[4..20]}... (#{message_data.length} bytes)" }
|
|
28
|
+
Message.new Marshal.load(message_data), sequence_number, (sequence_ack == 0 ? nil : sequence_ack)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def read_data(stream, length, label)
|
|
32
|
+
data = stream.read(length)
|
|
33
|
+
raise IOError, "connection closed" unless data
|
|
34
|
+
raise IOError, "incomplete #{label} data" unless data.length == length
|
|
35
|
+
data
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
require 'thread'
|
|
2
|
+
|
|
3
|
+
module Telegraph
|
|
4
|
+
class Operator
|
|
5
|
+
include Logging
|
|
6
|
+
|
|
7
|
+
attr_reader :switchboard
|
|
8
|
+
|
|
9
|
+
def self.listen(host, port, switchboard = Switchboard.new)
|
|
10
|
+
new TCPServer.new(host, port), switchboard
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def initialize(socket, switchboard)
|
|
14
|
+
@socket = socket
|
|
15
|
+
@switchboard = switchboard
|
|
16
|
+
@accept_thread = Thread.new do
|
|
17
|
+
@socket.listen 100
|
|
18
|
+
loop do
|
|
19
|
+
if @should_shutdown
|
|
20
|
+
@socket.close
|
|
21
|
+
@switchboard.close_all_wires
|
|
22
|
+
break
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
begin
|
|
26
|
+
client = @socket.accept_nonblock
|
|
27
|
+
debug { "Accepted connection: #{client.inspect}" }
|
|
28
|
+
@switchboard.add_wire Wire.new(client)
|
|
29
|
+
rescue Errno::EAGAIN, Errno::ECONNABORTED, Errno::EPROTO, Errno::EINTR
|
|
30
|
+
connection_ready, = IO.select([@socket], nil, nil, 0.25)
|
|
31
|
+
retry if connection_ready
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def port
|
|
38
|
+
@socket.addr[1]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def shutdown
|
|
42
|
+
debug { "Shutting down" }
|
|
43
|
+
@should_shutdown = true
|
|
44
|
+
@accept_thread.join
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module Telegraph
|
|
2
|
+
class Switchboard
|
|
3
|
+
include Logging
|
|
4
|
+
|
|
5
|
+
def process_messages(options = {:timeout => 0})
|
|
6
|
+
yield next_message(options) while true
|
|
7
|
+
rescue NoMessageAvailable
|
|
8
|
+
retry
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def next_message(options = {:timeout => 0})
|
|
12
|
+
debug { "Waiting for next message on any of #{live_wires.size} wires for #{options[:timeout]} seconds" }
|
|
13
|
+
|
|
14
|
+
if live_wires.empty?
|
|
15
|
+
sleep 0.01
|
|
16
|
+
Thread.pass
|
|
17
|
+
raise NoMessageAvailable
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
readers, = IO.select live_wires.map {|w| w.stream}, nil, nil, options[:timeout]
|
|
21
|
+
raise NoMessageAvailable unless readers
|
|
22
|
+
|
|
23
|
+
wire = using_wires {|wires| wires.detect {|w| w.stream == readers.first} }
|
|
24
|
+
return wire.next_message(options.merge(:timeout => 0)), wire
|
|
25
|
+
rescue LineDead => e
|
|
26
|
+
debug { "LineDead: #{e.message} while reading message from wire" }
|
|
27
|
+
raise NoMessageAvailable
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def any_live_wires?
|
|
31
|
+
live_wires.any?
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def drop_wire(wire)
|
|
35
|
+
using_wires {|w| w.delete wire }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def add_wire(wire)
|
|
39
|
+
using_wires {|w| w << wire }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def close_all_wires
|
|
43
|
+
debug { "Closing all wires" }
|
|
44
|
+
using_wires {|w| w.each { |wire| wire.close rescue nil } }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def live_wires
|
|
48
|
+
using_wires {|w| w.select {|wire| !wire.closed?}}
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def using_wires
|
|
52
|
+
@wires ||= []
|
|
53
|
+
@wires_mutex ||= Mutex.new
|
|
54
|
+
@wires_mutex.synchronize { yield @wires }
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
module Telegraph
|
|
2
|
+
class Wire
|
|
3
|
+
include Logging
|
|
4
|
+
|
|
5
|
+
attr_reader :stream
|
|
6
|
+
|
|
7
|
+
def self.connect(host, port)
|
|
8
|
+
wire = new TCPSocket.new(host, port)
|
|
9
|
+
return wire unless block_given?
|
|
10
|
+
begin
|
|
11
|
+
yield wire
|
|
12
|
+
ensure
|
|
13
|
+
wire.close
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def initialize(stream)
|
|
18
|
+
@sequence = AckSequence.new
|
|
19
|
+
@stream = stream
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def close
|
|
23
|
+
if @stream.closed?
|
|
24
|
+
debug { "stream already closed" }
|
|
25
|
+
else
|
|
26
|
+
debug { "closing stream #{@stream.inspect}" }
|
|
27
|
+
@stream.close
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def closed?
|
|
32
|
+
@stream.closed?
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def send_message(body, options = {})
|
|
36
|
+
sequence_ack = options[:ack] ? options[:ack].sequence_number : nil
|
|
37
|
+
message = Message.new(body, @sequence.next, sequence_ack)
|
|
38
|
+
message.write stream
|
|
39
|
+
unacked_sequences_numbers[message.sequence_number] = message if options[:need_ack]
|
|
40
|
+
rescue IOError, Errno::EPIPE, Errno::ECONNRESET => e
|
|
41
|
+
close rescue nil
|
|
42
|
+
raise LineDead, e.message
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def process_messages(options = {:timeout => 0})
|
|
46
|
+
yield next_message(options) while true
|
|
47
|
+
rescue NoMessageAvailable
|
|
48
|
+
retry
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def next_message(options = {:timeout => 0})
|
|
52
|
+
begin
|
|
53
|
+
raise NoMessageAvailable unless IO.select [@stream], nil, nil, options[:timeout]
|
|
54
|
+
message = Message.read(@stream)
|
|
55
|
+
unacked_sequences_numbers.delete message.sequence_ack if message.sequence_ack
|
|
56
|
+
return message
|
|
57
|
+
rescue IOError, Errno::ECONNRESET => e
|
|
58
|
+
raise LineDead, e.message
|
|
59
|
+
end
|
|
60
|
+
rescue LineDead
|
|
61
|
+
close rescue nil
|
|
62
|
+
raise
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def unacked_sequences_numbers
|
|
66
|
+
@unacked_sequences_numbers ||= {}
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def unacked_messages
|
|
70
|
+
unacked_sequences_numbers.values
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|