brb 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,9 @@
1
+ 0.3.0 (May 21, 2010)
2
+
3
+ * Added Callback functionality through block
4
+ * Added Callback example
5
+ * Little spec refactoring
6
+
1
7
  0.2.2 (Apr 21, 2010)
2
8
 
3
9
  * Change silent option to verbose
File without changes
@@ -1,17 +1,16 @@
1
1
  = BrB - Easy and Fast distributed ruby
2
2
 
3
- BrB is a simple, fully transparent and extremely fast interface for doing simple distributed ruby.
4
- The main power of the architecture is provided by EventMachine (Fast and reliable IO event library).
3
+ BrB is a simple, fully transparent and extremely fast interface for doing simple distributed Ruby.
4
+ The core of the architecture is provided by EventMachine (a fast and reliable IO event library).
5
5
 
6
- BrB was build in order to achieve these 4 main goals :
7
- * Simple and fast message passing between distant ruby process.
8
- * Message passing with handling of return value when needed.
9
- * Being extremely fast in order to handle more than a few thousand messages per seconds.
6
+ BrB was built in order to achieve these 4 main goals :
7
+ * Simple and fast message passing between distant Ruby processes.
8
+ * Message passing with of return values when needed.
9
+ * Being extremely fast in order to handle more than a few thousand messages per second.
10
10
  * Being completely transparent for developer.
11
11
 
12
- The principle is simple and inspired from Drb (Original distributed ruby library) :
13
- A process want to expose an object on the network, and want other ruby processes calling directly method on it.
14
- BrB handle all that part, it's fully transparent in the ruby code.
12
+ The principle is simple and inspired from Drb (standard distributed ruby library) :
13
+ A process exposes an object over the network and any ruby process (after having established a connection tunnel) can directly call a method on the exposed object. BrB handles that part, so it’s fully transparent in the Ruby code.
15
14
 
16
15
  BrB only support message passing with Marshable dumpable object : String, symbol, Array, hash, Number, Object etc...
17
16
  That mean you can not send file descriptor, Thread or another funky things like that :)
@@ -21,7 +20,8 @@ That mean you can not send file descriptor, Thread or another funky things like
21
20
  * Unlimited objects exposed
22
21
  * Processes can expose object and be client to exposed object too at the same time
23
22
  * Do not wait for return by default : just do simple message passing
24
- * Wait for return if needed by simply adding <em>_block</em> at the end of the function name
23
+ * Handle return values without blocking with the usage of a simple block
24
+ * Blocking wait for return if needed by simply adding <em>_block</em> at the end of the method name
25
25
  * Transmission of Exception when blocking call
26
26
  * Thread safe if used correctly with Event Machine
27
27
 
@@ -31,7 +31,7 @@ First of all, a process declare himself as a sever and expose any object on a gi
31
31
  Then, any number of distant processes can create a <em>Tunnel</em> with that server and can expose an object in exchange too.
32
32
  After connection are ready, just call method on the tunnel. It will just act like normal method calling on the exposed object !
33
33
 
34
- == What BrB is usable for ?
34
+ == What BrB is designed for ?
35
35
 
36
36
  * Doing Simple message passing between ruby process.
37
37
  * Connecting hundred of ruby process transparently.
data/Rakefile CHANGED
@@ -7,7 +7,7 @@ spec_files = Rake::FileList["spec/**/*_spec.rb"]
7
7
  desc "Run specs for current Rails version"
8
8
  Spec::Rake::SpecTask.new do |t|
9
9
  t.spec_files = spec_files
10
- t.spec_opts = ["-c"]
10
+ t.spec_opts = ["-c --format specdoc"]
11
11
  end
12
12
 
13
13
  task :default => :spec
@@ -26,6 +26,14 @@ puts " >> Calling long call, and wait for response..."
26
26
  r = core.simple_long_api_method_block
27
27
  puts " > Api long response : #{r}"
28
28
 
29
+ ## Calling method with a callback block for handling the return value
30
+ core.simple_api_method do |r|
31
+ puts " > Get the callback response : #{r}"
32
+ end
33
+
34
+ puts " >> Callback method has been called continue .."
35
+ sleep 2
36
+
29
37
  core.stop_service
30
38
 
31
39
  # Our job is over, close event machine :
@@ -4,6 +4,7 @@ class ExposedCoreObject
4
4
 
5
5
  def simple_api_method
6
6
  puts "#{Thread.current} > In simple api method, now sleeping"
7
+ yield if block_given?
7
8
  sleep 1
8
9
  puts "#{Thread.current} > Done sleeping in simple api method, return"
9
10
  return 'OK'
data/lib/brb.rb CHANGED
File without changes
@@ -1 +1,9 @@
1
- # Future BrB custom exceptions will come here
1
+ # Future BrB custom exceptions will come here
2
+ class BrBException < Exception
3
+ end
4
+
5
+ class BrBCallbackWithBlockingMethodException < BrBException
6
+ def initialize
7
+ super('Out request can not be blocking and have a callback at the same time !')
8
+ end
9
+ end
@@ -1,5 +1,9 @@
1
1
  module BrB
2
2
  module Request
3
+
4
+ MessageRequestCode = :s
5
+ CallbackRequestCode = :c
6
+ ReturnCode = :r
3
7
 
4
8
  def is_brb_request_blocking?(meth)
5
9
  if m = meth.to_s and m.rindex('_block') == (m.size - 6)
@@ -9,23 +13,34 @@ module BrB
9
13
  end
10
14
 
11
15
  # Execute a request on a distant object
12
- def new_brb_out_request(meth, *args)
16
+ def new_brb_out_request(meth, *args, &blck)
13
17
  Thread.current[:brb_nb_out] ||= 0
14
18
  Thread.current[:brb_nb_out] += 1
15
19
 
16
- block = is_brb_request_blocking?(meth) ? Thread.current.to_s.to_sym : nil
20
+ raise BrBCallbackWithBlockingMethodException.new if is_brb_request_blocking?(meth) and block_given?
21
+
22
+ block = (is_brb_request_blocking?(meth) or block_given?) ? Thread.current.to_s.to_sym : nil
17
23
  if block
18
24
  args << block
19
25
  args << Thread.current[:brb_nb_out]
20
26
  end
27
+
28
+ if block_given?
29
+ # Simulate a method with _block in order to make BrB send the answer
30
+ meth = "#{meth}_block".to_sym
31
+ end
21
32
 
22
- args.size > 0 ? brb_send([:s, meth, args]) : brb_send([:s, meth])
33
+ args.size > 0 ? brb_send([MessageRequestCode, meth, args]) : brb_send([MessageRequestCode, meth])
34
+
35
+ if block_given?
36
+ # Declare the callback
37
+ declare_callback(block, Thread.current[:brb_nb_out], &blck)
38
+
39
+ elsif block # Block until the request return
23
40
 
24
- # Block jusqu'au retour de la requete
25
- if block
26
41
  #TimeMonitor.instance.watch_thread!(@timeout_rcv_value || 45)
27
42
  begin
28
- r = recv(block, Thread.current[:brb_nb_out])
43
+ r = recv(block, Thread.current[:brb_nb_out], &blck)
29
44
  rescue Exception => e
30
45
  raise e
31
46
  ensure
@@ -52,9 +67,9 @@ module BrB
52
67
  thread = args.pop
53
68
  begin
54
69
  r = ((args.size > 0) ? @object.send(m, *args) : @object.send(m))
55
- brb_send([:r, r, thread, idrequest])
70
+ brb_send([ReturnCode, r, thread, idrequest])
56
71
  rescue Exception => e
57
- brb_send([:r, e, thread, idrequest])
72
+ brb_send([ReturnCode, e, thread, idrequest])
58
73
  tputs e.to_s
59
74
  tputs e.backtrace.join("\n")
60
75
  #raise e
@@ -33,6 +33,10 @@ module BrB
33
33
 
34
34
  @queue = Queue.new
35
35
  @buffer = ''
36
+
37
+ # Callbacks handling :
38
+ @callbacks = {}
39
+ @callbacks_mutex = Mutex.new
36
40
  end
37
41
 
38
42
  # EventMachine Callback, called after connection has been initialized
@@ -78,9 +82,9 @@ module BrB
78
82
  end
79
83
 
80
84
  # When no method is found on tunnel interface, create an brb out request
81
- def method_missing(meth, *args)
85
+ def method_missing(meth, *args, &block)
82
86
  return nil if !@active
83
- new_brb_out_request(meth, *args)
87
+ new_brb_out_request(meth, *args, &block)
84
88
  end
85
89
  end
86
90
  end
@@ -54,13 +54,17 @@ module BrB
54
54
  @buffer << data
55
55
 
56
56
  while obj = load_request
57
- if obj[0] == :r
57
+ if obj[0] == BrB::Request::ReturnCode
58
+
59
+ # Return if we have a callback handling the return :
60
+ next if treat_callback_return(obj[1], obj[2], obj[3])
61
+
62
+ # No callback, so blocking thread is waiting :
58
63
  @replock.lock
59
64
  @responses[obj[2]] ||= Queue.new
60
65
  @replock.unlock
61
66
  @responses[obj[2]] << [obj[1], obj[3]]
62
67
  else
63
-
64
68
  @queue << obj
65
69
 
66
70
  EM.defer do
@@ -79,6 +83,51 @@ module BrB
79
83
  end
80
84
  end
81
85
 
86
+ # Declare a new callback to call for a given request
87
+ # Thread safe code
88
+ def declare_callback(key, nb_out, &block)
89
+ @callbacks_mutex.lock
90
+
91
+ @callbacks[key] ||= {}
92
+ @callbacks[key][nb_out] = block
93
+
94
+ ensure
95
+ @callbacks_mutex.unlock
96
+ end
97
+
98
+ # Return associated callback if present
99
+ # And if present, delete the associate callback from the table
100
+ # Thread safe code
101
+ def get_callback(key, nb_out)
102
+ @callbacks_mutex.lock
103
+
104
+ if @callbacks[key] and b = @callbacks[key].delete(nb_out)
105
+ return b
106
+ end
107
+
108
+ ensure
109
+ @callbacks_mutex.unlock
110
+ end
111
+
112
+ # Call a callback if present, return true if exists
113
+ # Non blocking action, use EM.defer
114
+ def treat_callback_return(ret, key, nb_out)
115
+
116
+ if b = get_callback(key, nb_out)
117
+ EM.defer do
118
+ # With arity, handle multiple block arguments or no arguments
119
+ b.arity == 1 ? b.call(ret) : (b.arity == 0 ? b.call : b.call(*ret))
120
+ end
121
+
122
+ # A callback has been found and called, return true
123
+ return true
124
+ end
125
+
126
+ # No callback, do nothing
127
+ return nil
128
+ end
129
+
130
+ # Blocking method that wait on the @responses table an answer
82
131
  def recv(key, nb_out)
83
132
  begin
84
133
  @replock.lock
@@ -86,6 +135,12 @@ module BrB
86
135
  @replock.unlock
87
136
  while rep = r.pop
88
137
  if rep[1] == nb_out # On check ke c'est bien la réponse que l'on attend
138
+
139
+ # Call the callback
140
+ if block_given?
141
+ yield(rep[0])
142
+ end
143
+
89
144
  return rep[0]
90
145
  end
91
146
  if rep[1] > nb_out
@@ -1,6 +1,6 @@
1
- require File.dirname(__FILE__) + '/../spec_helper'
1
+ require 'spec_helper'
2
2
 
3
- describe :brb_service do
3
+ describe :brb_massive_usage do
4
4
  before(:all) do
5
5
  @brb = BrB::Service
6
6
  @brb.stop_service
@@ -13,6 +13,10 @@ describe :brb_service do
13
13
  end
14
14
  end
15
15
 
16
+ def random_client
17
+ @clients[rand(@clients.size)]
18
+ end
19
+
16
20
  # Start the service
17
21
  it "should the service be started" do
18
22
  @clients.each do |cl|
@@ -23,26 +27,42 @@ describe :brb_service do
23
27
  end
24
28
 
25
29
  it "should works with massive simple messaging" do
26
- nb_call_before = @brb_test.nb_call || 0
27
- nb_call_to_do = 500
30
+ nb_call_before = @brb_test.nb_call || 0
31
+ nb_call_to_do = 500
32
+
33
+ @clients.each do |cl|
34
+ nb_call_to_do.times do
35
+ cl.noarg
36
+ end
37
+ end
38
+
39
+ sleep 5
40
+ # Wait a little in order to be sure all the stack is processed
41
+ @brb_test.last_call.should == :noarg
42
+ @brb_test.nb_call.should == (nb_call_to_do * @clients.size) + nb_call_before
43
+ end
28
44
 
29
- @clients.each do |cl|
30
- nb_call_to_do.times do
31
- cl.noarg
45
+ it "should works with massive callbacks" do
46
+ block_called = 0
47
+ nb_callbacks = 1000
48
+ nb_callbacks.times do |i|
49
+ random_client.return_same_value(i) do |callback_return_value|
50
+ callback_return_value.should == i
51
+ block_called += 1
32
52
  end
33
53
  end
34
54
 
35
- sleep 6
36
- # Wait a little in order to be sure all the stack is processed
37
- @brb_test.last_call.should == :noarg
38
- @brb_test.nb_call.should == (nb_call_to_do * @clients.size) + nb_call_before
55
+ sleep 2
56
+ # Wait a little in order to be sure the method is called
57
+ @brb_test.last_call.should == :return_same_value
58
+ block_called.should == nb_callbacks
39
59
  end
40
-
41
- it "should works with massive simple messaging including blocking messaging" do
60
+
61
+ it "should works with massive simple messaging including blocking messaging and callbacks" do
42
62
  nb_call_before = @brb_test.nb_call || 0
43
63
  nb_call_to_do = 500
44
64
  nb_call_blocking_to_do = 50
45
-
65
+
46
66
  t = Thread.new do
47
67
  @clients.each do |cl|
48
68
  nb_call_blocking_to_do.times do
@@ -51,17 +71,28 @@ describe :brb_service do
51
71
  end
52
72
  end
53
73
  end
54
-
74
+
75
+ block_called = 0
76
+ nb_callbacks = 1000
77
+ nb_callbacks.times do |i|
78
+ random_client.return_same_value(i) do |callback_return_value|
79
+ callback_return_value.should == i
80
+ block_called += 1
81
+ end
82
+ end
83
+
84
+
55
85
  @clients.each do |cl|
56
86
  nb_call_to_do.times do
57
87
  cl.noarg
58
88
  end
59
89
  end
60
-
61
- sleep 6
90
+
91
+ sleep 5
92
+ block_called.should == nb_callbacks
62
93
  t.join
63
94
  # Wait a little in order to be sure all the stack is processed
64
- @brb_test.nb_call.should == (nb_call_to_do * @clients.size + nb_call_blocking_to_do * @clients.size) + nb_call_before
95
+ @brb_test.nb_call.should == nb_callbacks + (nb_call_to_do * @clients.size + nb_call_blocking_to_do * @clients.size) + nb_call_before
65
96
  end
66
97
 
67
98
  # Finally, stop the service
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/../spec_helper'
1
+ require 'spec_helper'
2
2
 
3
3
  describe :brb_service do
4
4
  before(:all) do
@@ -1,9 +1,9 @@
1
- require File.dirname(__FILE__) + '/../spec_helper'
1
+ require 'spec_helper'
2
2
 
3
3
  $last_unregistered = nil
4
4
  $last_registered = nil
5
5
 
6
- describe :brb_service do
6
+ describe :brb_tunnel do
7
7
  before(:all) do
8
8
  @brb = BrB::Service
9
9
  @brb.stop_service
@@ -90,6 +90,12 @@ describe :brb_service do
90
90
  @client.one_arg_with_return_block(h).should == h
91
91
  end
92
92
 
93
+ it "should correctly return multiple values" do
94
+ r1, r2 = @client.return_same_value_twice_block(:ret, :ret2)
95
+ r1.should == :ret
96
+ r2.should == :ret2
97
+ end
98
+
93
99
  it "should dump to symbol undumpable value by using the proxy" do
94
100
  @client.return_same_value_block(Thread.current).class.should == Symbol
95
101
  @client.return_same_value_block(Thread.current).should == Thread.current.to_s.to_sym
@@ -103,6 +109,52 @@ describe :brb_service do
103
109
  end
104
110
  e.should be_a NameError
105
111
  end
112
+
113
+ it "should use block as non blocking callback with return value" do
114
+ block_called = nil
115
+ @client.return_same_value(:arg) do |v|
116
+ v.should == :arg
117
+ block_called = true
118
+ end
119
+ sleep 0.2
120
+ # Wait a little in order to be sure the method is called
121
+ @brb_test.last_call.should == :return_same_value
122
+ block_called.should == true
123
+ end
124
+
125
+ it "should correctly handle multiple values return with callbacks" do
126
+ block_called = nil
127
+ @client.return_same_value_twice(:ret, :ret2) do |r1, r2|
128
+ r1.should == :ret
129
+ r2.should == :ret2
130
+ block_called = true
131
+ end
132
+ sleep 0.2
133
+ # Wait a little in order to be sure the method is called
134
+ @brb_test.last_call.should == :return_same_value_twice
135
+ block_called.should == true
136
+ end
137
+
138
+ it "should correctly handle no block args return with callbacks" do
139
+ block_called = nil
140
+ @client.return_same_value_twice(:ret, :ret2) do
141
+ block_called = true
142
+ end
143
+ sleep 0.2
144
+ # Wait a little in order to be sure the method is called
145
+ @brb_test.last_call.should == :return_same_value_twice
146
+ block_called.should == true
147
+ end
148
+
149
+ it "should raise an exception when calling a blocking method with a callback" do
150
+ e = nil
151
+ begin
152
+ @client.return_same_value_block(:arg) do |v|
153
+ end
154
+ rescue Exception => e
155
+ end
156
+ e.should_not be_nil
157
+ end
106
158
 
107
159
  # Finally, stop the service
108
160
  it "should stop the service after usage" do
@@ -110,7 +162,7 @@ describe :brb_service do
110
162
  @brb.uri.should be_nil
111
163
  end
112
164
 
113
- # Finally, stop the service
165
+ # Finally, stop the client tunnel
114
166
  it "should stop the tunnel after usage" do
115
167
  @client.stop_service
116
168
  @client.active?.should_not be_true
@@ -46,4 +46,9 @@ class BrBTest
46
46
  increment_nb_call(:return_same_value)
47
47
  return val
48
48
  end
49
+
50
+ def return_same_value_twice(val, val2)
51
+ increment_nb_call(:return_same_value_twice)
52
+ return val, val2
53
+ end
49
54
  end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 2
8
- - 2
9
- version: 0.2.2
7
+ - 3
8
+ - 0
9
+ version: 0.3.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Guillaume Luccisano
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-04-21 00:00:00 +02:00
17
+ date: 2010-05-21 00:00:00 +02:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency