multiprocessing 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a816b2236a62749e3a5a34d2aa1f4786300c3ef7
4
+ data.tar.gz: 7a5291e800ff6077642ecd792fe9a65f339afa99
5
+ SHA512:
6
+ metadata.gz: a99c37e3fd90b73b7520114d36339c544691a84c55c4267c24caae479ce6399a5631087bb7e0ce931d7b4374db7e06be19d2fd500da4bc191ca0bd8be27088c5
7
+ data.tar.gz: d03ef0421e9f3014bae846bcaa00e9394d8ee3bf696cfa1d7376feae612dd6e2ca317262070e3fa5ab579b445f2d23c3bf9501c4456eab96770f4ec58a3c4a05
data/.gitignore CHANGED
@@ -7,7 +7,7 @@ Gemfile.lock
7
7
  InstalledFiles
8
8
  _yardoc
9
9
  coverage
10
- doc/
10
+ doc
11
11
  lib/bundler/man
12
12
  pkg
13
13
  rdoc
@@ -16,3 +16,5 @@ test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
18
  .DS_Store
19
+ *.swp
20
+
@@ -0,0 +1,4 @@
1
+ --title "Documentation for MultiProcessing"
2
+ -
3
+ README.md
4
+ LICENSE.txt
@@ -0,0 +1,121 @@
1
+ MultiProcessing
2
+ ====================
3
+
4
+ MultiProcessingはRubyにおいてプロセス間同期とプロセス間通信の機能を提供します(することを目指しています).
5
+ 各クラスはRubyの標準添付ライブラリthreadで提供されているクラスのような動作をすることを目指しています.
6
+
7
+ MultiProcessing includes classes for inter-process synchronization and communication.
8
+ The classes can be used like ones in ruby standard library for thread.
9
+
10
+ 現状で使用できるクラスは以下です.
11
+ 各クラスはMultiProcessingモジュールの下に作成されています.
12
+
13
+ * Mutex
14
+ * ConditionVariable
15
+ * Semaphore
16
+ * Queue
17
+ * ExternalObject
18
+
19
+ また,IO.named\_pipe が追加されます
20
+
21
+ いずれのクラスもプロセス間通信にパイプ(IO.pipe)を使用しています.
22
+ また,複数のプロセスで1つの同期オブジェクトを共有するためにforkを使用する事を想定しているため,
23
+ Unix系OSのみでのみ使用する事ができます.
24
+
25
+ (Windowsするためには,名前付き共有セマフォなどの同期機能を使用する必要がありそうですね・・・)
26
+
27
+ Install
28
+ ----------------
29
+
30
+ gem install multiprocessing
31
+
32
+ Mutex
33
+ ----------------
34
+
35
+ 標準添付ライブラリthreadのMutexと同様の使い方をします.
36
+
37
+ m = MultiProcessing::Mutex.new
38
+ fork do
39
+ m.synchronize do
40
+ # some critical work
41
+ end
42
+ end
43
+ m.synchronize do
44
+ # come critical work
45
+ end
46
+ Process.waitall
47
+
48
+ lockした後unlockする前にforkすると(synchronize中でforkした場合も)そのクリティカルセクションは並列に動作してしまうので注意してください.
49
+ (forkしたときに子プロセスでは子スレッドが全て殺されるため,別のスレッドでforkすれば問題ないと思います.)
50
+
51
+ ConditionVariable
52
+ ----------------
53
+
54
+ 標準添付ライブラリthreadのConditionVariableと同じ使い方をします.
55
+
56
+ m = MultiProcessing::Mutex.new
57
+ cond = MultiProcessing::ConditionVariable.new
58
+ fork do
59
+ m.synchronize do
60
+ cond.wait(m)
61
+ # something to do
62
+ end
63
+ end
64
+ sleep 1
65
+ # on condition changed
66
+ cond.signal
67
+ Process.waitall
68
+
69
+ Semaphore
70
+ ----------------
71
+
72
+ Semaphoreは標準添付ライブラリに含まれていませんが作りました.
73
+
74
+ s = MultiProcessing::Semaphore.new 2
75
+
76
+ コンストラクタにリソース量の初期値を与えてください.
77
+ Pでリソースのロック,Vで解放をします.
78
+
79
+ Pはlock,Vはunlockという名前のエイリアスが用意してあります.
80
+
81
+ Queue
82
+ ----------------
83
+
84
+ Queueも標準threadライブラリのQueueと同様の使い方をします.
85
+
86
+ q = MultiProcessing::Quque.new
87
+ fork do
88
+ q.push :nyan
89
+ q.close.join_thread
90
+ end
91
+ p q.pop
92
+
93
+ プロセス間の通信にはパイプを使っています.Queue#pushをすると,パイプにデータを書き込むバックグラウンドスレッドが起動します(pythonのmultiprocessingを参考にしました).
94
+ プロセスが終了する際,バックグラウンドスレッドがパイプにデータを書き込み終わるまで待たないと,Queueが書き込みMutexをロックしたままになる場合があります.
95
+ 終了時は
96
+
97
+ q.close.join_thread
98
+
99
+ として,書き込みスレッドの終了を待ってください.close後にキューにデータをpushすると例外が発生します.
100
+
101
+ オブジェクトのシリアライズにMarshal.dumpを使用しているので,Marshal.dumpできないオブジェクトをpushできません.
102
+
103
+ その他
104
+ ----------------
105
+
106
+ ### 関係ありそうなライブラリ
107
+
108
+ * https://github.com/pmahoney/process_shared
109
+
110
+ ### TODO
111
+
112
+ * ExternalObject, IO.named\_pipe のSpecを書く
113
+ * ドキュメントを書く
114
+
115
+ ライセンス
116
+ ----------------
117
+
118
+ Author: clicube
119
+
120
+ MIT License
121
+
data/README.md CHANGED
@@ -1,130 +1,124 @@
1
- MultiProcessing
2
- ====================
1
+ # MultiProcessing #
3
2
 
4
- MultiProcessingはRubyにおいてプロセス間同期とプロセス間通信の機能を提供します(することを目指しています).
5
- 各クラスはRubyの標準添付ライブラリthreadで提供されているクラスのような動作をすることを目指しています.
3
+ MultiProcessing provides classes for
4
+ inter-process synchronization and communication in Ruby.
6
5
 
7
- MultiProcessing includes classes for inter-process synchronization and communication.
8
- The classes can be used like ones in ruby standard library for thread.
6
+ These classes can be used like ones in Ruby standard library for multi threading.
9
7
 
10
- 現状で使用できるクラスは以下です.
11
- 各クラスはMultiProcessingモジュールの下に作成されています.
8
+ To realize communicating across processes,
9
+ MultiProcessing uses pipes (IO.pipe).
10
+ You have to use #fork to create process which accesses synchonizing/communication object.
11
+ For this reason, MultiProcessing can be used only on Unix or Linux.
12
+
13
+ ## Install ##
14
+
15
+ gem install multiprocessing
16
+
17
+ ## Provided Classes ##
12
18
 
13
19
  * Mutex
14
20
  * ConditionVariable
15
21
  * Semaphore
16
22
  * Queue
17
- * Process
18
- * ExternalObject
23
+ * (ExternalObject: under development)
19
24
 
20
- また,IO.named\_pipe が追加されます
25
+ In addition, IO.name\_pipe is added.
21
26
 
22
- いずれのクラスもプロセス間通信にパイプ(IO.pipe)を使用しています.
23
- また,複数のプロセスで1つの同期オブジェクトを共有するためにforkを使用する事を想定しているため,
24
- Unix系OSのみでのみ使用する事ができます.
27
+ ## Mutex ##
25
28
 
26
- (Windowsするためには,名前付き共有セマフォなどの同期機能を使用する必要がありそうですね・・・)
29
+ Process version of Mutex.
30
+ It can be used like ::Mutex in Ruby standard library.
27
31
 
28
- Mutex
29
- ----------------
32
+ Example:
30
33
 
31
- 標準添付ライブラリthreadのMutexと同様の使い方をします.
32
-
33
- m = MultiProcessing::Mutex.new
34
- fork do
35
- m.synchronize do
36
- # some critical work
34
+ require 'multiprocessing'
35
+
36
+ mutex = MultiProcessing::Mutex.new
37
+ 3.times do
38
+ fork do
39
+ mutex.synchronize do
40
+ # critical section
41
+ puts Process.pid
42
+ sleep 1
43
+ end
37
44
  end
38
45
  end
39
- m.synchronize do
40
- # come critical work
41
- end
42
46
  Process.waitall
47
+ # => prints 3 pids of forked process in 1 sec interval
48
+
49
+ Note: Do not fork in critical section.
43
50
 
44
- lockした後unlockする前にforkすると(synchronize中でforkした場合も)そのクリティカルセクションは並列に動作してしまうので注意してください.
45
- (forkしたときに子プロセスでは子スレッドが全て殺されるため,別のスレッドでforkすれば問題ないと思います.)
51
+ ## ConditionVariable ##
46
52
 
47
- ConditionVariable
48
- ----------------
53
+ Process version of ConditionVariable.
54
+ It can be used like ::ConditionVariable in Ruby standard library.
49
55
 
50
- 標準添付ライブラリthreadのConditionVariableと同じ使い方をします.
56
+ Example:
51
57
 
52
- m = MultiProcessing::Mutex.new
53
- cond = MultiProcessing::ConditionVariable.new
58
+ require 'multiprocessing'
59
+
60
+ m = MultiProcessing::Mutex.new
61
+ cond = MultiProcessing::ConditionVariable.new
62
+ 3.times do
54
63
  fork do
55
64
  m.synchronize do
65
+ puts "waiting pid:#{Process.pid}"
56
66
  cond.wait(m)
57
- # something to do
67
+ puts "restarted pid:#{Process.pid}"
58
68
  end
59
69
  end
60
- sleep 1
61
- # on condition changed
62
- cond.signal
63
- Process.waitall
70
+ end
71
+ sleep 0.1 # => 3 processes get waiting a signal
72
+ cond.signal # => One process restarts
73
+ cond.broadcast # => Remaining 2 process restart
74
+ Process.waitall
75
+
76
+
77
+ ## Semaphore ##
64
78
 
65
- Semaphore
66
- ----------------
79
+ Semaphore is like Mutex but it can manage multiple resources.
80
+ It is initialized with initiali number of resources.
81
+ It can be release from the thread or process which didn't lock the semaphore.
67
82
 
68
- Semaphoreは標準添付ライブラリに含まれていませんが作りました.
83
+ Example:
69
84
 
85
+ require 'multiprocessing'
86
+
70
87
  s = MultiProcessing::Semaphore.new 2
88
+ 3.times do
89
+ fork do
90
+ s.synchronize do
91
+ puts "pid: #{Process.pid}"
92
+ sleep 1
93
+ end
94
+ end
95
+ end
96
+ Process.waitall
97
+ # => two processes prints its pid immediately
98
+ # but the other does late.
71
99
 
72
- コンストラクタにリソース量の初期値を与えてください.
73
- Pでリソースのロック,Vで解放をします.
100
+ ## Queue ##
74
101
 
75
- Pはlock,Vはunlockという名前のエイリアスが用意してあります.
102
+ Process version of Queue.
103
+ It provides away to communication between processes.
104
+ It can be used like ::Queue in Ruby standard library.
76
105
 
77
- Queue
78
- ----------------
106
+ Queue usees pipes to communicate with other processes.
107
+ Queue#push} starts background thread ti write data to the pipe.
108
+ Avoiding to exit process before writing to the pipe,
109
+ use Queue#close and Queue#join\_thread.
110
+ Queue#join\_thread waits until all data is written to the pipe.
79
111
 
80
- Queueも標準threadライブラリのQueueと同様の使い方をします.
112
+ Example:
81
113
 
82
- q = MultiProcessing::Quque.new
114
+ require 'multiprocessing'
115
+
116
+ q = MultiProcessing::Queue.new
83
117
  fork do
84
118
  q.push :nyan
119
+ q.push :wan
85
120
  q.close.join_thread
86
121
  end
87
- p q.pop
88
-
89
- プロセス間の通信にはパイプを使っています.Queue#pushをすると,パイプにデータを書き込むバックグラウンドスレッドが起動します(pythonのmultiprocessingを参考にしました).
90
- プロセスが終了する際,バックグラウンドスレッドがパイプにデータを書き込み終わるまで待たないと,Queueが書き込みMutexをロックしたままになる場合があります.
91
- 終了時は
92
-
93
- q.close.join_thread
94
-
95
- として,書き込みスレッドの終了を待ってください.close後にキューにデータをpushすると例外が発生します.
96
-
97
- オブジェクトのシリアライズにMarshal.dumpを使用しているので,Marshal.dumpできないオブジェクトをpushできません.
98
-
99
- Process
100
- ----------------
101
-
102
- 標準のProcessモジュールは
103
-
104
- >Process がプロセスを表現するクラスではなく、プロセスに対する操作 をまとめたモジュールであることに注意してください。
105
-
106
- とのことなので,プロセスを表現するっぽいクラスを作ってみました.
107
-
108
- p = MultiProcessing::Process.new { # something to do }
109
- p.join
110
-
111
- とかできます.
112
-
113
- その他
114
- ----------------
115
-
116
- ### 関係ありそうなライブラリ
117
-
118
- * https://github.com/pmahoney/process_shared
119
-
120
- ### TODO
121
-
122
- * ConditionVariable, ExternalObject, IO.named\_pipe のSpecを書く
123
-
124
- ライセンス
125
- ----------------
126
-
127
- Author: clicube
128
-
129
- MIT License
122
+ q.pop # => :nyan
123
+ q.pop # => :wan
130
124
 
data/Rakefile CHANGED
@@ -1 +1,24 @@
1
- require "bundler/gem_tasks"
1
+ require 'bundler/gem_tasks'
2
+
3
+ task :default => :spec
4
+
5
+ #### RSpec Task####
6
+
7
+ require 'rspec/core/rake_task'
8
+
9
+ RSpec::Core::RakeTask.new(:spec, :name) do |t,task_args|
10
+ t.pattern = "spec/**/#{task_args[:name]}_spec.rb" if task_args[:name]
11
+ t.rspec_opts = "--color --order rand"
12
+ end
13
+
14
+
15
+ #### Yard Task ####
16
+
17
+ require 'yard'
18
+ require 'yard/rake/yardoc_task'
19
+
20
+ YARD::Rake::YardocTask.new("yard") do |t|
21
+ t.options = ["--no-stats"]
22
+ t.after = ->{ YARD::CLI::Stats.run("--list-undoc") }
23
+ end
24
+
@@ -2,7 +2,47 @@ require File.expand_path(File.dirname(__FILE__) + '/multiprocessing/mutex')
2
2
  require File.expand_path(File.dirname(__FILE__) + '/multiprocessing/conditionvariable')
3
3
  require File.expand_path(File.dirname(__FILE__) + '/multiprocessing/semaphore')
4
4
  require File.expand_path(File.dirname(__FILE__) + '/multiprocessing/queue')
5
- require File.expand_path(File.dirname(__FILE__) + '/multiprocessing/process')
6
5
  require File.expand_path(File.dirname(__FILE__) + '/multiprocessing/namedpipe')
7
6
  require File.expand_path(File.dirname(__FILE__) + '/multiprocessing/externalobject')
8
7
 
8
+ ##
9
+ #
10
+ # MultiProcessing provides classes for
11
+ # inter-process synchronization and communication in Ruby.
12
+ #
13
+ # These classes can be used like ones in Ruby standard library for multi threading.
14
+ #
15
+ # To realize communitation across processes, MultiProcessing uses pipe (IO.pipe).
16
+ # You have to use fork to create multiple processes
17
+ # which accesses synchronizing/communication object.
18
+ # For this reason, MultiProcessing can be used only on Unix or Linux.
19
+ #
20
+ #
21
+ module MultiProcessing
22
+
23
+ # documentation is at below
24
+ if Thread.respond_to?(:handle_interrupt)
25
+ def try_handle_interrupt *args, &block
26
+ Thread.handle_interrupt *args, &block
27
+ end
28
+ else
29
+ def try_handle_interrupt *args
30
+ yield
31
+ end
32
+ end
33
+ module_function :try_handle_interrupt
34
+ ##
35
+ #
36
+ # @!method try_handle_interrupt *args
37
+ # @scope class
38
+ #
39
+ # If Thread has handle_interrupt (Ruby 2.0.0 or later), call it with given arguments and block.
40
+ # If not, simply yield passed block
41
+ #
42
+ # @param [Array<Object>] args arguments to give Thread.handle_interrupt
43
+ # @return [Object] returned value of the block
44
+ #
45
+ ##
46
+
47
+ end
48
+
@@ -2,6 +2,33 @@ require File.expand_path(File.dirname(__FILE__) + '/processerror')
2
2
  require File.expand_path(File.dirname(__FILE__) + '/mutex')
3
3
 
4
4
  module MultiProcessing
5
+
6
+ ##
7
+ #
8
+ # Process version of ConditionVariable
9
+ # This can be used like ::ConditionVariable in Ruby standard library.
10
+ #
11
+ # Note that ConditionVariable uses 2 pipes.
12
+ #
13
+ # @example
14
+ # require 'multiprocessing'
15
+ #
16
+ # m = MultiProcessing::Mutex.new
17
+ # cond = MultiProcessing::ConditionVariable.new
18
+ # 3.times do
19
+ # fork do
20
+ # m.synchronize do
21
+ # puts "waiting pid:#{Process.pid}"
22
+ # cond.wait(m)
23
+ # puts "restarted pid:#{Process.pid}"
24
+ # end
25
+ # end
26
+ # end
27
+ # sleep 0.1 # => 3 processes get waiting a signal
28
+ # cond.signal # => One process restarts
29
+ # cond.broadcast # => Remaining 2 process restart
30
+ # Process.waitall
31
+ #
5
32
  class ConditionVariable
6
33
 
7
34
  def initialize
@@ -9,76 +36,71 @@ module MultiProcessing
9
36
  @signal_pout,@signal_pin = IO.pipe
10
37
  end
11
38
 
39
+ ##
40
+ #
41
+ # Wakes up all threads waiting for this lock.
42
+ #
43
+ # @return [Fixnum] Number of threads waked up
44
+ #
12
45
  def broadcast
13
- while(signal); end
46
+ n = 0
47
+ while(signal)
48
+ n += 1
49
+ end
50
+ return n
14
51
  end
15
52
 
53
+ ##
54
+ #
55
+ # Wakes up one of threads waiting for this lock.
56
+ #
57
+ # @note An order of waking up is indefinite.
58
+ #
59
+ # @return [Boolean] Returns true if wakes up. Returns false if no threads were waiting.
60
+ #
16
61
  def signal
17
- begin
18
- @waiting_pout.read_nonblock 1
19
- @signal_pin.syswrite 1
20
- #@signal_pin.write 1
21
- #@signal_pin.flush
22
- return true
23
- rescue Errno::EAGAIN
24
- return nil
62
+ MultiProcessing.try_handle_interrupt(RuntimeError => :never) do
63
+ begin
64
+ @waiting_pout.read_nonblock 1
65
+ @signal_pin.syswrite 1
66
+ return true
67
+ rescue Errno::EAGAIN
68
+ return false
69
+ end
25
70
  end
26
71
  end
27
72
 
73
+ ##
74
+ #
75
+ # Releases the lock held in mutex and waits, reacquires the lock on wakeup.
76
+ #
77
+ # @param [Mutex] mutex An instance of MultiProcessing::Mutex. It must be locked.
78
+ # @return [ConditionVariable] itself
79
+ # @note Do not pass an instance of ::Mutex. Pass an instance of MultiProcessing::Mutex.
80
+ #
81
+ # @raise [TypeError]
82
+ # @raise [ArgumentError]
83
+ #
28
84
  def wait(mutex)
29
- raise MultiProcessing::ProcessError.new("mutex must be instance of MultiProcessing::Mutex") if mutex.class != MultiProcessing::Mutex
30
- @waiting_pin.syswrite 1
31
- #@waiting_pin.write 1
32
- #@waiting_pin.flush
33
- mutex.unlock
34
- @signal_pout.readpartial 1
35
- mutex.lock
36
- self
85
+ MultiProcessing.try_handle_interrupt(RuntimeError => :never) do
86
+ raise TypeError.new("mutex must be instance of MultiProcessing::Mutex") if mutex.class != MultiProcessing::Mutex
87
+ raise ArgumentError.new("mutex must be locked") unless mutex.locked?
88
+ @waiting_pin.syswrite 1
89
+ mutex.unlock
90
+ begin
91
+ MultiProcessing.try_handle_interrupt(RuntimeError => :on_blocking) do
92
+ @signal_pout.readpartial 1
93
+ end
94
+ rescue Exception => e
95
+ @waiting_pout.readpartial 1
96
+ raise e
97
+ ensure
98
+ mutex.lock
99
+ end
100
+ self
101
+ end
37
102
  end
38
103
 
39
104
  end
40
105
  end
41
106
 
42
- if __FILE__ == $0
43
-
44
- m = MultiProcessing::Mutex.new
45
- cond = MultiProcessing::ConditionVariable.new
46
- fork do
47
- m.synchronize do
48
- sleep 1
49
- puts "waiting p1"
50
- cond.wait(m)
51
- puts "restarted p1"
52
- sleep 1
53
- puts "end p1"
54
- end
55
- end
56
- fork do
57
- m.synchronize do
58
- sleep 1
59
- puts "waiting p2"
60
- cond.wait(m)
61
- puts "restarted p2"
62
- sleep 1
63
- puts "end p2"
64
- end
65
- end
66
- fork do
67
- m.synchronize do
68
- sleep 1
69
- puts "waiting p3"
70
- cond.wait(m)
71
- puts "restarted p3"
72
- sleep 1
73
- puts "end p3"
74
- end
75
- end
76
- sleep 5
77
- puts "cond signaling"
78
- cond.signal
79
- sleep 3
80
- puts "cond broadcasting"
81
- cond.broadcast
82
- Process.waitall
83
- end
84
-