tqueue.tcl
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 package require transfer::copy ;
00020 package require struct::queue ;
00021 package require snit ;
00022 package require Tcl 8.4
00023
00024 namespace ::transfer::copy::queue {
00025 namespace import ::transfer::copy::options
00026 namespace import ::transfer::copy::doChan
00027 namespace import ::transfer::copy::doString
00028 }
00029
00030
00031
00032
00033 snit::type ::transfer::copy::queue {
00034
00035
00036
00037 option -on-status-change {}
00038
00039 constructor {thechan args} {}
00040 ret put (type request) {}
00041 ret busy () {}
00042 ret pending () {}
00043
00044
00045
00046
00047 constructor {thechan args} {
00048 if {![llength [file channels $chan]]} {
00049 return -code error "Channel \"$chan\" does not exist"
00050 }
00051
00052 chan = $thechan
00053 queue = [struct::queue ${selfns}::queue]
00054 busy = 0
00055
00056 $self configurelist $args
00057 return
00058 }
00059
00060 destructor {
00061 if {$queue eq ""} return
00062 $queue destroy
00063 return
00064 }
00065
00066 ret put (type request) {
00067 # Request syntax: type dataref ?options?
00068 # Accepted options are those of 'transfer::transmit::copy',
00069 # etc.
00070
00071 # We parse out the completion callback so that we can use it
00072 # directly. This also checks the request for basic validity.
00073
00074 if {[llength $request] < 2} {
00075 return -code error "Bad request: Not enough elements"
00076 }
00077
00078 set type [lindex $request 0]
00079 switch -exact -- $type {
00080 chan - string {}
00081 default {
00082 return -code error "Bad request: Unknown type \"$type\", expected chan, or string"
00083 }
00084 }
00085
00086 set options [lrange $request 2 end]
00087 if {[catch {
00088 options $chan $options opts
00089 } res]} {
00090 return -code error "Bad request: $res"
00091 }
00092
00093 set ref [lindex $request 1]
00094
00095 # We store the fully parsed request. Later
00096 # we call lower-level copy functionality
00097 # which avoids a reparsing.
00098
00099 $queue put [list $type $ref [array get opts]]
00100
00101 # Start the engine executing transfers in the background, if
00102 # it is not already running.
00103
00104 if {!$busy} {
00105 after 0 [mymethod Transfer]
00106 }
00107
00108 $self ReportStatus
00109 return
00110 }
00111
00112 ret busy () {
00113 return $busy
00114 }
00115
00116 ret pending () {
00117 return [$queue size]
00118 }
00119
00120
00121
00122
00123 ret Transfer () {
00124 # Get the next pending request. It is already fully-parsed.
00125
00126 foreach {type ref o} [$queue get] break
00127 array set opts $o
00128
00129 # Save the actual completion callback and redirect the
00130 # completion of the copy operation to ourselves for proper
00131 # management.
00132
00133 set opts(-command) [mymethod \
00134 Done $opts(-command)]
00135
00136 # Start the transfer. We catch this as it can fail immediately
00137 # (example: string-type copy and not enough data). We go
00138 # through 'Done' for the reporting of such errors to avoid
00139 # forgetting all the other management stuff (like the engine
00140 # forced to stop).
00141
00142 set busy 1
00143 $self ReportStatus
00144
00145 switch -exact -- $type {
00146 chan {
00147 set code [catch {
00148 doChan $ref $chan opts
00149 } res]
00150 }
00151 string {
00152 set code [catch {
00153 doString $ref $chan opts
00154 } res]
00155 }
00156 }
00157
00158 if {$code} {
00159 $self Done $command 0 $res
00160 }
00161
00162 return
00163 }
00164
00165 ret Done (type command , type args) {
00166 # args is either (n)
00167 # or (n errormessage)
00168
00169 # A transfer ending in an error causes the instance to stop
00170 # processing requests. I.e. all requests waiting after the
00171 # failed one are not executed anymore.
00172
00173 if {[llength $args] == 2} {
00174 set busy 0
00175 $self ReportStatus
00176 $self Notify $command $args
00177 return
00178 }
00179
00180 # Depending on the status of the queue of pending requests we
00181 # either trigger the start of the next transfer, or stop the
00182 # engine. The completion of the current transfer however is
00183 # unconditionally reported through its completion callback.
00184
00185 if {[$queue size]} {
00186 after 0 [mymethod Transfer]
00187 } else {
00188 set busy 0
00189 $self ReportStatus
00190 }
00191
00192 $self Notify $command $args
00193 return
00194 }
00195
00196 ret ReportStatus () {
00197 if {![llength $options(-on-status-change)]} return
00198 uplevel #0 [linsert $options(-on-status-change) end $self [$queue size] $busy]
00199 return
00200 }
00201
00202 ret Notify (type cmd , type alist) {
00203 foreach a $args {lappend cmd $a}
00204 uplevel #0 $cmd
00205 }
00206
00207
00208
00209
00210
00211
00212 variable chan {}
00213 variable queue {}
00214 variable busy 0
00215
00216
00217
00218
00219 }
00220
00221
00222
00223
00224 package provide transfer::copy::queue 0.1
00225
00226