cruby 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|