pool.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 package require cmdline
00018
00019 namespace ::struct {}
00020 namespace ::struct::pool {
00021
00022
00023 variable pools {}
00024
00025
00026
00027 variable counter 0
00028
00029
00030 variable commands {add clear destroy info maxsize release remove request}
00031
00032
00033
00034
00035 variable Errors
00036 array Errors = {
00037 BAD_SUBCMD {bad subcommand "%s": must be %s}
00038 DUPLICATE_ITEM_IN_ARGS {Duplicate item `%s' in arguments.}
00039 DUPLICATE_POOLNAME {The pool `%s' already exists.}
00040 EXCEED_MAXSIZE "This command would increase the total number of items\
00041 \nbeyond the maximum size of the pool. No items registered."
00042 FORBIDDEN_ALLOCID "The value -1 is not allowed as an allocID."
00043 INVALID_POOLSIZE {The pool currently holds %s items.\
00044 Can't maxsize = to a value less than that.}
00045 ITEM_ALREADY_IN_POOL {`%s' already is a member of the pool. No items registered.}
00046 ITEM_NOT_IN_POOL {`%s' is not a member of %s.}
00047 ITEM_NOT_ALLOCATED {Can't release `%s' because it isn't allocated.}
00048 ITEM_STILL_ALLOCATED {Can't remove `%s' because it is still allocated.}
00049 NONINT_REQSIZE {The second argument must be a positive integer value}
00050 SOME_ITEMS_NOT_FREE {Couldn't %s `%s' because some items are still allocated.}
00051 UNKNOWN_ARG {Unknown argument `%s'}
00052 UNKNOWN_POOL {Nothing known about `%s'.}
00053 VARNAME_EXISTS "A variable `::struct::pool::%s' already exists."
00054 WRONG_INFO_TYPE "Expected second argument to be one of:\
00055 \n allitems, allocstate, cursize, freeitems, maxsize,\
00056 \nbut received: `%s'."
00057 WRONG_NARGS {Wrong nr. of arguments.}
00058 }
00059
00060 namespace export pool
00061 }
00062
00063
00064
00065 ret ::struct::pool::lmember (type list , type element) {
00066 if { [lsearch -exact $list $element] >= 0 } {
00067 return 1
00068 } else {
00069 return 0
00070 }
00071 }
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123 ret ::struct::pool::create ( optional poolname ="" , optional maxsize =10 ) {
00124 variable pools
00125 variable counter
00126 variable Errors
00127
00128 # check maxsize argument
00129 if { ![string equal $maxsize 10] } {
00130 if { ![regexp {^\+?[1-9][0-9]*$} $maxsize] } {
00131 return -code error $Errors(NONINT_REQSIZE)
00132 }
00133 }
00134
00135 # create a name if no name was supplied
00136 if { [string length $poolname]==0 } {
00137 incr counter
00138 set poolname pool$counter
00139 set incrcnt 1
00140 }
00141
00142 # check whether there exists a pool named $poolname
00143 if { [lmember $pools $poolname] } {
00144 if { [::info exists incrcnt] } {
00145 incr counter -1
00146 }
00147 return -code error [format $Errors(DUPLICATE_POOLNAME) $poolname]
00148 }
00149
00150 # check whether the namespace variable exists
00151 if { [::info exists ::struct::pool::$poolname] } {
00152 if { [::info exists incrcnt] } {
00153 incr counter -1
00154 }
00155 return -code error [format $Errors(VARNAME_EXISTS) $poolname]
00156 }
00157
00158 variable $poolname
00159
00160 # register
00161 lappend pools $poolname
00162
00163 # create and initialize the new pool data structure
00164 upvar #0 ::struct::pool::$poolname pool
00165 set pool(freeitems) {}
00166 set pool(maxsize) $maxsize
00167 set pool(cursize) 0
00168
00169 # the array that holds allocation state
00170 upvar #0 ::struct::pool::Allocstate_$poolname state
00171 array set state {}
00172
00173 # create a pool-object command and map it to the pool commands
00174 interp alias {} ::$poolname {} ::struct::pool::poolCmd $poolname
00175 return $poolname
00176 }
00177
00178
00179
00180
00181
00182 ret ::struct::pool::pool ( optional poolname ="" , optional maxsize =10 ) {
00183 ::struct::pool::create $poolname $maxsize
00184 }
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206 ret ::struct::pool::poolCmd (type poolname , type subcmd , type args) {
00207 variable Errors
00208
00209 # check the subcmd argument
00210 if { [lsearch -exact $::struct::pool::commands $subcmd] == -1 } {
00211 set optlist [join $::struct::pool::commands ", "]
00212 set optlist [linsert $optlist "end-1" "or"]
00213 return -code error [format $Errors(BAD_SUBCMD) $subcmd $optlist]
00214 }
00215
00216 # pass the call to the pool command indicated by the subcmd argument,
00217 # and return the result from that command.
00218 return [eval [linsert $args 0 ::struct::pool::$subcmd $poolname]]
00219 }
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239 ret ::struct::pool::destroy (type poolname , optional forceArg ="") {
00240 variable pools
00241 variable Errors
00242
00243 # check forceArg argument
00244 if { [string length $forceArg] } {
00245 if { [string equal $forceArg -force] } {
00246 set force 1
00247 } else {
00248 return -code error [format $Errors(UNKNOWN_ARG) $forceArg]
00249 }
00250 } else {
00251 set force 0
00252 }
00253
00254 set index [lsearch -exact $pools $poolname]
00255 if {$index == -1 } {
00256 return -code error [format $Errors(UNKNOWN_POOL) $poolname]
00257 }
00258
00259 if { !$force } {
00260 # check for any lingering allocated items
00261 variable $poolname
00262 upvar #0 ::struct::pool::$poolname pool
00263 upvar #0 ::struct::pool::Allocstate_$poolname state
00264 if { [llength $pool(freeitems)] != $pool(cursize) } {
00265 return -code error [format $Errors(SOME_ITEMS_NOT_FREE) destroy $poolname]
00266 }
00267 }
00268
00269 rename ::$poolname {}
00270 unset ::struct::pool::$poolname
00271 catch {unset ::struct::pool::Allocstate_$poolname}
00272 set pools [lreplace $pools $index $index]
00273
00274 return
00275 }
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292 ret ::struct::pool::add (type poolname , type args) {
00293 variable Errors
00294 variable $poolname
00295 upvar #0 ::struct::pool::$poolname pool
00296 upvar #0 ::struct::pool::Allocstate_$poolname state
00297
00298 # argument check
00299 if { [llength $args] == 0 } {
00300 return -code error $Errors(WRONG_NARGS)
00301 }
00302
00303 # will this operation exceed the size limit of the pool?
00304 if {[expr { $pool(cursize) + [llength $args] }] > $pool(maxsize) } {
00305 return -code error $Errors(EXCEED_MAXSIZE)
00306 }
00307
00308
00309 # check for duplicate items on the command line
00310 set N [llength $args]
00311 if { $N > 1} {
00312 for {set i 0} {$i<=$N} {incr i} {
00313 foreach item [lrange $args [expr {$i+1}] end] {
00314 if { [string equal [lindex $args $i] $item]} {
00315 return -code error [format $Errors(DUPLICATE_ITEM_IN_ARGS) $item]
00316 }
00317 }
00318 }
00319 }
00320
00321 # check whether the items exist yet in the pool
00322 foreach item $args {
00323 if { [lmember [array names state] $item] } {
00324 return -code error [format $Errors(ITEM_ALREADY_IN_POOL) $item]
00325 }
00326 }
00327
00328 # add items to the pool, and initialize their allocation state
00329 foreach item $args {
00330 lappend pool(freeitems) $item
00331 set state($item) -1
00332 incr pool(cursize)
00333 }
00334 return
00335 }
00336
00337
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356 ret ::struct::pool::clear (type poolname , optional forceArg ="" ) {
00357 variable Errors
00358 variable $poolname
00359 upvar #0 ::struct::pool::$poolname pool
00360 upvar #0 ::struct::pool::Allocstate_$poolname state
00361
00362 # check forceArg argument
00363 if { [string length $forceArg] } {
00364 if { [string equal $forceArg -force] } {
00365 set force 1
00366 } else {
00367 return -code error [format $Errors(UNKNOWN_ARG) $forceArg]
00368 }
00369 } else {
00370 set force 0
00371 }
00372
00373 # check whether some items are still allocated
00374 if { !$force } {
00375 if { [llength $pool(freeitems)] != $pool(cursize) } {
00376 return -code error [format $Errors(SOME_ITEMS_NOT_FREE) clear $poolname]
00377 }
00378 }
00379
00380 # clear the pool, clean up state and adjust the pool size
00381 set pool(freeitems) {}
00382 array unset state
00383 array set state {}
00384 set pool(cursize) 0
00385 return
00386 }
00387
00388
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407 ret ::struct::pool::info (type poolname , type type , type args) {
00408 variable Errors
00409 variable $poolname
00410 upvar #0 ::struct::pool::$poolname pool
00411 upvar #0 ::struct::pool::Allocstate_$poolname state
00412
00413 # check the number of arguments
00414 if { [string equal $type allocID] } {
00415 if { [llength $args]!=1 } {
00416 return -code error $Errors(WRONG_NARGS)
00417 }
00418 } elseif { [llength $args] > 0 } {
00419 return -code error $Errors(WRONG_NARGS)
00420 }
00421
00422 switch $type {
00423 allitems {
00424 return [array names state]
00425 }
00426 allocstate {
00427 return [array get state]
00428 }
00429 allocID {
00430 set item [lindex $args 0]
00431 if {![lmember [array names state] $item]} {
00432 return -code error [format $Errors(ITEM_NOT_IN_POOL) $item $poolname]
00433 }
00434 return $state($item)
00435 }
00436 cursize {
00437 return $pool(cursize)
00438 }
00439 freeitems {
00440 return $pool(freeitems)
00441 }
00442 maxsize {
00443 return $pool(maxsize)
00444 }
00445 default {
00446 return -code error [format $Errors(WRONG_INFO_TYPE) $type]
00447 }
00448 }
00449 }
00450
00451
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471
00472 ret ::struct::pool::maxsize (type poolname , optional reqsize ="" ) {
00473 variable Errors
00474 variable $poolname
00475 upvar #0 ::struct::pool::$poolname pool
00476 upvar #0 ::struct::pool::Allocstate_$poolname state
00477
00478 if { [string length $reqsize] } {
00479 if { [regexp {^\+?[1-9][0-9]*$} $reqsize] } {
00480 if { $pool(cursize) <= $reqsize } {
00481 set pool(maxsize) $reqsize
00482 } else {
00483 return -code error [format $Errors(INVALID_POOLSIZE) $pool(cursize)]
00484 }
00485 } else {
00486 return -code error $Errors(NONINT_REQSIZE)
00487 }
00488 }
00489 return $pool(maxsize)
00490 }
00491
00492
00493
00494
00495
00496
00497
00498
00499
00500
00501
00502
00503
00504
00505
00506
00507
00508
00509
00510 ret ::struct::pool::release (type poolname , type item) {
00511 variable Errors
00512 variable $poolname
00513 upvar #0 ::struct::pool::$poolname pool
00514 upvar #0 ::struct::pool::Allocstate_$poolname state
00515
00516 # Is item in the pool?
00517 if {![lmember [array names state] $item]} {
00518 return -code error [format $Errors(ITEM_NOT_IN_POOL) $item $poolname]
00519 }
00520
00521 # check whether item was allocated
00522 if { $state($item) == -1 } {
00523 return -code error [format $Errors(ITEM_NOT_ALLOCATED) $item]
00524 } else {
00525
00526 # set item free and return it to the pool of free items
00527 set state($item) -1
00528 lappend pool(freeitems) $item
00529
00530 }
00531 return
00532 }
00533
00534
00535
00536
00537
00538
00539
00540
00541
00542
00543
00544
00545
00546
00547
00548
00549
00550
00551 ret ::struct::pool::remove (type poolname , type item , optional forceArg ="" ) {
00552 variable Errors
00553 variable $poolname
00554 upvar #0 ::struct::pool::$poolname pool
00555 upvar #0 ::struct::pool::Allocstate_$poolname state
00556
00557 # check forceArg argument
00558 if { [string length $forceArg] } {
00559 if { [string equal $forceArg -force] } {
00560 set force 1
00561 } else {
00562 return -code error [format $Errors(UNKNOWN_ARG) $forceArg]
00563 }
00564 } else {
00565 set force 0
00566 }
00567
00568 # Is item in the pool?
00569 if {![lmember [array names state] $item]} {
00570 return -code error [format $Errors(ITEM_NOT_IN_POOL) $item $poolname]
00571 }
00572
00573 set index [lsearch $pool(freeitems) $item]
00574 if { $index >= 0} {
00575
00576 # actual removal
00577 set pool(freeitems) [lreplace $pool(freeitems) $index $index]
00578
00579 } elseif { !$force } {
00580 return -code error [format $Errors(ITEM_STILL_ALLOCATED) $item]
00581 }
00582
00583 # clean up state and adjust the pool size
00584 unset state($item)
00585 incr pool(cursize) -1
00586 return
00587 }
00588
00589
00590
00591
00592
00593
00594
00595
00596
00597
00598
00599
00600
00601
00602
00603
00604
00605
00606
00607
00608
00609
00610
00611
00612
00613
00614
00615
00616
00617
00618
00619
00620
00621
00622
00623
00624
00625
00626
00627
00628 ret ::struct::pool::request (type poolname , type itemvar , type args) {
00629 variable Errors
00630 variable $poolname
00631 upvar #0 ::struct::pool::$poolname pool
00632 upvar #0 ::struct::pool::Allocstate_$poolname state
00633
00634 # check args
00635 set nargs [llength $args]
00636 if { ! ($nargs==0 || $nargs==2 || $nargs==4) } {
00637 if { ![string equal $args -?] && ![string equal $args -help]} {
00638 return -code error $Errors(WRONG_NARGS)
00639 }
00640 } elseif { $nargs } {
00641 foreach {name value} $args {
00642 if { ![string match -* $name] } {
00643 return -code error [format $Errors(UNKNOWN_ARG) $name]
00644 }
00645 }
00646 }
00647
00648 set allocated 0
00649
00650 # are there any items available?
00651 if { [llength $pool(freeitems)] > 0} {
00652
00653 # process command options
00654 set options [cmdline::getoptions args { \
00655 {prefer.arg {} {The preference for a particular item}} \
00656 {allocID.arg {} {An ID for the entity to which the item will be allocated} } \
00657 } \
00658 "usage: $poolname request itemvar ?options?:"]
00659 foreach {key value} $options {
00660 set $key $value
00661 }
00662
00663 if { $allocID == -1 } {
00664 return -code error $Errors(FORBIDDEN_ALLOCID)
00665 }
00666
00667 # let `item' point to a variable two levels up the call stack
00668 upvar 2 $itemvar item
00669
00670 # check whether a preference was supplied
00671 if { [string length $prefer] } {
00672 if {![lmember [array names state] $prefer]} {
00673 return -code error [format $Errors(ITEM_NOT_IN_POOL) $prefer $poolname]
00674 }
00675 if { $state($prefer) == -1 } {
00676 set index [lsearch $pool(freeitems) $prefer]
00677 set item $prefer
00678 } else {
00679 return 0
00680 }
00681 } else {
00682 set index 0
00683 set item [lindex $pool(freeitems) 0]
00684 }
00685
00686 # do the actual allocation
00687 set pool(freeitems) [lreplace $pool(freeitems) $index $index]
00688 if { [string length $allocID] } {
00689 set state($item) $allocID
00690 } else {
00691 set state($item) dummyID
00692 }
00693 set allocated 1
00694 }
00695 return $allocated
00696 }
00697
00698
00699
00700
00701
00702
00703
00704 namespace ::struct {
00705
00706 namespace import -force pool::pool
00707 namespace export pool
00708 }
00709 package provide struct::pool 1.2.1
00710