tqueue.tcl

Go to the documentation of this file.
00001 /*  -*- tcl -*-*/
00002 /*  ### ### ### ######### ######### #########*/
00003 /** 
00004  * Transfer class built on top of the basic facilities. Accepts many
00005  * transfer requests, any time, and executes them serially. Each
00006  * request has its own progress and completion commands.
00007  *
00008  * Note: The output channel used is part of the queue, and not
00009  *       contained in the transfer requests themselves. Otherwise
00010  *       we would not need a queue and serialized execution.
00011  *
00012  * Instances also have a general callback to report the instance status
00013  * (#pending transfer requests, busy).
00014  */
00015 
00016 /*  ### ### ### ######### ######### #########*/
00017 /*  Requirements*/
00018 
00019 package require transfer::copy ; /*  Basic transfer facilities*/
00020 package require struct::queue  ; /*  Request queue*/
00021 package require snit           ; /*  OO system*/
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 /*  Implementation*/
00032 
00033 snit::type ::transfer::copy::queue {
00034     /*  ### ### ### ######### ######### #########*/
00035     /*  API*/
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     /*  Implementation*/
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     /*  Internal helper commands*/
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     /*  Data structures*/
00209     /*  - Channel the transfered data is written to*/
00210     /*  - Queue of pending requests.*/
00211 
00212     variable chan  {}
00213     variable queue {}
00214     variable busy  0
00215 
00216     /** 
00217      * ### ### ### ######### ######### #########
00218  */
00219 }
00220 
00221 /*  ### ### ### ######### ######### #########*/
00222 /*  Ready*/
00223 
00224 package provide transfer::copy::queue 0.1
00225 
00226 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1