/* ``Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions 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 #include #include #endif #include #include #include #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 (enif_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_ts_1 = NULL; static Allctr_t *alloc_ts_2 = NULL; static void stop_allocators(void) { 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_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_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_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; threads[i].arg.no_ops_per_bl = NO_OF_OPS_PER_BL; 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, (unsigned char)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, (unsigned char)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; } ERL_NIF_INIT(threads, testcase_nif_funcs, testcase_nif_init, NULL, NULL, NULL);