procrastinate 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/HISTORY.txt ADDED
@@ -0,0 +1,37 @@
1
+ = 0.4.0 / ???
2
+
3
+ + Schedules blocks as well (Scheduler#schedule).
4
+
5
+ ! Fixes a small timing bug that would provoke a ChildDeath where the child
6
+   exited correctly in reality.
7
+
8
+ ! Fixes a data race on the result variable. This would have your process
9
+ hang forever waiting for a result that was already available.
10
+
11
+ = 0.3.1 / 13Sep2011
12
+
13
+ ! fix processor detection for Mac OS X Lion
14
+
15
+ = 0.3.0 / 27Dez2010
16
+ * create_proxy is now just 'proxy' to friends. This looks cleaner.
17
+
18
+ * require 'procrastinate/implicit' allows ignoring the scheduler in daily
19
+ usage. This is probably what will be more common.
20
+
21
+ * Auto-detection of the number of cores procrastinate runs on.
22
+ (SpawnStrategy::Default)
23
+
24
+ * Ruby 1.8 now uses absolutely poor busy-loop synchs. This is still better
25
+ than inserting puts in the code to unblock some magical mystical internal
26
+ state. I'll try to find a better solution.
27
+
28
+ = 0.2.0 / 22Dez2010
29
+ * Big rewrite, trying to make things more clear. Also: There might be more
30
+ features lurking in there.
31
+
32
+ * All proxy method calls now have a return value that works like a future:
33
+ Access to the real return value through future.value, which might block.
34
+
35
+ = 0.1.0 / 10Dez2010
36
+
37
+ * Initial version
data/README CHANGED
@@ -60,6 +60,10 @@ This library runs with at least Ruby 1.8.7 and Ruby 1.9.
60
60
  Ruby 1.9-p136 users must use this patch:
61
61
  https://gist.github.com/762807
62
62
 
63
+ As a general remark: Interaction with Ruby versions is significant. Please
64
+ use the latest version available to you, since fork & threading bugs are
65
+ likely to be fixed there.
66
+
63
67
  KNOWN BUGS
64
68
 
65
69
  Due to the way we handle signal traps, you cannot start more than one
data/Rakefile CHANGED
@@ -1,70 +1,39 @@
1
- require 'rspec'
2
- require 'rspec/core/rake_task'
3
- Rspec::Core::RakeTask.new
4
- task :default => :spec
5
-
6
1
  require "rubygems"
7
- require "rake/gempackagetask"
8
- require "rake/rdoctask"
9
-
10
- # This builds the actual gem. For details of what all these options
11
- # mean, and other ones you can add, check the documentation here:
12
- #
13
- # http://rubygems.org/read/chapter/20
14
- #
15
- spec = Gem::Specification.new do |s|
16
-
17
- # Change these as appropriate
18
- s.name = "procrastinate"
19
- s.version = "0.3.1"
20
- s.summary = "Framework to run tasks in separate processes."
21
- s.authors = ['Kaspar Schiess', 'Patrick Marchi']
22
- s.email = ['kaspar.schiess@absurd.li', 'mail@patrickmarchi.ch']
23
- s.homepage = "http://github.com/kschiess/procrastinate"
24
-
25
- s.has_rdoc = true
26
- s.extra_rdoc_files = %w(README)
27
- s.rdoc_options = %w(--main README)
2
+ require "rdoc/task"
3
+ require 'rspec/core/rake_task'
4
+ require 'rubygems/package_task'
28
5
 
29
- # Add any extra files to include in the gem
30
- s.files = %w(LICENSE Rakefile README) + Dir.glob("{spec,lib/**/*}")
31
- s.require_paths = ["lib"]
6
+ desc "Run all tests: Exhaustive."
7
+ RSpec::Core::RakeTask.new
32
8
 
33
- # If you want to depend on other gems, add them here, along with any
34
- # relevant versions
35
- s.add_dependency("state_machine", "~> 0.9.4")
9
+ task :default => :spec
36
10
 
37
- # If your tests use any gems, include them here
38
- s.add_development_dependency("rspec")
39
- s.add_development_dependency("flexmock")
11
+ task :stats do
12
+ %w(lib spec).each do |path|
13
+ printf "%10s:", path
14
+ system %Q(find #{path} -name "*.rb" | xargs wc -l | grep total)
15
+ end
40
16
  end
41
17
 
42
- desc "Regenerate the .gemspec file for github/bundler."
43
- task :gemspec do
44
- # Generate the gemspec file for github.
45
- file = File.dirname(__FILE__) + "/#{spec.name}.gemspec"
46
- File.open(file, "w") {|f| f << spec.to_ruby }
18
+ # Generate documentation
19
+ RDoc::Task.new do |rdoc|
20
+ rdoc.title = "procrastinate - a framework to run tasks in separate processes."
21
+ rdoc.options << '--line-numbers'
22
+ rdoc.options << '--fmt' << 'shtml' # explictly set shtml generator
23
+ rdoc.template = 'direct' # lighter template used on railsapi.com
24
+ rdoc.main = "README"
25
+ rdoc.rdoc_files.include("README", "lib/**/*.rb")
26
+ rdoc.rdoc_dir = "rdoc"
47
27
  end
48
28
 
49
- # This task actually builds the gem. We also regenerate a static
50
- # .gemspec file, which is useful if something (i.e. GitHub) will
51
- # be automatically building a gem for this project. If you're not
52
- # using GitHub, edit as appropriate.
53
- #
54
- # To publish your gem online, install the 'gemcutter' gem; Read more
55
- # about that here: http://gemcutter.org/pages/gem_docs
56
- Rake::GemPackageTask.new(spec) do |pkg|
57
- pkg.gem_spec = spec
58
- end
29
+ desc 'Clear out RDoc'
30
+ task :clean => [:clobber_rdoc, :clobber_package]
59
31
 
60
- # Generate documentation
61
- Rake::RDocTask.new do |rd|
62
- rd.main = "README"
63
- rd.rdoc_files.include("README", "lib/**/*.rb")
64
- rd.rdoc_dir = "rdoc"
65
- end
32
+ # This task actually builds the gem.
33
+ task :gem => :spec
34
+ spec = eval(File.read('procrastinate.gemspec'))
66
35
 
67
- desc 'Clear out RDoc and generated packages'
68
- task :clean => [:clobber_rdoc, :clobber_package] do
69
- rm "#{spec.name}.gemspec"
36
+ desc "Generate the gem package."
37
+ Gem::PackageTask.new(spec) do |pkg|
38
+ # pkg.need_tar = true
70
39
  end
@@ -0,0 +1,26 @@
1
+
2
+ $:.unshift File.dirname(__FILE__) + '/../lib'
3
+
4
+ require 'procrastinate'
5
+
6
+ class Worker < Struct.new(:runtime)
7
+ def do_work
8
+ puts "#{Process.pid}: starting..."
9
+ runtime.lock('lock') do
10
+ puts "#{Process.pid}: holds lock"
11
+ sleep 0.1
12
+ puts "#{Process.pid}: releases"
13
+ end
14
+ puts "#{Process.pid}: done."
15
+ end
16
+ end
17
+
18
+ Procrastinate::Lock.base = '/tmp' # This will have to be moved into scheduler
19
+ scheduler = Procrastinate::Scheduler.start
20
+ worker = scheduler.proxy(Worker.new(scheduler.runtime))
21
+
22
+ 10.times do
23
+ worker.do_work
24
+ end
25
+
26
+ scheduler.shutdown
@@ -0,0 +1,30 @@
1
+
2
+ # Computes Pascals triangle using procrastinate. This is mainly a stress test
3
+ # for result handling code and is NOT how I would parallelize this task!
4
+
5
+ $:.unshift File.dirname(__FILE__) + '/../lib'
6
+
7
+ require 'procrastinate'
8
+ require 'procrastinate/implicit'
9
+ include Procrastinate
10
+
11
+ V = Struct.new(:value) do
12
+ def ready?; true; end
13
+ end
14
+
15
+ current = [
16
+ V.new(1),
17
+ V.new(1)
18
+ ]
19
+
20
+ loop do
21
+ last = current
22
+
23
+ puts last.map { |e| sprintf("%3d", e.value) }.join(' ')
24
+
25
+ current = [V.new(1)] +
26
+ last.each_cons(2).map { |l,r|
27
+ Procrastinate.schedule { l.value + r.value }
28
+ } +
29
+ [V.new(1)]
30
+ end
@@ -0,0 +1,20 @@
1
+
2
+ $:.unshift File.dirname(__FILE__) + '/../lib'
3
+
4
+ require 'procrastinate/implicit'
5
+
6
+ class Worker
7
+ def do_work
8
+ puts "> Starting work in process #{Process.pid}"
9
+ sleep 2
10
+ puts "< Work completed in process #{Process.pid}"
11
+ end
12
+ end
13
+
14
+ worker = Procrastinate.proxy(Worker.new)
15
+
16
+ 10.times do
17
+ worker.do_work
18
+ end
19
+
20
+ Procrastinate.join
@@ -0,0 +1,22 @@
1
+
2
+ $:.unshift File.dirname(__FILE__) + '/../lib'
3
+
4
+ require 'procrastinate'
5
+ include Procrastinate
6
+
7
+ class Worker
8
+ def do_work
9
+ puts "> Starting work in process #{Process.pid}"
10
+ sleep 2
11
+ puts "< Work completed in process #{Process.pid}"
12
+ end
13
+ end
14
+
15
+ scheduler = Scheduler.start(SpawnStrategy::Throttled.new(5))
16
+ worker = scheduler.proxy(Worker.new)
17
+
18
+ 10.times do
19
+ worker.do_work
20
+ end
21
+
22
+ scheduler.shutdown
@@ -29,6 +29,17 @@ module Procrastinate
29
29
  end
30
30
  module_function :proxy
31
31
 
32
+ # call-seq:
33
+ # Procrastinate.schedule { some_work }
34
+ #
35
+ # Schedules a block to be executed in its own thread. Returns the future that
36
+ # will return the blocks return value.
37
+ #
38
+ def schedule(&block)
39
+ scheduler.schedule(&block)
40
+ end
41
+ module_function :schedule
42
+
32
43
  # call-seq:
33
44
  # Procrastinate.join
34
45
  #
@@ -89,9 +89,12 @@ class Procrastinate::ProcessManager
89
89
  trap('CHLD', 'DEFAULT')
90
90
  end
91
91
 
92
- # Called from the child management thread, will put that thread to sleep
92
+ # Called from the child management thread, will put that thread to sleep
93
93
  # until someone requests it to become active again. See #wakeup.
94
94
  #
95
+ # This method also depletes the child queue, reading end of processing
96
+ # messages from all childs and dispatching them to the children.
97
+ #
95
98
  def wait_for_event
96
99
  cp_read_end = control_pipe.first
97
100
 
@@ -100,11 +103,10 @@ class Procrastinate::ProcessManager
100
103
 
101
104
  read_child_messages if ready.include? @cmc_server
102
105
 
103
- # Kill children here, since we've just depleted the communication
104
- # endpoint. This avoids the situation where the child process
105
- # communicates but we remove it from our records before it can be told
106
- # about it.
107
- kill_children
106
+ # Send the tracking code for the child processes the final notifications
107
+ # and remove them from the children hash. At this point we know that
108
+ # no messages are waiting in the child queue.
109
+ finalize_children
108
110
 
109
111
  if ready.include? cp_read_end
110
112
  # Consume the data (not important)
@@ -112,18 +114,19 @@ class Procrastinate::ProcessManager
112
114
  return
113
115
  end
114
116
  end
115
-
116
- # rescue Errno::EAGAIN, Errno::EINTR
117
- # TODO Is this needed?
118
- # A signal has been received. Mostly, this is as if we had received
119
- # something in the control pipe.
120
117
  end
121
118
 
122
- def kill_children
123
- children.delete_if { |pid, child| child.dead? }
119
+ def finalize_children
120
+ children.
121
+ select { |pid, child| child.stopped? }.
122
+ each { |pid, child| child.finalize }
123
+
124
+ children.delete_if { |pid, child|
125
+ child.removable? }
124
126
  end
125
127
 
126
- # Once the @cmc_server endpoint is ready, loops and reads all child communication.
128
+ # Once the @cmc_server endpoint is ready, loops and reads all child
129
+ # communication.
127
130
  #
128
131
  def read_child_messages
129
132
  loop do
@@ -138,15 +141,18 @@ class Procrastinate::ProcessManager
138
141
  # that still needs decoding.
139
142
  #
140
143
  def decode_and_handle_message(msg)
141
- pid, obj = Marshal.load(msg)
144
+ pid, obj = begin
145
+ Marshal.load(msg)
146
+ rescue => b
147
+ # Messages that cannot be unmarshalled will be ignored.
148
+ warn "Can't unmarshal child communication: #{b}"
149
+ end
150
+
142
151
  if child=children[pid]
143
152
  child.incoming_message(obj)
144
153
  else
145
154
  warn "Communication from child #{pid} received, but child is gone."
146
155
  end
147
- rescue => b
148
- # Messages that cannot be unmarshalled will be ignored.
149
- warn "Can't unmarshal child communication."
150
156
  end
151
157
 
152
158
  # Calls completion handlers for all the childs that have now exited.
@@ -158,7 +164,11 @@ class Procrastinate::ProcessManager
158
164
 
159
165
  # Trigger the completion callback
160
166
  if child=children[child_pid]
161
- child.died
167
+ child.sigchld_received
168
+ # Maybe there are messages queued for this child. If nothing is queued
169
+ # up, the thread will hang in the select in #wait_for_event unless
170
+ # we wake it up.
171
+ wakeup
162
172
  end
163
173
  end
164
174
  rescue Errno::ECHILD
@@ -190,11 +200,16 @@ class Procrastinate::ProcessManager
190
200
 
191
201
  exit! # this seems to be needed to avoid rspecs cleanup tasks
192
202
  end
203
+
204
+ # This should never fire: New children are spawned only after we loose
205
+ # track of the old ones because they have been successfully processed.
206
+ fail "PID REUSE!" if children.has_key?(pid)
193
207
 
194
208
  # The spawning is done in the same thread as the reaping is done. This is
195
209
  # why no race condition to the following line exists. (or in other code,
196
210
  # for that matter.)
197
- children[pid] = ChildProcess.new(completion_handler, result).tap { |s| s.start }
211
+ children[pid] = ChildProcess.new(completion_handler, result).
212
+ tap { |s| s.start }
198
213
  end
199
214
 
200
215
  # Gets executed in child process to clean up file handles and pipes that the
@@ -2,21 +2,38 @@
2
2
  # a child exits and the object that will handle child-master communication
3
3
  # if desired.
4
4
  #
5
- class Procrastinate::ProcessManager::ChildProcess < Struct.new(:handler, :result, :state)
5
+ Procrastinate::ProcessManager::ChildProcess =
6
+ Struct.new(:handler, :result, :state) do
7
+
8
+ def initialize(handler, result)
9
+ super(handler, result, "new")
10
+ end
11
+
6
12
  state_machine :state, :initial => :new do
7
- event(:start) { transition :new => :running }
8
- event(:died) { transition :running => :dead }
13
+ event(:start) { transition :new => :running }
14
+ event(:sigchld_received) { transition :running => :stopped }
15
+ event(:finalize) { transition :stopped => :removable }
9
16
 
10
- after_transition :on => :died, :do => :call_completion_handlers
17
+ after_transition :on => :sigchld_received,
18
+ :do => :call_completion_handlers
19
+ after_transition :on => :finalize,
20
+ :do => :notify_result
11
21
  end
12
22
 
13
- # Calls the completion handler for the child. This is triggered by the
14
- # transition into the 'dead' state.
23
+ # Calls the completion handler for the child. At this stage, the process is
24
+ # not around anymore, but we still need to do some tracking.
15
25
  #
16
26
  def call_completion_handlers
17
- result.process_died if result
18
27
  handler.call if handler
19
28
  end
29
+
30
+ # Notifies the childs result value that the child has died and that no
31
+ # more messages will be read. If this is the only notification, the result
32
+ # will yield a ChildDeath exception.
33
+ #
34
+ def notify_result
35
+ result.process_died if result
36
+ end
20
37
 
21
38
  # Handles incoming messages from the tasks process.
22
39
  #
@@ -17,7 +17,8 @@ class Procrastinate::Proxy
17
17
 
18
18
  def method_missing(name, *args, &block)
19
19
  if respond_to? name
20
- task = Procrastinate::Task::MethodCall.new(@worker, name, args, block)
20
+ task = Procrastinate::Task::Callable.new(
21
+ lambda { @worker.send(name, *args, &block) })
21
22
  @scheduler.schedule(task)
22
23
 
23
24
  return task.result
@@ -57,12 +57,22 @@ class Procrastinate::Scheduler
57
57
  # Called by the proxy to schedule work. You can implement your own Task
58
58
  # classes; the relevant interface consists of only a #run method.
59
59
  #
60
- def schedule(task)
60
+ def schedule(task=nil, &block)
61
61
  fail "Shutting down..." if @state != :running
62
+
63
+ fail ArgumentError, "Either task or block must be given." \
64
+ if !task && !block
65
+
66
+ if block
67
+ task = Procrastinate::Task::Callable.new(block)
68
+ end
69
+
62
70
  task_queue << task
63
71
 
64
72
  # Create an occasion for spawning
65
73
  manager.wakeup
74
+
75
+ task.result
66
76
  end
67
77
 
68
78
  # Waits for the currently queued work to complete. This can be used at the
@@ -90,14 +100,14 @@ class Procrastinate::Scheduler
90
100
  #
91
101
  def shutdown(hard=false)
92
102
  join unless hard
93
-
103
+
94
104
  # Set the flag that will provoke shutdown
95
105
  @state = :real_shutdown
96
106
  # Wake the manager up, making it check the flag
97
107
  manager.wakeup
98
108
  # Wait for the manager to finish its work. This waits for child processes
99
109
  # and then reaps their result, avoiding zombies.
100
- @thread.join
110
+ @thread.join if @thread
101
111
  end
102
112
 
103
113
  private
@@ -2,5 +2,5 @@
2
2
  # A collection of tasks that can be performed with procrastinate.
3
3
  #
4
4
  module Procrastinate::Task
5
- autoload :MethodCall, 'procrastinate/task/method_call'
5
+ autoload :Callable, 'procrastinate/task/callable'
6
6
  end
@@ -0,0 +1,27 @@
1
+
2
+ require 'procrastinate/task/result'
3
+
4
+ module Procrastinate::Task
5
+ # Calls the block and returns the result of execution.
6
+ #
7
+ class Callable
8
+ attr_reader :block
9
+ attr_reader :result
10
+
11
+ def initialize(block)
12
+ @b = block
13
+ @result = Result.new
14
+ end
15
+
16
+ # Runs this task. Gets passed an endpoint that can be used to communicate
17
+ # values back to the master. Every time you write a value to that endpoint
18
+ # (using #send), the server will call #incoming_message on the task object
19
+ # in the master process. This allows return values and other communication
20
+ # from children to the master (and to the caller in this case).
21
+ #
22
+ def run(endpoint)
23
+ r = @b.call
24
+ endpoint.send r if endpoint
25
+ end
26
+ end
27
+ end
@@ -15,7 +15,8 @@ class Procrastinate::Task::Result
15
15
  # *control thread*
16
16
  #
17
17
  def incoming_message(obj)
18
- return if ready?
18
+ fail "Read child answer too late, already marked dead." if ready? && @exception
19
+ fail "Double child answer." if ready?
19
20
 
20
21
  @value = obj
21
22
  @value_ready.set
@@ -1,3 +1,17 @@
1
+
2
+ # A flag that will allow threads to wait until it is set. Once it is set, it
3
+ # cannot be unset.
4
+ #
5
+ # Guarantees that this class tries to make:
6
+ #
7
+ # 1) No thread will start waiting for the flag once it is already set. There
8
+ # no set-hole, meaning that no thread goes to sleep while we're waking up
9
+ # threads because the flag has been set.
10
+ #
11
+ # Candidate stdlib classes violate some of these guarantees, here are some
12
+ # candidates:
13
+ # * ConditionVariable - violates 1)
14
+ #
1
15
  class Procrastinate::Utils::OneTimeFlag
2
16
  def initialize
3
17
  @waiting = []
@@ -12,7 +26,9 @@ class Procrastinate::Utils::OneTimeFlag
12
26
 
13
27
  @waiting_m.synchronize do
14
28
  @waiting << Thread.current
15
- @waiting_m.sleep(0.001) until set?
29
+ until set?
30
+ @waiting_m.sleep(0.001)
31
+ end
16
32
  end
17
33
  end
18
34
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: procrastinate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,11 +10,11 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2011-09-13 00:00:00.000000000Z
13
+ date: 2011-12-16 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: state_machine
17
- requirement: &70138440330900 !ruby/object:Gem::Requirement
17
+ requirement: &70314600072220 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ~>
@@ -22,10 +22,10 @@ dependencies:
22
22
  version: 0.9.4
23
23
  type: :runtime
24
24
  prerelease: false
25
- version_requirements: *70138440330900
25
+ version_requirements: *70314600072220
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: rspec
28
- requirement: &70138440330260 !ruby/object:Gem::Requirement
28
+ requirement: &70314600070260 !ruby/object:Gem::Requirement
29
29
  none: false
30
30
  requirements:
31
31
  - - ! '>='
@@ -33,10 +33,10 @@ dependencies:
33
33
  version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
- version_requirements: *70138440330260
36
+ version_requirements: *70314600070260
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: flexmock
39
- requirement: &70138440329160 !ruby/object:Gem::Requirement
39
+ requirement: &70314600069360 !ruby/object:Gem::Requirement
40
40
  none: false
41
41
  requirements:
42
42
  - - ! '>='
@@ -44,7 +44,7 @@ dependencies:
44
44
  version: '0'
45
45
  type: :development
46
46
  prerelease: false
47
- version_requirements: *70138440329160
47
+ version_requirements: *70314600069360
48
48
  description:
49
49
  email:
50
50
  - kaspar.schiess@absurd.li
@@ -54,6 +54,7 @@ extensions: []
54
54
  extra_rdoc_files:
55
55
  - README
56
56
  files:
57
+ - HISTORY.txt
57
58
  - LICENSE
58
59
  - Rakefile
59
60
  - README
@@ -71,13 +72,17 @@ files:
71
72
  - lib/procrastinate/spawn_strategy/simple.rb
72
73
  - lib/procrastinate/spawn_strategy/throttled.rb
73
74
  - lib/procrastinate/spawn_strategy.rb
74
- - lib/procrastinate/task/method_call.rb
75
+ - lib/procrastinate/task/callable.rb
75
76
  - lib/procrastinate/task/result.rb
76
77
  - lib/procrastinate/task.rb
77
78
  - lib/procrastinate/utils/one_time_flag.rb
78
79
  - lib/procrastinate/utils/one_time_flag_ruby18_shim.rb
79
80
  - lib/procrastinate/utils.rb
80
81
  - lib/procrastinate.rb
82
+ - examples/locking.rb
83
+ - examples/pascal.rb
84
+ - examples/simple.rb
85
+ - examples/throttled.rb
81
86
  homepage: http://github.com/kschiess/procrastinate
82
87
  licenses: []
83
88
  post_install_message:
@@ -92,6 +97,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
92
97
  - - ! '>='
93
98
  - !ruby/object:Gem::Version
94
99
  version: '0'
100
+ segments:
101
+ - 0
102
+ hash: 4532369798177350543
95
103
  required_rubygems_version: !ruby/object:Gem::Requirement
96
104
  none: false
97
105
  requirements:
@@ -1,35 +0,0 @@
1
-
2
- require 'procrastinate/task/result'
3
-
4
- # Constructs an object of type +klass+ and calls a method on it.
5
- #
6
- class Procrastinate::Task::MethodCall
7
- include Procrastinate::Task
8
-
9
- attr_reader :i
10
- attr_reader :m
11
- attr_reader :a
12
- attr_reader :b
13
-
14
- def initialize(instance, method, arguments, block)
15
- @i = instance
16
- @m = method
17
- @a = arguments
18
- @b = block
19
- end
20
-
21
- # Runs this task. Gets passed an endpoint that can be used to communicate
22
- # values back to the master. Every time you write a value to that endpoint
23
- # (using #send), the server will call #incoming_message on the task object
24
- # in the master process. This allows return values and other communication
25
- # from children to the master (and to the caller in this case).
26
- #
27
- def run(endpoint)
28
- r = @i.send(@m, *@a, &@b)
29
- endpoint.send r if endpoint
30
- end
31
-
32
- def result
33
- @result ||= Result.new
34
- end
35
- end