ten::handle class
[scpubgit/TenDotTcl.git] / ten / ten.tcl
CommitLineData
0e93bc12 1package require Tcl 8.5
3c6a0ce7 2
3package require snit
0e93bc12 4package require json::write
5package require json
3c6a0ce7 6
7namespace eval ::ten:: {
8 set library [file dirname [info script]]
0e93bc12 9
10 proc nofuture {args} {}
3c6a0ce7 11}
12
13snit::type ten::connector::perl {
14
15 method connect {} {
16 set conn_fh [open {|object-remote-node} r+]
b0dd2971 17 set firstline [gets $conn_fh]
3c6a0ce7 18 switch $firstline {
19 Shere {}
20 default { error "Expected Shere, got $firstline" }
21 }
22
23 set channel [ten::read_channel %AUTO% -fh $conn_fh]
24
25 set conn [
26 ten::connection %AUTO% -send_to_fh $conn_fh -read_channel $channel
27 ]
28
29 return $conn
30 }
b0dd2971 31}
32
33snit::type ten::connection {
34
35 option -send_to_fh
36 option -read_channel
37
38 constructor {args} {
39 $self configurelist $args
40 fconfigure $options(-send_to_fh) -buffering none
41 $options(-read_channel) configurelist [list \
42 -on_line_call [ mymethod Receive ] \
43 -on_close_call [ mymethod ChannelClosed ] \
44 ]
45 }
46
0e93bc12 47 method Receive {line} {
48 set tcl_line [ json::json2dict $line ]
49 set type [ lindex $tcl_line 0 ]
50 set rest [ lrange $tcl_line 1 end ]
51 $self "receive_$type" {*}$rest
52 }
53
54 method receive_call_free {future_id id args} {
55 $self receive_call $future_id $id "" {*}$args
56 $self receive_free $id
57 }
58
59 method receive_call {future_id id args} {
60 if {$future_id == "NULL"} {
61 set future ten::nofuture
62 } else {
63 set future $future_id
64 }
65 $self Invoke $future $id {*}$args
66 }
67
68 method receive_free {id} {
69 $id free
70 }
71
72 method Invoke {future local ctx method args} {
73 set result [$local $method {*}$args]
74 $future done $result
b0dd2971 75 }
76
77 method ChannelClosed {} {
78 error "Fail"
79 }
80
81 method send {message_type args} {
82 set future [ten::future %AUTO%]
0e93bc12 83 set call_args [list \
84 [ json::write string $message_type ] \
85 [ json::write string $future ] \
86 {*}$args
87 ]
88 $self Send $call_args
89 return $future
90 }
91
92 method Send {to_send} {
93 set send_this [ json::write array {*}$to_send ]
94 puts $options(-send_to_fh) $send_this
b0dd2971 95 }
96}
97
98snit::type ten::future {
99
c7db0954 100 variable callbacks ""
b0dd2971 101 variable is_ready 0
102 variable result ""
103 variable failure ""
0e93bc12 104 variable retain_count 1
b0dd2971 105
106 method done {args} {
107 if [$self is_ready] {
108 error "Future $self already completed"
109 }
110 set result $args
111 $self MarkReady
112 }
113
114 method fail {args} {
115 if [$self is_ready] {
116 error "Future $self already completed"
117 }
118 set failure $args
119 $self MarkReady
120 }
121
122 method on_ready {cb_code} {
c7db0954 123 if [$self is_ready] {
124 eval [concat $cb_code $self]
125 } else {
126 lappend callbacks [list ready $cb_code]
127 }
b0dd2971 128 }
129
130 method on_done {cb_code} {
c7db0954 131 if [llength $result] {
132 eval [concat $cb_code [list $result]]
133 } else {
134 lappend callbacks [list done $cb_code]
135 }
b0dd2971 136 }
137
138 method on_fail {cb_code} {
c7db0954 139 if [llength $failure] {
140 eval [concat $cb_code [list $failure]]
141 } else {
142 lappend callbacks [list fail $cb_code]
143 }
b0dd2971 144 }
145
146 method MarkReady {} {
147 set is_ready 1
148 foreach cb $callbacks {
149 set cb_type [lindex $cb 0]
150 set cb_code [lindex $cb 1]
151 switch $cb_type {
152 ready { eval [concat $cb_code $self] }
153 done {
154 if ![$self is_failure] {
155 eval [concat $cb_code [list $result]]
156 }
157 }
158 failed {
159 if [$self is_failure] {
160 eval [concat $cb_code [list $failure]]
161 }
162 }
163 }
164 }
165 }
166
167 method AssertReady {} {
168 if {!$is_ready} {
169 error "Future not ready"
170 }
171 }
172
173 method is_ready {} { return $is_ready }
174
175 method get {} {
176 $self AssertReady
177 if [$self is_failure] {
178 error $failure
179 }
180 return $result
181 }
182
183 method is_failure {} {
184 $self AssertReady
185 if [llength $failure] {
186 return 1
187 }
188 return 0
189 }
190
191 method failure {} {
192 $self AssertReady
193 return $failure
194 }
195
0e93bc12 196 method await_ready {} {
b0dd2971 197 vwait "${selfns}::is_ready"
198 }
0e93bc12 199
47f04c56 200 method await_get {} {
201 $self retain
202 $self await_ready
203 if [$self is_failure] {
204 set err [$self failure]
205 $self free
206 error $err
207 }
208 set res [$self get]
209 $self free
210 return $res
211 }
212
0e93bc12 213 method retain {} {
214 incr retain_count
215 }
216
217 method free {} {
218 set retain_count [ expr $retain_count - 1 ]
219 if {$retain_count == 0} {
220 $self destroy
221 }
222 }
b0dd2971 223}
3c6a0ce7 224
225snit::type ten::read_channel {
226 option -fh
227 option -on_close_call
228 option -on_line_call
229
230 constructor {args} {
231 $self configurelist $args
232 fconfigure $options(-fh) -blocking 0
233 fileevent $options(-fh) readable [mymethod ReceiveData]
234 }
235
236 method ReceiveData {} {
237 set chan $options(-fh)
238 if [eof $chan] {
239 if [llength $options(-on_close_call)] {
240 eval $options(-on_close_call)
241 }
242 } else {
243 if [llength $options(-on_line_call)] {
e63453e3 244 while {[string length [set line [gets $chan]]] > 0} {
0e93bc12 245 eval [concat $options(-on_line_call) [ list $line ]]
3c6a0ce7 246 }
247 }
248 }
249 }
250}
251
2cecba5d 252snit::type ten::handle {
253 option -connection
254 option -id
255
256 method call {name args} {
257 return [[$self start $name {*}$args] await_get]
258 }
259
260 method start {name args} {
261 $options(-connection) send call [json::write string $options(-id)] 0 [json::write string $name] {*}$args
262 }
263}
264
b0dd2971 265package provide ten 0.0.1