simple-future 1.0.0.pre1 → 1.0.0.pre2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5fa5bd91f1ad1ba0fd72c55c38a363fbdfd8e742
4
- data.tar.gz: 2217c9d0f1f2be5da29dc6a7f09c833a525a4d3e
3
+ metadata.gz: 7c4f875f9bf0dd10ea66a28115a7d98b1b830881
4
+ data.tar.gz: d16d4b9e415423ec1789481317091dee15702c62
5
5
  SHA512:
6
- metadata.gz: cbd761d667cb70b150537526e5e776141bfd6686e0b3ce90ae5d9decd977af525726f7895fd8abdf66f912a47efa0f7b4f8559a091d7bcbcceca9de1661b2ebc
7
- data.tar.gz: eae2629d1cd0bd8cc131e4ea3f7fcf02bf83ba3068753f455ccfd4d64c8e8e22a908fe635d43e752397f2a347d0257b28c516c42e2089381b574a57130b6d079
6
+ metadata.gz: fe659fc6bec48944e9bad47a0a27ced29d1b0747a2c4c8b35253513eb45fce98f330cbd74a94a41600e91170194d1473a47b270ce69490e8ed8def8972e23d8e
7
+ data.tar.gz: cb1dfa3152fae30148823f29d5d2718cf9597c2aeac632b359ee5d257730e5cbd8118b115c39f28eb0ad83f5e2233607124372bccb0b0a4fb16438074e63717c
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018 Chris Reuter
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,175 @@
1
+ # SimpleFuture: Simple, Process-based Concurrency
2
+
3
+ `SimpleFuture` is a Ruby module that gives you pretty good concurrency
4
+ without a lot of work. It doesn't use threads and so will avoid their
5
+ common pitfalls as well working on Rubies that don't support them.
6
+
7
+ It is a simple implementation of [Future][1] construct.
8
+
9
+ ## Resources
10
+
11
+ * [Home Page](https://github.com/suetanvil/simple-future/)
12
+ * [Issues](https://github.com/suetanvil/simple-future/issues)
13
+ * [Reference Docs](http://www.rubydoc.info/gems/simple-future/1.0.0/)
14
+
15
+
16
+ ## Basic idea
17
+
18
+ Suppose you need to do a bunch of long-running things concurrently
19
+ (e.g. transcode a bunch of videos) and you don't want to have to deal
20
+ with threads.
21
+
22
+ The easy thing to do is fork a subprocess for each item and then use
23
+ `Marshal` and `IO.pipe` to retrieve the result(s).
24
+
25
+ That's pretty much what `SimpleFuture` does for you. You pass it a
26
+ block and it runs it in a forked child and gives you back the result
27
+ when it's ready. And as a bonus, it will limit the number of children
28
+ it creates at one time, so you don't have to.
29
+
30
+ ## Those videos, for example
31
+
32
+ So let's say you're transcoding a bunch of (legally obtained) video
33
+ files:
34
+
35
+ for vid in Dir.glob('*.mp4')
36
+ run_transcode(vid, "../transcoded/") or
37
+ puts "Error transcoding #{vid}"
38
+ end
39
+
40
+ This is nice and simple, but it isn't taking advantage of the zillions
41
+ of cores you have on your fancy modern workstation. So you use
42
+ `SimpleFuture`:
43
+
44
+ for vid in Dir.glob('*.mp4')
45
+ SimpleFuture.new { run_transcode(vid, "../transcoded/") }
46
+ end
47
+
48
+ SimpleFuture.wait_for_all
49
+
50
+ And this will do what you want. In particular, it limits itself to
51
+ one process per CPU core so you don't thrash your system. (You can
52
+ change this limit via `SimpleFuture.max_tasks`.)
53
+
54
+ ## Wait for it
55
+
56
+ Notice that we end the script with `wait_for_all`. This is because
57
+ each `SimpleFuture` needs to have its `wait` method called at some
58
+ point. This is what `wait_for_all` does. (`SimpleFuture.new` will
59
+ also sometimes call an existing instance's `wait` but you can't depend
60
+ on that.)
61
+
62
+ Since `wait` blocks until the child process finishes, so will
63
+ `wait_for_all`. If you don't like that, you can also use `all_done?`;
64
+ this won't block so you can do other stuff while waiting:
65
+
66
+ while !SimpleFuture.all_done?
67
+ puts "Waiting for child processes to finish."
68
+ sleep 1
69
+ end
70
+
71
+ By the way, it's harmless to call `wait_for_all` if nothing is running
72
+ so it's often good practice to just call it before quitting.
73
+
74
+
75
+ ## I need answers
76
+
77
+ But let's say that `transcode()` also returns an important result such
78
+ as the compression ratio and you want to know the average. This means
79
+ that you need to get a result back from the child
80
+ process. Fortunately, `SimpleFuture` handles this.
81
+
82
+ First, we need to keep all of the `SimpleFuture` objects:
83
+
84
+ futures = []
85
+ for vid in Dir.glob('*.mp4')
86
+ futures.push(SimpleFuture.new { run_transcode(vid, "../transcoded/") })
87
+ end
88
+
89
+ Next, we use `map` to extract the results:
90
+
91
+ ratios = futures.map{|f| f.value}
92
+
93
+ And then compute the average:
94
+
95
+ sum = ratios.inject(0.0, :+)
96
+ puts "Average compression ratio: #{sum / ratios.size}" if
97
+ ratios.size > 0
98
+
99
+ Note that we're not calling `wait_for_all` here. This is because
100
+ `value` already calls `wait` and since it gets called on each
101
+ `SimpleFuture`, we know for sure that all child processes have been
102
+ cleaned up.
103
+
104
+ Also, it may be tempting to merge the loop and the map above but this
105
+ is a **bad idea**:
106
+
107
+ ratios = []
108
+ for vid in Dir.glob('*.mp4')
109
+ f = SimpleFuture.new { run_transcode(vid, "../transcoded/") }
110
+ ratios.push(f.wait) # BAD IDEA! DON'T DO THIS!
111
+ end
112
+
113
+ Because `wait` stops until the child process finishes, you're
114
+ effectively going back to single-threaded processing. You need to
115
+ create a collection of `SimpleFuture`s first and *then* `wait` on
116
+ them.
117
+
118
+ ## Oooopsie!
119
+
120
+ `SimpleFuture` tries to only throw exceptions in two cases: something
121
+ is wrong with your code or something has gone wrong beyond its control
122
+ (e.g. Ruby crashed). In either case, the right thing at this point is
123
+ usually to quit. (If that's not an option, the rubydocs will give you
124
+ the gory details on what throws what.)
125
+
126
+ Generally, you should:
127
+
128
+ 1. Never quit the child process; always exit the block with a (simple,
129
+ Marshal-compatible) value.
130
+ 2. Avoid throwing exceptions out of the child block unless it's to
131
+ avoid breaking rule 1.
132
+
133
+ Also, you can probably use low-level systems methods to trick
134
+ `SimpleFuture` into doing the wrong thing. Don't do that.
135
+
136
+
137
+ ## Installation
138
+
139
+ SimpleFuture is available as a gem:
140
+
141
+ $ [sudo] gem install simple-future
142
+
143
+ Source code is available at GitHub:
144
+
145
+ $ git clone https://github.com/suetanvil/simple-future.git
146
+ $ cd simple-future
147
+ $ rake
148
+
149
+ To build, you need to install `rake`, `rspec` and `yard`.
150
+
151
+ It should work on Ruby 2.2.0 or later, provided your Ruby/OS
152
+ combination supports `Process.fork()`. To confirm this, evaluate
153
+
154
+ Process.respond_to?(:fork) # true
155
+
156
+ If it returns true, you're good to go. If not, the gem will noisily
157
+ fail.
158
+
159
+
160
+ ## Bugs and Quirks
161
+
162
+ `SimpleFuture.new` will block if there are too many child processes.
163
+ In this case, it waits for the **oldest** process to finish, even if
164
+ other processes have finished in the meantime.
165
+
166
+ The unit tests use `sleep` to give child processes a relatively
167
+ predictable execution times and test based on that. This means
168
+ there's a chance that the tests can fail on a sufficiently slow or
169
+ overloaded system. (It's unlikely that this can happen in real life,
170
+ though.)
171
+
172
+
173
+
174
+
175
+ [1]:https://en.wikipedia.org/wiki/Futures_and_promises
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ require 'rake'
2
+ require 'rspec/core/rake_task'
3
+ require 'yard'
4
+
5
+ RSpec::Core::RakeTask.new(:test) do |t|
6
+ t.pattern = Dir.glob('spec/*_spec.rb')
7
+ end
8
+
9
+ YARD::Rake::YardocTask.new(:docs_via_yard) do |t|
10
+ t.files = ['lib/*.rb']
11
+ end
12
+
13
+ task :gem do
14
+ `gem build simple-future.gemspec`
15
+ end
16
+
17
+ task :clean do
18
+ gems = Dir.glob("simple-future-*.gem")
19
+ rm gems if gems.size > 0
20
+ rm_rf "doc"
21
+ end
22
+
23
+ task :clobber => [:clean] do
24
+ rm_rf ".yardoc"
25
+ end
26
+
27
+ task :default => [:docs_via_yard, :test, :gem]
@@ -0,0 +1,411 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>
7
+ Exception: SimpleFuture::ChildError
8
+
9
+ &mdash; Documentation by YARD 0.9.12
10
+
11
+ </title>
12
+
13
+ <link rel="stylesheet" href="../css/style.css" type="text/css" charset="utf-8" />
14
+
15
+ <link rel="stylesheet" href="../css/common.css" type="text/css" charset="utf-8" />
16
+
17
+ <script type="text/javascript" charset="utf-8">
18
+ pathId = "SimpleFuture::ChildError";
19
+ relpath = '../';
20
+ </script>
21
+
22
+
23
+ <script type="text/javascript" charset="utf-8" src="../js/jquery.js"></script>
24
+
25
+ <script type="text/javascript" charset="utf-8" src="../js/app.js"></script>
26
+
27
+
28
+ </head>
29
+ <body>
30
+ <div class="nav_wrap">
31
+ <iframe id="nav" src="../class_list.html?1"></iframe>
32
+ <div id="resizer"></div>
33
+ </div>
34
+
35
+ <div id="main" tabindex="-1">
36
+ <div id="header">
37
+ <div id="menu">
38
+
39
+ <a href="../_index.html">Index (C)</a> &raquo;
40
+ <span class='title'><span class='object_link'><a href="../SimpleFuture.html" title="SimpleFuture (class)">SimpleFuture</a></span></span>
41
+ &raquo;
42
+ <span class="title">ChildError</span>
43
+
44
+ </div>
45
+
46
+ <div id="search">
47
+
48
+ <a class="full_list_link" id="class_list_link"
49
+ href="../class_list.html">
50
+
51
+ <svg width="24" height="24">
52
+ <rect x="0" y="4" width="24" height="4" rx="1" ry="1"></rect>
53
+ <rect x="0" y="12" width="24" height="4" rx="1" ry="1"></rect>
54
+ <rect x="0" y="20" width="24" height="4" rx="1" ry="1"></rect>
55
+ </svg>
56
+ </a>
57
+
58
+ </div>
59
+ <div class="clear"></div>
60
+ </div>
61
+
62
+ <div id="content"><h1>Exception: SimpleFuture::ChildError
63
+
64
+
65
+
66
+ </h1>
67
+ <div class="box_info">
68
+
69
+ <dl>
70
+ <dt>Inherits:</dt>
71
+ <dd>
72
+ <span class="inheritName"><span class='object_link'><a href="Error.html" title="SimpleFuture::Error (class)">Error</a></span></span>
73
+
74
+ <ul class="fullTree">
75
+ <li>Object</li>
76
+
77
+ <li class="next">RuntimeError</li>
78
+
79
+ <li class="next"><span class='object_link'><a href="Error.html" title="SimpleFuture::Error (class)">Error</a></span></li>
80
+
81
+ <li class="next">SimpleFuture::ChildError</li>
82
+
83
+ </ul>
84
+ <a href="#" class="inheritanceTree">show all</a>
85
+
86
+ </dd>
87
+ </dl>
88
+
89
+
90
+
91
+
92
+
93
+
94
+
95
+
96
+
97
+
98
+
99
+ <dl>
100
+ <dt>Defined in:</dt>
101
+ <dd>lib/simple-future.rb</dd>
102
+ </dl>
103
+
104
+ </div>
105
+
106
+ <h2>Overview</h2><div class="docstring">
107
+ <div class="discussion">
108
+
109
+ <p>Exception class for the case where an uncaught exception is thrown in the
110
+ child process.</p>
111
+
112
+
113
+ </div>
114
+ </div>
115
+ <div class="tags">
116
+
117
+
118
+ </div>
119
+
120
+
121
+
122
+ <h2>Instance Attribute Summary <small><a href="#" class="summary_toggle">collapse</a></small></h2>
123
+ <ul class="summary">
124
+
125
+ <li class="public ">
126
+ <span class="summary_signature">
127
+
128
+ <a href="#cause-instance_method" title="#cause (instance method)">#<strong>cause</strong> &#x21d2; Object </a>
129
+
130
+
131
+
132
+ </span>
133
+
134
+
135
+
136
+
137
+ <span class="note title readonly">readonly</span>
138
+
139
+
140
+
141
+
142
+
143
+
144
+
145
+
146
+
147
+ <span class="summary_desc"><div class='inline'>
148
+ <p>If the child process threw an exception, this is it.</p>
149
+ </div></span>
150
+
151
+ </li>
152
+
153
+
154
+ </ul>
155
+
156
+
157
+
158
+
159
+
160
+ <h2>
161
+ Instance Method Summary
162
+ <small><a href="#" class="summary_toggle">collapse</a></small>
163
+ </h2>
164
+
165
+ <ul class="summary">
166
+
167
+ <li class="public ">
168
+ <span class="summary_signature">
169
+
170
+ <a href="#initialize-instance_method" title="#initialize (instance method)">#<strong>initialize</strong>(msg, cause = nil) &#x21d2; ChildError </a>
171
+
172
+
173
+
174
+ </span>
175
+
176
+
177
+ <span class="note title constructor">constructor</span>
178
+
179
+
180
+
181
+
182
+
183
+
184
+
185
+
186
+ <span class="summary_desc"><div class='inline'>
187
+ <p>A new instance of ChildError.</p>
188
+ </div></span>
189
+
190
+ </li>
191
+
192
+
193
+ <li class="public ">
194
+ <span class="summary_signature">
195
+
196
+ <a href="#to_s-instance_method" title="#to_s (instance method)">#<strong>to_s</strong> &#x21d2; Object </a>
197
+
198
+
199
+
200
+ </span>
201
+
202
+
203
+
204
+
205
+
206
+
207
+
208
+
209
+
210
+ <span class="summary_desc"><div class='inline'></div></span>
211
+
212
+ </li>
213
+
214
+
215
+ </ul>
216
+
217
+
218
+
219
+
220
+
221
+
222
+
223
+
224
+
225
+ <div id="constructor_details" class="method_details_list">
226
+ <h2>Constructor Details</h2>
227
+
228
+ <div class="method_details first">
229
+ <h3 class="signature first" id="initialize-instance_method">
230
+
231
+ #<strong>initialize</strong>(msg, cause = nil) &#x21d2; <tt><span class='object_link'><a href="" title="SimpleFuture::ChildError (class)">ChildError</a></span></tt>
232
+
233
+
234
+
235
+
236
+
237
+ </h3><div class="docstring">
238
+ <div class="discussion">
239
+
240
+ <p>Returns a new instance of ChildError</p>
241
+
242
+
243
+ </div>
244
+ </div>
245
+ <div class="tags">
246
+ <p class="tag_title">Parameters:</p>
247
+ <ul class="param">
248
+
249
+ <li>
250
+
251
+ <span class='name'>msg</span>
252
+
253
+
254
+ <span class='type'>(<tt>String</tt>)</span>
255
+
256
+
257
+
258
+ &mdash;
259
+ <div class='inline'>
260
+ <p>The exception text.</p>
261
+ </div>
262
+
263
+ </li>
264
+
265
+ <li>
266
+
267
+ <span class='name'>cause</span>
268
+
269
+
270
+ <span class='type'>(<tt>Exception</tt>)</span>
271
+
272
+
273
+ <em class="default">(defaults to: <tt>nil</tt>)</em>
274
+
275
+
276
+ &mdash;
277
+ <div class='inline'>
278
+ <p>If valid, the exception raised in the child</p>
279
+ </div>
280
+
281
+ </li>
282
+
283
+ </ul>
284
+
285
+
286
+ </div><table class="source_code">
287
+ <tr>
288
+ <td>
289
+ <pre class="lines">
290
+
291
+
292
+ 52
293
+ 53
294
+ 54
295
+ 55</pre>
296
+ </td>
297
+ <td>
298
+ <pre class="code"><span class="info file"># File 'lib/simple-future.rb', line 52</span>
299
+
300
+ <span class='kw'>def</span> <span class='id identifier rubyid_initialize'>initialize</span><span class='lparen'>(</span><span class='id identifier rubyid_msg'>msg</span><span class='comma'>,</span> <span class='id identifier rubyid_cause'>cause</span> <span class='op'>=</span> <span class='kw'>nil</span><span class='rparen'>)</span>
301
+ <span class='kw'>super</span><span class='lparen'>(</span><span class='id identifier rubyid_msg'>msg</span><span class='rparen'>)</span>
302
+ <span class='ivar'>@cause</span> <span class='op'>=</span> <span class='id identifier rubyid_cause'>cause</span>
303
+ <span class='kw'>end</span></pre>
304
+ </td>
305
+ </tr>
306
+ </table>
307
+ </div>
308
+
309
+ </div>
310
+
311
+ <div id="instance_attr_details" class="attr_details">
312
+ <h2>Instance Attribute Details</h2>
313
+
314
+
315
+ <span id=""></span>
316
+ <div class="method_details first">
317
+ <h3 class="signature first" id="cause-instance_method">
318
+
319
+ #<strong>cause</strong> &#x21d2; <tt>Object</tt> <span class="extras">(readonly)</span>
320
+
321
+
322
+
323
+
324
+
325
+ </h3><div class="docstring">
326
+ <div class="discussion">
327
+
328
+ <p>If the child process threw an exception, this is it. Otherwise, it&#39;s
329
+ nil.</p>
330
+
331
+
332
+ </div>
333
+ </div>
334
+ <div class="tags">
335
+
336
+
337
+ </div><table class="source_code">
338
+ <tr>
339
+ <td>
340
+ <pre class="lines">
341
+
342
+
343
+ 48
344
+ 49
345
+ 50</pre>
346
+ </td>
347
+ <td>
348
+ <pre class="code"><span class="info file"># File 'lib/simple-future.rb', line 48</span>
349
+
350
+ <span class='kw'>def</span> <span class='id identifier rubyid_cause'>cause</span>
351
+ <span class='ivar'>@cause</span>
352
+ <span class='kw'>end</span></pre>
353
+ </td>
354
+ </tr>
355
+ </table>
356
+ </div>
357
+
358
+ </div>
359
+
360
+
361
+ <div id="instance_method_details" class="method_details_list">
362
+ <h2>Instance Method Details</h2>
363
+
364
+
365
+ <div class="method_details first">
366
+ <h3 class="signature first" id="to_s-instance_method">
367
+
368
+ #<strong>to_s</strong> &#x21d2; <tt>Object</tt>
369
+
370
+
371
+
372
+
373
+
374
+ </h3><table class="source_code">
375
+ <tr>
376
+ <td>
377
+ <pre class="lines">
378
+
379
+
380
+ 57
381
+ 58
382
+ 59
383
+ 60
384
+ 61</pre>
385
+ </td>
386
+ <td>
387
+ <pre class="code"><span class="info file"># File 'lib/simple-future.rb', line 57</span>
388
+
389
+ <span class='kw'>def</span> <span class='id identifier rubyid_to_s'>to_s</span>
390
+ <span class='id identifier rubyid_result'>result</span> <span class='op'>=</span> <span class='kw'>super</span><span class='period'>.</span><span class='id identifier rubyid_to_s'>to_s</span>
391
+ <span class='id identifier rubyid_result'>result</span> <span class='op'>+=</span> <span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'> (cause: </span><span class='embexpr_beg'>#{</span><span class='id identifier rubyid_cause'>cause</span><span class='period'>.</span><span class='id identifier rubyid_class'>class</span><span class='embexpr_end'>}</span><span class='tstring_content'> &#39;</span><span class='embexpr_beg'>#{</span><span class='ivar'>@cause</span><span class='period'>.</span><span class='id identifier rubyid_to_s'>to_s</span><span class='embexpr_end'>}</span><span class='tstring_content'>&#39;)</span><span class='tstring_end'>&quot;</span></span> <span class='kw'>if</span> <span class='ivar'>@cause</span>
392
+ <span class='kw'>return</span> <span class='id identifier rubyid_result'>result</span>
393
+ <span class='kw'>end</span></pre>
394
+ </td>
395
+ </tr>
396
+ </table>
397
+ </div>
398
+
399
+ </div>
400
+
401
+ </div>
402
+
403
+ <div id="footer">
404
+ Generated on Wed Jan 17 18:36:16 2018 by
405
+ <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
406
+ 0.9.12 (ruby-2.4.3).
407
+ </div>
408
+
409
+ </div>
410
+ </body>
411
+ </html>