/* ``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);
}