1/* 2 * This file implements a family of commands for sharing variables 3 * between threads. 4 * 5 * Initial code is taken from nsd/tclvar.c found in AOLserver 3.+ 6 * distribution and modified to support Tcl 8.0+ command object interface 7 * and internal storage in private shared Tcl objects. 8 * 9 * Copyright (c) 2002 by Zoran Vasiljevic. 10 * 11 * See the file "license.terms" for information on usage and redistribution 12 * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 13 * 14 * RCS: @(#) $Id: threadSvCmd.c,v 1.50 2010/03/31 08:50:24 vasiljevic Exp $ 15 * ---------------------------------------------------------------------------- 16 */ 17 18#include "threadSvCmd.h" 19 20#include "threadSvListCmd.h" /* Shared variants of list commands */ 21#include "threadSvKeylistCmd.h" /* Shared variants of list commands */ 22#include "psGdbm.h" /* The gdbm persistent store implementation */ 23 24#ifdef NS_AOLSERVER 25# define HIDE_DOTNAMES /* tsv::names cmd does not list .<name> arrays */ 26#endif 27 28/* 29 * Number of buckets to spread shared arrays into. Each bucket is 30 * associated with one mutex so locking a bucket locks all arrays 31 * in that bucket as well. The number of buckets should be a prime. 32 */ 33 34#define NUMBUCKETS 31 35 36/* 37 * Number of object containers 38 * to allocate in one shot. 39 */ 40 41#define OBJS_TO_ALLOC_EACH_TIME 100 42 43/* 44 * Handle hiding of errorLine in 8.6 45 */ 46#if (TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION < 6) 47#define ERRORLINE(interp) ((interp)->errorLine) 48#else 49#define ERRORLINE(interp) (Tcl_GetErrorLine(interp)) 50#endif 51 52/* 53 * Reference to Tcl object types used in object-copy code. 54 * Those are referenced read-only, thus no mutex protection. 55 */ 56 57static const Tcl_ObjType* booleanObjTypePtr; 58static const Tcl_ObjType* byteArrayObjTypePtr; 59static const Tcl_ObjType* doubleObjTypePtr; 60static const Tcl_ObjType* intObjTypePtr; 61static const Tcl_ObjType* stringObjTypePtr; 62 63/* 64 * In order to be fully stub enabled, a small 65 * hack is needed to query the tclEmptyStringRep 66 * global symbol defined by Tcl. See Sv_Init. 67 */ 68 69char *Sv_tclEmptyStringRep = NULL; 70 71/* 72 * Global variables used within this file. 73 */ 74 75static Bucket* buckets; /* Array of buckets. */ 76static Tcl_Mutex bucketsMutex; /* Protects the array of buckets */ 77 78static SvCmdInfo* svCmdInfo; /* Linked list of registered commands */ 79static RegType* regType; /* Linked list of registered obj types */ 80static PsStore* psStore; /* Linked list of registered pers. stores */ 81 82static Tcl_Mutex svMutex; /* Protects inserts into above lists */ 83static Tcl_Mutex initMutex; /* Serializes initialization issues */ 84 85/* 86 * The standard commands found in AOLserver nsv_* interface. 87 * For sharp-eye readers: the implementaion of the "lappend" command 88 * is moved to new list-command package, since it realy belongs there. 89 */ 90 91static Tcl_ObjCmdProc SvObjObjCmd; 92static Tcl_ObjCmdProc SvAppendObjCmd; 93static Tcl_ObjCmdProc SvIncrObjCmd; 94static Tcl_ObjCmdProc SvSetObjCmd; 95static Tcl_ObjCmdProc SvExistsObjCmd; 96static Tcl_ObjCmdProc SvGetObjCmd; 97static Tcl_ObjCmdProc SvArrayObjCmd; 98static Tcl_ObjCmdProc SvUnsetObjCmd; 99static Tcl_ObjCmdProc SvNamesObjCmd; 100 101/* 102 * New commands added to 103 * standard set of nsv_* 104 */ 105 106static Tcl_ObjCmdProc SvPopObjCmd; 107static Tcl_ObjCmdProc SvMoveObjCmd; 108static Tcl_ObjCmdProc SvLockObjCmd; 109 110/* 111 * Forward declarations for functions to 112 * manage buckets, arrays and shared objects. 113 */ 114 115static Container* CreateContainer(Array*, Tcl_HashEntry*, Tcl_Obj*); 116static Container* AcquireContainer(Array*, const char*, int); 117 118static Array* CreateArray(Bucket*, const char*); 119static Array* LockArray(Tcl_Interp*, const char*, int); 120 121static int ReleaseContainer(Tcl_Interp*, Container*, int); 122static int DeleteContainer(Container*); 123static int FlushArray(Array*); 124static int DeleteArray(Array*); 125 126static void SvAllocateContainers(Bucket*); 127static void SvRegisterStdCommands(void); 128 129#ifdef SV_FINALIZE 130static void SvFinalizeContainers(Bucket*); 131static void SvFinalize(ClientData); 132#endif /* SV_FINALIZE */ 133 134static PsStore* GetPsStore(char *handle); 135 136static int SvObjDispatchObjCmd _ANSI_ARGS_ ((ClientData arg, 137 Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])); 138 139/* 140 *----------------------------------------------------------------------------- 141 * 142 * Sv_RegisterCommand -- 143 * 144 * Utility to register commands to be loaded at module start. 145 * 146 * Results: 147 * None. 148 * 149 * Side effects; 150 * New command will be added to a linked list of registered commands. 151 * 152 *----------------------------------------------------------------------------- 153 */ 154 155void 156Sv_RegisterCommand(cmdName, objProc, delProc, clientData) 157 const char *cmdName; /* Name of command to register */ 158 Tcl_ObjCmdProc *objProc; /* Object-based command procedure */ 159 Tcl_CmdDeleteProc *delProc; /* Command delete procedure */ 160 ClientData clientData; /* Private data ptr to pass to cmd */ 161{ 162 int len = strlen(cmdName) + strlen(TSV_CMD_PREFIX); 163 SvCmdInfo *newCmd = (SvCmdInfo*)Tcl_Alloc(sizeof(SvCmdInfo) + len + 1); 164 165 /* 166 * Setup new command structure 167 */ 168 169 newCmd->cmdName = (char*)((char*)newCmd + sizeof(SvCmdInfo)); 170 171 newCmd->objProcPtr = objProc; 172 newCmd->delProcPtr = delProc; 173 newCmd->clientData = clientData; 174 175 /* 176 * Rewrite command name. This is needed so we can 177 * easily turn-on the compatiblity with AOLserver 178 * command names. 179 */ 180 181 strcpy(newCmd->cmdName, TSV_CMD_PREFIX); 182 strcat(newCmd->cmdName, cmdName); 183 newCmd->name = newCmd->cmdName + strlen(TSV_CMD_PREFIX); 184 185 /* 186 * Plug-in in shared list of commands. 187 */ 188 189 Tcl_MutexLock(&svMutex); 190 if (svCmdInfo == NULL) { 191 svCmdInfo = newCmd; 192 newCmd->nextPtr = NULL; 193 } else { 194 newCmd->nextPtr = svCmdInfo; 195 svCmdInfo = newCmd; 196 } 197 Tcl_MutexUnlock(&svMutex); 198 199 return; 200} 201 202/* 203 *----------------------------------------------------------------------------- 204 * 205 * Sv_RegisterObjType -- 206 * 207 * Registers custom object duplicator function for a specific 208 * object type. Registered function will be called by the 209 * private object creation routine every time an object is 210 * plugged out or in the shared array. This way we assure that 211 * Tcl objects do not get shared per-reference between threads. 212 * 213 * Results: 214 * None. 215 * 216 * Side effects; 217 * Memory gets allocated. 218 * 219 *----------------------------------------------------------------------------- 220 */ 221 222void 223Sv_RegisterObjType(typePtr, dupProc) 224 const Tcl_ObjType *typePtr; /* Type of object to register */ 225 Tcl_DupInternalRepProc *dupProc; /* Custom object duplicator */ 226{ 227 RegType *newType = (RegType*)Tcl_Alloc(sizeof(RegType)); 228 229 /* 230 * Setup new type structure 231 */ 232 233 newType->typePtr = typePtr; 234 newType->dupIntRepProc = dupProc; 235 236 /* 237 * Plug-in in shared list 238 */ 239 240 Tcl_MutexLock(&svMutex); 241 newType->nextPtr = regType; 242 regType = newType; 243 Tcl_MutexUnlock(&svMutex); 244} 245 246/* 247 *----------------------------------------------------------------------------- 248 * 249 * Sv_RegisterPsStore -- 250 * 251 * Registers a handler to the persistent storage. 252 * 253 * Results: 254 * None. 255 * 256 * Side effects; 257 * Memory gets allocated. 258 * 259 *----------------------------------------------------------------------------- 260 */ 261 262void 263Sv_RegisterPsStore(psStorePtr) 264 PsStore *psStorePtr; 265{ 266 267 PsStore *psPtr = (PsStore*)Tcl_Alloc(sizeof(PsStore)); 268 269 *psPtr = *psStorePtr; 270 271 /* 272 * Plug-in in shared list 273 */ 274 275 Tcl_MutexLock(&svMutex); 276 if (psStore == NULL) { 277 psStore = psPtr; 278 psStore->nextPtr = NULL; 279 } else { 280 psPtr->nextPtr = psStore; 281 psStore = psPtr; 282 } 283 Tcl_MutexUnlock(&svMutex); 284} 285 286/* 287 *----------------------------------------------------------------------------- 288 * 289 * Sv_GetContainer -- 290 * 291 * This is the workhorse of the module. It returns the container 292 * with the shared Tcl object. It also locks the container, so 293 * when finished with operation on the Tcl object, one has to 294 * unlock the container by calling the Sv_PutContainer(). 295 * If instructed, this command might also create new container 296 * with empty Tcl object. 297 * 298 * Results: 299 * A standard Tcl result. 300 * 301 * Side effects: 302 * New container might be created. 303 * 304 *----------------------------------------------------------------------------- 305 */ 306 307int 308Sv_GetContainer(interp, objc, objv, retObj, offset, flags) 309 Tcl_Interp *interp; /* Current interpreter. */ 310 int objc; /* Number of arguments */ 311 Tcl_Obj *const objv[]; /* Argument objects. */ 312 Container **retObj; /* OUT: shared object container */ 313 int *offset; /* Shift in argument list */ 314 int flags; /* Options for locking shared array */ 315{ 316 const char *array, *key; 317 318 if (*retObj == NULL) { 319 Array *arrayPtr = NULL; 320 321 /* 322 * Parse mandatory arguments: <cmd> array key 323 */ 324 325 if (objc < 3) { 326 Tcl_WrongNumArgs(interp, 1, objv, "array key ?args?"); 327 return TCL_ERROR; 328 } 329 330 array = Tcl_GetString(objv[1]); 331 key = Tcl_GetString(objv[2]); 332 333 *offset = 3; /* Consumed three arguments: cmd, array, key */ 334 335 /* 336 * Lock the shared array and locate the shared object 337 */ 338 339 arrayPtr = LockArray(interp, array, flags); 340 if (arrayPtr == NULL) { 341 return TCL_BREAK; 342 } 343 *retObj = AcquireContainer(arrayPtr, Tcl_GetString(objv[2]), flags); 344 if (*retObj == NULL) { 345 UnlockArray(arrayPtr); 346 Tcl_AppendResult(interp, "no key ", array, "(", key, ")", NULL); 347 return TCL_BREAK; 348 } 349 } else { 350 Tcl_HashTable *handles = &((*retObj)->bucketPtr->handles); 351 LOCK_CONTAINER(*retObj); 352 if (Tcl_FindHashEntry(handles, (char*)(*retObj)) == NULL) { 353 UNLOCK_CONTAINER(*retObj); 354 Tcl_SetResult(interp, "key has been deleted", TCL_STATIC); 355 return TCL_BREAK; 356 } 357 *offset = 2; /* Consumed two arguments: object, cmd */ 358 } 359 360 return TCL_OK; 361} 362 363/* 364 *----------------------------------------------------------------------------- 365 * 366 * Sv_PutContainer -- 367 * 368 * Releases the container obtained by the Sv_GetContainer. 369 * 370 * Results: 371 * A standard Tcl result. 372 * 373 * Side effects: 374 * For bound arrays, update the underlying persistent storage. 375 * 376 *----------------------------------------------------------------------------- 377 */ 378 379int 380Sv_PutContainer(interp, svObj, mode) 381 Tcl_Interp *interp; /* For error reporting; might be NULL */ 382 Container *svObj; /* Shared object container */ 383 int mode; /* One of SV_XXX modes */ 384{ 385 int ret; 386 387 ret = ReleaseContainer(interp, svObj, mode); 388 UnlockArray(svObj->arrayPtr); 389 390 return ret; 391} 392 393/* 394 *----------------------------------------------------------------------------- 395 * 396 * GetPsStore -- 397 * 398 * Performs a lookup in the list of registered persistent storage 399 * handlers. If the match is found, duplicates the persistent 400 * storage record and passes the copy to the caller. 401 * 402 * Results: 403 * Pointer to the newly allocated persistent storage handler. Caller 404 * must free this block when done with it. If none found, returns NULL, 405 * 406 * Side effects; 407 * Memory gets allocated. Caller should free the return value of this 408 * function using Tcl_Free(). 409 * 410 *----------------------------------------------------------------------------- 411 */ 412 413static PsStore* 414GetPsStore(char *handle) 415{ 416 int i; 417 char *type = handle, *addr, *delimiter = strchr(handle, ':'); 418 PsStore *tmpPtr, *psPtr = NULL; 419 420 /* 421 * Expect the handle in the following format: <type>:<address> 422 * where "type" must match one of the registered presistent store 423 * types (gdbm, tcl, whatever) and <address> is what is passed to 424 * the open procedure of the registered store. 425 * 426 * Example: gdbm:/path/to/gdbm/file 427 */ 428 429 /* 430 * Try to see wether some array is already bound to the 431 * same persistent storage address. 432 */ 433 434 for (i = 0; i < NUMBUCKETS; i++) { 435 Tcl_HashSearch search; 436 Tcl_HashEntry *hPtr; 437 Bucket *bucketPtr = &buckets[i]; 438 LOCK_BUCKET(bucketPtr); 439 hPtr = Tcl_FirstHashEntry(&bucketPtr->arrays, &search); 440 while (hPtr) { 441 Array *arrayPtr = (Array*)Tcl_GetHashValue(hPtr); 442 if (arrayPtr->bindAddr && arrayPtr->psPtr) { 443 if (strcmp(arrayPtr->bindAddr, handle) == 0) { 444 UNLOCK_BUCKET(bucketPtr); 445 return NULL; /* Array already bound */ 446 } 447 } 448 hPtr = Tcl_NextHashEntry(&search); 449 } 450 UNLOCK_BUCKET(bucketPtr); 451 } 452 453 /* 454 * Split the address and storage handler 455 */ 456 457 if (delimiter == NULL) { 458 addr = NULL; 459 } else { 460 *delimiter = 0; 461 addr = delimiter + 1; 462 } 463 464 /* 465 * No array was bound to the same persistent storage. 466 * Lookup the persistent storage to bind to. 467 */ 468 469 Tcl_MutexLock(&svMutex); 470 for (tmpPtr = psStore; tmpPtr; tmpPtr = tmpPtr->nextPtr) { 471 if (strcmp(tmpPtr->type, type) == 0) { 472 tmpPtr->psHandle = (*tmpPtr->psOpen)(addr); 473 if (tmpPtr->psHandle) { 474 psPtr = (PsStore*)Tcl_Alloc(sizeof(PsStore)); 475 *psPtr = *tmpPtr; 476 psPtr->nextPtr = NULL; 477 } 478 break; 479 } 480 } 481 Tcl_MutexUnlock(&svMutex); 482 483 if (delimiter) { 484 *delimiter = ':'; 485 } 486 487 return psPtr; 488} 489 490/* 491 *----------------------------------------------------------------------------- 492 * 493 * AcquireContainer -- 494 * 495 * Finds a variable within an array and returns it's container. 496 * 497 * Results: 498 * Pointer to variable object. 499 * 500 * Side effects; 501 * New variable may be created. For bound arrays, try to locate 502 * the key in the persistent storage as well. 503 * 504 *----------------------------------------------------------------------------- 505 */ 506 507static Container * 508AcquireContainer(arrayPtr, key, flags) 509 Array *arrayPtr; 510 const char *key; 511 int flags; 512{ 513 int new; 514 Tcl_Obj *tclObj = NULL; 515 Tcl_HashEntry *hPtr = Tcl_FindHashEntry(&arrayPtr->vars, key); 516 517 if (hPtr == NULL) { 518 PsStore *psPtr = arrayPtr->psPtr; 519 if (psPtr) { 520 char *val = NULL; 521 int len = 0; 522 if ((*psPtr->psGet)(psPtr->psHandle, key, &val, &len) == 0) { 523 tclObj = Tcl_NewStringObj(val, len); 524 (*psPtr->psFree)(val); 525 } 526 } 527 if (!(flags & FLAGS_CREATEVAR) && tclObj == NULL) { 528 return NULL; 529 } 530 if (tclObj == NULL) { 531 tclObj = Tcl_NewObj(); 532 } 533 hPtr = Tcl_CreateHashEntry(&arrayPtr->vars, key, &new); 534 Tcl_SetHashValue(hPtr, CreateContainer(arrayPtr, hPtr, tclObj)); 535 } 536 537 return (Container*)Tcl_GetHashValue(hPtr); 538} 539 540/* 541 *----------------------------------------------------------------------------- 542 * 543 * ReleaseContainer -- 544 * 545 * Does some post-processing on the used container. This is mostly 546 * needed when the container has been modified and needs to be 547 * saved in the bound persistent storage. 548 * 549 * Results: 550 * A standard Tcl result 551 * 552 * Side effects: 553 * Persistent storage, if bound, might be modified. 554 * 555 *----------------------------------------------------------------------------- 556 */ 557 558static int 559ReleaseContainer(interp, svObj, mode) 560 Tcl_Interp *interp; 561 Container *svObj; 562 int mode; 563{ 564 PsStore *psPtr = svObj->arrayPtr->psPtr; 565 int len; 566 char *key, *val; 567 568 switch (mode) { 569 case SV_UNCHANGED: return TCL_OK; 570 case SV_ERROR: return TCL_ERROR; 571 case SV_CHANGED: 572 if (psPtr) { 573 key = Tcl_GetHashKey(&svObj->arrayPtr->vars, svObj->entryPtr); 574 val = Tcl_GetStringFromObj(svObj->tclObj, &len); 575 if ((*psPtr->psPut)(psPtr->psHandle, key, val, len) == -1) { 576 const char *err = (*psPtr->psError)(psPtr->psHandle); 577 Tcl_SetObjResult(interp, Tcl_NewStringObj(err, -1)); 578 return TCL_ERROR; 579 } 580 } 581 return TCL_OK; 582 } 583 584 return TCL_ERROR; /* Should never be reached */ 585} 586 587/* 588 *----------------------------------------------------------------------------- 589 * 590 * CreateContainer -- 591 * 592 * Creates new shared container holding Tcl object to be stored 593 * in the shared array 594 * 595 * Results: 596 * The container pointer. 597 * 598 * Side effects: 599 * Memory gets allocated. 600 * 601 *----------------------------------------------------------------------------- 602 */ 603 604static Container * 605CreateContainer(arrayPtr, entryPtr, tclObj) 606 Array *arrayPtr; 607 Tcl_HashEntry *entryPtr; 608 Tcl_Obj *tclObj; 609{ 610 Container *svObj; 611 612 if (arrayPtr->bucketPtr->freeCt == NULL) { 613 SvAllocateContainers(arrayPtr->bucketPtr); 614 } 615 616 svObj = arrayPtr->bucketPtr->freeCt; 617 arrayPtr->bucketPtr->freeCt = svObj->nextPtr; 618 619 svObj->arrayPtr = arrayPtr; 620 svObj->bucketPtr = arrayPtr->bucketPtr; 621 svObj->tclObj = tclObj; 622 svObj->entryPtr = entryPtr; 623 svObj->handlePtr = NULL; 624 625 if (svObj->tclObj) { 626 Tcl_IncrRefCount(svObj->tclObj); 627 } 628 629 return svObj; 630} 631 632/* 633 *----------------------------------------------------------------------------- 634 * 635 * DeleteContainer -- 636 * 637 * Destroys the container and the Tcl object within it. For bound 638 * shared arrays, the underlying persistent store is updated as well. 639 * 640 * Results: 641 * None. 642 * 643 * Side effects: 644 * Memory gets reclaimed. If the shared array was bound to persistent 645 * storage, it removes the corresponding record. 646 * 647 *----------------------------------------------------------------------------- 648 */ 649 650static int 651DeleteContainer(svObj) 652 Container *svObj; 653{ 654 if (svObj->tclObj) { 655 Tcl_DecrRefCount(svObj->tclObj); 656 } 657 if (svObj->handlePtr) { 658 Tcl_DeleteHashEntry(svObj->handlePtr); 659 } 660 if (svObj->entryPtr) { 661 PsStore *psPtr = svObj->arrayPtr->psPtr; 662 if (psPtr) { 663 char *key = Tcl_GetHashKey(&svObj->arrayPtr->vars,svObj->entryPtr); 664 if ((*psPtr->psDelete)(psPtr->psHandle, key) == -1) { 665 return TCL_ERROR; 666 } 667 } 668 Tcl_DeleteHashEntry(svObj->entryPtr); 669 } 670 671 svObj->arrayPtr = NULL; 672 svObj->entryPtr = NULL; 673 svObj->handlePtr = NULL; 674 svObj->tclObj = NULL; 675 676 svObj->nextPtr = svObj->bucketPtr->freeCt; 677 svObj->bucketPtr->freeCt = svObj; 678 679 return TCL_OK; 680} 681 682/* 683 *----------------------------------------------------------------------------- 684 * 685 * LockArray -- 686 * 687 * Find (or create) the array structure for shared array and lock it. 688 * Array structure must be later unlocked with UnlockArray. 689 * 690 * Results: 691 * TCL_OK or TCL_ERROR if no such array. 692 * 693 * Side effects: 694 * Sets *arrayPtrPtr with Array pointer or leave error in given interp. 695 * 696 *----------------------------------------------------------------------------- 697 */ 698 699static Array * 700LockArray(interp, array, flags) 701 Tcl_Interp *interp; /* Interpreter to leave result. */ 702 const char *array; /* Name of array to lock */ 703 int flags; /* FLAGS_CREATEARRAY/FLAGS_NOERRMSG*/ 704{ 705 register const char *p; 706 register unsigned int result; 707 register int i; 708 Bucket *bucketPtr; 709 Array *arrayPtr; 710 711 /* 712 * Compute a hash to map an array to a bucket. 713 */ 714 715 p = array; 716 result = 0; 717 while (*p++) { 718 i = *p; 719 result += (result << 3) + i; 720 } 721 i = result % NUMBUCKETS; 722 bucketPtr = &buckets[i]; 723 724 /* 725 * Lock the bucket and find the array, or create a new one. 726 * The bucket will be left locked on success. 727 */ 728 729 LOCK_BUCKET(bucketPtr); /* Note: no matching unlock below ! */ 730 if (flags & FLAGS_CREATEARRAY) { 731 arrayPtr = CreateArray(bucketPtr, array); 732 } else { 733 Tcl_HashEntry *hPtr = Tcl_FindHashEntry(&bucketPtr->arrays, array); 734 if (hPtr == NULL) { 735 UNLOCK_BUCKET(bucketPtr); 736 if (!(flags & FLAGS_NOERRMSG)) { 737 Tcl_AppendResult(interp, "\"", array, 738 "\" is not a thread shared array", NULL); 739 } 740 return NULL; 741 } 742 arrayPtr = (Array*)Tcl_GetHashValue(hPtr); 743 } 744 745 return arrayPtr; 746} 747/* 748 *----------------------------------------------------------------------------- 749 * 750 * FlushArray -- 751 * 752 * Unset all keys in an array. 753 * 754 * Results: 755 * None. 756 * 757 * Side effects: 758 * Array is cleaned but it's variable hash-hable still lives. 759 * For bound arrays, the persistent store is updated accordingly. 760 * 761 *----------------------------------------------------------------------------- 762 */ 763 764static int 765FlushArray(arrayPtr) 766 Array *arrayPtr; /* Name of array to flush */ 767{ 768 Tcl_HashEntry *hPtr; 769 Tcl_HashSearch search; 770 771 for (hPtr = Tcl_FirstHashEntry(&arrayPtr->vars, &search); hPtr; 772 hPtr = Tcl_NextHashEntry(&search)) { 773 if (DeleteContainer((Container*)Tcl_GetHashValue(hPtr)) != TCL_OK) { 774 return TCL_ERROR; 775 } 776 } 777 778 return TCL_OK; 779} 780 781/* 782 *----------------------------------------------------------------------------- 783 * 784 * CreateArray -- 785 * 786 * Creates new shared array instance. 787 * 788 * Results: 789 * Pointer to the newly created array 790 * 791 * Side effects: 792 * Memory gets allocated 793 * 794 *----------------------------------------------------------------------------- 795 */ 796 797static Array * 798CreateArray(bucketPtr, arrayName) 799 Bucket *bucketPtr; 800 const char *arrayName; 801{ 802 int new; 803 Array *arrayPtr; 804 Tcl_HashEntry *hPtr; 805 806 hPtr = Tcl_CreateHashEntry(&bucketPtr->arrays, arrayName, &new); 807 if (!new) { 808 return (Array*)Tcl_GetHashValue(hPtr); 809 } 810 811 arrayPtr = (Array*)Tcl_Alloc(sizeof(Array)); 812 arrayPtr->bucketPtr = bucketPtr; 813 arrayPtr->entryPtr = hPtr; 814 arrayPtr->psPtr = NULL; 815 arrayPtr->bindAddr = NULL; 816 817 Tcl_InitHashTable(&arrayPtr->vars, TCL_STRING_KEYS); 818 Tcl_SetHashValue(hPtr, arrayPtr); 819 820 return arrayPtr; 821} 822 823/* 824 *----------------------------------------------------------------------------- 825 * 826 * DeleteArray -- 827 * 828 * Deletes the shared array. 829 * 830 * Results: 831 * A standard Tcl result. 832 * 833 * Side effects: 834 * Memory gets reclaimed. 835 * 836 *----------------------------------------------------------------------------- 837 */ 838 839static int 840DeleteArray(arrayPtr) 841 Array *arrayPtr; 842{ 843 if (FlushArray(arrayPtr) == -1) { 844 return TCL_ERROR; 845 } 846 if (arrayPtr->psPtr) { 847 PsStore *psPtr = arrayPtr->psPtr; 848 if ((*psPtr->psClose)(psPtr->psHandle) == -1) { 849 return TCL_ERROR; 850 } 851 Tcl_Free((char*)arrayPtr->psPtr), arrayPtr->psPtr = NULL; 852 } 853 if (arrayPtr->bindAddr) { 854 Tcl_Free(arrayPtr->bindAddr); 855 } 856 if (arrayPtr->entryPtr) { 857 Tcl_DeleteHashEntry(arrayPtr->entryPtr); 858 } 859 860 Tcl_DeleteHashTable(&arrayPtr->vars); 861 Tcl_Free((char*)arrayPtr); 862 863 return TCL_OK; 864} 865 866/* 867 *----------------------------------------------------------------------------- 868 * 869 * SvAllocateContainers -- 870 * 871 * Any similarity with the Tcl AllocateFreeObj function is purely 872 * coincidental... Just joking; this is (almost) 100% copy of it! :-) 873 * 874 * Results: 875 * None. 876 * 877 * Side effects: 878 * Allocates memory for many containers at the same time 879 * 880 *----------------------------------------------------------------------------- 881 */ 882 883static void 884SvAllocateContainers(bucketPtr) 885 Bucket *bucketPtr; 886{ 887 Container tmp[2]; 888 size_t objSizePlusPadding = (size_t)(((char*)(tmp+1))-(char*)tmp); 889 size_t bytesToAlloc = (OBJS_TO_ALLOC_EACH_TIME * objSizePlusPadding); 890 char *basePtr; 891 register Container *prevPtr = NULL, *objPtr = NULL; 892 register int i; 893 894 basePtr = (char*)Tcl_Alloc(bytesToAlloc); 895 memset(basePtr, 0, bytesToAlloc); 896 897 objPtr = (Container*)basePtr; 898 objPtr->chunkAddr = basePtr; /* Mark chunk address for reclaim */ 899 900 for (i = 0; i < OBJS_TO_ALLOC_EACH_TIME; i++) { 901 objPtr->nextPtr = prevPtr; 902 prevPtr = objPtr; 903 objPtr = (Container*)(((char*)objPtr) + objSizePlusPadding); 904 } 905 bucketPtr->freeCt = prevPtr; 906} 907 908#ifdef SV_FINALIZE 909/* 910 *----------------------------------------------------------------------------- 911 * 912 * SvFinalizeContainers -- 913 * 914 * Reclaim memory for free object containers per bucket. 915 * 916 * Results: 917 * None. 918 * 919 * Side effects: 920 * Memory gets reclaimed 921 * 922 *----------------------------------------------------------------------------- 923 */ 924 925static void 926SvFinalizeContainers(bucketPtr) 927 Bucket *bucketPtr; 928{ 929 Container *tmpPtr, *objPtr = bucketPtr->freeCt; 930 931 while (objPtr) { 932 if (objPtr->chunkAddr == (char*)objPtr) { 933 tmpPtr = objPtr->nextPtr; 934 Tcl_Free((char*)objPtr); 935 objPtr = tmpPtr; 936 } else { 937 objPtr = objPtr->nextPtr; 938 } 939 } 940} 941#endif /* SV_FINALIZE */ 942 943/* 944 *----------------------------------------------------------------------------- 945 * 946 * Sv_DuplicateObj -- 947 * 948 * Create and return a new object that is (mostly) a duplicate of the 949 * argument object. We take care that the duplicate object is either 950 * a proper object copy, i.e. w/o hidden references to original object 951 * elements or a plain string object, i.e one w/o internal representation. 952 * 953 * Decision about wether to produce a real duplicate or a string object 954 * is done as follows: 955 * 956 * 1) Scalar Tcl object types are properly copied by default; 957 * these include: boolean, int double, string and byteArray types. 958 * 2) Object registered with Sv_RegisterObjType are duplicated 959 * using custom duplicator function which is guaranteed to 960 * produce a proper deep copy of the object in question. 961 * 3) All other object types are stringified; these include 962 * miscelaneous Tcl objects (cmdName, nsName, bytecode, etc, etc) 963 * and all user-defined objects. 964 * 965 * Results: 966 * The return value is a pointer to a newly created Tcl_Obj. This 967 * object has reference count 0 and the same type, if any, as the 968 * source object objPtr. Also: 969 * 970 * 1) If the source object has a valid string rep, we copy it; 971 * otherwise, the new string rep is marked invalid. 972 * 2) If the source object has an internal representation (i.e. its 973 * typePtr is non-NULL), the new object's internal rep is set to 974 * a copy; otherwise the new internal rep is marked invalid. 975 * 976 * Side effects: 977 * Some object may, when copied, loose their type, i.e. will become 978 * just plain string objects. 979 * 980 *----------------------------------------------------------------------------- 981 */ 982 983Tcl_Obj * 984Sv_DuplicateObj(objPtr) 985 register Tcl_Obj *objPtr; /* The object to duplicate. */ 986{ 987 register Tcl_Obj *dupPtr = Tcl_NewObj(); 988 989 /* 990 * Handle the internal rep 991 */ 992 993 if (objPtr->typePtr != NULL) { 994 if (objPtr->typePtr->dupIntRepProc == NULL) { 995 dupPtr->internalRep = objPtr->internalRep; 996 dupPtr->typePtr = objPtr->typePtr; 997 Tcl_InvalidateStringRep(dupPtr); 998 } else { 999 if ( objPtr->typePtr == booleanObjTypePtr \ 1000 || objPtr->typePtr == byteArrayObjTypePtr \ 1001 || objPtr->typePtr == doubleObjTypePtr \ 1002 || objPtr->typePtr == intObjTypePtr \ 1003 || objPtr->typePtr == stringObjTypePtr) { 1004 /* 1005 * Cover all "safe" obj types (see header comment) 1006 */ 1007 (*objPtr->typePtr->dupIntRepProc)(objPtr, dupPtr); 1008 Tcl_InvalidateStringRep(dupPtr); 1009 } else { 1010 int found = 0; 1011 register RegType *regPtr; 1012 /* 1013 * Cover special registered types. Assume not 1014 * very many of those, so this sequential walk 1015 * should be fast enough. 1016 */ 1017 for (regPtr = regType; regPtr; regPtr = regPtr->nextPtr) { 1018 if (objPtr->typePtr == regPtr->typePtr) { 1019 (*regPtr->dupIntRepProc)(objPtr, dupPtr); 1020 Tcl_InvalidateStringRep(dupPtr); 1021 found = 1; 1022 break; 1023 } 1024 } 1025 /* 1026 * Assure at least string rep of the source 1027 * is present, which will be copied below. 1028 */ 1029 if (found == 0 && objPtr->bytes == NULL 1030 && objPtr->typePtr->updateStringProc != NULL) { 1031 (*objPtr->typePtr->updateStringProc)(objPtr); 1032 } 1033 } 1034 } 1035 } 1036 1037 /* 1038 * Handle the string rep 1039 */ 1040 1041 if (objPtr->bytes == NULL) { 1042 dupPtr->bytes = NULL; 1043 } else if (objPtr->bytes != Sv_tclEmptyStringRep) { 1044 /* A copy of TclInitStringRep macro */ 1045 dupPtr->bytes = (char*)Tcl_Alloc((unsigned)objPtr->length + 1); 1046 if (objPtr->length > 0) { 1047 memcpy((void*)dupPtr->bytes,(void*)objPtr->bytes, 1048 (unsigned)objPtr->length); 1049 } 1050 dupPtr->length = objPtr->length; 1051 dupPtr->bytes[objPtr->length] = '\0'; 1052 } 1053 1054 return dupPtr; 1055} 1056 1057/* 1058 *----------------------------------------------------------------------------- 1059 * 1060 * SvObjDispatchObjCmd -- 1061 * 1062 * The method command for dispatching sub-commands of the shared 1063 * object. 1064 * 1065 * Results: 1066 * A standard Tcl result. 1067 * 1068 * Side effects: 1069 * Depends on the dispatched command 1070 * 1071 *----------------------------------------------------------------------------- 1072 */ 1073 1074static int 1075SvObjDispatchObjCmd(arg, interp, objc, objv) 1076 ClientData arg; /* Just passed to the command. */ 1077 Tcl_Interp *interp; /* Current interpreter. */ 1078 int objc; /* Number of arguments. */ 1079 Tcl_Obj *const objv[]; /* Argument objects. */ 1080{ 1081 const char *cmdName; 1082 SvCmdInfo *cmdPtr; 1083 1084 if (objc < 2) { 1085 Tcl_WrongNumArgs(interp, 1, objv, "args"); 1086 return TCL_ERROR; 1087 } 1088 1089 cmdName = Tcl_GetString(objv[1]); 1090 1091 /* 1092 * Do simple linear search. We may later replace this list 1093 * with the hash table to gain speed. Currently, the list 1094 * of registered commands is so small, so this will work 1095 * fast enough. 1096 */ 1097 1098 for (cmdPtr = svCmdInfo; cmdPtr; cmdPtr = cmdPtr->nextPtr) { 1099 if (!strcmp(cmdPtr->name, cmdName)) { 1100 return (*cmdPtr->objProcPtr)(arg, interp, objc, objv); 1101 } 1102 } 1103 1104 Tcl_AppendResult(interp, "invalid command name \"", cmdName, "\"", NULL); 1105 return TCL_ERROR; 1106} 1107 1108/* 1109 *----------------------------------------------------------------------------- 1110 * 1111 * SvObjObjCmd -- 1112 * 1113 * Creates the object command for a shared array. 1114 * 1115 * Results: 1116 * A standard Tcl result. 1117 * 1118 * Side effects: 1119 * New Tcl command gets created. 1120 * 1121 *----------------------------------------------------------------------------- 1122 */ 1123 1124static int 1125SvObjObjCmd(dummy, interp, objc, objv) 1126 ClientData dummy; /* Not used. */ 1127 Tcl_Interp *interp; /* Current interpreter. */ 1128 int objc; /* Number of arguments. */ 1129 Tcl_Obj *const objv[]; /* Argument objects. */ 1130{ 1131 int new, off, ret, flg; 1132 char buf[128]; 1133 Tcl_Obj *val = NULL; 1134 Container *svObj = NULL; 1135 1136 /* 1137 * Syntax: sv::object array key ?var? 1138 */ 1139 1140 ret = Sv_GetContainer(interp, objc, objv, &svObj, &off, 0); 1141 switch (ret) { 1142 case TCL_BREAK: /* Shared array was not found */ 1143 if ((objc - off)) { 1144 val = objv[off]; 1145 } 1146 Tcl_ResetResult(interp); 1147 flg = FLAGS_CREATEARRAY | FLAGS_CREATEVAR; 1148 ret = Sv_GetContainer(interp, objc, objv, &svObj, &off, flg); 1149 if (ret != TCL_OK) { 1150 return TCL_ERROR; 1151 } 1152 Tcl_DecrRefCount(svObj->tclObj); 1153 svObj->tclObj = Sv_DuplicateObj(val ? val : Tcl_NewObj()); 1154 Tcl_IncrRefCount(svObj->tclObj); 1155 break; 1156 case TCL_ERROR: 1157 return TCL_ERROR; 1158 } 1159 1160 if (svObj->handlePtr == NULL) { 1161 Tcl_HashTable *handles = &svObj->arrayPtr->bucketPtr->handles; 1162 svObj->handlePtr = Tcl_CreateHashEntry(handles, (char*)svObj, &new); 1163 } 1164 1165 /* 1166 * Format the command name 1167 */ 1168 1169 sprintf(buf, "::%p", (int*)svObj); 1170 Tcl_CreateObjCommand(interp, buf, SvObjDispatchObjCmd, (int*)svObj, NULL); 1171 Tcl_ResetResult(interp); 1172 Tcl_SetObjResult(interp, Tcl_NewStringObj(buf, -1)); 1173 1174 return Sv_PutContainer(interp, svObj, SV_UNCHANGED); 1175} 1176 1177/* 1178 *----------------------------------------------------------------------------- 1179 * 1180 * SvArrayObjCmd -- 1181 * 1182 * This procedure is invoked to process the "tsv::array" command. 1183 * See the user documentation for details on what it does. 1184 * 1185 * Results: 1186 * A standard Tcl result. 1187 * 1188 * Side effects: 1189 * See the user documentation. 1190 * 1191 *----------------------------------------------------------------------------- 1192 */ 1193 1194static int 1195SvArrayObjCmd(arg, interp, objc, objv) 1196 ClientData arg; /* Pointer to object container. */ 1197 Tcl_Interp *interp; /* Current interpreter. */ 1198 int objc; /* Number of arguments. */ 1199 Tcl_Obj *const objv[]; /* Argument objects. */ 1200{ 1201 int i, argx = 0, lobjc = 0, index, ret = TCL_OK; 1202 const char *arrayName = NULL; 1203 Array *arrayPtr = NULL; 1204 Tcl_Obj **lobjv = NULL; 1205 Container *svObj, *elObj = NULL; 1206 1207 static const char *opts[] = { 1208 "set", "reset", "get", "names", "size", "exists", "isbound", 1209 "bind", "unbind", NULL 1210 }; 1211 enum options { 1212 ASET, ARESET, AGET, ANAMES, ASIZE, AEXISTS, AISBOUND, 1213 ABIND, AUNBIND 1214 }; 1215 1216 svObj = (Container*)arg; 1217 1218 if (objc < 3) { 1219 Tcl_WrongNumArgs(interp, 1, objv, "option array"); 1220 return TCL_ERROR; 1221 } 1222 1223 arrayName = Tcl_GetString(objv[2]); 1224 arrayPtr = LockArray(interp, arrayName, FLAGS_NOERRMSG); 1225 1226 if (objc > 3) { 1227 argx = 3; 1228 } 1229 1230 Tcl_ResetResult(interp); 1231 1232 if (Tcl_GetIndexFromObj(interp,objv[1],opts,"option",0,&index) != TCL_OK) { 1233 ret = TCL_ERROR; 1234 1235 } else if (index == AEXISTS) { 1236 Tcl_SetBooleanObj(Tcl_GetObjResult(interp), arrayPtr ? 1 : 0); 1237 1238 } else if (index == AISBOUND) { 1239 if (arrayPtr == NULL) { 1240 Tcl_SetBooleanObj(Tcl_GetObjResult(interp), 0); 1241 } else { 1242 Tcl_SetBooleanObj(Tcl_GetObjResult(interp), arrayPtr->psPtr ? 1:0); 1243 } 1244 1245 } else if (index == ASIZE) { 1246 if (arrayPtr == NULL) { 1247 Tcl_SetIntObj(Tcl_GetObjResult(interp), 0); 1248 } else { 1249 Tcl_SetLongObj(Tcl_GetObjResult(interp),arrayPtr->vars.numEntries); 1250 } 1251 1252 } else if (index == ASET || index == ARESET) { 1253 if (argx == (objc - 1)) { 1254 if (argx && Tcl_ListObjGetElements(interp, objv[argx], &lobjc, 1255 &lobjv) != TCL_OK) { 1256 ret = TCL_ERROR; 1257 goto cmdExit; 1258 } 1259 } else { 1260 lobjc = objc - 3; 1261 lobjv = (Tcl_Obj**)objv + 3; 1262 } 1263 if (lobjc & 1) { 1264 Tcl_AppendResult(interp, "list must have an even number" 1265 " of elements", NULL); 1266 ret = TCL_ERROR; 1267 goto cmdExit; 1268 } 1269 if (arrayPtr == NULL) { 1270 arrayPtr = LockArray(interp, arrayName, FLAGS_CREATEARRAY); 1271 } 1272 if (index == ARESET) { 1273 ret = FlushArray(arrayPtr); 1274 if (ret != TCL_OK) { 1275 if (arrayPtr->psPtr) { 1276 PsStore *psPtr = arrayPtr->psPtr; 1277 char *err = (*psPtr->psError)(psPtr->psHandle); 1278 Tcl_SetObjResult(interp, Tcl_NewStringObj(err, -1)); 1279 } 1280 goto cmdExit; 1281 } 1282 } 1283 for (i = 0; i < lobjc; i += 2) { 1284 const char *key = Tcl_GetString(lobjv[i]); 1285 elObj = AcquireContainer(arrayPtr, key, FLAGS_CREATEVAR); 1286 Tcl_DecrRefCount(elObj->tclObj); 1287 elObj->tclObj = Sv_DuplicateObj(lobjv[i+1]); 1288 Tcl_IncrRefCount(elObj->tclObj); 1289 if (ReleaseContainer(interp, elObj, SV_CHANGED) != TCL_OK) { 1290 ret = TCL_ERROR; 1291 goto cmdExit; 1292 } 1293 } 1294 1295 } else if (index == AGET || index == ANAMES) { 1296 if (arrayPtr) { 1297 Tcl_HashSearch search; 1298 Tcl_Obj *resObj = Tcl_NewListObj(0, NULL); 1299 const char *pattern = (argx == 0) ? NULL : Tcl_GetString(objv[argx]); 1300 Tcl_HashEntry *hPtr = Tcl_FirstHashEntry(&arrayPtr->vars,&search); 1301 while (hPtr) { 1302 char *key = Tcl_GetHashKey(&arrayPtr->vars, hPtr); 1303 if (pattern == NULL || Tcl_StringMatch(key, pattern)) { 1304 Tcl_ListObjAppendElement(interp, resObj, 1305 Tcl_NewStringObj(key, -1)); 1306 if (index == AGET) { 1307 elObj = (Container*)Tcl_GetHashValue(hPtr); 1308 Tcl_ListObjAppendElement(interp, resObj, 1309 Sv_DuplicateObj(elObj->tclObj)); 1310 } 1311 } 1312 hPtr = Tcl_NextHashEntry(&search); 1313 } 1314 Tcl_SetObjResult(interp, resObj); 1315 } 1316 1317 } else if (index == ABIND) { 1318 1319 /* 1320 * This is more complex operation, requiring some clarification. 1321 * 1322 * When binding an already existing array, we walk the array 1323 * first and store all key/value pairs found there in the 1324 * persistent storage. Then we proceed with the below. 1325 * 1326 * When binding an non-existent array, we open the persistent 1327 * storage and cache all key/value pairs found there into tne 1328 * newly created shared array. 1329 */ 1330 1331 PsStore *psPtr; 1332 int len; 1333 char *psurl, *key = NULL, *val = NULL; 1334 1335 if (objc < 4) { 1336 Tcl_WrongNumArgs(interp, 2, objv, "array handle"); 1337 ret = TCL_ERROR; 1338 goto cmdExit; 1339 } 1340 1341 if (arrayPtr && arrayPtr->psPtr) { 1342 Tcl_AppendResult(interp, "array is already bound", NULL); 1343 ret = TCL_ERROR; 1344 goto cmdExit; 1345 } 1346 1347 psurl = Tcl_GetStringFromObj(objv[3], &len); 1348 psPtr = GetPsStore(psurl); 1349 1350 if (psPtr == NULL) { 1351 Tcl_AppendResult(interp, "can't open persistent storage on \"", 1352 psurl, "\"", NULL); 1353 ret = TCL_ERROR; 1354 goto cmdExit; 1355 } 1356 if (arrayPtr) { 1357 Tcl_HashSearch search; 1358 Tcl_HashEntry *hPtr = Tcl_FirstHashEntry(&arrayPtr->vars,&search); 1359 arrayPtr->psPtr = psPtr; 1360 arrayPtr->bindAddr = strcpy(Tcl_Alloc(len+1), psurl); 1361 while (hPtr) { 1362 svObj = Tcl_GetHashValue(hPtr); 1363 if (ReleaseContainer(interp, svObj, SV_CHANGED) != TCL_OK) { 1364 ret = TCL_ERROR; 1365 goto cmdExit; 1366 } 1367 hPtr = Tcl_NextHashEntry(&search); 1368 } 1369 } else { 1370 arrayPtr = LockArray(interp, arrayName, FLAGS_CREATEARRAY); 1371 arrayPtr->psPtr = psPtr; 1372 arrayPtr->bindAddr = strcpy(Tcl_Alloc(len+1), psurl); 1373 } 1374 if (!(*psPtr->psFirst)(psPtr->psHandle, &key, &val, &len)) { 1375 do { 1376 (*psPtr->psFree)(val); /* What a waste! */ 1377 AcquireContainer(arrayPtr, key, FLAGS_CREATEVAR); 1378 } while (!(*psPtr->psNext)(psPtr->psHandle, &key, &val, &len)); 1379 } 1380 1381 } else if (index == AUNBIND) { 1382 if (arrayPtr && arrayPtr->psPtr) { 1383 PsStore *psPtr = arrayPtr->psPtr; 1384 if ((*psPtr->psClose)(psPtr->psHandle) == -1) { 1385 char *err = (*psPtr->psError)(psPtr->psHandle); 1386 Tcl_SetObjResult(interp, Tcl_NewStringObj(err, -1)); 1387 ret = TCL_ERROR; 1388 goto cmdExit; 1389 } 1390 Tcl_Free((char*)arrayPtr->psPtr), arrayPtr->psPtr = NULL; 1391 } else { 1392 Tcl_AppendResult(interp, "shared variable is not bound", NULL); 1393 ret = TCL_ERROR; 1394 goto cmdExit; 1395 } 1396 } 1397 1398 cmdExit: 1399 if (arrayPtr) { 1400 UnlockArray(arrayPtr); 1401 } 1402 1403 return ret; 1404} 1405 1406/* 1407 *----------------------------------------------------------------------------- 1408 * 1409 * SvUnsetObjCmd -- 1410 * 1411 * This procedure is invoked to process the "tsv::unset" command. 1412 * See the user documentation for details on what it does. 1413 * 1414 * Results: 1415 * A standard Tcl result. 1416 * 1417 * Side effects: 1418 * See the user documentation. 1419 * 1420 *----------------------------------------------------------------------------- 1421 */ 1422 1423static int 1424SvUnsetObjCmd(dummy, interp, objc, objv) 1425 ClientData dummy; /* Not used. */ 1426 Tcl_Interp *interp; /* Current interpreter. */ 1427 int objc; /* Number of arguments. */ 1428 Tcl_Obj *const objv[]; /* Argument objects. */ 1429{ 1430 int ii; 1431 const char *arrayName; 1432 Array *arrayPtr; 1433 1434 if (objc < 2) { 1435 Tcl_WrongNumArgs(interp, 1, objv, "array ?key ...?"); 1436 return TCL_ERROR; 1437 } 1438 1439 arrayName = Tcl_GetString(objv[1]); 1440 arrayPtr = LockArray(interp, arrayName, 0); 1441 1442 if (arrayPtr == NULL) { 1443 return TCL_ERROR; 1444 } 1445 if (objc == 2) { 1446 UnlockArray(arrayPtr); 1447 if (DeleteArray(arrayPtr) != TCL_OK) { 1448 return TCL_ERROR; 1449 } 1450 } else { 1451 for (ii = 2; ii < objc; ii++) { 1452 const char *key = Tcl_GetString(objv[ii]); 1453 Tcl_HashEntry *hPtr = Tcl_FindHashEntry(&arrayPtr->vars, key); 1454 if (hPtr) { 1455 if (DeleteContainer((Container*)Tcl_GetHashValue(hPtr)) 1456 != TCL_OK) { 1457 UnlockArray(arrayPtr); 1458 return TCL_ERROR; 1459 } 1460 } else { 1461 UnlockArray(arrayPtr); 1462 Tcl_AppendResult(interp,"no key ",arrayName,"(",key,")",NULL); 1463 return TCL_ERROR; 1464 } 1465 } 1466 UnlockArray(arrayPtr); 1467 } 1468 1469 return TCL_OK; 1470} 1471 1472/* 1473 *----------------------------------------------------------------------------- 1474 * 1475 * SvNamesObjCmd -- 1476 * 1477 * This procedure is invoked to process the "tsv::names" command. 1478 * See the user documentation for details on what it does. 1479 * 1480 * Results: 1481 * A standard Tcl result. 1482 * 1483 * Side effects: 1484 * See the user documentation. 1485 * 1486 *----------------------------------------------------------------------------- 1487 */ 1488 1489static int 1490SvNamesObjCmd(dummy, interp, objc, objv) 1491 ClientData dummy; /* Not used. */ 1492 Tcl_Interp *interp; /* Current interpreter. */ 1493 int objc; /* Number of arguments. */ 1494 Tcl_Obj *const objv[]; /* Argument objects. */ 1495{ 1496 int i, len; 1497 const char *pattern = NULL; 1498 Tcl_HashEntry *hPtr; 1499 Tcl_HashSearch search; 1500 Tcl_Obj *resObj; 1501 1502 if (objc > 2) { 1503 Tcl_WrongNumArgs(interp, 1, objv, "?pattern?"); 1504 return TCL_ERROR; 1505 } 1506 if (objc == 2) { 1507 pattern = Tcl_GetStringFromObj(objv[1], &len); 1508 } 1509 1510 resObj = Tcl_NewListObj(0, NULL); 1511 1512 for (i = 0; i < NUMBUCKETS; i++) { 1513 Bucket *bucketPtr = &buckets[i]; 1514 LOCK_BUCKET(bucketPtr); 1515 hPtr = Tcl_FirstHashEntry(&bucketPtr->arrays, &search); 1516 while (hPtr) { 1517 char *key = Tcl_GetHashKey(&bucketPtr->arrays, hPtr); 1518#ifdef HIDE_DOTNAMES 1519 if (*key != '.' /* Hide .<name> arrays */ && 1520#else 1521 if (1 && 1522#endif 1523 (pattern == NULL || Tcl_StringMatch(key, pattern))) { 1524 Tcl_ListObjAppendElement(interp, resObj, 1525 Tcl_NewStringObj(key, -1)); 1526 } 1527 hPtr = Tcl_NextHashEntry(&search); 1528 } 1529 UNLOCK_BUCKET(bucketPtr); 1530 } 1531 1532 Tcl_SetObjResult(interp, resObj); 1533 1534 return TCL_OK; 1535} 1536 1537/* 1538 *----------------------------------------------------------------------------- 1539 * 1540 * SvGetObjCmd -- 1541 * 1542 * This procedure is invoked to process "tsv::get" command. 1543 * See the user documentation for details on what it does. 1544 * 1545 * Results: 1546 * A standard Tcl result. 1547 * 1548 * Side effects: 1549 * See the user documentation. 1550 * 1551 *----------------------------------------------------------------------------- 1552 */ 1553 1554static int 1555SvGetObjCmd(arg, interp, objc, objv) 1556 ClientData arg; /* Pointer to object container. */ 1557 Tcl_Interp *interp; /* Current interpreter. */ 1558 int objc; /* Number of arguments. */ 1559 Tcl_Obj *const objv[]; /* Argument objects. */ 1560{ 1561 int off, ret; 1562 Tcl_Obj *res; 1563 Container *svObj = (Container*)arg; 1564 1565 /* 1566 * Syntax: 1567 * tsv::get array key ?var? 1568 * $object get ?var? 1569 */ 1570 1571 ret = Sv_GetContainer(interp, objc, objv, &svObj, &off, 0); 1572 switch (ret) { 1573 case TCL_BREAK: 1574 if ((objc - off) == 0) { 1575 return TCL_ERROR; 1576 } else { 1577 Tcl_ResetResult(interp); 1578 Tcl_SetIntObj(Tcl_GetObjResult(interp), 0); 1579 return TCL_OK; 1580 } 1581 case TCL_ERROR: 1582 return TCL_ERROR; 1583 } 1584 1585 res = Sv_DuplicateObj(svObj->tclObj); 1586 1587 if ((objc - off) == 0) { 1588 Tcl_SetObjResult(interp, res); 1589 } else { 1590 if (Tcl_ObjSetVar2(interp, objv[off], NULL, res, 0) == NULL) { 1591 Tcl_DecrRefCount(res); 1592 goto cmd_err; 1593 } 1594 Tcl_ResetResult(interp); 1595 Tcl_SetIntObj(Tcl_GetObjResult(interp), 1); 1596 } 1597 1598 return Sv_PutContainer(interp, svObj, SV_UNCHANGED); 1599 1600 cmd_err: 1601 return Sv_PutContainer(interp, svObj, SV_ERROR); 1602} 1603 1604/* 1605 *----------------------------------------------------------------------------- 1606 * 1607 * SvExistsObjCmd -- 1608 * 1609 * This procedure is invoked to process "tsv::exists" command. 1610 * See the user documentation for details on what it does. 1611 * 1612 * Results: 1613 * A standard Tcl result. 1614 * 1615 * Side effects: 1616 * See the user documentation. 1617 * 1618 *----------------------------------------------------------------------------- 1619 */ 1620 1621static int 1622SvExistsObjCmd(arg, interp, objc, objv) 1623 ClientData arg; /* Pointer to object container. */ 1624 Tcl_Interp *interp; /* Current interpreter. */ 1625 int objc; /* Number of arguments. */ 1626 Tcl_Obj *const objv[]; /* Argument objects. */ 1627{ 1628 int off, ret; 1629 Container *svObj = (Container*)arg; 1630 1631 /* 1632 * Syntax: 1633 * tsv::exists array key 1634 * $object exists 1635 */ 1636 1637 ret = Sv_GetContainer(interp, objc, objv, &svObj, &off, 0); 1638 switch (ret) { 1639 case TCL_BREAK: /* Array/key not found */ 1640 Tcl_ResetResult(interp); 1641 Tcl_SetBooleanObj(Tcl_GetObjResult(interp), 0); 1642 return TCL_OK; 1643 case TCL_ERROR: 1644 return TCL_ERROR; 1645 } 1646 1647 Tcl_ResetResult(interp); 1648 Tcl_SetBooleanObj(Tcl_GetObjResult(interp), 1); 1649 1650 return Sv_PutContainer(interp, svObj, SV_UNCHANGED); 1651} 1652 1653/* 1654 *----------------------------------------------------------------------------- 1655 * 1656 * SvSetObjCmd -- 1657 * 1658 * This procedure is invoked to process the "tsv::set" command. 1659 * See the user documentation for details on what it does. 1660 * 1661 * Results: 1662 * A standard Tcl result. 1663 * 1664 * Side effects: 1665 * See the user documentation. 1666 * 1667 *----------------------------------------------------------------------------- 1668 */ 1669 1670static int 1671SvSetObjCmd(arg, interp, objc, objv) 1672 ClientData arg; /* Pointer to object container */ 1673 Tcl_Interp *interp; /* Current interpreter. */ 1674 int objc; /* Number of arguments. */ 1675 Tcl_Obj *const objv[]; /* Argument objects. */ 1676{ 1677 int ret, off, flg, mode; 1678 Tcl_Obj *val; 1679 Container *svObj = (Container*)arg; 1680 1681 /* 1682 * Syntax: 1683 * tsv::set array key ?value? 1684 * $object set ?value? 1685 */ 1686 1687 ret = Sv_GetContainer(interp, objc, objv, &svObj, &off, 0); 1688 switch (ret) { 1689 case TCL_BREAK: 1690 if ((objc - off) == 0) { 1691 return TCL_ERROR; 1692 } else { 1693 Tcl_ResetResult(interp); 1694 flg = FLAGS_CREATEARRAY | FLAGS_CREATEVAR; 1695 ret = Sv_GetContainer(interp, objc, objv, &svObj, &off, flg); 1696 if (ret != TCL_OK) { 1697 return TCL_ERROR; 1698 } 1699 } 1700 break; 1701 case TCL_ERROR: 1702 return TCL_ERROR; 1703 } 1704 if ((objc - off)) { 1705 val = objv[off]; 1706 Tcl_DecrRefCount(svObj->tclObj); 1707 svObj->tclObj = Sv_DuplicateObj(val); 1708 Tcl_IncrRefCount(svObj->tclObj); 1709 mode = SV_CHANGED; 1710 } else { 1711 val = Sv_DuplicateObj(svObj->tclObj); 1712 mode = SV_UNCHANGED; 1713 } 1714 1715 Tcl_SetObjResult(interp, val); 1716 1717 return Sv_PutContainer(interp, svObj, mode); 1718} 1719 1720/* 1721 *----------------------------------------------------------------------------- 1722 * 1723 * SvIncrObjCmd -- 1724 * 1725 * This procedure is invoked to process the "tsv::incr" command. 1726 * See the user documentation for details on what it does. 1727 * 1728 * Results: 1729 * A standard Tcl result. 1730 * 1731 * Side effects: 1732 * See the user documentation. 1733 * 1734 *----------------------------------------------------------------------------- 1735 */ 1736 1737static int 1738SvIncrObjCmd(arg, interp, objc, objv) 1739 ClientData arg; /* Pointer to object container */ 1740 Tcl_Interp *interp; /* Current interpreter. */ 1741 int objc; /* Number of arguments. */ 1742 Tcl_Obj *const objv[]; /* Argument objects. */ 1743{ 1744 int off, ret, flg, new = 0; 1745 long incrValue = 1, currValue = 0; 1746 Container *svObj = (Container*)arg; 1747 1748 /* 1749 * Syntax: 1750 * tsv::incr array key ?increment? 1751 * $object incr ?increment? 1752 */ 1753 1754 ret = Sv_GetContainer(interp, objc, objv, &svObj, &off, 0); 1755 if (ret != TCL_OK) { 1756 if (ret != TCL_BREAK) { 1757 return TCL_ERROR; 1758 } 1759 flg = FLAGS_CREATEARRAY | FLAGS_CREATEVAR; 1760 Tcl_ResetResult(interp); 1761 ret = Sv_GetContainer(interp, objc, objv, &svObj, &off, flg); 1762 if (ret != TCL_OK) { 1763 return TCL_ERROR; 1764 } 1765 new = 1; 1766 } 1767 if ((objc - off)) { 1768 ret = Tcl_GetLongFromObj(interp, objv[off], &incrValue); 1769 if (ret != TCL_OK) { 1770 goto cmd_err; 1771 } 1772 } 1773 if (new) { 1774 currValue = 0; 1775 } else { 1776 ret = Tcl_GetLongFromObj(interp, svObj->tclObj, &currValue); 1777 if (ret != TCL_OK) { 1778 goto cmd_err; 1779 } 1780 } 1781 1782 incrValue += currValue; 1783 Tcl_SetLongObj(svObj->tclObj, incrValue); 1784 Tcl_ResetResult(interp); 1785 Tcl_SetLongObj(Tcl_GetObjResult(interp), incrValue); 1786 1787 return Sv_PutContainer(interp, svObj, SV_CHANGED); 1788 1789 cmd_err: 1790 return Sv_PutContainer(interp, svObj, SV_ERROR); 1791} 1792 1793/* 1794 *----------------------------------------------------------------------------- 1795 * 1796 * SvAppendObjCmd -- 1797 * 1798 * This procedure is invoked to process the "tsv::append" command. 1799 * See the user documentation for details on what it does. 1800 * 1801 * Results: 1802 * A standard Tcl result. 1803 * 1804 * Side effects: 1805 * See the user documentation. 1806 * 1807 *----------------------------------------------------------------------------- 1808 */ 1809 1810static int 1811SvAppendObjCmd(arg, interp, objc, objv) 1812 ClientData arg; /* Pointer to object container */ 1813 Tcl_Interp *interp; /* Current interpreter. */ 1814 int objc; /* Number of arguments. */ 1815 Tcl_Obj *const objv[]; /* Argument objects. */ 1816{ 1817 int i, off, flg, ret; 1818 Container *svObj = (Container*)arg; 1819 1820 /* 1821 * Syntax: 1822 * tsv::append array key value ?value ...? 1823 * $object append value ?value ...? 1824 */ 1825 1826 flg = FLAGS_CREATEARRAY | FLAGS_CREATEVAR; 1827 ret = Sv_GetContainer(interp, objc, objv, &svObj, &off, flg); 1828 if (ret != TCL_OK) { 1829 return TCL_ERROR; 1830 } 1831 if ((objc - off) < 1) { 1832 Tcl_WrongNumArgs(interp, off, objv, "value ?value ...?"); 1833 goto cmd_err; 1834 } 1835 for (i = off; i < objc; ++i) { 1836 Tcl_AppendObjToObj(svObj->tclObj, Sv_DuplicateObj(objv[i])); 1837 } 1838 1839 Tcl_SetObjResult(interp, Sv_DuplicateObj(svObj->tclObj)); 1840 1841 return Sv_PutContainer(interp, svObj, SV_CHANGED); 1842 1843 cmd_err: 1844 return Sv_PutContainer(interp, svObj, SV_ERROR); 1845} 1846 1847/* 1848 *----------------------------------------------------------------------------- 1849 * 1850 * SvPopObjCmd -- 1851 * 1852 * This procedure is invoked to process "tsv::pop" command. 1853 * See the user documentation for details on what it does. 1854 * 1855 * Results: 1856 * A standard Tcl result. 1857 * 1858 * Side effects: 1859 * See the user documentation. 1860 * 1861 *----------------------------------------------------------------------------- 1862 */ 1863 1864static int 1865SvPopObjCmd(arg, interp, objc, objv) 1866 ClientData arg; /* Pointer to object container */ 1867 Tcl_Interp *interp; /* Current interpreter. */ 1868 int objc; /* Number of arguments. */ 1869 Tcl_Obj *const objv[]; /* Argument objects. */ 1870{ 1871 int ret, off; 1872 Tcl_Obj *retObj; 1873 Array *arrayPtr = NULL; 1874 Container *svObj = (Container*)arg; 1875 1876 /* 1877 * Syntax: 1878 * tsv::pop array key ?var? 1879 * $object pop ?var? 1880 * 1881 * Note: the object command will run into error next time ! 1882 */ 1883 1884 ret = Sv_GetContainer(interp, objc, objv, &svObj, &off, 0); 1885 switch (ret) { 1886 case TCL_BREAK: 1887 if ((objc - off) == 0) { 1888 return TCL_ERROR; 1889 } else { 1890 Tcl_ResetResult(interp); 1891 Tcl_SetIntObj(Tcl_GetObjResult(interp), 0); 1892 return TCL_OK; 1893 } 1894 case TCL_ERROR: 1895 return TCL_ERROR; 1896 } 1897 1898 arrayPtr = svObj->arrayPtr; 1899 1900 retObj = svObj->tclObj; 1901 svObj->tclObj = NULL; 1902 1903 if (DeleteContainer(svObj) != TCL_OK) { 1904 if (svObj->arrayPtr->psPtr) { 1905 PsStore *psPtr = svObj->arrayPtr->psPtr; 1906 char *err = (*psPtr->psError)(psPtr->psHandle); 1907 Tcl_SetObjResult(interp, Tcl_NewStringObj(err,-1)); 1908 } 1909 ret = TCL_ERROR; 1910 goto cmd_exit; 1911 } 1912 1913 if ((objc - off) == 0) { 1914 Tcl_SetObjResult(interp, retObj); 1915 } else { 1916 if (Tcl_ObjSetVar2(interp, objv[off], NULL, retObj, 0) == NULL) { 1917 ret = TCL_ERROR; 1918 goto cmd_exit; 1919 } 1920 Tcl_ResetResult(interp); 1921 Tcl_SetIntObj(Tcl_GetObjResult(interp), 1); 1922 } 1923 1924 cmd_exit: 1925 Tcl_DecrRefCount(retObj); 1926 UnlockArray(arrayPtr); 1927 1928 return ret; 1929} 1930 1931/* 1932 *----------------------------------------------------------------------------- 1933 * 1934 * SvMoveObjCmd -- 1935 * 1936 * This procedure is invoked to process the "tsv::move" command. 1937 * See the user documentation for details on what it does. 1938 * 1939 * 1940 * Results: 1941 * A standard Tcl result. 1942 * 1943 * Side effects: 1944 * See the user documentation. 1945 * 1946 *----------------------------------------------------------------------------- 1947 */ 1948 1949static int 1950SvMoveObjCmd(arg, interp, objc, objv) 1951 ClientData arg; /* Pointer to object container. */ 1952 Tcl_Interp *interp; /* Current interpreter. */ 1953 int objc; /* Number of arguments. */ 1954 Tcl_Obj *const objv[]; /* Argument objects. */ 1955{ 1956 int ret, off, new; 1957 const char *toKey; 1958 Tcl_HashEntry *hPtr; 1959 Container *svObj = (Container*)arg; 1960 1961 /* 1962 * Syntax: 1963 * tsv::move array key to 1964 * $object move to 1965 */ 1966 1967 ret = Sv_GetContainer(interp, objc, objv, &svObj, &off, 0); 1968 if (ret != TCL_OK) { 1969 return TCL_ERROR; 1970 } 1971 1972 toKey = Tcl_GetString(objv[off]); 1973 hPtr = Tcl_CreateHashEntry(&svObj->arrayPtr->vars, toKey, &new); 1974 1975 if (!new) { 1976 Tcl_AppendResult(interp, "key \"", toKey, "\" exists", NULL); 1977 goto cmd_err; 1978 } 1979 if (svObj->entryPtr) { 1980 char *key = Tcl_GetHashKey(&svObj->arrayPtr->vars, svObj->entryPtr); 1981 if (svObj->arrayPtr->psPtr) { 1982 PsStore *psPtr = svObj->arrayPtr->psPtr; 1983 if ((*psPtr->psDelete)(psPtr->psHandle, key) == -1) { 1984 char *err = (*psPtr->psError)(psPtr->psHandle); 1985 Tcl_SetObjResult(interp, Tcl_NewStringObj(err, -1)); 1986 return TCL_ERROR; 1987 } 1988 } 1989 Tcl_DeleteHashEntry(svObj->entryPtr); 1990 } 1991 1992 svObj->entryPtr = hPtr; 1993 Tcl_SetHashValue(hPtr, svObj); 1994 1995 return Sv_PutContainer(interp, svObj, SV_CHANGED); 1996 1997 cmd_err: 1998 return Sv_PutContainer(interp, svObj, SV_ERROR); 1999 2000} 2001 2002/* 2003 *---------------------------------------------------------------------- 2004 * 2005 * SvLockObjCmd -- 2006 * 2007 * This procedure is invoked to process "tsv::lock" Tcl command. 2008 * See the user documentation for details on what it does. 2009 * 2010 * Results: 2011 * A standard Tcl result. 2012 * 2013 * Side effects: 2014 * See the user documentation. 2015 * 2016 *---------------------------------------------------------------------- 2017 */ 2018 2019static int 2020SvLockObjCmd(dummy, interp, objc, objv) 2021 ClientData dummy; /* Not used. */ 2022 Tcl_Interp *interp; /* Current interpreter. */ 2023 int objc; /* Number of arguments. */ 2024 Tcl_Obj *const objv[]; /* Argument objects. */ 2025{ 2026 int ret; 2027 Tcl_Obj *scriptObj; 2028 Bucket *bucketPtr; 2029 Array *arrayPtr = NULL; 2030 2031 /* 2032 * Syntax: 2033 * 2034 * tsv::lock array arg ?arg ...? 2035 */ 2036 2037 if (objc < 3) { 2038 Tcl_AppendResult(interp, "wrong # args: should be \"", 2039 Tcl_GetString(objv[0]), "array arg ?arg...?\"", NULL); 2040 return TCL_ERROR; 2041 } 2042 2043 arrayPtr = LockArray(interp, Tcl_GetString(objv[1]), FLAGS_CREATEARRAY); 2044 bucketPtr = arrayPtr->bucketPtr; 2045 2046 /* 2047 * Evaluate passed arguments as Tcl script. Note that 2048 * Tcl_EvalObjEx throws away the passed object by 2049 * doing an decrement reference count on it. This also 2050 * means we need not build object bytecode rep. 2051 */ 2052 2053 if (objc == 3) { 2054 scriptObj = Tcl_DuplicateObj(objv[2]); 2055 } else { 2056 scriptObj = Tcl_ConcatObj(objc-2, objv + 2); 2057 } 2058 2059 Tcl_AllowExceptions(interp); 2060 ret = Tcl_EvalObjEx(interp, scriptObj, TCL_EVAL_DIRECT); 2061 2062 if (ret == TCL_ERROR) { 2063 char msg[32 + TCL_INTEGER_SPACE]; 2064 sprintf(msg, "\n (\"eval\" body line %d)", ERRORLINE(interp)); 2065 Tcl_AddObjErrorInfo(interp, msg, -1); 2066 } 2067 2068 /* 2069 * We unlock the bucket directly, w/o going to Sv_Unlock() 2070 * since it needs the array which may be unset by the script. 2071 */ 2072 2073 UNLOCK_BUCKET(bucketPtr); 2074 2075 return ret; 2076} 2077 2078/* 2079 *----------------------------------------------------------------------------- 2080 * 2081 * Sv_RegisterStdCommands -- 2082 * 2083 * Register standard shared variable commands 2084 * 2085 * Results: 2086 * A standard Tcl result. 2087 * 2088 * Side effects: 2089 * Memory gets allocated 2090 * 2091 *----------------------------------------------------------------------------- 2092 */ 2093 2094static void 2095SvRegisterStdCommands(void) 2096{ 2097 static int initialized = 0; 2098 2099 if (initialized == 0) { 2100 Tcl_MutexLock(&initMutex); 2101 if (initialized == 0) { 2102 Sv_RegisterCommand("var", SvObjObjCmd, NULL, NULL); 2103 Sv_RegisterCommand("object", SvObjObjCmd, NULL, NULL); 2104 Sv_RegisterCommand("set", SvSetObjCmd, NULL, NULL); 2105 Sv_RegisterCommand("unset", SvUnsetObjCmd, NULL, NULL); 2106 Sv_RegisterCommand("get", SvGetObjCmd, NULL, NULL); 2107 Sv_RegisterCommand("incr", SvIncrObjCmd, NULL, NULL); 2108 Sv_RegisterCommand("exists", SvExistsObjCmd, NULL, NULL); 2109 Sv_RegisterCommand("append", SvAppendObjCmd, NULL, NULL); 2110 Sv_RegisterCommand("array", SvArrayObjCmd, NULL, NULL); 2111 Sv_RegisterCommand("names", SvNamesObjCmd, NULL, NULL); 2112 Sv_RegisterCommand("pop", SvPopObjCmd, NULL, NULL); 2113 Sv_RegisterCommand("move", SvMoveObjCmd, NULL, NULL); 2114 Sv_RegisterCommand("lock", SvLockObjCmd, NULL, NULL); 2115 initialized = 1; 2116 } 2117 Tcl_MutexUnlock(&initMutex); 2118 } 2119} 2120 2121/* 2122 *----------------------------------------------------------------------------- 2123 * 2124 * Sv_Init -- 2125 * 2126 * Creates commands in current interpreter. 2127 * 2128 * Results: 2129 * None. 2130 * 2131 * Side effects 2132 * Many new command created in current interpreter. Global data 2133 * structures used by them initialized as well. 2134 * 2135 *----------------------------------------------------------------------------- 2136 */ 2137int 2138Sv_Init (interp) 2139 Tcl_Interp *interp; 2140{ 2141 register int i; 2142 Bucket *bucketPtr; 2143 SvCmdInfo *cmdPtr; 2144 2145 /* 2146 * Add keyed-list datatype 2147 */ 2148 2149 TclX_KeyedListInit(interp); 2150 Sv_RegisterKeylistCommands(); 2151 2152 /* 2153 * Register standard (nsv_* compatible) and our 2154 * own extensive set of list manipulating commands 2155 */ 2156 2157 SvRegisterStdCommands(); 2158 Sv_RegisterListCommands(); 2159 2160 /* 2161 * Get Tcl object types. These are used 2162 * in custom object duplicator function. 2163 */ 2164 2165 booleanObjTypePtr = Tcl_GetObjType("boolean"); 2166 byteArrayObjTypePtr = Tcl_GetObjType("bytearray"); 2167 doubleObjTypePtr = Tcl_GetObjType("double"); 2168 intObjTypePtr = Tcl_GetObjType("int"); 2169 stringObjTypePtr = Tcl_GetObjType("string"); 2170 2171 /* 2172 * Plug-in registered commands in current interpreter 2173 */ 2174 2175 for (cmdPtr = svCmdInfo; cmdPtr; cmdPtr = cmdPtr->nextPtr) { 2176 Tcl_CreateObjCommand(interp, cmdPtr->cmdName, cmdPtr->objProcPtr, 2177 (ClientData)cmdPtr->clientData, (Tcl_CmdDeleteProc*)0); 2178 } 2179 2180 /* 2181 * Create array of buckets and initialize each bucket 2182 */ 2183 2184 if (buckets == NULL) { 2185 Tcl_MutexLock(&bucketsMutex); 2186 if (buckets == NULL) { 2187 buckets = (Bucket *)Tcl_Alloc(sizeof(Bucket) * NUMBUCKETS); 2188 for (i = 0; i < NUMBUCKETS; ++i) { 2189 bucketPtr = &buckets[i]; 2190 memset(bucketPtr, 0, sizeof(Bucket)); 2191 Tcl_InitHashTable(&bucketPtr->arrays, TCL_STRING_KEYS); 2192 Tcl_InitHashTable(&bucketPtr->handles, TCL_ONE_WORD_KEYS); 2193 } 2194 2195 /* 2196 * There is no other way to get Sv_tclEmptyStringRep 2197 * pointer value w/o this trick. 2198 */ 2199 2200 { 2201 Tcl_Obj *dummy = Tcl_NewObj(); 2202 Sv_tclEmptyStringRep = dummy->bytes; 2203 Tcl_DecrRefCount(dummy); 2204 } 2205 2206#ifdef HAVE_GDBM 2207 /* 2208 * Register persistent store handlers 2209 */ 2210 Sv_RegisterGdbmStore(); 2211#endif 2212 } 2213 Tcl_MutexUnlock(&bucketsMutex); 2214 } 2215 2216 return TCL_OK; 2217} 2218 2219int Sv_SafeInit (interp) 2220 Tcl_Interp *interp; 2221{ 2222 return (Sv_Init(interp)); 2223} 2224 2225 2226#ifdef SV_FINALIZE 2227/* 2228 * Left for reference, but unused since multithreaded finalization is 2229 * unsolvable in the general case. Brave souls can revive this by 2230 * installing a late exit handler on Thread's behalf, bringing the 2231 * function back onto the Tcl_Finalize (but not Tcl_Exit) path. 2232 */ 2233 2234/* 2235 *----------------------------------------------------------------------------- 2236 * 2237 * SvFinalize -- 2238 * 2239 * Unset all arrays and reclaim all buckets. 2240 * 2241 * Results: 2242 * None. 2243 * 2244 * Side effects 2245 * Memory gets reclaimed. 2246 * 2247 *----------------------------------------------------------------------------- 2248 */ 2249 2250static void 2251SvFinalize (clientData) 2252 ClientData clientData; 2253{ 2254 register int i; 2255 SvCmdInfo *cmdPtr; 2256 RegType *regPtr; 2257 2258 Tcl_HashEntry *hashPtr; 2259 Tcl_HashSearch search; 2260 2261 /* 2262 * Reclaim memory for shared arrays 2263 */ 2264 2265 if (buckets != NULL) { 2266 Tcl_MutexLock(&bucketsMutex); 2267 if (buckets != NULL) { 2268 for (i = 0; i < NUMBUCKETS; ++i) { 2269 Bucket *bucketPtr = &buckets[i]; 2270 hashPtr = Tcl_FirstHashEntry(&bucketPtr->arrays, &search); 2271 while (hashPtr != NULL) { 2272 Array *arrayPtr = (Array*)Tcl_GetHashValue(hashPtr); 2273 UnlockArray(arrayPtr); 2274 DeleteArray(arrayPtr); 2275 hashPtr = Tcl_NextHashEntry(&search); 2276 } 2277 if (bucketPtr->lock) { 2278 Sp_RecursiveMutexFinalize(&bucketPtr->lock); 2279 } 2280 SvFinalizeContainers(bucketPtr); 2281 Tcl_DeleteHashTable(&bucketPtr->handles); 2282 Tcl_DeleteHashTable(&bucketPtr->arrays); 2283 } 2284 Tcl_Free((char *)buckets), buckets = NULL; 2285 } 2286 buckets = NULL; 2287 Tcl_MutexUnlock(&bucketsMutex); 2288 } 2289 2290 Tcl_MutexLock(&svMutex); 2291 2292 /* 2293 * Reclaim memory for registered commands 2294 */ 2295 2296 if (svCmdInfo != NULL) { 2297 cmdPtr = svCmdInfo; 2298 while (cmdPtr) { 2299 SvCmdInfo *tmpPtr = cmdPtr->nextPtr; 2300 Tcl_Free((char*)cmdPtr); 2301 cmdPtr = tmpPtr; 2302 } 2303 svCmdInfo = NULL; 2304 } 2305 2306 /* 2307 * Reclaim memory for registered object types 2308 */ 2309 2310 if (regType != NULL) { 2311 regPtr = regType; 2312 while (regPtr) { 2313 RegType *tmpPtr = regPtr->nextPtr; 2314 Tcl_Free((char*)regPtr); 2315 regPtr = tmpPtr; 2316 } 2317 regType = NULL; 2318 } 2319 2320 Tcl_MutexUnlock(&svMutex); 2321} 2322#endif /* SV_FINALIZE */ 2323 2324/* EOF $RCSfile: threadSvCmd.c,v $ */ 2325 2326/* Emacs Setup Variables */ 2327/* Local Variables: */ 2328/* mode: C */ 2329/* indent-tabs-mode: nil */ 2330/* c-basic-offset: 4 */ 2331/* End: */ 2332 2333