/*- * See the file LICENSE for redistribution information. * * Copyright (c) 1996, 2010 Oracle and/or its affiliates. All rights reserved. * * $Id$ */ #include "db_config.h" #include "db_int.h" static inline int __db_fcntl_mutex_lock_int __P((ENV *, db_mutex_t, db_timeout_t, int)); /* * __db_fcntl_mutex_init -- * Initialize a fcntl mutex. * * PUBLIC: int __db_fcntl_mutex_init __P((ENV *, db_mutex_t, u_int32_t)); */ int __db_fcntl_mutex_init(env, mutex, flags) ENV *env; db_mutex_t mutex; u_int32_t flags; { COMPQUIET(env, NULL); COMPQUIET(mutex, MUTEX_INVALID); COMPQUIET(flags, 0); return (0); } /* * __db_fcntl_mutex_lock_int * Internal function to lock a mutex, blocking only when requested */ inline int __db_fcntl_mutex_lock_int(env, mutex, timeout, wait) ENV *env; db_mutex_t mutex; db_timeout_t timeout; int wait; { DB_ENV *dbenv; DB_MUTEX *mutexp; DB_MUTEXMGR *mtxmgr; DB_THREAD_INFO *ip; struct flock k_lock; int locked, ms, ret; db_timespec now, timespec; db_timeout_t time_left; dbenv = env->dbenv; if (!MUTEX_ON(env) || F_ISSET(dbenv, DB_ENV_NOLOCKING)) return (0); mtxmgr = env->mutex_handle; mutexp = MUTEXP_SET(mtxmgr, mutex); CHECK_MTX_THREAD(env, mutexp); #ifdef HAVE_STATISTICS if (F_ISSET(mutexp, DB_MUTEX_LOCKED)) ++mutexp->mutex_set_wait; else ++mutexp->mutex_set_nowait; #endif /* Initialize the lock. */ k_lock.l_whence = SEEK_SET; k_lock.l_start = mutex; k_lock.l_len = 1; if (timeout != 0) { timespecclear(×pec); __clock_set_expires(env, ×pec, timeout); } /* * Only check the thread state once, by initializing the thread * control block pointer to null. If it is not the failchk * thread, then ip will have a valid value subsequent times * in the loop. */ ip = NULL; for (locked = 0;;) { /* * Wait for the lock to become available; wait 1ms initially, * up to 1 second. */ for (ms = 1; F_ISSET(mutexp, DB_MUTEX_LOCKED);) { if (F_ISSET(dbenv, DB_ENV_FAILCHK) && ip == NULL && dbenv->is_alive(dbenv, mutexp->pid, mutexp->tid, 0) == 0) { ret = __env_set_state(env, &ip, THREAD_VERIFY); if (ret != 0 || ip->dbth_state == THREAD_FAILCHK) return (DB_RUNRECOVERY); } if (!wait) return (DB_LOCK_NOTGRANTED); if (timeout != 0) { timespecclear(&now); if (__clock_expired(env, &now, ×pec)) return (DB_TIMEOUT); DB_TIMESPEC_TO_TIMEOUT(time_left, &now, 0); time_left = timeout - time_left; if (ms * US_PER_MS > time_left) ms = time_left / US_PER_MS; } __os_yield(NULL, 0, ms * US_PER_MS); if ((ms <<= 1) > MS_PER_SEC) ms = MS_PER_SEC; } /* Acquire an exclusive kernel lock on the byte. */ k_lock.l_type = F_WRLCK; if (fcntl(env->lockfhp->fd, F_SETLKW, &k_lock)) goto err; /* If the resource is still available, it's ours. */ if (!F_ISSET(mutexp, DB_MUTEX_LOCKED)) { locked = 1; F_SET(mutexp, DB_MUTEX_LOCKED); dbenv->thread_id(dbenv, &mutexp->pid, &mutexp->tid); } /* Release the kernel lock. */ k_lock.l_type = F_UNLCK; if (fcntl(env->lockfhp->fd, F_SETLK, &k_lock)) goto err; /* * If we got the resource lock we're done. * * !!! * We can't check to see if the lock is ours, because we may * be trying to block ourselves in the lock manager, and so * the holder of the lock that's preventing us from getting * the lock may be us! (Seriously.) */ if (locked) break; } #ifdef DIAGNOSTIC /* * We want to switch threads as often as possible. Yield every time * we get a mutex to ensure contention. */ if (F_ISSET(dbenv, DB_ENV_YIELDCPU)) __os_yield(env, 0, 0); #endif return (0); err: ret = __os_get_syserr(); __db_syserr(env, ret, "fcntl lock failed"); return (__env_panic(env, __os_posix_err(ret))); } /* * __db_fcntl_mutex_lock * Lock a mutex, blocking if necessary. * * PUBLIC: int __db_fcntl_mutex_lock __P((ENV *, db_mutex_t, db_timeout_t)); */ int __db_fcntl_mutex_lock(env, mutex, timeout) ENV *env; db_mutex_t mutex; db_timeout_t timeout; { return (__db_fcntl_mutex_lock_int(env, mutex, timeout, 1)); } /* * __db_fcntl_mutex_trylock * Try to lock a mutex, without blocking when it is busy. * * PUBLIC: int __db_fcntl_mutex_trylock __P((ENV *, db_mutex_t)); */ int __db_fcntl_mutex_trylock(env, mutex) ENV *env; db_mutex_t mutex; { return (__db_fcntl_mutex_lock_int(env, mutex, 0, 0)); } /* * __db_fcntl_mutex_unlock -- * Release a mutex. * * PUBLIC: int __db_fcntl_mutex_unlock __P((ENV *, db_mutex_t)); */ int __db_fcntl_mutex_unlock(env, mutex) ENV *env; db_mutex_t mutex; { DB_ENV *dbenv; DB_MUTEX *mutexp; DB_MUTEXMGR *mtxmgr; dbenv = env->dbenv; if (!MUTEX_ON(env) || F_ISSET(dbenv, DB_ENV_NOLOCKING)) return (0); mtxmgr = env->mutex_handle; mutexp = MUTEXP_SET(mtxmgr, mutex); #ifdef DIAGNOSTIC if (!F_ISSET(mutexp, DB_MUTEX_LOCKED)) { __db_errx(env, "fcntl unlock failed: lock already unlocked"); return (__env_panic(env, EACCES)); } #endif /* * Release the resource. We don't have to acquire any locks because * processes trying to acquire the lock are waiting for the flag to * go to 0. Once that happens the waiters will serialize acquiring * an exclusive kernel lock before locking the mutex. */ F_CLR(mutexp, DB_MUTEX_LOCKED); return (0); } /* * __db_fcntl_mutex_destroy -- * Destroy a mutex. * * PUBLIC: int __db_fcntl_mutex_destroy __P((ENV *, db_mutex_t)); */ int __db_fcntl_mutex_destroy(env, mutex) ENV *env; db_mutex_t mutex; { COMPQUIET(env, NULL); COMPQUIET(mutex, MUTEX_INVALID); return (0); }