static void *
ops(void *arg)
{
    TINFO *tinfo;
    WT_CONNECTION *conn;
    WT_CURSOR *cursor, *cursor_insert;
    WT_SESSION *session;
    WT_ITEM key, value;
    uint64_t keyno, ckpt_op, reset_op, session_op;
    uint32_t op;
    uint8_t *keybuf, *valbuf;
    u_int np;
    int ckpt_available, dir, insert, intxn, notfound, readonly;
    char *ckpt_config, ckpt_name[64];

    tinfo = arg;

    conn = g.wts_conn;
    keybuf = valbuf = NULL;
    readonly = 0;			/* -Wconditional-uninitialized */

    /* Initialize the per-thread random number generator. */
    __wt_random_init(&tinfo->rnd);

    /* Set up the default key and value buffers. */
    key_gen_setup(&keybuf);
    val_gen_setup(&tinfo->rnd, &valbuf);

    /* Set the first operation where we'll create sessions and cursors. */
    session_op = 0;
    session = NULL;
    cursor = cursor_insert = NULL;

    /* Set the first operation where we'll perform checkpoint operations. */
    ckpt_op = g.c_checkpoints ? mmrand(&tinfo->rnd, 100, 10000) : 0;
    ckpt_available = 0;

    /* Set the first operation where we'll reset the session. */
    reset_op = mmrand(&tinfo->rnd, 100, 10000);

    for (intxn = 0; !tinfo->quit; ++tinfo->ops) {
        /*
         * We can't checkpoint or swap sessions/cursors while in a
         * transaction, resolve any running transaction.
         */
        if (intxn &&
                (tinfo->ops == ckpt_op || tinfo->ops == session_op)) {
            testutil_check(
                session->commit_transaction(session, NULL));
            ++tinfo->commit;
            intxn = 0;
        }

        /* Open up a new session and cursors. */
        if (tinfo->ops == session_op ||
                session == NULL || cursor == NULL) {
            if (session != NULL)
                testutil_check(session->close(session, NULL));

            testutil_check(conn->open_session(conn, NULL,
                                              ops_session_config(&tinfo->rnd), &session));

            /*
             * 10% of the time, perform some read-only operations
             * from a checkpoint.
             *
             * Skip that if we single-threaded and doing checks
             * against a Berkeley DB database, because that won't
             * work because the Berkeley DB database records won't
             * match the checkpoint.  Also skip if we are using
             * LSM, because it doesn't support reads from
             * checkpoints.
             */
            if (!SINGLETHREADED && !DATASOURCE("lsm") &&
                    ckpt_available && mmrand(&tinfo->rnd, 1, 10) == 1) {
                testutil_check(session->open_cursor(session,
                                                    g.uri, NULL, ckpt_name, &cursor));

                /* Pick the next session/cursor close/open. */
                session_op += 250;

                /* Checkpoints are read-only. */
                readonly = 1;
            } else {
                /*
                 * Open two cursors: one for overwriting and one
                 * for append (if it's a column-store).
                 *
                 * The reason is when testing with existing
                 * records, we don't track if a record was
                 * deleted or not, which means we must use
                 * cursor->insert with overwriting configured.
                 * But, in column-store files where we're
                 * testing with new, appended records, we don't
                 * want to have to specify the record number,
                 * which requires an append configuration.
                 */
                testutil_check(session->open_cursor(session,
                                                    g.uri, NULL, "overwrite", &cursor));
                if (g.type == FIX || g.type == VAR)
                    testutil_check(session->open_cursor(
                                       session, g.uri,
                                       NULL, "append", &cursor_insert));

                /* Pick the next session/cursor close/open. */
                session_op += mmrand(&tinfo->rnd, 100, 5000);

                /* Updates supported. */
                readonly = 0;
            }
        }

        /* Checkpoint the database. */
        if (tinfo->ops == ckpt_op && g.c_checkpoints) {
            /*
             * LSM and data-sources don't support named checkpoints,
             * and we can't drop a named checkpoint while there's a
             * cursor open on it, otherwise 20% of the time name the
             * checkpoint.
             */
            if (DATASOURCE("helium") || DATASOURCE("kvsbdb") ||
                    DATASOURCE("lsm") ||
                    readonly || mmrand(&tinfo->rnd, 1, 5) == 1)
                ckpt_config = NULL;
            else {
                (void)snprintf(ckpt_name, sizeof(ckpt_name),
                               "name=thread-%d", tinfo->id);
                ckpt_config = ckpt_name;
            }

            /* Named checkpoints lock out backups */
            if (ckpt_config != NULL)
                testutil_check(
                    pthread_rwlock_wrlock(&g.backup_lock));

            testutil_checkfmt(
                session->checkpoint(session, ckpt_config),
                "%s", ckpt_config == NULL ? "" : ckpt_config);

            if (ckpt_config != NULL)
                testutil_check(
                    pthread_rwlock_unlock(&g.backup_lock));

            /* Rephrase the checkpoint name for cursor open. */
            if (ckpt_config == NULL)
                strcpy(ckpt_name,
                       "checkpoint=WiredTigerCheckpoint");
            else
                (void)snprintf(ckpt_name, sizeof(ckpt_name),
                               "checkpoint=thread-%d", tinfo->id);
            ckpt_available = 1;

            /* Pick the next checkpoint operation. */
            ckpt_op += mmrand(&tinfo->rnd, 5000, 20000);
        }

        /*
         * Reset the session every now and then, just to make sure that
         * operation gets tested. Note the test is not for equality, we
         * have to do the reset outside of a transaction.
         */
        if (tinfo->ops > reset_op && !intxn) {
            testutil_check(session->reset(session));

            /* Pick the next reset operation. */
            reset_op += mmrand(&tinfo->rnd, 20000, 50000);
        }

        /*
         * If we're not single-threaded and we're not in a transaction,
         * start a transaction 20% of the time.
         */
        if (!SINGLETHREADED &&
                !intxn && mmrand(&tinfo->rnd, 1, 10) >= 8) {
            testutil_check(
                session->begin_transaction(session, NULL));
            intxn = 1;
        }

        insert = notfound = 0;

        keyno = mmrand(&tinfo->rnd, 1, (u_int)g.rows);
        key.data = keybuf;
        value.data = valbuf;

        /*
         * Perform some number of operations: the percentage of deletes,
         * inserts and writes are specified, reads are the rest.  The
         * percentages don't have to add up to 100, a high percentage
         * of deletes will mean fewer inserts and writes.  Modifications
         * are always followed by a read to confirm it worked.
         */
        op = readonly ? UINT32_MAX : mmrand(&tinfo->rnd, 1, 100);
        if (op < g.c_delete_pct) {
            ++tinfo->remove;
            switch (g.type) {
            case ROW:
                /*
                 * If deleting a non-existent record, the cursor
                 * won't be positioned, and so can't do a next.
                 */
                if (row_remove(cursor, &key, keyno, &notfound))
                    goto deadlock;
                break;
            case FIX:
            case VAR:
                if (col_remove(cursor, &key, keyno, &notfound))
                    goto deadlock;
                break;
            }
        } else if (op < g.c_delete_pct + g.c_insert_pct) {
            ++tinfo->insert;
            switch (g.type) {
            case ROW:
                if (row_insert(
                            tinfo, cursor, &key, &value, keyno))
                    goto deadlock;
                insert = 1;
                break;
            case FIX:
            case VAR:
                /*
                 * We can only append so many new records, if
                 * we've reached that limit, update a record
                 * instead of doing an insert.
                 */
                if (g.append_cnt >= g.append_max)
                    goto skip_insert;

                /* Insert, then reset the insert cursor. */
                if (col_insert(tinfo,
                               cursor_insert, &key, &value, &keyno))
                    goto deadlock;
                testutil_check(
                    cursor_insert->reset(cursor_insert));

                insert = 1;
                break;
            }
        } else if (
            op < g.c_delete_pct + g.c_insert_pct + g.c_write_pct) {
            ++tinfo->update;
            switch (g.type) {
            case ROW:
                if (row_update(
                            tinfo, cursor, &key, &value, keyno))
                    goto deadlock;
                break;
            case FIX:
            case VAR:
skip_insert:
                if (col_update(tinfo,
                               cursor, &key, &value, keyno))
                    goto deadlock;
                break;
            }
        } else {
            ++tinfo->search;
            if (read_row(cursor, &key, keyno, 0))
                if (intxn)
                    goto deadlock;
            continue;
        }

        /*
         * The cursor is positioned if we did any operation other than
         * insert, do a small number of next/prev cursor operations in
         * a random direction.
         */
        if (!insert) {
            dir = (int)mmrand(&tinfo->rnd, 0, 1);
            for (np = 0; np < mmrand(&tinfo->rnd, 1, 100); ++np) {
                if (notfound)
                    break;
                if (nextprev(cursor, dir, &notfound))
                    goto deadlock;
            }
        }

        /* Read to confirm the operation. */
        ++tinfo->search;
        if (read_row(cursor, &key, keyno, 0))
            goto deadlock;

        /* Reset the cursor: there is no reason to keep pages pinned. */
        testutil_check(cursor->reset(cursor));

        /*
         * If we're in the transaction, commit 40% of the time and
         * rollback 10% of the time.
         */
        if (intxn)
            switch (mmrand(&tinfo->rnd, 1, 10)) {
            case 1:
            case 2:
            case 3:
            case 4:		/* 40% */
                testutil_check(session->commit_transaction(
                                   session, NULL));
                ++tinfo->commit;
                intxn = 0;
                break;
            case 5:					/* 10% */
                if (0) {
deadlock:
                    ++tinfo->deadlock;
                }
                testutil_check(session->rollback_transaction(
                                   session, NULL));
                ++tinfo->rollback;
                intxn = 0;
                break;
            default:
                break;
            }
    }

    if (session != NULL)
        testutil_check(session->close(session, NULL));

    free(keybuf);
    free(valbuf);

    tinfo->state = TINFO_COMPLETE;
    return (NULL);
}
void WiredTigerSessionCache::releaseSession(WiredTigerSession* session) {
    invariant(session);
    invariant(session->cursorsOut() == 0);

    const int shuttingDown = _shuttingDown.fetchAndAdd(1);
    ON_BLOCK_EXIT([this] { _shuttingDown.fetchAndSubtract(1); });

    if (shuttingDown & kShuttingDownMask) {
        // There is a race condition with clean shutdown, where the storage engine is ripped from
        // underneath OperationContexts, which are not "active" (i.e., do not have any locks), but
        // are just about to delete the recovery unit. See SERVER-16031 for more information. Since
        // shutting down the WT_CONNECTION will close all WT_SESSIONS, we shouldn't also try to
        // directly close this session.
        session->_session = nullptr;  // Prevents calling _session->close() in destructor.
        delete session;
        return;
    }

    {
        WT_SESSION* ss = session->getSession();
        uint64_t range;
        // This checks that we are only caching idle sessions and not something which might hold
        // locks or otherwise prevent truncation.
        invariantWTOK(ss->transaction_pinned_range(ss, &range));
        invariant(range == 0);

        // Release resources in the session we're about to cache.
        // If we are using hybrid caching, then close cursors now and let them
        // be cached at the WiredTiger level.
        if (kWiredTigerCursorCacheSize.load() < 0) {
            session->closeAllCursors("");
        }
        invariantWTOK(ss->reset(ss));
    }

    // If the cursor epoch has moved on, close all cursors in the session.
    uint64_t cursorEpoch = _cursorEpoch.load();
    if (session->_getCursorEpoch() != cursorEpoch)
        session->closeCursorsForQueuedDrops(_engine);

    bool returnedToCache = false;
    uint64_t currentEpoch = _epoch.load();
    bool dropQueuedIdentsAtSessionEnd = session->isDropQueuedIdentsAtSessionEndAllowed();

    // Reset this session's flag for dropping queued idents to default, before returning it to
    // session cache.
    session->dropQueuedIdentsAtSessionEndAllowed(true);

    if (session->_getEpoch() == currentEpoch) {  // check outside of lock to reduce contention
        stdx::lock_guard<stdx::mutex> lock(_cacheLock);
        if (session->_getEpoch() == _epoch.load()) {  // recheck inside the lock for correctness
            returnedToCache = true;
            _sessions.push_back(session);
        }
    } else
        invariant(session->_getEpoch() < currentEpoch);

    if (!returnedToCache)
        delete session;

    if (dropQueuedIdentsAtSessionEnd && _engine && _engine->haveDropsQueued())
        _engine->dropSomeQueuedIdents();
}