aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/test/erl_drv_thread_SUITE_data
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/test/erl_drv_thread_SUITE_data')
-rw-r--r--erts/emulator/test/erl_drv_thread_SUITE_data/Makefile.src33
-rw-r--r--erts/emulator/test/erl_drv_thread_SUITE_data/basic.c291
-rw-r--r--erts/emulator/test/erl_drv_thread_SUITE_data/rwlock.c214
-rw-r--r--erts/emulator/test/erl_drv_thread_SUITE_data/testcase_driver.c260
-rw-r--r--erts/emulator/test/erl_drv_thread_SUITE_data/testcase_driver.h58
-rw-r--r--erts/emulator/test/erl_drv_thread_SUITE_data/tsd.c173
6 files changed, 1029 insertions, 0 deletions
diff --git a/erts/emulator/test/erl_drv_thread_SUITE_data/Makefile.src b/erts/emulator/test/erl_drv_thread_SUITE_data/Makefile.src
new file mode 100644
index 0000000000..216707e8a5
--- /dev/null
+++ b/erts/emulator/test/erl_drv_thread_SUITE_data/Makefile.src
@@ -0,0 +1,33 @@
+# ``The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved via the world wide web at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+# Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+# AB. All Rights Reserved.''
+#
+# $Id$
+#
+
+TEST_DRVS = basic@dll@ rwlock@dll@ tsd@dll@
+CC = @CC@
+LD = @LD@
+CFLAGS = @SHLIB_CFLAGS@ -I@erl_include@ @DEFS@
+SHLIB_EXTRA_LDLIBS = testcase_driver@obj@
+
+all: $(TEST_DRVS)
+
+@SHLIB_RULES@
+
+testcase_driver@obj@: testcase_driver.c testcase_driver.h
+$(TEST_DRVS): testcase_driver@obj@
+
+
+
diff --git a/erts/emulator/test/erl_drv_thread_SUITE_data/basic.c b/erts/emulator/test/erl_drv_thread_SUITE_data/basic.c
new file mode 100644
index 0000000000..fca2c1dbea
--- /dev/null
+++ b/erts/emulator/test/erl_drv_thread_SUITE_data/basic.c
@@ -0,0 +1,291 @@
+/* ``The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved via the world wide web at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+ * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+ * AB. All Rights Reserved.''
+ *
+ * $Id$
+ */
+
+#include "testcase_driver.h"
+
+#ifdef __WIN32__
+#include <windows.h>
+#else
+#include <unistd.h>
+#endif
+#include <errno.h>
+
+#define NO_OF_THREADS 2
+
+static int die;
+static int cw_passed;
+static int res_tf0;
+static int res_tf1;
+static ErlDrvMutex *mtx;
+static ErlDrvCond *cnd;
+static int need_join[NO_OF_THREADS];
+static ErlDrvTid tid[NO_OF_THREADS];
+static ErlDrvThreadOpts *topts;
+
+typedef struct {
+ int n;
+} thr_arg_t;
+
+static void
+do_sleep(unsigned secs)
+{
+#ifdef __WIN32__
+ Sleep((DWORD) secs*1000);
+#else
+ sleep(secs);
+#endif
+}
+
+static void *tf0(void *vta)
+{
+ if (((thr_arg_t *) vta)->n == 0) {
+
+ erl_drv_mutex_lock(mtx);
+
+ erl_drv_cond_wait(cnd, mtx);
+
+ if (die) {
+ erl_drv_mutex_unlock(mtx);
+ return NULL;
+ }
+
+ cw_passed++;
+
+ erl_drv_cond_wait(cnd, mtx);
+
+ if (die) {
+ erl_drv_mutex_unlock(mtx);
+ return NULL;
+ }
+
+ cw_passed++;
+
+ erl_drv_mutex_unlock(mtx);
+ if (erl_drv_equal_tids(erl_drv_thread_self(), tid[0]))
+ res_tf0 = 0;
+ }
+
+ return (void *) &res_tf0;
+}
+
+
+static void *tf1(void *vta)
+{
+
+ if (((thr_arg_t *) vta)->n == 1) {
+
+ erl_drv_mutex_lock(mtx);
+
+ erl_drv_cond_wait(cnd, mtx);
+
+ if (die) {
+ erl_drv_mutex_unlock(mtx);
+ return NULL;
+ }
+
+ cw_passed++;
+
+ erl_drv_cond_wait(cnd, mtx);
+
+ if (die) {
+ erl_drv_mutex_unlock(mtx);
+ return NULL;
+ }
+
+ cw_passed++;
+
+ erl_drv_mutex_unlock(mtx);
+
+ if (erl_drv_equal_tids(erl_drv_thread_self(), tid[1]))
+ res_tf1 = 1;
+
+ erl_drv_thread_exit((void *) &res_tf1);
+
+ res_tf1 = 4711;
+ }
+ return NULL;
+}
+
+void
+testcase_run(TestCaseState_t *tcs)
+{
+ int i, r;
+ void *tres[2];
+ thr_arg_t ta[2];
+ ErlDrvTid my_tid;
+ ErlDrvSysInfo sinfo;
+
+ driver_system_info(&sinfo, sizeof(ErlDrvSysInfo));
+ if (!sinfo.thread_support)
+ testcase_skipped(tcs, "No thread support; nothing to test");
+
+ testcase_printf(tcs, "Initializing\n");
+
+ cw_passed = 0;
+ die = 0;
+ my_tid = erl_drv_thread_self();
+
+ for (i = 0; i < NO_OF_THREADS; i++)
+ need_join[i] = 0;
+
+ res_tf0 = 17;
+ res_tf1 = 17;
+
+ mtx = NULL;
+ cnd = NULL;
+ /* Create mutex and cond */
+ mtx = erl_drv_mutex_create("mutex");
+ ASSERT(tcs, mtx);
+ cnd = erl_drv_cond_create("cond");
+ ASSERT(tcs, cnd);
+ topts = erl_drv_thread_opts_create("thread opts");
+ ASSERT(tcs, topts);
+ topts->suggested_stack_size = 0; /* As small as possible */
+
+ testcase_printf(tcs, "Creating threads 0 & 1\n");
+
+ /* Create the threads */
+ ta[0].n = 0;
+ r = erl_drv_thread_create("thread 0", &tid[0], tf0, (void *) &ta[0], topts);
+ ASSERT(tcs, r == 0);
+ need_join[0] = 1;
+
+ ta[1].n = 1;
+ r = erl_drv_thread_create("thread 1", &tid[1], tf1, (void *) &ta[1], NULL);
+ ASSERT(tcs, r == 0);
+ need_join[1] = 1;
+
+ testcase_printf(tcs, "Testing tids\n");
+
+ ASSERT(tcs, !erl_drv_equal_tids(tid[0], my_tid));
+ ASSERT(tcs, !erl_drv_equal_tids(tid[1], my_tid));
+ ASSERT(tcs, !erl_drv_equal_tids(tid[0], tid[1]));
+ ASSERT(tcs, erl_drv_equal_tids(my_tid, erl_drv_thread_self()));
+
+ testcase_printf(tcs, "Testing mutex/cond\n");
+
+ /* Make sure the threads waits on cond wait */
+ do_sleep(1);
+
+ erl_drv_mutex_lock(mtx);
+
+ ASSERT_CLNUP(tcs, cw_passed == 0, erl_drv_mutex_unlock(mtx));
+
+ /* Let one thread pass one cond wait */
+ erl_drv_cond_signal(cnd);
+
+ erl_drv_mutex_unlock(mtx);
+
+ do_sleep(1);
+
+ erl_drv_mutex_lock(mtx);
+
+ ASSERT_CLNUP(tcs, cw_passed == 1, erl_drv_mutex_unlock(mtx));
+
+
+ /* Let both threads pass one cond wait */
+ erl_drv_cond_broadcast(cnd);
+
+ erl_drv_mutex_unlock(mtx);
+
+ do_sleep(1);
+
+ erl_drv_mutex_lock(mtx);
+
+ ASSERT_CLNUP(tcs, cw_passed == 3, erl_drv_mutex_unlock(mtx));
+
+
+ /* Let the thread that only have passed one cond wait pass the other one */
+ erl_drv_cond_signal(cnd);
+
+ erl_drv_mutex_unlock(mtx);
+
+ do_sleep(1);
+
+ erl_drv_mutex_lock(mtx);
+
+ ASSERT_CLNUP(tcs, cw_passed == 4, erl_drv_mutex_unlock(mtx));
+
+
+ testcase_printf(tcs, "Testing join\n");
+
+ /* Both threads should have passed both cond waits and exited;
+ join them and check returned values */
+
+ erl_drv_thread_join(tid[0], &tres[0]);
+ ASSERT_CLNUP(tcs, r == 0, erl_drv_mutex_unlock(mtx));
+ need_join[0] = 0;
+
+ ASSERT_CLNUP(tcs, tres[0] == &res_tf0, erl_drv_mutex_unlock(mtx));
+ ASSERT_CLNUP(tcs, res_tf0 == 0, erl_drv_mutex_unlock(mtx));
+
+ r = erl_drv_thread_join(tid[1], &tres[1]);
+ ASSERT_CLNUP(tcs, r == 0, erl_drv_mutex_unlock(mtx));
+ need_join[1] = 0;
+
+ ASSERT_CLNUP(tcs, tres[1] == &res_tf1, erl_drv_mutex_unlock(mtx));
+ ASSERT_CLNUP(tcs, res_tf1 == 1, erl_drv_mutex_unlock(mtx));
+
+ /* Test signaling when noone waits */
+
+ erl_drv_cond_signal(cnd);
+
+ /* Test broadcasting when noone waits */
+
+ erl_drv_cond_broadcast(cnd);
+
+ erl_drv_mutex_unlock(mtx);
+
+ erl_drv_mutex_destroy(mtx);
+ mtx = NULL;
+
+ erl_drv_cond_destroy(cnd);
+ cnd = NULL;
+
+ erl_drv_thread_opts_destroy(topts);
+ topts = NULL;
+
+ testcase_printf(tcs, "done\n");
+}
+
+char *
+testcase_name(void)
+{
+ return "basic";
+}
+
+void
+testcase_cleanup(TestCaseState_t *tcs)
+{
+ int i;
+ for (i = 0; i < NO_OF_THREADS; i++) {
+ if (need_join[i]) {
+ erl_drv_mutex_lock(mtx);
+ die = 1;
+ erl_drv_cond_broadcast(cnd);
+ erl_drv_mutex_unlock(mtx);
+ erl_drv_thread_join(tid[i], NULL);
+ }
+ }
+ if (mtx)
+ erl_drv_mutex_destroy(mtx);
+ if (cnd)
+ erl_drv_cond_destroy(cnd);
+ if (topts)
+ erl_drv_thread_opts_destroy(topts);
+}
diff --git a/erts/emulator/test/erl_drv_thread_SUITE_data/rwlock.c b/erts/emulator/test/erl_drv_thread_SUITE_data/rwlock.c
new file mode 100644
index 0000000000..064f52c16b
--- /dev/null
+++ b/erts/emulator/test/erl_drv_thread_SUITE_data/rwlock.c
@@ -0,0 +1,214 @@
+/* ``The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved via the world wide web at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+ * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+ * AB. All Rights Reserved.''
+ *
+ * $Id$
+ */
+
+#include "testcase_driver.h"
+
+#ifdef __WIN32__
+#include <windows.h>
+#else
+#include <unistd.h>
+#endif
+#include <errno.h>
+
+#define NO_OF_THREADS 17
+
+struct {
+ int alive;
+ ErlDrvTid tid;
+} test_thr[NO_OF_THREADS] = {0};
+
+
+static int die;
+static int ready;
+static int rlocked;
+static int rwlocked;
+static int do_rlock;
+static int do_rwlock;
+static ErlDrvMutex *mtx;
+static ErlDrvCond *cnd;
+static ErlDrvRWLock *rwlck;
+
+static void
+do_sleep(unsigned secs)
+{
+#ifdef __WIN32__
+ Sleep((DWORD) secs*1000);
+#else
+ sleep(secs);
+#endif
+}
+
+static void *tf(void *unused)
+{
+
+ erl_drv_mutex_lock(mtx);
+ ready++;
+ if (ready == NO_OF_THREADS)
+ erl_drv_cond_broadcast(cnd);
+ while (!do_rlock)
+ erl_drv_cond_wait(cnd, mtx);
+ erl_drv_mutex_unlock(mtx);
+
+ erl_drv_rwlock_rlock(rwlck);
+
+ /* make sure everyone rlocks at the same time */
+ erl_drv_mutex_lock(mtx);
+ rlocked++;
+ if (rlocked == NO_OF_THREADS)
+ erl_drv_cond_broadcast(cnd);
+ while (rlocked != NO_OF_THREADS && !die)
+ erl_drv_cond_wait(cnd, mtx);
+ erl_drv_mutex_unlock(mtx);
+
+ erl_drv_rwlock_runlock(rwlck);
+
+ erl_drv_mutex_lock(mtx);
+ while (!do_rwlock && !die)
+ erl_drv_cond_wait(cnd, mtx);
+ if (die) {
+ erl_drv_mutex_unlock(mtx);
+ return NULL;
+ }
+ erl_drv_mutex_unlock(mtx);
+
+ erl_drv_rwlock_rwlock(rwlck);
+ rwlocked++;
+ erl_drv_rwlock_rwunlock(rwlck);
+
+ return NULL;
+}
+
+void
+testcase_run(TestCaseState_t *tcs)
+{
+ int i, r;
+ ErlDrvSysInfo sinfo;
+
+ driver_system_info(&sinfo, sizeof(ErlDrvSysInfo));
+ if (!sinfo.thread_support)
+ testcase_skipped(tcs, "No thread support; nothing to test");
+
+ testcase_printf(tcs, "Initializing\n");
+ die = 0;
+ ready = 0;
+ rlocked = 0;
+ rwlocked = 0;
+ do_rlock = 0;
+ do_rwlock = 0;
+
+ mtx = erl_drv_mutex_create("test mutex");
+ cnd = erl_drv_cond_create("test cond");
+ rwlck = erl_drv_rwlock_create("test rwlock");
+ ASSERT(tcs, mtx && cnd && rwlck);
+
+ testcase_printf(tcs, "Creating %d threads\n", NO_OF_THREADS);
+ /* Create the threads */
+ for (i = 0; i < NO_OF_THREADS; i++) {
+ char name[100];
+ sprintf(name, "thread %d", i);
+ r = erl_drv_thread_create(name,
+ &test_thr[i].tid,
+ tf,
+ NULL,
+ NULL);
+ ASSERT(tcs, r == 0);
+ test_thr[i].alive = 1;
+ }
+
+ testcase_printf(tcs, "Testing\n");
+ erl_drv_rwlock_rwlock(rwlck);
+
+ erl_drv_mutex_lock(mtx);
+ while (ready != NO_OF_THREADS)
+ erl_drv_cond_wait(cnd, mtx);
+ do_rlock = 1;
+ erl_drv_cond_broadcast(cnd);
+ erl_drv_mutex_unlock(mtx);
+
+ do_sleep(1);
+
+ erl_drv_mutex_lock(mtx);
+
+ ASSERT_CLNUP(tcs,
+ rlocked == 0,
+ do {
+ erl_drv_mutex_unlock(mtx);
+ erl_drv_rwlock_rwunlock(rwlck);
+ } while (0));
+
+ erl_drv_mutex_unlock(mtx);
+ erl_drv_rwlock_rwunlock(rwlck);
+
+ do_sleep(2);
+
+ erl_drv_mutex_lock(mtx);
+ ASSERT_CLNUP(tcs, rlocked == NO_OF_THREADS, erl_drv_mutex_unlock(mtx));
+ do_rwlock = 1;
+ erl_drv_cond_broadcast(cnd);
+ erl_drv_mutex_unlock(mtx);
+
+ testcase_printf(tcs, "Joining threads\n");
+ /* Join the threads */
+ for (i = 0; i < NO_OF_THREADS; i++) {
+ void *res;
+ r = erl_drv_thread_join(test_thr[i].tid, NULL);
+ test_thr[i].alive = 0;
+ ASSERT(tcs, r == 0);
+ }
+
+ erl_drv_mutex_lock(mtx);
+ ASSERT_CLNUP(tcs, rwlocked == NO_OF_THREADS, erl_drv_mutex_unlock(mtx));
+ erl_drv_mutex_unlock(mtx);
+
+ erl_drv_mutex_destroy(mtx);
+ mtx = NULL;
+ erl_drv_cond_destroy(cnd);
+ cnd = NULL;
+ erl_drv_rwlock_destroy(rwlck);
+ rwlck = NULL;
+
+ testcase_printf(tcs, "done\n");
+}
+
+char *
+testcase_name(void)
+{
+ return "rwlock";
+}
+
+void
+testcase_cleanup(TestCaseState_t *tcs)
+{
+ int i;
+ for (i = 0; i < NO_OF_THREADS; i++) {
+ if (test_thr[i].alive) {
+ erl_drv_mutex_lock(mtx);
+ die = 1;
+ erl_drv_cond_broadcast(cnd);
+ erl_drv_mutex_unlock(mtx);
+ erl_drv_thread_join(test_thr[i].tid, NULL);
+ }
+ }
+
+ if (mtx)
+ erl_drv_mutex_destroy(mtx);
+ if (cnd)
+ erl_drv_cond_destroy(cnd);
+ if (rwlck)
+ erl_drv_rwlock_destroy(rwlck);
+}
diff --git a/erts/emulator/test/erl_drv_thread_SUITE_data/testcase_driver.c b/erts/emulator/test/erl_drv_thread_SUITE_data/testcase_driver.c
new file mode 100644
index 0000000000..1e98844838
--- /dev/null
+++ b/erts/emulator/test/erl_drv_thread_SUITE_data/testcase_driver.c
@@ -0,0 +1,260 @@
+/* ``The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved via the world wide web at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+ * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+ * AB. All Rights Reserved.''
+ *
+ * $Id$
+ */
+
+#include "testcase_driver.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <setjmp.h>
+#include <string.h>
+
+#ifdef __WIN32__
+#undef HAVE_VSNPRINTF
+#define HAVE_VSNPRINTF 1
+#define vsnprintf _vsnprintf
+#endif
+
+#ifndef HAVE_VSNPRINTF
+#define HAVE_VSNPRINTF 0
+#endif
+
+#define COMMENT_BUF_SZ 4096
+
+#define TESTCASE_FAILED 0
+#define TESTCASE_SKIPPED 1
+#define TESTCASE_SUCCEEDED 2
+
+typedef struct {
+ TestCaseState_t visible;
+ ErlDrvPort port;
+ int result;
+ jmp_buf done_jmp_buf;
+ char *comment;
+ char comment_buf[COMMENT_BUF_SZ];
+} InternalTestCaseState_t;
+
+ErlDrvData testcase_drv_start(ErlDrvPort port, char *command);
+void testcase_drv_stop(ErlDrvData drv_data);
+void testcase_drv_run(ErlDrvData drv_data, char *buf, int len);
+
+static ErlDrvEntry testcase_drv_entry = {
+ NULL,
+ testcase_drv_start,
+ testcase_drv_stop,
+ testcase_drv_run
+};
+
+
+DRIVER_INIT(testcase_drv)
+{
+ testcase_drv_entry.driver_name = testcase_name();
+ return &testcase_drv_entry;
+}
+
+ErlDrvData
+testcase_drv_start(ErlDrvPort port, char *command)
+{
+ InternalTestCaseState_t *itcs = (InternalTestCaseState_t *)
+ driver_alloc(sizeof(InternalTestCaseState_t));
+ if (!itcs) {
+ return ERL_DRV_ERROR_GENERAL;
+ }
+
+ itcs->visible.testcase_name = testcase_name();
+ itcs->visible.extra = NULL;
+ itcs->port = port;
+ itcs->result = TESTCASE_FAILED;
+ itcs->comment = "";
+
+ return (ErlDrvData) itcs;
+}
+
+void
+testcase_drv_stop(ErlDrvData drv_data)
+{
+ testcase_cleanup((TestCaseState_t *) drv_data);
+ driver_free((void *) drv_data);
+}
+
+void
+testcase_drv_run(ErlDrvData drv_data, char *buf, int len)
+{
+ InternalTestCaseState_t *itcs = (InternalTestCaseState_t *) drv_data;
+ ErlDrvTermData result_atom;
+ ErlDrvTermData msg[12];
+
+ itcs->visible.command = buf;
+ itcs->visible.command_len = len;
+
+ if (setjmp(itcs->done_jmp_buf) == 0) {
+ testcase_run((TestCaseState_t *) itcs);
+ itcs->result = TESTCASE_SUCCEEDED;
+ }
+
+ switch (itcs->result) {
+ case TESTCASE_SUCCEEDED:
+ result_atom = driver_mk_atom("succeeded");
+ break;
+ case TESTCASE_SKIPPED:
+ result_atom = driver_mk_atom("skipped");
+ break;
+ case TESTCASE_FAILED:
+ default:
+ result_atom = driver_mk_atom("failed");
+ break;
+ }
+
+ msg[0] = ERL_DRV_ATOM;
+ msg[1] = (ErlDrvTermData) result_atom;
+
+ msg[2] = ERL_DRV_PORT;
+ msg[3] = driver_mk_port(itcs->port);
+
+ msg[4] = ERL_DRV_ATOM;
+ msg[5] = driver_mk_atom(itcs->visible.testcase_name);
+
+ msg[6] = ERL_DRV_STRING;
+ msg[7] = (ErlDrvTermData) itcs->comment;
+ msg[8] = (ErlDrvTermData) strlen(itcs->comment);
+
+ msg[9] = ERL_DRV_TUPLE;
+ msg[10] = (ErlDrvTermData) 4;
+
+ driver_output_term(itcs->port, msg, 11);
+}
+
+int
+testcase_assertion_failed(TestCaseState_t *tcs,
+ char *file, int line, char *assertion)
+{
+ testcase_failed(tcs, "%s:%d: Assertion failed: \"%s\"",
+ file, line, assertion);
+ return 0;
+}
+
+void
+testcase_printf(TestCaseState_t *tcs, char *frmt, ...)
+{
+ InternalTestCaseState_t *itcs = (InternalTestCaseState_t *) tcs;
+ ErlDrvTermData msg[12];
+ va_list va;
+ va_start(va, frmt);
+#if HAVE_VSNPRINTF
+ vsnprintf(itcs->comment_buf, COMMENT_BUF_SZ, frmt, va);
+#else
+ vsprintf(itcs->comment_buf, frmt, va);
+#endif
+ va_end(va);
+
+ msg[0] = ERL_DRV_ATOM;
+ msg[1] = (ErlDrvTermData) driver_mk_atom("print");
+
+ msg[2] = ERL_DRV_PORT;
+ msg[3] = driver_mk_port(itcs->port);
+
+ msg[4] = ERL_DRV_ATOM;
+ msg[5] = driver_mk_atom(itcs->visible.testcase_name);
+
+ msg[6] = ERL_DRV_STRING;
+ msg[7] = (ErlDrvTermData) itcs->comment_buf;
+ msg[8] = (ErlDrvTermData) strlen(itcs->comment_buf);
+
+ msg[9] = ERL_DRV_TUPLE;
+ msg[10] = (ErlDrvTermData) 4;
+
+ driver_output_term(itcs->port, msg, 11);
+}
+
+
+void testcase_succeeded(TestCaseState_t *tcs, char *frmt, ...)
+{
+ InternalTestCaseState_t *itcs = (InternalTestCaseState_t *) tcs;
+ va_list va;
+ va_start(va, frmt);
+#if HAVE_VSNPRINTF
+ vsnprintf(itcs->comment_buf, COMMENT_BUF_SZ, frmt, va);
+#else
+ vsprintf(itcs->comment_buf, frmt, va);
+#endif
+ va_end(va);
+
+ itcs->result = TESTCASE_SUCCEEDED;
+ itcs->comment = itcs->comment_buf;
+
+ longjmp(itcs->done_jmp_buf, 1);
+}
+
+void testcase_skipped(TestCaseState_t *tcs, char *frmt, ...)
+{
+ InternalTestCaseState_t *itcs = (InternalTestCaseState_t *) tcs;
+ va_list va;
+ va_start(va, frmt);
+#if HAVE_VSNPRINTF
+ vsnprintf(itcs->comment_buf, COMMENT_BUF_SZ, frmt, va);
+#else
+ vsprintf(itcs->comment_buf, frmt, va);
+#endif
+ va_end(va);
+
+ itcs->result = TESTCASE_SKIPPED;
+ itcs->comment = itcs->comment_buf;
+
+ longjmp(itcs->done_jmp_buf, 1);
+}
+
+void testcase_failed(TestCaseState_t *tcs, char *frmt, ...)
+{
+ InternalTestCaseState_t *itcs = (InternalTestCaseState_t *) tcs;
+ char buf[10];
+ size_t bufsz = sizeof(buf);
+ va_list va;
+ va_start(va, frmt);
+#if HAVE_VSNPRINTF
+ vsnprintf(itcs->comment_buf, COMMENT_BUF_SZ, frmt, va);
+#else
+ vsprintf(itcs->comment_buf, frmt, va);
+#endif
+ va_end(va);
+
+ itcs->result = TESTCASE_FAILED;
+ itcs->comment = itcs->comment_buf;
+
+ if (erl_drv_getenv("ERL_ABORT_ON_FAILURE", buf, &bufsz) == 0
+ && strcmp("true", buf) == 0) {
+ fprintf(stderr, "Testcase \"%s\" failed: %s\n",
+ itcs->visible.testcase_name, itcs->comment);
+ abort();
+ }
+
+ longjmp(itcs->done_jmp_buf, 1);
+}
+
+void *testcase_alloc(size_t size)
+{
+ return driver_alloc(size);
+}
+
+void *testcase_realloc(void *ptr, size_t size)
+{
+ return driver_realloc(ptr, size);
+}
+
+void testcase_free(void *ptr)
+{
+ driver_free(ptr);
+}
diff --git a/erts/emulator/test/erl_drv_thread_SUITE_data/testcase_driver.h b/erts/emulator/test/erl_drv_thread_SUITE_data/testcase_driver.h
new file mode 100644
index 0000000000..18d5229780
--- /dev/null
+++ b/erts/emulator/test/erl_drv_thread_SUITE_data/testcase_driver.h
@@ -0,0 +1,58 @@
+/* ``The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved via the world wide web at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+ * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+ * AB. All Rights Reserved.''
+ *
+ * $Id$
+ */
+
+#ifndef TESTCASE_DRIVER_H__
+#define TESTCASE_DRIVER_H__
+
+#include "erl_driver.h"
+#include <stdlib.h>
+
+typedef struct {
+ char *testcase_name;
+ char *command;
+ int command_len;
+ void *extra;
+} TestCaseState_t;
+
+#define ASSERT_CLNUP(TCS, B, CLN) \
+do { \
+ if (!(B)) { \
+ CLN; \
+ testcase_assertion_failed((TCS), __FILE__, __LINE__, #B); \
+ } \
+} while (0)
+
+#define ASSERT(TCS, B) ASSERT_CLNUP(TCS, B, (void) 0)
+
+
+void testcase_printf(TestCaseState_t *tcs, char *frmt, ...);
+void testcase_succeeded(TestCaseState_t *tcs, char *frmt, ...);
+void testcase_skipped(TestCaseState_t *tcs, char *frmt, ...);
+void testcase_failed(TestCaseState_t *tcs, char *frmt, ...);
+int testcase_assertion_failed(TestCaseState_t *tcs, char *file, int line,
+ char *assertion);
+void *testcase_alloc(size_t size);
+void *testcase_realloc(void *ptr, size_t size);
+void testcase_free(void *ptr);
+
+
+char *testcase_name(void);
+void testcase_run(TestCaseState_t *tcs);
+void testcase_cleanup(TestCaseState_t *tcs);
+
+#endif
diff --git a/erts/emulator/test/erl_drv_thread_SUITE_data/tsd.c b/erts/emulator/test/erl_drv_thread_SUITE_data/tsd.c
new file mode 100644
index 0000000000..3809c915e0
--- /dev/null
+++ b/erts/emulator/test/erl_drv_thread_SUITE_data/tsd.c
@@ -0,0 +1,173 @@
+/* ``The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved via the world wide web at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+ * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+ * AB. All Rights Reserved.''
+ *
+ * $Id$
+ */
+
+#include "testcase_driver.h"
+#include <stdio.h>
+
+#define NO_OF_THREADS 17
+#define NO_OF_KEYS 4711
+
+struct {
+ int alive;
+ ErlDrvTid tid;
+} test_thr[NO_OF_THREADS] = {0};
+
+struct {
+ int used;
+ ErlDrvTSDKey key;
+} test_key[NO_OF_KEYS] = {0};
+
+typedef struct {
+ int n;
+} thr_arg_t;
+
+static void *tf(void *vta)
+{
+ int i;
+ int thr_val = (((thr_arg_t *) vta)->n << 16);
+
+ for (i = 0; i < NO_OF_KEYS; i++)
+ erl_drv_tsd_set(test_key[i].key, (void *) (thr_val | i));
+
+ for (i = 0; i < NO_OF_KEYS; i++)
+ if (erl_drv_tsd_get(test_key[i].key) != ((void *) (thr_val | i)))
+ return (void *) 1;
+
+ for (i = 0; i < NO_OF_KEYS; i++)
+ erl_drv_tsd_set(test_key[i].key, NULL);
+
+ for (i = 0; i < NO_OF_KEYS; i++)
+ if (erl_drv_tsd_get(test_key[i].key) != NULL)
+ return (void *) 2;
+
+ return (void *) 3;
+}
+
+void
+thr_key_clnup(void)
+{
+ int i;
+ for (i = 0; i < NO_OF_KEYS; i++)
+ erl_drv_tsd_set(test_key[i].key, NULL);
+}
+
+void
+testcase_run(TestCaseState_t *tcs)
+{
+ int i, r;
+ thr_arg_t ta[NO_OF_THREADS];
+ ErlDrvSysInfo sinfo;
+
+ testcase_printf(tcs, "Initializing\n");
+
+ driver_system_info(&sinfo, sizeof(ErlDrvSysInfo));
+
+ for (i = 0; i < NO_OF_KEYS; i++) {
+ char name[100];
+ sprintf(name, "key %d", i);
+ r = erl_drv_tsd_key_create(name, &test_key[i].key);
+ ASSERT(tcs, r == 0);
+ test_key[i].used = 1;
+ }
+
+ for (i = 0; i < NO_OF_KEYS; i++)
+ erl_drv_tsd_set(test_key[i].key,
+ (void *) (((NO_OF_THREADS+1) << 16) | i));
+
+ if (!sinfo.thread_support)
+ testcase_printf(tcs, "No thread support; testing tsd in one thread\n");
+ else {
+ testcase_printf(tcs, "Creating %d threads\n", NO_OF_THREADS);
+
+ /* Create the threads */
+ for (i = 0; i < NO_OF_THREADS; i++) {
+ char name[100];
+ ta[i].n = 0;
+ sprintf(name, "thread %d", i);
+ r = erl_drv_thread_create(name,
+ &test_thr[i].tid,
+ tf,
+ (void *) &ta[i],
+ NULL);
+ ASSERT_CLNUP(tcs, r == 0, thr_key_clnup());
+ test_thr[i].alive = 1;
+ }
+ }
+
+ testcase_printf(tcs, "Testing tsd\n");
+
+ for (i = 0; i < NO_OF_KEYS; i++)
+ ASSERT_CLNUP(tcs,
+ ((void *) (((NO_OF_THREADS+1) << 16) | i)
+ == erl_drv_tsd_get(test_key[i].key)),
+ thr_key_clnup());
+
+ testcase_printf(tcs, "Joining threads\n");
+
+ if (sinfo.thread_support) {
+ /* Join the threads */
+ for (i = 0; i < NO_OF_THREADS; i++) {
+ void *res;
+ r = erl_drv_thread_join(test_thr[i].tid, &res);
+ test_thr[i].alive = 0;
+ ASSERT_CLNUP(tcs, r == 0, thr_key_clnup());
+ ASSERT_CLNUP(tcs, res == ((void *) 3), thr_key_clnup());
+ }
+ }
+
+ thr_key_clnup();
+
+ for (i = 0; i < NO_OF_KEYS; i++)
+ ASSERT(tcs, NULL == erl_drv_tsd_get(test_key[i].key));
+
+ testcase_printf(tcs, "Destroying keys\n");
+
+ for (i = 0; i < NO_OF_KEYS; i++)
+ if (test_key[i].used) {
+ test_key[i].used = 0;
+ erl_drv_tsd_key_destroy(test_key[i].key);
+ }
+
+ testcase_printf(tcs, "done\n");
+
+ if (!sinfo.thread_support)
+ testcase_succeeded(tcs, "No thread support; only one thread tested");
+}
+
+char *
+testcase_name(void)
+{
+ return "tsd";
+}
+
+void
+testcase_cleanup(TestCaseState_t *tcs)
+{
+ int i;
+ for (i = 0; i < NO_OF_THREADS; i++)
+ if (test_thr[i].alive) {
+ test_thr[i].alive = 0;
+ erl_drv_thread_join(test_thr[i].tid, NULL);
+ }
+
+ for (i = 0; i < NO_OF_KEYS; i++)
+ if (test_key[i].used) {
+ test_key[i].used = 0;
+ erl_drv_tsd_key_destroy(test_key[i].key);
+ }
+}