blob: 40953d07193a32796dc739ace66126896cf09eee [file] [log] [blame]
//------------------------------------------------------------------
//
// @@@ START COPYRIGHT @@@
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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.
//
// @@@ END COPYRIGHT @@@
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <linux/unistd.h>
#include "seabed/fserr.h"
#include "seabed/ms.h"
#include "seabed/pctl.h"
#include "seabed/pevents.h"
#include "seabed/timer.h"
#include "seabed/thread.h"
#include "tchkfe.h"
#include "tchkos.h"
#include "tmsfsutil.h"
#include "tutil.h"
#include "tutilp.h"
#define gettid() (pid_t) syscall(__NR_gettid)
enum {
MAX_THR = 50
};
class Thread_Test : public SB_Thread::Thread {
public:
Thread_Test(Function fun, const char *name, int inx)
: SB_Thread::Thread(fun, name), whoami(inx) {
}
void work();
private:
int whoami;
};
class Thread_Test2 : public SB_Thread::Thread {
public:
Thread_Test2(Function fun, const char *name, int inx)
: SB_Thread::Thread(fun, name), whoami(inx) {
}
void work();
private:
int whoami;
};
class Thread_Test3 : public SB_Thread::Thread {
public:
Thread_Test3(Function fun, const char *name, int inx)
: SB_Thread::Thread(fun, name), whoami(inx) {
}
void work();
private:
int whoami;
};
SB_Thread::CV cv1;
SB_Thread::CV cv2;
int maxth = 1;
char my_name[BUFSIZ];
MS_SRE_TPOP t1;
MS_SRE_TPOP t2;
Thread_Test *thr[MAX_THR];
Thread_Test2 *thr2[2];
Thread_Test3 *thr3[MAX_THR];
short tleid_test2;
short tleid_test3;
int tles = 0;
SB_Thread::SL tles_sl;
int to_tol = -1;
bool verbose = false;
void cbx(MS_SRE_TPOP *t,
SB_Thread::CV *cv,
int tleid,
int toval,
short parm1,
long parm2) {
t->sre_tleId = tleid;
t->sre_tleTOVal = toval;
t->sre_tleParm1 = parm1;
t->sre_tleParm2 = parm2;
cv->signal(true);
}
void cb1(int tleid, int toval, short parm1, long parm2) {
if (verbose)
printf("cv1 signal\n");
cbx(&t1, &cv1, tleid, toval, parm1, parm2);
}
void cb2(int tleid, int toval, short parm1, long parm2) {
if (verbose)
printf("cv2 signal\n");
cbx(&t2, &cv2, tleid, toval, parm1, parm2);
}
void cbcan(int tleid, int toval, short parm1, long parm2) {
if (verbose)
printf("cbcan called tleid=%d, toval=%d, p1=%d, p2=%ld\n",
tleid, toval, parm1, parm2);
assert(0); // should not be called
}
void Thread_Test::work() {
_xcc_status cc;
int lerr;
MS_SRE_TPOP sre;
int status;
struct timeval t_elapsed;
struct timeval t_start;
struct timeval t_stop;
int tid;
short tleid1;
short tleid2;
short tleid3;
short tleid4;
long to_act;
long to_exp;
tid = gettid();
// drain events
lerr = XWAIT(-1, -2);
TEST_CHK_WAITIGNORE(lerr);
lerr = XWAIT(-1, -2);
TEST_CHK_WAITIGNORE(lerr);
util_time_timer_start(&t_start);
cc = XSIGNALTIMEOUT(40, 4, tid, &tleid1);
TEST_CHK_CCEQ(cc);
cc = XSIGNALTIMEOUT(80, 8, tid, &tleid2);
TEST_CHK_CCEQ(cc);
cc = XSIGNALTIMEOUT(20, 2, tid, &tleid3);
TEST_CHK_CCEQ(cc);
cc = XSIGNALTIMEOUT(160, 16, tid, &tleid4);
TEST_CHK_CCEQ(cc);
if (verbose)
printf("w=%d\n", whoami);
while (tles < 4 * maxth) {
lerr = XWAIT(-1, 10);
do {
lerr = XMSG_LISTEN_((short *) &sre, // sre
0, // listenopts
0); // listenertag
switch (lerr) {
case XSRETYPE_TPOP:
util_time_timer_stop(&t_stop);
util_time_elapsed(&t_start, &t_stop, &t_elapsed);
to_act = t_elapsed.tv_sec * 1000000 + t_elapsed.tv_usec;
to_exp = sre.sre_tleTOVal * 10000;
if (verbose || (to_act < to_exp))
printf("w=%d, tid=%d, tleid=%d, exp-to=%ld, act-to=%ld\n",
whoami, tid, sre.sre_tleId, to_exp, to_act);
assert(to_act >= to_exp);
if (to_tol > 0)
assert(to_act <= (to_exp + to_tol));
status = tles_sl.lock();
TEST_CHK_STATUSOK(status);
tles++;
status = tles_sl.unlock();
TEST_CHK_STATUSOK(status);
if (verbose)
printf("w=%d, TLEs=%d\n", whoami, tles);
break;
case XSRETYPE_NOWORK:
break;
default:
assert((lerr == XSRETYPE_TPOP) || (lerr == XSRETYPE_NOWORK));
}
} while (lerr != XSRETYPE_NOWORK);
}
}
void Thread_Test2::work() {
_xcc_status cc;
int lerr;
MS_SRE_TPOP sre;
if (whoami == 0) {
while (tleid_test2 < 0)
SB_Thread::Sthr::yield();
usleep(100000);
cc = XCANCELTIMEOUT(tleid_test2);
TEST_CHK_CCNE(cc);
} else {
lerr = XWAIT(LREQ, -1);
assert(lerr == LREQ);
cc = XSIGNALTIMEOUT(1, 1, 1, &tleid_test2);
TEST_CHK_CCEQ(cc);
lerr = XWAIT(LREQ, -1);
assert(lerr == LREQ);
lerr = XMSG_LISTEN_((short *) &sre, // sre
0, // listenopts
0); // listenertag
}
}
void Thread_Test3::work() {
_xcc_status cc;
int inx;
int lerr;
MS_SRE_TPOP sre;
short tleid;
for (inx = 0; inx < 1000; inx++) {
cc = XSIGNALTIMEOUT(1, 1, 1, &tleid);
TEST_CHK_CCEQ(cc);
do {
lerr = XWAIT(LREQ, -1);
assert(lerr == LREQ);
lerr = XMSG_LISTEN_((short *) &sre, // sre
0, // listenopts
0); // listenertag
} while (lerr == XSRETYPE_NOWORK);
assert(lerr == XSRETYPE_TPOP);
}
}
void *thr_test(void *arg) {
Thread_Test *lthr = (Thread_Test *) arg; // cast
lthr->work();
return NULL;
}
void *thr_test2(void *arg) {
Thread_Test2 *lthr = (Thread_Test2 *) arg; // cast
lthr->work();
return NULL;
}
void *thr_test3(void *arg) {
Thread_Test3 *lthr = (Thread_Test3 *) arg; // cast
lthr->work();
return NULL;
}
void test_cancel() {
_xcc_status cc;
short tleid1;
short tleid2;
if (verbose)
printf("cancel-test\n");
cc = XSIGNALTIMEOUT(4, 4, 4, &tleid1);
TEST_CHK_CCEQ(cc);
cc = XCANCELTIMEOUT(tleid1);
TEST_CHK_CCEQ(cc);
cc = XCANCELTIMEOUT(tleid1);
assert(_xstatus_ge(cc));
cc = XSIGNALTIMEOUT(1, 1, 1, &tleid1);
TEST_CHK_CCEQ(cc);
usleep(4000);
cc = XCANCELTIMEOUT(tleid1);
TEST_CHK_CCEQ(cc);
cc = XSIGNALTIMEOUT(1, 1, 1, &tleid1);
TEST_CHK_CCEQ(cc);
cc = XSIGNALTIMEOUT(1, 1, 1, &tleid2);
TEST_CHK_CCEQ(cc);
usleep(4000);
cc = XCANCELTIMEOUT(tleid2);
TEST_CHK_CCEQ(cc);
cc = XCANCELTIMEOUT(tleid1);
TEST_CHK_CCEQ(cc);
cc = XSIGNALTIMEOUT(200, 1, 1, &tleid1);
TEST_CHK_CCEQ(cc);
cc = XSIGNALTIMEOUT(100, 1, 1, &tleid2);
TEST_CHK_CCEQ(cc);
cc = XCANCELTIMEOUT(tleid1);
TEST_CHK_CCEQ(cc);
cc = XCANCELTIMEOUT(tleid2);
TEST_CHK_CCEQ(cc);
}
void test_cancel_cb() {
int disable;
int ferr;
short tleid1;
short tleid2;
if (verbose)
printf("cancel-cb-test\n");
ferr = timer_start_cb(4, 4, 4, &tleid1, &cbcan);
assert(ferr == XZFIL_ERR_OK);
ferr = timer_cancel(tleid1);
assert(ferr == XZFIL_ERR_OK);
disable = msg_test_assert_disable();
ferr = timer_cancel(tleid1);
assert(ferr == XZFIL_ERR_NOTFOUND);
msg_test_assert_enable(disable);
ferr = timer_start_cb(1, 1, 1, &tleid1, &cbcan);
assert(ferr == XZFIL_ERR_OK);
usleep(4000);
ferr = timer_cancel(tleid1);
assert(ferr == XZFIL_ERR_OK);
ferr = timer_start_cb(1, 1, 1, &tleid1, &cbcan);
assert(ferr == XZFIL_ERR_OK);
ferr = timer_start_cb(1, 1, 1, &tleid2, &cbcan);
assert(ferr == XZFIL_ERR_OK);
usleep(4000);
ferr = timer_cancel(tleid2);
assert(ferr == XZFIL_ERR_OK);
ferr = timer_cancel(tleid1);
assert(ferr == XZFIL_ERR_OK);
ferr = timer_start_cb(214800, 4, 4, &tleid1, &cbcan); // test CR 6539
assert(ferr == XZFIL_ERR_OK);
ferr = timer_cancel(tleid1);
assert(ferr == XZFIL_ERR_OK);
}
void test_timers() {
_xcc_status cc;
int lerr;
MS_SRE_TPOP sre;
struct timeval t_elapsed;
struct timeval t_start1;
struct timeval t_start2;
struct timeval t_stop;
short tleid1;
short tleid2;
long to_act;
long to_exp;
if (verbose)
printf("timer-test\n");
// drain pending LREQ
lerr = XWAIT(LREQ, -1);
assert(lerr == LREQ);
// check timers
util_time_timer_start(&t_start1);
cc = XSIGNALTIMEOUT(40, 2, 2, &tleid1);
TEST_CHK_CCEQ(cc);
usleep(30000);
util_time_timer_start(&t_start2);
cc = XSIGNALTIMEOUT(40, 4, 4, &tleid2);
TEST_CHK_CCEQ(cc);
lerr = XWAIT(LREQ, -1);
assert(lerr == LREQ);
lerr = XMSG_LISTEN_((short *) &sre, // sre
0, // listenopts
0); // listenertag
assert(lerr == XSRETYPE_TPOP);
util_time_timer_stop(&t_stop);
util_time_elapsed(&t_start1, &t_stop, &t_elapsed);
to_act = t_elapsed.tv_sec * 1000000 + t_elapsed.tv_usec;
to_exp = sre.sre_tleTOVal * 10000;
if (verbose || (to_act < to_exp))
printf("tleid=%d, exp-to=%ld, act-to=%ld\n",
sre.sre_tleId, to_exp, to_act);
assert(to_act >= to_exp);
if (to_tol > 0)
assert(to_act <= (to_exp + to_tol));
lerr = XWAIT(LREQ, -1);
assert(lerr == LREQ);
lerr = XMSG_LISTEN_((short *) &sre, // sre
0, // listenopts
0); // listenertag
assert(lerr == XSRETYPE_TPOP);
util_time_timer_stop(&t_stop);
util_time_elapsed(&t_start2, &t_stop, &t_elapsed);
to_act = t_elapsed.tv_sec * 1000000 + t_elapsed.tv_usec;
to_exp = sre.sre_tleTOVal * 10000;
if (verbose || (to_act < to_exp))
printf("tleid=%d, exp-to=%ld, act-to=%ld\n",
sre.sre_tleId, to_exp, to_act);
assert(to_act >= to_exp);
if (to_tol > 0)
assert(to_act <= (to_exp + to_tol));
}
void test_timers_alloc() {
enum { MAX_TLES = 98 };
_xcc_status cc;
short inx;
int inx2;
short tleid[MAX_TLES];
if (verbose)
printf("timer-test-alloc\n");
for (inx2 = 0; inx2 < MAX_TLES; inx2++) {
for (inx = 0; inx < MAX_TLES; inx++) {
cc = XSIGNALTIMEOUT(100, inx, inx, &tleid[inx]);
TEST_CHK_CCEQ(cc);
}
for (inx = 0; inx < MAX_TLES; inx++) {
cc = XCANCELTIMEOUT(tleid[inx]);
TEST_CHK_CCEQ(cc);
}
cc = XSIGNALTIMEOUT(100, inx, inx, &tleid[0]);
TEST_CHK_CCEQ(cc);
cc = XCANCELTIMEOUT(tleid[0]);
TEST_CHK_CCEQ(cc);
}
}
void test_timers_cb() {
int ferr;
int status;
struct timeval t_elapsed;
struct timeval t_start1;
struct timeval t_start2;
struct timeval t_stop;
short tleid1;
short tleid2;
long to_act;
long to_exp;
if (verbose)
printf("timer-cb-test\n");
// check timers
util_time_timer_start(&t_start1);
ferr = timer_start_cb(40, 2, 2, &tleid1, &cb1);
assert(ferr == XZFIL_ERR_OK);
usleep(30000);
util_time_timer_start(&t_start2);
ferr = timer_start_cb(40, 4, 4, &tleid2, &cb2);
assert(ferr == XZFIL_ERR_OK);
status = cv1.wait(true);
TEST_CHK_STATUSOK(status);
if (verbose)
printf("cv1 wait done\n");
util_time_timer_stop(&t_stop);
util_time_elapsed(&t_start1, &t_stop, &t_elapsed);
to_act = t_elapsed.tv_sec * 1000000 + t_elapsed.tv_usec;
to_exp = t1.sre_tleTOVal * 10000;
if (verbose || (to_act < to_exp))
printf("tleid=%d, exp-to=%ld, act-to=%ld\n",
t1.sre_tleId, to_exp, to_act);
assert(to_act >= to_exp);
if (to_tol > 0)
assert(to_act <= (to_exp + to_tol));
status = cv2.wait(true);
TEST_CHK_STATUSOK(status);
if (verbose)
printf("cv2 wait done\n");
util_time_timer_stop(&t_stop);
util_time_elapsed(&t_start2, &t_stop, &t_elapsed);
to_act = t_elapsed.tv_sec * 1000000 + t_elapsed.tv_usec;
to_exp = t2.sre_tleTOVal * 10000;
if (verbose || (to_act < to_exp))
printf("tleid=%d, exp-to=%ld, act-to=%ld\n",
t2.sre_tleId, to_exp, to_act);
assert(to_act >= to_exp);
if (to_tol > 0)
assert(to_act <= (to_exp + to_tol));
}
void test_thread1() {
int inx;
int status;
if (verbose)
printf("thread1-test\n");
for (inx = 0; inx < maxth; inx++) {
char name[10];
sprintf(name, "s%d", inx);
thr[inx] = new Thread_Test(thr_test, name, inx);
}
for (inx = 0; inx < maxth; inx++) {
thr[inx]->start();
SB_Thread::Sthr::yield();
}
for (inx = 0; inx < maxth; inx++) {
void *res;
status = thr[inx]->join(&res);
TEST_CHK_STATUSOK(status);
printf("joined with thread %d\n", inx);
}
}
void test_thread2() {
int inx;
int status;
if (verbose)
printf("thread2-test\n");
tleid_test2 = -1;
for (inx = 0; inx < 2; inx++) {
char name[10];
sprintf(name, "s%d", inx);
thr2[inx] = new Thread_Test2(thr_test2, name, inx);
}
for (inx = 0; inx < 2; inx++) {
thr2[inx]->start();
SB_Thread::Sthr::yield();
}
for (inx = 0; inx < 2; inx++) {
void *res;
status = thr2[inx]->join(&res);
TEST_CHK_STATUSOK(status);
printf("joined with thread %d\n", inx);
}
}
void test_thread3() {
int inx;
int status;
if (verbose)
printf("thread3-test\n");
for (inx = 0; inx < maxth; inx++) {
char name[10];
sprintf(name, "s%d", inx);
thr3[inx] = new Thread_Test3(thr_test3, name, inx);
}
for (inx = 0; inx < maxth; inx++) {
thr3[inx]->start();
SB_Thread::Sthr::yield();
}
for (inx = 0; inx < maxth; inx++) {
void *res;
status = thr3[inx]->join(&res);
TEST_CHK_STATUSOK(status);
printf("joined with thread %d\n", inx);
}
}
int main(int argc, char *argv[]) {
bool chook = false;
int ferr;
int loop = 10;
TAD zargs[] = {
{ "-chook", TA_Bool, TA_NOMAX, &chook },
{ "-client", TA_Ign, TA_NOMAX, NULL },
{ "-loop", TA_Int, TA_NOMAX, &loop },
{ "-maxth", TA_Int, MAX_THR, &maxth },
{ "-server", TA_Ign, TA_NOMAX, NULL },
{ "-tol", TA_Int, TA_NOMAX, &to_tol },
{ "-v", TA_Bool, TA_NOMAX, &verbose },
{ "", TA_End, TA_NOMAX, NULL }
};
msfs_util_init(&argc, &argv, msg_debug_hook);
arg_proc_args(zargs, false, argc, argv);
if (chook)
msg_debug_hook("c", "c");
util_test_start(true);
ferr = msg_mon_process_startup(true); // system messages?
TEST_CHK_FEOK(ferr);
util_gethostname(my_name, sizeof(my_name));
#if 0 // reproduce bug 1570
// - add sleep(1) in sb_timer_setitimer when pv_to != 0
#endif
test_cancel();
test_timers();
test_timers_alloc();
test_thread1();
test_cancel_cb();
test_timers_cb();
test_thread3();
#if 0 // reproduce bug 728
// - must add sleep(1) after call-to-sb_timer_comp_q_remove() in MSG_LISTEN_
test_thread2();
#endif
ferr = msg_mon_process_shutdown();
TEST_CHK_FEOK(ferr);
util_test_finish(true);
return 0;
}