summaryrefslogtreecommitdiff
path: root/src/os_win
diff options
context:
space:
mode:
Diffstat (limited to 'src/os_win')
-rw-r--r--src/os_win/os_fs.c23
-rw-r--r--src/os_win/os_mtx_cond.c43
-rw-r--r--src/os_win/os_snprintf.c50
-rw-r--r--src/os_win/os_thread.c20
-rw-r--r--src/os_win/os_vsnprintf.c41
-rw-r--r--src/os_win/os_yield.c8
6 files changed, 105 insertions, 80 deletions
diff --git a/src/os_win/os_fs.c b/src/os_win/os_fs.c
index 2f76fff04a5..5cf47ea5763 100644
--- a/src/os_win/os_fs.c
+++ b/src/os_win/os_fs.c
@@ -87,22 +87,19 @@ __win_fs_rename(WT_FILE_SYSTEM *file_system,
WT_ERR(__wt_to_utf16_string(session, to, &to_wide));
/*
- * Check if file exists since Windows does not override the file if
- * it exists.
+ * We want an atomic rename, but that's not guaranteed by MoveFileExW
+ * (or by any MSDN API). Don't set the MOVEFILE_COPY_ALLOWED flag to
+ * prevent the system from falling back to a copy and delete process.
+ * Do set the MOVEFILE_WRITE_THROUGH flag so the window is as small
+ * as possible, just in case. WiredTiger renames are done in a single
+ * directory and we expect that to be an atomic metadata update on any
+ * modern filesystem.
*/
- if (GetFileAttributesW(to_wide->data) != INVALID_FILE_ATTRIBUTES)
- if (DeleteFileW(to_wide->data) == FALSE) {
- windows_error = __wt_getlasterror();
- __wt_errx(session,
- "%s: file-rename: DeleteFileW: %s",
- to, __wt_formatmessage(session, windows_error));
- WT_ERR(__wt_map_windows_error(windows_error));
- }
-
- if (MoveFileW(from_wide->data, to_wide->data) == FALSE) {
+ if (MoveFileExW(from_wide->data, to_wide->data,
+ MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH) == FALSE) {
windows_error = __wt_getlasterror();
__wt_errx(session,
- "%s to %s: file-rename: MoveFileW: %s",
+ "%s to %s: file-rename: MoveFileExW: %s",
from, to, __wt_formatmessage(session, windows_error));
WT_ERR(__wt_map_windows_error(windows_error));
}
diff --git a/src/os_win/os_mtx_cond.c b/src/os_win/os_mtx_cond.c
index 79c62ccd7f2..0001c6c2322 100644
--- a/src/os_win/os_mtx_cond.c
+++ b/src/os_win/os_mtx_cond.c
@@ -13,8 +13,7 @@
* Allocate and initialize a condition variable.
*/
int
-__wt_cond_alloc(WT_SESSION_IMPL *session,
- const char *name, bool is_signalled, WT_CONDVAR **condp)
+__wt_cond_alloc(WT_SESSION_IMPL *session, const char *name, WT_CONDVAR **condp)
{
WT_CONDVAR *cond;
@@ -26,7 +25,7 @@ __wt_cond_alloc(WT_SESSION_IMPL *session,
InitializeConditionVariable(&cond->cond);
cond->name = name;
- cond->waiters = is_signalled ? -1 : 0;
+ cond->waiters = 0;
*condp = cond;
return (0);
@@ -38,8 +37,8 @@ __wt_cond_alloc(WT_SESSION_IMPL *session,
* out period expires, let the caller know.
*/
void
-__wt_cond_wait_signal(
- WT_SESSION_IMPL *session, WT_CONDVAR *cond, uint64_t usecs, bool *signalled)
+__wt_cond_wait_signal(WT_SESSION_IMPL *session, WT_CONDVAR *cond,
+ uint64_t usecs, bool (*run_func)(WT_SESSION_IMPL *), bool *signalled)
{
BOOL sleepret;
DWORD milliseconds, windows_error;
@@ -59,8 +58,26 @@ __wt_cond_wait_signal(
EnterCriticalSection(&cond->mtx);
locked = true;
+ /*
+ * It's possible to race with threads waking us up. That's not a problem
+ * if there are multiple wakeups because the next wakeup will get us, or
+ * if we're only pausing for a short period. It's a problem if there's
+ * only a single wakeup, our waker is likely waiting for us to exit.
+ * After acquiring the mutex (so we're guaranteed to be awakened by any
+ * future wakeup call), optionally check if we're OK to keep running.
+ * This won't ensure our caller won't just loop and call us again, but
+ * at least it's not our fault.
+ *
+ * Assert we're not waiting longer than a second if not checking the
+ * run status.
+ */
+ WT_ASSERT(session, run_func != NULL || usecs <= WT_MILLION);
+
+ if (run_func != NULL && !run_func(session))
+ goto skipping;
+
if (usecs > 0) {
- milliseconds64 = usecs / 1000;
+ milliseconds64 = usecs / WT_THOUSAND;
/*
* Check for 32-bit unsigned integer overflow
@@ -90,7 +107,7 @@ __wt_cond_wait_signal(
if (sleepret == 0) {
windows_error = __wt_getlasterror();
if (windows_error == ERROR_TIMEOUT) {
- *signalled = false;
+skipping: *signalled = false;
sleepret = 1;
}
}
@@ -117,17 +134,17 @@ void
__wt_cond_signal(WT_SESSION_IMPL *session, WT_CONDVAR *cond)
{
WT_DECL_RET;
- bool locked;
-
- locked = false;
__wt_verbose(session, WT_VERB_MUTEX, "signal %s", cond->name);
/*
- * Our callers are often setting flags to cause a thread to exit. Add
- * a barrier to ensure the flags are seen by the threads.
+ * Our callers often set flags to cause a thread to exit. Add a barrier
+ * to ensure exit flags are seen by the sleeping threads, otherwise we
+ * can wake up a thread, it immediately goes back to sleep, and we'll
+ * hang. Use a full barrier (we may not write before waiting on thread
+ * join).
*/
- WT_WRITE_BARRIER();
+ WT_FULL_BARRIER();
/*
* Fast path if we are in (or can enter), a state where the next waiter
diff --git a/src/os_win/os_snprintf.c b/src/os_win/os_snprintf.c
index a6056ff9342..f3025b12a60 100644
--- a/src/os_win/os_snprintf.c
+++ b/src/os_win/os_snprintf.c
@@ -8,17 +8,47 @@
#include "wt_internal.h"
-_Check_return_opt_ int __cdecl _wt_snprintf(
- _Out_writes_(_MaxCount) char * _DstBuf,
- _In_ size_t _MaxCount,
- _In_z_ _Printf_format_string_ const char * _Format, ...)
+/*
+ * __wt_vsnprintf_len_incr --
+ * POSIX vsnprintf convenience function, incrementing the returned size.
+ */
+int
+__wt_vsnprintf_len_incr(
+ char *buf, size_t size, size_t *retsizep, const char *fmt, va_list ap)
{
- va_list args;
- WT_DECL_RET;
+ int len;
+
+ /*
+ * WiredTiger calls with length 0 to get the needed buffer size. Call
+ * the count only version in this case, _vsnprintf_s will invoke the
+ * invalid parameter handler if count is less than or equal to zero.
+ */
+ if (size == 0) {
+ *retsizep += (size_t)_vscprintf(fmt, ap);
+ return (0);
+ }
+
+ /*
+ * Additionally, the invalid parameter handler is invoked if buffer or
+ * format is a NULL pointer.
+ */
+ if (buf == NULL || fmt == NULL)
+ return (EINVAL);
+
+ /*
+ * If the storage required to store the data and a terminating null
+ * exceeds size, the invalid parameter handler is invoked, unless
+ * count is _TRUNCATE, in which case as much of the string as will
+ * fit in the buffer is written and -1 returned.
+ */
+ if ((len = _vsnprintf_s(buf, size, _TRUNCATE, fmt, ap)) >= 0) {
+ *retsizep += (size_t)len;
+ return (0);
+ }
- va_start(args, _Format);
- ret = _wt_vsnprintf(_DstBuf, _MaxCount, _Format, args);
- va_end(args);
+ /* Return the buffer size required. */
+ if (len == -1)
+ *retsizep += (size_t)_vscprintf(fmt, ap);
- return (ret);
+ return (0);
}
diff --git a/src/os_win/os_thread.c b/src/os_win/os_thread.c
index a34dff776b6..4c8f212bb4f 100644
--- a/src/os_win/os_thread.c
+++ b/src/os_win/os_thread.c
@@ -16,6 +16,13 @@ int
__wt_thread_create(WT_SESSION_IMPL *session,
wt_thread_t *tidret, WT_THREAD_CALLBACK(*func)(void *), void *arg)
{
+ /*
+ * Creating a thread isn't a memory barrier, but WiredTiger commonly
+ * sets flags and or state and then expects worker threads to start.
+ * Include a barrier to ensure safety in those cases.
+ */
+ WT_FULL_BARRIER();
+
/* Spawn a new thread of control. */
*tidret = (HANDLE)_beginthreadex(NULL, 0, func, arg, 0, NULL);
if (*tidret != 0)
@@ -33,6 +40,13 @@ __wt_thread_join(WT_SESSION_IMPL *session, wt_thread_t tid)
{
DWORD windows_error;
+ /*
+ * Joining a thread isn't a memory barrier, but WiredTiger commonly
+ * sets flags and or state and then expects worker threads to halt.
+ * Include a barrier to ensure safety in those cases.
+ */
+ WT_FULL_BARRIER();
+
if ((windows_error =
WaitForSingleObject(tid, INFINITE)) != WAIT_OBJECT_0) {
if (windows_error == WAIT_FAILED)
@@ -58,10 +72,10 @@ __wt_thread_join(WT_SESSION_IMPL *session, wt_thread_t tid)
* __wt_thread_id --
* Fill in a printable version of the process and thread IDs.
*/
-void
+int
__wt_thread_id(char *buf, size_t buflen)
{
- (void)snprintf(buf, buflen,
+ return (__wt_snprintf(buf, buflen,
"%" PRIu64 ":%" PRIu64,
- (uint64_t)GetCurrentProcessId(), (uint64_t)GetCurrentThreadId);
+ (uint64_t)GetCurrentProcessId(), (uint64_t)GetCurrentThreadId));
}
diff --git a/src/os_win/os_vsnprintf.c b/src/os_win/os_vsnprintf.c
deleted file mode 100644
index 63f96e79d5b..00000000000
--- a/src/os_win/os_vsnprintf.c
+++ /dev/null
@@ -1,41 +0,0 @@
-/*-
- * Copyright (c) 2014-2016 MongoDB, Inc.
- * Copyright (c) 2008-2014 WiredTiger, Inc.
- * All rights reserved.
- *
- * See the file LICENSE for redistribution information.
- */
-
-#include "wt_internal.h"
-
-_Check_return_opt_ int __cdecl _wt_vsnprintf(
- _Out_writes_(_MaxCount) char * _DstBuf,
- _In_ size_t _MaxCount,
- _In_z_ _Printf_format_string_ const char * _Format,
- va_list _ArgList)
-{
- int len;
-
- /*
- * WiredTiger will call with length 0 to get the needed buffer size
- * We call the count only version in this case since vsnprintf_s assumes
- * length is greater than zero or else it triggers the invalid_parameter
- * handler.
- */
- if (_MaxCount == 0) {
- return _vscprintf(_Format, _ArgList);
- }
-
- len = (size_t)_vsnprintf_s(
- _DstBuf, _MaxCount, _TRUNCATE, _Format, _ArgList);
-
- /*
- * The MSVC implementation returns -1 on truncation instead of what
- * it would have written. We could let callers iteratively grow the
- * buffer, or just ask us how big a buffer they would like.
- */
- if (len == -1)
- len = _vscprintf(_Format, _ArgList) + 1;
-
- return (len);
-}
diff --git a/src/os_win/os_yield.c b/src/os_win/os_yield.c
index aab1559e072..038f2efe162 100644
--- a/src/os_win/os_yield.c
+++ b/src/os_win/os_yield.c
@@ -15,5 +15,13 @@
void
__wt_yield(void)
{
+ /*
+ * Yielding the processor isn't documented as a memory barrier, and it's
+ * a reasonable expectation to have. There's no reason not to explicitly
+ * include a barrier since we're giving up the CPU, and ensures callers
+ * aren't ever surprised.
+ */
+ WT_FULL_BARRIER();
+
SwitchToThread();
}