summaryrefslogtreecommitdiff
path: root/src/third_party/wiredtiger/test/thread
diff options
context:
space:
mode:
Diffstat (limited to 'src/third_party/wiredtiger/test/thread')
-rw-r--r--src/third_party/wiredtiger/test/thread/Makefile.am12
-rw-r--r--src/third_party/wiredtiger/test/thread/file.c118
-rw-r--r--src/third_party/wiredtiger/test/thread/rw.c356
-rwxr-xr-xsrc/third_party/wiredtiger/test/thread/smoke.sh13
-rw-r--r--src/third_party/wiredtiger/test/thread/stats.c86
-rw-r--r--src/third_party/wiredtiger/test/thread/t.c304
-rw-r--r--src/third_party/wiredtiger/test/thread/thread.h61
7 files changed, 950 insertions, 0 deletions
diff --git a/src/third_party/wiredtiger/test/thread/Makefile.am b/src/third_party/wiredtiger/test/thread/Makefile.am
new file mode 100644
index 00000000000..a58f019b513
--- /dev/null
+++ b/src/third_party/wiredtiger/test/thread/Makefile.am
@@ -0,0 +1,12 @@
+AM_CPPFLAGS = -I$(top_builddir) -I$(top_srcdir)/src/include \
+ -I$(top_srcdir)/test/utility
+
+noinst_PROGRAMS = t
+t_LDADD = $(top_builddir)/libwiredtiger.la
+t_SOURCES = thread.h file.c rw.c stats.c t.c
+t_LDFLAGS = -static
+
+TESTS = smoke.sh
+
+clean-local:
+ rm -rf WiredTiger* wt.* *.core __stats
diff --git a/src/third_party/wiredtiger/test/thread/file.c b/src/third_party/wiredtiger/test/thread/file.c
new file mode 100644
index 00000000000..81ec6ad44f8
--- /dev/null
+++ b/src/third_party/wiredtiger/test/thread/file.c
@@ -0,0 +1,118 @@
+/*-
+ * Public Domain 2014-2016 MongoDB, Inc.
+ * Public Domain 2008-2014 WiredTiger, Inc.
+ *
+ * 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.
+ */
+
+#include "thread.h"
+
+static void
+file_create(const char *name)
+{
+ WT_SESSION *session;
+ int ret;
+ char *p, *end, config[128];
+
+ if ((ret = conn->open_session(conn, NULL, NULL, &session)) != 0)
+ testutil_die(ret, "conn.session");
+
+ p = config;
+ end = config + sizeof(config);
+ p += snprintf(p, (size_t)(end - p),
+ "key_format=%s,"
+ "internal_page_max=%d,"
+ "leaf_page_max=%d,",
+ ftype == ROW ? "u" : "r", 16 * 1024, 128 * 1024);
+ if (ftype == FIX)
+ (void)snprintf(p, (size_t)(end - p), ",value_format=3t");
+
+ if ((ret = session->create(session, name, config)) != 0)
+ if (ret != EEXIST)
+ testutil_die(ret, "session.create");
+
+ if ((ret = session->close(session, NULL)) != 0)
+ testutil_die(ret, "session.close");
+}
+
+void
+load(const char *name)
+{
+ WT_CURSOR *cursor;
+ WT_ITEM *key, _key, *value, _value;
+ WT_SESSION *session;
+ char keybuf[64], valuebuf[64];
+ u_int keyno;
+ int ret;
+
+ file_create(name);
+
+ if ((ret = conn->open_session(conn, NULL, NULL, &session)) != 0)
+ testutil_die(ret, "conn.session");
+
+ if ((ret =
+ session->open_cursor(session, name, NULL, "bulk", &cursor)) != 0)
+ testutil_die(ret, "cursor.open");
+
+ key = &_key;
+ value = &_value;
+ for (keyno = 1; keyno <= nkeys; ++keyno) {
+ if (ftype == ROW) {
+ key->data = keybuf;
+ key->size = (uint32_t)
+ snprintf(keybuf, sizeof(keybuf), "%017u", keyno);
+ cursor->set_key(cursor, key);
+ } else
+ cursor->set_key(cursor, (uint32_t)keyno);
+ value->data = valuebuf;
+ if (ftype == FIX)
+ cursor->set_value(cursor, 0x01);
+ else {
+ value->size = (uint32_t)
+ snprintf(valuebuf, sizeof(valuebuf), "%37u", keyno);
+ cursor->set_value(cursor, value);
+ }
+ if ((ret = cursor->insert(cursor)) != 0)
+ testutil_die(ret, "cursor.insert");
+ }
+
+ if ((ret = session->close(session, NULL)) != 0)
+ testutil_die(ret, "session.close");
+}
+
+void
+verify(const char *name)
+{
+ WT_SESSION *session;
+ int ret;
+
+ if ((ret = conn->open_session(conn, NULL, NULL, &session)) != 0)
+ testutil_die(ret, "conn.session");
+
+ if ((ret = session->verify(session, name, NULL)) != 0)
+ testutil_die(ret, "session.create");
+
+ if ((ret = session->close(session, NULL)) != 0)
+ testutil_die(ret, "session.close");
+}
diff --git a/src/third_party/wiredtiger/test/thread/rw.c b/src/third_party/wiredtiger/test/thread/rw.c
new file mode 100644
index 00000000000..913fa6e6c25
--- /dev/null
+++ b/src/third_party/wiredtiger/test/thread/rw.c
@@ -0,0 +1,356 @@
+/*-
+ * Public Domain 2014-2016 MongoDB, Inc.
+ * Public Domain 2008-2014 WiredTiger, Inc.
+ *
+ * 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.
+ */
+
+#include "thread.h"
+
+static void print_stats(u_int);
+static void *reader(void *);
+static void *writer(void *);
+
+typedef struct {
+ char *name; /* object name */
+ u_int nops; /* Thread op count */
+
+ WT_RAND_STATE rnd; /* RNG */
+
+ int remove; /* cursor.remove */
+ int update; /* cursor.update */
+ int reads; /* cursor.search */
+} INFO;
+
+static INFO *run_info;
+
+int
+rw_start(u_int readers, u_int writers)
+{
+ struct timeval start, stop;
+ double seconds;
+ pthread_t *tids;
+ u_int i, name_index, offset, total_nops;
+ int ret;
+ void *thread_ret;
+
+ tids = NULL; /* Keep GCC 4.1 happy. */
+ total_nops = 0;
+
+ /* Create per-thread structures. */
+ if ((run_info = calloc(
+ (size_t)(readers + writers), sizeof(*run_info))) == NULL ||
+ (tids = calloc((size_t)(readers + writers), sizeof(*tids))) == NULL)
+ testutil_die(errno, "calloc");
+
+ /* Create the files and load the initial records. */
+ for (i = 0; i < writers; ++i) {
+ if (i == 0 || multiple_files) {
+ if ((run_info[i].name = malloc(64)) == NULL)
+ testutil_die(errno, "malloc");
+ snprintf(run_info[i].name, 64, FNAME, i);
+
+ /* Vary by orders of magnitude */
+ if (vary_nops)
+ run_info[i].nops = WT_MAX(1000, max_nops >> i);
+ load(run_info[i].name);
+ } else
+ run_info[i].name = run_info[0].name;
+
+ /* Setup op count if not varying ops. */
+ if (run_info[i].nops == 0)
+ run_info[i].nops = max_nops;
+ total_nops += run_info[i].nops;
+ }
+
+ /* Setup the reader configurations */
+ for (i = 0; i < readers; ++i) {
+ offset = i + writers;
+ if (multiple_files) {
+ if ((run_info[offset].name = malloc(64)) == NULL)
+ testutil_die(errno, "malloc");
+ /* Have readers read from tables with writes. */
+ name_index = i % writers;
+ snprintf(
+ run_info[offset].name, 64, FNAME, name_index);
+
+ /* Vary by orders of magnitude */
+ if (vary_nops)
+ run_info[offset].nops =
+ WT_MAX(1000, max_nops >> name_index);
+ } else
+ run_info[offset].name = run_info[0].name;
+
+ /* Setup op count if not varying ops. */
+ if (run_info[offset].nops == 0)
+ run_info[offset].nops = max_nops;
+ total_nops += run_info[offset].nops;
+ }
+
+ (void)gettimeofday(&start, NULL);
+
+ /* Create threads. */
+ for (i = 0; i < readers; ++i)
+ if ((ret = pthread_create(
+ &tids[i], NULL, reader, (void *)(uintptr_t)i)) != 0)
+ testutil_die(ret, "pthread_create");
+ for (; i < readers + writers; ++i) {
+ if ((ret = pthread_create(
+ &tids[i], NULL, writer, (void *)(uintptr_t)i)) != 0)
+ testutil_die(ret, "pthread_create");
+ }
+
+ /* Wait for the threads. */
+ for (i = 0; i < readers + writers; ++i)
+ (void)pthread_join(tids[i], &thread_ret);
+
+ (void)gettimeofday(&stop, NULL);
+ seconds = (stop.tv_sec - start.tv_sec) +
+ (stop.tv_usec - start.tv_usec) * 1e-6;
+ fprintf(stderr, "timer: %.2lf seconds (%d ops/second)\n",
+ seconds, (int)(((readers + writers) * total_nops) / seconds));
+
+ /* Verify the files. */
+ for (i = 0; i < readers + writers; ++i) {
+ verify(run_info[i].name);
+ if (!multiple_files)
+ break;
+ }
+
+ /* Output run statistics. */
+ print_stats(readers + writers);
+
+ /* Free allocated memory. */
+ for (i = 0; i < readers + writers; ++i) {
+ free(run_info[i].name);
+ if (!multiple_files)
+ break;
+ }
+
+ free(run_info);
+ free(tids);
+
+ return (0);
+}
+
+/*
+ * reader_op --
+ * Read operation.
+ */
+static inline void
+reader_op(WT_SESSION *session, WT_CURSOR *cursor, INFO *s)
+{
+ WT_ITEM *key, _key;
+ u_int keyno;
+ int ret;
+ char keybuf[64];
+
+ key = &_key;
+
+ keyno = __wt_random(&s->rnd) % nkeys + 1;
+ if (ftype == ROW) {
+ key->data = keybuf;
+ key->size = (uint32_t)
+ snprintf(keybuf, sizeof(keybuf), "%017u", keyno);
+ cursor->set_key(cursor, key);
+ } else
+ cursor->set_key(cursor, (uint32_t)keyno);
+ if ((ret = cursor->search(cursor)) != 0 && ret != WT_NOTFOUND)
+ testutil_die(ret, "cursor.search");
+ if (log_print)
+ (void)session->log_printf(session,
+ "Reader Thread %p key %017u", pthread_self(), keyno);
+}
+
+/*
+ * reader --
+ * Reader thread start function.
+ */
+static void *
+reader(void *arg)
+{
+ INFO *s;
+ WT_CURSOR *cursor;
+ WT_SESSION *session;
+ u_int i;
+ int id, ret;
+ char tid[128];
+
+ id = (int)(uintptr_t)arg;
+ s = &run_info[id];
+ __wt_thread_id(tid, sizeof(tid));
+ __wt_random_init(&s->rnd);
+
+ printf(" read thread %2d starting: tid: %s, file: %s\n",
+ id, tid, s->name);
+
+ __wt_yield(); /* Get all the threads created. */
+
+ if (session_per_op) {
+ for (i = 0; i < s->nops; ++i, ++s->reads, __wt_yield()) {
+ if ((ret = conn->open_session(
+ conn, NULL, NULL, &session)) != 0)
+ testutil_die(ret, "conn.open_session");
+ if ((ret = session->open_cursor(
+ session, s->name, NULL, NULL, &cursor)) != 0)
+ testutil_die(ret, "session.open_cursor");
+ reader_op(session, cursor, s);
+ if ((ret = session->close(session, NULL)) != 0)
+ testutil_die(ret, "session.close");
+ }
+ } else {
+ if ((ret = conn->open_session(
+ conn, NULL, NULL, &session)) != 0)
+ testutil_die(ret, "conn.open_session");
+ if ((ret = session->open_cursor(
+ session, s->name, NULL, NULL, &cursor)) != 0)
+ testutil_die(ret, "session.open_cursor");
+ for (i = 0; i < s->nops; ++i, ++s->reads, __wt_yield())
+ reader_op(session, cursor, s);
+ if ((ret = session->close(session, NULL)) != 0)
+ testutil_die(ret, "session.close");
+ }
+
+ printf(" read thread %2d stopping: tid: %s, file: %s\n",
+ id, tid, s->name);
+
+ return (NULL);
+}
+
+/*
+ * writer_op --
+ * Write operation.
+ */
+static inline void
+writer_op(WT_SESSION *session, WT_CURSOR *cursor, INFO *s)
+{
+ WT_ITEM *key, _key, *value, _value;
+ u_int keyno;
+ int ret;
+ char keybuf[64], valuebuf[64];
+
+ key = &_key;
+ value = &_value;
+
+ keyno = __wt_random(&s->rnd) % nkeys + 1;
+ if (ftype == ROW) {
+ key->data = keybuf;
+ key->size = (uint32_t)
+ snprintf(keybuf, sizeof(keybuf), "%017u", keyno);
+ cursor->set_key(cursor, key);
+ } else
+ cursor->set_key(cursor, (uint32_t)keyno);
+ if (keyno % 5 == 0) {
+ ++s->remove;
+ if ((ret =
+ cursor->remove(cursor)) != 0 && ret != WT_NOTFOUND)
+ testutil_die(ret, "cursor.remove");
+ } else {
+ ++s->update;
+ value->data = valuebuf;
+ if (ftype == FIX)
+ cursor->set_value(cursor, 0x10);
+ else {
+ value->size = (uint32_t)snprintf(
+ valuebuf, sizeof(valuebuf), "XXX %37u", keyno);
+ cursor->set_value(cursor, value);
+ }
+ if ((ret = cursor->update(cursor)) != 0)
+ testutil_die(ret, "cursor.update");
+ }
+ if (log_print)
+ (void)session->log_printf(session,
+ "Writer Thread %p key %017u", pthread_self(), keyno);
+}
+
+/*
+ * writer --
+ * Writer thread start function.
+ */
+static void *
+writer(void *arg)
+{
+ INFO *s;
+ WT_CURSOR *cursor;
+ WT_SESSION *session;
+ u_int i;
+ int id, ret;
+ char tid[128];
+
+ id = (int)(uintptr_t)arg;
+ s = &run_info[id];
+ __wt_thread_id(tid, sizeof(tid));
+ __wt_random_init(&s->rnd);
+
+ printf("write thread %2d starting: tid: %s, file: %s\n",
+ id, tid, s->name);
+
+ __wt_yield(); /* Get all the threads created. */
+
+ if (session_per_op) {
+ for (i = 0; i < s->nops; ++i, __wt_yield()) {
+ if ((ret = conn->open_session(
+ conn, NULL, NULL, &session)) != 0)
+ testutil_die(ret, "conn.open_session");
+ if ((ret = session->open_cursor(
+ session, s->name, NULL, NULL, &cursor)) != 0)
+ testutil_die(ret, "session.open_cursor");
+ writer_op(session, cursor, s);
+ if ((ret = session->close(session, NULL)) != 0)
+ testutil_die(ret, "session.close");
+ }
+ } else {
+ if ((ret = conn->open_session(
+ conn, NULL, NULL, &session)) != 0)
+ testutil_die(ret, "conn.open_session");
+ if ((ret = session->open_cursor(
+ session, s->name, NULL, NULL, &cursor)) != 0)
+ testutil_die(ret, "session.open_cursor");
+ for (i = 0; i < s->nops; ++i, __wt_yield())
+ writer_op(session, cursor, s);
+ if ((ret = session->close(session, NULL)) != 0)
+ testutil_die(ret, "session.close");
+ }
+
+ printf("write thread %2d stopping: tid: %s, file: %s\n",
+ id, tid, s->name);
+
+ return (NULL);
+}
+
+/*
+ * print_stats --
+ * Display reader/writer thread stats.
+ */
+static void
+print_stats(u_int nthreads)
+{
+ INFO *s;
+ u_int id;
+
+ s = run_info;
+ for (id = 0; id < nthreads; ++id, ++s)
+ printf("%3d: read %6d, remove %6d, update %6d\n",
+ id, s->reads, s->remove, s->update);
+}
diff --git a/src/third_party/wiredtiger/test/thread/smoke.sh b/src/third_party/wiredtiger/test/thread/smoke.sh
new file mode 100755
index 00000000000..9a235b1d8e9
--- /dev/null
+++ b/src/third_party/wiredtiger/test/thread/smoke.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+set -e
+
+# Smoke-test format as part of running "make check".
+$TEST_WRAPPER ./t -t f
+$TEST_WRAPPER ./t -S -F -t f
+
+$TEST_WRAPPER ./t -t r
+$TEST_WRAPPER ./t -S -F -t r
+
+$TEST_WRAPPER ./t -t v
+$TEST_WRAPPER ./t -S -F -t v
diff --git a/src/third_party/wiredtiger/test/thread/stats.c b/src/third_party/wiredtiger/test/thread/stats.c
new file mode 100644
index 00000000000..67a2c02719b
--- /dev/null
+++ b/src/third_party/wiredtiger/test/thread/stats.c
@@ -0,0 +1,86 @@
+/*-
+ * Public Domain 2014-2016 MongoDB, Inc.
+ * Public Domain 2008-2014 WiredTiger, Inc.
+ *
+ * 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.
+ */
+
+#include "thread.h"
+
+/*
+ * stats
+ * Dump the database/file statistics.
+ */
+void
+stats(void)
+{
+ FILE *fp;
+ WT_CURSOR *cursor;
+ WT_SESSION *session;
+ uint64_t v;
+ int ret;
+ char name[64];
+ const char *pval, *desc;
+
+ if ((ret = conn->open_session(conn, NULL, NULL, &session)) != 0)
+ testutil_die(ret, "conn.session");
+
+ if ((fp = fopen(FNAME_STAT, "w")) == NULL)
+ testutil_die(errno, "fopen " FNAME_STAT);
+
+ /* Connection statistics. */
+ if ((ret = session->open_cursor(session,
+ "statistics:", NULL, NULL, &cursor)) != 0)
+ testutil_die(ret, "session.open_cursor");
+
+ while ((ret = cursor->next(cursor)) == 0 &&
+ (ret = cursor->get_value(cursor, &desc, &pval, &v)) == 0)
+ (void)fprintf(fp, "%s=%s\n", desc, pval);
+
+ if (ret != WT_NOTFOUND)
+ testutil_die(ret, "cursor.next");
+ if ((ret = cursor->close(cursor)) != 0)
+ testutil_die(ret, "cursor.close");
+
+ /* File statistics. */
+ if (!multiple_files) {
+ (void)snprintf(name, sizeof(name), "statistics:" FNAME, 0);
+ if ((ret = session->open_cursor(
+ session, name, NULL, NULL, &cursor)) != 0)
+ testutil_die(ret, "session.open_cursor");
+
+ while ((ret = cursor->next(cursor)) == 0 &&
+ (ret = cursor->get_value(cursor, &desc, &pval, &v)) == 0)
+ (void)fprintf(fp, "%s=%s\n", desc, pval);
+
+ if (ret != WT_NOTFOUND)
+ testutil_die(ret, "cursor.next");
+ if ((ret = cursor->close(cursor)) != 0)
+ testutil_die(ret, "cursor.close");
+
+ if ((ret = session->close(session, NULL)) != 0)
+ testutil_die(ret, "session.close");
+ }
+ (void)fclose(fp);
+}
diff --git a/src/third_party/wiredtiger/test/thread/t.c b/src/third_party/wiredtiger/test/thread/t.c
new file mode 100644
index 00000000000..e72b54bf62a
--- /dev/null
+++ b/src/third_party/wiredtiger/test/thread/t.c
@@ -0,0 +1,304 @@
+/*-
+ * Public Domain 2014-2016 MongoDB, Inc.
+ * Public Domain 2008-2014 WiredTiger, Inc.
+ *
+ * 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.
+ */
+
+#include "thread.h"
+
+WT_CONNECTION *conn; /* WiredTiger connection */
+__ftype ftype; /* File type */
+u_int nkeys, max_nops; /* Keys, Operations */
+int vary_nops; /* Vary operations by thread */
+int log_print; /* Log print per operation */
+int multiple_files; /* File per thread */
+int session_per_op; /* New session per operation */
+
+static char home[512]; /* Program working dir */
+static char *progname; /* Program name */
+static FILE *logfp; /* Log file */
+
+static int handle_error(WT_EVENT_HANDLER *, WT_SESSION *, int, const char *);
+static int handle_message(WT_EVENT_HANDLER *, WT_SESSION *, const char *);
+static void onint(int);
+static void shutdown(void);
+static int usage(void);
+static void wt_connect(char *);
+static void wt_shutdown(void);
+
+extern int __wt_optind;
+extern char *__wt_optarg;
+
+int
+main(int argc, char *argv[])
+{
+ u_int readers, writers;
+ int ch, cnt, runs;
+ char *config_open, *working_dir;
+
+ if ((progname = strrchr(argv[0], DIR_DELIM)) == NULL)
+ progname = argv[0];
+ else
+ ++progname;
+
+ config_open = NULL;
+ working_dir = NULL;
+ ftype = ROW;
+ log_print = 0;
+ multiple_files = 0;
+ nkeys = 1000;
+ max_nops = 10000;
+ readers = 10;
+ runs = 1;
+ session_per_op = 0;
+ vary_nops = 0;
+ writers = 10;
+
+ while ((ch = __wt_getopt(
+ progname, argc, argv, "C:Fk:h:Ll:n:R:r:St:vW:")) != EOF)
+ switch (ch) {
+ case 'C': /* wiredtiger_open config */
+ config_open = __wt_optarg;
+ break;
+ case 'F': /* multiple files */
+ multiple_files = 1;
+ break;
+ case 'h':
+ working_dir = __wt_optarg;
+ break;
+ case 'k': /* rows */
+ nkeys = (u_int)atoi(__wt_optarg);
+ break;
+ case 'L': /* log print per operation */
+ log_print = 1;
+ break;
+ case 'l': /* log */
+ if ((logfp = fopen(__wt_optarg, "w")) == NULL) {
+ fprintf(stderr,
+ "%s: %s\n", __wt_optarg, strerror(errno));
+ return (EXIT_FAILURE);
+ }
+ break;
+ case 'n': /* operations */
+ max_nops = (u_int)atoi(__wt_optarg);
+ break;
+ case 'R':
+ readers = (u_int)atoi(__wt_optarg);
+ break;
+ case 'r': /* runs */
+ runs = atoi(__wt_optarg);
+ break;
+ case 'S': /* new session per operation */
+ session_per_op = 1;
+ break;
+ case 't':
+ switch (__wt_optarg[0]) {
+ case 'f':
+ ftype = FIX;
+ break;
+ case 'r':
+ ftype = ROW;
+ break;
+ case 'v':
+ ftype = VAR;
+ break;
+ default:
+ return (usage());
+ }
+ break;
+ case 'v': /* vary operation count */
+ vary_nops = 1;
+ break;
+ case 'W':
+ writers = (u_int)atoi(__wt_optarg);
+ break;
+ default:
+ return (usage());
+ }
+
+ argc -= __wt_optind;
+ argv += __wt_optind;
+ if (argc != 0)
+ return (usage());
+
+ testutil_work_dir_from_path(home, 512, working_dir);
+
+ if (vary_nops && !multiple_files) {
+ fprintf(stderr,
+ "Variable op counts only supported with multiple tables\n");
+ return (usage());
+ }
+
+ /* Clean up on signal. */
+ (void)signal(SIGINT, onint);
+
+ printf("%s: process %" PRIu64 "\n", progname, (uint64_t)getpid());
+ for (cnt = 1; runs == 0 || cnt <= runs; ++cnt) {
+ printf(
+ " %d: %u readers, %u writers\n", cnt, readers, writers);
+
+ shutdown(); /* Clean up previous runs */
+
+ wt_connect(config_open); /* WiredTiger connection */
+
+ if (rw_start(readers, writers)) /* Loop operations */
+ return (EXIT_FAILURE);
+
+ stats(); /* Statistics */
+
+ wt_shutdown(); /* WiredTiger shut down */
+ }
+ return (0);
+}
+
+/*
+ * wt_connect --
+ * Configure the WiredTiger connection.
+ */
+static void
+wt_connect(char *config_open)
+{
+ static WT_EVENT_HANDLER event_handler = {
+ handle_error,
+ handle_message,
+ NULL,
+ NULL /* Close handler. */
+ };
+ int ret;
+ char config[512];
+ size_t print_count;
+
+ testutil_clean_work_dir(home);
+ testutil_make_work_dir(home);
+
+ print_count = (size_t)snprintf(config, sizeof(config),
+ "create,statistics=(all),error_prefix=\"%s\",%s%s",
+ progname,
+ config_open == NULL ? "" : ",",
+ config_open == NULL ? "" : config_open);
+
+ if (print_count >= sizeof(config))
+ testutil_die(EINVAL, "Config string too long");
+
+ if ((ret = wiredtiger_open(home, &event_handler, config, &conn)) != 0)
+ testutil_die(ret, "wiredtiger_open");
+}
+
+/*
+ * wt_shutdown --
+ * Flush the file to disk and shut down the WiredTiger connection.
+ */
+static void
+wt_shutdown(void)
+{
+ WT_SESSION *session;
+ int ret;
+
+ if ((ret = conn->open_session(conn, NULL, NULL, &session)) != 0)
+ testutil_die(ret, "conn.session");
+
+ if ((ret = session->checkpoint(session, NULL)) != 0)
+ testutil_die(ret, "session.checkpoint");
+
+ if ((ret = conn->close(conn, NULL)) != 0)
+ testutil_die(ret, "conn.close");
+}
+
+/*
+ * shutdown --
+ * Clean up from previous runs.
+ */
+static void
+shutdown(void)
+{
+ testutil_clean_work_dir(home);
+}
+
+static int
+handle_error(WT_EVENT_HANDLER *handler,
+ WT_SESSION *session, int error, const char *errmsg)
+{
+ (void)(handler);
+ (void)(session);
+ (void)(error);
+
+ return (fprintf(stderr, "%s\n", errmsg) < 0 ? -1 : 0);
+}
+
+static int
+handle_message(WT_EVENT_HANDLER *handler,
+ WT_SESSION *session, const char *message)
+{
+ (void)(handler);
+ (void)(session);
+
+ if (logfp != NULL)
+ return (fprintf(logfp, "%s\n", message) < 0 ? -1 : 0);
+
+ return (printf("%s\n", message) < 0 ? -1 : 0);
+}
+
+/*
+ * onint --
+ * Interrupt signal handler.
+ */
+static void
+onint(int signo)
+{
+ (void)(signo);
+
+ shutdown();
+
+ fprintf(stderr, "\n");
+ exit(EXIT_FAILURE);
+}
+
+/*
+ * usage --
+ * Display usage statement and exit failure.
+ */
+static int
+usage(void)
+{
+ fprintf(stderr,
+ "usage: %s "
+ "[-FLSv] [-C wiredtiger-config] [-k keys] [-l log]\n\t"
+ "[-n ops] [-R readers] [-r runs] [-t f|r|v] [-W writers]\n",
+ progname);
+ fprintf(stderr, "%s",
+ "\t-C specify wiredtiger_open configuration arguments\n"
+ "\t-F create a file per thread\n"
+ "\t-k set number of keys to load\n"
+ "\t-L log print per operation\n"
+ "\t-l specify a log file\n"
+ "\t-n set number of operations each thread does\n"
+ "\t-R set number of reading threads\n"
+ "\t-r set number of runs (0 for continuous)\n"
+ "\t-S open/close a session on every operation\n"
+ "\t-t set a file type (fix | row | var)\n"
+ "\t-v do a different number of operations on different tables\n"
+ "\t-W set number of writing threads\n");
+ return (EXIT_FAILURE);
+}
diff --git a/src/third_party/wiredtiger/test/thread/thread.h b/src/third_party/wiredtiger/test/thread/thread.h
new file mode 100644
index 00000000000..36cdbebd210
--- /dev/null
+++ b/src/third_party/wiredtiger/test/thread/thread.h
@@ -0,0 +1,61 @@
+/*-
+ * Public Domain 2014-2016 MongoDB, Inc.
+ * Public Domain 2008-2014 WiredTiger, Inc.
+ *
+ * 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.
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <errno.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "test_util.i"
+
+#define FNAME "file:wt.%03d" /* File name */
+#define FNAME_STAT "__stats" /* File name for statistics */
+
+extern WT_CONNECTION *conn; /* WiredTiger connection */
+
+typedef enum { FIX, ROW, VAR } __ftype; /* File type */
+extern __ftype ftype;
+
+extern int log_print; /* Log print per operation */
+extern int multiple_files; /* File per thread */
+extern u_int nkeys; /* Keys to load */
+extern u_int max_nops; /* Operations per thread */
+extern int vary_nops; /* Operations per thread */
+extern int session_per_op; /* New session per operation */
+
+void load(const char *);
+int rw_start(u_int, u_int);
+void stats(void);
+void verify(const char *);