|  | /** | 
|  | * @file ethHash.c | 
|  | * | 
|  | * @brief Hashtable implementation | 
|  | * | 
|  | * @par | 
|  | * IXP400 SW Release version 2.0 | 
|  | * | 
|  | * -- Copyright Notice -- | 
|  | * | 
|  | * @par | 
|  | * Copyright 2001-2005, Intel Corporation. | 
|  | * All rights reserved. | 
|  | * | 
|  | * @par | 
|  | * Redistribution and use in source and binary forms, with or without | 
|  | * modification, are permitted provided that the following conditions | 
|  | * are met: | 
|  | * 1. Redistributions of source code must retain the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer. | 
|  | * 2. Redistributions in binary form must reproduce the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer in the | 
|  | *    documentation and/or other materials provided with the distribution. | 
|  | * 3. Neither the name of the Intel Corporation nor the names of its contributors | 
|  | *    may be used to endorse or promote products derived from this software | 
|  | *    without specific prior written permission. | 
|  | * | 
|  | * @par | 
|  | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' | 
|  | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
|  | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 
|  | * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE | 
|  | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
|  | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | 
|  | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | 
|  | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | 
|  | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | 
|  | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | 
|  | * SUCH DAMAGE. | 
|  | * | 
|  | * @par | 
|  | * -- End of Copyright Notice -- | 
|  | */ | 
|  |  | 
|  |  | 
|  | #include "IxEthDB_p.h" | 
|  | #include "IxEthDBLocks_p.h" | 
|  |  | 
|  | /** | 
|  | * @addtogroup EthDB | 
|  | * | 
|  | * @{ | 
|  | */ | 
|  |  | 
|  | /** | 
|  | * @brief initializes a hash table object | 
|  | * | 
|  | * @param hashTable uninitialized hash table structure | 
|  | * @param numBuckets number of buckets to use | 
|  | * @param entryHashFunction hash function used | 
|  | * to hash entire hash node data block (for adding) | 
|  | * @param matchFunctions array of match functions, indexed on type, | 
|  | * used to differentiate records with the same hash value | 
|  | * @param freeFunction function used to free node data blocks | 
|  | * | 
|  | * Initializes the given hash table object. | 
|  | * | 
|  | * @internal | 
|  | */ | 
|  | void ixEthDBInitHash(HashTable *hashTable, | 
|  | UINT32 numBuckets, | 
|  | HashFunction entryHashFunction, | 
|  | MatchFunction *matchFunctions, | 
|  | FreeFunction freeFunction) | 
|  | { | 
|  | UINT32 bucketIndex; | 
|  | UINT32 hashSize = numBuckets * sizeof(HashNode *); | 
|  |  | 
|  | /* entry hashing, matching and freeing methods */ | 
|  | hashTable->entryHashFunction  = entryHashFunction; | 
|  | hashTable->matchFunctions     = matchFunctions; | 
|  | hashTable->freeFunction       = freeFunction; | 
|  |  | 
|  | /* buckets */ | 
|  | hashTable->numBuckets = numBuckets; | 
|  |  | 
|  | /* set to 0 all buckets */ | 
|  | memset(hashTable->hashBuckets, 0, hashSize); | 
|  |  | 
|  | /* init bucket locks - note that initially all mutexes are unlocked after MutexInit()*/ | 
|  | for (bucketIndex = 0 ; bucketIndex < numBuckets ; bucketIndex++) | 
|  | { | 
|  | ixOsalFastMutexInit(&hashTable->bucketLocks[bucketIndex]); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @brief adds an entry to the hash table | 
|  | * | 
|  | * @param hashTable hash table to add the entry to | 
|  | * @param entry entry to add | 
|  | * | 
|  | * The entry will be hashed using the entry hashing function and added to the | 
|  | * hash table, unless a locking blockage occurs, in which case the caller | 
|  | * should retry. | 
|  | * | 
|  | * @retval IX_ETH_DB_SUCCESS if adding <i>entry</i> has succeeded | 
|  | * @retval IX_ETH_DB_NOMEM if there's no memory left in the hash node pool | 
|  | * @retval IX_ETH_DB_BUSY if there's a locking failure on the insertion path | 
|  | * | 
|  | * @internal | 
|  | */ | 
|  | IX_ETH_DB_PUBLIC IxEthDBStatus ixEthDBAddHashEntry(HashTable *hashTable, void *entry) | 
|  | { | 
|  | UINT32 hashValue   = hashTable->entryHashFunction(entry); | 
|  | UINT32 bucketIndex = hashValue % hashTable->numBuckets; | 
|  | HashNode *bucket   = hashTable->hashBuckets[bucketIndex]; | 
|  | HashNode *newNode; | 
|  |  | 
|  | LockStack locks; | 
|  |  | 
|  | INIT_STACK(&locks); | 
|  |  | 
|  | /* lock bucket */ | 
|  | PUSH_LOCK(&locks, &hashTable->bucketLocks[bucketIndex]); | 
|  |  | 
|  | /* lock insertion element (first in chain), if any */ | 
|  | if (bucket != NULL) | 
|  | { | 
|  | PUSH_LOCK(&locks, &bucket->lock); | 
|  | } | 
|  |  | 
|  | /* get new node */ | 
|  | newNode = ixEthDBAllocHashNode(); | 
|  |  | 
|  | if (newNode == NULL) | 
|  | { | 
|  | /* unlock everything */ | 
|  | UNROLL_STACK(&locks); | 
|  |  | 
|  | return IX_ETH_DB_NOMEM; | 
|  | } | 
|  |  | 
|  | /* init lock - note that mutexes are unlocked after MutexInit */ | 
|  | ixOsalFastMutexInit(&newNode->lock); | 
|  |  | 
|  | /* populate new link */ | 
|  | newNode->data = entry; | 
|  |  | 
|  | /* add to bucket */ | 
|  | newNode->next                       = bucket; | 
|  | hashTable->hashBuckets[bucketIndex] = newNode; | 
|  |  | 
|  | /* unlock bucket and insertion point */ | 
|  | UNROLL_STACK(&locks); | 
|  |  | 
|  | return IX_ETH_DB_SUCCESS; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @brief removes an entry from the hashtable | 
|  | * | 
|  | * @param hashTable hash table to remove entry from | 
|  | * @param keyType type of record key used for matching | 
|  | * @param reference reference key used to identify the entry | 
|  | * | 
|  | * The reference key will be hashed using the key hashing function, | 
|  | * the entry is searched using the hashed value and then examined | 
|  | * against the reference entry using the match function. A positive | 
|  | * match will trigger the deletion of the entry. | 
|  | * Locking failures are reported and the caller should retry. | 
|  | * | 
|  | * @retval IX_ETH_DB_SUCCESS if the removal was successful | 
|  | * @retval IX_ETH_DB_NO_SUCH_ADDR if the entry was not found | 
|  | * @retval IX_ETH_DB_BUSY if a locking failure occured during the process | 
|  | * | 
|  | * @internal | 
|  | */ | 
|  | IxEthDBStatus ixEthDBRemoveHashEntry(HashTable *hashTable, int keyType, void *reference) | 
|  | { | 
|  | UINT32 hashValue       = hashTable->entryHashFunction(reference); | 
|  | UINT32 bucketIndex     = hashValue % hashTable->numBuckets; | 
|  | HashNode *node         = hashTable->hashBuckets[bucketIndex]; | 
|  | HashNode *previousNode = NULL; | 
|  |  | 
|  | LockStack locks; | 
|  |  | 
|  | INIT_STACK(&locks); | 
|  |  | 
|  | while (node != NULL) | 
|  | { | 
|  | /* try to lock node */ | 
|  | PUSH_LOCK(&locks, &node->lock); | 
|  |  | 
|  | if (hashTable->matchFunctions[keyType](reference, node->data)) | 
|  | { | 
|  | /* found entry */ | 
|  | if (node->next != NULL) | 
|  | { | 
|  | PUSH_LOCK(&locks, &node->next->lock); | 
|  | } | 
|  |  | 
|  | if (previousNode == NULL) | 
|  | { | 
|  | /* node is head of chain */ | 
|  | PUSH_LOCK(&locks, &hashTable->bucketLocks[bucketIndex]); | 
|  |  | 
|  | hashTable->hashBuckets[bucketIndex] = node->next; | 
|  |  | 
|  | POP_LOCK(&locks); | 
|  | } | 
|  | else | 
|  | { | 
|  | /* relink */ | 
|  | previousNode->next = node->next; | 
|  | } | 
|  |  | 
|  | UNROLL_STACK(&locks); | 
|  |  | 
|  | /* free node */ | 
|  | hashTable->freeFunction(node->data); | 
|  | ixEthDBFreeHashNode(node); | 
|  |  | 
|  | return IX_ETH_DB_SUCCESS; | 
|  | } | 
|  | else | 
|  | { | 
|  | if (previousNode != NULL) | 
|  | { | 
|  | /* unlock previous node */ | 
|  | SHIFT_STACK(&locks); | 
|  | } | 
|  |  | 
|  | /* advance to next element in chain */ | 
|  | previousNode = node; | 
|  | node         = node->next; | 
|  | } | 
|  | } | 
|  |  | 
|  | UNROLL_STACK(&locks); | 
|  |  | 
|  | /* not found */ | 
|  | return IX_ETH_DB_NO_SUCH_ADDR; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @brief retrieves an entry from the hash table | 
|  | * | 
|  | * @param hashTable hash table to perform the search into | 
|  | * @param reference search key (a MAC address) | 
|  | * @param keyType type of record key used for matching | 
|  | * @param searchResult pointer where a reference to the located hash node | 
|  | * is placed | 
|  | * | 
|  | * Searches the entry with the same key as <i>reference</i> and places the | 
|  | * pointer to the resulting node in <i>searchResult</i>. | 
|  | * An implicit write access lock is granted after a search, which gives the | 
|  | * caller the opportunity to modify the entry. | 
|  | * Access should be released as soon as possible using @ref ixEthDBReleaseHashNode(). | 
|  | * | 
|  | * @see ixEthDBReleaseHashNode() | 
|  | * | 
|  | * @retval IX_ETH_DB_SUCCESS if the search was completed successfully | 
|  | * @retval IX_ETH_DB_NO_SUCH_ADDRESS if no entry with the given key was found | 
|  | * @retval IX_ETH_DB_BUSY if a locking failure has occured, in which case | 
|  | * the caller should retry | 
|  | * | 
|  | * @warning unless the return value is <b>IX_ETH_DB_SUCCESS</b> the searchResult | 
|  | * location is NOT modified and therefore using a NULL comparison test when the | 
|  | * value was not properly initialized would be an error | 
|  | * | 
|  | * @internal | 
|  | */ | 
|  | IxEthDBStatus ixEthDBSearchHashEntry(HashTable *hashTable, int keyType, void *reference, HashNode **searchResult) | 
|  | { | 
|  | UINT32 hashValue; | 
|  | HashNode *node; | 
|  |  | 
|  | hashValue = hashTable->entryHashFunction(reference); | 
|  | node      = hashTable->hashBuckets[hashValue % hashTable->numBuckets]; | 
|  |  | 
|  | while (node != NULL) | 
|  | { | 
|  | TRY_LOCK(&node->lock); | 
|  |  | 
|  | if (hashTable->matchFunctions[keyType](reference, node->data)) | 
|  | { | 
|  | *searchResult = node; | 
|  |  | 
|  | return IX_ETH_DB_SUCCESS; | 
|  | } | 
|  | else | 
|  | { | 
|  | UNLOCK(&node->lock); | 
|  |  | 
|  | node = node->next; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* not found */ | 
|  | return IX_ETH_DB_NO_SUCH_ADDR; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @brief reports the existence of an entry in the hash table | 
|  | * | 
|  | * @param hashTable hash table to perform the search into | 
|  | * @param reference search key (a MAC address) | 
|  | * @param keyType type of record key used for matching | 
|  | * | 
|  | * Searches the entry with the same key as <i>reference</i>. | 
|  | * No implicit write access lock is granted after a search, hence the | 
|  | * caller cannot access or modify the entry. The result is only temporary. | 
|  | * | 
|  | * @see ixEthDBReleaseHashNode() | 
|  | * | 
|  | * @retval IX_ETH_DB_SUCCESS if the search was completed successfully | 
|  | * @retval IX_ETH_DB_NO_SUCH_ADDRESS if no entry with the given key was found | 
|  | * @retval IX_ETH_DB_BUSY if a locking failure has occured, in which case | 
|  | * the caller should retry | 
|  | * | 
|  | * @internal | 
|  | */ | 
|  | IxEthDBStatus ixEthDBPeekHashEntry(HashTable *hashTable, int keyType, void *reference) | 
|  | { | 
|  | UINT32 hashValue; | 
|  | HashNode *node; | 
|  |  | 
|  | hashValue = hashTable->entryHashFunction(reference); | 
|  | node      = hashTable->hashBuckets[hashValue % hashTable->numBuckets]; | 
|  |  | 
|  | while (node != NULL) | 
|  | { | 
|  | TRY_LOCK(&node->lock); | 
|  |  | 
|  | if (hashTable->matchFunctions[keyType](reference, node->data)) | 
|  | { | 
|  | UNLOCK(&node->lock); | 
|  |  | 
|  | return IX_ETH_DB_SUCCESS; | 
|  | } | 
|  | else | 
|  | { | 
|  | UNLOCK(&node->lock); | 
|  |  | 
|  | node = node->next; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* not found */ | 
|  | return IX_ETH_DB_NO_SUCH_ADDR; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @brief releases the write access lock | 
|  | * | 
|  | * @pre the node should have been obtained via @ref ixEthDBSearchHashEntry() | 
|  | * | 
|  | * @see ixEthDBSearchHashEntry() | 
|  | * | 
|  | * @internal | 
|  | */ | 
|  | void ixEthDBReleaseHashNode(HashNode *node) | 
|  | { | 
|  | UNLOCK(&node->lock); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @brief initializes a hash iterator | 
|  | * | 
|  | * @param hashTable hash table to be iterated | 
|  | * @param iterator iterator object | 
|  | * | 
|  | * If the initialization is successful the iterator will point to the | 
|  | * first hash table record (if any). | 
|  | * Testing if the iterator has not passed the end of the table should be | 
|  | * done using the IS_ITERATOR_VALID(iteratorPtr) macro. | 
|  | * An implicit write access lock is granted on the entry pointed by the iterator. | 
|  | * The access is automatically revoked when the iterator is incremented. | 
|  | * If the caller decides to terminate the iteration before the end of the table is | 
|  | * passed then the manual access release method, @ref ixEthDBReleaseHashIterator, | 
|  | * must be called. | 
|  | * | 
|  | * @see ixEthDBReleaseHashIterator() | 
|  | * | 
|  | * @retval IX_ETH_DB_SUCCESS if initialization was successful and the iterator points | 
|  | * to the first valid table node | 
|  | * @retval IX_ETH_DB_FAIL if the table is empty | 
|  | * @retval IX_ETH_DB_BUSY if a locking failure has occured, in which case the caller | 
|  | * should retry | 
|  | * | 
|  | * @warning do not use ixEthDBReleaseHashNode() on entries pointed by the iterator, as this | 
|  | * might place the database in a permanent invalid lock state | 
|  | * | 
|  | * @internal | 
|  | */ | 
|  | IxEthDBStatus ixEthDBInitHashIterator(HashTable *hashTable, HashIterator *iterator) | 
|  | { | 
|  | iterator->bucketIndex  = 0; | 
|  | iterator->node         = NULL; | 
|  | iterator->previousNode = NULL; | 
|  |  | 
|  | return ixEthDBIncrementHashIterator(hashTable, iterator); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @brief releases the write access locks of the iterator nodes | 
|  | * | 
|  | * @warning use of this function is required only when the caller terminates an iteration | 
|  | * before reaching the end of the table | 
|  | * | 
|  | * @see ixEthDBInitHashIterator() | 
|  | * @see ixEthDBIncrementHashIterator() | 
|  | * | 
|  | * @param iterator iterator whose node(s) should be unlocked | 
|  | * | 
|  | * @internal | 
|  | */ | 
|  | void ixEthDBReleaseHashIterator(HashIterator *iterator) | 
|  | { | 
|  | if (iterator->previousNode != NULL) | 
|  | { | 
|  | UNLOCK(&iterator->previousNode->lock); | 
|  | } | 
|  |  | 
|  | if (iterator->node != NULL) | 
|  | { | 
|  | UNLOCK(&iterator->node->lock); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @brief incremenents an iterator so that it points to the next valid entry of the table | 
|  | * (if any) | 
|  | * | 
|  | * @param hashTable hash table to iterate | 
|  | * @param iterator iterator object | 
|  | * | 
|  | * @pre the iterator object must be initialized using @ref ixEthDBInitHashIterator() | 
|  | * | 
|  | * If the increment operation is successful the iterator will point to the | 
|  | * next hash table record (if any). | 
|  | * Testing if the iterator has not passed the end of the table should be | 
|  | * done using the IS_ITERATOR_VALID(iteratorPtr) macro. | 
|  | * An implicit write access lock is granted on the entry pointed by the iterator. | 
|  | * The access is automatically revoked when the iterator is re-incremented. | 
|  | * If the caller decides to terminate the iteration before the end of the table is | 
|  | * passed then the manual access release method, @ref ixEthDBReleaseHashIterator, | 
|  | * must be called. | 
|  | * Is is guaranteed that no other thread can remove or change the iterated entry until | 
|  | * the iterator is incremented successfully. | 
|  | * | 
|  | * @see ixEthDBReleaseHashIterator() | 
|  | * | 
|  | * @retval IX_ETH_DB_SUCCESS if the operation was successful and the iterator points | 
|  | * to the next valid table node | 
|  | * @retval IX_ETH_DB_FAIL if the iterator has passed the end of the table | 
|  | * @retval IX_ETH_DB_BUSY if a locking failure has occured, in which case the caller | 
|  | * should retry | 
|  | * | 
|  | * @warning do not use ixEthDBReleaseHashNode() on entries pointed by the iterator, as this | 
|  | * might place the database in a permanent invalid lock state | 
|  | * | 
|  | * @internal | 
|  | */ | 
|  | IxEthDBStatus ixEthDBIncrementHashIterator(HashTable *hashTable, HashIterator *iterator) | 
|  | { | 
|  | /* unless iterator is just initialized... */ | 
|  | if (iterator->node != NULL) | 
|  | { | 
|  | /* try next in chain */ | 
|  | if (iterator->node->next != NULL) | 
|  | { | 
|  | TRY_LOCK(&iterator->node->next->lock); | 
|  |  | 
|  | if (iterator->previousNode != NULL) | 
|  | { | 
|  | UNLOCK(&iterator->previousNode->lock); | 
|  | } | 
|  |  | 
|  | iterator->previousNode = iterator->node; | 
|  | iterator->node         = iterator->node->next; | 
|  |  | 
|  | return IX_ETH_DB_SUCCESS; | 
|  | } | 
|  | else | 
|  | { | 
|  | /* last in chain, prepare for next bucket */ | 
|  | iterator->bucketIndex++; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* try next used bucket */ | 
|  | for (; iterator->bucketIndex < hashTable->numBuckets ; iterator->bucketIndex++) | 
|  | { | 
|  | HashNode **nodePtr = &(hashTable->hashBuckets[iterator->bucketIndex]); | 
|  | HashNode *node = *nodePtr; | 
|  | #if (CPU!=SIMSPARCSOLARIS) && !defined (__wince) | 
|  | if (((iterator->bucketIndex & IX_ETHDB_BUCKET_INDEX_MASK) == 0) && | 
|  | (iterator->bucketIndex < (hashTable->numBuckets - IX_ETHDB_BUCKETPTR_AHEAD))) | 
|  | { | 
|  | /* preload next cache line (2 cache line ahead) */ | 
|  | nodePtr += IX_ETHDB_BUCKETPTR_AHEAD; | 
|  | __asm__ ("pld [%0];\n": : "r" (nodePtr)); | 
|  | } | 
|  | #endif | 
|  | if (node != NULL) | 
|  | { | 
|  | TRY_LOCK(&node->lock); | 
|  |  | 
|  | /* unlock last one or two nodes in the previous chain */ | 
|  | if (iterator->node != NULL) | 
|  | { | 
|  | UNLOCK(&iterator->node->lock); | 
|  |  | 
|  | if (iterator->previousNode != NULL) | 
|  | { | 
|  | UNLOCK(&iterator->previousNode->lock); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* redirect iterator */ | 
|  | iterator->previousNode = NULL; | 
|  | iterator->node         = node; | 
|  |  | 
|  | return IX_ETH_DB_SUCCESS; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* could not advance iterator */ | 
|  | if (iterator->node != NULL) | 
|  | { | 
|  | UNLOCK(&iterator->node->lock); | 
|  |  | 
|  | if (iterator->previousNode != NULL) | 
|  | { | 
|  | UNLOCK(&iterator->previousNode->lock); | 
|  | } | 
|  |  | 
|  | iterator->node = NULL; | 
|  | } | 
|  |  | 
|  | return IX_ETH_DB_END; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @brief removes an entry pointed by an iterator | 
|  | * | 
|  | * @param hashTable iterated hash table | 
|  | * @param iterator iterator object | 
|  | * | 
|  | * Removes the entry currently pointed by the iterator and repositions the iterator | 
|  | * on the next valid entry (if any). Handles locking issues automatically and | 
|  | * implicitely grants write access lock to the new pointed entry. | 
|  | * Failures due to concurrent threads having write access locks in the same region | 
|  | * preserve the state of the database and the iterator object, leaving the caller | 
|  | * free to retry without loss of access. It is guaranteed that only the thread owning | 
|  | * the iterator can remove the object pointed by the iterator. | 
|  | * | 
|  | * @retval IX_ETH_DB_SUCCESS if removal has succeeded | 
|  | * @retval IX_ETH_DB_BUSY if a locking failure has occured, in which case the caller | 
|  | * should retry | 
|  | * | 
|  | * @internal | 
|  | */ | 
|  | IxEthDBStatus ixEthDBRemoveEntryAtHashIterator(HashTable *hashTable, HashIterator *iterator) | 
|  | { | 
|  | HashIterator nextIteratorPos; | 
|  | LockStack locks; | 
|  |  | 
|  | INIT_STACK(&locks); | 
|  |  | 
|  | /* set initial bucket index for next position */ | 
|  | nextIteratorPos.bucketIndex = iterator->bucketIndex; | 
|  |  | 
|  | /* compute iterator position before removing anything and lock ahead */ | 
|  | if (iterator->node->next != NULL) | 
|  | { | 
|  | PUSH_LOCK(&locks, &iterator->node->next->lock); | 
|  |  | 
|  | /* reposition on the next node in the chain */ | 
|  | nextIteratorPos.node         = iterator->node->next; | 
|  | nextIteratorPos.previousNode = iterator->previousNode; | 
|  | } | 
|  | else | 
|  | { | 
|  | /* try next chain - don't know yet if we'll find anything */ | 
|  | nextIteratorPos.node = NULL; | 
|  |  | 
|  | /* if we find something it's a chain head */ | 
|  | nextIteratorPos.previousNode = NULL; | 
|  |  | 
|  | /* browse up in the buckets to find a non-null chain */ | 
|  | while (++nextIteratorPos.bucketIndex < hashTable->numBuckets) | 
|  | { | 
|  | nextIteratorPos.node = hashTable->hashBuckets[nextIteratorPos.bucketIndex]; | 
|  |  | 
|  | if (nextIteratorPos.node != NULL) | 
|  | { | 
|  | /* found a non-empty chain, try to lock head */ | 
|  | PUSH_LOCK(&locks, &nextIteratorPos.node->lock); | 
|  |  | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* restore links over the to-be-deleted item */ | 
|  | if (iterator->previousNode == NULL) | 
|  | { | 
|  | /* first in chain, lock bucket */ | 
|  | PUSH_LOCK(&locks, &hashTable->bucketLocks[iterator->bucketIndex]); | 
|  |  | 
|  | hashTable->hashBuckets[iterator->bucketIndex] = iterator->node->next; | 
|  |  | 
|  | POP_LOCK(&locks); | 
|  | } | 
|  | else | 
|  | { | 
|  | /* relink */ | 
|  | iterator->previousNode->next = iterator->node->next; | 
|  |  | 
|  | /* unlock last remaining node in current chain when moving between chains */ | 
|  | if (iterator->node->next == NULL) | 
|  | { | 
|  | UNLOCK(&iterator->previousNode->lock); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* delete entry */ | 
|  | hashTable->freeFunction(iterator->node->data); | 
|  | ixEthDBFreeHashNode(iterator->node); | 
|  |  | 
|  | /* reposition iterator */ | 
|  | *iterator = nextIteratorPos; | 
|  |  | 
|  | return IX_ETH_DB_SUCCESS; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @} | 
|  | */ |