summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKeith Bostic <keith.bostic@mongodb.com>2017-01-24 22:07:16 -0500
committerMichael Cahill <michael.cahill@mongodb.com>2017-01-25 14:07:16 +1100
commit8aa3922883e7f3d4a9003211faf595250c3bbfdd (patch)
tree11cf9d9a43df251433cd888446bdcb6c58041e75
parentd5ae763f990af5ba5522b07c18b9b37fdaae0e88 (diff)
downloadmongo-8aa3922883e7f3d4a9003211faf595250c3bbfdd.tar.gz
WT-3097 Avoid waiting for threads to timeout during close (#3253)
* Add run-time flags checking to __wt_cond_wait_signal(), and its wrappers (__wt_cond_wait(), __wt_cond_auto_wait_signal() and __wt_cond_auto_wait()) so callers of those functions can configure a check that ensures that if the waiting thread races with a waking thread that's turned off flags so the waiting thread quits, the waiting thread returns immediately. * Rework the WT_SESSION.transaction_sync code to wait for the entire time it's configured to wait, it will be awoken if the log reaches stability before that. * Assert we're not waiting longer than a second if not checking the run status. * Set/Clear WT_CONN_LOG_SERVER_RUN in __wt_logmgr_open/__wt_logmgr_destroy rather than in the connection open code. (It's the only server-run flag that gets set in the connection-open code, and I can't see any reason for that exception.)
-rw-r--r--dist/api_data.py2
-rw-r--r--dist/s_string.ok4
-rw-r--r--src/async/async_api.c5
-rw-r--r--src/async/async_worker.c2
-rw-r--r--src/conn/conn_cache.c6
-rw-r--r--src/conn/conn_cache_pool.c8
-rw-r--r--src/conn/conn_ckpt.c26
-rw-r--r--src/conn/conn_handle.c2
-rw-r--r--src/conn/conn_log.c50
-rw-r--r--src/conn/conn_open.c17
-rw-r--r--src/conn/conn_stat.c25
-rw-r--r--src/conn/conn_sweep.c24
-rw-r--r--src/evict/evict_lru.c16
-rw-r--r--src/include/extern.h8
-rw-r--r--src/include/extern_posix.h4
-rw-r--r--src/include/extern_win.h4
-rw-r--r--src/include/misc.i5
-rw-r--r--src/include/mutex.h4
-rw-r--r--src/include/wiredtiger.in2
-rw-r--r--src/log/log.c21
-rw-r--r--src/log/log_slot.c2
-rw-r--r--src/lsm/lsm_worker.c2
-rw-r--r--src/os_posix/os_mtx_cond.c28
-rw-r--r--src/os_win/os_mtx_cond.c43
-rw-r--r--src/session/session_api.c38
-rw-r--r--src/support/cond_auto.c80
-rw-r--r--src/support/thread_group.c2
27 files changed, 237 insertions, 193 deletions
diff --git a/dist/api_data.py b/dist/api_data.py
index b1332320a7c..1d669fa7fe0 100644
--- a/dist/api_data.py
+++ b/dist/api_data.py
@@ -718,7 +718,7 @@ wiredtiger_open_common =\
]),
Config('extensions', '', r'''
list of shared library extensions to load (using dlopen).
- Any values specified to an library extension are passed to
+ Any values specified to a library extension are passed to
WT_CONNECTION::load_extension as the \c config parameter
(for example,
<code>extensions=(/path/ext.so={entry=my_entry})</code>)''',
diff --git a/dist/s_string.ok b/dist/s_string.ok
index 2b998c27813..bb0cacd9d5d 100644
--- a/dist/s_string.ok
+++ b/dist/s_string.ok
@@ -1217,6 +1217,7 @@ upg
uri
uri's
uris
+usec
usecs
usedp
userbad
@@ -1247,6 +1248,9 @@ vunpack
vw
vxr
waitpid
+waker
+wakeup
+wakeups
walk's
warmup
wb
diff --git a/src/async/async_api.c b/src/async/async_api.c
index 54bcb7cd26c..026a008188c 100644
--- a/src/async/async_api.c
+++ b/src/async/async_api.c
@@ -240,8 +240,7 @@ __async_start(WT_SESSION_IMPL *session)
async = conn->async;
TAILQ_INIT(&async->formatqh);
WT_RET(__wt_spin_init(session, &async->ops_lock, "ops"));
- WT_RET(__wt_cond_alloc(
- session, "async flush", false, &async->flush_cond));
+ WT_RET(__wt_cond_alloc(session, "async flush", &async->flush_cond));
WT_RET(__wt_async_op_init(session));
/*
@@ -541,7 +540,7 @@ retry:
async->flush_op.state = WT_ASYNCOP_READY;
WT_RET(__wt_async_op_enqueue(session, &async->flush_op));
while (async->flush_state != WT_ASYNC_FLUSH_COMPLETE)
- __wt_cond_wait(session, async->flush_cond, 100000);
+ __wt_cond_wait(session, async->flush_cond, 100000, NULL);
/*
* Flush is done. Clear the flags.
*/
diff --git a/src/async/async_worker.c b/src/async/async_worker.c
index b1bc3902f7c..11f59ed14f1 100644
--- a/src/async/async_worker.c
+++ b/src/async/async_worker.c
@@ -107,7 +107,7 @@ __async_flush_wait(WT_SESSION_IMPL *session, WT_ASYNC *async, uint64_t my_gen)
{
while (async->flush_state == WT_ASYNC_FLUSHING &&
async->flush_gen == my_gen)
- __wt_cond_wait(session, async->flush_cond, 10000);
+ __wt_cond_wait(session, async->flush_cond, 10000, NULL);
}
/*
diff --git a/src/conn/conn_cache.c b/src/conn/conn_cache.c
index 2b0e5081f04..28dd06332e0 100644
--- a/src/conn/conn_cache.c
+++ b/src/conn/conn_cache.c
@@ -187,8 +187,8 @@ __wt_cache_create(WT_SESSION_IMPL *session, const char *cfg[])
WT_RET_MSG(session, EINVAL,
"eviction target must be lower than the eviction trigger");
- WT_RET(__wt_cond_auto_alloc(session, "cache eviction server",
- false, 10000, WT_MILLION, &cache->evict_cond));
+ WT_RET(__wt_cond_auto_alloc(session,
+ "cache eviction server", 10000, WT_MILLION, &cache->evict_cond));
WT_RET(__wt_spin_init(session, &cache->evict_pass_lock, "evict pass"));
WT_RET(__wt_spin_init(session,
&cache->evict_queue_lock, "cache eviction queue"));
@@ -312,7 +312,7 @@ __wt_cache_destroy(WT_SESSION_IMPL *session)
cache->bytes_dirty_intl + cache->bytes_dirty_leaf,
cache->pages_dirty_intl + cache->pages_dirty_leaf);
- WT_TRET(__wt_cond_auto_destroy(session, &cache->evict_cond));
+ WT_TRET(__wt_cond_destroy(session, &cache->evict_cond));
__wt_spin_destroy(session, &cache->evict_pass_lock);
__wt_spin_destroy(session, &cache->evict_queue_lock);
__wt_spin_destroy(session, &cache->evict_walk_lock);
diff --git a/src/conn/conn_cache_pool.c b/src/conn/conn_cache_pool.c
index 79c2fc23da5..49b766f4602 100644
--- a/src/conn/conn_cache_pool.c
+++ b/src/conn/conn_cache_pool.c
@@ -32,7 +32,7 @@
*/
#define WT_CACHE_POOL_APP_EVICT_MULTIPLIER 3
#define WT_CACHE_POOL_APP_WAIT_MULTIPLIER 6
-#define WT_CACHE_POOL_READ_MULTIPLIER 1
+#define WT_CACHE_POOL_READ_MULTIPLIER 1
static void __cache_pool_adjust(
WT_SESSION_IMPL *, uint64_t, uint64_t, bool, bool *);
@@ -104,8 +104,8 @@ __wt_cache_pool_config(WT_SESSION_IMPL *session, const char **cfg)
TAILQ_INIT(&cp->cache_pool_qh);
WT_ERR(__wt_spin_init(
session, &cp->cache_pool_lock, "cache shared pool"));
- WT_ERR(__wt_cond_alloc(session,
- "cache pool server", false, &cp->cache_pool_cond));
+ WT_ERR(__wt_cond_alloc(
+ session, "cache pool server", &cp->cache_pool_cond));
__wt_process.cache_pool = cp;
__wt_verbose(session,
@@ -733,7 +733,7 @@ __wt_cache_pool_server(void *arg)
F_ISSET(cache, WT_CACHE_POOL_RUN)) {
if (cp->currently_used <= cp->size)
__wt_cond_wait(
- session, cp->cache_pool_cond, WT_MILLION);
+ session, cp->cache_pool_cond, WT_MILLION, NULL);
/*
* Re-check pool run flag - since we want to avoid getting the
diff --git a/src/conn/conn_ckpt.c b/src/conn/conn_ckpt.c
index faeef4e71a2..7797ed4421c 100644
--- a/src/conn/conn_ckpt.c
+++ b/src/conn/conn_ckpt.c
@@ -63,6 +63,16 @@ __ckpt_server_config(WT_SESSION_IMPL *session, const char **cfg, bool *startp)
}
/*
+ * __ckpt_server_run_chk --
+ * Check to decide if the checkpoint server should continue running.
+ */
+static bool
+__ckpt_server_run_chk(WT_SESSION_IMPL *session)
+{
+ return (F_ISSET(S2C(session), WT_CONN_SERVER_CHECKPOINT));
+}
+
+/*
* __ckpt_server --
* The checkpoint server thread.
*/
@@ -78,14 +88,18 @@ __ckpt_server(void *arg)
conn = S2C(session);
wt_session = (WT_SESSION *)session;
- while (F_ISSET(conn, WT_CONN_SERVER_RUN) &&
- F_ISSET(conn, WT_CONN_SERVER_CHECKPOINT)) {
+ for (;;) {
/*
* Wait...
* NOTE: If the user only configured logsize, then usecs
* will be 0 and this wait won't return until signalled.
*/
- __wt_cond_wait(session, conn->ckpt_cond, conn->ckpt_usecs);
+ __wt_cond_wait(session,
+ conn->ckpt_cond, conn->ckpt_usecs, __ckpt_server_run_chk);
+
+ /* Check if we're quitting or being reconfigured. */
+ if (!__ckpt_server_run_chk(session))
+ break;
/*
* Checkpoint the database if the connection is marked dirty.
@@ -113,7 +127,8 @@ __ckpt_server(void *arg)
* it so we don't do another checkpoint
* immediately.
*/
- __wt_cond_wait(session, conn->ckpt_cond, 1);
+ __wt_cond_wait(
+ session, conn->ckpt_cond, 1, NULL);
}
} else
WT_STAT_CONN_INCR(session, txn_checkpoint_skipped);
@@ -152,8 +167,7 @@ __ckpt_server_start(WT_CONNECTION_IMPL *conn)
"checkpoint-server", true, session_flags, &conn->ckpt_session));
session = conn->ckpt_session;
- WT_RET(__wt_cond_alloc(
- session, "checkpoint server", false, &conn->ckpt_cond));
+ WT_RET(__wt_cond_alloc(session, "checkpoint server", &conn->ckpt_cond));
/*
* Start the thread.
diff --git a/src/conn/conn_handle.c b/src/conn/conn_handle.c
index 7203b75e4ae..54bcfd98aba 100644
--- a/src/conn/conn_handle.c
+++ b/src/conn/conn_handle.c
@@ -79,7 +79,7 @@ __wt_connection_init(WT_CONNECTION_IMPL *conn)
WT_RET(__wt_spin_init(
session, &conn->lsm_manager.switch_lock, "LSM switch queue lock"));
WT_RET(__wt_cond_alloc(
- session, "LSM worker cond", false, &conn->lsm_manager.work_cond));
+ session, "LSM worker cond", &conn->lsm_manager.work_cond));
/*
* Generation numbers.
diff --git a/src/conn/conn_log.c b/src/conn/conn_log.c
index 8f8f8614ba8..c6dd795389d 100644
--- a/src/conn/conn_log.c
+++ b/src/conn/conn_log.c
@@ -174,7 +174,7 @@ __logmgr_config(
WT_RET(__logmgr_sync_cfg(session, cfg));
if (conn->log_cond != NULL)
- __wt_cond_auto_signal(session, conn->log_cond);
+ __wt_cond_signal(session, conn->log_cond);
return (0);
}
@@ -341,7 +341,7 @@ __wt_log_truncate_files(
conn = S2C(session);
if (!FLD_ISSET(conn->log_flags, WT_CONN_LOG_ENABLED))
return (0);
- if (F_ISSET(conn, WT_CONN_SERVER_RUN) &&
+ if (F_ISSET(conn, WT_CONN_LOG_SERVER_RUN) &&
FLD_ISSET(conn->log_flags, WT_CONN_LOG_ARCHIVE))
WT_RET_MSG(session, EINVAL,
"Attempt to archive manually while a server is running");
@@ -505,8 +505,7 @@ __log_file_server(void *arg)
locked = false;
__wt_spin_unlock(session, &log->log_sync_lock);
} else {
- __wt_cond_auto_signal(
- session, conn->log_wrlsn_cond);
+ __wt_cond_signal(session, conn->log_wrlsn_cond);
/*
* We do not want to wait potentially a second
* to process this. Yield to give the wrlsn
@@ -517,8 +516,9 @@ __log_file_server(void *arg)
continue;
}
}
+
/* Wait until the next event. */
- __wt_cond_wait(session, conn->log_file_cond, WT_MILLION / 10);
+ __wt_cond_wait(session, conn->log_file_cond, 100000, NULL);
}
if (0) {
@@ -730,12 +730,8 @@ __log_wrlsn_server(void *arg)
if (yield++ < WT_THOUSAND)
__wt_yield();
else
- /*
- * Send in false because if we did any work we would
- * not be on this path.
- */
__wt_cond_auto_wait(
- session, conn->log_wrlsn_cond, did_work);
+ session, conn->log_wrlsn_cond, did_work, NULL);
}
/*
* On close we need to do this one more time because there could
@@ -840,10 +836,9 @@ __log_server(void *arg)
}
/* Wait until the next event. */
-
__wt_epoch(session, &start);
- __wt_cond_auto_wait_signal(session,
- conn->log_cond, did_work, &signalled);
+ __wt_cond_auto_wait_signal(
+ session, conn->log_cond, did_work, NULL, &signalled);
__wt_epoch(session, &now);
timediff = WT_TIMEDIFF_MS(now, start);
}
@@ -904,10 +899,8 @@ __wt_logmgr_create(WT_SESSION_IMPL *session, const char *cfg[])
WT_INIT_LSN(&log->write_lsn);
WT_INIT_LSN(&log->write_start_lsn);
log->fileid = 0;
- WT_RET(__wt_cond_alloc(
- session, "log sync", false, &log->log_sync_cond));
- WT_RET(__wt_cond_alloc(
- session, "log write", false, &log->log_write_cond));
+ WT_RET(__wt_cond_alloc(session, "log sync", &log->log_sync_cond));
+ WT_RET(__wt_cond_alloc(session, "log write", &log->log_write_cond));
WT_RET(__wt_log_open(session));
WT_RET(__wt_log_slot_init(session));
@@ -930,6 +923,8 @@ __wt_logmgr_open(WT_SESSION_IMPL *session)
if (!FLD_ISSET(conn->log_flags, WT_CONN_LOG_ENABLED))
return (0);
+ F_SET(conn, WT_CONN_LOG_SERVER_RUN);
+
/*
* Start the log close thread. It is not configurable.
* If logging is enabled, this thread runs.
@@ -937,8 +932,8 @@ __wt_logmgr_open(WT_SESSION_IMPL *session)
session_flags = WT_SESSION_NO_DATA_HANDLES;
WT_RET(__wt_open_internal_session(conn,
"log-close-server", false, session_flags, &conn->log_file_session));
- WT_RET(__wt_cond_alloc(conn->log_file_session,
- "log close server", false, &conn->log_file_cond));
+ WT_RET(__wt_cond_alloc(
+ conn->log_file_session, "log close server", &conn->log_file_cond));
/*
* Start the log file close thread.
@@ -954,8 +949,7 @@ __wt_logmgr_open(WT_SESSION_IMPL *session)
WT_RET(__wt_open_internal_session(conn, "log-wrlsn-server",
false, session_flags, &conn->log_wrlsn_session));
WT_RET(__wt_cond_auto_alloc(conn->log_wrlsn_session,
- "log write lsn server", false, 10000, WT_MILLION,
- &conn->log_wrlsn_cond));
+ "log write lsn server", 10000, WT_MILLION, &conn->log_wrlsn_cond));
WT_RET(__wt_thread_create(conn->log_wrlsn_session,
&conn->log_wrlsn_tid, __log_wrlsn_server, conn->log_wrlsn_session));
conn->log_wrlsn_tid_set = true;
@@ -969,13 +963,13 @@ __wt_logmgr_open(WT_SESSION_IMPL *session)
if (conn->log_session != NULL) {
WT_ASSERT(session, conn->log_cond != NULL);
WT_ASSERT(session, conn->log_tid_set == true);
- __wt_cond_auto_signal(session, conn->log_cond);
+ __wt_cond_signal(session, conn->log_cond);
} else {
/* The log server gets its own session. */
WT_RET(__wt_open_internal_session(conn,
"log-server", false, session_flags, &conn->log_session));
WT_RET(__wt_cond_auto_alloc(conn->log_session,
- "log server", false, 50000, WT_MILLION, &conn->log_cond));
+ "log server", 50000, WT_MILLION, &conn->log_cond));
/*
* Start the thread.
@@ -1001,6 +995,8 @@ __wt_logmgr_destroy(WT_SESSION_IMPL *session)
conn = S2C(session);
+ F_CLR(conn, WT_CONN_LOG_SERVER_RUN);
+
if (!FLD_ISSET(conn->log_flags, WT_CONN_LOG_ENABLED)) {
/*
* We always set up the log_path so printlog can work without
@@ -1011,7 +1007,7 @@ __wt_logmgr_destroy(WT_SESSION_IMPL *session)
return (0);
}
if (conn->log_tid_set) {
- __wt_cond_auto_signal(session, conn->log_cond);
+ __wt_cond_signal(session, conn->log_cond);
WT_TRET(__wt_thread_join(session, conn->log_tid));
conn->log_tid_set = false;
}
@@ -1026,7 +1022,7 @@ __wt_logmgr_destroy(WT_SESSION_IMPL *session)
conn->log_file_session = NULL;
}
if (conn->log_wrlsn_tid_set) {
- __wt_cond_auto_signal(session, conn->log_wrlsn_cond);
+ __wt_cond_signal(session, conn->log_wrlsn_cond);
WT_TRET(__wt_thread_join(session, conn->log_wrlsn_tid));
conn->log_wrlsn_tid_set = false;
}
@@ -1047,9 +1043,9 @@ __wt_logmgr_destroy(WT_SESSION_IMPL *session)
}
/* Destroy the condition variables now that all threads are stopped */
- WT_TRET(__wt_cond_auto_destroy(session, &conn->log_cond));
+ WT_TRET(__wt_cond_destroy(session, &conn->log_cond));
WT_TRET(__wt_cond_destroy(session, &conn->log_file_cond));
- WT_TRET(__wt_cond_auto_destroy(session, &conn->log_wrlsn_cond));
+ WT_TRET(__wt_cond_destroy(session, &conn->log_wrlsn_cond));
WT_TRET(__wt_cond_destroy(session, &conn->log->log_sync_cond));
WT_TRET(__wt_cond_destroy(session, &conn->log->log_write_cond));
diff --git a/src/conn/conn_open.c b/src/conn/conn_open.c
index f8029f2c728..5b20377d437 100644
--- a/src/conn/conn_open.c
+++ b/src/conn/conn_open.c
@@ -25,7 +25,7 @@ __wt_connection_open(WT_CONNECTION_IMPL *conn, const char *cfg[])
* Tell internal server threads to run: this must be set before opening
* any sessions.
*/
- F_SET(conn, WT_CONN_SERVER_RUN | WT_CONN_LOG_SERVER_RUN);
+ F_SET(conn, WT_CONN_SERVER_RUN);
/* WT_SESSION_IMPL array. */
WT_RET(__wt_calloc(session,
@@ -100,8 +100,12 @@ __wt_connection_close(WT_CONNECTION_IMPL *conn)
__wt_yield();
}
- /* Clear any pending async ops. */
+ /*
+ * Clear any pending async operations and shut down the async worker
+ * threads and system before closing LSM.
+ */
WT_TRET(__wt_async_flush(session));
+ WT_TRET(__wt_async_destroy(session));
/*
* Shut down server threads other than the eviction server, which is
@@ -110,14 +114,14 @@ __wt_connection_close(WT_CONNECTION_IMPL *conn)
* exit before files are closed.
*/
F_CLR(conn, WT_CONN_SERVER_RUN);
- WT_TRET(__wt_async_destroy(session));
WT_TRET(__wt_lsm_manager_destroy(session));
- WT_TRET(__wt_sweep_destroy(session));
F_SET(conn, WT_CONN_CLOSING);
-
WT_TRET(__wt_checkpoint_server_destroy(session));
WT_TRET(__wt_statlog_destroy(session, true));
+ WT_TRET(__wt_sweep_destroy(session));
+
+ /* The eviction server is shut down last. */
WT_TRET(__wt_evict_destroy(session));
/* Shut down the lookaside table, after all eviction is complete. */
@@ -126,7 +130,7 @@ __wt_connection_close(WT_CONNECTION_IMPL *conn)
/* Close open data handles. */
WT_TRET(__wt_conn_dhandle_discard(session));
- /* Shut down metadata tracking, required before creating tables. */
+ /* Shut down metadata tracking. */
WT_TRET(__wt_meta_track_destroy(session));
/*
@@ -140,7 +144,6 @@ __wt_connection_close(WT_CONNECTION_IMPL *conn)
FLD_ISSET(conn->log_flags, WT_CONN_LOG_RECOVER_DONE))
WT_TRET(__wt_txn_checkpoint_log(
session, true, WT_TXN_LOG_CKPT_STOP, NULL));
- F_CLR(conn, WT_CONN_LOG_SERVER_RUN);
WT_TRET(__wt_logmgr_destroy(session));
/* Free memory for collators, compressors, data sources. */
diff --git a/src/conn/conn_stat.c b/src/conn/conn_stat.c
index 3bcdfd7ecb1..31dc9c45992 100644
--- a/src/conn/conn_stat.c
+++ b/src/conn/conn_stat.c
@@ -485,8 +485,7 @@ __statlog_on_close(WT_SESSION_IMPL *session)
if (!FLD_ISSET(conn->stat_flags, WT_STAT_ON_CLOSE))
return (0);
- if (F_ISSET(conn, WT_CONN_SERVER_RUN) &&
- F_ISSET(conn, WT_CONN_SERVER_STATISTICS))
+ if (F_ISSET(conn, WT_CONN_SERVER_STATISTICS))
WT_RET_MSG(session, EINVAL,
"Attempt to log statistics while a server is running");
@@ -498,6 +497,16 @@ err: __wt_scr_free(session, &tmp);
}
/*
+ * __statlog_server_run_chk --
+ * Check to decide if the statistics log server should continue running.
+ */
+static bool
+__statlog_server_run_chk(WT_SESSION_IMPL *session)
+{
+ return (F_ISSET(S2C(session), WT_CONN_SERVER_STATISTICS));
+}
+
+/*
* __statlog_server --
* The statistics server thread.
*/
@@ -525,10 +534,14 @@ __statlog_server(void *arg)
WT_ERR(__wt_buf_init(session, &path, strlen(conn->stat_path) + 128));
WT_ERR(__wt_buf_init(session, &tmp, strlen(conn->stat_path) + 128));
- while (F_ISSET(conn, WT_CONN_SERVER_RUN) &&
- F_ISSET(conn, WT_CONN_SERVER_STATISTICS)) {
+ for (;;) {
/* Wait until the next event. */
- __wt_cond_wait(session, conn->stat_cond, conn->stat_usecs);
+ __wt_cond_wait(session, conn->stat_cond,
+ conn->stat_usecs, __statlog_server_run_chk);
+
+ /* Check if we're quitting or being reconfigured. */
+ if (!__statlog_server_run_chk(session))
+ break;
if (WT_STAT_ENABLED(session))
WT_ERR(__statlog_log_one(session, &path, &tmp));
@@ -563,7 +576,7 @@ __statlog_start(WT_CONNECTION_IMPL *conn)
session = conn->stat_session;
WT_RET(__wt_cond_alloc(
- session, "statistics log server", false, &conn->stat_cond));
+ session, "statistics log server", &conn->stat_cond));
/*
* Start the thread.
diff --git a/src/conn/conn_sweep.c b/src/conn/conn_sweep.c
index 7d5cb7d7c72..f9b7305c7d8 100644
--- a/src/conn/conn_sweep.c
+++ b/src/conn/conn_sweep.c
@@ -246,6 +246,16 @@ __sweep_remove_handles(WT_SESSION_IMPL *session)
}
/*
+ * __sweep_server_run_chk --
+ * Check to decide if the checkpoint server should continue running.
+ */
+static bool
+__sweep_server_run_chk(WT_SESSION_IMPL *session)
+{
+ return (F_ISSET(S2C(session), WT_CONN_SERVER_SWEEP));
+}
+
+/*
* __sweep_server --
* The handle sweep server thread.
*/
@@ -266,11 +276,15 @@ __sweep_server(void *arg)
/*
* Sweep for dead and excess handles.
*/
- while (F_ISSET(conn, WT_CONN_SERVER_RUN) &&
- F_ISSET(conn, WT_CONN_SERVER_SWEEP)) {
+ for (;;) {
/* Wait until the next event. */
- __wt_cond_wait(session,
- conn->sweep_cond, conn->sweep_interval * WT_MILLION);
+ __wt_cond_wait(session, conn->sweep_cond,
+ conn->sweep_interval * WT_MILLION, __sweep_server_run_chk);
+
+ /* Check if we're quitting or being reconfigured. */
+ if (!__sweep_server_run_chk(session))
+ break;
+
__wt_seconds(session, &now);
WT_STAT_CONN_INCR(session, dh_sweeps);
@@ -390,7 +404,7 @@ __wt_sweep_create(WT_SESSION_IMPL *session)
session = conn->sweep_session;
WT_RET(__wt_cond_alloc(
- session, "handle sweep server", false, &conn->sweep_cond));
+ session, "handle sweep server", &conn->sweep_cond));
WT_RET(__wt_thread_create(
session, &conn->sweep_tid, __sweep_server, session));
diff --git a/src/evict/evict_lru.c b/src/evict/evict_lru.c
index 0cf746f84eb..48ea1ccb02b 100644
--- a/src/evict/evict_lru.c
+++ b/src/evict/evict_lru.c
@@ -267,7 +267,7 @@ __wt_evict_server_wake(WT_SESSION_IMPL *session)
}
#endif
- __wt_cond_auto_signal(session, cache->evict_cond);
+ __wt_cond_signal(session, cache->evict_cond);
}
/*
@@ -311,9 +311,10 @@ __wt_evict_thread_run(WT_SESSION_IMPL *session, WT_THREAD *thread)
__wt_spin_unlock(session, &cache->evict_pass_lock);
WT_ERR(ret);
__wt_verbose(session, WT_VERB_EVICTSERVER, "sleeping");
+
/* Don't rely on signals: check periodically. */
__wt_cond_auto_wait(
- session, cache->evict_cond, did_work);
+ session, cache->evict_cond, did_work, NULL);
__wt_verbose(session, WT_VERB_EVICTSERVER, "waking");
} else
WT_ERR(__evict_lru_pages(session, false));
@@ -712,8 +713,8 @@ __evict_pass(WT_SESSION_IMPL *session)
*/
WT_STAT_CONN_INCR(session,
cache_eviction_server_slept);
- __wt_cond_wait(
- session, cache->evict_cond, WT_THOUSAND);
+ __wt_cond_wait(session,
+ cache->evict_cond, WT_THOUSAND, NULL);
continue;
}
@@ -1102,7 +1103,8 @@ __evict_lru_pages(WT_SESSION_IMPL *session, bool is_server)
/* If a worker thread found the queue empty, pause. */
if (ret == WT_NOTFOUND && !is_server &&
F_ISSET(S2C(session), WT_CONN_EVICTION_RUN))
- __wt_cond_wait(session, conn->evict_threads.wait_cond, 10000);
+ __wt_cond_wait(
+ session, conn->evict_threads.wait_cond, 10000, NULL);
return (ret == WT_NOTFOUND ? 0 : ret);
}
@@ -2102,8 +2104,8 @@ __wt_cache_eviction_worker(WT_SESSION_IMPL *session, bool busy, u_int pct_full)
break;
case WT_NOTFOUND:
/* Allow the queue to re-populate before retrying. */
- __wt_cond_wait(
- session, conn->evict_threads.wait_cond, 10000);
+ __wt_cond_wait(session,
+ conn->evict_threads.wait_cond, 10000, NULL);
cache->app_waits++;
break;
default:
diff --git a/src/include/extern.h b/src/include/extern.h
index 88fb8823930..eb2f9a0e784 100644
--- a/src/include/extern.h
+++ b/src/include/extern.h
@@ -613,11 +613,9 @@ extern void __wt_session_close_cache(WT_SESSION_IMPL *session) WT_GCC_FUNC_DECL_
extern int __wt_session_get_btree(WT_SESSION_IMPL *session, const char *uri, const char *checkpoint, const char *cfg[], uint32_t flags) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
extern int __wt_session_lock_checkpoint(WT_SESSION_IMPL *session, const char *checkpoint) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
extern int __wt_salvage(WT_SESSION_IMPL *session, const char *cfg[]) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
-extern int __wt_cond_auto_alloc( WT_SESSION_IMPL *session, const char *name, bool is_signalled, uint64_t min, uint64_t max, WT_CONDVAR **condp) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
-extern void __wt_cond_auto_signal(WT_SESSION_IMPL *session, WT_CONDVAR *cond) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
-extern void __wt_cond_auto_wait_signal( WT_SESSION_IMPL *session, WT_CONDVAR *cond, bool progress, bool *signalled) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
-extern void __wt_cond_auto_wait( WT_SESSION_IMPL *session, WT_CONDVAR *cond, bool progress) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
-extern int __wt_cond_auto_destroy(WT_SESSION_IMPL *session, WT_CONDVAR **condp) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
+extern int __wt_cond_auto_alloc(WT_SESSION_IMPL *session, const char *name, uint64_t min, uint64_t max, WT_CONDVAR **condp) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
+extern void __wt_cond_auto_wait_signal(WT_SESSION_IMPL *session, WT_CONDVAR *cond, bool progress, bool (*run_func)(WT_SESSION_IMPL *), bool *signalled) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
+extern void __wt_cond_auto_wait(WT_SESSION_IMPL *session, WT_CONDVAR *cond, bool progress, bool (*run_func)(WT_SESSION_IMPL *)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
extern int __wt_decrypt(WT_SESSION_IMPL *session, WT_ENCRYPTOR *encryptor, size_t skip, WT_ITEM *in, WT_ITEM *out) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
extern int __wt_encrypt(WT_SESSION_IMPL *session, WT_KEYED_ENCRYPTOR *kencryptor, size_t skip, WT_ITEM *in, WT_ITEM *out) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
extern void __wt_encrypt_size(WT_SESSION_IMPL *session, WT_KEYED_ENCRYPTOR *kencryptor, size_t incoming_size, size_t *sizep) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
diff --git a/src/include/extern_posix.h b/src/include/extern_posix.h
index 5acb7b0ed27..fed7835ada1 100644
--- a/src/include/extern_posix.h
+++ b/src/include/extern_posix.h
@@ -12,8 +12,8 @@ extern int __wt_posix_map(WT_FILE_HANDLE *fh, WT_SESSION *wt_session, void *mapp
extern int __wt_posix_map_preload(WT_FILE_HANDLE *fh, WT_SESSION *wt_session, const void *map, size_t length, void *mapped_cookie) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
extern int __wt_posix_map_discard(WT_FILE_HANDLE *fh, WT_SESSION *wt_session, void *map, size_t length, void *mapped_cookie) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
extern int __wt_posix_unmap(WT_FILE_HANDLE *fh, WT_SESSION *wt_session, void *mapped_region, size_t len, void *mapped_cookie) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
-extern int __wt_cond_alloc(WT_SESSION_IMPL *session, const char *name, bool is_signalled, WT_CONDVAR **condp) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
-extern void __wt_cond_wait_signal( WT_SESSION_IMPL *session, WT_CONDVAR *cond, uint64_t usecs, bool *signalled) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
+extern int __wt_cond_alloc(WT_SESSION_IMPL *session, const char *name, WT_CONDVAR **condp) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
+extern void __wt_cond_wait_signal(WT_SESSION_IMPL *session, WT_CONDVAR *cond, uint64_t usecs, bool (*run_func)(WT_SESSION_IMPL *), bool *signalled) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
extern void __wt_cond_signal(WT_SESSION_IMPL *session, WT_CONDVAR *cond) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
extern int __wt_cond_destroy(WT_SESSION_IMPL *session, WT_CONDVAR **condp) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
extern int __wt_once(void (*init_routine)(void)) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
diff --git a/src/include/extern_win.h b/src/include/extern_win.h
index 11b45f11304..0bfc821c7a6 100644
--- a/src/include/extern_win.h
+++ b/src/include/extern_win.h
@@ -10,8 +10,8 @@ extern int __wt_os_win(WT_SESSION_IMPL *session) WT_GCC_FUNC_DECL_ATTRIBUTE((war
extern int __wt_getenv(WT_SESSION_IMPL *session, const char *variable, const char **envp) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
extern int __wt_win_map(WT_FILE_HANDLE *file_handle, WT_SESSION *wt_session, void *mapped_regionp, size_t *lenp, void *mapped_cookiep) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
extern int __wt_win_unmap(WT_FILE_HANDLE *file_handle, WT_SESSION *wt_session, void *mapped_region, size_t length, void *mapped_cookie) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
-extern int __wt_cond_alloc(WT_SESSION_IMPL *session, const char *name, bool is_signalled, WT_CONDVAR **condp) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
-extern void __wt_cond_wait_signal( WT_SESSION_IMPL *session, WT_CONDVAR *cond, uint64_t usecs, bool *signalled) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
+extern int __wt_cond_alloc(WT_SESSION_IMPL *session, const char *name, WT_CONDVAR **condp) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
+extern void __wt_cond_wait_signal(WT_SESSION_IMPL *session, WT_CONDVAR *cond, uint64_t usecs, bool (*run_func)(WT_SESSION_IMPL *), bool *signalled) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
extern void __wt_cond_signal(WT_SESSION_IMPL *session, WT_CONDVAR *cond) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
extern int __wt_cond_destroy(WT_SESSION_IMPL *session, WT_CONDVAR **condp) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
extern int __wt_once(void (*init_routine)(void)) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
diff --git a/src/include/misc.i b/src/include/misc.i
index f36be32d6a2..d5692a3f9cf 100644
--- a/src/include/misc.i
+++ b/src/include/misc.i
@@ -11,11 +11,12 @@
* Wait on a mutex, optionally timing out.
*/
static inline void
-__wt_cond_wait(WT_SESSION_IMPL *session, WT_CONDVAR *cond, uint64_t usecs)
+__wt_cond_wait(WT_SESSION_IMPL *session,
+ WT_CONDVAR *cond, uint64_t usecs, bool (*run_func)(WT_SESSION_IMPL *))
{
bool notused;
- __wt_cond_wait_signal(session, cond, usecs, &notused);
+ __wt_cond_wait_signal(session, cond, usecs, run_func, &notused);
}
/*
diff --git a/src/include/mutex.h b/src/include/mutex.h
index 727a690bb1c..06b8c4a3304 100644
--- a/src/include/mutex.h
+++ b/src/include/mutex.h
@@ -21,8 +21,8 @@ struct __wt_condvar {
int waiters; /* Numbers of waiters, or
-1 if signalled with no waiters. */
/*
- * The following fields are only used for automatically adjusting
- * condition variables. They could be in a separate structure.
+ * The following fields are used for automatically adjusting condition
+ * variable wait times.
*/
uint64_t min_wait; /* Minimum wait duration */
uint64_t max_wait; /* Maximum wait duration */
diff --git a/src/include/wiredtiger.in b/src/include/wiredtiger.in
index 03bff7cd04f..f05d3d4ab55 100644
--- a/src/include/wiredtiger.in
+++ b/src/include/wiredtiger.in
@@ -2362,7 +2362,7 @@ struct __wt_connection {
* @config{exclusive, fail if the database already exists\, generally used with
* the \c create option., a boolean flag; default \c false.}
* @config{extensions, list of shared library extensions to load (using dlopen).
- * Any values specified to an library extension are passed to
+ * Any values specified to a library extension are passed to
* WT_CONNECTION::load_extension as the \c config parameter (for example\,
* <code>extensions=(/path/ext.so={entry=my_entry})</code>)., a list of strings;
* default empty.}
diff --git a/src/log/log.c b/src/log/log.c
index da500a74e87..614ae1a9b6d 100644
--- a/src/log/log.c
+++ b/src/log/log.c
@@ -43,11 +43,11 @@ __log_wait_for_earlier_slot(WT_SESSION_IMPL *session, WT_LOGSLOT *slot)
*/
if (F_ISSET(session, WT_SESSION_LOCKED_SLOT))
__wt_spin_unlock(session, &log->log_slot_lock);
- __wt_cond_auto_signal(session, conn->log_wrlsn_cond);
+ __wt_cond_signal(session, conn->log_wrlsn_cond);
if (++yield_count < WT_THOUSAND)
__wt_yield();
else
- __wt_cond_wait(session, log->log_write_cond, 200);
+ __wt_cond_wait(session, log->log_write_cond, 200, NULL);
if (F_ISSET(session, WT_SESSION_LOCKED_SLOT))
__wt_spin_lock(session, &log->log_slot_lock);
}
@@ -89,7 +89,7 @@ __wt_log_ckpt(WT_SESSION_IMPL *session, WT_LSN *ckp_lsn)
log = conn->log;
log->ckpt_lsn = *ckp_lsn;
if (conn->log_cond != NULL)
- __wt_cond_auto_signal(session, conn->log_cond);
+ __wt_cond_signal(session, conn->log_cond);
}
/*
@@ -170,7 +170,7 @@ __wt_log_force_sync(WT_SESSION_IMPL *session, WT_LSN *min_lsn)
*/
while (log->sync_lsn.l.file < min_lsn->l.file) {
__wt_cond_signal(session, S2C(session)->log_file_cond);
- __wt_cond_wait(session, log->log_sync_cond, 10000);
+ __wt_cond_wait(session, log->log_sync_cond, 10000, NULL);
}
__wt_spin_lock(session, &log->log_sync_lock);
WT_ASSERT(session, log->log_dir_fh != NULL);
@@ -915,7 +915,7 @@ __log_newfile(WT_SESSION_IMPL *session, bool conn_open, bool *created)
else {
WT_STAT_CONN_INCR(session, log_prealloc_missed);
if (conn->log_cond != NULL)
- __wt_cond_auto_signal(
+ __wt_cond_signal(
session, conn->log_cond);
}
}
@@ -1490,7 +1490,8 @@ __wt_log_release(WT_SESSION_IMPL *session, WT_LOGSLOT *slot, bool *freep)
*/
if (log->sync_lsn.l.file < slot->slot_end_lsn.l.file ||
__wt_spin_trylock(session, &log->log_sync_lock) != 0) {
- __wt_cond_wait(session, log->log_sync_cond, 10000);
+ __wt_cond_wait(
+ session, log->log_sync_cond, 10000, NULL);
continue;
}
locked = true;
@@ -2160,7 +2161,7 @@ __log_write_internal(WT_SESSION_IMPL *session, WT_ITEM *record, WT_LSN *lsnp,
* XXX I've seen times when conditions are NULL.
*/
if (conn->log_cond != NULL) {
- __wt_cond_auto_signal(session, conn->log_cond);
+ __wt_cond_signal(session, conn->log_cond);
__wt_yield();
} else
WT_ERR(__wt_log_force_write(session, 1, NULL));
@@ -2169,12 +2170,14 @@ __log_write_internal(WT_SESSION_IMPL *session, WT_ITEM *record, WT_LSN *lsnp,
/* Wait for our writes to reach the OS */
while (__wt_log_cmp(&log->write_lsn, &lsn) <= 0 &&
myslot.slot->slot_error == 0)
- __wt_cond_wait(session, log->log_write_cond, 10000);
+ __wt_cond_wait(
+ session, log->log_write_cond, 10000, NULL);
} else if (LF_ISSET(WT_LOG_FSYNC)) {
/* Wait for our writes to reach disk */
while (__wt_log_cmp(&log->sync_lsn, &lsn) <= 0 &&
myslot.slot->slot_error == 0)
- __wt_cond_wait(session, log->log_sync_cond, 10000);
+ __wt_cond_wait(
+ session, log->log_sync_cond, 10000, NULL);
}
/*
diff --git a/src/log/log_slot.c b/src/log/log_slot.c
index d70c0d689be..d6e692f8c51 100644
--- a/src/log/log_slot.c
+++ b/src/log/log_slot.c
@@ -349,7 +349,7 @@ __wt_log_slot_new(WT_SESSION_IMPL *session)
/*
* If we didn't find any free slots signal the worker thread.
*/
- __wt_cond_auto_signal(session, conn->log_wrlsn_cond);
+ __wt_cond_signal(session, conn->log_wrlsn_cond);
__wt_yield();
#ifdef HAVE_DIAGNOSTIC
++count;
diff --git a/src/lsm/lsm_worker.c b/src/lsm/lsm_worker.c
index b0d0758775d..ffa00c0a5e7 100644
--- a/src/lsm/lsm_worker.c
+++ b/src/lsm/lsm_worker.c
@@ -154,7 +154,7 @@ __lsm_worker(void *arg)
/* Don't busy wait if there was any work to do. */
if (!progress) {
- __wt_cond_wait(session, cookie->work_cond, 10000);
+ __wt_cond_wait(session, cookie->work_cond, 10000, NULL);
continue;
}
}
diff --git a/src/os_posix/os_mtx_cond.c b/src/os_posix/os_mtx_cond.c
index be8b1abda31..a5ee78f9e3e 100644
--- a/src/os_posix/os_mtx_cond.c
+++ b/src/os_posix/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;
WT_DECL_RET;
@@ -27,7 +26,7 @@ __wt_cond_alloc(WT_SESSION_IMPL *session,
WT_ERR(pthread_cond_init(&cond->cond, NULL));
cond->name = name;
- cond->waiters = is_signalled ? -1 : 0;
+ cond->waiters = 0;
*condp = cond;
return (0);
@@ -42,8 +41,8 @@ err: __wt_free(session, cond);
* 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)
{
struct timespec ts;
WT_DECL_RET;
@@ -62,6 +61,23 @@ __wt_cond_wait_signal(
WT_ERR(pthread_mutex_lock(&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) {
__wt_epoch(session, &ts);
ts.tv_sec += (time_t)
@@ -81,7 +97,7 @@ __wt_cond_wait_signal(
ret == ETIME ||
#endif
ret == ETIMEDOUT) {
- *signalled = false;
+skipping: *signalled = false;
ret = 0;
}
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/session/session_api.c b/src/session/session_api.c
index fcbfa8809b3..71626e098cb 100644
--- a/src/session/session_api.c
+++ b/src/session/session_api.c
@@ -1489,6 +1489,20 @@ err: API_END_RET(session, ret);
}
/*
+ * __transaction_sync_run_chk --
+ * Check to decide if the transaction sync call should continue running.
+ */
+static bool
+__transaction_sync_run_chk(WT_SESSION_IMPL *session)
+{
+ WT_CONNECTION_IMPL *conn;
+
+ conn = S2C(session);
+
+ return (FLD_ISSET(conn->flags, WT_CONN_LOG_SERVER_RUN));
+}
+
+/*
* __session_transaction_sync --
* WT_SESSION->transaction_sync method.
*/
@@ -1502,7 +1516,7 @@ __session_transaction_sync(WT_SESSION *wt_session, const char *config)
WT_SESSION_IMPL *session;
WT_TXN *txn;
struct timespec now, start;
- uint64_t timeout_ms, waited_ms;
+ uint64_t remaining_usec, timeout_ms, waited_ms;
bool forever;
session = (WT_SESSION_IMPL *)wt_session;
@@ -1555,22 +1569,20 @@ __session_transaction_sync(WT_SESSION *wt_session, const char *config)
__wt_epoch(session, &start);
/*
* Keep checking the LSNs until we find it is stable or we reach
- * our timeout.
+ * our timeout, or there's some other reason to quit.
*/
while (__wt_log_cmp(&session->bg_sync_lsn, &log->sync_lsn) > 0) {
+ if (!__transaction_sync_run_chk(session))
+ WT_ERR(ETIMEDOUT);
+
__wt_cond_signal(session, conn->log_file_cond);
__wt_epoch(session, &now);
waited_ms = WT_TIMEDIFF_MS(now, start);
- if (forever || waited_ms < timeout_ms)
- /*
- * Note, we will wait an increasing amount of time
- * each iteration, likely doubling. Also note that
- * the function timeout value is in usecs (we are
- * computing the wait time in msecs and passing that
- * in, unchanged, as the usecs to wait).
- */
- __wt_cond_wait(session, log->log_sync_cond, waited_ms);
- else
+ if (forever || waited_ms < timeout_ms) {
+ remaining_usec = (timeout_ms - waited_ms) * WT_THOUSAND;
+ __wt_cond_wait(session, log->log_sync_cond,
+ remaining_usec, __transaction_sync_run_chk);
+ } else
WT_ERR(ETIMEDOUT);
}
@@ -1825,7 +1837,7 @@ __open_session(WT_CONNECTION_IMPL *conn,
session_ret->name = NULL;
session_ret->id = i;
- WT_ERR(__wt_cond_alloc(session, "session", false, &session_ret->cond));
+ WT_ERR(__wt_cond_alloc(session, "session", &session_ret->cond));
if (WT_SESSION_FIRST_USE(session_ret))
__wt_random_init(&session_ret->rnd);
diff --git a/src/support/cond_auto.c b/src/support/cond_auto.c
index a3ae67f5baa..600e5eab0ff 100644
--- a/src/support/cond_auto.c
+++ b/src/support/cond_auto.c
@@ -1,29 +1,9 @@
/*-
- * Public Domain 2014-2016 MongoDB, Inc.
- * Public Domain 2008-2014 WiredTiger, Inc.
+ * Copyright (c) 2014-2016 MongoDB, Inc.
+ * Copyright (c) 2008-2014 WiredTiger, Inc.
+ * All rights reserved.
*
- * This is free and unencumbered software released into the public domain.
- *
- * Anyone is free to copy, modify, publish, use, compile, sell, or
- * distribute this software, either in source code form or as a compiled
- * binary, for any purpose, commercial or non-commercial, and by any
- * means.
- *
- * In jurisdictions that recognize copyright laws, the author or authors
- * of this software dedicate any and all copyright interest in the
- * software to the public domain. We make this dedication for the benefit
- * of the public at large and to the detriment of our heirs and
- * successors. We intend this dedication to be an overt act of
- * relinquishment in perpetuity of all present and future rights to this
- * software under copyright law.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
+ * See the file LICENSE for redistribution information.
*/
#include "wt_internal.h"
@@ -38,13 +18,12 @@
* Allocate and initialize an automatically adjusting condition variable.
*/
int
-__wt_cond_auto_alloc(
- WT_SESSION_IMPL *session, const char *name,
- bool is_signalled, uint64_t min, uint64_t max, WT_CONDVAR **condp)
+__wt_cond_auto_alloc(WT_SESSION_IMPL *session,
+ const char *name, uint64_t min, uint64_t max, WT_CONDVAR **condp)
{
WT_CONDVAR *cond;
- WT_RET(__wt_cond_alloc(session, name, is_signalled, condp));
+ WT_RET(__wt_cond_alloc(session, name, condp));
cond = *condp;
cond->min_wait = min;
@@ -55,33 +34,19 @@ __wt_cond_auto_alloc(
}
/*
- * __wt_cond_auto_signal --
- * Signal a condition variable.
- */
-void
-__wt_cond_auto_signal(WT_SESSION_IMPL *session, WT_CONDVAR *cond)
-{
-
- WT_ASSERT(session, cond->min_wait != 0);
- __wt_cond_signal(session, cond);
-}
-
-/*
* __wt_cond_auto_wait_signal --
* Wait on a mutex, optionally timing out. If we get it before the time
* out period expires, let the caller know.
- * TODO: Can this version of the API be removed, now that we have the
- * auto adjusting condition variables?
*/
void
-__wt_cond_auto_wait_signal(
- WT_SESSION_IMPL *session, WT_CONDVAR *cond, bool progress, bool *signalled)
+__wt_cond_auto_wait_signal(WT_SESSION_IMPL *session, WT_CONDVAR *cond,
+ bool progress, bool (*run_func)(WT_SESSION_IMPL *), bool *signalled)
{
uint64_t delta;
/*
* Catch cases where this function is called with a condition variable
- * that was initialized non-auto.
+ * that wasn't initialized to do automatic adjustments.
*/
WT_ASSERT(session, cond->min_wait != 0);
@@ -94,7 +59,8 @@ __wt_cond_auto_wait_signal(
cond->max_wait, cond->prev_wait + delta);
}
- __wt_cond_wait_signal(session, cond, cond->prev_wait, signalled);
+ __wt_cond_wait_signal(
+ session, cond, cond->prev_wait, run_func, signalled);
if (progress || *signalled)
WT_STAT_CONN_INCR(session, cond_auto_wait_reset);
@@ -108,24 +74,10 @@ __wt_cond_auto_wait_signal(
* out period expires, let the caller know.
*/
void
-__wt_cond_auto_wait(
- WT_SESSION_IMPL *session, WT_CONDVAR *cond, bool progress)
+__wt_cond_auto_wait(WT_SESSION_IMPL *session,
+ WT_CONDVAR *cond, bool progress, bool (*run_func)(WT_SESSION_IMPL *))
{
- bool signalled;
-
- /*
- * Call the signal version so the wait period is reset if the
- * condition is woken explicitly.
- */
- __wt_cond_auto_wait_signal(session, cond, progress, &signalled);
-}
+ bool notused;
-/*
- * __wt_cond_auto_destroy --
- * Destroy a condition variable.
- */
-int
-__wt_cond_auto_destroy(WT_SESSION_IMPL *session, WT_CONDVAR **condp)
-{
- return (__wt_cond_destroy(session, condp));
+ __wt_cond_auto_wait_signal(session, cond, progress, run_func, &notused);
}
diff --git a/src/support/thread_group.c b/src/support/thread_group.c
index beb143e63e2..2b4b7ad4e61 100644
--- a/src/support/thread_group.c
+++ b/src/support/thread_group.c
@@ -259,7 +259,7 @@ __wt_thread_group_create(
__wt_rwlock_init(session, &group->lock);
WT_ERR(__wt_cond_alloc(
- session, "Thread group cond", false, &group->wait_cond));
+ session, "thread group cond", &group->wait_cond));
cond_alloced = true;
__wt_writelock(session, &group->lock);