asynchronous 4.0.0.pre → 4.0.0

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: 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