diff options
-rw-r--r-- | includes/HsFFI.h | 2 | ||||
-rw-r--r-- | libraries/base/GHC/StaticPtr.hs | 6 | ||||
-rw-r--r-- | rts/FileLock.c | 12 | ||||
-rw-r--r-- | rts/Hash.c | 124 | ||||
-rw-r--r-- | rts/Hash.h | 46 | ||||
-rw-r--r-- | rts/Hpc.c | 12 | ||||
-rw-r--r-- | rts/Linker.c | 12 | ||||
-rw-r--r-- | rts/LinkerInternals.h | 8 | ||||
-rw-r--r-- | rts/RtsSymbolInfo.c | 6 | ||||
-rw-r--r-- | rts/StaticPtrTable.c | 17 |
10 files changed, 157 insertions, 88 deletions
diff --git a/includes/HsFFI.h b/includes/HsFFI.h index 32523b2c83..d021e6fb42 100644 --- a/includes/HsFFI.h +++ b/includes/HsFFI.h @@ -126,7 +126,7 @@ extern void hs_free_stable_ptr_unsafe (HsStablePtr sp); extern void hs_free_stable_ptr (HsStablePtr sp); extern void hs_free_fun_ptr (HsFunPtr fp); -extern StgPtr hs_spt_lookup(StgWord64 key1, StgWord64 key2); +extern StgPtr hs_spt_lookup(StgWord64 key[2]); extern int hs_spt_keys(StgPtr keys[], int szKeys); extern int hs_spt_key_count (void); diff --git a/libraries/base/GHC/StaticPtr.hs b/libraries/base/GHC/StaticPtr.hs index ee5ba786f6..b8d5c116d1 100644 --- a/libraries/base/GHC/StaticPtr.hs +++ b/libraries/base/GHC/StaticPtr.hs @@ -48,7 +48,7 @@ module GHC.StaticPtr ) where import Foreign.C.Types (CInt(..)) -import Foreign.Marshal (allocaArray, peekArray) +import Foreign.Marshal (allocaArray, peekArray, withArray) import GHC.Ptr (Ptr(..), nullPtr) import GHC.Fingerprint (Fingerprint(..)) import GHC.Prim @@ -87,13 +87,13 @@ staticKey (StaticPtr w0 w1 _ _) = Fingerprint (W64# w0) (W64# w1) -- unsafeLookupStaticPtr :: StaticKey -> IO (Maybe (StaticPtr a)) unsafeLookupStaticPtr (Fingerprint w1 w2) = do - ptr@(Ptr addr) <- hs_spt_lookup w1 w2 + ptr@(Ptr addr) <- withArray [w1, w2] hs_spt_lookup if (ptr == nullPtr) then return Nothing else case addrToAny# addr of (# spe #) -> return (Just spe) -foreign import ccall unsafe hs_spt_lookup :: Word64 -> Word64 -> IO (Ptr a) +foreign import ccall unsafe hs_spt_lookup :: Ptr Word64 -> IO (Ptr a) -- | A class for things buildable from static pointers. class IsStatic p where diff --git a/rts/FileLock.c b/rts/FileLock.c index ac21bea206..351d2a58f7 100644 --- a/rts/FileLock.c +++ b/rts/FileLock.c @@ -34,14 +34,14 @@ static HashTable *fd_hash; static Mutex file_lock_mutex; #endif -static int cmpLocks(StgWord w1, StgWord w2) +STATIC_INLINE int cmpLocks(StgWord w1, StgWord w2) { Lock *l1 = (Lock *)w1; Lock *l2 = (Lock *)w2; return (l1->device == l2->device && l1->inode == l2->inode); } -static int hashLock(const HashTable *table, StgWord w) +STATIC_INLINE int hashLock(const HashTable *table, StgWord w) { Lock *l = (Lock *)w; StgWord key = l->inode ^ (l->inode >> 32) ^ l->device ^ (l->device >> 32); @@ -52,7 +52,7 @@ static int hashLock(const HashTable *table, StgWord w) void initFileLocking(void) { - obj_hash = allocHashTable_(hashLock, cmpLocks); + obj_hash = allocHashTable(); fd_hash = allocHashTable(); /* ordinary word-based table */ #if defined(THREADED_RTS) initMutex(&file_lock_mutex); @@ -85,7 +85,7 @@ lockFile(int fd, StgWord64 dev, StgWord64 ino, int for_writing) key.device = dev; key.inode = ino; - lock = lookupHashTable(obj_hash, (StgWord)&key); + lock = lookupHashTable_(obj_hash, (StgWord)&key, hashLock, cmpLocks); if (lock == NULL) { @@ -93,7 +93,7 @@ lockFile(int fd, StgWord64 dev, StgWord64 ino, int for_writing) lock->device = dev; lock->inode = ino; lock->readers = for_writing ? -1 : 1; - insertHashTable(obj_hash, (StgWord)lock, (void *)lock); + insertHashTable_(obj_hash, (StgWord)lock, (void *)lock, hashLock); insertHashTable(fd_hash, fd, lock); RELEASE_LOCK(&file_lock_mutex); return 0; @@ -135,7 +135,7 @@ unlockFile(int fd) } if (lock->readers == 0) { - removeHashTable(obj_hash, (StgWord)lock, NULL); + removeHashTable_(obj_hash, (StgWord)lock, NULL, hashLock, cmpLocks); stgFree(lock); } removeHashTable(fd_hash, fd, NULL); diff --git a/rts/Hash.c b/rts/Hash.c index 2f611c9079..4e11228961 100644 --- a/rts/Hash.c +++ b/rts/Hash.c @@ -49,22 +49,25 @@ struct hashtable { HashList **dir[HDIRSIZE]; /* Directory of segments */ HashList *freeList; /* free list of HashLists */ HashListChunk *chunks; - HashFunction *hash; /* hash function */ - CompareFunction *compare; /* key comparison function */ }; +/* Create an identical structure, but is distinct on a type level, + * for string hash table. Since it's a direct embedding of + * a hashtable and not a reference, there shouldn't be + * any overhead post-compilation. */ +struct strhashtable { struct hashtable table; }; + /* ----------------------------------------------------------------------------- * Hash first using the smaller table. If the bucket is less than the * next bucket to be split, re-hash using the larger table. * -------------------------------------------------------------------------- */ - int hashWord(const HashTable *table, StgWord key) { int bucket; /* Strip the boring zero bits */ - key /= sizeof(StgWord); + key >>= sizeof(StgWord); /* Mod the size of the hash table (a power of 2) */ bucket = key & table->mask1; @@ -97,13 +100,13 @@ hashStr(const HashTable *table, StgWord w) return bucket; } -static int +STATIC_INLINE int compareWord(StgWord key1, StgWord key2) { return (key1 == key2); } -static int +STATIC_INLINE int compareStr(StgWord key1, StgWord key2) { return (strcmp((char *)key1, (char *)key2) == 0); @@ -114,7 +117,7 @@ compareStr(StgWord key1, StgWord key2) * Allocate a new segment of the dynamically growing hash table. * -------------------------------------------------------------------------- */ -static void +STATIC_INLINE void allocSegment(HashTable *table, int segment) { table->dir[segment] = stgMallocBytes(HSEGSIZE * sizeof(HashList *), @@ -128,8 +131,8 @@ allocSegment(HashTable *table, int segment) * by @table->split@ is affected by the expansion. * -------------------------------------------------------------------------- */ -static void -expand(HashTable *table) +STATIC_INLINE void +expand(HashTable *table, HashFunction f) { int oldsegment; int oldindex; @@ -170,7 +173,7 @@ expand(HashTable *table) old = new = NULL; for (hl = table->dir[oldsegment][oldindex]; hl != NULL; hl = next) { next = hl->next; - if (table->hash(table, hl->key) == newbucket) { + if (f(table, hl->key) == newbucket) { hl->next = new; new = hl; } else { @@ -184,19 +187,20 @@ expand(HashTable *table) return; } -void * -lookupHashTable(const HashTable *table, StgWord key) +STATIC_INLINE void* +lookupHashTable_inlined(const HashTable *table, StgWord key, + HashFunction f, CompareFunction cmp) { int bucket; int segment; int index; + HashList *hl; - bucket = table->hash(table, key); + bucket = f(table, key); segment = bucket / HSEGSIZE; index = bucket % HSEGSIZE; - CompareFunction *cmp = table->compare; for (hl = table->dir[segment][index]; hl != NULL; hl = hl->next) { if (cmp(hl->key, key)) return (void *) hl->data; @@ -206,6 +210,26 @@ lookupHashTable(const HashTable *table, StgWord key) return NULL; } +void * +lookupHashTable_(const HashTable *table, StgWord key, + HashFunction f, CompareFunction cmp) +{ + return lookupHashTable_inlined(table, key, f, cmp); +} + +void * +lookupHashTable(const HashTable *table, StgWord key) +{ + return lookupHashTable_inlined(table, key, hashWord, compareWord); +} + +void * +lookupStrHashTable(const StrHashTable* table, const char* key) +{ + return lookupHashTable_inlined(&table->table, (StgWord) key, + hashStr, compareStr); +} + // Puts up to szKeys keys of the hash table into the given array. Returns the // actual amount of keys that have been retrieved. // @@ -273,8 +297,9 @@ freeHashList (HashTable *table, HashList *hl) table->freeList = hl; } -void -insertHashTable(HashTable *table, StgWord key, const void *data) +STATIC_INLINE void +insertHashTable_inlined(HashTable *table, StgWord key, + const void *data, HashFunction f) { int bucket; int segment; @@ -287,9 +312,9 @@ insertHashTable(HashTable *table, StgWord key, const void *data) /* When the average load gets too high, we expand the table */ if (++table->kcount >= HLOAD * table->bcount) - expand(table); + expand(table, f); - bucket = table->hash(table, key); + bucket = f(table, key); segment = bucket / HSEGSIZE; index = bucket % HSEGSIZE; @@ -299,11 +324,30 @@ insertHashTable(HashTable *table, StgWord key, const void *data) hl->data = data; hl->next = table->dir[segment][index]; table->dir[segment][index] = hl; +} +void +insertHashTable_(HashTable *table, StgWord key, + const void *data, HashFunction f) +{ + return insertHashTable_inlined(table, key, data, f); } -void * -removeHashTable(HashTable *table, StgWord key, const void *data) +void +insertHashTable(HashTable *table, StgWord key, const void *data) +{ + insertHashTable_inlined(table, key, data, hashWord); +} + +void +insertStrHashTable(StrHashTable *table, const char * key, const void *data) +{ + insertHashTable_inlined(&table->table, (StgWord) key, data, hashStr); +} + +STATIC_INLINE void* +removeHashTable_inlined(HashTable *table, StgWord key, const void *data, + HashFunction f, CompareFunction cmp) { int bucket; int segment; @@ -311,12 +355,12 @@ removeHashTable(HashTable *table, StgWord key, const void *data) HashList *hl; HashList *prev = NULL; - bucket = table->hash(table, key); + bucket = f(table, key); segment = bucket / HSEGSIZE; index = bucket % HSEGSIZE; for (hl = table->dir[segment][index]; hl != NULL; hl = hl->next) { - if (table->compare(hl->key,key) && (data == NULL || hl->data == data)) { + if (cmp(hl->key, key) && (data == NULL || hl->data == data)) { if (prev == NULL) table->dir[segment][index] = hl->next; else @@ -333,6 +377,26 @@ removeHashTable(HashTable *table, StgWord key, const void *data) return NULL; } +void* +removeHashTable_(HashTable *table, StgWord key, const void *data, + HashFunction f, CompareFunction cmp) +{ + return removeHashTable_inlined(table, key, data, f, cmp); +} + +void * +removeHashTable(HashTable *table, StgWord key, const void *data) +{ + return removeHashTable_inlined(table, key, data, hashWord, compareWord); +} + +void * +removeStrHashTable(StrHashTable *table, const char * key, const void *data) +{ + return removeHashTable_inlined(&table->table, (StgWord) key, + data, hashStr, compareStr); +} + /* ----------------------------------------------------------------------------- * When we free a hash table, we are also good enough to free the * data part of each (key, data) pair, as long as our caller can tell @@ -406,7 +470,7 @@ mapHashTable(HashTable *table, void *data, MapHashFn fn) * -------------------------------------------------------------------------- */ HashTable * -allocHashTable_(HashFunction *hash, CompareFunction *compare) +allocHashTable(void) { HashTable *table; HashList **hb; @@ -426,24 +490,10 @@ allocHashTable_(HashFunction *hash, CompareFunction *compare) table->bcount = HSEGSIZE; table->freeList = NULL; table->chunks = NULL; - table->hash = hash; - table->compare = compare; return table; } -HashTable * -allocHashTable(void) -{ - return allocHashTable_(hashWord, compareWord); -} - -HashTable * -allocStrHashTable(void) -{ - return allocHashTable_(hashStr, compareStr); -} - void exitHashTable(void) { diff --git a/rts/Hash.h b/rts/Hash.h index be388fb62f..59e2e22a09 100644 --- a/rts/Hash.h +++ b/rts/Hash.h @@ -11,6 +11,7 @@ #include "BeginPrivate.h" typedef struct hashtable HashTable; /* abstract */ +typedef struct strhashtable StrHashTable; /* Hash table access where the keys are StgWords. * Values are passed into the hash table and stored as `const void *` values, @@ -40,27 +41,44 @@ void mapHashTable(HashTable *table, void *data, MapHashFn fn); * assumed to be allocated by the caller, and mustn't be deallocated * until the corresponding hash table entry has been removed). */ -HashTable * allocStrHashTable ( void ); - -#define lookupStrHashTable(table, key) \ - (lookupHashTable(table, (StgWord)key)) - -#define insertStrHashTable(table, key, data) \ - (insertHashTable(table, (StgWord)key, data)) - -#define removeStrHashTable(table, key, data) \ - (removeHashTable(table, (StgWord)key, data)) - -/* Hash tables for arbitrary keys */ +INLINE_HEADER StrHashTable * allocStrHashTable ( void ) +{ + return (StrHashTable*) allocHashTable(); +} + +void insertStrHashTable ( StrHashTable *table, const char * key, + const void *data ); +void * lookupStrHashTable ( const StrHashTable *table, const char * key); +void * removeStrHashTable ( StrHashTable *table, const char * key, + const void *data ); + +/* + * Hash tables for arbitrary key types. + * Generally, these functions allow for the specification of the + * HashFunction and CompareFunction. It's recommended that those + * are inlinable so there's a chance the compiler can discard + * some parameter-passing, as well as function calls, though note + * it's not guaranteed. Either way, the functions are parameters + * as the types should be statically known and thus + * storing them is unnecessary. + */ typedef int HashFunction(const HashTable *table, StgWord key); typedef int CompareFunction(StgWord key1, StgWord key2); -HashTable * allocHashTable_(HashFunction *hash, CompareFunction *compare); int hashWord(const HashTable *table, StgWord key); -int hashStr(const HashTable *table, StgWord key); +int hashStr(const HashTable *table, StgWord w); +void insertHashTable_ ( HashTable *table, StgWord key, + const void *data, HashFunction f ); +void * lookupHashTable_ ( const HashTable *table, StgWord key, + HashFunction f, CompareFunction cmp ); +void * removeHashTable_ ( HashTable *table, StgWord key, + const void *data, HashFunction f, + CompareFunction cmp ); /* Freeing hash tables */ void freeHashTable ( HashTable *table, void (*freeDataFun)(void *) ); +#define freeStrHashTable(table, f) \ + (freeHashTable((HashTable*) table, f)) void exitHashTable ( void ); @@ -39,7 +39,7 @@ static pid_t hpc_pid = 0; // pid of this process at hpc-boot time. static FILE *tixFile; // file being read/written static int tix_ch; // current char -static HashTable * moduleHash = NULL; // module name -> HpcModuleInfo +static StrHashTable * moduleHash = NULL; // module name -> HpcModuleInfo HpcModuleInfo *modules = 0; @@ -152,11 +152,11 @@ readTix(void) { expect(']'); ws(); - lookup = lookupHashTable(moduleHash, (StgWord)tmpModule->modName); + lookup = lookupStrHashTable(moduleHash, tmpModule->modName); if (lookup == NULL) { debugTrace(DEBUG_hpc,"readTix: new HpcModuleInfo for %s", tmpModule->modName); - insertHashTable(moduleHash, (StgWord)tmpModule->modName, tmpModule); + insertStrHashTable(moduleHash, tmpModule->modName, tmpModule); } else { ASSERT(lookup->tixArr != 0); ASSERT(!strcmp(tmpModule->modName, lookup->modName)); @@ -265,7 +265,7 @@ hs_hpc_module(char *modName, moduleHash = allocStrHashTable(); } - tmpModule = lookupHashTable(moduleHash, (StgWord)modName); + tmpModule = lookupStrHashTable(moduleHash, modName); if (tmpModule == NULL) { // Did not find entry so add one on. @@ -282,7 +282,7 @@ hs_hpc_module(char *modName, tmpModule->next = modules; tmpModule->from_file = false; modules = tmpModule; - insertHashTable(moduleHash, (StgWord)modName, tmpModule); + insertStrHashTable(moduleHash, modName, tmpModule); } else { @@ -392,7 +392,7 @@ exitHpc(void) { writeTix(f); } - freeHashTable(moduleHash, (void (*)(void *))freeHpcModuleInfo); + freeStrHashTable(moduleHash, (void (*)(void *))freeHpcModuleInfo); moduleHash = NULL; stgFree(tixFilename); diff --git a/rts/Linker.c b/rts/Linker.c index 7bd2e67278..3539f90584 100644 --- a/rts/Linker.c +++ b/rts/Linker.c @@ -158,7 +158,7 @@ 2) The number of duplicate symbols, since now only symbols that are true duplicates will display the error. */ -/*Str*/HashTable *symhash; +StrHashTable *symhash; /* List of currently loaded objects */ ObjectCode *objects = NULL; /* initially empty */ @@ -227,7 +227,7 @@ int ocTryLoad( ObjectCode* oc ); static void *mmap_32bit_base = (void *)MMAP_32BIT_BASE_DEFAULT; -static void ghciRemoveSymbolTable(HashTable *table, const SymbolName* key, +static void ghciRemoveSymbolTable(StrHashTable *table, const SymbolName* key, ObjectCode *owner) { RtsSymbolInfo *pinfo = lookupStrHashTable(table, key); @@ -262,7 +262,7 @@ static void ghciRemoveSymbolTable(HashTable *table, const SymbolName* key, */ int ghciInsertSymbolTable( pathchar* obj_name, - HashTable *table, + StrHashTable *table, const SymbolName* key, SymbolAddr* data, HsBool weak, @@ -373,7 +373,7 @@ int ghciInsertSymbolTable( * Returns: 0 on failure and result is not set, * nonzero on success and result set to nonzero pointer */ -HsBool ghciLookupSymbolInfo(HashTable *table, +HsBool ghciLookupSymbolInfo(StrHashTable *table, const SymbolName* key, RtsSymbolInfo **result) { RtsSymbolInfo *pinfo = lookupStrHashTable(table, key); @@ -524,7 +524,7 @@ exitLinker( void ) { } #endif if (linker_init_done == 1) { - freeHashTable(symhash, free); + freeStrHashTable(symhash, free); } #if defined(THREADED_RTS) closeMutex(&linker_mutex); @@ -1200,7 +1200,7 @@ void freeObjectCode (ObjectCode *oc) } if (oc->extraInfos != NULL) { - freeHashTable(oc->extraInfos, NULL); + freeStrHashTable(oc->extraInfos, NULL); oc->extraInfos = NULL; } diff --git a/rts/LinkerInternals.h b/rts/LinkerInternals.h index 0f47b82761..8a8df50098 100644 --- a/rts/LinkerInternals.h +++ b/rts/LinkerInternals.h @@ -243,7 +243,7 @@ typedef struct _ObjectCode { /* Holds the list of symbols in the .o file which require extra information.*/ - HashTable *extraInfos; + StrHashTable *extraInfos; #if RTS_LINKER_USE_MMAP == 1 /* The m32 allocators used for allocating small sections and symbol extras @@ -304,12 +304,12 @@ void addSection (Section *s, SectionKind kind, SectionAlloc alloc, void* start, StgWord size, StgWord mapped_offset, void* mapped_start, StgWord mapped_size); -HsBool ghciLookupSymbolInfo(HashTable *table, +HsBool ghciLookupSymbolInfo(StrHashTable *table, const SymbolName* key, RtsSymbolInfo **result); int ghciInsertSymbolTable( pathchar* obj_name, - HashTable *table, + StrHashTable *table, const SymbolName* key, SymbolAddr* data, HsBool weak, @@ -318,7 +318,7 @@ int ghciInsertSymbolTable( /* lock-free version of lookupSymbol */ SymbolAddr* lookupSymbol_ (SymbolName* lbl); -extern /*Str*/HashTable *symhash; +extern StrHashTable *symhash; pathchar* resolveSymbolAddr (pathchar* buffer, int size, diff --git a/rts/RtsSymbolInfo.c b/rts/RtsSymbolInfo.c index 0553308f36..1110d582d6 100644 --- a/rts/RtsSymbolInfo.c +++ b/rts/RtsSymbolInfo.c @@ -93,7 +93,7 @@ static void unmarkImport(SymbolInfo* info) /* ----------------------------------------------------------------------------- * Marks the symbol at the given address as weak or not. * If the extra symbol infos table has not been initialized -* yet this will create and allocate a new Hashtable +* yet this will create and allocate a new StrHashtable */ void setWeakSymbol(ObjectCode *owner, const void *label) { @@ -103,7 +103,7 @@ void setWeakSymbol(ObjectCode *owner, const void *label) /* ----------------------------------------------------------------------------- * Marks the symbol at the given address as import or not. * If the extra symbol infos table has not been initialized -* yet this will create and allocate a new Hashtable +* yet this will create and allocate a new StrHashtable */ void setImportSymbol(ObjectCode *owner, const void *label) { @@ -113,7 +113,7 @@ void setImportSymbol(ObjectCode *owner, const void *label) /* ----------------------------------------------------------------------------- * Clear the import symbol flag. * If the extra symbol infos table has not been initialized -* yet this will create and allocate a new Hashtable +* yet this will create and allocate a new StrHashtable */ void clearImportSymbol(ObjectCode *owner, const void *label) { diff --git a/rts/StaticPtrTable.c b/rts/StaticPtrTable.c index f5e6239ad0..9754cfad41 100644 --- a/rts/StaticPtrTable.c +++ b/rts/StaticPtrTable.c @@ -21,14 +21,14 @@ static Mutex spt_lock; #endif /// Hash function for the SPT. -static int hashFingerprint(const HashTable *table, StgWord key) { +STATIC_INLINE int hashFingerprint(const HashTable *table, StgWord key) { const StgWord64* ptr = (StgWord64*) key; // Take half of the key to compute the hash. return hashWord(table, *(ptr + 1)); } /// Comparison function for the SPT. -static int compareFingerprint(StgWord a, StgWord b) { +STATIC_INLINE int compareFingerprint(StgWord a, StgWord b) { const StgWord64* ptra = (StgWord64*) a; const StgWord64* ptrb = (StgWord64*) b; return *ptra == *ptrb && *(ptra + 1) == *(ptrb + 1); @@ -38,14 +38,14 @@ void hs_spt_insert_stableptr(StgWord64 key[2], StgStablePtr *entry) { // hs_spt_insert is called from constructor functions, so // the SPT needs to be initialized here. if (spt == NULL) { - spt = allocHashTable_(hashFingerprint, compareFingerprint); + spt = allocHashTable(); #if defined(THREADED_RTS) initMutex(&spt_lock); #endif } ACQUIRE_LOCK(&spt_lock); - insertHashTable(spt, (StgWord)key, entry); + insertHashTable_(spt, (StgWord)key, entry, hashFingerprint); RELEASE_LOCK(&spt_lock); } @@ -68,7 +68,8 @@ static void freeSptEntry(void* entry) { void hs_spt_remove(StgWord64 key[2]) { if (spt) { ACQUIRE_LOCK(&spt_lock); - StgStablePtr* entry = removeHashTable(spt, (StgWord)key, NULL); + StgStablePtr* entry = removeHashTable_(spt, (StgWord)key, NULL, + hashFingerprint, compareFingerprint); RELEASE_LOCK(&spt_lock); if (entry) @@ -76,11 +77,11 @@ void hs_spt_remove(StgWord64 key[2]) { } } -StgPtr hs_spt_lookup(StgWord64 key1, StgWord64 key2) { +StgPtr hs_spt_lookup(StgWord64 key[2]) { if (spt) { ACQUIRE_LOCK(&spt_lock); - StgWord64 key[2] = { key1, key2 }; - const StgStablePtr * entry = lookupHashTable(spt, (StgWord)key); + const StgStablePtr * entry = lookupHashTable_(spt, (StgWord)key, + hashFingerprint, compareFingerprint); const StgPtr ret = entry ? deRefStablePtr(*entry) : NULL; RELEASE_LOCK(&spt_lock); return ret; |