totally_lazy 0.0.20 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.idea/.name +1 -0
  3. data/.idea/.rakeTasks +7 -0
  4. data/.idea/compiler.xml +22 -0
  5. data/.idea/encodings.xml +6 -0
  6. data/.idea/misc.xml +19 -0
  7. data/.idea/modules.xml +8 -0
  8. data/.idea/vcs.xml +6 -0
  9. data/.travis.yml +2 -5
  10. data/Gemfile +6 -8
  11. data/Guardfile +2 -17
  12. data/LICENSE +202 -0
  13. data/Rakefile +18 -33
  14. data/VERSION +1 -1
  15. data/contributors.txt +1 -0
  16. data/lib/comparators.rb +9 -0
  17. data/lib/enumerators.rb +74 -0
  18. data/lib/functions.rb +66 -0
  19. data/lib/numbers.rb +38 -0
  20. data/lib/option.rb +38 -268
  21. data/lib/pair.rb +13 -51
  22. data/lib/predicates.rb +5 -0
  23. data/lib/sequence.rb +171 -526
  24. data/lib/strings.rb +13 -0
  25. data/lib/totally_lazy.rb +14 -165
  26. data/readme.md +2 -0
  27. data/spec/option_spec.rb +6 -182
  28. data/spec/sequence_spec.rb +202 -132
  29. data/spec/spec_helper.rb +0 -13
  30. data/totally_lazy.iml +74 -0
  31. metadata +58 -71
  32. data/.document +0 -5
  33. data/.rspec +0 -1
  34. data/LICENSE.txt +0 -20
  35. data/README.md +0 -173
  36. data/lib/any.rb +0 -13
  37. data/lib/functor.rb +0 -92
  38. data/lib/generators.rb +0 -161
  39. data/lib/parallel/parallel.rb +0 -442
  40. data/lib/parallel/processor_count.rb +0 -85
  41. data/lib/predicates/compare.rb +0 -25
  42. data/lib/predicates/conversions.rb +0 -22
  43. data/lib/predicates/numbers.rb +0 -21
  44. data/lib/predicates/predicates.rb +0 -141
  45. data/lib/predicates/where.rb +0 -34
  46. data/lib/predicates/where_processor.rb +0 -13
  47. data/lib/type_check.rb +0 -19
  48. data/lib/utils.rb +0 -9
  49. data/spec/functor_spec.rb +0 -35
  50. data/spec/generators_spec.rb +0 -37
  51. data/spec/pair_spec.rb +0 -44
  52. data/spec/predicate_spec.rb +0 -77
  53. data/spec/serialization_spec.rb +0 -56
  54. data/spec/type_check_spec.rb +0 -20
  55. data/spec/util_spec.rb +0 -10
  56. data/totally_lazy.gemspec +0 -101
@@ -1,442 +0,0 @@
1
- require 'rbconfig'
2
- require_relative 'processor_count'
3
-
4
- module Parallel
5
- extend Parallel::ProcessorCount
6
-
7
- class DeadWorker < StandardError
8
- end
9
-
10
- class Break < StandardError
11
- end
12
-
13
- class Kill < StandardError
14
- end
15
-
16
- Stop = Object.new
17
-
18
- INTERRUPT_SIGNAL = :SIGINT
19
-
20
- class ExceptionWrapper
21
- attr_reader :exception
22
- def initialize(exception)
23
- dumpable = Marshal.dump(exception) rescue nil
24
- unless dumpable
25
- exception = RuntimeError.new("Undumpable Exception -- #{exception.inspect}")
26
- end
27
-
28
- @exception = exception
29
- end
30
- end
31
-
32
- class Worker
33
- attr_reader :pid, :read, :write
34
- attr_accessor :thread
35
- def initialize(read, write, pid)
36
- @read, @write, @pid = read, write, pid
37
- end
38
-
39
- def close_pipes
40
- read.close
41
- write.close
42
- end
43
-
44
- def wait
45
- Process.wait(pid)
46
- rescue Interrupt
47
- # process died
48
- end
49
-
50
- def work(data)
51
- begin
52
- Marshal.dump(data, write)
53
- rescue Errno::EPIPE
54
- raise DeadWorker
55
- end
56
-
57
- begin
58
- Marshal.load(read)
59
- rescue EOFError
60
- raise DeadWorker
61
- end
62
- end
63
- end
64
-
65
- class ItemWrapper
66
- def initialize(array, mutex)
67
- @lambda = (array.respond_to?(:call) && array) || queue_wrapper(array)
68
- @items = array.to_a unless @lambda # turn Range and other Enumerable-s into an Array
69
- @mutex = mutex
70
- @index = -1
71
- end
72
-
73
- def producer?
74
- @lambda
75
- end
76
-
77
- def each_with_index(&block)
78
- if producer?
79
- loop do
80
- item, index = self.next
81
- break unless index
82
- yield(item, index)
83
- end
84
- else
85
- @items.each_with_index(&block)
86
- end
87
- end
88
-
89
- def next
90
- if producer?
91
- # - index and item stay in sync
92
- # - do not call lambda after it has returned Stop
93
- item, index = @mutex.synchronize do
94
- return if @stopped
95
- item = @lambda.call
96
- @stopped = (item == Parallel::Stop)
97
- return if @stopped
98
- [item, @index += 1]
99
- end
100
- else
101
- index = @mutex.synchronize { @index += 1 }
102
- return if index >= size
103
- item = @items[index]
104
- end
105
- [item, index]
106
- end
107
-
108
- def size
109
- @items.size
110
- end
111
-
112
- def pack(item, index)
113
- producer? ? [item, index] : index
114
- end
115
-
116
- def unpack(data)
117
- producer? ? data : [@items[data], data]
118
- end
119
-
120
- def queue_wrapper(array)
121
- array.respond_to?(:num_waiting) && array.respond_to?(:pop) && lambda { array.pop(false) }
122
- end
123
- end
124
-
125
- class << self
126
- def in_threads(options={:count => 2})
127
- count, options = extract_count_from_options(options)
128
-
129
- out = []
130
- threads = []
131
-
132
- count.times do |i|
133
- threads[i] = Thread.new do
134
- out[i] = yield(i)
135
- end
136
- end
137
-
138
- kill_on_ctrl_c(threads) { wait_for_threads(threads) }
139
-
140
- out
141
- end
142
-
143
- def in_processes(options = {}, &block)
144
- count, options = extract_count_from_options(options)
145
- count ||= processor_count
146
- map(0...count, options.merge(:in_processes => count), &block)
147
- end
148
-
149
- def each(array, options={}, &block)
150
- map(array, options.merge(:preserve_results => false), &block)
151
- array
152
- end
153
-
154
- def each_with_index(array, options={}, &block)
155
- each(array, options.merge(:with_index => true), &block)
156
- end
157
-
158
- def map(array, options = {}, &block)
159
- options[:mutex] = Mutex.new
160
-
161
- if RUBY_PLATFORM =~ /java/ and not options[:in_processes]
162
- method = :in_threads
163
- size = options[method] || processor_count
164
- elsif options[:in_threads]
165
- method = :in_threads
166
- size = options[method]
167
- else
168
- method = :in_processes
169
- if Process.respond_to?(:fork)
170
- size = options[method] || processor_count
171
- else
172
- $stderr.puts "Warning: Process.fork is not supported by this Ruby"
173
- size = 0
174
- end
175
- end
176
-
177
- items = ItemWrapper.new(array, options[:mutex])
178
-
179
- size = [items.producer? ? size : items.size, size].min
180
-
181
- options[:return_results] = (options[:preserve_results] != false || !!options[:finish])
182
- add_progress_bar!(items, options)
183
-
184
- if size == 0
185
- work_direct(items, options, &block)
186
- elsif method == :in_threads
187
- work_in_threads(items, options.merge(:count => size), &block)
188
- else
189
- work_in_processes(items, options.merge(:count => size), &block)
190
- end
191
- end
192
-
193
- def map_with_index(array, options={}, &block)
194
- map(array, options.merge(:with_index => true), &block)
195
- end
196
-
197
- private
198
-
199
- def add_progress_bar!(items, options)
200
- if title = options[:progress]
201
- raise "Progressbar and producers don't mix" if items.producer?
202
- require 'ruby-progressbar'
203
- progress = ProgressBar.create(
204
- :title => title,
205
- :total => items.size,
206
- :format => '%t |%E | %B | %a'
207
- )
208
- old_finish = options[:finish]
209
- options[:finish] = lambda do |item, i, result|
210
- old_finish.call(item, i, result) if old_finish
211
- progress.increment
212
- end
213
- end
214
- end
215
-
216
-
217
- def work_direct(items, options)
218
- results = []
219
- items.each_with_index do |e,i|
220
- results << (options[:with_index] ? yield(e,i) : yield(e))
221
- end
222
- results
223
- end
224
-
225
- def work_in_threads(items, options, &block)
226
- results = []
227
- exception = nil
228
-
229
- in_threads(options[:count]) do
230
- # as long as there are more items, work on one of them
231
- loop do
232
- break if exception
233
- item, index = items.next
234
- break unless index
235
-
236
- begin
237
- results[index] = with_instrumentation item, index, options do
238
- call_with_index(item, index, options, &block)
239
- end
240
- rescue StandardError => e
241
- exception = e
242
- break
243
- end
244
- end
245
- end
246
-
247
- handle_exception(exception, results)
248
- end
249
-
250
- def work_in_processes(items, options, &blk)
251
- workers = create_workers(items, options, &blk)
252
- results = []
253
- exception = nil
254
-
255
- kill_on_ctrl_c(workers.map(&:pid)) do
256
- in_threads(options[:count]) do |i|
257
- worker = workers[i]
258
- worker.thread = Thread.current
259
-
260
- begin
261
- loop do
262
- break if exception
263
- item, index = items.next
264
- break unless index
265
-
266
- output = with_instrumentation item, index, options do
267
- worker.work(items.pack(item, index))
268
- end
269
-
270
- if ExceptionWrapper === output
271
- exception = output.exception
272
- if Parallel::Kill === exception
273
- (workers - [worker]).each do |w|
274
- kill_that_thing!(w.thread)
275
- kill_that_thing!(w.pid)
276
- end
277
- end
278
- else
279
- results[index] = output
280
- end
281
- end
282
- ensure
283
- worker.close_pipes
284
- worker.wait # if it goes zombie, rather wait here to be able to debug
285
- end
286
- end
287
- end
288
-
289
- handle_exception(exception, results)
290
- end
291
-
292
- def create_workers(items, options, &block)
293
- workers = []
294
- Array.new(options[:count]).each do
295
- workers << worker(items, options.merge(:started_workers => workers), &block)
296
- end
297
- workers
298
- end
299
-
300
- def worker(items, options, &block)
301
- # use less memory on REE
302
- GC.copy_on_write_friendly = true if GC.respond_to?(:copy_on_write_friendly=)
303
-
304
- child_read, parent_write = IO.pipe
305
- parent_read, child_write = IO.pipe
306
-
307
- pid = Process.fork do
308
- begin
309
- options.delete(:started_workers).each(&:close_pipes)
310
-
311
- parent_write.close
312
- parent_read.close
313
-
314
- process_incoming_jobs(child_read, child_write, items, options, &block)
315
- ensure
316
- child_read.close
317
- child_write.close
318
- end
319
- end
320
-
321
- child_read.close
322
- child_write.close
323
-
324
- Worker.new(parent_read, parent_write, pid)
325
- end
326
-
327
- def process_incoming_jobs(read, write, items, options, &block)
328
- while !read.eof?
329
- data = Marshal.load(read)
330
- item, index = items.unpack(data)
331
- result = begin
332
- call_with_index(item, index, options, &block)
333
- rescue StandardError => e
334
- ExceptionWrapper.new(e)
335
- end
336
- Marshal.dump(result, write)
337
- end
338
- end
339
-
340
- def wait_for_threads(threads)
341
- interrupted = threads.compact.map do |t|
342
- begin
343
- t.join
344
- nil
345
- rescue Interrupt => e
346
- e # thread died, do not stop other threads
347
- end
348
- end.compact
349
- raise interrupted.first if interrupted.first
350
- end
351
-
352
- def handle_exception(exception, results)
353
- return nil if [Parallel::Break, Parallel::Kill].include? exception.class
354
- raise exception if exception
355
- results
356
- end
357
-
358
- # options is either a Integer or a Hash with :count
359
- def extract_count_from_options(options)
360
- if options.is_a?(Hash)
361
- count = options[:count]
362
- else
363
- count = options
364
- options = {}
365
- end
366
- [count, options]
367
- end
368
-
369
- # kill all these pids or threads if user presses Ctrl+c
370
- def kill_on_ctrl_c(things)
371
- @to_be_killed ||= []
372
- old_interrupt = nil
373
-
374
- if @to_be_killed.empty?
375
- old_interrupt = trap_interrupt do
376
- $stderr.puts 'Parallel execution interrupted, exiting ...'
377
- @to_be_killed.flatten.compact.each { |thing| kill_that_thing!(thing) }
378
- end
379
- end
380
-
381
- @to_be_killed << things
382
-
383
- yield
384
- ensure
385
- @to_be_killed.pop # free threads for GC and do not kill pids that could be used for new processes
386
- restore_interrupt(old_interrupt) if @to_be_killed.empty?
387
- end
388
-
389
- def trap_interrupt
390
- old = Signal.trap INTERRUPT_SIGNAL, 'IGNORE'
391
-
392
- Signal.trap INTERRUPT_SIGNAL do
393
- yield
394
- if old == "DEFAULT"
395
- raise Interrupt
396
- else
397
- old.call
398
- end
399
- end
400
-
401
- old
402
- end
403
-
404
- def restore_interrupt(old)
405
- Signal.trap INTERRUPT_SIGNAL, old
406
- end
407
-
408
- def kill_that_thing!(thing)
409
- if thing.is_a?(Thread)
410
- thing.kill
411
- else
412
- begin
413
- Process.kill(:KILL, thing)
414
- rescue Errno::ESRCH
415
- # some linux systems already automatically killed the children at this point
416
- # so we just ignore them not being there
417
- end
418
- end
419
- end
420
-
421
- def call_with_index(item, index, options, &block)
422
- args = [item]
423
- args << index if options[:with_index]
424
- if options[:return_results]
425
- block.call(*args)
426
- else
427
- block.call(*args)
428
- nil # avoid GC overhead of passing large results around
429
- end
430
- end
431
-
432
- def with_instrumentation(item, index, options)
433
- on_start = options[:start]
434
- on_finish = options[:finish]
435
- options[:mutex].synchronize { on_start.call(item, index) } if on_start
436
- result = yield
437
- result unless options[:preserve_results] == false
438
- ensure
439
- options[:mutex].synchronize { on_finish.call(item, index, result) } if on_finish
440
- end
441
- end
442
- end
@@ -1,85 +0,0 @@
1
- module Parallel
2
- module ProcessorCount
3
- # Number of processors seen by the OS and used for process scheduling.
4
- #
5
- # * AIX: /usr/sbin/pmcycles (AIX 5+), /usr/sbin/lsdev
6
- # * BSD: /sbin/sysctl
7
- # * Cygwin: /proc/cpuinfo
8
- # * Darwin: /usr/bin/hwprefs, /usr/sbin/sysctl
9
- # * HP-UX: /usr/sbin/ioscan
10
- # * IRIX: /usr/sbin/sysconf
11
- # * Linux: /proc/cpuinfo
12
- # * Minix 3+: /proc/cpuinfo
13
- # * Solaris: /usr/sbin/psrinfo
14
- # * Tru64 UNIX: /usr/sbin/psrinfo
15
- # * UnixWare: /usr/sbin/psrinfo
16
- #
17
- def processor_count
18
- @processor_count ||= begin
19
- os_name = RbConfig::CONFIG["target_os"]
20
- if os_name =~ /mingw|mswin/
21
- require 'win32ole'
22
- result = WIN32OLE.connect("winmgmts://").ExecQuery(
23
- "select NumberOfLogicalProcessors from Win32_Processor")
24
- result.to_enum.collect(&:NumberOfLogicalProcessors).reduce(:+)
25
- elsif File.readable?("/proc/cpuinfo")
26
- IO.read("/proc/cpuinfo").scan(/^processor/).size
27
- elsif File.executable?("/usr/bin/hwprefs")
28
- IO.popen("/usr/bin/hwprefs thread_count").read.to_i
29
- elsif File.executable?("/usr/sbin/psrinfo")
30
- IO.popen("/usr/sbin/psrinfo").read.scan(/^.*on-*line/).size
31
- elsif File.executable?("/usr/sbin/ioscan")
32
- IO.popen("/usr/sbin/ioscan -kC processor") do |out|
33
- out.read.scan(/^.*processor/).size
34
- end
35
- elsif File.executable?("/usr/sbin/pmcycles")
36
- IO.popen("/usr/sbin/pmcycles -m").read.count("\n")
37
- elsif File.executable?("/usr/sbin/lsdev")
38
- IO.popen("/usr/sbin/lsdev -Cc processor -S 1").read.count("\n")
39
- elsif File.executable?("/usr/sbin/sysconf") and os_name =~ /irix/i
40
- IO.popen("/usr/sbin/sysconf NPROC_ONLN").read.to_i
41
- elsif File.executable?("/usr/sbin/sysctl")
42
- IO.popen("/usr/sbin/sysctl -n hw.ncpu").read.to_i
43
- elsif File.executable?("/sbin/sysctl")
44
- IO.popen("/sbin/sysctl -n hw.ncpu").read.to_i
45
- else
46
- $stderr.puts "Unknown platform: " + RbConfig::CONFIG["target_os"]
47
- $stderr.puts "Assuming 1 processor."
48
- 1
49
- end
50
- end
51
- end
52
-
53
- # Number of physical processor cores on the current system.
54
- #
55
- def physical_processor_count
56
- @physical_processor_count ||= begin
57
- ppc = case RbConfig::CONFIG["target_os"]
58
- when /darwin1/
59
- IO.popen("/usr/sbin/sysctl -n hw.physicalcpu").read.to_i
60
- when /linux/
61
- cores = {} # unique physical ID / core ID combinations
62
- phy = 0
63
- IO.read("/proc/cpuinfo").scan(/^physical id.*|^core id.*/) do |ln|
64
- if ln.start_with?("physical")
65
- phy = ln[/\d+/]
66
- elsif ln.start_with?("core")
67
- cid = phy + ":" + ln[/\d+/]
68
- cores[cid] = true if not cores[cid]
69
- end
70
- end
71
- cores.count
72
- when /mswin|mingw/
73
- require 'win32ole'
74
- result_set = WIN32OLE.connect("winmgmts://").ExecQuery(
75
- "select NumberOfCores from Win32_Processor")
76
- result_set.to_enum.collect(&:NumberOfCores).reduce(:+)
77
- else
78
- processor_count
79
- end
80
- # fall back to logical count if physical info is invalid
81
- ppc > 0 ? ppc : processor_count
82
- end
83
- end
84
- end
85
- end
@@ -1,25 +0,0 @@
1
- module Predicates
2
-
3
- module Compare
4
-
5
- def equals(value)
6
- value_predicate(:equals,:==,value)
7
- end
8
-
9
- alias equal_to equals
10
-
11
- def greater_than(value)
12
- value_predicate(:greater_than,:>,value)
13
- end
14
-
15
- def less_than(value)
16
- value_predicate(:less_than,:<,value)
17
- end
18
-
19
- def matches(value)
20
- regex_predicate(:matches,value)
21
- end
22
-
23
- end
24
-
25
- end
@@ -1,22 +0,0 @@
1
- module Predicates
2
-
3
- module Conversions
4
-
5
- def as_string
6
- simple_transform(:as_string, -> (v) { v.to_s } )
7
- end
8
-
9
- def as_int
10
- simple_transform(:as_int, -> (v) { Type.responds(v, :to_i); v.to_i } )
11
- end
12
-
13
- def as_float
14
- simple_transform(:as_float, -> (v) { Type.responds(v, :to_f); v.to_f } )
15
- end
16
-
17
- def as_array
18
- simple_transform(:as_array, -> (v) { [v] } )
19
- end
20
-
21
- end
22
- end
@@ -1,21 +0,0 @@
1
- module Predicates
2
-
3
- module Numbers
4
-
5
- def even
6
- self_predicate(:even,:even?)
7
- end
8
-
9
- def odd
10
- self_predicate(:odd,:odd?)
11
- end
12
- #
13
- # def between(lower, higher)
14
- # -> (v, meth=:self, invert=false) do
15
- # invert ? inverted(v, meth, :between?) : regular(v, meth, :between?)
16
- # end
17
- # end
18
-
19
- end
20
-
21
- end