nis_hashitem.c revision 2
2N/A * The contents of this file are subject to the terms of the 2N/A * Common Development and Distribution License, Version 1.0 only 2N/A * (the "License"). You may not use this file except in compliance 2N/A * See the License for the specific language governing permissions 2N/A * and limitations under the License. 2N/A * When distributing Covered Code, include this CDDL HEADER in each 2N/A * If applicable, add the following below this CDDL HEADER, with the 2N/A * fields enclosed by brackets "[]" replaced with your own identifying 2N/A * information: Portions Copyright [yyyy] [name of copyright owner] 2N/A * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 2N/A * Use is subject to license terms. 2N/A#
pragma ident "%Z%%M% %I% %E% SMI" 2N/A/* We're the magician, so undefine the define-magic */ 2N/A * The hash table routines below implement nested (or recursive) 2N/A * one-writer-or-many-readers locking. The following restrictions 2N/A * Unless an item destructor has been established, an item 2N/A * MUST NOT be removed from a list (__nis_pop_item_mt() or 2N/A * (__nis_remove_item_mt()) when the thread performing 2N/A * the deletion is holding a read-only lock on the item. 2N/A * Doing so will result in the thread blocking in 2N/A * pthread_cond_wait() waiting for itself to signal on 2N/A * the condition variable. Deletion when the invoking 2N/A * thread is holding a write lock (any level of nesting), 2N/A * or no lock, is OK. 2N/A "(table->lock) pthread_mutex_init returned %d (%s)",
2N/A "(table->cond) pthread_cond_init returned %d (%s)",
2N/A "(table->traverser_id_lock) " 2N/A "pthread_mutex_init returned %d (%s)",
2N/A * We want exclusive access to everything in the 2N/A * table (list). Wait until there are no threads 2N/A * either traversing the list, or with exclusive 2N/A * access to an item. 2N/A "%d: lh table 0x%x trav cond wait",
2N/A * Called from the nis_*_item() functions. If no one's 2N/A * locked the table, lock it. If the table already is 2N/A * being traversed by us, do nothing. Otherwise, wait 2N/A /* Already locked; find out if it's us */ 2N/A /* It's us. No action. */ 2N/A /* Not us. Wait for the lock */ 2N/A "%d: lh table 0x%x cond wait",
2N/A * Since we're keeping track of who's traversing the 2N/A * table in order to avoid recursive locking in the 2N/A * nis_*item() functions, we might as well sanity check 2N/A * Leave traverser_id as it is, so that it 2N/A * possible to see which thread last held 2N/A * the traverser lock. 2N/A /* Wake up other traversers-to-be */ 2N/A * Called from the nis_*_item() functions. If we're 2N/A * traversing the table, leave it locked. 2N/A /* Assume table!=0, table lock held */ 2N/A for (s = (
unsigned char *)
name; *s != 0; s++) {
2N/A * The 'readwrite' argument is interpreted as follows on a successful 2N/A * < 0 Exclusive access to item 2N/A * 0 Item must not be used or referenced in any way 2N/A * > 0 Non-exclusive access (read-only) to item 2N/A * Except when 'readwrite' == 0, the caller must explicitly release the 2N/A * item (__nis_release_item()). 2N/A * readwrite: < 0 Exclusive access 2N/A * 0 No access; must not use returned item in any way, 2N/A * other than to confirm existence indicated by a non-NULL 2N/A * > 0 Non-exclusive (read-only) access 2N/A * If trylock != 0 and *trylock != 0 and the item exists but the requested 2N/A * lock type cannot be acquired, set *trylock = -1 and return 0. 2N/A * Block waiting for more favorable conditions unless: 2N/A * The item doesn't exist anymore 2N/A * 'readwrite' == 0 (verify existence only) 2N/A * There's a writer, but it's us 2N/A * There are no writers, and we're satisfied by RO access 2N/A * A trylock was requested 2N/A /* Wait until the first item isn't in use by another thread */ 2N/A /* List might be empty now */ 2N/A * We use keychain < 0 to indicate that the item isn't linked 2N/A * into the table anymore. 2N/A /* Adjust the count of locked items in the table */ 2N/A /* Wake up traversers-to-be */ 2N/A * Wake up any threads that were waiting for this item. Obviously, 2N/A * such threads must start over scanning the list. 2N/A * If the item isn't locked, and an item destructor has been 2N/A * established, invoke the destructor. 2N/A * If we get here, and the 'item' is NULL, we've popped the 2N/A * item, but also destroyed it. Returning NULL would make 2N/A * our caller believe the list is empty, so instead, we invoke 2N/A * ourselves to pop the next item. 2N/A /* Find the item, and make sure it's not in use */ 2N/A /* Remove nl from the hash chain */ 2N/A /* Remove nl from the linked list of all names */ 2N/A /* keychain < 0 means not in table anymore */ 2N/A * If this item was locked, we can now decrement the count of 2N/A * locked items for the table. 2N/A /* Wake up traversers-to-be */ 2N/A * If the item isn't locked, and an item destructor has been 2N/A * established, invoke the destructor. In that case, we return 2N/A * NULL, so that our caller doesn't try to reference the 2N/A * Release an item that had been acquired exclusively or non-exclusively. 2N/A * Note that 'readwrite' can assume any integer value, and thus can be 2N/A * used to release any level of recursive locking. It's the responsibility 2N/A * of the caller to make sure that proper nesting is maintained. 2N/A /* Caller confused; ignore */ 2N/A /* Wake up traversers-to-be */ 2N/A /* Wake up anyone else who wants this item */ 2N/A * Delete if no references, not linked into list, and destructor 2N/A * Return -1 if item checked out for both reading and writing, 1 if 2N/A * readonly, and 0 otherwise. 2N/A * __nis_scan_table_mt() 2N/A * Iterate over all items in a __nis_hash_table_mt. We ignore 2N/A * function should *not* insert or delete items. If the iterator 2N/A * function returns TRUE the scan terminates. For compatibility with 2N/A * the existing non-MT nis_scan_table() this function has no return