File: facade.tcl

package info (click to toggle)
tcllib 2.0%2Bdfsg-4
  • links: PTS
  • area: main
  • in suites: forky, trixie
  • size: 83,572 kB
  • sloc: tcl: 306,798; ansic: 14,272; sh: 3,035; xml: 1,766; yacc: 1,157; pascal: 881; makefile: 124; perl: 84; f90: 84; python: 33; ruby: 13; php: 11
file content (234 lines) | stat: -rw-r--r-- 6,487 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
# -*- tcl -*-
# # ## ### ##### ######## #############
# (C) 2011 Andreas Kupries

# Facade wrapping around some other channel. All operations on the
# facade are delegated to the wrapped channel. This makes it useful
# for debugging of Tcl's activity on a channel. While a transform can
# be used for that as well it does not have access to some things of
# the base-channel, i.e. all the event managment is not visible to it,
# whereas the facade has access to even this.

# @@ Meta Begin
# Package tcl::chan::facade 1.0.2
# Meta as::author {Colin McCormack}
# Meta as::author {Andreas Kupries}
# Meta as::copyright 2011
# Meta as::license BSD
# Meta description Facade wrapping around some other channel. All
# Meta description operations on the facade are delegated to the
# Meta description wrapped channel. This makes it useful for debugging
# Meta description of Tcl's activity on a channel. While a transform
# Meta description can be used for that as well it does not have
# Meta description access to some things of the base-channel, i.e. all
# Meta description the event managment is not visible to it, whereas
# Meta description the facade has access to even this.
# Meta platform tcl
# Meta require TclOO
# Meta require tcl::chan::core
# Meta require {Tcl 8.5}
# @@ Meta End

# # ## ### ##### ######## #############
## TODO document the special options of the facade
## TODO log integration.
## TODO document that facada takes ownership of the channel.

package require Tcl 8.5 9
package require TclOO
package require logger
package require tcl::chan::core

# # ## ### ##### ######## #############

namespace eval ::tcl::chan {}

logger::initNamespace ::tcl::chan::facade
proc ::tcl::chan::facade {args} {
    return [::chan create {read} [facade::implementation new {*}$args]]
}

# # ## ### ##### ######## #############

oo::class create ::tcl::chan::facade::implementation {
    superclass ::tcl::chan::core ; # -> initialize, finalize.

    # # ## ### ##### ######## #############

    # We are not using the standard event handling class, because here
    # it will not be timer-driven. We propagate anything related to
    # events to the wrapped channel instead and let it handle things.

    constructor {thechan} {
	# Access to the log(ger) commands.
	namespace path [list {*}[namespace path] ::tcl::chan::facade]

	set chan $thechan

	# set some configuration data
	set created [clock milliseconds]
	set used 0
	set user ""	;# user data - freeform

	# validate args
	if {$chan eq [self]} {
	    return -code error "recursive chan!  No good."
	} elseif {$chan eq ""} {
	    return -code error "Needs a chan argument"
	}

	set blocking [::chan configure $chan -blocking]
	return
    }

    destructor {
	log::debug {[self] destroyed}
	if {[catch { ::chan close $chan } e o]} {
	    log::debug {failed to close $chan [self] because "$e" ($o)}
	}
	return
    }

    variable chan used user created blocking

    method initialize {myself mode} {
	log::debug {$myself initialize $chan $mode}
	log::debug {$chan configured: ([::chan configure $chan])}
	return [next $chan $mode]
    }

    method finalize {myself} {
	log::debug {$myself finalize $chan}
	catch {::chan close $chan}
	catch {next $myself}
	catch {my destroy}
	return
    }

    method blocking {myself mode} {
	if {[catch {
	    ::chan configure $chan -blocking $mode
	    set blocking $mode
	} e o]} {
	    log::debug {$myself blocking $chan $mode -> error $e ($o)}
	} else {
	    log::debug {$myself blocking $chan $mode -> $e}
	}
	return
    }

    method watch {myself requestmask} {
	log::debug {$myself watch $chan $requestmask}

	if {"read" in $requestmask} {
	    fileevent readable $chan [my Callback Readable $myself]
	} else {
	    fileevent readable $chan {}
	}

	if {"write" in $requestmask} {
	    fileevent writable $chan [my Callback Writable $myself]
	} else {
	    fileevent writable $chan {}
	}
	return
    }

    method read {myself n} {
	log::debug {$myself read $chan begin eof: [::chan eof $chan], blocked: [::chan blocked $chan]}
	set used [clock milliseconds]

	if {[catch {
	    set data [::chan read $chan $n]
	} e o]} {
	    log::error {$myself read $chan $n -> error $e ($o)}
	} else {
	    log::debug {$myself read $chan $n -> [string length $data] bytes: [string map {\n \\n} "'[string range $data 0 20]...[string range $data end-20 end]"]'}
	    log::debug {$myself read $chan eof     = [::chan eof     $chan]}
	    log::debug {$myself read $chan blocked = [::chan blocked $chan]}
	    log::debug {$chan configured: ([::chan configure $chan])}

	    set gone [catch {chan eof $chan} eof]
	    if {
		($data eq {}) &&
		!$gone && !$eof && !$blocking
	    } {
		log::error {$myself EAGAIN}
		return -code error EAGAIN
	    }
	}

	log::debug {$myself read $chan result: [string length $data] bytes}
	return $data
    }

    method write {myself data} {
	log::debug {$myself write $chan [string length $data] / [::chan pending output $chan] / [::chan pending output $myself]}
	set used [clock milliseconds]
	::chan puts -nonewline $chan $data
	return [string length $data]
    }

    method configure {myself option value} {
	log::debug {[self] configure $myself $option -> $value}

	if {$option eq "-user"} {
	    set user $value
	    return
	}

	::chan configure $fd $option $value
	return
    }

    method cget {myself option} {
	switch -- $option {
	    -self    { return [self]   }
	    -fd      { return $chan    }
	    -used    { return $used    }
	    -created { return $created }
	    -user    { return $user    }
	    default  {
		return [::chan configure $chan $option]
	    }
	}
    }

    method cgetall {myself} {
	set result [::chan configure $chan]
	lappend result \
	    -self    [self] \
	    -fd      $chan \
	    -used    $used \
	    -created $created \
	    -user $user

	log::debug {[self] cgetall $myself -> $result}
	return $result
    }

    # # ## ### ##### ######## #############

    # Internals. Methods. Event generation.
    method Readable {myself} {
	log::debug {$myself readable $chan - [::chan pending input $chan]}
	::chan postevent $myself read
	return
    }

    method Writable {myself} {
	log::debug {$myself writable $chan - [::chan pending output $chan]}
	::chan postevent $myself write
	return
    }

    method Callback {method args} {
	list [uplevel 1 {namespace which my}] $method {*}$args
    }

    # # ## ### ##### ######## #############
}

# # ## ### ##### ######## #############
package provide tcl::chan::facade 1.0.2
return