concurrent-ruby 0.1.1.pre.2 → 0.1.1.pre.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -11,16 +11,17 @@ require 'concurrent/future'
11
11
  require 'concurrent/goroutine'
12
12
  require 'concurrent/promise'
13
13
  require 'concurrent/obligation'
14
- require 'concurrent/reactor'
15
14
  require 'concurrent/smart_mutex'
16
15
  require 'concurrent/utilities'
17
16
 
18
- require 'concurrent/drb_async_demux'
19
- require 'concurrent/tcp_sync_demux'
17
+ require 'concurrent/reactor'
18
+ require 'concurrent/reactor/drb_async_demux'
19
+ require 'concurrent/reactor/tcp_sync_demux'
20
20
 
21
21
  require 'concurrent/thread_pool'
22
22
  require 'concurrent/cached_thread_pool'
23
23
  require 'concurrent/fixed_thread_pool'
24
+ require 'concurrent/null_thread_pool'
24
25
 
25
26
  require 'concurrent/global_thread_pool'
26
27
 
@@ -1,6 +1,7 @@
1
1
  require 'observer'
2
2
  require 'thread'
3
3
 
4
+ require 'concurrent/global_thread_pool'
4
5
  require 'concurrent/utilities'
5
6
 
6
7
  module Concurrent
@@ -13,6 +14,7 @@ module Concurrent
13
14
  # A good example of an agent is a shared incrementing counter, such as the score in a video game.
14
15
  class Agent
15
16
  include Observable
17
+ include UsesGlobalThreadPool
16
18
 
17
19
  TIMEOUT = 5
18
20
 
@@ -26,7 +28,7 @@ module Concurrent
26
28
  @validator = nil
27
29
  @queue = Queue.new
28
30
 
29
- @thread = Thread.new{ work }
31
+ Agent.thread_pool.post{ work }
30
32
  end
31
33
 
32
34
  def value(timeout = 0) return @value; end
@@ -7,6 +7,7 @@ module Concurrent
7
7
  IllegalMethodCallError = Class.new(StandardError)
8
8
 
9
9
  class Defer
10
+ include UsesGlobalThreadPool
10
11
 
11
12
  def initialize(opts = {}, &block)
12
13
  operation = opts[:op] || opts[:operation]
@@ -22,7 +23,7 @@ module Concurrent
22
23
  if operation.nil?
23
24
  @running = false
24
25
  else
25
- self.go(thread_pool)
26
+ self.go
26
27
  end
27
28
  end
28
29
 
@@ -44,12 +45,11 @@ module Concurrent
44
45
  alias_method :catch, :rescue
45
46
  alias_method :on_error, :rescue
46
47
 
47
- def go(thread_pool = nil)
48
+ def go
48
49
  return nil if @running
49
50
  atomic {
50
- thread_pool ||= $GLOBAL_THREAD_POOL
51
51
  @running = true
52
- thread_pool.post { Thread.pass; fulfill }
52
+ Defer.thread_pool.post { Thread.pass; fulfill }
53
53
  }
54
54
  return nil
55
55
  end
@@ -10,6 +10,8 @@ module Concurrent
10
10
  attr_reader :execution_interval
11
11
  attr_reader :timeout_interval
12
12
 
13
+ protected
14
+
13
15
  def initialize(name, execution_interval, timeout_interval, thread)
14
16
  @name = name
15
17
  @execution_interval = execution_interval
@@ -18,6 +20,8 @@ module Concurrent
18
20
  @thread[:stop] = false
19
21
  end
20
22
 
23
+ public
24
+
21
25
  def status
22
26
  return @thread.status unless @thread.nil?
23
27
  end
@@ -12,11 +12,11 @@ module Kernel
12
12
  end
13
13
  module_function :agent
14
14
 
15
- def post(agent, &block)
16
- if agent.respond_to?(:post)
17
- return agent.post(&block)
15
+ def post(object, &block)
16
+ if object.respond_to?(:post)
17
+ return object.post(&block)
18
18
  else
19
- return nil
19
+ raise ArgumentError.new('object does not support #post')
20
20
  end
21
21
  end
22
22
  module_function :post
@@ -44,53 +44,53 @@ module Kernel
44
44
 
45
45
  ## obligation
46
46
 
47
- def deref(obligation, timeout = nil)
48
- if obligation.respond_to?(:deref)
49
- return obligation.deref(timeout)
50
- elsif obligation.respond_to?(:value)
51
- return obligation.deref(timeout)
47
+ def deref(object, timeout = nil)
48
+ if object.respond_to?(:deref)
49
+ return object.deref(timeout)
50
+ elsif object.respond_to?(:value)
51
+ return object.value(timeout)
52
52
  else
53
- return nil
53
+ raise ArgumentError.new('object does not support #deref')
54
54
  end
55
55
  end
56
56
  module_function :deref
57
57
 
58
- def pending?(obligation)
59
- if obligation.respond_to?(:pending?)
60
- return obligation.pending?
58
+ def pending?(object)
59
+ if object.respond_to?(:pending?)
60
+ return object.pending?
61
61
  else
62
- return false
62
+ raise ArgumentError.new('object does not support #pending?')
63
63
  end
64
64
  end
65
65
  module_function :pending?
66
66
 
67
- def fulfilled?(obligation)
68
- if obligation.respond_to?(:fulfilled?)
69
- return obligation.fulfilled?
70
- elsif obligation.respond_to?(:realized?)
71
- return obligation.realized?
67
+ def fulfilled?(object)
68
+ if object.respond_to?(:fulfilled?)
69
+ return object.fulfilled?
70
+ elsif object.respond_to?(:realized?)
71
+ return object.realized?
72
72
  else
73
- return false
73
+ raise ArgumentError.new('object does not support #fulfilled?')
74
74
  end
75
75
  end
76
76
  module_function :fulfilled?
77
77
 
78
- def realized?(obligation)
79
- if obligation.respond_to?(:realized?)
80
- return obligation.realized?
81
- elsif obligation.respond_to?(:fulfilled?)
82
- return obligation.fulfilled?
78
+ def realized?(object)
79
+ if object.respond_to?(:realized?)
80
+ return object.realized?
81
+ elsif object.respond_to?(:fulfilled?)
82
+ return object.fulfilled?
83
83
  else
84
- return false
84
+ raise ArgumentError.new('object does not support #realized?')
85
85
  end
86
86
  end
87
87
  module_function :realized?
88
88
 
89
- def rejected?(obligation)
90
- if obligation.respond_to?(:rejected?)
91
- return obligation.rejected?
89
+ def rejected?(object)
90
+ if object.respond_to?(:rejected?)
91
+ return object.rejected?
92
92
  else
93
- return false
93
+ raise ArgumentError.new('object does not support #rejected?')
94
94
  end
95
95
  end
96
96
  module_function :rejected?
@@ -8,22 +8,17 @@ module Concurrent
8
8
 
9
9
  class Future
10
10
  include Obligation
11
+ include UsesGlobalThreadPool
12
+
11
13
  behavior(:future)
12
14
 
13
15
  def initialize(*args, &block)
14
- if args.first.behaves_as?(:global_thread_pool)
15
- thread_pool = args.first
16
- args = args.slice(1, args.length)
17
- else
18
- thread_pool = $GLOBAL_THREAD_POOL
19
- end
20
-
21
16
  unless block_given?
22
17
  @state = :fulfilled
23
18
  else
24
19
  @value = nil
25
20
  @state = :pending
26
- thread_pool.post do
21
+ Future.thread_pool.post(*args) do
27
22
  Thread.pass
28
23
  work(*args, &block)
29
24
  end
@@ -1,3 +1,16 @@
1
1
  require 'concurrent/cached_thread_pool'
2
2
 
3
3
  $GLOBAL_THREAD_POOL ||= Concurrent::CachedThreadPool.new
4
+
5
+ module Concurrent
6
+
7
+ module UsesGlobalThreadPool
8
+
9
+ def self.included(base)
10
+ class << base
11
+ attr_accessor :thread_pool
12
+ end
13
+ base.thread_pool = $GLOBAL_THREAD_POOL
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,22 @@
1
+ require 'concurrent/global_thread_pool'
2
+
3
+ module Concurrent
4
+
5
+ class NullThreadPool
6
+ behavior(:global_thread_pool)
7
+
8
+ def self.post(*args, &block)
9
+ Thread.new(*args, &block)
10
+ return true
11
+ end
12
+
13
+ def post(*args, &block)
14
+ return NullThreadPool.post(*args, &block)
15
+ end
16
+
17
+ def <<(block)
18
+ NullThreadPool.post(&block)
19
+ return self
20
+ end
21
+ end
22
+ end
@@ -1,5 +1,6 @@
1
1
  require 'thread'
2
2
 
3
+ require 'concurrent/global_thread_pool'
3
4
  require 'concurrent/obligation'
4
5
  require 'concurrent/utilities'
5
6
 
@@ -7,6 +8,8 @@ module Concurrent
7
8
 
8
9
  class Promise
9
10
  include Obligation
11
+ include UsesGlobalThreadPool
12
+
10
13
  behavior(:future)
11
14
  behavior(:promise)
12
15
 
@@ -90,15 +93,6 @@ module Concurrent
90
93
  # @private
91
94
  Rescuer = Struct.new(:clazz, :block)
92
95
 
93
- # @private
94
- def root # :nodoc:
95
- return atomic {
96
- current = self
97
- current = current.parent until current.root?
98
- current
99
- }
100
- end
101
-
102
96
  # @private
103
97
  def root? # :nodoc:
104
98
  @parent.nil?
@@ -148,7 +142,7 @@ module Concurrent
148
142
 
149
143
  # @private
150
144
  def realize(*args) # :nodoc:
151
- Thread.new(@chain, @mutex, args) do |chain, mutex, args|
145
+ Promise.thread_pool.post(@chain, @mutex, args) do |chain, mutex, args|
152
146
  result = args.length == 1 ? args.first : args
153
147
  index = 0
154
148
  loop do
@@ -0,0 +1,74 @@
1
+ require 'drb/drb'
2
+ require 'drb/acl'
3
+ require 'functional'
4
+ require 'concurrent/reactor'
5
+
6
+ module Concurrent
7
+ class Reactor
8
+
9
+ class DRbAsyncDemux
10
+
11
+ behavior(:async_event_demux)
12
+
13
+ DEFAULT_URI = 'druby://localhost:12345'
14
+ DEFAULT_ACL = %[allow all]
15
+
16
+ def initialize(opts = {})
17
+ @uri = opts[:uri] || DEFAULT_URI
18
+ @acl = ACL.new(opts[:acl] || DEFAULT_ACL)
19
+ end
20
+
21
+ def set_reactor(reactor)
22
+ raise ArgumentError.new('invalid reactor') unless reactor.behaves_as?(:demux_reactor)
23
+ @reactor = reactor
24
+ end
25
+
26
+ def start
27
+ DRb.install_acl(@acl)
28
+ @service = DRb.start_service(@uri, Demultiplexer.new(@reactor))
29
+ end
30
+
31
+ def stop
32
+ @service = DRb.stop_service
33
+ end
34
+
35
+ def stopped?
36
+ return @service.nil?
37
+ end
38
+
39
+ private
40
+
41
+ class Demultiplexer
42
+
43
+ def initialize(reactor)
44
+ @reactor = reactor
45
+ end
46
+
47
+ Concurrent::Reactor::RESERVED_EVENTS.each do |event|
48
+ define_method(event){|*args| false }
49
+ end
50
+
51
+ def method_missing(method, *args, &block)
52
+ (class << self; self; end).class_eval do
53
+ define_method(method) do |*args|
54
+ result = @reactor.handle(method, *args)
55
+ case result.first
56
+ when :ok
57
+ return result.last
58
+ when :ex
59
+ raise result.last
60
+ when :noop
61
+ raise NoMethodError.new("undefined method '#{method}' for #{self}")
62
+ else
63
+ raise DRb::DRbUnknownError.new("unexpected error when calling method '#{method}'")
64
+ end
65
+ end
66
+ end
67
+ self.send(method, *args)
68
+ end
69
+ end
70
+ end
71
+
72
+ DRbAsyncDemultiplexer = DRbAsyncDemux
73
+ end
74
+ end
@@ -0,0 +1,98 @@
1
+ require 'socket'
2
+ require 'drb/acl'
3
+ require 'functional'
4
+ require 'concurrent/reactor'
5
+
6
+ module Concurrent
7
+ class Reactor
8
+
9
+ class TcpSyncDemux
10
+
11
+ behavior(:sync_event_demux)
12
+
13
+ DEFAULT_HOST = '127.0.0.1'
14
+ DEFAULT_PORT = 12345
15
+ DEFAULT_ACL = %[allow all]
16
+
17
+ def initialize(opts = {})
18
+ @host = opts[:host] || DEFAULT_HOST
19
+ @port = opts[:port] || DEFAULT_PORT
20
+ @acl = ACL.new(opts[:acl] || DEFAULT_ACL)
21
+ end
22
+
23
+ def start
24
+ @server = TCPServer.new(@host, @port)
25
+ end
26
+
27
+ def stop
28
+ atomic {
29
+ @socket.close unless @socket.nil?
30
+ @server.close unless @server.nil?
31
+ @server = @socket = nil
32
+ }
33
+ end
34
+
35
+ def stopped?
36
+ return @server.nil?
37
+ end
38
+
39
+ def accept
40
+ @socket = @server.accept if @socket.nil?
41
+ return nil unless @acl.allow_socket?(@socket)
42
+ event, args = get_message(@socket)
43
+ return nil if event.nil?
44
+ return Reactor::EventContext.new(event, args)
45
+ end
46
+
47
+ def respond(result, message)
48
+ return nil if @socket.nil?
49
+ @socket.puts(format_message(result, message))
50
+ end
51
+
52
+ def close
53
+ @socket.close
54
+ @socket = nil
55
+ end
56
+
57
+ def self.format_message(event, *args)
58
+ args = args.reduce('') do |memo, arg|
59
+ memo << "#{arg}\r\n"
60
+ end
61
+ return ":#{event}\r\n#{args}\r\n"
62
+ end
63
+ def format_message(*args) self.class.format_message(*args); end
64
+
65
+ def self.parse_message(message)
66
+ return atomic {
67
+ event = message.first.match /^:?(\w+)/
68
+ event = event[1].to_s.downcase.to_sym unless event.nil?
69
+
70
+ args = message.slice(1, message.length) || []
71
+
72
+ [event, args]
73
+ }
74
+ end
75
+ def parse_message(*args) self.class.parse_message(*args); end
76
+
77
+ def self.get_message(socket)
78
+ message = []
79
+ while line = socket.gets
80
+ if line.nil? || (line = line.strip).empty?
81
+ break
82
+ else
83
+ message << line
84
+ end
85
+ end
86
+
87
+ if message.empty?
88
+ return nil
89
+ else
90
+ return parse_message(message)
91
+ end
92
+ end
93
+ def get_message(*args) self.class.get_message(*args); end
94
+ end
95
+
96
+ TcpSyncDemultiplexer = TcpSyncDemux
97
+ end
98
+ end