#include "db_config.h"
#ifndef lint
-static const char revid[] = "Id: fop_util.c,v 1.49 2002/08/07 15:40:06 margo Exp ";
+static const char revid[] = "Id: fop_util.c,v 1.52 2002/09/10 02:41:42 bostic Exp ";
#endif /* not lint */
#ifndef NO_SYSTEM_INCLUDES
if (F_ISSET((F), DB_FH_VALID)) { \
if (LF_ISSET(DB_FCNTL_LOCKING)) \
(D)->saved_open_fhp = (F); \
- else if ((ret = __os_closehandle((D)->dbenv,(F))) != 0) \
+ else if ((t_ret = __os_closehandle((D)->dbenv,(F))) != 0) { \
+ if (ret == 0) \
+ ret = t_ret; \
goto err; \
+ } \
} \
}
DB_ENV *dbenv;
DB_FH fh, *fhp;
DB_LOCK elock, tmp_lock;
- DB_LSN lsn;
DB_TXN *stxn;
+ char *real_name, *real_tmpname, *tmpname;
db_lockmode_t lmode;
+ int created_fhp, created_locker, ret, tmp_created, t_ret, truncating;
+ size_t len;
u_int32_t locker, oflags;
u_int8_t mbuf[DBMETASIZE];
- int created_fhp, created_locker, ret, tmp_created, truncating;
- char *real_name, *real_tmpname, *tmpname;
DB_ASSERT(name != NULL);
/*
* If we open a file handle and our caller is doing fcntl(2) locking,
* we can't close it because that would discard the caller's lock.
- * Save it until we close the DB handle.
+ * Save it until we close or refresh the DB handle.
*/
if (LF_ISSET(DB_FCNTL_LOCKING)) {
if ((ret = __os_malloc(dbenv, sizeof(*fhp), &fhp)) != 0)
ret = EEXIST;
goto err;
}
-reopen: if ((ret = __fop_read_meta(dbenv, real_name,
- mbuf, sizeof(mbuf), fhp, 0, oflags)) != 0)
+
+ /*
+ * This is special handling for applications that
+ * are locking outside of Berkeley DB (e.g., Sendmail,
+ * Postfix). If we are relying on external FCNTL
+ * locking and we're going to truncate the file, we
+ * cannot first open the file to verify that it is
+ * a DB file and then close/reopen to do the truncate
+ * since that will lose the external FCNTL lock.
+ * So, we special case it and leap right into the
+ * truncate code.
+ */
+ if (LF_ISSET(DB_FCNTL_LOCKING) && LF_ISSET(DB_TRUNCATE))
+ goto do_trunc;
+
+reopen: ret = __fop_read_meta(dbenv, real_name,
+ mbuf, sizeof(mbuf), fhp,
+ LF_ISSET(DB_FCNTL_LOCKING) && txn == NULL ? 1 : 0,
+ &len, oflags);
+ /*
+ * This is special handling for applications that are doing
+ * file locking outside of Berkeley DB (e.g., Sendmail,
+ * Postfix). So, if you're doing FCNTL_LOCKING and are non
+ * transactional, we're going to treat 0-length files as a
+ * special case and let you proceed.
+ */
+ if (ret != 0 &&
+ LF_ISSET(DB_FCNTL_LOCKING) && txn == NULL && len == 0) {
+ tmpname = (char *)real_name;
+ real_name = NULL;
+ goto creat2;
+ }
+
+ if (ret != 0)
goto err;
if ((ret = __db_meta_setup(dbenv,
if ((ret = REL_ENVLOCK(dbenv, &elock)) != 0)
goto err;
} else {
- /* Someone else has file locked; need to wait. */
+ /*
+ * If someone is doing FCNTL locking outside of us,
+ * then we should never have a lock conflict and
+ * should never get to here. We need to assert that
+ * because we are about to close the fd which will
+ * release the FCNTL locks.
+ */
+ DB_ASSERT(!LF_ISSET(DB_FCNTL_LOCKING));
if ((ret = __os_closehandle(dbenv, fhp)) != 0)
goto err;
ret = __fop_lock_handle(dbenv,
*/
if ((ret = __os_closehandle(dbenv, fhp)) != 0)
goto err;
- if ((ret = __os_open(dbenv,
+do_trunc: if ((ret = __os_open(dbenv,
real_name, DB_OSO_TRUNC, 0, fhp)) != 0)
goto err;
/*
if ((ret = REL_ENVLOCK(dbenv, &elock)) != 0)
goto err;
-create: if (txn == NULL)
- ZERO_LSN(lsn);
- else
- lsn = txn->last_lsn;
- if ((ret = __db_backup_name(dbenv, name, txn, &tmpname, &lsn)) != 0)
+create: if ((ret = __db_backup_name(dbenv, name, txn, &tmpname)) != 0)
goto err;
if (TXN_ON(dbenv) && txn != NULL &&
(ret = dbenv->txn_begin(dbenv, txn, &stxn, 0)) != 0)
(void)__fop_remove(dbenv,
NULL, NULL, tmpname, DB_APP_DATA);
if (F_ISSET(fhp, DB_FH_VALID))
- (void)__os_closehandle(dbenv, fhp);
+ CLOSE_HANDLE(dbp, fhp);
if (LOCK_ISSET(tmp_lock))
__lock_put(dbenv, &tmp_lock);
if (LOCK_ISSET(dbp->handle_lock) && txn == NULL)
(void)__lock_id_free(dbenv, dbp->lid);
dbp->lid = DB_LOCK_INVALIDID;
}
- if (created_fhp)
+ if (created_fhp && !F_ISSET(fhp, DB_FH_VALID))
__os_free(dbenv, fhp);
}
-done: if (!truncating && tmpname != NULL)
+done: /*
+ * There are cases where real_name and tmpname take on the
+ * exact same string, so we need to make sure that we do not
+ * free twice.
+ */
+ if (!truncating && tmpname != NULL && tmpname != real_name)
__os_free(dbenv, tmpname);
if (real_name != NULL)
__os_free(dbenv, real_name);
{
DB *mdbp;
DB_ENV *dbenv;
- int remove, ret;
+ int do_remove, ret;
mdbp = NULL;
dbenv = dbp->dbenv;
/*
* The master's handle lock is under the control of the
- * subdb (it acquired the master's locker. We want to
+ * subdb (it acquired the master's locker). We want to
* keep the master's handle lock so that no one can remove
* the file while the subdb is open. If we register the
* trade event and then invalidate the copy of the lock
/* If we created the master file then we need to remove it. */
if (mdbp != NULL) {
- remove = F_ISSET(mdbp, DB_AM_CREATED) ? 1 : 0;
- if (remove)
+ do_remove = F_ISSET(mdbp, DB_AM_CREATED) ? 1 : 0;
+ if (do_remove)
F_SET(mdbp, DB_AM_DISCARD);
(void)__db_close_i(mdbp, txn, 0);
- if (remove) {
+ if (do_remove) {
(void)db_create(&mdbp, dbp->dbenv, 0);
(void)__db_remove_i(mdbp, txn, mname, NULL);
}
u_int32_t flags;
{
DB_ENV *dbenv;
+ DB_FH *fhp;
DB_LOCK elock;
u_int8_t mbuf[DBMETASIZE];
int ret;
}
/*
+ * We are about to open a file handle and then possibly close it.
+ * We cannot close handles if we are doing FCNTL locking. However,
+ * there is no way to pass the FCNTL flag into this routine via the
+ * user API. The only way we can get in here and be doing FCNTL
+ * locking is if we are trying to clean up an open that was called
+ * with FCNTL locking. In that case, the save_fhp should already be
+ * set. So, we use that field to tell us if we need to make sure
+ * that we shouldn't close the handle.
+ */
+ fhp = dbp->saved_open_fhp;
+ DB_ASSERT(LF_ISSET(DB_FCNTL_LOCKING) ||
+ fhp == NULL || !F_ISSET(fhp, DB_FH_VALID));
+
+ /*
* Lock environment to protect file open. That will enable us to
* read the meta-data page and get the fileid so that we can lock
* the handle.
*/
GET_ENVLOCK(dbenv, dbp->lid, &elock);
if ((ret = __fop_read_meta(dbenv,
- name, mbuf, sizeof(mbuf), NULL, 0, 0)) != 0)
+ name, mbuf, sizeof(mbuf), fhp, 0, NULL, 0)) != 0)
goto err;
if ((ret =
* Read the meta-data page from a file and return it in buf. The
* open file handle is returned in fhp.
*
- * PUBLIC: int __fop_read_meta __P((DB_ENV *,
- * PUBLIC: const char *, u_int8_t *, size_t, DB_FH *, int, u_int32_t));
+ * PUBLIC: int __fop_read_meta __P((DB_ENV *, const char *,
+ * PUBLIC: u_int8_t *, size_t, DB_FH *, int, size_t *, u_int32_t));
*/
int
-__fop_read_meta(dbenv, name, buf, size, fhp, errok, flags)
+__fop_read_meta(dbenv, name, buf, size, fhp, errok, nbytesp, flags)
DB_ENV *dbenv;
const char *name;
u_int8_t *buf;
size_t size;
DB_FH *fhp;
int errok;
+ size_t *nbytesp;
u_int32_t flags;
{
DB_FH fh, *lfhp;
size_t nr;
- int ret;
+ int myfhp, ret;
+ nr = 0;
+ myfhp = 0;
+ memset(&fh, 0, sizeof(fh));
lfhp = fhp == NULL ? &fh : fhp;
- memset(lfhp, 0, sizeof(*fhp));
- if ((ret = __os_open(dbenv, name, flags, 0, lfhp)) != 0)
+ myfhp = F_ISSET(lfhp, DB_FH_VALID);
+ if (!myfhp && (ret = __os_open(dbenv, name, flags, 0, lfhp)) != 0)
goto err;
if ((ret = __os_read(dbenv, lfhp, buf, size, &nr)) != 0) {
if (!errok)
}
err: /*
- * On error, we always close the handle. If there is no error,
- * then we only return the handle if the user didn't pass us
- * a handle into which to return it. If fhp is valid, then
- * lfhp is the same as fhp.
+ * On error, we would like to close the handle. However, if the
+ * handle was opened in the caller, we cannot. If there is no error,
+ * then we only close the handle if we opened it here.
*/
- if (F_ISSET(lfhp, DB_FH_VALID) && (ret != 0 || fhp == NULL))
+ if (!myfhp && F_ISSET((lfhp), DB_FH_VALID) && (ret != 0 || fhp == NULL))
__os_closehandle(dbenv, lfhp);
+
+ if (nbytesp != NULL)
+ *nbytesp = nr;
return (ret);
}
goto err;
/* We need to create a dummy file as a place holder. */
- if ((ret =
- __db_backup_name(dbenv, new, txn, &back, &txn->last_lsn)) != 0)
+ if ((ret = __db_backup_name(dbenv, new, stxn, &back)) != 0)
goto err;
if ((ret = __db_appname(dbenv,
DB_APP_DATA, back, flags, NULL, &realback)) != 0)
tmpdbt.data = tmpdbp->fileid;
tmpdbt.size = DB_FILE_ID_LEN;
namedbt.data = (void *)old;
- namedbt.size = strlen(old) + 1;
+ namedbt.size = (u_int32_t)strlen(old) + 1;
if ((t_ret =
__fop_file_remove_log(dbenv, txn, &lsn, 0, &fiddbt,
&tmpdbt, &namedbt, DB_APP_DATA, stxnid)) != 0 && ret == 0)
#include "db_config.h"
#ifndef lint
-static const char revid[] = "Id: mut_win32.c,v 1.6 2002/07/12 04:05:00 mjc Exp ";
+static const char revid[] = "Id: mut_win32.c,v 1.8 2002/09/10 02:37:25 bostic Exp ";
#endif /* not lint */
#ifndef NO_SYSTEM_INCLUDES
#define LOAD_ACTUAL_MUTEX_CODE
#include "db_int.h"
+/* We don't want to run this code even in "ordinary" diagnostic mode. */
+#undef MUTEX_DIAG
+
#define GET_HANDLE(mutexp, event) do { \
- int i; \
char idbuf[13]; \
\
- if (F_ISSET(mutexp, MUTEX_THREAD)) { \
- event = mutexp->event; \
- return (0); \
- } \
- \
- for (i = 0; i < 8; i++) \
- idbuf[(sizeof(idbuf) - 1) - i] = \
- "0123456789abcdef"[(mutexp->id >> (i * 4)) & 0xf]; \
- event = CreateEvent(NULL, TRUE, FALSE, idbuf); \
+ snprintf(idbuf, sizeof idbuf, "db.m%08x", mutexp->id); \
+ event = CreateEvent(NULL, FALSE, FALSE, idbuf); \
if (event == NULL) \
return (__os_win32_errno()); \
} while (0)
-#define RELEASE_HANDLE(mutexp, event) \
- if (!F_ISSET(mutexp, MUTEX_THREAD) && event != NULL) { \
- CloseHandle(event); \
- event = NULL; \
- }
-
/*
* __db_win32_mutex_init --
* Initialize a DB_MUTEX.
/*
* If this is a thread lock or the process has told us that there are
- * no other processes in the environment, use thread-only locks, they
- * are faster in some cases.
- *
- * This is where we decide to ignore locks we don't need to set -- if
- * the application isn't threaded, there aren't any threads to block.
+ * no other processes in the environment, and the application isn't
+ * threaded, there aren't any threads to block.
*/
if (LF_ISSET(MUTEX_THREAD) || F_ISSET(dbenv, DB_ENV_PRIVATE)) {
if (!F_ISSET(dbenv, DB_ENV_THREAD)) {
F_SET(mutexp, MUTEX_IGNORE);
return (0);
}
- F_SET(mutexp, MUTEX_THREAD);
- mutexp->event = CreateEvent(NULL, TRUE, FALSE, NULL);
- if (mutexp->event == NULL)
- return (__os_win32_errno());
- } else
- mutexp->id = ((getpid() & 0xffff) << 16) ^ (u_int32_t)mutexp;
+ }
+ mutexp->id = ((getpid() & 0xffff) << 16) ^ P_TO_UINT32(mutexp);
mutexp->spins = __os_spin(dbenv);
F_SET(mutexp, MUTEX_INITED);
DB_MUTEX *mutexp;
{
HANDLE event;
- u_long ms;
- int nspins;
+ int ret, ms, nspins;
+#ifdef MUTEX_DIAG
+ LARGE_INTEGER now;
+#endif
if (F_ISSET(dbenv, DB_ENV_NOLOCKING) || F_ISSET(mutexp, MUTEX_IGNORE))
return (0);
event = NULL;
ms = 50;
+ ret = 0;
loop: /* Attempt to acquire the resource for N spins. */
for (nspins = mutexp->spins; nspins > 0; --nspins) {
if (!MUTEX_SET(&mutexp->tas))
continue;
- if (mutexp->locked) {
- /*
- * If we are about to block for the first time,
- * increment the waiter count while we still hold
- * the mutex.
- */
- if (nspins == 1 && event == NULL)
- ++mutexp->nwaiters;
- MUTEX_UNSET(&mutexp->tas);
- continue;
- }
+#ifdef DIAGNOSTIC
+ if (mutexp->locked)
+ __db_err(dbenv,
+ "__db_win32_mutex_lock: mutex double-locked!");
- mutexp->locked = 1;
+ __os_id(&mutexp->locked);
+#endif
if (event == NULL)
++mutexp->mutex_set_nowait;
else {
++mutexp->mutex_set_wait;
- --mutexp->nwaiters;
+ CloseHandle(event);
+ InterlockedDecrement(&mutexp->nwaiters);
+#ifdef MUTEX_DIAG
+ if (ret != WAIT_OBJECT_0) {
+ QueryPerformanceCounter(&now);
+ printf("[%I64d]: Lost signal on mutex %p, "
+ "id %d, ms %d\n",
+ now.QuadPart, mutexp, mutexp->id, ms);
+ }
+#endif
}
- MUTEX_UNSET(&mutexp->tas);
- RELEASE_HANDLE(mutexp, event);
return (0);
}
/*
- * Yield the processor; wait 50 ms initially, up to 1 second.
- * This loop is needed to work around an unlikely race where the signal
- * from the unlocking thread gets lost.
+ * Yield the processor; wait 50 ms initially, up to 1 second. This
+ * loop is needed to work around a race where the signal from the
+ * unlocking thread gets lost. We start at 50 ms because it's unlikely
+ * to happen often and we want to avoid wasting CPU.
*/
- if (event == NULL)
+ if (event == NULL) {
+#ifdef MUTEX_DIAG
+ QueryPerformanceCounter(&now);
+ printf("[%I64d]: Waiting on mutex %p, id %d\n",
+ now.QuadPart, mutexp, mutexp->id);
+#endif
+ InterlockedIncrement(&mutexp->nwaiters);
GET_HANDLE(mutexp, event);
- if (WaitForSingleObject(event, ms) == WAIT_FAILED)
+ }
+ if ((ret = WaitForSingleObject(event, ms)) == WAIT_FAILED)
return (__os_win32_errno());
-
if ((ms <<= 1) > MS_PER_SEC)
ms = MS_PER_SEC;
{
int ret;
HANDLE event;
+#ifdef MUTEX_DIAG
+ LARGE_INTEGER now;
+#endif
if (F_ISSET(dbenv, DB_ENV_NOLOCKING) || F_ISSET(mutexp, MUTEX_IGNORE))
return (0);
#ifdef DIAGNOSTIC
- if (!mutexp->locked)
+ if (!mutexp->tas || !mutexp->locked)
__db_err(dbenv,
"__db_win32_mutex_unlock: ERROR: lock already unlocked");
-#endif
-
- ret = 0;
- /* We have to drop the mutex inside a critical section */
- while (!MUTEX_SET(&mutexp->tas))
- ;
mutexp->locked = 0;
+#endif
MUTEX_UNSET(&mutexp->tas);
+ ret = 0;
+
if (mutexp->nwaiters > 0) {
GET_HANDLE(mutexp, event);
+#ifdef MUTEX_DIAG
+ QueryPerformanceCounter(&now);
+ printf("[%I64d]: Signalling mutex %p, id %d\n",
+ now.QuadPart, mutexp, mutexp->id);
+#endif
if (!PulseEvent(event))
ret = __os_win32_errno();
- RELEASE_HANDLE(mutexp, event);
+ CloseHandle(event);
}
#ifdef DIAGNOSTIC
- if (ret)
+ if (ret != 0)
__db_err(dbenv,
"__db_win32_mutex_unlock: ERROR: unlock failed");
#endif
/*
* __db_win32_mutex_destroy --
- * Destroy a DB_MUTEX.
+ * Destroy a DB_MUTEX - noop with this implementation.
*
* PUBLIC: int __db_win32_mutex_destroy __P((DB_MUTEX *));
*/
__db_win32_mutex_destroy(mutexp)
DB_MUTEX *mutexp;
{
- int ret;
-
- if (F_ISSET(mutexp, MUTEX_IGNORE) || !F_ISSET(mutexp, MUTEX_THREAD))
- return (0);
-
- ret = 0;
- if (mutexp->event != NULL) {
- if (!CloseHandle(mutexp->event))
- ret = __os_win32_errno();
- mutexp->event = NULL;
- }
-
- return (ret);
+ return (0);
}