aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/test/alloc_SUITE_data/threads.c
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/test/alloc_SUITE_data/threads.c')
-rw-r--r--erts/emulator/test/alloc_SUITE_data/threads.c447
1 files changed, 447 insertions, 0 deletions
diff --git a/erts/emulator/test/alloc_SUITE_data/threads.c b/erts/emulator/test/alloc_SUITE_data/threads.c
new file mode 100644
index 0000000000..1247e5d7dd
--- /dev/null
+++ b/erts/emulator/test/alloc_SUITE_data/threads.c
@@ -0,0 +1,447 @@
+/* ``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 __WIN32__
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#endif
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include "testcase_driver.h"
+#include "allocator_test.h"
+
+#ifdef __WIN32__
+#undef HAVE_VSNPRINTF
+#define HAVE_VSNPRINTF 1
+#define vsnprintf _vsnprintf
+#endif
+
+#ifndef HAVE_VSNPRINTF
+#define HAVE_VSNPRINTF 0
+#endif
+
+#define NO_OF_ALLOC_SEQS 6
+#define NO_OF_THREADS (18)
+#define NO_OF_BLOCKS 10
+#define NO_OF_OPS_PER_BL 200
+#define SBC_THRESHOLD 8192
+
+#define BLOCK_ID(TN, BN) ((TN) << 4 | (BN))
+
+#define ERR_BUF_SZ 4096
+static char err_buf[ERR_BUF_SZ];
+static volatile int tc_failed;
+static int dead_thread_no;
+static erts_mutex tc_mutex;
+static erts_cond tc_cond;
+
+static void exit_thread(int t_no, int do_lock)
+{
+ if (do_lock)
+ THR_MTX_LOCK(tc_mutex);
+
+ while (dead_thread_no >= 0)
+ THR_COND_WAIT(tc_cond, tc_mutex);
+ dead_thread_no = t_no;
+
+ THR_COND_BCAST(tc_cond);
+ THR_MTX_UNLOCK(tc_mutex);
+ THR_EXIT(NULL);
+}
+
+static void fail(int t_no, char *frmt, ...)
+{
+ char buf[10];
+ size_t bufsz = sizeof(buf);
+ va_list va;
+
+ THR_MTX_LOCK(tc_mutex);
+
+ va_start(va, frmt);
+#if HAVE_VSNPRINTF
+ vsnprintf(err_buf, ERR_BUF_SZ, frmt, va);
+#else
+ vsprintf(err_buf, frmt, va);
+#endif
+ va_end(va);
+
+ tc_failed = 1;
+
+ if (erl_drv_getenv("ERL_ABORT_ON_FAILURE", buf, &bufsz) == 0
+ && strcmp("true", buf) == 0) {
+ fprintf(stderr, "Testcase \"%s\" failed: %s\n",
+ testcase_name(), err_buf);
+ abort();
+ }
+
+ exit_thread(t_no, 0);
+}
+
+static Allctr_t *alloc_not_ts = NULL;
+static Allctr_t *alloc_ts_1 = NULL;
+static Allctr_t *alloc_ts_2 = NULL;
+
+static void stop_allocators(void)
+{
+ if (alloc_not_ts) {
+ STOP_ALC(alloc_not_ts);
+ alloc_not_ts = NULL;
+ }
+ if (alloc_ts_1) {
+ STOP_ALC(alloc_ts_1);
+ alloc_ts_1 = NULL;
+ }
+ if (alloc_ts_2) {
+ STOP_ALC(alloc_ts_2);
+ alloc_ts_2 = NULL;
+ }
+}
+
+
+void *thread_func(void *arg);
+
+typedef struct {
+ Allctr_t *a;
+ int t_no;
+ int no_ops_per_bl;
+} ThreadData;
+
+
+char *
+testcase_name(void)
+{
+ return "threads";
+}
+
+void
+testcase_cleanup(TestCaseState_t *tcs)
+{
+ stop_allocators();
+}
+
+void
+testcase_run(TestCaseState_t *tcs)
+{
+ struct {
+ erts_thread tid;
+ ThreadData arg;
+ } threads[NO_OF_THREADS+1] = {{0}};
+ int no_threads;
+ int i;
+ char sbct_buf[10];
+ char *argv_org[] = {"-tasaobf", "-tmmsbc5000", "-tmmmbc5000", "-tsbct",
+ &sbct_buf[0], NULL};
+ char *argv[sizeof(argv_org)/sizeof(argv_org[0])];
+
+ if (!IS_THREADS_ENABLED)
+ testcase_skipped(tcs, "Threads not enabled");
+
+ alloc_not_ts = NULL;
+ alloc_ts_1 = NULL;
+ alloc_ts_2 = NULL;
+
+ err_buf[0] = '\0';
+
+ sprintf(sbct_buf, "%d", SBC_THRESHOLD/1024);
+
+ memcpy((void *) argv, argv_org, sizeof(argv_org));
+ alloc_not_ts = START_ALC("threads_not_ts", 0, argv);
+ ASSERT(tcs, alloc_not_ts);
+ memcpy((void *) argv, argv_org, sizeof(argv_org));
+ alloc_ts_1 = START_ALC("threads_ts_1", 1, argv);
+ ASSERT(tcs, alloc_ts_1);
+ memcpy((void *) argv, argv_org, sizeof(argv_org));
+ alloc_ts_2 = START_ALC("threads_ts_2", 1, argv);
+ ASSERT(tcs, alloc_ts_2);
+
+ ASSERT(tcs, !IS_ALLOC_THREAD_SAFE(alloc_not_ts));
+ ASSERT(tcs, IS_ALLOC_THREAD_SAFE(alloc_ts_1));
+ ASSERT(tcs, IS_ALLOC_THREAD_SAFE(alloc_ts_2));
+
+ tc_mutex = THR_MTX_CREATE();
+ tc_cond = THR_COND_CREATE();
+
+ THR_MTX_LOCK(tc_mutex);
+
+ dead_thread_no = -1;
+ no_threads = 0;
+
+ for(i = 1; i <= NO_OF_THREADS; i++) {
+ char *alc;
+ int res;
+
+ threads[i].arg.no_ops_per_bl = NO_OF_OPS_PER_BL;
+
+ if (i == 1) {
+ alc = "threads_not_ts";
+ threads[i].arg.no_ops_per_bl *= 2;
+ threads[i].arg.a = alloc_not_ts;
+ }
+ else if (i % 2 == 0) {
+ alc = "threads_ts_1";
+ threads[i].arg.a = alloc_ts_1;
+ }
+ else {
+ alc = "threads_ts_2";
+ threads[i].arg.a = alloc_ts_2;
+ }
+ threads[i].arg.t_no = i;
+
+ threads[i].tid = THR_CREATE(thread_func, (void *) &threads[i].arg);
+ if (threads[i].tid) {
+ testcase_printf(tcs, "Successfully created thread %d "
+ "using %s_alloc\n", i, alc);
+ no_threads++;
+ }
+ else {
+ tc_failed = 1;
+ sprintf(err_buf, "Failed to create thread %d\n", i);
+ break;
+ }
+
+ }
+
+ while (no_threads) {
+ THR_COND_WAIT(tc_cond, tc_mutex);
+ if (dead_thread_no >= 0) {
+ no_threads--;
+ THR_JOIN(threads[dead_thread_no].tid);
+ testcase_printf(tcs, "Thread %d died\n", dead_thread_no);
+ dead_thread_no = -1;
+ THR_COND_BCAST(tc_cond);
+ }
+ }
+
+ THR_MTX_UNLOCK(tc_mutex);
+ THR_MTX_DESTROY(tc_mutex);
+ THR_COND_DESTROY(tc_cond);
+
+ stop_allocators();
+
+ if (tc_failed)
+ testcase_failed(tcs, "%s", err_buf);
+}
+
+Ulong alloc_seq_1[] = {
+ 17,
+ SBC_THRESHOLD*2,
+ SBC_THRESHOLD*20,
+ SBC_THRESHOLD*2,
+ 17,
+ 0
+};
+
+Ulong alloc_seq_2[] = {
+ SBC_THRESHOLD*20,
+ SBC_THRESHOLD*2,
+ 17,
+ SBC_THRESHOLD*2,
+ SBC_THRESHOLD*20,
+ 0
+};
+
+Ulong alloc_seq_3[] = {
+ 1,
+ SBC_THRESHOLD/10,
+ SBC_THRESHOLD/9,
+ SBC_THRESHOLD/8,
+ SBC_THRESHOLD/7,
+ SBC_THRESHOLD/6,
+ SBC_THRESHOLD/5,
+ SBC_THRESHOLD/4,
+ SBC_THRESHOLD/3,
+ SBC_THRESHOLD/2,
+ SBC_THRESHOLD/1,
+ SBC_THRESHOLD*1,
+ SBC_THRESHOLD*2,
+ SBC_THRESHOLD*3,
+ SBC_THRESHOLD*4,
+ SBC_THRESHOLD*5,
+ SBC_THRESHOLD*6,
+ SBC_THRESHOLD*7,
+ SBC_THRESHOLD*8,
+ SBC_THRESHOLD*9,
+ SBC_THRESHOLD*10,
+ SBC_THRESHOLD*9,
+ SBC_THRESHOLD*8,
+ SBC_THRESHOLD*7,
+ SBC_THRESHOLD*6,
+ SBC_THRESHOLD*5,
+ SBC_THRESHOLD*4,
+ SBC_THRESHOLD*3,
+ SBC_THRESHOLD*2,
+ SBC_THRESHOLD*1,
+ SBC_THRESHOLD/2,
+ SBC_THRESHOLD/3,
+ SBC_THRESHOLD/4,
+ SBC_THRESHOLD/5,
+ SBC_THRESHOLD/6,
+ SBC_THRESHOLD/7,
+ SBC_THRESHOLD/8,
+ SBC_THRESHOLD/9,
+ SBC_THRESHOLD/10,
+ 1,
+ 0
+};
+
+Ulong alloc_seq_4[] = {
+ SBC_THRESHOLD*2,
+ SBC_THRESHOLD*3,
+ SBC_THRESHOLD*7,
+ SBC_THRESHOLD*8,
+ SBC_THRESHOLD*5,
+ SBC_THRESHOLD*6,
+ SBC_THRESHOLD*1,
+ SBC_THRESHOLD*10,
+ SBC_THRESHOLD*4,
+ SBC_THRESHOLD*2,
+ 0
+};
+
+Ulong alloc_seq_5[] = {
+ SBC_THRESHOLD/2,
+ SBC_THRESHOLD/3,
+ SBC_THRESHOLD/7,
+ SBC_THRESHOLD/8,
+ SBC_THRESHOLD/5,
+ SBC_THRESHOLD/6,
+ SBC_THRESHOLD/1,
+ SBC_THRESHOLD/10,
+ SBC_THRESHOLD/4,
+ SBC_THRESHOLD/2,
+ SBC_THRESHOLD/3,
+ SBC_THRESHOLD/7,
+ SBC_THRESHOLD/8,
+ SBC_THRESHOLD/5,
+ SBC_THRESHOLD/6,
+ SBC_THRESHOLD/1,
+ SBC_THRESHOLD/10,
+ SBC_THRESHOLD/4,
+ SBC_THRESHOLD/2,
+ 0
+};
+
+Ulong alloc_seq_6[] = {
+ 1, 50, 100, 50, 23, 46, 2345, 23, 54, 2, 0
+};
+
+Ulong *alloc_seqs[NO_OF_ALLOC_SEQS] = {
+ alloc_seq_1,
+ alloc_seq_2,
+ alloc_seq_3,
+ alloc_seq_4,
+ alloc_seq_5,
+ alloc_seq_6
+};
+
+typedef struct {
+ unsigned char *p;
+ Ulong s;
+ int i;
+ Ulong *as;
+} block;
+
+#define CHECK_BLOCK_DATA(T, P, S, D) \
+ check_block_data(__FILE__, __LINE__, (T), (P), (S), (D))
+
+static void
+check_block_data(char *file, int line, int t_no,
+ unsigned char *p, Ulong sz, int d)
+{
+ Ulong i;
+ for (i = 0; i < sz; i++)
+ if (p[i] != (unsigned char) d)
+ fail(t_no, "%s:%d: Thread no %d found clobbered data! "
+ "found id=%d; expected id=%d\n",
+ file, line, t_no, (int) p[i], d);
+}
+
+static void
+alloc_op(int t_no, Allctr_t *a, block *bp, int id, int clean_up)
+{
+ if (tc_failed)
+ exit_thread(t_no, 1);
+
+ if(bp->p)
+ CHECK_BLOCK_DATA(t_no, bp->p, bp->s, id);
+
+ if(bp->as[bp->i] == 0 || clean_up) {
+ FREE(a, bp->p);
+ bp->p = NULL;
+ bp->s = 0;
+ bp->i = 0; /* start from the beginning again */
+ return;
+ }
+
+ if(!bp->p) {
+ bp->s = bp->as[bp->i];
+ bp->p = (unsigned char *) ALLOC(a, bp->s);
+ if(!bp->p)
+ fail(t_no, "ALLOC(%lu) failed [id=%d])\n", bp->s, id);
+ memset((void *) bp->p, id, (size_t) bp->s);
+ }
+ else {
+ unsigned char *p = (unsigned char *) REALLOC(a, bp->p, bp->as[bp->i]);
+ if(!p)
+ fail(t_no, "REALLOC(0x%lx, %lu) failed [id=%d]\n",
+ (Ulong) bp->p, bp->as[bp->i], id);
+
+ if(bp->s < bp->as[bp->i]) {
+ CHECK_BLOCK_DATA(t_no, p, bp->s, id);
+ memset((void *) p, id, (size_t) bp->as[bp->i]);
+ }
+ else
+ CHECK_BLOCK_DATA(t_no, p, bp->as[bp->i], id);
+
+ bp->s = bp->as[bp->i];
+ bp->p = p;
+ }
+
+ bp->i++;
+}
+
+
+void *
+thread_func(void *arg)
+{
+ int i, j;
+ ThreadData *td = ((ThreadData *) arg);
+ block bs[NO_OF_BLOCKS];
+
+ for(i = 0; i < NO_OF_BLOCKS; i++) {
+ bs[i].p = NULL;
+ bs[i].s = 0;
+ bs[i].i = 0;
+ bs[i].as = alloc_seqs[i % NO_OF_ALLOC_SEQS];
+ }
+
+ for(i = 0; i < td->no_ops_per_bl; i++) {
+
+ for(j = 0; j < NO_OF_BLOCKS; j++)
+ alloc_op(td->t_no, td->a, &bs[j], BLOCK_ID(td->t_no, j), 0);
+ }
+
+ for(j = 0; j < NO_OF_BLOCKS; j++)
+ alloc_op(td->t_no, td->a, &bs[j], BLOCK_ID(td->t_no, j), 1);
+
+ exit_thread(td->t_no, 1);
+ return NULL;
+}