asynchronous 4.0.0.pre → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8b9e12c7186a3dc906e14faede2ecab396c1d6d1
4
- data.tar.gz: 6ce4ccbbfe6fd0bbf064e323278fec86ecd3b814
3
+ metadata.gz: b45ba44d151028eeee66d2cd93f6dccf1da66940
4
+ data.tar.gz: 50e3542ab8a04221d7f3cef1cad1afd400fd2adb
5
5
  SHA512:
6
- metadata.gz: 69ec170beaa0e9ff62e3044f25aa67f07ef42e7b6f5b1117aad5de72f907e4244a1a58039214db3ad09ac4b93149a3ee4902ed444104968daaabc67a4856e3c6
7
- data.tar.gz: 337e7edc8238f24a442f79f33ee265fbb4d51856e9bfa755a9ae41d4f21eaea4d0d855f045a64e0b9f8e1aed1c1482e83c58efc2aa0b464fb4e26c4be20b1f3f
6
+ metadata.gz: 8698c24aaf589aa730a168b15ec1803d9c4575325470a8799d31e2f2fdfc6efb07918a1d3e2d1d39317ceca7827c63a18df52407954df812ed7c71dff29f8ff0
7
+ data.tar.gz: 81e768d82ad49ff3333ff7ba4b3b2f837c701077fc4a367d9d76adb34245feac5ee6d407b25970c2eafdb23250da2a1f35058b43f66a40a0ae6d6b1cca7acf9c
data/Gemfile CHANGED
@@ -1,2 +1,4 @@
1
1
  source "http://rubygems.org"
2
- gemspec
2
+ gemspec
3
+
4
+ gem 'rspec'
@@ -1,14 +1,32 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- asynchronous (4.0.0.pre)
4
+ asynchronous (4.0.0)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
8
8
  specs:
9
+ diff-lcs (1.3)
10
+ rspec (3.7.0)
11
+ rspec-core (~> 3.7.0)
12
+ rspec-expectations (~> 3.7.0)
13
+ rspec-mocks (~> 3.7.0)
14
+ rspec-core (3.7.1)
15
+ rspec-support (~> 3.7.0)
16
+ rspec-expectations (3.7.0)
17
+ diff-lcs (>= 1.2.0, < 2.0)
18
+ rspec-support (~> 3.7.0)
19
+ rspec-mocks (3.7.0)
20
+ diff-lcs (>= 1.2.0, < 2.0)
21
+ rspec-support (~> 3.7.0)
22
+ rspec-support (3.7.0)
9
23
 
10
24
  PLATFORMS
11
25
  ruby
12
26
 
13
27
  DEPENDENCIES
14
28
  asynchronous!
29
+ rspec
30
+
31
+ BUNDLED WITH
32
+ 1.16.0
data/README.md CHANGED
@@ -2,19 +2,32 @@ Asynchronous
2
2
  ============
3
3
 
4
4
  Asynchronous Patterns for Ruby Based on Pure MRI CRuby code
5
- The goal is to use the original MRI C libs for achive
6
- real async patterns in ruby
5
+ The goal is to use the original MRI C libs for achieve
6
+ real async processing in ruby
7
7
 
8
- Well it is achived in Ruby really simple, and elegant way.
8
+ This is good for where cpu focused for included,
9
+ for example csv transformation
10
+
11
+ ### Example
12
+
13
+ ```ruby
14
+ require 'asynchronous/core_ext'
15
+
16
+ thr = async do
17
+ "some expensive work"
18
+ end
19
+
20
+ thr.value #> "some expensive work"
21
+ ```
9
22
 
10
23
 
11
24
  ### Quoting Sun's Multithreaded Programming Guide:
12
25
 
13
- Parallelism:
26
+ Parallelism:
14
27
  - A condition that arises when at least two threads are executing simultaneously.
15
28
 
16
- Concurrency:
17
- - A condition that exists when at least two threads are making progress.
29
+ Concurrency:
30
+ - A condition that exists when at least two threads are making progress.
18
31
  - A more generalized form of parallelism that can include time-slicing as a form of virtual parallelism
19
32
 
20
33
  #### for short:
@@ -25,101 +38,3 @@ Eg. multitasking on a single-core machine.
25
38
 
26
39
  Parallelism is when tasks literally run at the same time.
27
40
  Eg. on a multicore processor.
28
-
29
-
30
- ### OS managed thread (Native Threads)
31
-
32
- copy on write memory share,
33
- so you cant change anything in the mother process
34
- only with the return value later.
35
- This method is stronger the more CPU core the os have
36
-
37
- Ideal for Big jobs that require lot of memory allocation or
38
- heavy CPU load to get a value
39
- Like parsing (in some case)
40
-
41
- ```ruby
42
-
43
- thr = async :parallelism do
44
-
45
- sleep 4
46
- # remember, everything you
47
- # write here only mather in this block
48
- # will not affect the real process only
49
- # the last return value of the block
50
- 4 * 5
51
-
52
- end
53
-
54
- # to call the value:
55
- thr.value
56
-
57
- ```
58
-
59
- ## VM managed thread (Green Threads)
60
-
61
- you can use simple :c also instead of :concurrency as sym,
62
- remember :concurrency is all about GIL case, so
63
- you can modify the objects in memory
64
- This is ideal for little operations in simultaneously or
65
- when you need to update objects in the memory and not the
66
- return value what is mather.
67
- Remember well that GarbageCollector will affect the speed.
68
-
69
- ```ruby
70
-
71
- thr = async { sleep 3; 4 * 3 }
72
-
73
- # to call the value:
74
- calculation = thr.value
75
-
76
- ```
77
-
78
- ### Example
79
-
80
- you can use simple :p or :parallelism as nametag
81
- remember :parallelism is all about real OS thread case,
82
- so you CANT modify the objects in the memory,
83
- the normal variables will only be copy on write modify
84
-
85
- This is ideal for big operations where you need do a big process
86
- w/o the fear of the Garbage collector slowness or the GIL lock
87
- when you need to update objects in the memory use SharedMemory
88
-
89
- Remember! if the hardware only got 1 cpu, it will be like a harder
90
- to use concurrency with an expensive memory allocation
91
-
92
- ```ruby
93
-
94
- thr = async :OS do
95
-
96
- sleep 4
97
- 4 * 5
98
-
99
- end
100
-
101
- calculation = thr.value
102
- calculation += 1
103
-
104
- puts calculation
105
-
106
- ```
107
-
108
-
109
- there are other examples that you can check in the exampels folder
110
-
111
- ### known bugs
112
-
113
- In rare case when you get object_buffer error
114
- * use .join method on the async variable
115
-
116
- ```ruby
117
-
118
- thr = async :OS do
119
- sleep 4
120
- 4 * 5
121
- end
122
-
123
- thr.join #> thr
124
-
125
- ```
data/VERSION CHANGED
@@ -1 +1 @@
1
- 4.0.0.pre
1
+ 4.0.0
@@ -0,0 +1,11 @@
1
+ # Require Gemfile gems
2
+ require_relative 'bootstrap'
3
+
4
+ thr1 = async do
5
+ sleep 2
6
+ 4 * 2
7
+ end
8
+
9
+ calculation1 = thr1.value
10
+ calculation1 += 1
11
+ puts calculation1
@@ -1,2 +1,2 @@
1
1
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__),'..','lib'))
2
- require 'asynchronous'
2
+ require 'asynchronous/core_ext'
@@ -1,7 +1,7 @@
1
1
  #encoding: UTF-8
2
2
  module Asynchronous
3
+ require 'asynchronous/utils'
3
4
  require 'asynchronous/error'
4
- require 'asynchronous/concurrency'
5
- require 'asynchronous/parallelism'
6
- require 'asynchronous/kernel'
5
+ require 'asynchronous/thread'
6
+ require 'asynchronous/zombi_killer'
7
7
  end
@@ -0,0 +1,13 @@
1
+ require 'asynchronous'
2
+ #
3
+ # kernel method for asyncron calls
4
+ # basic version is :
5
+ #
6
+ # thr = async { "ruby Code here" }
7
+ # thr.value #> "ruby Code here"
8
+ #
9
+ Kernel.class_eval do
10
+ def async(&block)
11
+ ::Asynchronous::Thread.new(&block)
12
+ end
13
+ end
@@ -0,0 +1,87 @@
1
+ class Asynchronous::Thread
2
+ def initialize(&callable)
3
+ @value = nil
4
+ @value_received = false
5
+ @read, @write = ::IO.pipe
6
+ read_buffer
7
+
8
+ @pid = fork(&callable)
9
+ end
10
+
11
+ def join
12
+ wait
13
+ receive_value! unless @value_received
14
+ raise(@value.wrapped_error) if @value.is_a?(::Asynchronous::Error)
15
+ self
16
+ end
17
+
18
+ def kill
19
+ ::Process.kill('KILL', @pid)
20
+
21
+ wait
22
+ rescue Errno::ESRCH
23
+ nil
24
+ end
25
+
26
+ def status
27
+ alive? ? 'run' : false
28
+ end
29
+
30
+ def value
31
+ join; @value
32
+ end
33
+
34
+ protected
35
+
36
+ def alive?
37
+ ::Asynchronous::Utils.alive?(@pid)
38
+ end
39
+
40
+ def receive_value!
41
+ @write.close
42
+ read_buffer.join
43
+ @read.close
44
+ @value_received = true
45
+ end
46
+
47
+ def wait(flag = 0)
48
+ ::Process.wait(@pid, flag)
49
+ rescue Errno::ECHILD
50
+ nil
51
+ end
52
+
53
+ def fork(&callable)
54
+ ::Kernel.fork { main(&callable) }
55
+ end
56
+
57
+ def read_buffer
58
+ @read_buffer ||= ::Thread.new do
59
+ @value = Marshal.load(@read)
60
+ end
61
+ end
62
+
63
+ def main
64
+ ::Asynchronous::ZombiKiller.antidote
65
+
66
+ @read.close
67
+
68
+ result = begin
69
+ yield
70
+ rescue ::Exception => ex
71
+ ::Asynchronous::Error.new(ex)
72
+ end
73
+
74
+ write_result!(result)
75
+ end
76
+
77
+ def write_result!(result)
78
+ return unless Asynchronous::ZombiKiller.how_is_mom?
79
+ ::Marshal.dump(result, @write)
80
+ @write.flush
81
+ @write.close
82
+ rescue Errno::EPIPE
83
+ nil
84
+ end
85
+ end
86
+
87
+ # Marshal.load(File.binread(FNAME))
@@ -0,0 +1,10 @@
1
+ module Asynchronous::Utils
2
+ def alive?(pid)
3
+ ::Process.kill(0, pid)
4
+ return true
5
+ rescue ::Errno::ESRCH
6
+ return false
7
+ end
8
+
9
+ module_function :alive?
10
+ end
@@ -0,0 +1,20 @@
1
+ module Asynchronous::ZombiKiller
2
+ MOTHER_PID ||= $$
3
+
4
+ def how_is_mom?
5
+ Asynchronous::Utils.alive?(MOTHER_PID)
6
+ end
7
+
8
+ def antidote
9
+ Thread.main[:ZombiKiller] ||= ::Thread.new do
10
+ loop do
11
+ ::Kernel.exit unless how_is_mom?
12
+
13
+ ::Kernel.sleep(1)
14
+ end
15
+ end
16
+ end
17
+
18
+ module_function :antidote, :how_is_mom?
19
+
20
+ end
@@ -0,0 +1,152 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Asynchronous::Thread do
4
+ subject(:thread) { described_class.new(&block) }
5
+
6
+ describe '#join' do
7
+ subject(:joining) { thread.join }
8
+ let(:block) { proc { sleep(1.5) } }
9
+
10
+ it { is_expected.to be thread }
11
+ it { expect { 3.times { thread.join } }.to_not raise_error }
12
+ it { expect { Timeout.timeout(1) { joining } }.to raise_error(Timeout::Error) }
13
+ it { expect { joining; Process.kill(0, thread.instance_variable_get(:@pid)) }.to raise_error(Errno::ESRCH) }
14
+
15
+ context 'when exception raised in the thread' do
16
+ let(:block) { proc { raise 'boom' } }
17
+
18
+ it { expect { joining }.to raise_error 'boom' }
19
+ end
20
+ end
21
+
22
+ describe '#value' do
23
+ subject(:value) { thread.value }
24
+
25
+ [
26
+ true,
27
+ false,
28
+
29
+ 42,
30
+ -42,
31
+ (2**30),
32
+ (2**30) - 1,
33
+
34
+ 42.13,
35
+ -42.13,
36
+ (2**30) + 0.1,
37
+ (2**30) + 0.1 - 1,
38
+
39
+ [1, 2, 3],
40
+
41
+ { :a => :b },
42
+
43
+ 'hello world!',
44
+ :hello_world,
45
+ :"hello world!",
46
+
47
+ StandardError.new('Boom!')
48
+ ].each do |expected_value|
49
+ context "when the return value is a #{expected_value.class}" do
50
+ let(:block) { proc { expected_value } }
51
+
52
+ it { is_expected.to eq expected_value }
53
+ it { is_expected.to be_a expected_value.class }
54
+ end
55
+ end
56
+
57
+ context "when it's a custom class" do
58
+ expected_value = ExampleCustomClassForValue.new('test')
59
+ let(:block) { proc { expected_value } }
60
+
61
+ get_instance_variables = lambda do |object|
62
+ object.instance_variables.map { |iv| object.instance_variable_get(iv) }.sort
63
+ end
64
+
65
+ it { is_expected.to be_a expected_value.class }
66
+ it { expect(get_instance_variables.call(value)).to eq get_instance_variables.call(expected_value) }
67
+ end
68
+
69
+ context "when it's a huge string" do
70
+ long_string = 'X' * 1024 * 100
71
+ let(:block) { proc { long_string } }
72
+ it { is_expected.to eq long_string }
73
+ end
74
+
75
+ context 'when an exception raised in the async process' do
76
+ let(:block) { proc { raise 'boom!' } }
77
+ it { expect { value }.to raise_error 'boom!' }
78
+ end
79
+ end
80
+
81
+ describe '#kill' do
82
+ subject(:killing) { thread.kill }
83
+
84
+ context 'given the thread running in the background' do
85
+ let(:block) { proc { sleep(10) } }
86
+
87
+ it { expect { killing }.to_not raise_error }
88
+ end
89
+
90
+ context 'given thread not running in the background anymore' do
91
+ let(:block) { proc {} }
92
+
93
+ it { expect { sleep(1); killing }.to_not raise_error }
94
+ end
95
+
96
+ context 'given thread already killed' do
97
+ let(:block) { proc {} }
98
+
99
+ it { expect { sleep(1); 3.times { thread.kill } }.to_not raise_error }
100
+ end
101
+ end
102
+
103
+ describe '#status' do
104
+ subject(:status) { thread.status }
105
+
106
+ context 'when thread is alive' do
107
+ let(:block) { proc { sleep(5) } }
108
+
109
+ it { is_expected.to eq 'run' }
110
+ end
111
+
112
+ context 'when thread is completed' do
113
+ let(:block) { proc {} }
114
+ before { thread.join }
115
+ it { is_expected.to eq false }
116
+ end
117
+
118
+ context 'when thread is killed' do
119
+ let(:block) { proc { sleep(10) } }
120
+ before { thread.kill }
121
+ it { is_expected.to be false }
122
+ end
123
+ end
124
+
125
+ describe 'no zombies' do
126
+ subject(:pid) { `ruby -r "#{BOOTSTRAP_FILE_PATH}" -e "pid = async { sleep(15) }.instance_variable_get(:@pid); puts pid "`.to_i }
127
+
128
+ it { expect { pid; sleep(2); Process.kill(0, pid) }.to raise_error Errno::ESRCH }
129
+ end
130
+ end
131
+
132
+ # http://ruby-doc.org/core-2.2.0/Thread.html
133
+ # join
134
+ # key?
135
+ # keys
136
+ # kill
137
+ # pending_interrupt?
138
+ # priority
139
+ # priority=
140
+ # raise
141
+ # run
142
+ # safe_level
143
+ # set_trace_func
144
+ # status
145
+ # stop?
146
+ # terminate
147
+ # thread_variable?
148
+ # thread_variable_get
149
+ # thread_variable_set
150
+ # thread_variables
151
+ # value
152
+ # wakeup
@@ -0,0 +1,34 @@
1
+ require 'rspec'
2
+ require 'timeout'
3
+ require 'asynchronous'
4
+
5
+ RSpec.configure do |config|
6
+ config.expect_with :rspec do |expectations|
7
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
8
+ end
9
+
10
+ config.mock_with :rspec do |mocks|
11
+ mocks.verify_partial_doubles = true
12
+ end
13
+
14
+ config.shared_context_metadata_behavior = :apply_to_host_groups
15
+ end
16
+
17
+ Kernel.at_exit do
18
+ if Asynchronous::ZombiKiller::MOTHER_PID == $PROCESS_ID
19
+ begin
20
+ loop { Process.kill('KILL', Process.wait(-1, Process::WUNTRACED)) }
21
+ rescue Errno::ESRCH, Errno::ECHILD
22
+ puts 'done'
23
+ end
24
+ end
25
+ end
26
+
27
+ class ExampleCustomClassForValue
28
+ attr_reader :a
29
+ def initialize(a)
30
+ @a = a
31
+ end
32
+ end
33
+
34
+ BOOTSTRAP_FILE_PATH = File.expand_path(File.join(File.dirname(__FILE__), '..', 'examples', 'bootstrap.rb'))
data/t.rb ADDED
@@ -0,0 +1,10 @@
1
+
2
+
3
+ begin
4
+ 10_000.times do
5
+ Kernel.fork { sleep(10) }
6
+ end
7
+ rescue Exception => exception
8
+ puts exception.class
9
+ puts exception
10
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: asynchronous
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0.pre
4
+ version: 4.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam Luzsi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-22 00:00:00.000000000 Z
11
+ date: 2018-01-26 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: 'DSL for for dead simple to use asynchronous patterns in both VM managed
14
14
  and OS managed way (Concurrency and Parallelism) '
@@ -24,17 +24,18 @@ files:
24
24
  - Rakefile
25
25
  - VERSION
26
26
  - asynchronous.gemspec
27
- - examples/async_patterns.rb
27
+ - examples/basic.rb
28
28
  - examples/bootstrap.rb
29
- - examples/no_zombie_test.rb
30
- - examples/parallelism.rb
31
29
  - files.rb
32
- - lib/async.rb
33
30
  - lib/asynchronous.rb
34
- - lib/asynchronous/concurrency.rb
31
+ - lib/asynchronous/core_ext.rb
35
32
  - lib/asynchronous/error.rb
36
- - lib/asynchronous/kernel.rb
37
- - lib/asynchronous/parallelism.rb
33
+ - lib/asynchronous/thread.rb
34
+ - lib/asynchronous/utils.rb
35
+ - lib/asynchronous/zombi_killer.rb
36
+ - spec/asynchronous/thread_spec.rb
37
+ - spec/spec_helper.rb
38
+ - t.rb
38
39
  homepage: https://github.com/adamluzsi/asynchronous
39
40
  licenses: []
40
41
  metadata: {}
@@ -49,13 +50,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
49
50
  version: '0'
50
51
  required_rubygems_version: !ruby/object:Gem::Requirement
51
52
  requirements:
52
- - - ">"
53
+ - - ">="
53
54
  - !ruby/object:Gem::Version
54
- version: 1.3.1
55
+ version: '0'
55
56
  requirements: []
56
57
  rubyforge_project:
57
- rubygems_version: 2.2.2
58
+ rubygems_version: 2.6.13
58
59
  signing_key:
59
60
  specification_version: 4
60
61
  summary: Simple Async Based on standard CRuby
61
- test_files: []
62
+ test_files:
63
+ - spec/asynchronous/thread_spec.rb
64
+ - spec/spec_helper.rb
@@ -1,78 +0,0 @@
1
- # Require Gemfile gems
2
- require_relative 'bootstrap'
3
-
4
- # you can use simple :c also instead of :concurrency
5
- # remember :concurrency is all about GIL case, so
6
- # you can modify the objects in memory
7
- # This is ideal for little operations in simultaneously or
8
- # when you need to update objects in the memory
9
- thr1 = async :concurrency do
10
-
11
- sleep 2
12
- 4 * 2
13
-
14
- end
15
- puts "hello concurrency"
16
-
17
- calculation1 = thr1.value
18
- calculation1 += 1
19
-
20
- puts calculation1
21
-
22
- #>--------------------------------------------------
23
- # or you can use simple {} without sym and this will be by default a
24
- # :concurrency pattern
25
-
26
- thr2 = async { sleep 3; 4 * 3 }
27
-
28
- puts "hello simple concurrency"
29
-
30
- calculation2 = thr2.value
31
- calculation2 += 1
32
-
33
- # remember you have to use to cal the return value from the code block!
34
- puts calculation2
35
-
36
-
37
- #>--------------------------------------------------
38
- # now let's see the Parallelism
39
- # you can use simple :p or :parallelism as nametag
40
- # remember :parallelism is all about real OS thread case, so
41
- # you CANT modify the objects in memory only in sharedmemories,
42
- # the normal variables will only be copy on write modify
43
- # This is ideal for big operations where you need do a big process
44
- # w/o the fear of the Garbage collector slowness or the GIL lock
45
- # when you need to update objects in the memory use SharedMemory
46
- thr3 = async :parallelism do
47
-
48
- sleep 4
49
- 4 * 5
50
-
51
- end
52
- puts "hello parallelism"
53
-
54
- calculation3 = thr3.value
55
- calculation3 += 1
56
-
57
- puts calculation3
58
-
59
- #>--------------------------------------------------
60
-
61
- # more complex way
62
-
63
- puts "mixed usecase with arrays as return obj"
64
- thr4 = async :parallelism do
65
-
66
- sleep 4
67
- # some big database processing brutal memory eater stuff
68
- [4*5,"hy"]
69
-
70
- end
71
-
72
- thr5 = async {
73
- [5+1,"sup!"]
74
- }
75
-
76
- puts 'calc1 is eql calc2:',thr4.value == thr5.value
77
- puts (thr4.value+thr5.value).inspect
78
-
@@ -1,29 +0,0 @@
1
- require_relative 'bootstrap'
2
-
3
- thr = async :parallelism do
4
-
5
- # Zombie!
6
- loop do
7
- sleep 1
8
- puts "brains(#{$$})"
9
- end
10
- # inf loop
11
- #
12
-
13
- end
14
-
15
- puts $$
16
- Thread.new do
17
-
18
- sleep 5
19
-
20
- # this want to demonstrate that,
21
- # if the main process is killed,
22
- # you wont leave zombies behind!
23
- system "kill -9 #{$$}"
24
-
25
- end
26
-
27
- loop do
28
- sleep 1
29
- end
@@ -1,35 +0,0 @@
1
- require_relative 'bootstrap'
2
-
3
- async1= async :OS do
4
- 1000000*5
5
- end
6
-
7
- async2= async :OS do
8
-
9
- var = ("!" * 1000000)
10
- puts "the superHuge String length in the pipe is: #{var.length}"
11
-
12
- var
13
- end
14
-
15
- async2_error= async :OS do
16
-
17
- raise(SystemExit)
18
- puts 'never happen, to system error happened!'
19
-
20
- end
21
-
22
- async3= async :OS do
23
- 1000000*5.0
24
- end
25
-
26
- begin
27
- async2_error.value
28
- rescue Exception => ex
29
- puts "\n",'when error happen:',ex
30
- end
31
-
32
- puts "\n",'result length based on string form:'
33
- puts async1.value.to_s.length
34
- puts async2.value.to_s.length
35
- puts async3.value.to_s.length
@@ -1 +0,0 @@
1
- require 'asynchronous'
@@ -1,44 +0,0 @@
1
- # you can use simple :c also instead of :concurrency
2
- # remember :concurrency is all about GIL case, so
3
- # you can modify the objects in memory
4
- # This is ideal for little operations in simultaneously or
5
- # when you need to update objects in the memory
6
- class Asynchronous::Concurrency
7
-
8
- def initialize(&block)
9
-
10
- @rescue_state= nil
11
- @try_count = 0
12
-
13
- begin
14
- @value= nil
15
- @thread ||= ::Thread.new { block.call }
16
- @rescue_state= nil
17
- rescue ThreadError
18
- @rescue_state ||= true
19
- @try_count += 1
20
- if 3 <= @try_count
21
- @value= block.call
22
- @rescue_state= nil
23
- else
24
- sleep 5
25
- retry
26
- end
27
- end
28
-
29
- end
30
-
31
- def join
32
- if @value.nil?
33
- until @rescue_state.nil?
34
- sleep 1
35
- end
36
- @value= @thread.value
37
- end
38
- end
39
-
40
- def value
41
- join; @value
42
- end
43
-
44
- end
@@ -1,42 +0,0 @@
1
- # kernel method for asyncron calls
2
- # basic version is :
3
- #
4
- # var= async { "ruby Code here" }
5
- # var.value #> "ruby Code here"
6
- #
7
- # or
8
- #
9
- # var = async :parallelism do
10
- # "some awsome ruby code here!"
11
- # end
12
- #
13
- module Asynchronous::DCI
14
-
15
- extend self
16
-
17
- def async(type= :VM, &block)
18
- case type.to_s.downcase[0]
19
-
20
- # Concurrency / VM / Green
21
- when *%W[ c v g ]
22
- ::Asynchronous::Concurrency.new(&block)
23
-
24
- # Parallelism / OS / Native
25
- when *%W[ p o n ]
26
- ::Asynchronous::Parallelism.new(&block)
27
-
28
- else
29
- ::Asynchronous::Concurrency.new(&block)
30
-
31
- end
32
- end
33
-
34
- end
35
-
36
- Kernel.class_eval do
37
-
38
- def async(type=nil, &block)
39
- ::Asynchronous::DCI.async(type, &block)
40
- end
41
-
42
- end
@@ -1,135 +0,0 @@
1
- # now let's see the Parallelism
2
- # you can use simple :p also instead of :parallelism
3
- # remember :parallelism is all about real OS thread case, so
4
- # you CANT modify the objects in memory only copy on write modify
5
- # This is ideal for big operations where you need do a big process
6
- # and only get the return value so you can do big works without the fear of the
7
- # Garbage collector slowness or the GIL lock
8
- # when you need to update objects in the memory use :concurrency
9
- class Asynchronous::Parallelism
10
-
11
- attr_reader :pid
12
-
13
- @@pids ||= []
14
- @@motherpid ||= $$
15
-
16
- def initialize(disable_gc=true,&callable)
17
-
18
- @disable_gc = !!disable_gc
19
-
20
- @comm_line = ::IO.pipe
21
-
22
- @value = nil
23
- @read_buffer = nil
24
-
25
- read_buffer
26
-
27
- @pid = fork(&callable)
28
- @@pids.push(@pid)
29
-
30
- end
31
-
32
- def self.alive?(pid)
33
- ::Process.kill(0, pid)
34
- return true
35
- rescue ::Errno::ESRCH
36
- return false
37
- end
38
-
39
- def join
40
- if @value.nil?
41
-
42
- ::Process.wait(@pid, ::Process::WNOHANG)
43
-
44
- @comm_line[1].close
45
- @read_buffer.join
46
- @comm_line[0].close
47
-
48
- if @value.is_a?(::Asynchronous::Error)
49
- raise(@value.wrapped_error)
50
- end
51
-
52
- end; self
53
- end
54
-
55
- def value
56
- join; @value
57
- end
58
-
59
- protected
60
-
61
- def pipe_read
62
- @comm_line[0]
63
- end
64
-
65
- def pipe_write
66
- @comm_line[1]
67
- end
68
-
69
- def fork(&block)
70
- return ::Kernel.fork do
71
-
72
- GC.disable if @disable_gc
73
-
74
- begin
75
- ::Kernel.trap("TERM") do
76
- ::Kernel.exit
77
- end
78
- ::Thread.new do
79
- ::Kernel.loop do
80
- begin
81
- ::Kernel.sleep 1
82
- if ::Asynchronous::Parallelism.alive?(@@motherpid) == false
83
- ::Kernel.exit!
84
- end
85
- end
86
- end
87
- end
88
- end
89
-
90
- @comm_line[0].close
91
-
92
- result = begin
93
- block.call
94
- rescue ::Exception => ex
95
- ::Asynchronous::Error.new(ex)
96
- end
97
-
98
- dumped_result = ::Marshal.dump(result).to_s
99
-
100
- @comm_line[1].write(dumped_result)
101
- ::Kernel.sleep(dumped_result.length.to_f*0.005)
102
-
103
- @comm_line[1].flush
104
- @comm_line[1].close
105
-
106
- end
107
- end
108
-
109
- def read_buffer
110
- @read_buffer = ::Thread.new do
111
- while !@comm_line[0].eof?
112
-
113
- begin
114
- @value = ::Marshal.load(@comm_line[0])
115
- rescue ::ArgumentError => ex
116
- sleep(1)
117
- retry if ex.respond_to?(:message) && ex.message =~ /marshal data too short/i
118
- end
119
-
120
- end
121
- end
122
- end
123
-
124
- # kill kidos at Kernel Exit
125
- ::Kernel.at_exit do
126
- @@pids.each do |pid|
127
- begin
128
- ::Process.kill(:TERM, pid)
129
- rescue ::Errno::ESRCH, ::Errno::ECHILD
130
- #::STDOUT.puts "`kill': No such process (Errno::ESRCH)"
131
- end
132
- end
133
- end
134
-
135
- end