/* $Id$ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "test.h" /** * \page page_pjlib_thread_test Test: Thread Test * * This file contains \a thread_test() definition. * * \section thread_test_scope_sec Scope of Test * This tests: * - whether PJ_THREAD_SUSPENDED flag works. * - whether multithreading works. * - whether thread timeslicing works, and threads have equal * time-slice proportion. * * APIs tested: * - pj_thread_create() * - pj_thread_register() * - pj_thread_this() * - pj_thread_get_name() * - pj_thread_destroy() * - pj_thread_resume() * - pj_thread_sleep() * - pj_thread_join() * - pj_thread_destroy() * * * This file is pjlib-test/thread.c * * \include pjlib-test/thread.c */ #if INCLUDE_THREAD_TEST #include #define THIS_FILE "thread_test" static volatile int quit_flag=0; #if 0 # define TRACE__(args) PJ_LOG(3,args) #else # define TRACE__(args) #endif /* * The thread's entry point. * * Each of the thread mainly will just execute the loop which * increments a variable. */ static void* thread_proc(pj_uint32_t *pcounter) { /* Test that pj_thread_register() works. */ pj_thread_desc desc; pj_thread_t *this_thread; unsigned id; pj_status_t rc; id = *pcounter; PJ_UNUSED_ARG(id); /* Warning about unused var if TRACE__ is disabled */ TRACE__((THIS_FILE, " thread %d running..", id)); pj_bzero(desc, sizeof(desc)); rc = pj_thread_register("thread", desc, &this_thread); if (rc != PJ_SUCCESS) { app_perror("...error in pj_thread_register", rc); return NULL; } /* Test that pj_thread_this() works */ this_thread = pj_thread_this(); if (this_thread == NULL) { PJ_LOG(3,(THIS_FILE, "...error: pj_thread_this() returns NULL!")); return NULL; } /* Test that pj_thread_get_name() works */ if (pj_thread_get_name(this_thread) == NULL) { PJ_LOG(3,(THIS_FILE, "...error: pj_thread_get_name() returns NULL!")); return NULL; } /* Main loop */ for (;!quit_flag;) { (*pcounter)++; //Must sleep if platform doesn't do time-slicing. //pj_thread_sleep(0); } TRACE__((THIS_FILE, " thread %d quitting..", id)); return NULL; } /* * simple_thread() */ static int simple_thread(const char *title, unsigned flags) { pj_pool_t *pool; pj_thread_t *thread; pj_status_t rc; pj_uint32_t counter = 0; PJ_LOG(3,(THIS_FILE, "..%s", title)); pool = pj_pool_create(mem, NULL, 4000, 4000, NULL); if (!pool) return -1000; quit_flag = 0; TRACE__((THIS_FILE, " Creating thread 0..")); rc = pj_thread_create(pool, "thread", (pj_thread_proc*)&thread_proc, &counter, PJ_THREAD_DEFAULT_STACK_SIZE, flags, &thread); if (rc != PJ_SUCCESS) { app_perror("...error: unable to create thread", rc); return -1010; } TRACE__((THIS_FILE, " Main thread waiting..")); pj_thread_sleep(1500); TRACE__((THIS_FILE, " Main thread resuming..")); if (flags & PJ_THREAD_SUSPENDED) { /* Check that counter is still zero */ if (counter != 0) { PJ_LOG(3,(THIS_FILE, "...error: thread is not suspended")); return -1015; } rc = pj_thread_resume(thread); if (rc != PJ_SUCCESS) { app_perror("...error: resume thread error", rc); return -1020; } } PJ_LOG(3,(THIS_FILE, "..waiting for thread to quit..")); pj_thread_sleep(1500); quit_flag = 1; pj_thread_join(thread); pj_pool_release(pool); if (counter == 0) { PJ_LOG(3,(THIS_FILE, "...error: thread is not running")); return -1025; } PJ_LOG(3,(THIS_FILE, "...%s success", title)); return PJ_SUCCESS; } /* * timeslice_test() */ static int timeslice_test(void) { enum { NUM_THREADS = 4 }; pj_pool_t *pool; pj_uint32_t counter[NUM_THREADS], lowest, highest, diff; pj_thread_t *thread[NUM_THREADS]; unsigned i; pj_status_t rc; quit_flag = 0; pool = pj_pool_create(mem, NULL, 4000, 4000, NULL); if (!pool) return -10; PJ_LOG(3,(THIS_FILE, "..timeslice testing with %d threads", NUM_THREADS)); /* Create all threads in suspended mode. */ for (i=0; i i) { PJ_LOG(3,(THIS_FILE, "....ERROR! Thread %d-th is not suspended!", i)); return -30; } } /* Now resume all threads. */ for (i=0; i highest) highest = counter[i]; } /* Check that all threads are running. */ if (lowest < 2) { PJ_LOG(3,(THIS_FILE, "...ERROR: not all threads were running!")); return -70; } /* The difference between lowest and higest should be lower than 50%. */ diff = (highest-lowest)*100 / ((highest+lowest)/2); if ( diff >= 50) { PJ_LOG(3,(THIS_FILE, "...ERROR: thread didn't have equal timeslice!")); PJ_LOG(3,(THIS_FILE, ".....lowest counter=%u, highest counter=%u, diff=%u%%", lowest, highest, diff)); return -80; } else { PJ_LOG(3,(THIS_FILE, "...info: timeslice diff between lowest & highest=%u%%", diff)); } pj_pool_release(pool); return 0; } int thread_test(void) { int rc; rc = simple_thread("simple thread test", 0); if (rc != PJ_SUCCESS) return rc; rc = simple_thread("suspended thread test", PJ_THREAD_SUSPENDED); if (rc != PJ_SUCCESS) return rc; rc = timeslice_test(); if (rc != PJ_SUCCESS) return rc; return rc; } #else /* To prevent warning about "translation unit is empty" * when this test is disabled. */ int dummy_thread_test; #endif /* INCLUDE_THREAD_TEST */