/* * Copyright (c) 2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * 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 Apple Inc. ("Apple") nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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. * * Portions of this software have been released under the following terms: * * (c) Copyright 1989-1993 OPEN SOFTWARE FOUNDATION, INC. * (c) Copyright 1989-1993 HEWLETT-PACKARD COMPANY * (c) Copyright 1989-1993 DIGITAL EQUIPMENT CORPORATION * * To anyone who acknowledges that this file is provided "AS IS" * without any express or implied warranty: * permission to use, copy, modify, and distribute this file for any * purpose is hereby granted without fee, provided that the above * copyright notices and this notice appears in all source code copies, * and that none of the names of Open Software Foundation, Inc., Hewlett- * Packard Company or Digital Equipment Corporation be used * in advertising or publicity pertaining to distribution of the software * without specific, written prior permission. Neither Open Software * Foundation, Inc., Hewlett-Packard Company nor Digital * Equipment Corporation makes any representations about the suitability * of this software for any purpose. * * Copyright (c) 2007, Novell, Inc. All rights reserved. * 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 Novell Inc. nor the names of its contributors * may be used to endorse or promote products derived from this * this software without specific prior written permission. * * 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 HOLDERS 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. * * @APPLE_LICENSE_HEADER_END@ */ /* ** ** NAME: ** ** rpcmutex.c ** ** FACILITY: ** ** Remote Procedure Call (RPC) ** ** ABSTRACT: ** ** The support routines for rpcmutex.h abstraction. These should NOT ** be called directly; use the macros in rpcmutex.h . ** ** */ #include #if defined(RPC_MUTEX_DEBUG) || defined(RPC_MUTEX_STATS) /* * All of the routines return true on success, false on failure. * * There are races present on the modifications of the package wide * statistics as well as the per lock "busy", "try_lock" and "*_assert" * statistics, but we don't really care. We don't want to burden these * "informative" statistics with some other mutex. We're only providing * these stats so we can track gross trends. */ /* * !!! Since CMA pthreads doesn't provide a "null" handle, create our own. */ PRIVATE dcethread* rpc_g_null_thread_handle; #define NULL_THREAD rpc_g_null_thread_handle #define IS_MY_THREAD(t) dcethread_equal((t), my_thread) /* * Some package wide statistics. */ INTERNAL rpc_mutex_stats_t mutex_stats = {0}; INTERNAL rpc_cond_stats_t cond_stats = {0}; /* * R P C _ _ M U T E X _ I N I T */ PRIVATE boolean rpc__mutex_init ( rpc_mutex_p_t mp ) { mp->stats.busy = 0; mp->stats.lock = 0; mp->stats.try_lock = 0; mp->stats.unlock = 0; mp->stats.init = 1; mp->stats.deletes = 0; mp->stats.lock_assert = 0; mp->stats.unlock_assert = 0; mp->is_locked = false; mp->owner = NULL_THREAD; mp->locker_file = "never_locked"; mp->locker_line = 0; if (dcethread_mutex_init(&mp->m, NULL) != 0) { return (false); } mutex_stats.init++; return(true); } /* * R P C _ _ M U T E X _ D E L E T E */ PRIVATE boolean rpc__mutex_delete ( rpc_mutex_p_t mp ) { mp->stats.deletes++; mutex_stats.deletes++; dcethread_mutex_destroy(&mp->m); return(true); } /* * R P C _ _ M U T E X _ L O C K */ PRIVATE boolean rpc__mutex_lock ( rpc_mutex_p_t mp, char *file, int line ) { dcethread* my_thread = NULL_THREAD; boolean is_locked = mp->is_locked; boolean dbg; dbg = RPC_DBG(rpc_es_dbg_mutex, 5); if (dbg) { my_thread = dcethread_self(); if (is_locked && IS_MY_THREAD(mp->owner)) { RPC_DBG_PRINTF(rpc_e_dbg_mutex, 1, ("(rpc__mutex_lock) deadlock with self at %s/%d (previous lock at %s/%d)\n", file, line, mp->locker_file, mp->locker_line)); return(false); } } dcethread_mutex_lock(&mp->m); mp->is_locked = true; if (dbg) { mp->owner = my_thread; mp->locker_file = file; mp->locker_line = line; } if (is_locked) { mp->stats.busy++; mutex_stats.busy++; } mp->stats.lock++; mutex_stats.lock++; return(true); } /* * R P C _ _ M U T E X _ T R Y _ L O C K */ PRIVATE boolean rpc__mutex_try_lock ( rpc_mutex_p_t mp, boolean *bp, char *file, int line ) { dcethread* my_thread = NULL_THREAD; boolean is_locked = mp->is_locked; boolean dbg; dbg = RPC_DBG(rpc_es_dbg_mutex, 5); if (dbg) { my_thread = dcethread_self(); if (is_locked && IS_MY_THREAD(mp->owner)) { RPC_DBG_PRINTF(rpc_e_dbg_mutex, 1, ("(rpc__mutex_try_lock) deadlock with self at %s/%d (previous lock at %s/%d)\n", file, line, mp->locker_file, mp->locker_line)); return(false); } } *bp = dcethread_mutex_trylock(&mp->m); if (*bp) { mp->is_locked = true; if (dbg) { mp->owner = my_thread; mp->locker_file = file; mp->locker_line = line; } } else { mp->stats.busy++; mutex_stats.busy++; } mp->stats.try_lock++; mutex_stats.try_lock++; return(true); } /* * R P C _ _ M U T E X _ U N L O C K */ PRIVATE boolean rpc__mutex_unlock ( rpc_mutex_p_t mp ) { dcethread* my_thread; boolean is_locked = mp->is_locked; boolean dbg; dbg = RPC_DBG(rpc_es_dbg_mutex, 5); if (dbg) { if (! is_locked) { RPC_DBG_PRINTF(rpc_e_dbg_mutex, 1, ("(rpc__mutex_unlock) not locked\n")); return(false); } my_thread = dcethread_self(); if (!IS_MY_THREAD(mp->owner)) { RPC_DBG_PRINTF(rpc_e_dbg_mutex, 1, ("(rpc__mutex_unlock) not owner (owner at %s/%d)\n", mp->locker_file, mp->locker_line)); return(false); } mp->owner = NULL_THREAD; } mp->stats.unlock++; mutex_stats.unlock++; mp->is_locked = false; dcethread_mutex_unlock(&mp->m); return(true); } /* * R P C _ _ M U T E X _ L O C K _ A S S E R T * * assert that we are the owner of the lock. */ PRIVATE boolean rpc__mutex_lock_assert ( rpc_mutex_p_t mp ) { dcethread* my_thread; boolean is_locked = mp->is_locked; boolean dbg; dbg = RPC_DBG(rpc_es_dbg_mutex, 5); mp->stats.lock_assert++; mutex_stats.lock_assert++; if (dbg) { if (! is_locked) { RPC_DBG_PRINTF(rpc_e_dbg_mutex, 1, ("(rpc__mutex_lock_assert) not locked\n")); return(false); } my_thread = dcethread_self(); if (!IS_MY_THREAD(mp->owner)) { RPC_DBG_PRINTF(rpc_e_dbg_mutex, 1, ("(rpc__mutex_lock_assert) not owner\n")); return(false); } } return(true); } /* * R P C _ _ M U T E X _ U N L O C K _ A S S E R T * * assert that we are not the owner of the lock. */ PRIVATE boolean rpc__mutex_unlock_assert ( rpc_mutex_p_t mp ) { dcethread* my_thread; boolean is_locked = mp->is_locked; boolean dbg; dbg = RPC_DBG(rpc_es_dbg_mutex, 5); mp->stats.unlock_assert++; mutex_stats.unlock_assert++; if (dbg) { if (! is_locked) return(true); my_thread = dcethread_self(); if (IS_MY_THREAD(mp->owner)) { RPC_DBG_PRINTF(rpc_e_dbg_mutex, 1, ("(rpc__mutex_unlock_assert) owner\n")); return(false); } } return(true); } /* * R P C _ _ C O N D _ I N I T * * The "mp" is the mutex that is associated with the cv. */ boolean rpc__cond_init ( rpc_cond_p_t cp, rpc_mutex_p_t mp ) { cp->stats.init = 1; cp->stats.deletes = 0; cp->stats.wait = 0; cp->stats.signals = 0; cp->mp = mp; dcethread_cond_init(&cp->c, NULL); cond_stats.init++; return(true); } /* * R P C _ _ C O N D _ D E L E T E */ boolean rpc__cond_delete ( rpc_cond_p_t cp, rpc_mutex_p_t mp ATTRIBUTE_UNUSED ) { cp->stats.deletes++; cond_stats.deletes++; dcethread_cond_destroy(&cp->c); return(true); } /* * R P C _ _ C O N D _ W A I T * * The mutex is automatically released and reacquired by the wait. */ boolean rpc__cond_wait ( rpc_cond_p_t cp, rpc_mutex_p_t mp, char *file, int line ) { dcethread* my_thread; volatile boolean dbg; DO_NOT_CLOBBER(my_thread); cp->stats.wait++; cond_stats.wait++; dbg = RPC_DBG(rpc_es_dbg_mutex, 5); if (dbg) { if (! rpc__mutex_lock_assert(mp)) { RPC_DBG_PRINTF(rpc_e_dbg_mutex, 1, ("(rpc__cond_wait) mutex usage error\n")); return(false); } if (cp->mp != mp) { RPC_DBG_PRINTF(rpc_e_dbg_mutex, 1, ("(rpc__cond_wait) incorrect mutex\n")); return(false); } my_thread = dcethread_self(); mp->owner = NULL_THREAD; } mp->is_locked = false; TRY dcethread_cond_wait_throw(&cp->c, &mp->m); CATCH_ALL mp->is_locked = true; if (dbg) { mp->owner = my_thread; mp->locker_file = file; mp->locker_line = line; } RERAISE; ENDTRY mp->is_locked = true; if (dbg) { mp->owner = my_thread; mp->locker_file = file; mp->locker_line = line; } return(true); } /* * R P C _ _ C O N D _ T I M E D _ W A I T * * The mutex is automatically released and reacquired by the wait. */ boolean rpc__cond_timed_wait ( rpc_cond_p_t cp, rpc_mutex_p_t mp, struct timespec *wtime, char *file, int line ) { dcethread* my_thread; volatile boolean dbg; DO_NOT_CLOBBER(my_thread); cp->stats.wait++; cond_stats.wait++; dbg = RPC_DBG(rpc_es_dbg_mutex, 5); if (dbg) { if (! rpc__mutex_lock_assert(mp)) { RPC_DBG_PRINTF(rpc_e_dbg_mutex, 1, ("(rpc__cond_wait) mutex usage error\n")); return(false); } if (cp->mp != mp) { RPC_DBG_PRINTF(rpc_e_dbg_mutex, 1, ("(rpc__cond_wait) incorrect mutex\n")); return(false); } my_thread = dcethread_self(); mp->owner = NULL_THREAD; } mp->is_locked = false; TRY { dcethread_cond_timedwait_throw(&cp->c, &mp->m, wtime); } CATCH_ALL mp->is_locked = true; if (dbg) { mp->owner = my_thread; mp->locker_file = file; mp->locker_line = line; } RERAISE; ENDTRY mp->is_locked = true; if (dbg) { mp->owner = my_thread; mp->locker_file = file; mp->locker_line = line; } return(true); } /* * R P C _ _ C O N D _ S I G N A L * * It's not clear if it's legal to signal w/o holding the lock * (in the runtime's context); CMA clearly doesn't require it. */ boolean rpc__cond_signal ( rpc_cond_p_t cp, rpc_mutex_p_t mp ATTRIBUTE_UNUSED ) { cp->stats.signals++; cond_stats.signals++; dcethread_cond_signal(&cp->c); return(true); } /* * R P C _ _ C O N D _ B R O A D C A S T * * It's not clear if it's legal to broadcast w/o holding the lock * (in the runtime's context); CMA clearly doesn't require it. */ boolean rpc__cond_broadcast ( rpc_cond_p_t cp, rpc_mutex_p_t mp ATTRIBUTE_UNUSED ) { cp->stats.signals++; cond_stats.signals++; dcethread_cond_broadcast(&cp->c); return(true); } #else #ifdef MIREK_NOT_DEFINED INTERNAL void rpc__mutex_none (void) { } #endif #endif /* defined(RPC_MUTEX_DEBUG) || defined(RPC_MUTEX_STATS) */