async_enum 0.0.5 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/README.md +14 -5
- data/async_enum.gemspec +1 -1
- data/demos/lock.rb +2 -1
- data/demos/pool_size.rb +1 -1
- data/demos/sleep.rb +1 -1
- data/lib/enumerator/async.rb +32 -77
- data/test/async_enum_test/enumerator_async_test.rb +0 -10
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
OTY4MmYzYjc3ZjgxMDRjNjE5MWM4OGZhYTUzYWQwZmVkMTg4NDc2Mw==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YTY3NjJjYmI1ZGE4NzUxMjNmODJjN2YwMDMxOTYwYzM0NzZjOTFiOA==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
OWQ1NzEzMDQ4OGRmYmU2NjE0N2IzZjlkYjcyYTVmNGM5YzQ5NzUzYTZiMjIy
|
10
|
+
OThmOGNmZDUxZjdlYjEwOTU2MDlhZWU0YWU1M2ZhNzNkODU2NGU4OTYwNWEw
|
11
|
+
YjA0OGFkMWYyNDYzZGM1NTkwOTNlMTc5ZDg0Zjc2YzJmNzEwZmE=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
MTVmZjc3M2JhYmEwNTMxMjQzNGY1NWM0Y2UwYWExMmM4NzNjMTQ4NWI4ZDU3
|
14
|
+
NTRkYTI0ZjdkYjhmYjcyYzg0NTE3YTQyOTVmZGZjYTlmMDNlMWNjMzEyMjE3
|
15
|
+
NjM3ZDMxMjhlY2RhNmNmNjMzNTcxOWM3ZmE0OWJhNjNhODY2NGM=
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# async\_enum
|
1
|
+
# async\_enum ![](https://travis-ci.org/aj0strow/async_enum.png)
|
2
2
|
|
3
3
|
Inspired by Enumerable#lazy coupled with Enumerator::Lazy, and the Async.js library, I thought it would be cool to have syntax like `urls.async.map{ |url| http_get(url) }`.
|
4
4
|
|
@@ -83,12 +83,15 @@ end
|
|
83
83
|
|
84
84
|
#### Preventing race conditions
|
85
85
|
|
86
|
-
When programming concurrently, nasty bugs can come up because some operations aren't atomic. For instance, incrementing a variable `x += 1` will not necessarily work as expected.
|
86
|
+
When programming concurrently, nasty bugs can come up because some operations aren't atomic. For instance, incrementing a variable `x += 1` will not necessarily work as expected. Use a lock when encountering these types of errors.
|
87
87
|
|
88
88
|
```ruby
|
89
|
+
require 'async_enum'
|
90
|
+
|
89
91
|
count = 0
|
92
|
+
mutex = Mutex.new
|
90
93
|
('a'..'z').async.each do
|
91
|
-
|
94
|
+
mutex.synchronize do
|
92
95
|
count += 1
|
93
96
|
end
|
94
97
|
end
|
@@ -96,7 +99,7 @@ count
|
|
96
99
|
# => 26
|
97
100
|
```
|
98
101
|
|
99
|
-
|
102
|
+
There used to be a lock DSL syntax, but it ruined using async_enum in scoped method calls.
|
100
103
|
|
101
104
|
## Notes
|
102
105
|
|
@@ -116,6 +119,12 @@ $ cd async_enum
|
|
116
119
|
$ ruby -I lib demos/sleep.rb
|
117
120
|
```
|
118
121
|
|
119
|
-
|
122
|
+
#### Contributions
|
123
|
+
|
124
|
+
* Yoshida Tetsuya ([@yoshida-eth0](https://github.com/yoshida-eth0)) fixed the fiber error issue, and made the gem work with hashes.
|
120
125
|
|
121
126
|
Please report errors and feel free to contribute and improve things!
|
127
|
+
|
128
|
+
|
129
|
+
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/aj0strow/async_enum/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
|
130
|
+
|
data/async_enum.gemspec
CHANGED
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = 'async_enum'
|
7
|
-
s.version = '0.0.
|
7
|
+
s.version = '0.0.6'
|
8
8
|
s.authors = %w(aj0strow)
|
9
9
|
s.email = 'alexander.ostrow@gmail.com'
|
10
10
|
s.description = 'iterate over enumerable objects concurrently'
|
data/demos/lock.rb
CHANGED
data/demos/pool_size.rb
CHANGED
data/demos/sleep.rb
CHANGED
data/lib/enumerator/async.rb
CHANGED
@@ -3,35 +3,17 @@ require 'thread'
|
|
3
3
|
class Enumerator
|
4
4
|
class Async < Enumerator
|
5
5
|
|
6
|
-
class Lockset
|
7
|
-
def initialize
|
8
|
-
@semaphores = Hash.new do |locks, key|
|
9
|
-
locks[key] = Mutex.new
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
def lock(key = :__default__, &thread_unsafe_block)
|
14
|
-
@semaphores[key].synchronize(&thread_unsafe_block)
|
15
|
-
end
|
16
|
-
|
17
|
-
alias_method :evaluate, :instance_exec
|
18
|
-
end
|
19
|
-
|
20
6
|
EOQ = Object.new
|
21
7
|
private_constant :EOQ
|
22
8
|
|
23
9
|
def initialize(enum, pool_size = nil)
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
end
|
29
|
-
pool_size = pool_size.to_i
|
10
|
+
pool_size = (pool_size || enum.count).to_i
|
11
|
+
unless pool_size >= 1
|
12
|
+
message = "Thread pool size is invalid! Expected a positive integer but got: #{pool_size}"
|
13
|
+
raise ArgumentError, message
|
30
14
|
end
|
31
15
|
|
32
|
-
@enum = enum
|
33
|
-
@pool_size = pool_size
|
34
|
-
@lockset = Lockset.new
|
16
|
+
@enum, @pool_size = enum, pool_size
|
35
17
|
end
|
36
18
|
|
37
19
|
def to_a
|
@@ -41,86 +23,59 @@ class Enumerator
|
|
41
23
|
def sync
|
42
24
|
@enum
|
43
25
|
end
|
44
|
-
|
45
26
|
alias_method :to_enum, :sync
|
46
27
|
|
47
28
|
def size
|
48
29
|
@enum.size
|
49
30
|
end
|
50
31
|
|
51
|
-
def each
|
52
|
-
raise_error(
|
32
|
+
def each
|
33
|
+
raise_error(:each) unless block_given?
|
53
34
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
item != EOQ ? evaluate(item, &work) : break
|
62
|
-
end
|
35
|
+
queue = SizedQueue.new @pool_size
|
36
|
+
|
37
|
+
threads = @pool_size.times.map do
|
38
|
+
Thread.new do
|
39
|
+
loop do
|
40
|
+
item = queue.pop
|
41
|
+
item != EOQ ? yield(item) : break
|
63
42
|
end
|
64
43
|
end
|
65
|
-
|
66
|
-
begin
|
67
|
-
loop { queue.push @enum.next }
|
68
|
-
rescue StopIteration
|
69
|
-
ensure
|
70
|
-
@pool_size.times { queue.push EOQ }
|
71
|
-
end
|
72
|
-
|
73
|
-
threads.each(&:join)
|
74
|
-
@enum.rewind
|
75
|
-
else
|
76
|
-
unlimited_threads(&work).each(&:join)
|
77
44
|
end
|
45
|
+
|
46
|
+
begin
|
47
|
+
loop { queue.push @enum.next }
|
48
|
+
rescue StopIteration
|
49
|
+
ensure
|
50
|
+
@pool_size.times { queue.push EOQ }
|
51
|
+
end
|
52
|
+
|
53
|
+
threads.each(&:join)
|
54
|
+
@enum.rewind
|
78
55
|
self
|
79
56
|
end
|
80
57
|
|
81
58
|
def with_index(start = 0, &work)
|
82
59
|
@enum = @enum.with_index(start)
|
83
|
-
|
84
|
-
each(&work)
|
85
|
-
else
|
86
|
-
self
|
87
|
-
end
|
60
|
+
block_given? ? each(&work) : self
|
88
61
|
end
|
89
62
|
|
90
|
-
def with_object(
|
91
|
-
@enum = @enum.with_object(
|
92
|
-
|
93
|
-
each(&work); obj
|
94
|
-
else
|
95
|
-
self
|
96
|
-
end
|
63
|
+
def with_object(object, &work)
|
64
|
+
@enum = @enum.with_object(object)
|
65
|
+
block_given? ? (each(&work) and object) : self
|
97
66
|
end
|
98
67
|
|
99
|
-
def map
|
100
|
-
raise_error(
|
68
|
+
def map
|
69
|
+
raise_error(:map) unless block_given?
|
101
70
|
|
102
|
-
|
103
|
-
outs = []
|
71
|
+
[].tap do |outs|
|
104
72
|
with_index do |item, index|
|
105
|
-
outs[index] =
|
73
|
+
outs[index] = yield(item)
|
106
74
|
end
|
107
|
-
outs
|
108
|
-
else
|
109
|
-
unlimited_threads(&work).map(&:value)
|
110
75
|
end
|
111
76
|
end
|
112
77
|
|
113
78
|
private
|
114
|
-
|
115
|
-
def evaluate(*args, &work)
|
116
|
-
@lockset.instance_exec(*args, &work)
|
117
|
-
end
|
118
|
-
|
119
|
-
def unlimited_threads(&work)
|
120
|
-
@enum.map do |*args|
|
121
|
-
Thread.new{ evaluate(*args, &work) }
|
122
|
-
end
|
123
|
-
end
|
124
79
|
|
125
80
|
def raise_error(method)
|
126
81
|
raise ArgumentError, "Tried to call async #{method} without a block"
|
@@ -87,15 +87,5 @@ class EnumeratorAsyncTest < Test
|
|
87
87
|
strs = @enum.map{ |i| sleep rand; i.to_s }
|
88
88
|
assert_equal '1 2 3 4 5', strs.join(' ')
|
89
89
|
end
|
90
|
-
|
91
|
-
test 'lock in block' do
|
92
|
-
count = 0
|
93
|
-
1000.times.async(5).each do
|
94
|
-
lock :count do
|
95
|
-
count += 1
|
96
|
-
end
|
97
|
-
end
|
98
|
-
assert_equal 1000, count
|
99
|
-
end
|
100
90
|
|
101
91
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: async_enum
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- aj0strow
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2014-01-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|