stomp 1.1.5 → 1.1.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,7 @@
1
+ == ?
2
+
3
+ * Fixed multi-thread app hanging
4
+
1
5
  == 1.1.5 2010-17-03
2
6
 
3
7
  * Added publish method (send is now deprecated)
@@ -17,6 +17,7 @@ require 'stomp/ext/hash'
17
17
  require 'stomp/connection'
18
18
  require 'stomp/client'
19
19
  require 'stomp/message'
20
+ require 'stomp/version'
20
21
  require 'stomp/errors'
21
22
 
22
23
  module Stomp
@@ -286,15 +286,12 @@ module Stomp
286
286
 
287
287
  @listener_thread = Thread.start do
288
288
  while true
289
- message = @connection.poll
290
- case
291
- when message.nil?
292
- sleep 0.1
293
- when message.command == 'MESSAGE'
289
+ message = @connection.receive
290
+ if message.command == 'MESSAGE'
294
291
  if listener = @listeners[message.headers['destination']]
295
292
  listener.call(message)
296
293
  end
297
- when message.command == 'RECEIPT'
294
+ elsif message.command == 'RECEIPT'
298
295
  if listener = @receipt_listeners[message.headers['receipt-id']]
299
296
  listener.call(message)
300
297
  end
@@ -1,5 +1,4 @@
1
1
  require 'socket'
2
- require 'monitor'
3
2
  require 'timeout'
4
3
 
5
4
  module Stomp
@@ -71,8 +70,11 @@ module Stomp
71
70
  @parameters = nil
72
71
  end
73
72
 
73
+ # Use Mutexes: only one lock per each thread
74
+ # Revert to original implementation attempt
74
75
  @transmit_semaphore = Mutex.new
75
- @read_semaphore = Monitor.new
76
+ @read_semaphore = Mutex.new
77
+ @socket_semaphore = Mutex.new
76
78
 
77
79
  @subscriptions = {}
78
80
  @failure = nil
@@ -98,7 +100,7 @@ module Stomp
98
100
  end
99
101
 
100
102
  def socket
101
- @read_semaphore.synchronize do
103
+ @socket_semaphore.synchronize do
102
104
  used_socket = @socket
103
105
  used_socket = nil if closed?
104
106
 
@@ -154,7 +156,7 @@ module Stomp
154
156
  end
155
157
 
156
158
  def change_host
157
- @parameters[:hosts].shuffle! if @parameters[:randomize]
159
+ @parameters[:hosts] = @parameters[:hosts].sort_by { rand } if @parameters[:randomize]
158
160
 
159
161
  # Set first as master and send it to the end of array
160
162
  current_host = @parameters[:hosts].shift
@@ -169,7 +171,7 @@ module Stomp
169
171
  end
170
172
 
171
173
  def max_reconnect_attempts?
172
- !(@parameters.nil? || @parameters[:max_reconnect_attempts].nil?) && @parameters[:max_reconnect_attempts] != 0 && @connection_attempts > @parameters[:max_reconnect_attempts]
174
+ !(@parameters.nil? || @parameters[:max_reconnect_attempts].nil?) && @parameters[:max_reconnect_attempts] != 0 && @connection_attempts >= @parameters[:max_reconnect_attempts]
173
175
  end
174
176
 
175
177
  def increase_reconnect_delay
@@ -308,10 +310,10 @@ module Stomp
308
310
  # Return a pending message if one is available, otherwise
309
311
  # return nil
310
312
  def poll
311
- @read_semaphore.synchronize do
312
- return nil if @socket.nil? || !@socket.ready?
313
- receive
314
- end
313
+ # No need for a read lock here. The receive method eventually fullfills
314
+ # that requirement.
315
+ return nil if @socket.nil? || !@socket.ready?
316
+ receive
315
317
  end
316
318
 
317
319
  # Receive a frame, block until the frame is received
@@ -20,5 +20,5 @@ class ::Hash
20
20
  end
21
21
 
22
22
  symbolized
23
- end unless self.respond_to? :symbolize_keys
23
+ end unless self.method_defined?(:symbolize_keys)
24
24
  end
@@ -2,7 +2,7 @@ module Stomp
2
2
  module Version #:nodoc: all
3
3
  MAJOR = 1
4
4
  MINOR = 1
5
- PATCH = 5
5
+ PATCH = 6
6
6
  STRING = "#{MAJOR}.#{MINOR}.#{PATCH}"
7
7
  end
8
8
  end
@@ -334,11 +334,12 @@ describe Stomp::Connection do
334
334
  @parameters[:max_reconnect_attempts] = limit
335
335
  @connection = Stomp::Connection.new(@parameters)
336
336
 
337
- @connection.instance_variable_set(:@connection_attempts, limit)
337
+ @connection.instance_variable_set(:@connection_attempts, limit-1)
338
338
  @connection.max_reconnect_attempts?.should be_false
339
339
 
340
- @connection.instance_variable_set(:@connection_attempts, limit+1)
340
+ @connection.instance_variable_set(:@connection_attempts, limit)
341
341
  @connection.max_reconnect_attempts?.should be_true
342
+
342
343
  end
343
344
  end
344
345
  end
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{stomp}
8
- s.version = "1.1.5"
8
+ s.version = "1.1.6"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Brian McCallister", "Marius Mathiesen", "Thiago Morello"]
12
- s.date = %q{2010-03-17}
12
+ s.date = %q{2010-06-10}
13
13
  s.description = %q{Ruby client for the Stomp messaging protocol}
14
14
  s.email = ["brianm@apache.org", "marius@stones.com", "morellon@gmail.com"]
15
15
  s.executables = ["catstomp", "stompcat"]
@@ -7,6 +7,9 @@ class TestClient < Test::Unit::TestCase
7
7
 
8
8
  def setup
9
9
  @client = Stomp::Client.new(user, passcode, host, port)
10
+ # Multi_thread test data
11
+ @max_threads = 20
12
+ @max_msgs = 50
10
13
  end
11
14
 
12
15
  def teardown
@@ -36,7 +39,6 @@ class TestClient < Test::Unit::TestCase
36
39
  assert_equal message_text, received.body
37
40
  end
38
41
 
39
- # BROKEN
40
42
  def test_noack
41
43
  @client.publish destination, message_text
42
44
 
@@ -49,11 +51,13 @@ class TestClient < Test::Unit::TestCase
49
51
  # was never acked so should be resent to next client
50
52
 
51
53
  @client = Stomp::Client.new(user, passcode, host, port)
52
- received = nil
53
- @client.subscribe(destination) {|msg| received = msg}
54
- sleep 0.01 until received
54
+ received2 = nil
55
+ @client.subscribe(destination) {|msg| received2 = msg}
56
+ sleep 0.01 until received2
55
57
 
56
- assert_equal message_text, received.body
58
+ assert_equal message_text, received2.body
59
+ assert_equal received.body, received2.body
60
+ assert_equal received.headers['message-id'], received2.headers['message-id']
57
61
  end
58
62
 
59
63
  def test_receipts
@@ -178,13 +182,82 @@ class TestClient < Test::Unit::TestCase
178
182
  def test_unsubscribe
179
183
  message = nil
180
184
  client = Stomp::Client.new(user, passcode, host, port, true)
181
- client.subscribe(destination, :ack => 'client') { |m| message = m }
182
- @client.publish destination, message_text
183
- Timeout::timeout(4) do
184
- sleep 0.01 until message
185
+ assert_nothing_raised {
186
+ client.subscribe(destination, :ack => 'client') { |m| message = m }
187
+ @client.publish destination, message_text
188
+ Timeout::timeout(4) do
189
+ sleep 0.01 until message
190
+ end
191
+ }
192
+ assert_equal message_text, message.body
193
+ assert_nothing_raised {
194
+ client.unsubscribe destination # was throwing exception on unsub at one point
195
+ client.close
196
+ }
197
+ # Same message should remain on the queue. Receive it again with ack=>auto.
198
+ message_copy = nil
199
+ client = Stomp::Client.new(user, passcode, host, port, true)
200
+ assert_nothing_raised {
201
+ client.subscribe(destination, :ack => 'auto') { |m| message_copy = m }
202
+ Timeout::timeout(4) do
203
+ sleep 0.01 until message_copy
204
+ end
205
+ }
206
+ assert_equal message_text, message_copy.body
207
+ assert_equal message.headers['message-id'], message_copy.headers['message-id']
208
+ end
209
+
210
+ def test_thread_one_subscribe
211
+ msg = nil
212
+ Thread.new(@client) do |acli|
213
+ assert_nothing_raised {
214
+ acli.subscribe(destination) { |m| msg = m }
215
+ Timeout::timeout(4) do
216
+ sleep 0.01 until msg
217
+ end
218
+ }
185
219
  end
186
- client.unsubscribe destination # was throwing exception on unsub at one point
220
+ #
221
+ @client.publish(destination, message_text)
222
+ sleep 1
223
+ assert_not_nil msg
224
+ end
187
225
 
226
+ def test_thread_multi_subscribe
227
+ #
228
+ lock = Mutex.new
229
+ msg_ctr = 0
230
+ 1.upto(@max_threads) do |tnum|
231
+ # Threads within threads .....
232
+ Thread.new(@client) do |acli|
233
+ assert_nothing_raised {
234
+ acli.subscribe(destination) { |m|
235
+ msg = m
236
+ lock.synchronize do
237
+ msg_ctr += 1
238
+ end
239
+ # Simulate message processing
240
+ sleep 0.05
241
+ }
242
+ }
243
+ end
244
+ end
245
+ #
246
+ 1.upto(@max_msgs) do |mnum|
247
+ msg = Time.now.to_s + " #{mnum}"
248
+ @client.publish(destination, message_text)
249
+ end
250
+ #
251
+ max_sleep=5
252
+ sleep_incr = 0.10
253
+ total_slept = 0
254
+ while true
255
+ break if @max_msgs == msg_ctr
256
+ total_slept += sleep_incr
257
+ break if total_slept > max_sleep
258
+ sleep sleep_incr
259
+ end
260
+ assert_equal @max_msgs, msg_ctr
188
261
  end
189
262
 
190
263
  private
@@ -7,6 +7,9 @@ class TestStomp < Test::Unit::TestCase
7
7
 
8
8
  def setup
9
9
  @conn = Stomp::Connection.open(user, passcode, host, port)
10
+ # Data for multi_thread tests
11
+ @max_threads = 20
12
+ @max_msgs = 100
10
13
  end
11
14
 
12
15
  def teardown
@@ -107,6 +110,117 @@ class TestStomp < Test::Unit::TestCase
107
110
  assert_equal "b\0", msg_b.body
108
111
  end
109
112
 
113
+ def test_thread_hang_one
114
+ received = nil
115
+ Thread.new(@conn) do |amq|
116
+ while true
117
+ received = amq.receive
118
+ end
119
+ end
120
+ #
121
+ @conn.subscribe( make_destination )
122
+ message = Time.now.to_s
123
+ @conn.publish(make_destination, message)
124
+ sleep 1
125
+ assert_not_nil received
126
+ assert_equal message, received.body
127
+ end
128
+
129
+ def test_thread_poll_one
130
+ received = nil
131
+ Thread.new(@conn) do |amq|
132
+ while true
133
+ received = amq.poll
134
+ # One message is needed
135
+ Thread.exit if received
136
+ sleep 0.1
137
+ end
138
+ end
139
+ #
140
+ @conn.subscribe( make_destination )
141
+ message = Time.now.to_s
142
+ @conn.publish(make_destination, message)
143
+ sleep 1
144
+ assert_not_nil received
145
+ assert_equal message, received.body
146
+ end
147
+
148
+ def test_multi_thread_receive
149
+ lock = Mutex.new
150
+ msg_ctr = 0
151
+ #
152
+ 1.upto(@max_threads) do |tnum|
153
+ Thread.new(@conn) do |amq|
154
+ while true
155
+ received = amq.receive
156
+ lock.synchronize do
157
+ msg_ctr += 1
158
+ end
159
+ # Simulate message processing
160
+ sleep 0.05
161
+ end
162
+ end
163
+ end
164
+ #
165
+ @conn.subscribe( make_destination )
166
+ 1.upto(@max_msgs) do |mnum|
167
+ msg = Time.now.to_s + " #{mnum}"
168
+ @conn.publish(make_destination, msg)
169
+ end
170
+ #
171
+ max_sleep=5
172
+ sleep_incr = 0.10
173
+ total_slept = 0
174
+ while true
175
+ break if @max_msgs == msg_ctr
176
+ total_slept += sleep_incr
177
+ break if total_slept > max_sleep
178
+ sleep sleep_incr
179
+ end
180
+ assert_equal @max_msgs, msg_ctr
181
+ end
182
+
183
+ def test_multi_thread_poll
184
+ #
185
+ lock = Mutex.new
186
+ msg_ctr = 0
187
+ #
188
+ 1.upto(@max_threads) do |tnum|
189
+ Thread.new(@conn) do |amq|
190
+ while true
191
+ received = amq.poll
192
+ if received
193
+ lock.synchronize do
194
+ msg_ctr += 1
195
+ end
196
+ # Simulate message processing
197
+ sleep 0.05
198
+ else
199
+ # Wait a bit for more work
200
+ sleep 0.05
201
+ end
202
+ end
203
+ end
204
+ end
205
+ #
206
+ @conn.subscribe( make_destination )
207
+ 1.upto(@max_msgs) do |mnum|
208
+ msg = Time.now.to_s + " #{mnum}"
209
+ @conn.publish(make_destination, msg)
210
+ end
211
+ #
212
+ max_sleep=5
213
+ sleep_incr = 0.10
214
+ total_slept = 0
215
+ while true
216
+ break if @max_msgs == msg_ctr
217
+ total_slept += sleep_incr
218
+ break if total_slept > max_sleep
219
+ sleep sleep_incr
220
+ end
221
+ assert_equal @max_msgs, msg_ctr
222
+ end
223
+
110
224
  private
111
225
  def make_destination
112
226
  "/queue/test/ruby/stomp/" + name
@@ -133,3 +247,4 @@ class TestStomp < Test::Unit::TestCase
133
247
  assert_equal "txn message", msg.body
134
248
  end
135
249
  end
250
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stomp
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.5
4
+ version: 1.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian McCallister
@@ -11,7 +11,7 @@ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
13
 
14
- date: 2010-03-17 00:00:00 -03:00
14
+ date: 2010-06-10 00:00:00 -03:00
15
15
  default_executable:
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency