From cab228435bde1b5496522c03a4ce9840f2ef3701 Mon Sep 17 00:00:00 2001 From: Panu Matilainen Date: Mon, 30 Jul 2007 11:58:31 +0300 Subject: Update internal BDB to version 4.6.18. --- db/mutex/mut_alloc.c | 6 +- db/mutex/mut_failchk.c | 5 +- db/mutex/mut_fcntl.c | 7 +- db/mutex/mut_method.c | 5 +- db/mutex/mut_pthread.c | 29 +- db/mutex/mut_region.c | 99 ++--- db/mutex/mut_stat.c | 99 +++-- db/mutex/mut_stub.c | 234 +++++++++++ db/mutex/mut_tas.c | 45 ++- db/mutex/mut_win32.c | 24 +- db/mutex/mutex.c | 392 ------------------ db/mutex/test_mutex.c | 1041 ++++++++++++++++++++++++++++++++++++++++++++++++ db/mutex/tm.c | 1010 ---------------------------------------------- db/mutex/uts4_cc.s | 5 +- 14 files changed, 1486 insertions(+), 1515 deletions(-) create mode 100644 db/mutex/mut_stub.c delete mode 100644 db/mutex/mutex.c create mode 100644 db/mutex/test_mutex.c delete mode 100644 db/mutex/tm.c (limited to 'db/mutex') diff --git a/db/mutex/mut_alloc.c b/db/mutex/mut_alloc.c index bfc453dc6..12789cce1 100644 --- a/db/mutex/mut_alloc.c +++ b/db/mutex/mut_alloc.c @@ -1,10 +1,9 @@ /*- * See the file LICENSE for redistribution information. * - * Copyright (c) 1999-2006 - * Oracle Corporation. All rights reserved. + * Copyright (c) 1999,2007 Oracle. All rights reserved. * - * $Id: mut_alloc.c,v 12.15 2006/08/24 14:46:16 bostic Exp $ + * $Id: mut_alloc.c,v 12.18 2007/05/17 15:15:45 bostic Exp $ */ #include "db_config.h" @@ -119,6 +118,7 @@ __mutex_alloc_int(dbenv, locksys, alloc_id, flags, indxp) *indxp = mtxregion->mutex_next; mutexp = MUTEXP_SET(*indxp); + DB_ASSERT(dbenv, ((uintptr_t)mutexp & (dbenv->mutex_align - 1)) == 0); mtxregion->mutex_next = mutexp->mutex_next_link; --mtxregion->stat.st_mutex_free; diff --git a/db/mutex/mut_failchk.c b/db/mutex/mut_failchk.c index 3d9d46a98..707dc57cf 100644 --- a/db/mutex/mut_failchk.c +++ b/db/mutex/mut_failchk.c @@ -1,10 +1,9 @@ /*- * See the file LICENSE for redistribution information. * - * Copyright (c) 2005-2006 - * Oracle Corporation. All rights reserved. + * Copyright (c) 2005,2007 Oracle. All rights reserved. * - * $Id: mut_failchk.c,v 12.3 2006/08/24 14:46:16 bostic Exp $ + * $Id: mut_failchk.c,v 12.5 2007/05/17 15:15:45 bostic Exp $ */ #include "db_config.h" diff --git a/db/mutex/mut_fcntl.c b/db/mutex/mut_fcntl.c index eb4c6ef7f..03a55a6fc 100644 --- a/db/mutex/mut_fcntl.c +++ b/db/mutex/mut_fcntl.c @@ -1,10 +1,9 @@ /*- * See the file LICENSE for redistribution information. * - * Copyright (c) 1996-2006 - * Oracle Corporation. All rights reserved. + * Copyright (c) 1996,2007 Oracle. All rights reserved. * - * $Id: mut_fcntl.c,v 12.20 2006/08/24 14:46:16 bostic Exp $ + * $Id: mut_fcntl.c,v 12.23 2007/05/17 15:15:45 bostic Exp $ */ #include "db_config.h" @@ -73,7 +72,7 @@ __db_fcntl_mutex_lock(dbenv, mutex) * up to 1 second. */ for (ms = 1; F_ISSET(mutexp, DB_MUTEX_LOCKED);) { - __os_sleep(NULL, 0, ms * USEC_PER_MS); + __os_sleep(NULL, 0, ms * US_PER_MS); if ((ms <<= 1) > MS_PER_SEC) ms = MS_PER_SEC; } diff --git a/db/mutex/mut_method.c b/db/mutex/mut_method.c index 08da11d94..47a2aebf5 100644 --- a/db/mutex/mut_method.c +++ b/db/mutex/mut_method.c @@ -1,10 +1,9 @@ /*- * See the file LICENSE for redistribution information. * - * Copyright (c) 1996-2006 - * Oracle Corporation. All rights reserved. + * Copyright (c) 1996,2007 Oracle. All rights reserved. * - * $Id: mut_method.c,v 12.12 2006/08/24 14:46:16 bostic Exp $ + * $Id: mut_method.c,v 12.14 2007/05/17 15:15:45 bostic Exp $ */ #include "db_config.h" diff --git a/db/mutex/mut_pthread.c b/db/mutex/mut_pthread.c index 08c7b388b..58c916e1c 100644 --- a/db/mutex/mut_pthread.c +++ b/db/mutex/mut_pthread.c @@ -1,15 +1,19 @@ /*- * See the file LICENSE for redistribution information. * - * Copyright (c) 1999-2006 - * Oracle Corporation. All rights reserved. + * Copyright (c) 1999,2007 Oracle. All rights reserved. * - * $Id: mut_pthread.c,v 12.19 2006/08/24 14:46:16 bostic Exp $ + * $Id: mut_pthread.c,v 12.24 2007/06/21 16:39:20 ubell Exp $ */ #include "db_config.h" #include "db_int.h" + +/* + * This is where we load in architecture/compiler specific mutex code. + */ +#define LOAD_ACTUAL_MUTEX_CODE #include "dbinc/mutex_int.h" #ifdef HAVE_MUTEX_SOLARIS_LWP @@ -181,7 +185,7 @@ __db_pthread_mutex_lock(dbenv, mutex) mtxregion = mtxmgr->reginfo.primary; mutexp = MUTEXP_SET(mutex); -#ifdef HAVE_STATISTICS +#if defined(HAVE_STATISTICS) && !defined(HAVE_MUTEX_HYBRID) /* * We want to know which mutexes are contentious, but don't want to * do an interlocked test here -- that's slower when the underlying @@ -200,6 +204,17 @@ __db_pthread_mutex_lock(dbenv, mutex) goto err; if (F_ISSET(mutexp, DB_MUTEX_SELF_BLOCK)) { + /* + * If we are using hybrid mutexes then the pthread mutexes + * are only used to wait after spinning on the TAS mutex. + * Set the wait flag before checking to see if the mutex + * is still locked. The holder will clear the bit before + * checking the wait flag. + */ +#ifdef HAVE_MUTEX_HYBRID + mutexp->wait++; + MUTEX_MEMBAR(mutexp->wait); +#endif while (F_ISSET(mutexp, DB_MUTEX_LOCKED)) { RET_SET((pthread_cond_wait( &mutexp->cond, &mutexp->mutex)), ret); @@ -223,9 +238,13 @@ __db_pthread_mutex_lock(dbenv, mutex) } } +#ifdef HAVE_MUTEX_HYBRID + mutexp->wait--; +#else F_SET(mutexp, DB_MUTEX_LOCKED); dbenv->thread_id(dbenv, &mutexp->pid, &mutexp->tid); CHECK_MTX_THREAD(dbenv, mutexp); +#endif /* * According to HP-UX engineers contacted by Netscape, @@ -297,7 +316,7 @@ __db_pthread_mutex_unlock(dbenv, mutex) mtxregion = mtxmgr->reginfo.primary; mutexp = MUTEXP_SET(mutex); -#ifdef DIAGNOSTIC +#if !defined(HAVE_MUTEX_HYBRID) && defined(DIAGNOSTIC) if (!F_ISSET(mutexp, DB_MUTEX_LOCKED)) { __db_errx( dbenv, "pthread unlock failed: lock already unlocked"); diff --git a/db/mutex/mut_region.c b/db/mutex/mut_region.c index 6e1b4f3ad..99ad80c70 100644 --- a/db/mutex/mut_region.c +++ b/db/mutex/mut_region.c @@ -1,10 +1,9 @@ /*- * See the file LICENSE for redistribution information. * - * Copyright (c) 1996-2006 - * Oracle Corporation. All rights reserved. + * Copyright (c) 1996,2007 Oracle. All rights reserved. * - * $Id: mut_region.c,v 12.18 2006/08/24 14:46:16 bostic Exp $ + * $Id: mut_region.c,v 12.25 2007/05/17 15:15:45 bostic Exp $ */ #include "db_config.h" @@ -13,8 +12,10 @@ #include "dbinc/log.h" #include "dbinc/lock.h" #include "dbinc/mp.h" +#include "dbinc/txn.h" #include "dbinc/mutex_int.h" +static size_t __mutex_align_size __P((DB_ENV *)); static int __mutex_region_init __P((DB_ENV *, DB_MUTEXMGR *)); static size_t __mutex_region_size __P((DB_ENV *)); @@ -22,11 +23,12 @@ static size_t __mutex_region_size __P((DB_ENV *)); * __mutex_open -- * Open a mutex region. * - * PUBLIC: int __mutex_open __P((DB_ENV *)); + * PUBLIC: int __mutex_open __P((DB_ENV *, int)); */ int -__mutex_open(dbenv) +__mutex_open(dbenv, create_ok) DB_ENV *dbenv; + int create_ok; { DB_MUTEXMGR *mtxmgr; DB_MUTEXREGION *mtxregion; @@ -57,20 +59,21 @@ __mutex_open(dbenv) __lock_region_mutex_count(dbenv) + __log_region_mutex_count(dbenv) + __memp_region_mutex_count(dbenv) + + __txn_region_mutex_count(dbenv) + dbenv->mutex_inc + 100; /* Create/initialize the mutex manager structure. */ if ((ret = __os_calloc(dbenv, 1, sizeof(DB_MUTEXMGR), &mtxmgr)) != 0) return (ret); - /* Join/create the txn region. */ + /* Join/create the mutex region. */ mtxmgr->reginfo.dbenv = dbenv; mtxmgr->reginfo.type = REGION_TYPE_MUTEX; mtxmgr->reginfo.id = INVALID_REGION_ID; mtxmgr->reginfo.flags = REGION_JOIN_OK; - if (F_ISSET(dbenv, DB_ENV_CREATE)) + if (create_ok) F_SET(&mtxmgr->reginfo, REGION_CREATE_OK); - if ((ret = __db_r_attach(dbenv, + if ((ret = __env_region_attach(dbenv, &mtxmgr->reginfo, __mutex_region_size(dbenv))) != 0) goto err; @@ -82,7 +85,7 @@ __mutex_open(dbenv) /* Set the local addresses. */ mtxregion = mtxmgr->reginfo.primary = R_ADDR(&mtxmgr->reginfo, mtxmgr->reginfo.rp->primary); - mtxmgr->mutex_array = R_ADDR(&mtxmgr->reginfo, mtxregion->mutex_offset); + mtxmgr->mutex_array = R_ADDR(&mtxmgr->reginfo, mtxregion->mutex_off); dbenv->mutex_handle = mtxmgr; @@ -123,20 +126,11 @@ __mutex_open(dbenv) } } - /* - * Initialize thread tracking. We want to do this as early - * as possible in case we die. This sits in the mutex region - * so do it now. - */ - if ((ret = __env_thread_init(dbenv, - F_ISSET(&mtxmgr->reginfo, REGION_CREATE))) != 0) - goto err; - return (0); err: dbenv->mutex_handle = NULL; if (mtxmgr->reginfo.addr != NULL) - (void)__db_r_detach(dbenv, &mtxmgr->reginfo, 0); + (void)__env_region_detach(dbenv, &mtxmgr->reginfo, 0); __os_free(dbenv, mtxmgr); return (ret); @@ -159,8 +153,8 @@ __mutex_region_init(dbenv, mtxmgr) COMPQUIET(mutexp, NULL); - if ((ret = __db_shalloc(&mtxmgr->reginfo, - sizeof(DB_MUTEXREGION), 0, &mtxmgr->reginfo.primary)) != 0) { + if ((ret = __env_alloc(&mtxmgr->reginfo, + sizeof(DB_MUTEXREGION), &mtxmgr->reginfo.primary)) != 0) { __db_errx(dbenv, "Unable to allocate memory for the mutex region"); return (ret); @@ -174,8 +168,7 @@ __mutex_region_init(dbenv, mtxmgr) dbenv, MTX_MUTEX_REGION, 0, &mtxregion->mtx_region)) != 0) return (ret); - mtxregion->mutex_size = - (size_t)DB_ALIGN(sizeof(DB_MUTEX), dbenv->mutex_align); + mtxregion->mutex_size = __mutex_align_size(dbenv); mtxregion->stat.st_mutex_align = dbenv->mutex_align; mtxregion->stat.st_mutex_cnt = dbenv->mutex_cnt; @@ -183,22 +176,29 @@ __mutex_region_init(dbenv, mtxmgr) /* * Get a chunk of memory to be used for the mutexes themselves. Each - * piece of the memory must be properly aligned. + * piece of the memory must be properly aligned, and that alignment + * may be more restrictive than the memory alignment returned by the + * underlying allocation code. We already know how much memory each + * mutex in the array will take up, but we need to offset the first + * mutex in the array so the array begins properly aligned. * * The OOB mutex (MUTEX_INVALID) is 0. To make this work, we ignore * the first allocated slot when we build the free list. We have to * correct the count by 1 here, though, otherwise our counter will be * off by 1. */ - if ((ret = __db_shalloc(&mtxmgr->reginfo, + if ((ret = __env_alloc(&mtxmgr->reginfo, + mtxregion->stat.st_mutex_align + (mtxregion->stat.st_mutex_cnt + 1) * mtxregion->mutex_size, - mtxregion->stat.st_mutex_align, &mutex_array)) != 0) { + &mutex_array)) != 0) { __db_errx(dbenv, "Unable to allocate memory for mutexes from the region"); return (ret); } - mtxregion->mutex_offset = R_OFFSET(&mtxmgr->reginfo, mutex_array); + mtxregion->mutex_off_alloc = R_OFFSET(&mtxmgr->reginfo, mutex_array); + mutex_array = ALIGNP_INC(mutex_array, mtxregion->stat.st_mutex_align); + mtxregion->mutex_off = R_OFFSET(&mtxmgr->reginfo, mutex_array); mtxmgr->mutex_array = mutex_array; /* @@ -225,13 +225,13 @@ __mutex_region_init(dbenv, mtxmgr) } /* - * __mutex_dbenv_refresh -- + * __mutex_env_refresh -- * Clean up after the mutex region on a close or failed open. * - * PUBLIC: int __mutex_dbenv_refresh __P((DB_ENV *)); + * PUBLIC: int __mutex_env_refresh __P((DB_ENV *)); */ int -__mutex_dbenv_refresh(dbenv) +__mutex_env_refresh(dbenv) DB_ENV *dbenv; { DB_MUTEXMGR *mtxmgr; @@ -257,12 +257,12 @@ __mutex_dbenv_refresh(dbenv) __mutex_resource_return(dbenv, reginfo); #endif /* Discard the mutex array. */ - __db_shalloc_free( - reginfo, R_ADDR(reginfo, mtxregion->mutex_offset)); + __env_alloc_free( + reginfo, R_ADDR(reginfo, mtxregion->mutex_off_alloc)); } /* Detach from the region. */ - ret = __db_r_detach(dbenv, reginfo, 0); + ret = __env_region_detach(dbenv, reginfo, 0); __os_free(dbenv, mtxmgr); @@ -271,6 +271,18 @@ __mutex_dbenv_refresh(dbenv) return (ret); } +/* + * __mutex_align_size -- + * Return how much memory each mutex will take up if an array of them + * are to be properly aligned, individually, within the array. + */ +static size_t +__mutex_align_size(dbenv) + DB_ENV *dbenv; +{ + return ((size_t)DB_ALIGN(sizeof(DB_MUTEX), (dbenv)->mutex_align)); +} + /* * __mutex_region_size -- * Return the amount of space needed for the mutex region. @@ -282,16 +294,11 @@ __mutex_region_size(dbenv) size_t s; s = sizeof(DB_MUTEXMGR) + 1024; - s += dbenv->mutex_cnt * - __db_shalloc_size(sizeof(DB_MUTEX), dbenv->mutex_align); - /* - * Allocate space for thread info blocks. Max is only advisory, - * so we allocate 25% more. - */ - s += (dbenv->thr_max + dbenv->thr_max/4) * - __db_shalloc_size(sizeof(DB_THREAD_INFO), sizeof(roff_t)); - s += dbenv->thr_nbucket * - __db_shalloc_size(sizeof(DB_HASHTAB), sizeof(roff_t)); + + /* We discard one mutex for the OOB slot. */ + s += __env_alloc_size( + (dbenv->mutex_cnt + 1) *__mutex_align_size(dbenv)); + return (s); } @@ -320,8 +327,8 @@ __mutex_resource_return(dbenv, infop) * * Walk the list of mutexes and destroy any live ones. * - * This is just like joining a region -- the REGINFO we're handed - * is the same as the one returned by __db_r_attach(), all we have + * This is just like joining a region -- the REGINFO we're handed is + * the same as the one returned by __env_region_attach(), all we have * to do is fill in the links. * * !!! @@ -334,7 +341,7 @@ __mutex_resource_return(dbenv, infop) mtxmgr->reginfo = *infop; mtxregion = mtxmgr->reginfo.primary = R_ADDR(&mtxmgr->reginfo, mtxmgr->reginfo.rp->primary); - mtxmgr->mutex_array = R_ADDR(&mtxmgr->reginfo, mtxregion->mutex_offset); + mtxmgr->mutex_array = R_ADDR(&mtxmgr->reginfo, mtxregion->mutex_off); /* * This is a little strange, but the mutex_handle is what all of the diff --git a/db/mutex/mut_stat.c b/db/mutex/mut_stat.c index 1e642fe2f..829d54a8f 100644 --- a/db/mutex/mut_stat.c +++ b/db/mutex/mut_stat.c @@ -1,10 +1,9 @@ /*- * See the file LICENSE for redistribution information. * - * Copyright (c) 1996-2006 - * Oracle Corporation. All rights reserved. + * Copyright (c) 1996,2007 Oracle. All rights reserved. * - * $Id: mut_stat.c,v 12.17 2006/08/24 14:46:16 bostic Exp $ + * $Id: mut_stat.c,v 12.26 2007/05/17 17:18:01 bostic Exp $ */ #include "db_config.h" @@ -19,22 +18,21 @@ static int __mutex_print_all __P((DB_ENV *, u_int32_t)); static const char *__mutex_print_id __P((int)); static int __mutex_print_stats __P((DB_ENV *, u_int32_t)); static void __mutex_print_summary __P((DB_ENV *)); +static int __mutex_stat __P((DB_ENV *, DB_MUTEX_STAT **, u_int32_t)); /* - * __mutex_stat -- - * DB_ENV->mutex_stat. + * __mutex_stat_pp -- + * DB_ENV->mutex_stat pre/post processing. * - * PUBLIC: int __mutex_stat __P((DB_ENV *, DB_MUTEX_STAT **, u_int32_t)); + * PUBLIC: int __mutex_stat_pp __P((DB_ENV *, DB_MUTEX_STAT **, u_int32_t)); */ int -__mutex_stat(dbenv, statp, flags) +__mutex_stat_pp(dbenv, statp, flags) DB_ENV *dbenv; DB_MUTEX_STAT **statp; u_int32_t flags; { - DB_MUTEXMGR *mtxmgr; - DB_MUTEXREGION *mtxregion; - DB_MUTEX_STAT *stats; + DB_THREAD_INFO *ip; int ret; PANIC_CHECK(dbenv); @@ -43,6 +41,29 @@ __mutex_stat(dbenv, statp, flags) "DB_ENV->mutex_stat", flags, DB_STAT_CLEAR)) != 0) return (ret); + ENV_ENTER(dbenv, ip); + REPLICATION_WRAP(dbenv, (__mutex_stat(dbenv, statp, flags)), ret); + ENV_LEAVE(dbenv, ip); + return (ret); +} + +/* + * __mutex_stat -- + * DB_ENV->mutex_stat. + */ +static int +__mutex_stat(dbenv, statp, flags) + DB_ENV *dbenv; + DB_MUTEX_STAT **statp; + u_int32_t flags; +{ + DB_MUTEXMGR *mtxmgr; + DB_MUTEXREGION *mtxregion; + DB_MUTEX_STAT *stats; + int ret; + + PANIC_CHECK(dbenv); + *statp = NULL; mtxmgr = dbenv->mutex_handle; mtxregion = mtxmgr->reginfo.primary; @@ -70,17 +91,17 @@ __mutex_stat(dbenv, statp, flags) } /* - * __mutex_stat_print - * DB_ENV->mutex_stat_print method. + * __mutex_stat_print_pp -- + * DB_ENV->mutex_stat_print pre/post processing. * - * PUBLIC: int __mutex_stat_print __P((DB_ENV *, u_int32_t)); + * PUBLIC: int __mutex_stat_print_pp __P((DB_ENV *, u_int32_t)); */ int -__mutex_stat_print(dbenv, flags) +__mutex_stat_print_pp(dbenv, flags) DB_ENV *dbenv; u_int32_t flags; { - u_int32_t orig_flags; + DB_THREAD_INFO *ip; int ret; PANIC_CHECK(dbenv); @@ -89,8 +110,28 @@ __mutex_stat_print(dbenv, flags) flags, DB_STAT_ALL | DB_STAT_CLEAR)) != 0) return (ret); + ENV_ENTER(dbenv, ip); + REPLICATION_WRAP(dbenv, (__mutex_stat_print(dbenv, flags)), ret); + ENV_LEAVE(dbenv, ip); + return (ret); +} + +/* + * __mutex_stat_print + * DB_ENV->mutex_stat_print method. + * + * PUBLIC: int __mutex_stat_print __P((DB_ENV *, u_int32_t)); + */ +int +__mutex_stat_print(dbenv, flags) + DB_ENV *dbenv; + u_int32_t flags; +{ + u_int32_t orig_flags; + int ret; + orig_flags = flags; - LF_CLR(DB_STAT_CLEAR); + LF_CLR(DB_STAT_CLEAR | DB_STAT_SUBSYSTEM); if (flags == 0 || LF_ISSET(DB_STAT_ALL)) { ret = __mutex_print_stats(dbenv, orig_flags); __mutex_print_summary(dbenv); @@ -149,10 +190,6 @@ __mutex_print_stats(dbenv, flags) u_int32_t flags; { DB_MUTEX_STAT *sp; - DB_MUTEXMGR *mtxmgr; - DB_MUTEXREGION *mtxregion; - REGINFO *infop; - THREAD_INFO *thread; int ret; if ((ret = __mutex_stat(dbenv, &sp, LF_ISSET(DB_STAT_CLEAR))) != 0) @@ -176,20 +213,6 @@ __mutex_print_stats(dbenv, flags) __os_ufree(dbenv, sp); - /* - * Dump out the info we have on thread tracking, we do it here only - * because we share the region. - */ - if (dbenv->thr_hashtab != NULL) { - mtxmgr = dbenv->mutex_handle; - mtxregion = mtxmgr->reginfo.primary; - infop = &mtxmgr->reginfo; - thread = R_ADDR(infop, mtxregion->thread_off); - STAT_ULONG("Thread blocks allocated", thread->thr_count); - STAT_ULONG("Thread allocation threshold", thread->thr_max); - STAT_ULONG("Thread hash buckets", thread->thr_nbucket); - } - return (0); } @@ -222,7 +245,7 @@ __mutex_print_all(dbenv, flags) mtxmgr = dbenv->mutex_handle; mtxregion = mtxmgr->reginfo.primary; - __db_print_reginfo(dbenv, &mtxmgr->reginfo, "Mutex"); + __db_print_reginfo(dbenv, &mtxmgr->reginfo, "Mutex", flags); __db_msg(dbenv, "%s", DB_GLOBAL(db_line)); __db_msg(dbenv, "DB_MUTEXREGION structure:"); @@ -282,6 +305,8 @@ __mutex_print_debug_single(dbenv, tag, mutex, flags) DB_MSGBUF_INIT(&mb); mbp = &mb; + if (LF_ISSET(DB_STAT_SUBSYSTEM)) + LF_CLR(DB_STAT_CLEAR); __db_msgadd(dbenv, mbp, "%lu\t%s ", (u_long)mutex, tag); __mutex_print_debug_stats(dbenv, mbp, mutex, flags); DB_MSGBUF_FLUSH(dbenv, mbp); @@ -427,7 +452,7 @@ __mutex_clear(dbenv, mutex) #else /* !HAVE_STATISTICS */ int -__mutex_stat(dbenv, statp, flags) +__mutex_stat_pp(dbenv, statp, flags) DB_ENV *dbenv; DB_MUTEX_STAT **statp; u_int32_t flags; @@ -439,7 +464,7 @@ __mutex_stat(dbenv, statp, flags) } int -__mutex_stat_print(dbenv, flags) +__mutex_stat_print_pp(dbenv, flags) DB_ENV *dbenv; u_int32_t flags; { diff --git a/db/mutex/mut_stub.c b/db/mutex/mut_stub.c new file mode 100644 index 000000000..d4620c771 --- /dev/null +++ b/db/mutex/mut_stub.c @@ -0,0 +1,234 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1996,2007 Oracle. All rights reserved. + * + * $Id: mut_stub.c,v 12.4 2007/05/17 15:15:45 bostic Exp $ + */ + +#ifndef HAVE_MUTEX_SUPPORT +#include "db_config.h" + +#include "db_int.h" +#include "dbinc/db_page.h" +#include "dbinc/db_am.h" + +/* + * If the library wasn't compiled with mutex support, various routines + * aren't available. Stub them here, returning an appropriate error. + */ +static int __db_nomutex __P((DB_ENV *)); + +/* + * __db_nomutex -- + * Error when a Berkeley DB build doesn't include mutexes. + */ +static int +__db_nomutex(dbenv) + DB_ENV *dbenv; +{ + __db_errx(dbenv, + "library build did not include support for mutexes"); + return (DB_OPNOTSUP); +} + +int +__mutex_alloc_pp(dbenv, flags, indxp) + DB_ENV *dbenv; + u_int32_t flags; + db_mutex_t *indxp; +{ + COMPQUIET(flags, 0); + COMPQUIET(indxp, NULL); + return (__db_nomutex(dbenv)); +} + +int +__mutex_alloc(dbenv, alloc_id, flags, indxp) + DB_ENV *dbenv; + int alloc_id; + u_int32_t flags; + db_mutex_t *indxp; +{ + COMPQUIET(dbenv, NULL); + COMPQUIET(alloc_id, 0); + COMPQUIET(flags, 0); + *indxp = MUTEX_INVALID; + return (0); +} + +void +__mutex_clear(dbenv, mutex) + DB_ENV *dbenv; + db_mutex_t mutex; +{ + COMPQUIET(dbenv, NULL); + COMPQUIET(mutex, MUTEX_INVALID); +} + +int +__mutex_free_pp(dbenv, indx) + DB_ENV *dbenv; + db_mutex_t indx; +{ + COMPQUIET(indx, 0); + return (__db_nomutex(dbenv)); +} + +int +__mutex_free(dbenv, indxp) + DB_ENV *dbenv; + db_mutex_t *indxp; +{ + COMPQUIET(dbenv, NULL); + *indxp = MUTEX_INVALID; + return (0); +} + +int +__mutex_get_align(dbenv, alignp) + DB_ENV *dbenv; + u_int32_t *alignp; +{ + COMPQUIET(alignp, NULL); + return (__db_nomutex(dbenv)); +} + +int +__mutex_get_increment(dbenv, incrementp) + DB_ENV *dbenv; + u_int32_t *incrementp; +{ + COMPQUIET(incrementp, NULL); + return (__db_nomutex(dbenv)); +} + +int +__mutex_get_max(dbenv, maxp) + DB_ENV *dbenv; + u_int32_t *maxp; +{ + COMPQUIET(maxp, NULL); + return (__db_nomutex(dbenv)); +} + +int +__mutex_get_tas_spins(dbenv, tas_spinsp) + DB_ENV *dbenv; + u_int32_t *tas_spinsp; +{ + COMPQUIET(tas_spinsp, NULL); + return (__db_nomutex(dbenv)); +} + +int +__mutex_lock_pp(dbenv, indx) + DB_ENV *dbenv; + db_mutex_t indx; +{ + COMPQUIET(indx, 0); + return (__db_nomutex(dbenv)); +} + +void +__mutex_print_debug_single(dbenv, tag, mutex, flags) + DB_ENV *dbenv; + const char *tag; + db_mutex_t mutex; + u_int32_t flags; +{ + COMPQUIET(dbenv, NULL); + COMPQUIET(tag, NULL); + COMPQUIET(mutex, MUTEX_INVALID); + COMPQUIET(flags, 0); +} + +void +__mutex_print_debug_stats(dbenv, mbp, mutex, flags) + DB_ENV *dbenv; + DB_MSGBUF *mbp; + db_mutex_t mutex; + u_int32_t flags; +{ + COMPQUIET(dbenv, NULL); + COMPQUIET(mbp, NULL); + COMPQUIET(mutex, MUTEX_INVALID); + COMPQUIET(flags, 0); +} + +int +__mutex_set_align(dbenv, align) + DB_ENV *dbenv; + u_int32_t align; +{ + COMPQUIET(align, 0); + return (__db_nomutex(dbenv)); +} + +int +__mutex_set_increment(dbenv, increment) + DB_ENV *dbenv; + u_int32_t increment; +{ + COMPQUIET(increment, 0); + return (__db_nomutex(dbenv)); +} + +int +__mutex_set_max(dbenv, max) + DB_ENV *dbenv; + u_int32_t max; +{ + COMPQUIET(max, 0); + return (__db_nomutex(dbenv)); +} + +int +__mutex_set_tas_spins(dbenv, tas_spins) + DB_ENV *dbenv; + u_int32_t tas_spins; +{ + COMPQUIET(tas_spins, 0); + return (__db_nomutex(dbenv)); +} + +void +__mutex_set_wait_info(dbenv, mutex, waitp, nowaitp) + DB_ENV *dbenv; + db_mutex_t mutex; + u_int32_t *waitp, *nowaitp; +{ + COMPQUIET(dbenv, NULL); + COMPQUIET(mutex, MUTEX_INVALID); + *waitp = *nowaitp = 0; +} + +int +__mutex_stat_pp(dbenv, statp, flags) + DB_ENV *dbenv; + DB_MUTEX_STAT **statp; + u_int32_t flags; +{ + COMPQUIET(statp, NULL); + COMPQUIET(flags, 0); + return (__db_nomutex(dbenv)); +} + +int +__mutex_stat_print_pp(dbenv, flags) + DB_ENV *dbenv; + u_int32_t flags; +{ + COMPQUIET(flags, 0); + return (__db_nomutex(dbenv)); +} + +int +__mutex_unlock_pp(dbenv, indx) + DB_ENV *dbenv; + db_mutex_t indx; +{ + COMPQUIET(indx, 0); + return (__db_nomutex(dbenv)); +} +#endif /* !HAVE_MUTEX_SUPPORT */ diff --git a/db/mutex/mut_tas.c b/db/mutex/mut_tas.c index bbe25fa8e..c9f3cddcf 100644 --- a/db/mutex/mut_tas.c +++ b/db/mutex/mut_tas.c @@ -1,10 +1,9 @@ /*- * See the file LICENSE for redistribution information. * - * Copyright (c) 1996-2006 - * Oracle Corporation. All rights reserved. + * Copyright (c) 1996,2007 Oracle. All rights reserved. * - * $Id: mut_tas.c,v 12.20 2006/08/24 14:46:16 bostic Exp $ + * $Id: mut_tas.c,v 12.27 2007/06/21 16:39:20 ubell Exp $ */ #include "db_config.h" @@ -12,7 +11,7 @@ #include "db_int.h" /* - * This is where we load in the actual test-and-set mutex code. + * This is where we load in architecture/compiler specific mutex code. */ #define LOAD_ACTUAL_MUTEX_CODE #include "dbinc/mutex_int.h" @@ -51,6 +50,11 @@ __db_tas_mutex_init(dbenv, mutex, flags) __db_syserr(dbenv, ret, "TAS: mutex initialize"); return (__os_posix_err(ret)); } +#ifdef HAVE_MUTEX_HYBRID + if ((ret = __db_pthread_mutex_init(dbenv, + mutex, flags | DB_MUTEX_SELF_BLOCK)) != 0) + return (ret); +#endif return (0); } @@ -69,8 +73,11 @@ __db_tas_mutex_lock(dbenv, mutex) DB_MUTEXMGR *mtxmgr; DB_MUTEXREGION *mtxregion; u_int32_t nspins; +#ifdef HAVE_MUTEX_HYBRID + int ret; +#else u_long ms, max_ms; - +#endif if (!MUTEX_ON(dbenv) || F_ISSET(dbenv, DB_ENV_NOLOCKING)) return (0); @@ -85,6 +92,7 @@ __db_tas_mutex_lock(dbenv, mutex) ++mutexp->mutex_set_nowait; #endif +#ifndef HAVE_MUTEX_HYBRID /* * Wait 1ms initially, up to 10ms for mutexes backing logical database * locks, and up to 25 ms for mutual exclusion data structure mutexes. @@ -92,6 +100,7 @@ __db_tas_mutex_lock(dbenv, mutex) */ ms = 1; max_ms = F_ISSET(mutexp, DB_MUTEX_LOGICAL_LOCK) ? 10 : 25; +#endif loop: /* Attempt to acquire the resource for N spins. */ for (nspins = @@ -167,9 +176,14 @@ relock: } /* Wait for the lock to become available. */ - __os_sleep(dbenv, 0, ms * USEC_PER_MS); +#ifdef HAVE_MUTEX_HYBRID + if ((ret = __db_pthread_mutex_lock(dbenv, mutex)) != 0) + return (ret); +#else + __os_sleep(dbenv, 0, ms * US_PER_MS); if ((ms <<= 1) > max_ms) ms = max_ms; +#endif /* * We're spinning. The environment might be hung, and somebody else @@ -196,6 +210,9 @@ __db_tas_mutex_unlock(dbenv, mutex) DB_MUTEX *mutexp; DB_MUTEXMGR *mtxmgr; DB_MUTEXREGION *mtxregion; +#ifdef HAVE_MUTEX_HYBRID + int ret; +#endif if (!MUTEX_ON(dbenv) || F_ISSET(dbenv, DB_ENV_NOLOCKING)) return (0); @@ -210,8 +227,15 @@ __db_tas_mutex_unlock(dbenv, mutex) return (__db_panic(dbenv, EACCES)); } #endif + F_CLR(mutexp, DB_MUTEX_LOCKED); +#ifdef HAVE_MUTEX_HYBRID + MUTEX_MEMBAR(mutexp->flags); + if (mutexp->wait && + (ret = __db_pthread_mutex_unlock(dbenv, mutex)) != 0) + return (ret); +#endif MUTEX_UNSET(&mutexp->tas); return (0); @@ -231,6 +255,9 @@ __db_tas_mutex_destroy(dbenv, mutex) DB_MUTEX *mutexp; DB_MUTEXMGR *mtxmgr; DB_MUTEXREGION *mtxregion; +#ifdef HAVE_MUTEX_HYBRID + int ret; +#endif if (!MUTEX_ON(dbenv)) return (0); @@ -241,5 +268,11 @@ __db_tas_mutex_destroy(dbenv, mutex) MUTEX_DESTROY(&mutexp->tas); +#ifdef HAVE_MUTEX_HYBRID + if ((ret = __db_pthread_mutex_destroy(dbenv, mutex)) != 0) + return (ret); +#endif + + COMPQUIET(mutexp, NULL); /* MUTEX_DESTROY may not be defined. */ return (0); } diff --git a/db/mutex/mut_win32.c b/db/mutex/mut_win32.c index 083784310..2d0385e90 100644 --- a/db/mutex/mut_win32.c +++ b/db/mutex/mut_win32.c @@ -1,10 +1,9 @@ /* * See the file LICENSE for redistribution information. * - * Copyright (c) 2002-2006 - * Oracle Corporation. All rights reserved. + * Copyright (c) 2002,2007 Oracle. All rights reserved. * - * $Id: mut_win32.c,v 12.21 2006/08/24 14:46:16 bostic Exp $ + * $Id: mut_win32.c,v 12.25 2007/06/28 11:14:04 alexg Exp $ */ #include "db_config.h" @@ -48,6 +47,7 @@ static __inline int get_handle(dbenv, mutexp, eventp) for (id = (mutexp)->id; id != 0; id >>= 4) *--p = hex_digits[id & 0xf]; +#ifndef DB_WINCE if (!security_initialized) { InitializeSecurityDescriptor(&null_sd, SECURITY_DESCRIPTOR_REVISION); @@ -57,6 +57,7 @@ static __inline int get_handle(dbenv, mutexp, eventp) all_sa.lpSecurityDescriptor = &null_sd; security_initialized = 1; } +#endif if ((*eventp = CreateEvent(&all_sa, FALSE, FALSE, idbuf)) == NULL) { ret = __os_get_syserr(); @@ -111,6 +112,9 @@ __db_win32_mutex_lock(dbenv, mutex) #ifdef MUTEX_DIAG LARGE_INTEGER now; #endif +#ifdef DB_WINCE + volatile db_threadid_t tmp_tid; +#endif if (!MUTEX_ON(dbenv) || F_ISSET(dbenv, DB_ENV_NOLOCKING)) return (0); @@ -130,6 +134,20 @@ loop: /* Attempt to acquire the resource for N spins. */ * We can avoid the (expensive) interlocked instructions if * the mutex is already "set". */ +#ifdef DB_WINCE + /* + * Memory mapped regions on Windows CE cause problems with + * InterlockedExchange calls. Each page in a mapped region + * needs to have been written to prior to an + * InterlockedExchange call, or the InterlockedExchange call + * hangs. This does not seem to be documented anywhere. For + * now, read/write a non-critical piece of memory from the + * shared region prior to attempting an InterlockedExchange + * operation. + */ + tmp_tid = mutexp->tid; + mutexp->tid = tmp_tid; +#endif if (mutexp->tas || !MUTEX_SET(&mutexp->tas)) { /* * Some systems (notably those with newer Intel CPUs) diff --git a/db/mutex/mutex.c b/db/mutex/mutex.c deleted file mode 100644 index 9d925d113..000000000 --- a/db/mutex/mutex.c +++ /dev/null @@ -1,392 +0,0 @@ -/*- - * See the file LICENSE for redistribution information. - * - * Copyright (c) 1999-2004 - * Sleepycat Software. All rights reserved. - * - * $Id: mutex.c,v 11.43 2004/10/15 16:59:44 bostic Exp $ - */ - -#include "db_config.h" - -#ifndef NO_SYSTEM_INCLUDES -#include - -#include -#endif - -#include "db_int.h" - -#if defined(MUTEX_NO_MALLOC_LOCKS) || defined(HAVE_MUTEX_SYSTEM_RESOURCES) -#include "dbinc/db_shash.h" -#include "dbinc/lock.h" -#include "dbinc/log.h" -#include "dbinc/mp.h" -#include "dbinc/txn.h" -#endif - -static int __db_mutex_alloc_int __P((DB_ENV *, REGINFO *, DB_MUTEX **)); -#ifdef HAVE_MUTEX_SYSTEM_RESOURCES -static REGMAINT * __db_mutex_maint __P((DB_ENV *, REGINFO *)); -#endif - -/* - * __db_mutex_setup -- - * External interface to allocate, and/or initialize, record - * mutexes. - * - * PUBLIC: int __db_mutex_setup __P((DB_ENV *, REGINFO *, void *, u_int32_t)); - */ -int -__db_mutex_setup(dbenv, infop, ptr, flags) - DB_ENV *dbenv; - REGINFO *infop; - void *ptr; - u_int32_t flags; -{ - DB_MUTEX *mutex; - REGMAINT *maint; - u_int32_t iflags, offset; - int ret; - - ret = 0; - /* - * If they indicated the region is not locked, then lock it. - * This is only needed when we have unusual mutex resources. - * (I.e. MUTEX_NO_MALLOC_LOCKS or HAVE_MUTEX_SYSTEM_RESOURCES) - */ -#if defined(MUTEX_NO_MALLOC_LOCKS) || defined(HAVE_MUTEX_SYSTEM_RESOURCES) - if (!LF_ISSET(MUTEX_NO_RLOCK)) - R_LOCK(dbenv, infop); -#endif - /* - * Allocate the mutex if they asked us to. - */ - mutex = NULL; - if (LF_ISSET(MUTEX_ALLOC)) { - if ((ret = __db_mutex_alloc_int(dbenv, infop, ptr)) != 0) - goto err; - mutex = *(DB_MUTEX **)ptr; - } else - mutex = (DB_MUTEX *)ptr; - - /* - * Set up to initialize the mutex. - */ - iflags = LF_ISSET(MUTEX_LOGICAL_LOCK | MUTEX_THREAD | MUTEX_SELF_BLOCK); - switch (infop->type) { - case REGION_TYPE_LOCK: - offset = P_TO_UINT32(mutex) + DB_FCNTL_OFF_LOCK; - break; - case REGION_TYPE_MPOOL: - offset = P_TO_UINT32(mutex) + DB_FCNTL_OFF_MPOOL; - break; - default: - offset = P_TO_UINT32(mutex) + DB_FCNTL_OFF_GEN; - break; - } - maint = NULL; -#ifdef HAVE_MUTEX_SYSTEM_RESOURCES - if (!LF_ISSET(MUTEX_NO_RECORD)) - maint = (REGMAINT *)__db_mutex_maint(dbenv, infop); -#endif - - ret = __db_mutex_init(dbenv, mutex, offset, iflags, infop, maint); -err: -#if defined(MUTEX_NO_MALLOC_LOCKS) || defined(HAVE_MUTEX_SYSTEM_RESOURCES) - if (!LF_ISSET(MUTEX_NO_RLOCK)) - R_UNLOCK(dbenv, infop); -#endif - /* - * If we allocated the mutex but had an error on init'ing, - * then we must free it before returning. - * !!! - * Free must be done after releasing region lock. - */ - if (ret != 0 && LF_ISSET(MUTEX_ALLOC) && mutex != NULL) { - __db_mutex_free(dbenv, infop, mutex); - *(DB_MUTEX **)ptr = NULL; - } - return (ret); -} - -/* - * __db_mutex_alloc_int -- - * Allocate and initialize a mutex. - */ -static int -__db_mutex_alloc_int(dbenv, infop, storep) - DB_ENV *dbenv; - REGINFO *infop; - DB_MUTEX **storep; -{ - int ret; - - /* - * If the architecture supports mutexes in heap memory, use heap memory. - * If it doesn't, we have to allocate space in a region. If allocation - * in the region fails, fallback to allocating from the mpool region, - * because it's big, it almost always exists and if it's entirely dirty, - * we can free buffers until memory is available. - */ -#if defined(MUTEX_NO_MALLOC_LOCKS) || defined(HAVE_MUTEX_SYSTEM_RESOURCES) - ret = __db_shalloc(infop, sizeof(DB_MUTEX), MUTEX_ALIGN, storep); - - if (ret == ENOMEM && MPOOL_ON(dbenv)) { - DB_MPOOL *dbmp; - - dbmp = dbenv->mp_handle; - if ((ret = __memp_alloc(dbmp, - dbmp->reginfo, NULL, sizeof(DB_MUTEX), NULL, storep)) == 0) - (*storep)->flags = MUTEX_MPOOL; - } else - (*storep)->flags = 0; -#else - COMPQUIET(dbenv, NULL); - COMPQUIET(infop, NULL); - ret = __os_calloc(dbenv, 1, sizeof(DB_MUTEX), storep); -#endif - if (ret != 0) - __db_err(dbenv, "Unable to allocate memory for mutex"); - return (ret); -} - -/* - * __db_mutex_free -- - * Free a mutex. - * - * PUBLIC: void __db_mutex_free __P((DB_ENV *, REGINFO *, DB_MUTEX *)); - */ -void -__db_mutex_free(dbenv, infop, mutexp) - DB_ENV *dbenv; - REGINFO *infop; - DB_MUTEX *mutexp; -{ -#if defined(MUTEX_NO_MALLOC_LOCKS) || defined(HAVE_MUTEX_SYSTEM_RESOURCES) - R_LOCK(dbenv, infop); -#if defined(HAVE_MUTEX_SYSTEM_RESOURCES) - if (F_ISSET(mutexp, MUTEX_INITED)) - __db_shlocks_clear(mutexp, infop, NULL); -#endif - if (F_ISSET(mutexp, MUTEX_MPOOL)) { - DB_MPOOL *dbmp; - - dbmp = dbenv->mp_handle; - R_LOCK(dbenv, dbmp->reginfo); - __db_shalloc_free(&dbmp->reginfo[0], mutexp); - R_UNLOCK(dbenv, dbmp->reginfo); - } else - __db_shalloc_free(infop, mutexp); - R_UNLOCK(dbenv, infop); -#else - COMPQUIET(dbenv, NULL); - COMPQUIET(infop, NULL); - __os_free(dbenv, mutexp); -#endif -} - -#ifdef HAVE_MUTEX_SYSTEM_RESOURCES -/* - * __db_shreg_locks_record -- - * Record an entry in the shared locks area. - * Region lock must be held in caller. - */ -static int -__db_shreg_locks_record(dbenv, mutexp, infop, rp) - DB_ENV *dbenv; - DB_MUTEX *mutexp; - REGINFO *infop; - REGMAINT *rp; -{ - u_int i; - - if (!F_ISSET(mutexp, MUTEX_INITED)) - return (0); - DB_ASSERT(mutexp->reg_off == INVALID_ROFF); - rp->stat.st_records++; - i = (roff_t *)R_ADDR(infop, rp->regmutex_hint) - &rp->regmutexes[0]; - if (rp->regmutexes[i] != INVALID_ROFF) { - /* - * Our hint failed, search for an open slot. - */ - rp->stat.st_hint_miss++; - for (i = 0; i < rp->reglocks; i++) - if (rp->regmutexes[i] == INVALID_ROFF) - break; - if (i == rp->reglocks) { - rp->stat.st_max_locks++; - __db_err(dbenv, - "Region mutexes: Exceeded maximum lock slots %lu", - (u_long)rp->reglocks); - return (ENOMEM); - } - } else - rp->stat.st_hint_hit++; - /* - * When we get here, i is an empty slot. Record this - * mutex, set hint to point to the next slot and we are done. - */ - rp->regmutexes[i] = R_OFFSET(infop, mutexp); - mutexp->reg_off = R_OFFSET(infop, &rp->regmutexes[i]); - rp->regmutex_hint = (i < rp->reglocks - 1) ? - R_OFFSET(infop, &rp->regmutexes[i+1]) : - R_OFFSET(infop, &rp->regmutexes[0]); - return (0); -} - -/* - * __db_shreg_locks_clear -- - * Erase an entry in the shared locks area. - * - * PUBLIC: void __db_shreg_locks_clear __P((DB_MUTEX *, REGINFO *, REGMAINT *)); - */ -void -__db_shreg_locks_clear(mutexp, infop, rp) - DB_MUTEX *mutexp; - REGINFO *infop; - REGMAINT *rp; -{ - /* - * !!! - * Assumes the caller's region lock is held. - */ - if (!F_ISSET(mutexp, MUTEX_INITED)) - return; - /* - * This function is generally only called on a forcible remove of an - * environment. We recorded our index in the mutex, find and clear it. - */ - DB_ASSERT(mutexp->reg_off != INVALID_ROFF); - DB_ASSERT(*(roff_t *)R_ADDR(infop, mutexp->reg_off) == \ - R_OFFSET(infop, mutexp)); - *(roff_t *)R_ADDR(infop, mutexp->reg_off) = 0; - if (rp != NULL) { - rp->regmutex_hint = mutexp->reg_off; - rp->stat.st_clears++; - } - mutexp->reg_off = INVALID_ROFF; - __db_mutex_destroy(mutexp); -} - -/* - * __db_shreg_locks_destroy -- - * Destroy all mutexes in a region's range. - * - * PUBLIC: void __db_shreg_locks_destroy __P((REGINFO *, REGMAINT *)); - */ -void -__db_shreg_locks_destroy(infop, rp) - REGINFO *infop; - REGMAINT *rp; -{ - u_int32_t i; - - /* - * Go through the list of all mutexes and destroy them. - */ - for (i = 0; i < rp->reglocks; i++) - if (rp->regmutexes[i] != 0) { - rp->stat.st_destroys++; - __db_mutex_destroy(R_ADDR(infop, rp->regmutexes[i])); - } -} - -/* - * __db_shreg_mutex_init -- - * Initialize a shared memory mutex. - * - * PUBLIC: int __db_shreg_mutex_init __P((DB_ENV *, DB_MUTEX *, u_int32_t, - * PUBLIC: u_int32_t, REGINFO *, REGMAINT *)); - */ -int -__db_shreg_mutex_init(dbenv, mutexp, offset, flags, infop, rp) - DB_ENV *dbenv; - DB_MUTEX *mutexp; - u_int32_t offset; - u_int32_t flags; - REGINFO *infop; - REGMAINT *rp; -{ - int ret; - - if ((ret = __db_mutex_init_int(dbenv, mutexp, offset, flags)) != 0) - return (ret); - /* - * Some mutexes cannot be recorded, but we want one interface. - * So, if we have no REGMAINT, then just return. - */ - if (rp == NULL) - return (ret); - /* - * !!! - * Since __db_mutex_init_int is a macro, we may not be - * using the 'offset' as it is only used for one type - * of mutex. We COMPQUIET it here, after the call above. - */ - COMPQUIET(offset, 0); - ret = __db_shreg_locks_record(dbenv, mutexp, infop, rp); - - /* - * If we couldn't record it and we are returning an error, - * we need to destroy the mutex we just created. - */ - if (ret) - __db_mutex_destroy(mutexp); - - return (ret); -} - -/* - * __db_shreg_maintinit -- - * Initialize a region's maintenance information. - * - * PUBLIC: void __db_shreg_maintinit __P((REGINFO *, void *addr, size_t)); - */ -void -__db_shreg_maintinit(infop, addr, size) - REGINFO *infop; - void *addr; - size_t size; -{ - REGMAINT *rp; - u_int32_t i; - - rp = (REGMAINT *)addr; - memset(addr, 0, sizeof(REGMAINT)); - rp->reglocks = size / sizeof(roff_t); - rp->regmutex_hint = R_OFFSET(infop, &rp->regmutexes[0]); - for (i = 0; i < rp->reglocks; i++) - rp->regmutexes[i] = INVALID_ROFF; -} - -static REGMAINT * -__db_mutex_maint(dbenv, infop) - DB_ENV *dbenv; - REGINFO *infop; -{ - roff_t moff; - - switch (infop->type) { - case REGION_TYPE_LOCK: - moff = ((DB_LOCKREGION *) - R_ADDR(infop, infop->rp->primary))->maint_off; - break; - case REGION_TYPE_LOG: - moff = ((LOG *)R_ADDR(infop, infop->rp->primary))->maint_off; - break; - case REGION_TYPE_MPOOL: - moff = ((MPOOL *)R_ADDR(infop, infop->rp->primary))->maint_off; - break; - case REGION_TYPE_TXN: - moff = ((DB_TXNREGION *) - R_ADDR(infop, infop->rp->primary))->maint_off; - break; - default: - __db_err(dbenv, - "Attempting to record mutex in a region not set up to do so"); - return (NULL); - } - return ((REGMAINT *)R_ADDR(infop, moff)); -} -#endif /* HAVE_MUTEX_SYSTEM_RESOURCES */ diff --git a/db/mutex/test_mutex.c b/db/mutex/test_mutex.c new file mode 100644 index 000000000..b0fc8397b --- /dev/null +++ b/db/mutex/test_mutex.c @@ -0,0 +1,1041 @@ +/* + * Standalone mutex tester for Berkeley DB mutexes. + * + * $Id: test_mutex.c,v 12.21 2007/06/21 16:02:29 bostic Exp $ + */ + +#include "db_config.h" + +#include "db_int.h" + +#include + +#ifdef DB_WIN32 +#define MUTEX_THREAD_TEST 1 + +extern int getopt(int, char * const *, const char *); + +typedef HANDLE os_pid_t; +typedef HANDLE os_thread_t; + +#define os_thread_create(thrp, attr, func, arg) \ + (((*(thrp) = CreateThread(NULL, 0, \ + (LPTHREAD_START_ROUTINE)(func), (arg), 0, NULL)) == NULL) ? -1 : 0) +#define os_thread_join(thr, statusp) \ + ((WaitForSingleObject((thr), INFINITE) == WAIT_OBJECT_0) && \ + GetExitCodeThread((thr), (LPDWORD)(statusp)) ? 0 : -1) +#define os_thread_self() GetCurrentThreadId() + +#else /* !DB_WIN32 */ + +typedef pid_t os_pid_t; + +/* + * There's only one mutex implementation that can't support thread-level + * locking: UNIX/fcntl mutexes. + * + * The general Berkeley DB library configuration doesn't look for the POSIX + * pthread functions, with one exception -- pthread_yield. + * + * Use these two facts to decide if we're going to build with or without + * threads. + */ +#if !defined(HAVE_MUTEX_FCNTL) && defined(HAVE_PTHREAD_YIELD) +#define MUTEX_THREAD_TEST 1 + +#include + +typedef pthread_t os_thread_t; + +#define os_thread_create(thrp, attr, func, arg) \ + pthread_create((thrp), (attr), (func), (arg)) +#define os_thread_join(thr, statusp) pthread_join((thr), (statusp)) +#define os_thread_self() pthread_self() +#endif /* HAVE_PTHREAD_YIELD */ +#endif /* !DB_WIN32 */ + +#define OS_BAD_PID ((os_pid_t)-1) + +#define TESTDIR "TESTDIR" /* Working area */ +#define MT_FILE "TESTDIR/mutex.file" +#define MT_FILE_QUIT "TESTDIR/mutex.file.quit" + +/* + * The backing data layout: + * TM[1] per-thread mutex array lock + * TM[nthreads] per-thread mutex array + * TM[maxlocks] per-lock mutex array + */ +typedef struct { + db_mutex_t mutex; /* Mutex. */ + u_long id; /* Holder's ID. */ + u_int wakeme; /* Request to awake. */ +} TM; + +DB_ENV *dbenv; /* Backing environment */ +size_t len; /* Backing data chunk size. */ + +u_int8_t *gm_addr; /* Global mutex */ +u_int8_t *lm_addr; /* Locker mutexes */ +u_int8_t *tm_addr; /* Thread mutexes */ + +#ifdef MUTEX_THREAD_TEST +os_thread_t *kidsp; /* Locker threads */ +os_thread_t wakep; /* Wakeup thread */ +#endif + +#ifndef HAVE_MMAP +u_int nprocs = 1; /* -p: Processes. */ +u_int nthreads = 20; /* -t: Threads. */ +#elif MUTEX_THREAD_TEST +u_int nprocs = 5; /* -p: Processes. */ +u_int nthreads = 4; /* -t: Threads. */ +#else +u_int nprocs = 20; /* -p: Processes. */ +u_int nthreads = 1; /* -t: Threads. */ +#endif + +u_int maxlocks = 20; /* -l: Backing locks. */ +u_int nlocks = 10000; /* -n: Locks per process. */ +int verbose; /* -v: Verbosity. */ + +const char *progname; + +void data_off(u_int8_t *, DB_FH *); +void data_on(u_int8_t **, u_int8_t **, u_int8_t **, DB_FH **, int); +int locker_start(u_long); +int locker_wait(void); +os_pid_t os_spawn(const char *, char *const[]); +int os_wait(os_pid_t *, u_int); +void *run_lthread(void *); +void *run_wthread(void *); +os_pid_t spawn_proc(u_long, char *, char *); +void tm_env_close(void); +int tm_env_init(void); +void tm_mutex_destroy(void); +void tm_mutex_init(void); +void tm_mutex_stats(void); +int usage(void); +int wakeup_start(u_long); +int wakeup_wait(void); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + enum {LOCKER, WAKEUP, PARENT} rtype; + extern int optind; + extern char *optarg; + os_pid_t wakeup_pid, *pids; + u_long id; + u_int i; + DB_FH *fhp, *map_fhp; + int ch, err; + char *p, *tmpath, cmd[1024]; + + if ((progname = __db_rpath(argv[0])) == NULL) + progname = argv[0]; + else + ++progname; + + rtype = PARENT; + id = 0; + tmpath = argv[0]; + while ((ch = getopt(argc, argv, "l:n:p:T:t:v")) != EOF) + switch (ch) { + case 'l': + maxlocks = (u_int)atoi(optarg); + break; + case 'n': + nlocks = (u_int)atoi(optarg); + break; + case 'p': + nprocs = (u_int)atoi(optarg); + break; + case 't': + if ((nthreads = (u_int)atoi(optarg)) == 0) + nthreads = 1; +#if !defined(MUTEX_THREAD_TEST) + if (nthreads != 1) { + fprintf(stderr, + "%s: thread support not available or not compiled for this platform.\n", + progname); + return (EXIT_FAILURE); + } +#endif + break; + case 'T': + if (!memcmp(optarg, "locker", sizeof("locker") - 1)) + rtype = LOCKER; + else if ( + !memcmp(optarg, "wakeup", sizeof("wakeup") - 1)) + rtype = WAKEUP; + else + return (usage()); + if ((p = strchr(optarg, '=')) == NULL) + return (usage()); + id = (u_long)atoi(p + 1); + break; + case 'v': + verbose = 1; + break; + case '?': + default: + return (usage()); + } + argc -= optind; + argv += optind; + + /* + * If we're not running a multi-process test, we should be running + * a multi-thread test. + */ + if (nprocs == 1 && nthreads == 1) { + fprintf(stderr, + "%s: running in a single process requires multiple threads\n", + progname); + return (EXIT_FAILURE); + } + + len = sizeof(TM) * (1 + nthreads * nprocs + maxlocks); + + /* + * In the multi-process test, the parent spawns processes that exec + * the original binary, ending up here. Each process joins the DB + * environment separately and then calls the supporting function. + */ + if (rtype == LOCKER || rtype == WAKEUP) { + __os_sleep(dbenv, 3, 0); /* Let everyone catch up. */ + /* Initialize random numbers. */ + srand((u_int)time(NULL) % (u_int)getpid()); + + if (tm_env_init() != 0) /* Join the environment. */ + exit(EXIT_FAILURE); + /* Join the backing data. */ + data_on(&gm_addr, &tm_addr, &lm_addr, &map_fhp, 0); + if (verbose) + printf( + "Backing file: global (%#lx), threads (%#lx), locks (%#lx)\n", + (u_long)gm_addr, (u_long)tm_addr, (u_long)lm_addr); + + if ((rtype == LOCKER ? + locker_start(id) : wakeup_start(id)) != 0) + exit(EXIT_FAILURE); + if ((rtype == LOCKER ? locker_wait() : wakeup_wait()) != 0) + exit(EXIT_FAILURE); + + data_off(gm_addr, map_fhp); /* Detach from backing data. */ + + tm_env_close(); /* Detach from environment. */ + + exit(EXIT_SUCCESS); + } + + /* + * The following code is only executed by the original parent process. + * + * Clean up from any previous runs. + */ + snprintf(cmd, sizeof(cmd), "rm -rf %s", TESTDIR); + (void)system(cmd); + snprintf(cmd, sizeof(cmd), "mkdir %s", TESTDIR); + (void)system(cmd); + + printf( + "%s: %u processes, %u threads/process, %u lock requests from %u locks\n", + progname, nprocs, nthreads, nlocks, maxlocks); + printf("%s: backing data %lu bytes\n", progname, (u_long)len); + + if (tm_env_init() != 0) /* Create the environment. */ + exit(EXIT_FAILURE); + /* Create the backing data. */ + data_on(&gm_addr, &tm_addr, &lm_addr, &map_fhp, 1); + if (verbose) + printf( + "backing data: global (%#lx), threads (%#lx), locks (%#lx)\n", + (u_long)gm_addr, (u_long)tm_addr, (u_long)lm_addr); + + tm_mutex_init(); /* Initialize mutexes. */ + + if (nprocs > 1) { /* Run the multi-process test. */ + /* Allocate array of locker process IDs. */ + if ((pids = calloc(nprocs, sizeof(os_pid_t))) == NULL) { + fprintf(stderr, "%s: %s\n", progname, strerror(errno)); + goto fail; + } + + /* Spawn locker processes and threads. */ + for (i = 0; i < nprocs; ++i) { + if ((pids[i] = + spawn_proc(id, tmpath, "locker")) == OS_BAD_PID) { + fprintf(stderr, + "%s: failed to spawn a locker\n", progname); + goto fail; + } + id += nthreads; + } + + /* Spawn wakeup process/thread. */ + if ((wakeup_pid = + spawn_proc(id, tmpath, "wakeup")) == OS_BAD_PID) { + fprintf(stderr, + "%s: failed to spawn waker\n", progname); + goto fail; + } + ++id; + + /* Wait for all lockers to exit. */ + if ((err = os_wait(pids, nprocs)) != 0) { + fprintf(stderr, "%s: locker wait failed with %d\n", + progname, err); + goto fail; + } + + /* Signal wakeup process to exit. */ + if ((err = __os_open( + dbenv, MT_FILE_QUIT, 0, DB_OSO_CREATE, 0664, &fhp)) != 0) { + fprintf(stderr, + "%s: open %s\n", progname, db_strerror(err)); + goto fail; + } + (void)__os_closehandle(dbenv, fhp); + + /* Wait for wakeup process/thread. */ + if ((err = os_wait(&wakeup_pid, 1)) != 0) { + fprintf(stderr, "%s: %lu: exited %d\n", + progname, (u_long)wakeup_pid, err); + goto fail; + } + } else { /* Run the single-process test. */ + /* Spawn locker threads. */ + if (locker_start(0) != 0) + goto fail; + + /* Spawn wakeup thread. */ + if (wakeup_start(nthreads) != 0) + goto fail; + + /* Wait for all lockers to exit. */ + if (locker_wait() != 0) + goto fail; + + /* Signal wakeup process to exit. */ + if ((err = __os_open( + dbenv, MT_FILE_QUIT, 0, DB_OSO_CREATE, 0664, &fhp)) != 0) { + fprintf(stderr, + "%s: open %s\n", progname, db_strerror(err)); + goto fail; + } + (void)__os_closehandle(dbenv, fhp); + + /* Wait for wakeup thread. */ + if (wakeup_wait() != 0) + goto fail; + } + + tm_mutex_stats(); /* Display run statistics. */ + tm_mutex_destroy(); /* Destroy mutexes. */ + + data_off(gm_addr, map_fhp); /* Detach from backing data. */ + + tm_env_close(); /* Detach from environment. */ + + printf("%s: test succeeded\n", progname); + return (EXIT_SUCCESS); + +fail: printf("%s: FAILED!\n", progname); + return (EXIT_FAILURE); +} + +int +locker_start(id) + u_long id; +{ +#if defined(MUTEX_THREAD_TEST) + u_int i; + int err; + + /* + * Spawn off threads. We have nthreads all locking and going to + * sleep, and one other thread cycling through and waking them up. + */ + if ((kidsp = + (os_thread_t *)calloc(sizeof(os_thread_t), nthreads)) == NULL) { + fprintf(stderr, "%s: %s\n", progname, strerror(errno)); + return (1); + } + for (i = 0; i < nthreads; i++) + if ((err = os_thread_create( + &kidsp[i], NULL, run_lthread, (void *)(id + i))) != 0) { + fprintf(stderr, "%s: failed spawning thread: %s\n", + progname, db_strerror(err)); + return (1); + } + return (0); +#else + return (run_lthread((void *)id) == NULL ? 0 : 1); +#endif +} + +int +locker_wait() +{ +#if defined(MUTEX_THREAD_TEST) + u_int i; + void *retp; + + /* Wait for the threads to exit. */ + for (i = 0; i < nthreads; i++) { + (void)os_thread_join(kidsp[i], &retp); + if (retp != NULL) { + fprintf(stderr, + "%s: thread exited with error\n", progname); + return (1); + } + } + free(kidsp); +#endif + return (0); +} + +void * +run_lthread(arg) + void *arg; +{ + TM *gp, *mp, *tp; + u_long id, tid; + u_int lock, nl; + int err, i; + + id = (uintptr_t)arg; +#if defined(MUTEX_THREAD_TEST) + tid = (u_long)os_thread_self(); +#else + tid = 0; +#endif + printf("Locker: ID %03lu (PID: %lu; TID: %lx)\n", + id, (u_long)getpid(), tid); + + gp = (TM *)gm_addr; + tp = (TM *)(tm_addr + id * sizeof(TM)); + + for (nl = nlocks; nl > 0;) { + /* Select and acquire a data lock. */ + lock = (u_int)rand() % maxlocks; + mp = (TM *)(lm_addr + lock * sizeof(TM)); + if (verbose) + printf("%03lu: lock %d (mtx: %lu)\n", + id, lock, (u_long)mp->mutex); + + if ((err = dbenv->mutex_lock(dbenv, mp->mutex)) != 0) { + fprintf(stderr, "%s: %03lu: never got lock %d: %s\n", + progname, id, lock, db_strerror(err)); + return ((void *)1); + } + if (mp->id != 0) { + fprintf(stderr, + "%s: RACE! (%03lu granted lock %d held by %03lu)\n", + progname, id, lock, mp->id); + return ((void *)1); + } + mp->id = id; + + /* + * Pretend to do some work, periodically checking to see if + * we still hold the mutex. + */ + for (i = 0; i < 3; ++i) { + __os_sleep(dbenv, 0, (u_long)rand() % 3); + if (mp->id != id) { + fprintf(stderr, + "%s: RACE! (%03lu stole lock %d from %03lu)\n", + progname, mp->id, lock, id); + return ((void *)1); + } + } + + /* + * Test self-blocking and unlocking by other threads/processes: + * + * acquire the global lock + * set our wakeup flag + * release the global lock + * acquire our per-thread lock + * + * The wakeup thread will wake us up. + */ + if ((err = dbenv->mutex_lock(dbenv, gp->mutex)) != 0) { + fprintf(stderr, "%s: %03lu: global lock: %s\n", + progname, id, db_strerror(err)); + return ((void *)1); + } + if (tp->id != 0 && tp->id != id) { + fprintf(stderr, + "%s: %03lu: per-thread mutex isn't mine, owned by %03lu\n", + progname, id, tp->id); + return ((void *)1); + } + tp->id = id; + if (verbose) + printf("%03lu: self-blocking (mtx: %lu)\n", + id, (u_long)tp->mutex); + if (tp->wakeme) { + fprintf(stderr, + "%s: %03lu: wakeup flag incorrectly set\n", + progname, id); + return ((void *)1); + } + tp->wakeme = 1; + if ((err = dbenv->mutex_unlock(dbenv, gp->mutex)) != 0) { + fprintf(stderr, + "%s: %03lu: global unlock: %s\n", + progname, id, db_strerror(err)); + return ((void *)1); + } + if ((err = dbenv->mutex_lock(dbenv, tp->mutex)) != 0) { + fprintf(stderr, "%s: %03lu: per-thread lock: %s\n", + progname, id, db_strerror(err)); + return ((void *)1); + } + /* Time passes... */ + if (tp->wakeme) { + fprintf(stderr, "%s: %03lu: wakeup flag not cleared\n", + progname, id); + return ((void *)1); + } + + if (verbose) + printf("%03lu: release %d (mtx: %lu)\n", + id, lock, (u_long)mp->mutex); + + /* Release the data lock. */ + mp->id = 0; + if ((err = dbenv->mutex_unlock(dbenv, mp->mutex)) != 0) { + fprintf(stderr, + "%s: %03lu: lock release: %s\n", + progname, id, db_strerror(err)); + return ((void *)1); + } + + if (--nl % 100 == 0) + printf("%03lu: %d\n", id, nl); + } + + return (NULL); +} + +int +wakeup_start(id) + u_long id; +{ +#if defined(MUTEX_THREAD_TEST) + int err; + + /* + * Spawn off wakeup thread. + */ + if ((err = os_thread_create( + &wakep, NULL, run_wthread, (void *)id)) != 0) { + fprintf(stderr, "%s: failed spawning wakeup thread: %s\n", + progname, db_strerror(err)); + return (1); + } + return (0); +#else + return (run_wthread((void *)id) == NULL ? 0 : 1); +#endif +} + +int +wakeup_wait() +{ +#if defined(MUTEX_THREAD_TEST) + void *retp; + + /* + * A file is created when the wakeup thread is no longer needed. + */ + (void)os_thread_join(wakep, &retp); + if (retp != NULL) { + fprintf(stderr, + "%s: wakeup thread exited with error\n", progname); + return (1); + } +#endif + return (0); +} + +/* + * run_wthread -- + * Thread to wake up other threads that are sleeping. + */ +void * +run_wthread(arg) + void *arg; +{ + TM *gp, *tp; + u_long id, tid; + u_int check_id; + int err; + + id = (uintptr_t)arg; +#if defined(MUTEX_THREAD_TEST) + tid = (u_long)os_thread_self(); +#else + tid = 0; +#endif + printf("Wakeup: ID %03lu (PID: %lu; TID: %lx)\n", + id, (u_long)getpid(), tid); + + gp = (TM *)gm_addr; + + /* Loop, waking up sleepers and periodically sleeping ourselves. */ + for (check_id = 0;; ++check_id) { + /* Check to see if the locking threads have finished. */ + if (__os_exists(dbenv, MT_FILE_QUIT, NULL) == 0) + break; + + /* Check for ID wraparound. */ + if (check_id == nthreads * nprocs) + check_id = 0; + + /* Check for a thread that needs a wakeup. */ + tp = (TM *)(tm_addr + check_id * sizeof(TM)); + if (!tp->wakeme) + continue; + + if (verbose) { + printf("%03lu: wakeup thread %03lu (mtx: %lu)\n", + id, tp->id, (u_long)tp->mutex); + (void)fflush(stdout); + } + + /* Acquire the global lock. */ + if ((err = dbenv->mutex_lock(dbenv, gp->mutex)) != 0) { + fprintf(stderr, "%s: wakeup: global lock: %s\n", + progname, db_strerror(err)); + return ((void *)1); + } + + tp->wakeme = 0; + if ((err = dbenv->mutex_unlock(dbenv, tp->mutex)) != 0) { + fprintf(stderr, "%s: wakeup: unlock: %s\n", + progname, db_strerror(err)); + return ((void *)1); + } + + if ((err = dbenv->mutex_unlock(dbenv, gp->mutex)) != 0) { + fprintf(stderr, "%s: wakeup: global unlock: %s\n", + progname, db_strerror(err)); + return ((void *)1); + } + + __os_sleep(dbenv, 0, (u_long)rand() % 3); + } + return (NULL); +} + +/* + * tm_env_init -- + * Create the backing database environment. + */ +int +tm_env_init() +{ + u_int32_t flags; + int ret; + char *home; + + /* + * Create an environment object and initialize it for error + * reporting. + */ + if ((ret = db_env_create(&dbenv, 0)) != 0) { + fprintf(stderr, "%s: %s\n", progname, db_strerror(ret)); + return (1); + } + dbenv->set_errfile(dbenv, stderr); + dbenv->set_errpfx(dbenv, progname); + + /* Allocate enough mutexes. */ + if ((ret = dbenv->mutex_set_increment(dbenv, + 1 + nthreads * nprocs + maxlocks)) != 0) { + dbenv->err(dbenv, ret, "dbenv->mutex_set_increment"); + return (1); + } + + flags = DB_CREATE; + if (nprocs == 1) { + home = NULL; + flags |= DB_PRIVATE; + } else + home = TESTDIR; + if (nthreads != 1) + flags |= DB_THREAD; + if ((ret = dbenv->open(dbenv, home, flags, 0)) != 0) { + dbenv->err(dbenv, ret, "environment open: %s", home); + return (1); + } + + return (0); +} + +/* + * tm_env_close -- + * Close the backing database environment. + */ +void +tm_env_close() +{ + (void)dbenv->close(dbenv, 0); +} + +/* + * tm_mutex_init -- + * Initialize the mutexes. + */ +void +tm_mutex_init() +{ + TM *mp; + u_int i; + int err; + + if (verbose) + printf("Allocate the global mutex: "); + mp = (TM *)gm_addr; + if ((err = dbenv->mutex_alloc(dbenv, 0, &mp->mutex)) != 0) { + fprintf(stderr, "%s: DB_ENV->mutex_alloc (global): %s\n", + progname, db_strerror(err)); + exit(EXIT_FAILURE); + } + if (verbose) + printf("%lu\n", (u_long)mp->mutex); + + if (verbose) + printf( + "Allocate %d per-thread, self-blocking mutexes: ", + nthreads * nprocs); + for (i = 0; i < nthreads * nprocs; ++i) { + mp = (TM *)(tm_addr + i * sizeof(TM)); + if ((err = dbenv->mutex_alloc( + dbenv, DB_MUTEX_SELF_BLOCK, &mp->mutex)) != 0) { + fprintf(stderr, + "%s: DB_ENV->mutex_alloc (per-thread %d): %s\n", + progname, i, db_strerror(err)); + exit(EXIT_FAILURE); + } + if ((err = dbenv->mutex_lock(dbenv, mp->mutex)) != 0) { + fprintf(stderr, + "%s: DB_ENV->mutex_lock (per-thread %d): %s\n", + progname, i, db_strerror(err)); + exit(EXIT_FAILURE); + } + if (verbose) + printf("%lu ", (u_long)mp->mutex); + } + if (verbose) + printf("\n"); + + if (verbose) + printf("Allocate %d per-lock mutexes: ", maxlocks); + for (i = 0; i < maxlocks; ++i) { + mp = (TM *)(lm_addr + i * sizeof(TM)); + if ((err = dbenv->mutex_alloc(dbenv, 0, &mp->mutex)) != 0) { + fprintf(stderr, + "%s: DB_ENV->mutex_alloc (per-lock: %d): %s\n", + progname, i, db_strerror(err)); + exit(EXIT_FAILURE); + } + if (verbose) + printf("%lu ", (u_long)mp->mutex); + } + if (verbose) + printf("\n"); +} + +/* + * tm_mutex_destroy -- + * Destroy the mutexes. + */ +void +tm_mutex_destroy() +{ + TM *gp, *mp; + u_int i; + int err; + + if (verbose) + printf("Destroy the global mutex.\n"); + gp = (TM *)gm_addr; + if ((err = dbenv->mutex_free(dbenv, gp->mutex)) != 0) { + fprintf(stderr, "%s: DB_ENV->mutex_free (global): %s\n", + progname, db_strerror(err)); + exit(EXIT_FAILURE); + } + + if (verbose) + printf("Destroy the per-thread mutexes.\n"); + for (i = 0; i < nthreads * nprocs; ++i) { + mp = (TM *)(tm_addr + i * sizeof(TM)); + if ((err = dbenv->mutex_free(dbenv, mp->mutex)) != 0) { + fprintf(stderr, + "%s: DB_ENV->mutex_free (per-thread %d): %s\n", + progname, i, db_strerror(err)); + exit(EXIT_FAILURE); + } + } + + if (verbose) + printf("Destroy the per-lock mutexes.\n"); + for (i = 0; i < maxlocks; ++i) { + mp = (TM *)(lm_addr + i * sizeof(TM)); + if ((err = dbenv->mutex_free(dbenv, mp->mutex)) != 0) { + fprintf(stderr, + "%s: DB_ENV->mutex_free (per-lock: %d): %s\n", + progname, i, db_strerror(err)); + exit(EXIT_FAILURE); + } + } +} + +/* + * tm_mutex_stats -- + * Display mutex statistics. + */ +void +tm_mutex_stats() +{ +#ifdef HAVE_STATISTICS + TM *mp; + u_int32_t set_wait, set_nowait; + u_int i; + + printf("Per-lock mutex statistics.\n"); + for (i = 0; i < maxlocks; ++i) { + mp = (TM *)(lm_addr + i * sizeof(TM)); + __mutex_set_wait_info(dbenv, mp->mutex, &set_wait, &set_nowait); + printf("mutex %2d: wait: %lu; no wait %lu\n", i, + (u_long)set_wait, (u_long)set_nowait); + } +#endif +} + +/* + * data_on -- + * Map in or allocate the backing data space. + */ +void +data_on(gm_addrp, tm_addrp, lm_addrp, fhpp, init) + u_int8_t **gm_addrp, **tm_addrp, **lm_addrp; + DB_FH **fhpp; + int init; +{ + DB_FH *fhp; + size_t nwrite; + int err; + void *addr; + + fhp = NULL; + + /* + * In a single process, use heap memory. + */ + if (nprocs == 1) { + if (init) { + if ((err = + __os_calloc(dbenv, (size_t)len, 1, &addr)) != 0) + exit(EXIT_FAILURE); + } else { + fprintf(stderr, + "%s: init should be set for single process call\n", + progname); + exit(EXIT_FAILURE); + } + } else { + if (init) { + if (verbose) + printf("Create the backing file.\n"); + + if ((err = __os_open(dbenv, MT_FILE, 0, + DB_OSO_CREATE | DB_OSO_TRUNC, 0666, &fhp)) == -1) { + fprintf(stderr, "%s: %s: open: %s\n", + progname, MT_FILE, db_strerror(err)); + exit(EXIT_FAILURE); + } + + if ((err = __os_seek(dbenv, fhp, 0, 0, len)) != 0 || + (err = + __os_write(dbenv, fhp, &err, 1, &nwrite)) != 0 || + nwrite != 1) { + fprintf(stderr, "%s: %s: seek/write: %s\n", + progname, MT_FILE, db_strerror(err)); + exit(EXIT_FAILURE); + } + } else + if ((err = + __os_open(dbenv, MT_FILE, 0, 0, 0, &fhp)) != 0) + exit(EXIT_FAILURE); + + if ((err = + __os_mapfile(dbenv, MT_FILE, fhp, len, 0, &addr)) != 0) + exit(EXIT_FAILURE); + } + + *gm_addrp = (u_int8_t *)addr; + addr = (u_int8_t *)addr + sizeof(TM); + *tm_addrp = (u_int8_t *)addr; + addr = (u_int8_t *)addr + sizeof(TM) * (nthreads * nprocs); + *lm_addrp = (u_int8_t *)addr; + + if (fhpp != NULL) + *fhpp = fhp; +} + +/* + * data_off -- + * Discard or de-allocate the backing data space. + */ +void +data_off(addr, fhp) + u_int8_t *addr; + DB_FH *fhp; +{ + if (nprocs == 1) + __os_free(dbenv, addr); + else { + if (__os_unmapfile(dbenv, addr, len) != 0) + exit(EXIT_FAILURE); + if (__os_closehandle(dbenv, fhp) != 0) + exit(EXIT_FAILURE); + } +} + +/* + * usage -- + * + */ +int +usage() +{ + fprintf(stderr, "usage: %s %s\n\t%s\n", progname, + "[-v] [-l maxlocks]", + "[-n locks] [-p procs] [-T locker=ID|wakeup=ID] [-t threads]"); + return (EXIT_FAILURE); +} + +/* + * os_wait -- + * Wait for an array of N procs. + */ +int +os_wait(procs, n) + os_pid_t *procs; + u_int n; +{ + u_int i; + int status; +#if defined(DB_WIN32) + DWORD ret; +#endif + + status = 0; + +#if defined(DB_WIN32) + do { + ret = WaitForMultipleObjects(n, procs, FALSE, INFINITE); + i = ret - WAIT_OBJECT_0; + if (i < 0 || i >= n) + return (__os_posix_err(__os_get_syserr())); + + if ((GetExitCodeProcess(procs[i], &ret) == 0) || (ret != 0)) + return (ret); + + /* remove the process handle from the list */ + while (++i < n) + procs[i - 1] = procs[i]; + } while (--n); +#elif !defined(HAVE_VXWORKS) + do { + if (wait(&status) == -1) + return (__os_posix_err(__os_get_syserr())); + + if (WIFEXITED(status) == 0 || WEXITSTATUS(status) != 0) { + for (i = 0; i < n; i++) + (void)kill(procs[i], SIGKILL); + return (WEXITSTATUS(status)); + } + } while (--n); +#endif + + return (0); +} + +os_pid_t +spawn_proc(id, tmpath, typearg) + u_long id; + char *tmpath, *typearg; +{ + char lbuf[16], nbuf[16], pbuf[16], tbuf[16], Tbuf[256]; + char *const vbuf = verbose ? "-v" : NULL; + char *args[] = { NULL /* tmpath */, + "-l", NULL /* lbuf */, "-n", NULL /* nbuf */, + "-p", NULL /* pbuf */, "-t", NULL /* tbuf */, + "-T", NULL /* Tbuf */, NULL /* vbuf */, + NULL + }; + + args[0] = tmpath; + snprintf(lbuf, sizeof(lbuf), "%d", maxlocks); + args[2] = lbuf; + snprintf(nbuf, sizeof(nbuf), "%d", nlocks); + args[4] = nbuf; + snprintf(pbuf, sizeof(pbuf), "%d", nprocs); + args[6] = pbuf; + snprintf(tbuf, sizeof(tbuf), "%d", nthreads); + args[8] = tbuf; + snprintf(Tbuf, sizeof(Tbuf), "%s=%lu", typearg, id); + args[10] = Tbuf; + args[11] = vbuf; + + return (os_spawn(tmpath, args)); +} + +os_pid_t +os_spawn(path, argv) + const char *path; + char *const argv[]; +{ + os_pid_t pid; + int status; + + COMPQUIET(pid, 0); + COMPQUIET(status, 0); + +#ifdef HAVE_VXWORKS + fprintf(stderr, "%s: os_spawn not supported for VxWorks.\n", progname); + return (OS_BAD_PID); +#elif defined(HAVE_QNX) + /* + * For QNX, we cannot fork if we've ever used threads. So + * we'll use their spawn function. We use 'spawnl' which + * is NOT a POSIX function. + * + * The return value of spawnl is just what we want depending + * on the value of the 'wait' arg. + */ + return (spawnv(P_NOWAIT, path, argv)); +#elif defined(DB_WIN32) + return (os_pid_t)(_spawnv(P_NOWAIT, path, argv)); +#else + if ((pid = fork()) != 0) { + if (pid == -1) + return (OS_BAD_PID); + return (pid); + } else { + (void)execv(path, argv); + exit(EXIT_FAILURE); + } +#endif +} diff --git a/db/mutex/tm.c b/db/mutex/tm.c deleted file mode 100644 index de647fa1f..000000000 --- a/db/mutex/tm.c +++ /dev/null @@ -1,1010 +0,0 @@ -/* - * Standalone mutex tester for Berkeley DB mutexes. - * - * $Id: tm.c,v 12.14 2006/07/17 15:16:46 bostic Exp $ - */ - -#include "db_config.h" - -#include "db_int.h" - -#include - -#if defined(MUTEX_THREAD_TEST) -#include -#endif - -#ifdef DB_WIN32 -extern int getopt(int, char * const *, const char *); - -typedef HANDLE os_pid_t; -typedef HANDLE os_thread_t; - -#define os_thread_create(thrp, attr, func, arg) \ - (((*(thrp) = CreateThread(NULL, 0, \ - (LPTHREAD_START_ROUTINE)(func), (arg), 0, NULL)) == NULL) ? -1 : 0) -#define os_thread_join(thr, statusp) \ - ((WaitForSingleObject((thr), INFINITE) == WAIT_OBJECT_0) && \ - GetExitCodeThread((thr), (LPDWORD)(statusp)) ? 0 : -1) -#define os_thread_self() GetCurrentThreadId() - -#else /* !DB_WIN32 */ - -typedef pid_t os_pid_t; - -#ifdef MUTEX_THREAD_TEST -typedef pthread_t os_thread_t; -#endif - -#define os_thread_create(thrp, attr, func, arg) \ - pthread_create((thrp), (attr), (func), (arg)) -#define os_thread_join(thr, statusp) pthread_join((thr), (statusp)) -#define os_thread_self() pthread_self() -#endif - -#define OS_BAD_PID (os_pid_t)-1 - -#define TESTDIR "TESTDIR" /* Working area */ -#define MT_FILE "TESTDIR/mutex.file" -#define MT_FILE_QUIT "TESTDIR/mutex.file.quit" - -/* - * The backing file layout: - * TM[1] per-thread mutex array lock - * TM[nthreads] per-thread mutex array - * TM[maxlocks] per-lock mutex array - */ -typedef struct { - db_mutex_t mutex; /* Mutex. */ - u_long id; /* Holder's ID. */ - u_int wakeme; /* Request to awake. */ -} TM; - -DB_ENV *dbenv; /* Backing environment */ -size_t len; /* Backing file size. */ - -u_int8_t *gm_addr; /* Global mutex */ -u_int8_t *lm_addr; /* Locker mutexes */ -u_int8_t *tm_addr; /* Thread mutexes */ - -#ifdef MUTEX_THREAD_TEST -os_thread_t *kidsp; /* Locker threads */ -os_thread_t wakep; /* Wakeup thread */ -#endif - -int maxlocks = 20; /* -l: Backing locks. */ -int nlocks = 10000; /* -n: Locks per processes. */ -int nprocs = 20; /* -p: Processes. */ -int nthreads = 1; /* -t: Threads. */ -int verbose; /* -v: Verbosity. */ - -int locker_start(u_long); -int locker_wait(void); -void map_file(u_int8_t **, u_int8_t **, u_int8_t **, DB_FH **); -os_pid_t os_spawn(const char *, char *const[]); -int os_wait(os_pid_t *, int); -void *run_lthread(void *); -void *run_wthread(void *); -os_pid_t spawn_proc(u_long, char *, char *); -void tm_env_close(void); -int tm_env_init(void); -void tm_file_init(void); -void tm_mutex_destroy(void); -void tm_mutex_init(void); -void tm_mutex_stats(void); -void unmap_file(u_int8_t *, DB_FH *); -int usage(void); -int wakeup_start(u_long); -int wakeup_wait(void); - -int -main(argc, argv) - int argc; - char *argv[]; -{ - enum {LOCKER, WAKEUP, PARENT} rtype; - extern int optind; - extern char *optarg; - os_pid_t wakeup_pid, *pids; - u_long id; - DB_FH *fhp, *map_fhp; - int ch, err, i; - char *p, *tmpath, cmd[1024]; - - rtype = PARENT; - id = 0; - tmpath = argv[0]; - while ((ch = getopt(argc, argv, "l:n:p:T:t:v")) != EOF) - switch (ch) { - case 'l': - maxlocks = atoi(optarg); - break; - case 'n': - nlocks = atoi(optarg); - break; - case 'p': - nprocs = atoi(optarg); - break; - case 't': - if ((nthreads = atoi(optarg)) == 0) - nthreads = 1; -#if !defined(MUTEX_THREAD_TEST) - if (nthreads != 1) { - (void)fprintf(stderr, - "tm: thread support not available or not compiled for this platform.\n"); - return (EXIT_FAILURE); - } -#endif - break; - case 'T': - if (!memcmp(optarg, "locker", sizeof("locker") - 1)) - rtype = LOCKER; - else if ( - !memcmp(optarg, "wakeup", sizeof("wakeup") - 1)) - rtype = WAKEUP; - else - return (usage()); - if ((p = strchr(optarg, '=')) == NULL) - return (usage()); - id = atoi(p + 1); - break; - case 'v': - verbose = 1; - break; - case '?': - default: - return (usage()); - } - argc -= optind; - argv += optind; - - /* - * If we're not running a multi-process test, we should be running - * a multi-thread test. - */ - if (nprocs == 1 && nthreads == 1) { - fprintf(stderr, - "tm: running in a single process requires multiple threads\n"); - return (EXIT_FAILURE); - } - - len = sizeof(TM) * (1 + nthreads * nprocs + maxlocks); - - /* - * In the multi-process test, the parent spawns processes that exec - * the original binary, ending up here. Each process joins the DB - * environment separately and then calls the supporting function. - */ - if (rtype == LOCKER || rtype == WAKEUP) { - __os_sleep(dbenv, 3, 0); /* Let everyone catch up. */ - /* Initialize random numbers. */ - srand((u_int)time(NULL) % getpid()); - - if (tm_env_init() != 0) /* Join the environment. */ - exit(EXIT_FAILURE); - /* Join the backing file. */ - map_file(&gm_addr, &tm_addr, &lm_addr, &map_fhp); - if (verbose) - printf( - "Backing file: global (%#lx), threads (%#lx), locks (%#lx)\n", - (u_long)gm_addr, (u_long)tm_addr, (u_long)lm_addr); - - if ((rtype == LOCKER ? - locker_start(id) : wakeup_start(id)) != 0) - exit(EXIT_FAILURE); - if ((rtype == LOCKER ? locker_wait() : wakeup_wait()) != 0) - exit(EXIT_FAILURE); - - unmap_file(gm_addr, map_fhp); /* Detach from backing file. */ - - tm_env_close(); /* Detach from environment. */ - - exit(EXIT_SUCCESS); - } - - /* - * The following code is only executed by the original parent process. - * - * Clean up from any previous runs. - */ - snprintf(cmd, sizeof(cmd), "rm -rf %s", TESTDIR); - (void)system(cmd); - snprintf(cmd, sizeof(cmd), "mkdir %s", TESTDIR); - (void)system(cmd); - - printf( - "tm: %d processes, %d threads/process, %d lock requests from %d locks\n", - nprocs, nthreads, nlocks, maxlocks); - printf("tm: backing file %lu bytes\n", (u_long)len); - - if (tm_env_init() != 0) /* Create the environment. */ - exit(EXIT_FAILURE); - - tm_file_init(); /* Initialize backing file. */ - - /* Map in the backing file. */ - map_file(&gm_addr, &tm_addr, &lm_addr, &map_fhp); - if (verbose) - printf( - "backing file: global (%#lx), threads (%#lx), locks (%#lx)\n", - (u_long)gm_addr, (u_long)tm_addr, (u_long)lm_addr); - - tm_mutex_init(); /* Initialize mutexes. */ - - if (nprocs > 1) { /* Run the multi-process test. */ - /* Allocate array of locker process IDs. */ - if ((pids = calloc(nprocs, sizeof(os_pid_t))) == NULL) { - fprintf(stderr, "tm: %s\n", strerror(errno)); - goto fail; - } - - /* Spawn locker processes and threads. */ - for (i = 0; i < nprocs; ++i) { - if ((pids[i] = - spawn_proc(id, tmpath, "locker")) == OS_BAD_PID) { - fprintf(stderr, - "tm: failed to spawn a locker\n"); - goto fail; - } - id += nthreads; - } - - /* Spawn wakeup process/thread. */ - if ((wakeup_pid = - spawn_proc(id, tmpath, "wakeup")) == OS_BAD_PID) { - fprintf(stderr, "tm: failed to spawn waker\n"); - goto fail; - } - ++id; - - /* Wait for all lockers to exit. */ - if ((err = os_wait(pids, nprocs)) != 0) { - fprintf(stderr, "locker wait failed with %d\n", err); - goto fail; - } - - /* Signal wakeup process to exit. */ - if ((err = __os_open( - dbenv, MT_FILE_QUIT, DB_OSO_CREATE, 0664, &fhp)) != 0) { - fprintf(stderr, "tm: open %s\n", db_strerror(err)); - goto fail; - } - (void)__os_closehandle(dbenv, fhp); - - /* Wait for wakeup process/thread. */ - if ((err = os_wait(&wakeup_pid, 1)) != 0) { - fprintf(stderr, - "%lu: exited %d\n", (u_long)wakeup_pid, err); - goto fail; - } - } else { /* Run the single-process test. */ - /* Spawn locker threads. */ - if (locker_start(0) != 0) - goto fail; - - /* Spawn wakeup thread. */ - if (wakeup_start(nthreads) != 0) - goto fail; - - /* Wait for all lockers to exit. */ - if (locker_wait() != 0) - goto fail; - - /* Signal wakeup process to exit. */ - if ((err = __os_open( - dbenv, MT_FILE_QUIT, DB_OSO_CREATE, 0664, &fhp)) != 0) { - fprintf(stderr, "tm: open %s\n", db_strerror(err)); - goto fail; - } - (void)__os_closehandle(dbenv, fhp); - - /* Wait for wakeup thread. */ - if (wakeup_wait() != 0) - goto fail; - } - - tm_mutex_stats(); /* Display run statistics. */ - tm_mutex_destroy(); /* Destroy mutexes. */ - - unmap_file(gm_addr, map_fhp); /* Detach from backing file. */ - - tm_env_close(); /* Detach from environment. */ - - printf("tm: test succeeded\n"); - return (EXIT_SUCCESS); - -fail: printf("tm: FAILED!\n"); - return (EXIT_FAILURE); -} - -int -locker_start(id) - u_long id; -{ -#if defined(MUTEX_THREAD_TEST) - int err, i; - - /* - * Spawn off threads. We have nthreads all locking and going to - * sleep, and one other thread cycling through and waking them up. - */ - if ((kidsp = - (os_thread_t *)calloc(sizeof(os_thread_t), nthreads)) == NULL) { - fprintf(stderr, "tm: %s\n", strerror(errno)); - return (1); - } - for (i = 0; i < nthreads; i++) - if ((err = os_thread_create( - &kidsp[i], NULL, run_lthread, (void *)(id + i))) != 0) { - fprintf(stderr, "tm: failed spawning thread: %s\n", - db_strerror(err)); - return (1); - } - return (0); -#else - return (run_lthread((void *)id) == NULL ? 0 : 1); -#endif -} - -int -locker_wait() -{ -#if defined(MUTEX_THREAD_TEST) - int i; - void *retp; - - /* Wait for the threads to exit. */ - for (i = 0; i < nthreads; i++) { - os_thread_join(kidsp[i], &retp); - if (retp != NULL) { - fprintf(stderr, "tm: thread exited with error\n"); - return (1); - } - } - free(kidsp); -#endif - return (0); -} - -void * -run_lthread(arg) - void *arg; -{ - TM *gp, *mp, *tp; - u_long id, tid; - int err, i, lock, nl; - - id = (uintptr_t)arg; -#if defined(MUTEX_THREAD_TEST) - tid = (u_long)os_thread_self(); -#else - tid = 0; -#endif - printf("Locker: ID %03lu (PID: %lu; TID: %lx)\n", - id, (u_long)getpid(), tid); - - gp = (TM *)gm_addr; - tp = (TM *)(tm_addr + id * sizeof(TM)); - - for (nl = nlocks; nl > 0;) { - /* Select and acquire a data lock. */ - lock = rand() % maxlocks; - mp = (TM *)(lm_addr + lock * sizeof(TM)); - if (verbose) - printf("%03lu: lock %d (mtx: %lu)\n", - id, lock, (u_long)mp->mutex); - - if ((err = dbenv->mutex_lock(dbenv, mp->mutex)) != 0) { - fprintf(stderr, "%03lu: never got lock %d: %s\n", - id, lock, db_strerror(err)); - return ((void *)1); - } - if (mp->id != 0) { - fprintf(stderr, - "RACE! (%03lu granted lock %d held by %03lu)\n", - id, lock, mp->id); - return ((void *)1); - } - mp->id = id; - - /* - * Pretend to do some work, periodically checking to see if - * we still hold the mutex. - */ - for (i = 0; i < 3; ++i) { - __os_sleep(dbenv, 0, rand() % 3); - if (mp->id != id) { - fprintf(stderr, - "RACE! (%03lu stole lock %d from %03lu)\n", - mp->id, lock, id); - return ((void *)1); - } - } - - /* - * Test self-blocking and unlocking by other threads/processes: - * - * acquire the global lock - * set our wakeup flag - * release the global lock - * acquire our per-thread lock - * - * The wakeup thread will wake us up. - */ - if ((err = dbenv->mutex_lock(dbenv, gp->mutex)) != 0) { - fprintf(stderr, - "%03lu: global lock: %s\n", id, db_strerror(err)); - return ((void *)1); - } - if (tp->id != 0 && tp->id != id) { - fprintf(stderr, - "%03lu: per-thread mutex isn't mine, owned by %03lu\n", - id, tp->id); - return ((void *)1); - } - tp->id = id; - if (verbose) - printf("%03lu: self-blocking (mtx: %lu)\n", - id, (u_long)tp->mutex); - if (tp->wakeme) { - fprintf(stderr, - "%03lu: wakeup flag incorrectly set\n", id); - return ((void *)1); - } - tp->wakeme = 1; - if ((err = dbenv->mutex_unlock(dbenv, gp->mutex)) != 0) { - fprintf(stderr, - "%03lu: global unlock: %s\n", id, db_strerror(err)); - return ((void *)1); - } - if ((err = dbenv->mutex_lock(dbenv, tp->mutex)) != 0) { - fprintf(stderr, "%03lu: per-thread lock: %s\n", - id, db_strerror(err)); - return ((void *)1); - } - /* Time passes... */ - if (tp->wakeme) { - fprintf(stderr, "%03lu: wakeup flag not cleared\n", id); - return ((void *)1); - } - - if (verbose) - printf("%03lu: release %d (mtx: %lu)\n", - id, lock, (u_long)mp->mutex); - - /* Release the data lock. */ - mp->id = 0; - if ((err = dbenv->mutex_unlock(dbenv, mp->mutex)) != 0) { - fprintf(stderr, - "%03lu: lock release: %s\n", id, db_strerror(err)); - return ((void *)1); - } - - if (--nl % 100 == 0) { - fprintf(stderr, "%03lu: %d\n", id, nl); - /* - * Windows buffers stderr and the output looks wrong - * without this. - */ - fflush(stderr); - } - } - - return (NULL); -} - -int -wakeup_start(id) - u_long id; -{ -#if defined(MUTEX_THREAD_TEST) - int err; - - /* - * Spawn off wakeup thread. - */ - if ((err = os_thread_create( - &wakep, NULL, run_wthread, (void *)id)) != 0) { - fprintf(stderr, "tm: failed spawning wakeup thread: %s\n", - db_strerror(err)); - return (1); - } - return (0); -#else - return (run_wthread((void *)id) == NULL ? 0 : 1); -#endif -} - -int -wakeup_wait() -{ -#if defined(MUTEX_THREAD_TEST) - void *retp; - - /* - * A file is created when the wakeup thread is no longer needed. - */ - os_thread_join(wakep, &retp); - if (retp != NULL) { - fprintf(stderr, "tm: wakeup thread exited with error\n"); - return (1); - } -#endif - return (0); -} - -/* - * run_wthread -- - * Thread to wake up other threads that are sleeping. - */ -void * -run_wthread(arg) - void *arg; -{ - TM *gp, *tp; - u_long id, tid; - int check_id, err; - - id = (uintptr_t)arg; -#if defined(MUTEX_THREAD_TEST) - tid = (u_long)os_thread_self(); -#else - tid = 0; -#endif - printf("Wakeup: ID %03lu (PID: %lu; TID: %lx)\n", - id, (u_long)getpid(), tid); - - gp = (TM *)gm_addr; - - /* Loop, waking up sleepers and periodically sleeping ourselves. */ - for (check_id = 0;; ++check_id) { - /* Check to see if the locking threads have finished. */ - if (__os_exists(dbenv, MT_FILE_QUIT, NULL) == 0) - break; - - /* Check for ID wraparound. */ - if (check_id == nthreads * nprocs) - check_id = 0; - - /* Check for a thread that needs a wakeup. */ - tp = (TM *)(tm_addr + check_id * sizeof(TM)); - if (!tp->wakeme) - continue; - - if (verbose) { - printf("%03lu: wakeup thread %03lu (mtx: %lu)\n", - id, tp->id, (u_long)tp->mutex); - fflush(stdout); - } - - /* Acquire the global lock. */ - if ((err = dbenv->mutex_lock(dbenv, gp->mutex)) != 0) { - fprintf(stderr, - "wakeup: global lock: %s\n", db_strerror(err)); - return ((void *)1); - } - - tp->wakeme = 0; - if ((err = dbenv->mutex_unlock(dbenv, tp->mutex)) != 0) { - fprintf(stderr, - "wakeup: unlock: %s\n", db_strerror(err)); - return ((void *)1); - } - - if ((err = dbenv->mutex_unlock(dbenv, gp->mutex))) { - fprintf(stderr, - "wakeup: global unlock: %s\n", db_strerror(err)); - return ((void *)1); - } - - __os_sleep(dbenv, 0, rand() % 3); - } - return (NULL); -} - -/* - * tm_env_init -- - * Create the backing database environment. - */ -int -tm_env_init() -{ - u_int32_t flags; - int ret; - char *home; - - /* - * Create an environment object and initialize it for error - * reporting. - */ - if ((ret = db_env_create(&dbenv, 0)) != 0) { - fprintf(stderr, "tm: %s\n", db_strerror(ret)); - return (1); - } - dbenv->set_errfile(dbenv, stderr); - dbenv->set_errpfx(dbenv, "tm"); - - /* Allocate enough mutexes. */ - if ((ret = dbenv->mutex_set_increment(dbenv, - 1 + nthreads * nprocs + maxlocks)) != 0) { - dbenv->err(dbenv, ret, "dbenv->mutex_set_increment"); - return (1); - } - - flags = DB_CREATE; - if (nprocs == 1) { - home = NULL; - flags |= DB_PRIVATE; - } else - home = TESTDIR; - if (nthreads != 1) - flags |= DB_THREAD; - if ((ret = dbenv->open(dbenv, home, flags, 0)) != 0) { - dbenv->err(dbenv, ret, "environment open: %s", home); - return (1); - } - - return (0); -} - -/* - * tm_env_close -- - * Close the backing database environment. - */ -void -tm_env_close() -{ - (void)dbenv->close(dbenv, 0); -} - -/* - * tm_file_init -- - * Initialize the backing file. - */ -void -tm_file_init() -{ - DB_FH *fhp; - int err; - size_t nwrite; - - /* Initialize the backing file. */ - if (verbose) - printf("Create the backing file.\n"); - - (void)unlink(MT_FILE); - - if ((err = __os_open(dbenv, MT_FILE, - DB_OSO_CREATE | DB_OSO_TRUNC, 0666, &fhp)) == -1) { - (void)fprintf(stderr, - "%s: open: %s\n", MT_FILE, db_strerror(err)); - exit(EXIT_FAILURE); - } - - if ((err = __os_seek(dbenv, fhp, 0, 0, len)) != 0 || - (err = __os_write(dbenv, fhp, &err, 1, &nwrite)) != 0 || - nwrite != 1) { - (void)fprintf(stderr, - "%s: seek/write: %s\n", MT_FILE, db_strerror(err)); - exit(EXIT_FAILURE); - } - (void)__os_closehandle(dbenv, fhp); -} - -/* - * tm_mutex_init -- - * Initialize the mutexes. - */ -void -tm_mutex_init() -{ - TM *mp; - int err, i; - - if (verbose) - printf("Allocate the global mutex: "); - mp = (TM *)gm_addr; - if ((err = dbenv->mutex_alloc(dbenv, 0, &mp->mutex)) != 0) { - fprintf(stderr, - "DB_ENV->mutex_alloc (global): %s\n", db_strerror(err)); - exit(EXIT_FAILURE); - } - if (verbose) - printf("%lu\n", (u_long)mp->mutex); - - if (verbose) - printf( - "Allocate %d per-thread, self-blocking mutexes: ", - nthreads * nprocs); - for (i = 0; i < nthreads * nprocs; ++i) { - mp = (TM *)(tm_addr + i * sizeof(TM)); - if ((err = dbenv->mutex_alloc( - dbenv, DB_MUTEX_SELF_BLOCK, &mp->mutex)) != 0) { - fprintf(stderr, - "DB_ENV->mutex_alloc (per-thread %d): %s\n", - i, db_strerror(err)); - exit(EXIT_FAILURE); - } - if ((err = dbenv->mutex_lock(dbenv, mp->mutex)) != 0) { - fprintf(stderr, - "DB_ENV->mutex_lock (per-thread %d): %s\n", - i, db_strerror(err)); - exit(EXIT_FAILURE); - } - if (verbose) - printf("%lu ", (u_long)mp->mutex); - } - if (verbose) - printf("\n"); - - if (verbose) - printf("Allocate %d per-lock mutexes: ", maxlocks); - for (i = 0; i < maxlocks; ++i) { - mp = (TM *)(lm_addr + i * sizeof(TM)); - if ((err = dbenv->mutex_alloc(dbenv, 0, &mp->mutex)) != 0) { - fprintf(stderr, - "DB_ENV->mutex_alloc (per-lock: %d): %s\n", - i, db_strerror(err)); - exit(EXIT_FAILURE); - } - if (verbose) - printf("%lu ", (u_long)mp->mutex); - } - if (verbose) - printf("\n"); -} - -/* - * tm_mutex_destroy -- - * Destroy the mutexes. - */ -void -tm_mutex_destroy() -{ - TM *gp, *mp; - int err, i; - - if (verbose) - printf("Destroy the global mutex.\n"); - gp = (TM *)gm_addr; - if ((err = dbenv->mutex_free(dbenv, gp->mutex)) != 0) { - fprintf(stderr, - "DB_ENV->mutex_free (global): %s\n", db_strerror(err)); - exit(EXIT_FAILURE); - } - - if (verbose) - printf("Destroy the per-thread mutexes.\n"); - for (i = 0; i < nthreads * nprocs; ++i) { - mp = (TM *)(tm_addr + i * sizeof(TM)); - if ((err = dbenv->mutex_free(dbenv, mp->mutex)) != 0) { - fprintf(stderr, - "DB_ENV->mutex_free (per-thread %d): %s\n", - i, db_strerror(err)); - exit(EXIT_FAILURE); - } - } - - if (verbose) - printf("Destroy the per-lock mutexes.\n"); - for (i = 0; i < maxlocks; ++i) { - mp = (TM *)(lm_addr + i * sizeof(TM)); - if ((err = dbenv->mutex_free(dbenv, mp->mutex)) != 0) { - fprintf(stderr, - "DB_ENV->mutex_free (per-lock: %d): %s\n", - i, db_strerror(err)); - exit(EXIT_FAILURE); - } - } - - (void)unlink(MT_FILE); -} - -/* - * tm_mutex_stats -- - * Display mutex statistics. - */ -void -tm_mutex_stats() -{ -#ifdef HAVE_STATISTICS - TM *mp; - int i; - u_int32_t set_wait, set_nowait; - - printf("Per-lock mutex statistics.\n"); - for (i = 0; i < maxlocks; ++i) { - mp = (TM *)(lm_addr + i * sizeof(TM)); - __mutex_set_wait_info(dbenv, mp->mutex, &set_wait, &set_nowait); - printf("mutex %2d: wait: %lu; no wait %lu\n", i, - (u_long)set_wait, (u_long)set_nowait); - } -#endif -} - -/* - * map_file -- - * Map in the backing file. - */ -void -map_file(gm_addrp, tm_addrp, lm_addrp, fhpp) - u_int8_t **gm_addrp, **tm_addrp, **lm_addrp; - DB_FH **fhpp; -{ - void *addr; - DB_FH *fhp; - int err; - -#ifndef MAP_FAILED -#define MAP_FAILED (void *)-1 -#endif -#ifndef MAP_FILE -#define MAP_FILE 0 -#endif - if ((err = __os_open(dbenv, MT_FILE, 0, 0, &fhp)) != 0) { - fprintf(stderr, "%s: open %s\n", MT_FILE, db_strerror(err)); - exit(EXIT_FAILURE); - } - - if ((err = __os_mapfile(dbenv, MT_FILE, fhp, len, 0, &addr)) != 0) { - fprintf(stderr, "%s: mmap: %s\n", MT_FILE, db_strerror(err)); - exit(EXIT_FAILURE); - } - - *gm_addrp = (u_int8_t *)addr; - addr = (u_int8_t *)addr + sizeof(TM); - *tm_addrp = (u_int8_t *)addr; - addr = (u_int8_t *)addr + sizeof(TM) * (nthreads * nprocs); - *lm_addrp = (u_int8_t *)addr; - - if (fhpp != NULL) - *fhpp = fhp; -} - -/* - * unmap_file -- - * Discard backing file map. - */ -void -unmap_file(addr, fhp) - u_int8_t *addr; - DB_FH *fhp; -{ - int err; - - if ((err = __os_unmapfile(dbenv, addr, len)) != 0) { - fprintf(stderr, "munmap: %s\n", db_strerror(err)); - exit(EXIT_FAILURE); - } - if ((err = __os_closehandle(dbenv, fhp)) != 0) { - fprintf(stderr, "close: %s\n", db_strerror(err)); - exit(EXIT_FAILURE); - } -} - -/* - * usage -- - * - */ -int -usage() -{ - (void)fprintf(stderr, "%s\n\t%s\n", - "usage: tm [-v] [-l maxlocks]", - "[-n locks] [-p procs] [-T locker=ID|wakeup=ID] [-t threads]"); - return (EXIT_FAILURE); -} - -/* - * os_wait -- - * Wait for an array of N procs. - */ -int -os_wait(procs, nprocs) - os_pid_t *procs; - int nprocs; -{ - int i, status; -#if defined(DB_WIN32) - DWORD ret; -#endif - - status = 0; - -#if defined(DB_WIN32) - do { - ret = WaitForMultipleObjects(nprocs, procs, FALSE, INFINITE); - i = ret - WAIT_OBJECT_0; - if (i < 0 || i >= nprocs) - return (__os_posix_err(__os_get_syserr())); - - if ((GetExitCodeProcess(procs[i], &ret) == 0) || (ret != 0)) - return (ret); - - /* remove the process handle from the list */ - while (++i < nprocs) - procs[i - 1] = procs[i]; - } while (--nprocs); -#elif !defined(HAVE_VXWORKS) - do { - if ((i = wait(&status)) == -1) - return (__os_posix_err(__os_get_syserr())); - - if (WIFEXITED(status) == 0 || WEXITSTATUS(status) != 0) { - for (i = 0; i < nprocs; i++) - kill(procs[i], SIGKILL); - return (WEXITSTATUS(status)); - } - } while (--nprocs); -#endif - - return (0); -} - -os_pid_t -spawn_proc(id, tmpath, typearg) - u_long id; - char *tmpath, *typearg; -{ - char lbuf[16], nbuf[16], pbuf[16], tbuf[16], Tbuf[256]; - char *const vbuf = verbose ? "-v" : NULL; - char *args[] = { NULL /* tmpath */, - "-l", NULL /* lbuf */, "-n", NULL /* nbuf */, - "-p", NULL /* pbuf */, "-t", NULL /* tbuf */, - "-T", NULL /* Tbuf */, NULL /* vbuf */, - NULL - }; - - args[0] = tmpath; - snprintf(lbuf, sizeof(lbuf), "%d", maxlocks); - args[2] = lbuf; - snprintf(nbuf, sizeof(nbuf), "%d", nlocks); - args[4] = nbuf; - snprintf(pbuf, sizeof(pbuf), "%d", nprocs); - args[6] = pbuf; - snprintf(tbuf, sizeof(tbuf), "%d", nthreads); - args[8] = tbuf; - snprintf(Tbuf, sizeof(Tbuf), "%s=%lu", typearg, id); - args[10] = Tbuf; - args[11] = vbuf; - - return (os_spawn(tmpath, args)); -} - -os_pid_t -os_spawn(path, argv) - const char *path; - char *const argv[]; -{ - os_pid_t pid; - int status; - - COMPQUIET(pid, 0); - COMPQUIET(status, 0); - -#ifdef HAVE_VXWORKS - fprintf(stderr, "ERROR: os_spawn not supported for VxWorks.\n"); - return (OS_BAD_PID); -#elif defined(HAVE_QNX) - /* - * For QNX, we cannot fork if we've ever used threads. So - * we'll use their spawn function. We use 'spawnl' which - * is NOT a POSIX function. - * - * The return value of spawnl is just what we want depending - * on the value of the 'wait' arg. - */ - return (spawnv(P_NOWAIT, path, argv)); -#elif defined(DB_WIN32) - return (os_pid_t)(_spawnv(P_NOWAIT, path, argv)); -#else - if ((pid = fork()) != 0) { - if (pid == -1) - return (OS_BAD_PID); - return (pid); - } else { - execv(path, argv); - exit(EXIT_FAILURE); - } -#endif -} diff --git a/db/mutex/uts4_cc.s b/db/mutex/uts4_cc.s index a8f2498ab..526ac3196 100644 --- a/db/mutex/uts4_cc.s +++ b/db/mutex/uts4_cc.s @@ -1,9 +1,8 @@ / See the file LICENSE for redistribution information. / - / Copyright (c) 1997-2006 - / Oracle Corporation. All rights reserved. + / Copyright (c) 1997,2007 Oracle. All rights reserved. / - / $Id: uts4_cc.s,v 12.3 2006/08/24 14:46:16 bostic Exp $ + / $Id: uts4_cc.s,v 12.5 2007/05/17 15:15:45 bostic Exp $ / / int uts_lock ( int *p, int i ); / Update the lock word pointed to by p with the -- cgit v1.2.3