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 +37 -0
- data/README +4 -0
- data/Rakefile +28 -59
- data/examples/locking.rb +26 -0
- data/examples/pascal.rb +30 -0
- data/examples/simple.rb +20 -0
- data/examples/throttled.rb +22 -0
- data/lib/procrastinate/implicit.rb +11 -0
- data/lib/procrastinate/process_manager.rb +35 -20
- data/lib/procrastinate/process_manager/child_process.rb +24 -7
- data/lib/procrastinate/proxy.rb +2 -1
- data/lib/procrastinate/scheduler.rb +13 -3
- data/lib/procrastinate/task.rb +1 -1
- data/lib/procrastinate/task/callable.rb +27 -0
- data/lib/procrastinate/task/result.rb +2 -1
- data/lib/procrastinate/utils/one_time_flag.rb +17 -1
- metadata +17 -9
- data/lib/procrastinate/task/method_call.rb +0 -35
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 "
|
8
|
-
require
|
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
|
-
|
30
|
-
|
31
|
-
s.require_paths = ["lib"]
|
6
|
+
desc "Run all tests: Exhaustive."
|
7
|
+
RSpec::Core::RakeTask.new
|
32
8
|
|
33
|
-
|
34
|
-
# relevant versions
|
35
|
-
s.add_dependency("state_machine", "~> 0.9.4")
|
9
|
+
task :default => :spec
|
36
10
|
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
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
|
-
|
50
|
-
|
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
|
-
#
|
61
|
-
|
62
|
-
|
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
|
68
|
-
|
69
|
-
|
36
|
+
desc "Generate the gem package."
|
37
|
+
Gem::PackageTask.new(spec) do |pkg|
|
38
|
+
# pkg.need_tar = true
|
70
39
|
end
|
data/examples/locking.rb
ADDED
@@ -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
|
data/examples/pascal.rb
ADDED
@@ -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
|
data/examples/simple.rb
ADDED
@@ -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
|
-
#
|
104
|
-
#
|
105
|
-
#
|
106
|
-
|
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
|
123
|
-
children.
|
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
|
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 =
|
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.
|
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).
|
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
|
-
|
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)
|
8
|
-
event(:
|
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 => :
|
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.
|
14
|
-
#
|
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
|
#
|
data/lib/procrastinate/proxy.rb
CHANGED
@@ -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::
|
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
|
data/lib/procrastinate/task.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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.
|
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-
|
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: &
|
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: *
|
25
|
+
version_requirements: *70314600072220
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: rspec
|
28
|
-
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: *
|
36
|
+
version_requirements: *70314600070260
|
37
37
|
- !ruby/object:Gem::Dependency
|
38
38
|
name: flexmock
|
39
|
-
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: *
|
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/
|
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
|