async_enum 0.0.5 → 0.0.6

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,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- ZDFhMWYwZjQ0ZWE1Njc1NzJiZGU3ZTEwMDAyMzIzN2M4ZmZhZDM4ZA==
4
+ OTY4MmYzYjc3ZjgxMDRjNjE5MWM4OGZhYTUzYWQwZmVkMTg4NDc2Mw==
5
5
  data.tar.gz: !binary |-
6
- ZDAzNTI1ZjczYzU4NjQ0YTNhNTVmMTkzMzdjNzEyMzk4Y2Q4ZjUwMw==
6
+ YTY3NjJjYmI1ZGE4NzUxMjNmODJjN2YwMDMxOTYwYzM0NzZjOTFiOA==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- YmQ0MjFmMTU3NGQyMWEzZTQ4MWY5NjM2NGZhMWVhN2Q0OTU4ZGY3ZTBhNDI3
10
- NWUyNmQ1YzZlMWVkNjA0YmIwNWE5OTZhNTdhNzg1ZjUxZTdkZDkxZjUzMzM4
11
- MDA5NmFhZGU3NzE5MTQyMGMzOTU3N2IzNTUxYzczMzgwMzcwNTE=
9
+ OWQ1NzEzMDQ4OGRmYmU2NjE0N2IzZjlkYjcyYTVmNGM5YzQ5NzUzYTZiMjIy
10
+ OThmOGNmZDUxZjdlYjEwOTU2MDlhZWU0YWU1M2ZhNzNkODU2NGU4OTYwNWEw
11
+ YjA0OGFkMWYyNDYzZGM1NTkwOTNlMTc5ZDg0Zjc2YzJmNzEwZmE=
12
12
  data.tar.gz: !binary |-
13
- ZjdiNzk0OWI1MGI0YzE1MzE5NjExYzI4OTNjNGYzNmM1YzQ0ZmViODk0NTA0
14
- OWMyZWUyNjQwOTY5MzdlYzEzZjc2OThjNWVhZGExYWE1MzI2MmQ4NTM4M2Zj
15
- YmZlMmFhMjljNTk1ODVhYWIxZTY2MTIwOWUyNDQ3MmQ1ZjRkZDc=
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. To provide easy locking, there's a DSL-style `lock` method you can use in the block passed to the async enum.
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
- lock :count do
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
- The name of the lock doesn't matter, but using the variable name helps make the code understandable. You should try to use 1 lock per thread-unsafe variable.
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
- **Disclaimer**: I am not an expert at multithreading. Quite the opposite in fact.
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
+
@@ -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.5'
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'
@@ -1,8 +1,9 @@
1
1
  require 'async_enum'
2
2
 
3
3
  count = 0
4
+ mutex = Mutex.new
4
5
  ('a'..'z').async.each do
5
- lock :count do
6
+ mutex.synchronize do
6
7
  count += 1
7
8
  end
8
9
  end
@@ -2,7 +2,7 @@ require 'benchmark'
2
2
  require 'async_enum'
3
3
 
4
4
  default = Benchmark.measure do
5
- 500.times.async.each{ true }
5
+ 500.times.async.each{ true }
6
6
  end
7
7
 
8
8
  limited = Benchmark.measure do
@@ -2,7 +2,7 @@ require 'benchmark'
2
2
  require 'async_enum'
3
3
 
4
4
  sync = Benchmark.measure do
5
- 5.times{ sleep 0.1 }
5
+ 5.times{ sleep 0.1 }
6
6
  end
7
7
 
8
8
  async = Benchmark.measure do
@@ -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
- if pool_size
25
- unless pool_size >= 1
26
- message = "Thread pool size is invalid! Expected a positive integer but got: #{pool_size}"
27
- raise ArgumentError, message
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(&work)
52
- raise_error('each') unless block_given?
32
+ def each
33
+ raise_error(:each) unless block_given?
53
34
 
54
- if @pool_size
55
- queue = SizedQueue.new @pool_size
56
-
57
- threads = @pool_size.times.map do
58
- Thread.new do
59
- loop do
60
- item = queue.pop
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
- if block_given?
84
- each(&work)
85
- else
86
- self
87
- end
60
+ block_given? ? each(&work) : self
88
61
  end
89
62
 
90
- def with_object(obj, &work)
91
- @enum = @enum.with_object(obj)
92
- if block_given?
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(&work)
100
- raise_error('map') unless block_given?
68
+ def map
69
+ raise_error(:map) unless block_given?
101
70
 
102
- if @pool_size
103
- outs = []
71
+ [].tap do |outs|
104
72
  with_index do |item, index|
105
- outs[index] = evaluate(item, &work)
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.5
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: 2013-10-29 00:00:00.000000000 Z
11
+ date: 2014-01-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler