summaryrefslogtreecommitdiff
path: root/src/third_party/wiredtiger/src/support/generation.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/third_party/wiredtiger/src/support/generation.c')
-rw-r--r--src/third_party/wiredtiger/src/support/generation.c522
1 files changed, 252 insertions, 270 deletions
diff --git a/src/third_party/wiredtiger/src/support/generation.c b/src/third_party/wiredtiger/src/support/generation.c
index a4278d6deab..0cd95d3b09a 100644
--- a/src/third_party/wiredtiger/src/support/generation.c
+++ b/src/third_party/wiredtiger/src/support/generation.c
@@ -9,391 +9,373 @@
#include "wt_internal.h"
/*
- * WiredTiger uses generations to manage various resources. Threads publish an
- * a current generation before accessing a resource, and clear it when they are
- * done. For example, a thread wanting to replace an object in memory replaces
- * the object and increments the object's generation. Once no threads have the
- * previous generation published, it is safe to discard the previous version of
- * the object.
+ * WiredTiger uses generations to manage various resources. Threads publish a current generation
+ * before accessing a resource, and clear it when they are done. For example, a thread wanting to
+ * replace an object in memory replaces the object and increments the object's generation. Once no
+ * threads have the previous generation published, it is safe to discard the previous version of the
+ * object.
*/
/*
* __wt_gen_init --
- * Initialize the connection's generations.
+ * Initialize the connection's generations.
*/
void
__wt_gen_init(WT_SESSION_IMPL *session)
{
- int i;
+ int i;
- /*
- * All generations start at 1, a session with a generation of 0 isn't
- * using the resource.
- */
- for (i = 0; i < WT_GENERATIONS; ++i)
- S2C(session)->generations[i] = 1;
+ /*
+ * All generations start at 1, a session with a generation of 0 isn't using the resource.
+ */
+ for (i = 0; i < WT_GENERATIONS; ++i)
+ S2C(session)->generations[i] = 1;
- /* Ensure threads see the state change. */
- WT_WRITE_BARRIER();
+ /* Ensure threads see the state change. */
+ WT_WRITE_BARRIER();
}
/*
* __wt_gen --
- * Return the resource's generation.
+ * Return the resource's generation.
*/
uint64_t
__wt_gen(WT_SESSION_IMPL *session, int which)
{
- return (S2C(session)->generations[which]);
+ return (S2C(session)->generations[which]);
}
/*
* __wt_gen_next --
- * Switch the resource to its next generation.
+ * Switch the resource to its next generation.
*/
uint64_t
__wt_gen_next(WT_SESSION_IMPL *session, int which)
{
- return (__wt_atomic_addv64(&S2C(session)->generations[which], 1));
+ return (__wt_atomic_addv64(&S2C(session)->generations[which], 1));
}
/*
* __wt_gen_next_drain --
- * Switch the resource to its next generation, then wait for it to drain.
+ * Switch the resource to its next generation, then wait for it to drain.
*/
uint64_t
__wt_gen_next_drain(WT_SESSION_IMPL *session, int which)
{
- uint64_t v;
+ uint64_t v;
- v = __wt_atomic_addv64(&S2C(session)->generations[which], 1);
+ v = __wt_atomic_addv64(&S2C(session)->generations[which], 1);
- __wt_gen_drain(session, which, v);
+ __wt_gen_drain(session, which, v);
- return (v);
+ return (v);
}
/*
* __wt_gen_drain --
- * Wait for the resource to drain.
+ * Wait for the resource to drain.
*/
void
__wt_gen_drain(WT_SESSION_IMPL *session, int which, uint64_t generation)
{
- WT_CONNECTION_IMPL *conn;
- WT_SESSION_IMPL *s;
- uint64_t v;
- uint32_t i, session_cnt;
- int pause_cnt;
-
- conn = S2C(session);
-
- /*
- * No lock is required because the session array is fixed size, but it
- * may contain inactive entries. We must review any active session, so
- * insert a read barrier after reading the active session count. That
- * way, no matter what sessions come or go, we'll check the slots for
- * all of the sessions that could have been active when we started our
- * check.
- */
- WT_ORDERED_READ(session_cnt, conn->session_cnt);
- for (pause_cnt = 0,
- s = conn->sessions, i = 0; i < session_cnt; ++s, ++i) {
- if (!s->active)
- continue;
-
- for (;;) {
- /* Ensure we only read the value once. */
- WT_ORDERED_READ(v, s->generations[which]);
-
- /*
- * The generation argument is newer than the limit. Wait
- * for threads in generations older than the argument
- * generation, threads in argument generations are OK.
- *
- * The thread's generation may be 0 (that is, not set).
- */
- if (v == 0 || v >= generation)
- break;
-
- /* If we're waiting on ourselves, we're deadlocked. */
- if (session == s) {
- WT_ASSERT(session, session != s);
- WT_IGNORE_RET(__wt_panic(session));
- }
-
- /*
- * The pause count is cumulative, quit spinning if it's
- * not doing us any good, that can happen in generations
- * that don't move quickly.
- */
- if (++pause_cnt < WT_THOUSAND)
- WT_PAUSE();
- else
- __wt_sleep(0, 10);
- }
- }
+ WT_CONNECTION_IMPL *conn;
+ WT_SESSION_IMPL *s;
+ uint64_t v;
+ uint32_t i, session_cnt;
+ int pause_cnt;
+
+ conn = S2C(session);
+
+ /*
+ * No lock is required because the session array is fixed size, but it may contain inactive
+ * entries. We must review any active session, so insert a read barrier after reading the active
+ * session count. That way, no matter what sessions come or go, we'll check the slots for all of
+ * the sessions that could have been active when we started our check.
+ */
+ WT_ORDERED_READ(session_cnt, conn->session_cnt);
+ for (pause_cnt = 0, s = conn->sessions, i = 0; i < session_cnt; ++s, ++i) {
+ if (!s->active)
+ continue;
+
+ for (;;) {
+ /* Ensure we only read the value once. */
+ WT_ORDERED_READ(v, s->generations[which]);
+
+ /*
+ * The generation argument is newer than the limit. Wait
+ * for threads in generations older than the argument
+ * generation, threads in argument generations are OK.
+ *
+ * The thread's generation may be 0 (that is, not set).
+ */
+ if (v == 0 || v >= generation)
+ break;
+
+ /* If we're waiting on ourselves, we're deadlocked. */
+ if (session == s) {
+ WT_ASSERT(session, session != s);
+ WT_IGNORE_RET(__wt_panic(session));
+ }
+
+ /*
+ * The pause count is cumulative, quit spinning if it's not doing us any good, that can
+ * happen in generations that don't move quickly.
+ */
+ if (++pause_cnt < WT_THOUSAND)
+ WT_PAUSE();
+ else
+ __wt_sleep(0, 10);
+ }
+ }
}
/*
* __gen_oldest --
- * Return the oldest generation in use for the resource.
+ * Return the oldest generation in use for the resource.
*/
static uint64_t
__gen_oldest(WT_SESSION_IMPL *session, int which)
{
- WT_CONNECTION_IMPL *conn;
- WT_SESSION_IMPL *s;
- uint64_t oldest, v;
- uint32_t i, session_cnt;
-
- conn = S2C(session);
-
- /*
- * No lock is required because the session array is fixed size, but it
- * may contain inactive entries. We must review any active session, so
- * insert a read barrier after reading the active session count. That
- * way, no matter what sessions come or go, we'll check the slots for
- * all of the sessions that could have been active when we started our
- * check.
- */
- WT_ORDERED_READ(session_cnt, conn->session_cnt);
- for (oldest = conn->generations[which] + 1,
- s = conn->sessions, i = 0; i < session_cnt; ++s, ++i) {
- if (!s->active)
- continue;
-
- /* Ensure we only read the value once. */
- WT_ORDERED_READ(v, s->generations[which]);
-
- if (v != 0 && v < oldest)
- oldest = v;
- }
-
- return (oldest);
+ WT_CONNECTION_IMPL *conn;
+ WT_SESSION_IMPL *s;
+ uint64_t oldest, v;
+ uint32_t i, session_cnt;
+
+ conn = S2C(session);
+
+ /*
+ * No lock is required because the session array is fixed size, but it may contain inactive
+ * entries. We must review any active session, so insert a read barrier after reading the active
+ * session count. That way, no matter what sessions come or go, we'll check the slots for all of
+ * the sessions that could have been active when we started our check.
+ */
+ WT_ORDERED_READ(session_cnt, conn->session_cnt);
+ for (oldest = conn->generations[which] + 1, s = conn->sessions, i = 0; i < session_cnt;
+ ++s, ++i) {
+ if (!s->active)
+ continue;
+
+ /* Ensure we only read the value once. */
+ WT_ORDERED_READ(v, s->generations[which]);
+
+ if (v != 0 && v < oldest)
+ oldest = v;
+ }
+
+ return (oldest);
}
/*
* __wt_gen_active --
- * Return if a specified generation is in use for the resource.
+ * Return if a specified generation is in use for the resource.
*/
bool
__wt_gen_active(WT_SESSION_IMPL *session, int which, uint64_t generation)
{
- WT_CONNECTION_IMPL *conn;
- WT_SESSION_IMPL *s;
- uint64_t v;
- uint32_t i, session_cnt;
-
- conn = S2C(session);
-
- /*
- * No lock is required because the session array is fixed size, but it
- * may contain inactive entries. We must review any active session, so
- * insert a read barrier after reading the active session count. That
- * way, no matter what sessions come or go, we'll check the slots for
- * all of the sessions that could have been active when we started our
- * check.
- */
- WT_ORDERED_READ(session_cnt, conn->session_cnt);
- for (s = conn->sessions, i = 0; i < session_cnt; ++s, ++i) {
- if (!s->active)
- continue;
-
- /* Ensure we only read the value once. */
- WT_ORDERED_READ(v, s->generations[which]);
-
- if (v != 0 && generation >= v)
- return (true);
- }
-
- return (false);
+ WT_CONNECTION_IMPL *conn;
+ WT_SESSION_IMPL *s;
+ uint64_t v;
+ uint32_t i, session_cnt;
+
+ conn = S2C(session);
+
+ /*
+ * No lock is required because the session array is fixed size, but it may contain inactive
+ * entries. We must review any active session, so insert a read barrier after reading the active
+ * session count. That way, no matter what sessions come or go, we'll check the slots for all of
+ * the sessions that could have been active when we started our check.
+ */
+ WT_ORDERED_READ(session_cnt, conn->session_cnt);
+ for (s = conn->sessions, i = 0; i < session_cnt; ++s, ++i) {
+ if (!s->active)
+ continue;
+
+ /* Ensure we only read the value once. */
+ WT_ORDERED_READ(v, s->generations[which]);
+
+ if (v != 0 && generation >= v)
+ return (true);
+ }
+
+ return (false);
}
/*
* __wt_session_gen --
- * Return the thread's resource generation.
+ * Return the thread's resource generation.
*/
uint64_t
__wt_session_gen(WT_SESSION_IMPL *session, int which)
{
- return (session->generations[which]);
+ return (session->generations[which]);
}
/*
* __wt_session_gen_enter --
- * Publish a thread's resource generation.
+ * Publish a thread's resource generation.
*/
void
__wt_session_gen_enter(WT_SESSION_IMPL *session, int which)
{
- /*
- * Don't enter a generation we're already in, it will likely result in
- * code intended to be protected by a generation running outside one.
- */
- WT_ASSERT(session, session->generations[which] == 0);
-
- /*
- * Assign the thread's resource generation and publish it, ensuring
- * threads waiting on a resource to drain see the new value. Check we
- * haven't raced with a generation update after publishing, we rely on
- * the published value not being missed when scanning for the oldest
- * generation.
- */
- do {
- session->generations[which] = __wt_gen(session, which);
- WT_WRITE_BARRIER();
- } while (session->generations[which] != __wt_gen(session, which));
+ /*
+ * Don't enter a generation we're already in, it will likely result in code intended to be
+ * protected by a generation running outside one.
+ */
+ WT_ASSERT(session, session->generations[which] == 0);
+
+ /*
+ * Assign the thread's resource generation and publish it, ensuring threads waiting on a
+ * resource to drain see the new value. Check we haven't raced with a generation update after
+ * publishing, we rely on the published value not being missed when scanning for the oldest
+ * generation.
+ */
+ do {
+ session->generations[which] = __wt_gen(session, which);
+ WT_WRITE_BARRIER();
+ } while (session->generations[which] != __wt_gen(session, which));
}
/*
* __wt_session_gen_leave --
- * Leave a thread's resource generation.
+ * Leave a thread's resource generation.
*/
void
__wt_session_gen_leave(WT_SESSION_IMPL *session, int which)
{
- /* Ensure writes made by this thread are visible. */
- WT_PUBLISH(session->generations[which], 0);
+ /* Ensure writes made by this thread are visible. */
+ WT_PUBLISH(session->generations[which], 0);
- /* Let threads waiting for the resource to drain proceed quickly. */
- WT_FULL_BARRIER();
+ /* Let threads waiting for the resource to drain proceed quickly. */
+ WT_FULL_BARRIER();
}
/*
* __stash_discard --
- * Discard any memory from a session stash that we can.
+ * Discard any memory from a session stash that we can.
*/
static void
__stash_discard(WT_SESSION_IMPL *session, int which)
{
- WT_CONNECTION_IMPL *conn;
- WT_SESSION_STASH *session_stash;
- WT_STASH *stash;
- size_t i;
- uint64_t oldest;
-
- conn = S2C(session);
- session_stash = &session->stash[which];
-
- /* Get the resource's oldest generation. */
- oldest = __gen_oldest(session, which);
-
- for (i = 0,
- stash = session_stash->list; i < session_stash->cnt; ++i, ++stash) {
- if (stash->p == NULL)
- continue;
- /*
- * The list is expected to be in generation-sorted order, quit
- * as soon as we find a object we can't discard.
- */
- if (stash->gen >= oldest)
- break;
-
- (void)__wt_atomic_sub64(&conn->stashed_bytes, stash->len);
- (void)__wt_atomic_sub64(&conn->stashed_objects, 1);
-
- /*
- * It's a bad thing if another thread is in this memory after
- * we free it, make sure nothing good happens to that thread.
- */
- __wt_overwrite_and_free_len(session, stash->p, stash->len);
- }
-
- /*
- * If there are enough free slots at the beginning of the list, shuffle
- * everything down.
- */
- if (i > 100 || i == session_stash->cnt)
- if ((session_stash->cnt -= i) > 0)
- memmove(session_stash->list, stash,
- session_stash->cnt * sizeof(*stash));
+ WT_CONNECTION_IMPL *conn;
+ WT_SESSION_STASH *session_stash;
+ WT_STASH *stash;
+ size_t i;
+ uint64_t oldest;
+
+ conn = S2C(session);
+ session_stash = &session->stash[which];
+
+ /* Get the resource's oldest generation. */
+ oldest = __gen_oldest(session, which);
+
+ for (i = 0, stash = session_stash->list; i < session_stash->cnt; ++i, ++stash) {
+ if (stash->p == NULL)
+ continue;
+ /*
+ * The list is expected to be in generation-sorted order, quit as soon as we find a object
+ * we can't discard.
+ */
+ if (stash->gen >= oldest)
+ break;
+
+ (void)__wt_atomic_sub64(&conn->stashed_bytes, stash->len);
+ (void)__wt_atomic_sub64(&conn->stashed_objects, 1);
+
+ /*
+ * It's a bad thing if another thread is in this memory after we free it, make sure nothing
+ * good happens to that thread.
+ */
+ __wt_overwrite_and_free_len(session, stash->p, stash->len);
+ }
+
+ /*
+ * If there are enough free slots at the beginning of the list, shuffle everything down.
+ */
+ if (i > 100 || i == session_stash->cnt)
+ if ((session_stash->cnt -= i) > 0)
+ memmove(session_stash->list, stash, session_stash->cnt * sizeof(*stash));
}
/*
* __wt_stash_discard --
- * Discard any memory from a session stash that we can.
+ * Discard any memory from a session stash that we can.
*/
void
__wt_stash_discard(WT_SESSION_IMPL *session)
{
- WT_SESSION_STASH *session_stash;
- int which;
-
- for (which = 0; which < WT_GENERATIONS; ++which) {
- session_stash = &session->stash[which];
- if (session_stash->cnt >= 1)
- __stash_discard(session, which);
- }
+ WT_SESSION_STASH *session_stash;
+ int which;
+
+ for (which = 0; which < WT_GENERATIONS; ++which) {
+ session_stash = &session->stash[which];
+ if (session_stash->cnt >= 1)
+ __stash_discard(session, which);
+ }
}
/*
* __wt_stash_add --
- * Add a new entry into a session stash list.
+ * Add a new entry into a session stash list.
*/
int
-__wt_stash_add(WT_SESSION_IMPL *session,
- int which, uint64_t generation, void *p, size_t len)
+__wt_stash_add(WT_SESSION_IMPL *session, int which, uint64_t generation, void *p, size_t len)
{
- WT_CONNECTION_IMPL *conn;
- WT_SESSION_STASH *session_stash;
- WT_STASH *stash;
-
- conn = S2C(session);
- session_stash = &session->stash[which];
-
- /* Grow the list as necessary. */
- WT_RET(__wt_realloc_def(session, &session_stash->alloc,
- session_stash->cnt + 1, &session_stash->list));
-
- /*
- * If no caller stashes memory with a lower generation than a previously
- * stashed object, the list is in generation-sorted order and discarding
- * can be faster. (An error won't cause problems other than we might not
- * discard stashed objects as soon as we otherwise would have.)
- */
- stash = session_stash->list + session_stash->cnt++;
- stash->p = p;
- stash->len = len;
- stash->gen = generation;
-
- (void)__wt_atomic_add64(&conn->stashed_bytes, len);
- (void)__wt_atomic_add64(&conn->stashed_objects, 1);
-
- /* See if we can free any previous entries. */
- if (session_stash->cnt > 1)
- __stash_discard(session, which);
-
- return (0);
+ WT_CONNECTION_IMPL *conn;
+ WT_SESSION_STASH *session_stash;
+ WT_STASH *stash;
+
+ conn = S2C(session);
+ session_stash = &session->stash[which];
+
+ /* Grow the list as necessary. */
+ WT_RET(__wt_realloc_def(
+ session, &session_stash->alloc, session_stash->cnt + 1, &session_stash->list));
+
+ /*
+ * If no caller stashes memory with a lower generation than a previously stashed object, the
+ * list is in generation-sorted order and discarding can be faster. (An error won't cause
+ * problems other than we might not discard stashed objects as soon as we otherwise would have.)
+ */
+ stash = session_stash->list + session_stash->cnt++;
+ stash->p = p;
+ stash->len = len;
+ stash->gen = generation;
+
+ (void)__wt_atomic_add64(&conn->stashed_bytes, len);
+ (void)__wt_atomic_add64(&conn->stashed_objects, 1);
+
+ /* See if we can free any previous entries. */
+ if (session_stash->cnt > 1)
+ __stash_discard(session, which);
+
+ return (0);
}
/*
* __wt_stash_discard_all --
- * Discard all memory from a session's stash.
+ * Discard all memory from a session's stash.
*/
void
__wt_stash_discard_all(WT_SESSION_IMPL *session_safe, WT_SESSION_IMPL *session)
{
- WT_SESSION_STASH *session_stash;
- WT_STASH *stash;
- size_t i;
- int which;
-
- /*
- * This function is called during WT_CONNECTION.close to discard any
- * memory that remains. For that reason, we take two WT_SESSION_IMPL
- * arguments: session_safe is still linked to the WT_CONNECTION and
- * can be safely used for calls to other WiredTiger functions, while
- * session is the WT_SESSION_IMPL we're cleaning up.
- */
- for (which = 0; which < WT_GENERATIONS; ++which) {
- session_stash = &session->stash[which];
-
- for (i = 0, stash = session_stash->list;
- i < session_stash->cnt; ++i, ++stash)
- __wt_free(session_safe, stash->p);
-
- __wt_free(session_safe, session_stash->list);
- session_stash->cnt = session_stash->alloc = 0;
- }
+ WT_SESSION_STASH *session_stash;
+ WT_STASH *stash;
+ size_t i;
+ int which;
+
+ /*
+ * This function is called during WT_CONNECTION.close to discard any memory that remains. For
+ * that reason, we take two WT_SESSION_IMPL arguments: session_safe is still linked to the
+ * WT_CONNECTION and can be safely used for calls to other WiredTiger functions, while session
+ * is the WT_SESSION_IMPL we're cleaning up.
+ */
+ for (which = 0; which < WT_GENERATIONS; ++which) {
+ session_stash = &session->stash[which];
+
+ for (i = 0, stash = session_stash->list; i < session_stash->cnt; ++i, ++stash)
+ __wt_free(session_safe, stash->p);
+
+ __wt_free(session_safe, session_stash->list);
+ session_stash->cnt = session_stash->alloc = 0;
+ }
}