cruby 0.0.1
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.
- data/CHANGELOG +0 -0
- data/README +7 -0
- data/Rakefile +85 -0
- data/examples/echoc.rb +148 -0
- data/examples/echod.rb +18 -0
- data/examples/fib.rb +45 -0
- data/examples/prime.rb +40 -0
- data/lib/crbtk.rb +77 -0
- data/lib/cruby.rb +2 -0
- data/lib/cruby/channel.rb +197 -0
- data/lib/cruby/coroutine.rb +87 -0
- data/lib/cruby/event.rb +51 -0
- data/lib/cruby/version.rb +9 -0
- data/test/cruby_test.rb +56 -0
- data/test/test_helper.rb +2 -0
- metadata +72 -0
data/CHANGELOG
ADDED
File without changes
|
data/README
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/clean'
|
4
|
+
require 'rake/testtask'
|
5
|
+
require 'rake/packagetask'
|
6
|
+
require 'rake/gempackagetask'
|
7
|
+
require 'rake/rdoctask'
|
8
|
+
require 'rake/contrib/rubyforgepublisher'
|
9
|
+
require 'fileutils'
|
10
|
+
include FileUtils
|
11
|
+
require File.join(File.dirname(__FILE__), 'lib', 'cruby', 'version')
|
12
|
+
|
13
|
+
AUTHOR = "hattori"
|
14
|
+
EMAIL = "hattori@isp.co.jp"
|
15
|
+
DESCRIPTION = "CRuby is a library that provids concurrent programming in Ruby"
|
16
|
+
RUBYFORGE_PROJECT = "cruby"
|
17
|
+
HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
|
18
|
+
BIN_FILES = %w( )
|
19
|
+
|
20
|
+
|
21
|
+
NAME = "cruby"
|
22
|
+
REV = File.read(".svn/entries")[/committed-rev="(d+)"/, 1] rescue nil
|
23
|
+
VERS = ENV['VERSION'] || (Cruby::VERSION::STRING + (REV ? ".#{REV}" : ""))
|
24
|
+
CLEAN.include ['**/.*.sw?', '*.gem', '.config']
|
25
|
+
RDOC_OPTS = ['--quiet', '--title', "cruby documentation",
|
26
|
+
"--opname", "index.html",
|
27
|
+
"--line-numbers",
|
28
|
+
"--main", "README",
|
29
|
+
"--inline-source"]
|
30
|
+
|
31
|
+
desc "Packages up cruby gem."
|
32
|
+
task :default => [:test]
|
33
|
+
task :package => [:clean]
|
34
|
+
|
35
|
+
Rake::TestTask.new("test") { |t|
|
36
|
+
t.libs << "test"
|
37
|
+
t.pattern = "test/**/*_test.rb"
|
38
|
+
t.verbose = true
|
39
|
+
}
|
40
|
+
|
41
|
+
spec =
|
42
|
+
Gem::Specification.new do |s|
|
43
|
+
s.name = NAME
|
44
|
+
s.version = VERS
|
45
|
+
s.platform = Gem::Platform::RUBY
|
46
|
+
s.has_rdoc = true
|
47
|
+
s.extra_rdoc_files = ["README", "CHANGELOG"]
|
48
|
+
s.rdoc_options += RDOC_OPTS + ['--exclude', '^(examples|extras)/']
|
49
|
+
s.summary = DESCRIPTION
|
50
|
+
s.description = DESCRIPTION
|
51
|
+
s.author = AUTHOR
|
52
|
+
s.email = EMAIL
|
53
|
+
s.homepage = HOMEPATH
|
54
|
+
s.executables = BIN_FILES
|
55
|
+
s.rubyforge_project = RUBYFORGE_PROJECT
|
56
|
+
s.bindir = "bin"
|
57
|
+
s.require_path = "lib"
|
58
|
+
s.autorequire = "cruby"
|
59
|
+
|
60
|
+
#s.add_dependency('activesupport', '>=1.3.1')
|
61
|
+
#s.required_ruby_version = '>= 1.8.2'
|
62
|
+
|
63
|
+
s.files = %w(README CHANGELOG Rakefile) +
|
64
|
+
Dir.glob("{bin,doc,test,lib,templates,generator,extras,website,script}/**/*") +
|
65
|
+
Dir.glob("ext/**/*.{h,c,rb}") +
|
66
|
+
Dir.glob("examples/**/*.rb") +
|
67
|
+
Dir.glob("tools/*.rb")
|
68
|
+
|
69
|
+
# s.extensions = FileList["ext/**/extconf.rb"].to_a
|
70
|
+
end
|
71
|
+
|
72
|
+
Rake::GemPackageTask.new(spec) do |p|
|
73
|
+
p.need_tar = true
|
74
|
+
p.gem_spec = spec
|
75
|
+
end
|
76
|
+
|
77
|
+
task :install do
|
78
|
+
name = "#{NAME}-#{VERS}.gem"
|
79
|
+
sh %{rake package}
|
80
|
+
sh %{sudo gem install pkg/#{name}}
|
81
|
+
end
|
82
|
+
|
83
|
+
task :uninstall => [:clean] do
|
84
|
+
sh %{sudo gem uninstall #{NAME}}
|
85
|
+
end
|
data/examples/echoc.rb
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'crbtk'
|
3
|
+
|
4
|
+
class EchoCtrl
|
5
|
+
# �R���g���[���Ŏg�p����C�x���g��`
|
6
|
+
EVT_CONNECT = 'EVT_CONNECT' # �ڑ��C�x���g
|
7
|
+
EVT_DISCONN = 'EVT_DISCONN' # �ؒf�C�x���g
|
8
|
+
EVT_SENDMSG = 'EVT_SENDMSG' # ���M�C�x���g
|
9
|
+
|
10
|
+
ECHO_PORT = 7
|
11
|
+
|
12
|
+
def initialize view
|
13
|
+
@view = view
|
14
|
+
@sock = nil
|
15
|
+
# �`���l���̏�����
|
16
|
+
@chSockIn = nil
|
17
|
+
@chSockOut = nil
|
18
|
+
@chConnect = TkChannel.new(EVT_CONNECT)
|
19
|
+
@chDisconn = TkChannel.new(EVT_DISCONN)
|
20
|
+
@chSendMsg = TkChannel.new(EVT_SENDMSG)
|
21
|
+
end
|
22
|
+
|
23
|
+
def loop
|
24
|
+
host = @chConnect.recv # �ڑ��v���҂�
|
25
|
+
@sock = TCPSocket.open(host, ECHO_PORT)
|
26
|
+
@chSockIn = IoChannel.new(@sock)
|
27
|
+
@chSockOut = IoChannel.new(@sock)
|
28
|
+
|
29
|
+
def innerLoop
|
30
|
+
Event.choose[
|
31
|
+
@chSendMsg.recvEvt.wrap{|m| # ���M�v���҂�
|
32
|
+
@chSockOut.send(m) # ���b�Z�[�W���M
|
33
|
+
m = @chSockIn.recv # ���b�Z�[�W��M�҂�
|
34
|
+
@view.set_recv(m)
|
35
|
+
innerLoop
|
36
|
+
},
|
37
|
+
@chDiscon.recvEvt.wrap{ @sock.close }].sync
|
38
|
+
end
|
39
|
+
loop
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
#
|
45
|
+
# �\���n
|
46
|
+
#
|
47
|
+
class EchoView
|
48
|
+
|
49
|
+
# �ڑ��{�^���������̏���
|
50
|
+
def connect
|
51
|
+
@host_e.background 'gray'
|
52
|
+
@host_e.state 'disabled'
|
53
|
+
@conn_b.text '�ؒf'
|
54
|
+
@conn_b.command { disconnect }
|
55
|
+
@mesg_e.background 'white'
|
56
|
+
@mesg_e.state 'normal'
|
57
|
+
@send_b.state 'normal'
|
58
|
+
|
59
|
+
CRubyTk.send(EchoCtrl::CONNECT, @host_e.value)
|
60
|
+
end
|
61
|
+
|
62
|
+
# �ؒf�{�^���������̏���
|
63
|
+
def disconnect
|
64
|
+
@host_e.background 'white'
|
65
|
+
@host_e.state 'normal'
|
66
|
+
@conn_b.text '�ڑ�'
|
67
|
+
@conn_b.command { connect }
|
68
|
+
@mesg_e.background 'gray'
|
69
|
+
@mesg_e.state 'disabled'
|
70
|
+
@send_b.state 'disabled'
|
71
|
+
|
72
|
+
CRubyTk.send(EchoCtrl::DISCONN)
|
73
|
+
end
|
74
|
+
|
75
|
+
def initialize
|
76
|
+
root = TkRoot.new { title "EchoClient" }
|
77
|
+
top = TkFrame.new(root) { background "white" }
|
78
|
+
|
79
|
+
#
|
80
|
+
# �z�X�g���F[ ][�ڑ�]
|
81
|
+
#
|
82
|
+
TkLabel.new(top) {
|
83
|
+
background "white"
|
84
|
+
text '�z�X�g��:'
|
85
|
+
grid('row'=>0, 'column'=>0, 'sticky'=>'w')
|
86
|
+
}
|
87
|
+
@host_e = TkEntry.new(top) {
|
88
|
+
grid('row'=>0, 'column'=>1, 'sticky'=>'ns', 'padx'=>5,'pady'=>5)
|
89
|
+
}
|
90
|
+
@conn_b = TkButton.new(top) {
|
91
|
+
grid('row'=>0, 'column'=>2, 'sticky'=>'e')
|
92
|
+
}
|
93
|
+
|
94
|
+
#
|
95
|
+
# ���́F[ ][���M]
|
96
|
+
#
|
97
|
+
TkLabel.new(top) {
|
98
|
+
background "white"
|
99
|
+
text '����:'
|
100
|
+
grid('row'=>1, 'column'=>0, 'sticky'=>'w')
|
101
|
+
}
|
102
|
+
@mesg_e = TkEntry.new(top) {
|
103
|
+
grid('row'=>1, 'column'=>1, 'sticky'=>'ns', 'padx'=>5,'pady'=>5)
|
104
|
+
}
|
105
|
+
@send_b = TkButton.new(top) {
|
106
|
+
text '���M'
|
107
|
+
grid('row'=>1, 'column'=>2, 'sticky'=>'e')
|
108
|
+
}
|
109
|
+
|
110
|
+
#
|
111
|
+
# �����F[ ]
|
112
|
+
#
|
113
|
+
TkLabel.new(top) {
|
114
|
+
background "white"
|
115
|
+
text '����:'
|
116
|
+
grid('row'=>2, 'column'=>0, 'sticky'=>'w')
|
117
|
+
}
|
118
|
+
@recv_m = TkMessage.new(top) {
|
119
|
+
background "white"
|
120
|
+
relief 'ridge'
|
121
|
+
aspect 1000
|
122
|
+
grid('row'=>2, 'column'=>1, 'columnspan'=>2, 'sticky'=>'news')
|
123
|
+
}
|
124
|
+
def set_recv mesg
|
125
|
+
# �����ƂƂ��ɕ\������
|
126
|
+
@recv_m.text = Time.now.to_s + ":" + mesg
|
127
|
+
end
|
128
|
+
#
|
129
|
+
# [�I��]
|
130
|
+
#
|
131
|
+
exit_b = TkButton.new(top) {
|
132
|
+
text '�I��'
|
133
|
+
grid('row'=>3, 'column'=>1, 'sticky'=>'news')
|
134
|
+
}
|
135
|
+
|
136
|
+
top.pack('fill' => 'both', 'side' => 'top')
|
137
|
+
|
138
|
+
# �e�{�^���ɑ���A�N�V������ݒ�
|
139
|
+
@send_b.command { CRubyTk.send(EchoCtrol::SENDMSG, @mesg_e.value) }
|
140
|
+
exit_b.command { exit }
|
141
|
+
|
142
|
+
disconnect # �������
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
# EchoCtrl.new(EchoView.new)
|
148
|
+
# CRubyTk.mainloop
|
data/examples/echod.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require '../lib/cruby'
|
3
|
+
|
4
|
+
def echod sch
|
5
|
+
while true
|
6
|
+
(sch.recvEvt.wrap {|ch| CRuby::Coroutine.spawn {echoProc(ch)}}).sync
|
7
|
+
end
|
8
|
+
end
|
9
|
+
def echoProc ch
|
10
|
+
while true
|
11
|
+
ch.send(ch.recv) rescue return
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
gs = TCPServer.open(10007)
|
17
|
+
echod(CRuby::TcpServerChannel.new(gs))
|
18
|
+
|
data/examples/fib.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
#
|
2
|
+
# Fibプロセスネットワーク
|
3
|
+
#
|
4
|
+
require 'cruby'
|
5
|
+
|
6
|
+
# 加算器
|
7
|
+
def add (inCh1,inCh2,outCh)
|
8
|
+
outCh.send(inCh1.recv+inCh2.recv)
|
9
|
+
add(inCh1,inCh2,outCh)
|
10
|
+
end
|
11
|
+
|
12
|
+
def add (inCh1,inCh2,outCh)
|
13
|
+
outCh.send(inCh1.recv+inCh2.recv)
|
14
|
+
add(inCh1,inCh2,outCh)
|
15
|
+
end
|
16
|
+
|
17
|
+
def copy (inCh,outCh1,outCh2)
|
18
|
+
x = inCh.recv
|
19
|
+
outCh1.send(x); outCh2.send(x)
|
20
|
+
copy(inCh,outCh1,outCh2)
|
21
|
+
end
|
22
|
+
|
23
|
+
def delay (n,inCh,outCh)
|
24
|
+
outCh.send(n)
|
25
|
+
delay(inCh.recv,inCh,outCh)
|
26
|
+
end
|
27
|
+
|
28
|
+
def fib
|
29
|
+
outCh = CRuby::Channel.new()
|
30
|
+
c1 = CRuby::Channel.new()
|
31
|
+
c2 = CRuby::Channel.new()
|
32
|
+
c3 = CRuby::Channel.new()
|
33
|
+
c4 = CRuby::Channel.new()
|
34
|
+
c5 = CRuby::Channel.new()
|
35
|
+
CRuby::Coroutine.spawn{delay(0,c4,c5)}
|
36
|
+
CRuby::Coroutine.spawn{copy(c2,c3,c4)}
|
37
|
+
CRuby::Coroutine.spawn{add(c3,c5,c1)}
|
38
|
+
CRuby::Coroutine.spawn{copy(c1,c2,outCh)}
|
39
|
+
c1.send(1)
|
40
|
+
outCh
|
41
|
+
end
|
42
|
+
|
43
|
+
ch = fib
|
44
|
+
20.times { p ch.recv }
|
45
|
+
|
data/examples/prime.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
#
|
2
|
+
# n,n+1,n+2,...を送信するチャネルを返す
|
3
|
+
#
|
4
|
+
require 'cruby'
|
5
|
+
|
6
|
+
def counter (n, ch)
|
7
|
+
ch.send(n); counter(n+1,ch)
|
8
|
+
end
|
9
|
+
|
10
|
+
def filter (p, inCh, outCh)
|
11
|
+
i = inCh.recv()
|
12
|
+
if i % p != 0 then
|
13
|
+
outCh.send(i)
|
14
|
+
end
|
15
|
+
filter(p,inCh,outCh)
|
16
|
+
end
|
17
|
+
|
18
|
+
#
|
19
|
+
# 篩にかける
|
20
|
+
#
|
21
|
+
def sieve (inCh, outCh)
|
22
|
+
ch = CRuby::Channel.new()
|
23
|
+
p = inCh.recv(); outCh.send(p)
|
24
|
+
CRuby::Coroutine.spawn { filter(p,inCh,ch) }
|
25
|
+
sieve(ch,outCh)
|
26
|
+
end
|
27
|
+
|
28
|
+
def primes n
|
29
|
+
nCh = CRuby::Channel.new()
|
30
|
+
CRuby::Coroutine.spawn { counter(2,nCh) }
|
31
|
+
pCh = CRuby::Channel.new()
|
32
|
+
CRuby::Coroutine.spawn { sieve(nCh,pCh) }
|
33
|
+
|
34
|
+
for i in 1..n
|
35
|
+
p pCh.recv()
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
primes(50)
|
data/lib/crbtk.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'tk'
|
2
|
+
require 'cruby'
|
3
|
+
|
4
|
+
# ���荞�ݗp�̗�O��`
|
5
|
+
class InterruptException < Exception
|
6
|
+
attr_reader :evtnam, :param
|
7
|
+
def initialize(evtnam,param)
|
8
|
+
@evtnam = evtnam
|
9
|
+
@param = param
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module Coroutine
|
14
|
+
def Coroutine.dispatch
|
15
|
+
while true
|
16
|
+
k = @@rdyQ.dequeue
|
17
|
+
k.call if k != nil
|
18
|
+
|
19
|
+
begin
|
20
|
+
@@io.call if @@io != nil
|
21
|
+
Coroutine.interruptible true
|
22
|
+
sleep
|
23
|
+
rescue InterruptException
|
24
|
+
Coroutine.interruptible false
|
25
|
+
# �C�x���g�z������
|
26
|
+
EventChannel.setEvent($!.evtnam, $!.param)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
assert false
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class CRubyTk
|
34
|
+
def self.mainloop
|
35
|
+
@@thread = Thread.current
|
36
|
+
Thread.new {
|
37
|
+
while true
|
38
|
+
Tk.do_one_event(Tk::EventFlag::ALL)
|
39
|
+
end
|
40
|
+
}
|
41
|
+
Coroutine.dispatch
|
42
|
+
end
|
43
|
+
|
44
|
+
# �R�[���o�b�N�̕�������Ă��
|
45
|
+
def self.send(evtnam, param = nil)
|
46
|
+
Coroutine.interrupt
|
47
|
+
@@thread.raise InterruptException.new(evtnam, param)
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
class EventChannel < Channel
|
53
|
+
@@evtTbl = Hash.new # �C�x���g�����L�[�Ƃ���e�[�u��
|
54
|
+
|
55
|
+
def initialize(evtnam)
|
56
|
+
super()
|
57
|
+
@evtnam = evtnam
|
58
|
+
@@evtTbl[evtnam] = self
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.setEvent(evtnam, param)
|
62
|
+
# �C�x���g�L���[����`���l��������o��
|
63
|
+
ch = @@evtTbl[evtnam]
|
64
|
+
p
|
65
|
+
callcc {|k| ch.input(param,k) }
|
66
|
+
end
|
67
|
+
|
68
|
+
def input(mesg, ret)
|
69
|
+
# �`���l���̎�M�҂��L���[�̃v���Z�X�����o��
|
70
|
+
item = @recvQ.dequeue
|
71
|
+
if item
|
72
|
+
item['flg'][0] = true
|
73
|
+
callcc {|k| Coroutine.enqueue(k); ret.call}
|
74
|
+
item['cont'].call(mesg) rescue p $!
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/lib/cruby.rb
ADDED
@@ -0,0 +1,197 @@
|
|
1
|
+
class CRuby::Channel
|
2
|
+
|
3
|
+
def initialize
|
4
|
+
@sendQ = CRuby::Queue.new()
|
5
|
+
@recvQ = CRuby::Queue.new()
|
6
|
+
end
|
7
|
+
|
8
|
+
def send msg
|
9
|
+
sendEvt(msg).sync
|
10
|
+
end
|
11
|
+
|
12
|
+
def recv
|
13
|
+
recvEvt.sync
|
14
|
+
end
|
15
|
+
|
16
|
+
# 送信イベントを生成する
|
17
|
+
def sendEvt msg
|
18
|
+
pollFn = Proc.new { @recvQ.poll }
|
19
|
+
doFn = Proc.new {
|
20
|
+
callcc {|k|
|
21
|
+
item = @recvQ.dequeue
|
22
|
+
item['flg'][0] = true
|
23
|
+
CRuby::Coroutine.enqueue(k)
|
24
|
+
item['cont'].call(msg)
|
25
|
+
}
|
26
|
+
}
|
27
|
+
blockFn = Proc.new {|flg,k| @sendQ.enqueue({'flg'=>flg,'msg'=>msg,'cont'=>k}) }
|
28
|
+
CRuby::Event.new([CRuby::Event::BEvt.new(pollFn, doFn, blockFn)])
|
29
|
+
end
|
30
|
+
|
31
|
+
# 受信イベントを生成する
|
32
|
+
def recvEvt
|
33
|
+
pollFn = Proc.new { @sendQ.poll }
|
34
|
+
doFn = Proc.new {
|
35
|
+
item = @sendQ.dequeue
|
36
|
+
item['flg'][0] = true
|
37
|
+
CRuby::Coroutine.enqueue(item['cont'])
|
38
|
+
item['msg']
|
39
|
+
}
|
40
|
+
blockFn = Proc.new {|flg,k| @recvQ.enqueue({'flg'=>flg,'cont'=>k}) }
|
41
|
+
CRuby::Event.new([CRuby::Event::BEvt.new(pollFn, doFn, blockFn)])
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
# 入出力とタイマー処理
|
47
|
+
module IOT
|
48
|
+
@@inTbl = Hash.new {[]} # 入力IOをキーとするテーブル
|
49
|
+
@@outTbl = Hash.new {[]} # 出力IOをキーとするテーブル
|
50
|
+
@@timerQ = []
|
51
|
+
|
52
|
+
def IOT.input io
|
53
|
+
# IOキューから入力チャネルを一つ取り出す
|
54
|
+
ich = @@inTbl[io].shift
|
55
|
+
# キューが空になればエントリを削除
|
56
|
+
if @@inTbl[io].empty? then
|
57
|
+
@@inTbl.delete(io)
|
58
|
+
end
|
59
|
+
ich.input
|
60
|
+
end
|
61
|
+
|
62
|
+
def IOT.output io
|
63
|
+
# IOキューから出力チャネルを一つ取り出す
|
64
|
+
och = @@outTbl[io].shift
|
65
|
+
# キューが空になればエントリを削除
|
66
|
+
if @@outTbl[io].empty? then
|
67
|
+
@@outTbl.delete(io)
|
68
|
+
end
|
69
|
+
och.output
|
70
|
+
end
|
71
|
+
|
72
|
+
def IOT.exec
|
73
|
+
tm = nil
|
74
|
+
# IOキューを調べる
|
75
|
+
rds = @@inTbl.keys
|
76
|
+
wds = @@outTbl.keys
|
77
|
+
nxt = @@timerQ.first
|
78
|
+
|
79
|
+
# どちらも空の場合
|
80
|
+
if rds.empty? && wds.empty? && nxt == nil then
|
81
|
+
return nil
|
82
|
+
end
|
83
|
+
if nxt then
|
84
|
+
tm = nxt[:time] - Time.now
|
85
|
+
if tm < 0 then
|
86
|
+
tm = 0
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
CRuby::Coroutine.interruptible true # 割り込み可能にセット
|
91
|
+
ds = select(rds,wds,nil,tm)
|
92
|
+
CRuby::Coroutine.interruptible false # もとに戻す
|
93
|
+
if (ds == nil)
|
94
|
+
@@timerQ.shift
|
95
|
+
nxt[:chan].timeout
|
96
|
+
else
|
97
|
+
# 入力処理
|
98
|
+
ds[0].each {|io| input(io) }
|
99
|
+
# 出力処理
|
100
|
+
ds[1].each {|io| output(io) }
|
101
|
+
end
|
102
|
+
CRuby::Coroutine.dispatch
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
class CRuby::IoChannel < CRuby::Channel
|
107
|
+
include IOT
|
108
|
+
|
109
|
+
attr_reader :io
|
110
|
+
|
111
|
+
def initialize io
|
112
|
+
super()
|
113
|
+
@io = io
|
114
|
+
end
|
115
|
+
|
116
|
+
# 送信イベントを生成する
|
117
|
+
def sendEvt msg
|
118
|
+
pollFn = Proc.new { false }
|
119
|
+
blockFn = Proc.new {|flg,k|
|
120
|
+
if flg[0] == true then
|
121
|
+
CRuby::Coroutine.dispatch
|
122
|
+
end
|
123
|
+
@@outTbl[@io] <<= self
|
124
|
+
@sendQ.enqueue({'flg'=>flg,'msg'=>msg,'cont'=>k})
|
125
|
+
}
|
126
|
+
CRuby::Event.new([CRuby::Event::BEvt.new(pollFn, nil, blockFn)])
|
127
|
+
end
|
128
|
+
|
129
|
+
# 受信イベントを生成する
|
130
|
+
def recvEvt
|
131
|
+
pollFn = Proc.new { false }
|
132
|
+
blockFn = Proc.new {|flg,k|
|
133
|
+
if flg[0] == true then
|
134
|
+
CRuby::Coroutine.dispatch
|
135
|
+
end
|
136
|
+
@@inTbl[@io] <<= self
|
137
|
+
@recvQ.enqueue({'flg'=>flg,'cont'=>k})
|
138
|
+
}
|
139
|
+
CRuby::Event.new([CRuby::Event::BEvt.new(pollFn, nil, blockFn)])
|
140
|
+
end
|
141
|
+
|
142
|
+
# 入力処理を行う
|
143
|
+
def input
|
144
|
+
# EOFの処理
|
145
|
+
msg = @io.readpartial(4096)
|
146
|
+
# チャネルの受信待ちキューのプロセスを取り出す
|
147
|
+
item = @recvQ.dequeue
|
148
|
+
item['flg'][0] = true
|
149
|
+
item['cont'].call(msg)
|
150
|
+
end
|
151
|
+
|
152
|
+
# 出力処理を行う
|
153
|
+
def output
|
154
|
+
# チャネルの送信待ちキューのプロセスを取り出す
|
155
|
+
item = @sendQ.dequeue
|
156
|
+
item['flg'][0] = true
|
157
|
+
CRuby::Coroutine.enqueue(item['cont'])
|
158
|
+
io.write(item['msg'])
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
|
163
|
+
class CRuby::TimerChannel < CRuby::Channel
|
164
|
+
include IOT
|
165
|
+
|
166
|
+
# 送信イベントを生成する
|
167
|
+
def sendEvt msg
|
168
|
+
pollFn = Proc.new { false }
|
169
|
+
blockFn = Proc.new {|flg,k|
|
170
|
+
if flg[0] == true then
|
171
|
+
CRuby::Coroutine.dispatch
|
172
|
+
end
|
173
|
+
@@timerQ <<= {:time=>Time.now+msg,:chan=>self}
|
174
|
+
@@timerQ.sort! {|a,b| a[:time]<=>b[:time]}
|
175
|
+
@sendQ.enqueue({'flg'=>flg,'msg'=>msg,'cont'=>k})
|
176
|
+
}
|
177
|
+
CRuby::Event.new([CRuby::Event::BEvt.new(pollFn, nil, blockFn)])
|
178
|
+
end
|
179
|
+
|
180
|
+
def timeout
|
181
|
+
item = @sendQ.dequeue
|
182
|
+
item['flg'][0] = true
|
183
|
+
CRuby::Coroutine.enqueue(item['cont'])
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
class CRuby::TcpServerChannel < CRuby::IoChannel
|
188
|
+
include IOT
|
189
|
+
|
190
|
+
def input
|
191
|
+
s = @io.accept
|
192
|
+
# チャネルの受信待ちキューのプロセスを取り出す
|
193
|
+
item = @recvQ.dequeue
|
194
|
+
item['flg'][0] = true
|
195
|
+
item['cont'].call(CRuby::IoChannel.new(s))
|
196
|
+
end
|
197
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
class CRuby::Queue
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@queue = []
|
7
|
+
end
|
8
|
+
|
9
|
+
def empty?
|
10
|
+
@queue == []
|
11
|
+
end
|
12
|
+
|
13
|
+
def enqueue val
|
14
|
+
@queue.push val
|
15
|
+
end
|
16
|
+
|
17
|
+
def dequeue
|
18
|
+
return @queue.shift
|
19
|
+
end
|
20
|
+
|
21
|
+
# このメソッドは別のクラスに移すべき
|
22
|
+
def poll
|
23
|
+
# dirtyなエントリを削除
|
24
|
+
@queue.delete_if {|item| item['flg']==[true] }
|
25
|
+
not empty?
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
module CRuby::Coroutine
|
31
|
+
@@rdyQ = CRuby::Queue.new
|
32
|
+
|
33
|
+
@@flag = false # 割り込み可能フラグ
|
34
|
+
@@mutex = Mutex.new
|
35
|
+
@@condv = ConditionVariable.new
|
36
|
+
|
37
|
+
def self.rdyq
|
38
|
+
p @@rdyQ
|
39
|
+
end
|
40
|
+
|
41
|
+
# メインスレッドに割り込みをかける
|
42
|
+
def self.interrupt
|
43
|
+
# 割り込み可能になるまで待つ
|
44
|
+
@@mutex.synchronize {
|
45
|
+
while not @@flag
|
46
|
+
@@condv.wait(@@mutex)
|
47
|
+
end
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
# 割り込み可能フラグをセットする
|
52
|
+
def self.interruptible flag
|
53
|
+
@@mutex.synchronize {
|
54
|
+
if flag
|
55
|
+
@@condv.signal
|
56
|
+
end
|
57
|
+
@@flag = flag
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.empty?
|
62
|
+
@@rdyQ.empty?
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.dispatch
|
66
|
+
k = @@rdyQ.dequeue
|
67
|
+
if k != nil then
|
68
|
+
k.call
|
69
|
+
assert false
|
70
|
+
end
|
71
|
+
IOT.exec
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.spawn &f
|
75
|
+
callcc {|parentK|
|
76
|
+
@@rdyQ.enqueue(parentK)
|
77
|
+
f.call # 例外捕捉する必要あり
|
78
|
+
CRuby::Coroutine.dispatch
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.enqueue k
|
83
|
+
@@rdyQ.enqueue(k)
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
data/lib/cruby/event.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
class CRuby::Event
|
2
|
+
attr_reader :evts
|
3
|
+
|
4
|
+
class BEvt
|
5
|
+
attr_reader :pollFn, :doFn, :blockFn
|
6
|
+
def initialize (pollFn, doFn, blockFn)
|
7
|
+
@pollFn = pollFn
|
8
|
+
@doFn = doFn
|
9
|
+
@blockFn = blockFn
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize bevts
|
14
|
+
@evts = bevts
|
15
|
+
end
|
16
|
+
|
17
|
+
def sync
|
18
|
+
bevt = @evts.find {|evt| evt.pollFn.call() }
|
19
|
+
if bevt then
|
20
|
+
bevt.doFn.call()
|
21
|
+
else
|
22
|
+
callcc {|k|
|
23
|
+
flag = [false]
|
24
|
+
@evts.each {|evt| evt.blockFn.call(flag,k) }
|
25
|
+
CRuby::Coroutine.dispatch()
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# 新しいイベントを生成する
|
31
|
+
def wrap &f
|
32
|
+
CRuby::Event.new(@evts.map {|evt|
|
33
|
+
doFn = Proc.new { f.call(evt.doFn.call()) }
|
34
|
+
blockFn = Proc.new {|flg,k| # k はsync実行後の継続
|
35
|
+
callcc {|kk| # kk はblockFn実行後の継続
|
36
|
+
k.call(f.call(callcc {|kkk| # kkk は元のblockFn実行後の継続
|
37
|
+
kk.call(evt.blockFn.call(flg,kkk))
|
38
|
+
}))}}
|
39
|
+
BEvt.new(evt.pollFn,doFn,blockFn)
|
40
|
+
})
|
41
|
+
end
|
42
|
+
|
43
|
+
# イベントを選択する
|
44
|
+
def self.choose evts
|
45
|
+
CRuby::Event.new((evts.map {|ev| ev.evts}).flatten)
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.select evts
|
49
|
+
CRuby::Event.choose(evts).sync
|
50
|
+
end
|
51
|
+
end
|
data/test/cruby_test.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
|
3
|
+
class CrubyTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_truth
|
9
|
+
assert true
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_cruby_queue
|
13
|
+
q = CRuby::Queue.new
|
14
|
+
assert(q.empty?)
|
15
|
+
|
16
|
+
q.enqueue("foo")
|
17
|
+
assert(!q.empty?)
|
18
|
+
assert_equal("foo", q.dequeue)
|
19
|
+
assert(q.empty?)
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_cruby_coroutine
|
23
|
+
CRuby::Coroutine.spawn { assert(true) }
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_cruby_channel
|
27
|
+
ch = CRuby::Channel.new
|
28
|
+
|
29
|
+
CRuby::Coroutine.spawn { ch.send("hello"); assert(true) }
|
30
|
+
assert_equal("hello", ch.recv)
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_cruby_select
|
34
|
+
ch1 = CRuby::Channel.new
|
35
|
+
ch2 = CRuby::Channel.new
|
36
|
+
CRuby::Coroutine.spawn { ch2.send("hello") }
|
37
|
+
assert_equal("hello",
|
38
|
+
CRuby::Event.select([
|
39
|
+
ch1.recvEvt.wrap {|m| assert(false); m},
|
40
|
+
ch2.recvEvt.wrap {|m| assert(true); m}
|
41
|
+
])
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_cruby_timer
|
46
|
+
timer = CRuby::TimerChannel.new
|
47
|
+
timer.send(0.5)
|
48
|
+
assert(true)
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_cruby_stdout
|
52
|
+
chout = CRuby::IoChannel.new(STDOUT)
|
53
|
+
chout.send("\nhoge\n")
|
54
|
+
assert(true)
|
55
|
+
end
|
56
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.0
|
3
|
+
specification_version: 1
|
4
|
+
name: cruby
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.0.1
|
7
|
+
date: 2006-11-08 00:00:00 +09:00
|
8
|
+
summary: CRuby is a library that provids concurrent programming in Ruby
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: hattori@isp.co.jp
|
12
|
+
homepage: http://cruby.rubyforge.org
|
13
|
+
rubyforge_project: cruby
|
14
|
+
description: CRuby is a library that provids concurrent programming in Ruby
|
15
|
+
autorequire: cruby
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- hattori
|
31
|
+
files:
|
32
|
+
- README
|
33
|
+
- CHANGELOG
|
34
|
+
- Rakefile
|
35
|
+
- test/cruby_test.rb
|
36
|
+
- test/test_helper.rb
|
37
|
+
- lib/crbtk.rb
|
38
|
+
- lib/cruby
|
39
|
+
- lib/cruby.rb
|
40
|
+
- lib/cruby/version.rb
|
41
|
+
- lib/cruby/coroutine.rb
|
42
|
+
- lib/cruby/event.rb
|
43
|
+
- lib/cruby/channel.rb
|
44
|
+
- examples/prime.rb
|
45
|
+
- examples/fib.rb
|
46
|
+
- examples/echoc.rb
|
47
|
+
- examples/echod.rb
|
48
|
+
test_files: []
|
49
|
+
|
50
|
+
rdoc_options:
|
51
|
+
- --quiet
|
52
|
+
- --title
|
53
|
+
- cruby documentation
|
54
|
+
- --opname
|
55
|
+
- index.html
|
56
|
+
- --line-numbers
|
57
|
+
- --main
|
58
|
+
- README
|
59
|
+
- --inline-source
|
60
|
+
- --exclude
|
61
|
+
- ^(examples|extras)/
|
62
|
+
extra_rdoc_files:
|
63
|
+
- README
|
64
|
+
- CHANGELOG
|
65
|
+
executables: []
|
66
|
+
|
67
|
+
extensions: []
|
68
|
+
|
69
|
+
requirements: []
|
70
|
+
|
71
|
+
dependencies: []
|
72
|
+
|