diff --git a/src/btree/bt_split.c b/src/btree/bt_split.c
index d58851a2a..711f204ca 100644
--- a/src/btree/bt_split.c
+++ b/src/btree/bt_split.c
@@ -57,17 +57,6 @@ static int
 __split_safe_free(WT_SESSION_IMPL *session,
     uint64_t split_gen, bool exclusive, void *p, size_t s)
 {
-	/* We should only call safe free if we aren't pinning the memory. */
-	WT_ASSERT(session,
-	    __wt_session_gen(session, WT_GEN_SPLIT) != split_gen);
-
-	/*
-	 * We have swapped something in a page: if we don't have exclusive
-	 * access, check whether there are other threads in the same tree.
-	 */
-	if (!exclusive && __wt_gen_oldest(session, WT_GEN_SPLIT) > split_gen)
-		exclusive = true;
-
 	if (exclusive) {
 		__wt_overwrite_and_free_len(session, p, s);
 		return (0);
@@ -366,8 +355,7 @@ __split_ref_prepare(
 		WT_PAGE_UNLOCK(session, child);
 
 #ifdef HAVE_DIAGNOSTIC
-		WT_WITH_PAGE_INDEX(session,
-		    __split_verify_intl_key_order(session, child));
+		__split_verify_intl_key_order(session, child);
 #endif
 	}
 }
@@ -406,12 +394,7 @@ __split_root(WT_SESSION_IMPL *session, WT_PAGE *root)
 	WT_RET(__wt_page_modify_init(session, root));
 	__wt_page_modify_set(session, root);
 
-	/*
-	 * Our caller is holding the root page locked to single-thread splits,
-	 * which means we can safely look at the page's index without setting a
-	 * split generation.
-	 */
-	pindex = WT_INTL_INDEX_GET_SAFE(root);
+	WT_INTL_INDEX_GET(session, root, pindex);
 
 	/*
 	 * Decide how many child pages to create, then calculate the standard
@@ -538,9 +521,10 @@ __split_root(WT_SESSION_IMPL *session, WT_PAGE *root)
 	    WT_TIMING_STRESS_SPLIT_RACE_2, 100 * WT_THOUSAND);
 
 	/*
-	 * Get a generation for this split, mark the root page.  This must be
-	 * after the new index is swapped into place in order to know that no
-	 * readers are looking at the old index.
+	 * Mark the root page with the current generation and start a new
+	 * generation.  This must be after the new index is swapped into place
+	 * in order to know that when the old generation is empty, no readers
+	 * are looking at the old index.
 	 *
 	 * Note: as the root page cannot currently be evicted, the root split
 	 * generation isn't ever used. That said, it future proofs eviction
@@ -553,9 +537,7 @@ __split_root(WT_SESSION_IMPL *session, WT_PAGE *root)
 	root->pg_intl_split_gen = split_gen;
 
 #ifdef HAVE_DIAGNOSTIC
-	WT_WITH_PAGE_INDEX(session,
-	    ret = __split_verify_root(session, root));
-	WT_ERR(ret);
+	WT_ERR(__split_verify_root(session, root));
 #endif
 
 	/* The split is complete and verified, ignore benign errors. */
@@ -633,11 +615,7 @@ __split_parent(WT_SESSION_IMPL *session, WT_REF *ref, WT_REF **ref_new,
 	WT_RET(__wt_page_modify_init(session, parent));
 	__wt_page_modify_set(session, parent);
 
-	/*
-	 * We've locked the parent, which means it cannot split (which is the
-	 * only reason to worry about split generation values).
-	 */
-	pindex = WT_INTL_INDEX_GET_SAFE(parent);
+	WT_INTL_INDEX_GET(session, parent, pindex);
 	parent_entries = pindex->entries;
 
 	/*
@@ -650,8 +628,8 @@ __split_parent(WT_SESSION_IMPL *session, WT_REF *ref, WT_REF **ref_new,
 	for (deleted_entries = 0, i = 0; i < parent_entries; ++i) {
 		next_ref = pindex->index[i];
 		WT_ASSERT(session, next_ref->state != WT_REF_SPLIT);
-		if ((discard && next_ref == ref) ||
-		    (next_ref->state == WT_REF_DELETED &&
+		if ((discard && next_ref == ref) || (next_ref != ref &&
+		    next_ref->state == WT_REF_DELETED &&
 		    __wt_delete_page_skip(session, next_ref, true) &&
 		    __wt_atomic_casv32(
 		    &next_ref->state, WT_REF_DELETED, WT_REF_SPLIT))) {
@@ -738,41 +716,33 @@ __split_parent(WT_SESSION_IMPL *session, WT_REF *ref, WT_REF **ref_new,
 	__page_split_timing_stress(session,
 	    WT_TIMING_STRESS_SPLIT_RACE_4, 100 * WT_THOUSAND);
 
-	/*
-	 * Get a generation for this split, mark the page.  This must be after
-	 * the new index is swapped into place in order to know that no readers
-	 * are looking at the old index.
-	 *
-	 * Getting a new split generation implies a full barrier, no additional
-	 * barrier is needed.
-	 */
-	split_gen = __wt_gen_next(session, WT_GEN_SPLIT);
-	parent->pg_intl_split_gen = split_gen;
-
 	/*
 	 * If discarding the page's original WT_REF field, reset it to split.
 	 * Threads cursoring through the tree were blocked because that WT_REF
 	 * state was set to locked. Changing the locked state to split unblocks
 	 * those threads and causes them to re-calculate their position based
 	 * on the just-updated parent page's index.
+	 *
+	 * Set the discarded WT_REF state to split, ensuring we don't race with
+	 * any discard of the WT_REF deleted fields.
 	 */
-	if (discard) {
-		/*
-		 * Set the discarded WT_REF state to split, ensuring we don't
-		 * race with any discard of the WT_REF deleted fields.
-		 */
+	if (discard)
 		WT_PUBLISH(ref->state, WT_REF_SPLIT);
 
-		/*
-		 * Push out the change: not required for correctness, but stops
-		 * threads spinning on incorrect page references.
-		 */
-		WT_FULL_BARRIER();
-	}
+	/*
+	 * Mark the parent page with the current generation and start a new
+	 * generation.  This must be after the new index is swapped into place
+	 * in order to know that when the old generation is empty, no readers
+	 * are looking at the old index.
+	 *
+	 * Getting a new split generation implies a full barrier, no additional
+	 * barrier is needed.
+	 */
+	split_gen = __wt_gen_next(session, WT_GEN_SPLIT);
+	parent->pg_intl_split_gen = split_gen;
 
 #ifdef HAVE_DIAGNOSTIC
-	WT_WITH_PAGE_INDEX(session,
-	    __split_verify_intl_key_order(session, parent));
+	__split_verify_intl_key_order(session, parent);
 #endif
 
 	/* The split is complete and verified, ignore benign errors. */
@@ -939,12 +909,7 @@ __split_internal(WT_SESSION_IMPL *session, WT_PAGE *parent, WT_PAGE *page)
 	page_decr = page_incr = parent_incr = 0;
 	complete = WT_ERR_RETURN;
 
-	/*
-	 * Our caller is holding the page locked to single-thread splits, which
-	 * means we can safely look at the page's index without setting a split
-	 * generation.
-	 */
-	pindex = WT_INTL_INDEX_GET_SAFE(page);
+	WT_INTL_INDEX_GET(session, page, pindex);
 
 	/*
 	 * Decide how many child pages to create, then calculate the standard
@@ -1096,9 +1061,10 @@ __split_internal(WT_SESSION_IMPL *session, WT_PAGE *parent, WT_PAGE *page)
 	    WT_TIMING_STRESS_SPLIT_RACE_6, 100 * WT_THOUSAND);
 
 	/*
-	 * Get a generation for this split, mark the parent page.  This must be
-	 * after the new index is swapped into place in order to know that no
-	 * readers are looking at the old index.
+	 * Mark the parent page with the current generation and start a new
+	 * generation.  This must be after the new index is swapped into place
+	 * in order to know that when the old generation is empty, no readers
+	 * are looking at the old index.
 	 *
 	 * Getting a new split generation implies a full barrier, no additional
 	 * barrier is needed.
@@ -1107,10 +1073,8 @@ __split_internal(WT_SESSION_IMPL *session, WT_PAGE *parent, WT_PAGE *page)
 	page->pg_intl_split_gen = split_gen;
 
 #ifdef HAVE_DIAGNOSTIC
-	WT_WITH_PAGE_INDEX(session,
-	    __split_verify_intl_key_order(session, parent));
-	WT_WITH_PAGE_INDEX(session,
-	    __split_verify_intl_key_order(session, page));
+	__split_verify_intl_key_order(session, parent);
+	__split_verify_intl_key_order(session, page);
 #endif
 
 	/* The split is complete and verified, ignore benign errors. */
@@ -1260,12 +1224,7 @@ __split_internal_should_split(WT_SESSION_IMPL *session, WT_REF *ref)
 	btree = S2BT(session);
 	page = ref->page;
 
-	/*
-	 * Our caller is holding the parent page locked to single-thread splits,
-	 * which means we can safely look at the page's index without setting a
-	 * split generation.
-	 */
-	pindex = WT_INTL_INDEX_GET_SAFE(page);
+	WT_INTL_INDEX_GET(session, page, pindex);
 
 	/* Sanity check for a reasonable number of on-page keys. */
 	if (pindex->entries < 100)
@@ -2060,6 +2019,7 @@ __wt_split_insert(WT_SESSION_IMPL *session, WT_REF *ref)
 	 * split.
 	 */
 	WT_WITH_PAGE_INDEX(session, ret = __split_insert_lock(session, ref));
+	__wt_stash_discard(session);
 	return (ret);
 }
 
@@ -2169,6 +2129,7 @@ __wt_split_multi(WT_SESSION_IMPL *session, WT_REF *ref, int closing)
 	 */
 	WT_WITH_PAGE_INDEX(session,
 	    ret = __split_multi_lock(session, ref, closing));
+	__wt_stash_discard(session);
 	return (ret);
 }
 
@@ -2206,6 +2167,7 @@ __wt_split_reverse(WT_SESSION_IMPL *session, WT_REF *ref)
 	 * split.
 	 */
 	WT_WITH_PAGE_INDEX(session, ret = __split_reverse(session, ref));
+	__wt_stash_discard(session);
 	return (ret);
 }
 
diff --git a/src/btree/row_srch.c b/src/btree/row_srch.c
index 20acda8a1..ff4bcd2c1 100644
--- a/src/btree/row_srch.c
+++ b/src/btree/row_srch.c
@@ -293,6 +293,10 @@ restart:	/*
 		if (page->type != WT_PAGE_ROW_INT)
 			break;
 
+		uint64_t my_gen = __wt_session_gen(session, WT_GEN_SPLIT);
+		WT_UNUSED(my_gen);
+		uint64_t split_gen = page->pg_intl_split_gen;
+		WT_UNUSED(split_gen);
 		WT_INTL_INDEX_GET(session, page, pindex);
 
 		/*
diff --git a/src/evict/evict_page.c b/src/evict/evict_page.c
index 93add0596..395d88c3e 100644
--- a/src/evict/evict_page.c
+++ b/src/evict/evict_page.c
@@ -135,7 +135,7 @@ __wt_evict(WT_SESSION_IMPL *session, WT_REF *ref, bool closing)
 	 * previous eviction generation (which must be as low as the current
 	 * generation), untouched.
 	 */
-	if (__wt_session_gen(session, WT_GEN_EVICT) == 0) {
+	if (!__wt_session_gen_active(session, WT_GEN_EVICT)) {
 		local_gen = true;
 		__wt_session_gen_enter(session, WT_GEN_EVICT);
 	}
diff --git a/src/include/btmem.h b/src/include/btmem.h
index 9752737ef..5788b6682 100644
--- a/src/include/btmem.h
+++ b/src/include/btmem.h
@@ -553,7 +553,7 @@ struct __wt_page {
 	((page)->u.intl.__index)
 #define	WT_INTL_INDEX_GET(session, page, pindex) do {			\
 	WT_ASSERT(session,						\
-	    __wt_session_gen(session, WT_GEN_SPLIT) != 0);		\
+	    __wt_session_gen_active(session, WT_GEN_SPLIT));		\
 	(pindex) = WT_INTL_INDEX_GET_SAFE(page);			\
 } while (0)
 #define	WT_INTL_INDEX_SET(page, v) do {					\
@@ -1239,13 +1239,13 @@ struct __wt_insert_head {
  * potentially free it.
  */
 #define	WT_ENTER_PAGE_INDEX(session) do {				\
-	uint64_t __prev_split_gen =					\
-	    __wt_session_gen(session, WT_GEN_SPLIT);			\
-	if (__prev_split_gen == 0)					\
+	bool __local_gen_enter =					\
+	    !__wt_session_gen_active(session, WT_GEN_SPLIT);		\
+	if (__local_gen_enter)						\
 		__wt_session_gen_enter(session, WT_GEN_SPLIT);
 
 #define	WT_LEAVE_PAGE_INDEX(session)					\
-	if (__prev_split_gen == 0)					\
+	if (__local_gen_enter)						\
 		__wt_session_gen_leave(session, WT_GEN_SPLIT);		\
 	} while (0)
 
diff --git a/src/include/extern.h b/src/include/extern.h
index f4498c884..d889dda04 100644
--- a/src/include/extern.h
+++ b/src/include/extern.h
@@ -704,6 +704,7 @@ extern uint64_t __wt_gen_next_drain(WT_SESSION_IMPL *session, int which);
 extern void __wt_gen_drain(WT_SESSION_IMPL *session, int which, uint64_t generation);
 extern uint64_t __wt_gen_oldest(WT_SESSION_IMPL *session, int which);
 extern uint64_t __wt_session_gen(WT_SESSION_IMPL *session, int which);
+extern bool __wt_session_gen_active(WT_SESSION_IMPL *session, int which) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result));
 extern void __wt_session_gen_enter(WT_SESSION_IMPL *session, int which);
 extern void __wt_session_gen_leave(WT_SESSION_IMPL *session, int which);
 extern void __wt_stash_discard(WT_SESSION_IMPL *session);
diff --git a/src/include/session.h b/src/include/session.h
index 39e24cfa8..1ed8e2638 100644
--- a/src/include/session.h
+++ b/src/include/session.h
@@ -230,7 +230,8 @@ struct __wt_session_impl {
 		struct __wt_stash {
 			void	*p;	/* Memory, length */
 			size_t	 len;
-			uint64_t gen;	/* Generation */
+			uint64_t gen;	/* Safe to free when all readers reach
+					   this generation */
 		} *list;
 		size_t  cnt;		/* Array entries */
 		size_t  alloc;		/* Allocated bytes */
diff --git a/src/support/generation.c b/src/support/generation.c
index b962bca73..849449901 100644
--- a/src/support/generation.c
+++ b/src/support/generation.c
@@ -157,9 +157,9 @@ __wt_gen_oldest(WT_SESSION_IMPL *session, int which)
 	 * all of the sessions that could have been active when we started our
 	 * check.
 	 */
+	WT_ORDERED_READ(oldest, conn->generations[which]);
 	WT_ORDERED_READ(session_cnt, conn->session_cnt);
-	for (oldest = conn->generations[which] + 1,
-	    s = conn->sessions, i = 0; i < session_cnt; ++s, ++i) {
+	for (s = conn->sessions, i = 0; i < session_cnt; ++s, ++i) {
 		if (!s->active)
 			continue;
 
@@ -183,6 +183,16 @@ __wt_session_gen(WT_SESSION_IMPL *session, int which)
 	return (session->generations[which]);
 }
 
+/*
+ * __wt_session_gen_active --
+ *	Return whether the session has entered a generation.
+ */
+bool
+__wt_session_gen_active(WT_SESSION_IMPL *session, int which)
+{
+	return (__wt_session_gen(session, which) != 0);
+}
+
 /*
  * __wt_session_gen_enter --
  *	Publish a thread's resource generation.
@@ -191,10 +201,11 @@ 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.
+	 * Don't enter a generation if we're already in one, it will likely
+	 * result in code intended to be protected by a generation running
+	 * outside one.
 	 */
-	WT_ASSERT(session, session->generations[which] == 0);
+	WT_ASSERT(session, !__wt_session_gen_active(session, which));
 
 	/*
 	 * Assign the thread's resource generation and publish it, ensuring
@@ -205,7 +216,7 @@ __wt_session_gen_enter(WT_SESSION_IMPL *session, int which)
 	 */
 	do {
 		session->generations[which] = __wt_gen(session, which);
-		WT_WRITE_BARRIER();
+		WT_FULL_BARRIER();
 	} while (session->generations[which] != __wt_gen(session, which));
 }
 
@@ -234,18 +245,23 @@ __stash_discard(WT_SESSION_IMPL *session, int which)
 	WT_SESSION_STASH *session_stash;
 	WT_STASH *stash;
 	size_t i;
-	uint64_t oldest;
+	uint64_t newest, oldest;
+	uint64_t first_free = 0;
+	char msg[4096] = "";
+	size_t msg_len = 0;
 
 	conn = S2C(session);
 	session_stash = &session->stash[which];
 
-	/* Get the resource's oldest generation. */
+	/* Get the resource's newest / oldest generations. */
+	newest = session_stash->list[session_stash->cnt - 1].gen;
 	oldest = __wt_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.
@@ -253,6 +269,9 @@ __stash_discard(WT_SESSION_IMPL *session, int which)
 		if (stash->gen >= oldest)
 			break;
 
+		if (first_free == 0)
+			first_free = stash->gen;
+
 		(void)__wt_atomic_sub64(&conn->stashed_bytes, stash->len);
 		(void)__wt_atomic_sub64(&conn->stashed_objects, 1);
 
@@ -260,6 +279,7 @@ __stash_discard(WT_SESSION_IMPL *session, int which)
 		 * It's a bad thing if another thread is in this memory after
 		 * we free it, make sure nothing good happens to that thread.
 		 */
+		msg_len += snprintf(msg + msg_len, sizeof(msg) - msg_len, " freed %p with gen %" PRIu64 "\n", stash->p, stash->gen);
 		__wt_overwrite_and_free_len(session, stash->p, stash->len);
 	}
 
@@ -271,6 +291,9 @@ __stash_discard(WT_SESSION_IMPL *session, int which)
 		if ((session_stash->cnt -= i) > 0)
 			memmove(session_stash->list, stash,
 			    session_stash->cnt * sizeof(*stash));
+
+	if (first_free != 0)
+		fprintf(stderr, "session %" PRIu32 " stash_discard gen %" PRIu64 " with oldest %" PRIu64 ", newest %" PRIu64 "\n%s", session->id, first_free, oldest, newest, msg);
 }
 
 /*
@@ -300,11 +323,19 @@ __wt_stash_add(WT_SESSION_IMPL *session,
 {
 	WT_CONNECTION_IMPL *conn;
 	WT_SESSION_STASH *session_stash;
-	WT_STASH *stash;
+	WT_STASH *prev, *stash;
 
 	conn = S2C(session);
 	session_stash = &session->stash[which];
 
+	/*
+	 * The freeing session must have an active generation, and that the
+	 * generation when the free becomes safe is after the one the session
+	 * entered.
+	 */
+	WT_ASSERT(session, __wt_session_gen_active(session, which) &&
+	    generation > __wt_session_gen(session, which));
+
 	/* Grow the list as necessary. */
 	WT_RET(__wt_realloc_def(session, &session_stash->alloc,
 	    session_stash->cnt + 1, &session_stash->list));
@@ -320,13 +351,14 @@ __wt_stash_add(WT_SESSION_IMPL *session,
 	stash->len = len;
 	stash->gen = generation;
 
+	if (session_stash->cnt > 1) {
+		prev = stash - 1;
+		WT_ASSERT(session, stash->gen >= prev->gen);
+	}
+
 	(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);
 }
 
diff --git a/src/support/hazard.c b/src/support/hazard.c
index 815c876f4..4213f8ace 100644
--- a/src/support/hazard.c
+++ b/src/support/hazard.c
@@ -19,9 +19,9 @@ static void __hazard_dump(WT_SESSION_IMPL *);
 static int
 hazard_grow(WT_SESSION_IMPL *session)
 {
+	WT_DECL_RET;
 	WT_HAZARD *nhazard;
 	size_t size;
-	uint64_t hazard_gen;
 	void *ohazard;
 
 	/*
@@ -51,13 +51,14 @@ hazard_grow(WT_SESSION_IMPL *session)
 	/*
 	 * Threads using the hazard pointer array from now on will use the new
 	 * one. Increment the hazard pointer generation number, and schedule a
-	 * future free of the old memory. Ignore any failure, leak the memory.
+	 * future free of the old memory.
 	 */
-	hazard_gen = __wt_gen_next(session, WT_GEN_HAZARD);
-	WT_IGNORE_RET(
-	    __wt_stash_add(session, WT_GEN_HAZARD, hazard_gen, ohazard, 0));
+	__wt_session_gen_enter(session, WT_GEN_HAZARD);
+	ret = __wt_stash_add(session, WT_GEN_HAZARD,
+	    __wt_gen_next(session, WT_GEN_HAZARD), ohazard, 0);
+	__wt_session_gen_leave(session, WT_GEN_HAZARD);
 
-	return (0);
+	return (ret);
 }
 
 /*
diff --git a/src/txn/txn.c b/src/txn/txn.c
index b2952cbec..9d966e66f 100644
--- a/src/txn/txn.c
+++ b/src/txn/txn.c
@@ -546,7 +546,7 @@ __wt_txn_release(WT_SESSION_IMPL *session)
 	__wt_logrec_free(session, &txn->logrec);
 
 	/* Discard any memory from the session's stash that we can. */
-	WT_ASSERT(session, __wt_session_gen(session, WT_GEN_SPLIT) == 0);
+	WT_ASSERT(session, !__wt_session_gen_active(session, WT_GEN_SPLIT));
 	__wt_stash_discard(session);
 
 	/*
