Skip to content

Commit

Permalink
Added programming test of timers and cleaned up the "basic" programmi…
Browse files Browse the repository at this point in the history
…ng test
  • Loading branch information
rsms committed Oct 28, 2012
1 parent fbaac1e commit 7d9cb19
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 76 deletions.
4 changes: 0 additions & 4 deletions sol/sched.c
Expand Up @@ -126,7 +126,6 @@ typedef struct {
} STimer;

static void _TimerCallback(EVLoop *evloop, ev_timer *w, int revents) {
STrace();
SSched* s = (SSched*)ev_userdata(evloop);
STimer* timer = (STimer*)w;
_DumpRQAndWQ(s);
Expand Down Expand Up @@ -158,7 +157,6 @@ static void _TimerCallback(EVLoop *evloop, ev_timer *w, int revents) {
// Start a timer
static inline STimer*
_TimerStart(SSched* s, STask* task, SNumber after_ms, SNumber repeat_ms) {
STrace();
STimer* timer = (STimer*)malloc(sizeof(STimer)); // FIXME: malloc
timer->task = task;

Expand All @@ -181,7 +179,6 @@ _TimerStart(SSched* s, STask* task, SNumber after_ms, SNumber repeat_ms) {
}

static inline void _TimerCancel(SSched* s, STimer* timer) {
STrace();
assert(timer->task != 0);
assert(timer->task->wtype == STaskWaitTimer);
assert(timer->task->wp == timer);
Expand All @@ -194,7 +191,6 @@ static inline void _TimerCancel(SSched* s, STimer* timer) {
// Returns true if `t` has subtasks which are still alive.
inline static bool S_ALWAYS_INLINE
_EndTask(SSched* s, STask* t, STaskStatus status) {
STrace();

if (status == STaskStatusError) {
SLogE("Task error");
Expand Down
112 changes: 40 additions & 72 deletions test/test_prog_basics.c
Expand Up @@ -40,9 +40,9 @@ void test_data(SVM* vm) {
};
size_t instr_offs = 3;
SInstr* start_pc = instructions + instr_offs - 1;
SFunc* fun = SFuncCreate(constants, instructions);
SFunc* func = SFuncCreate(constants, instructions);
SSched* sched = SSchedCreate();
STask* task = STaskCreate(fun, 0, 0);
STask* task = STaskCreate(func, 0, 0);

// R(0) = K(1) == 10.0, R(1) = K(0) == 5.0
assert(SchedExec(vm, sched, task) == STaskStatusYield);
Expand All @@ -58,68 +58,6 @@ void test_data(SVM* vm) {
}


void test_control_flow_on_yield(SVM* vm, SSched* s, STask* t, SInstr* pc) {
STrace();

// AR's PC should be 1 less than the PC when this callback function is called.
assert(t->ar->pc+1 == pc);

// AR's PC should be at the first YIELD instruction
assert(SInstrGetOP(*t->ar->pc) == S_OP_YIELD);
assert(SInstrGetA(*t->ar->pc) == 0); // yield resources
}

void test_control_flow_on_timeout(SVM* vm, SSched* s, STask* t, SInstr* pc) {
STrace();

// AR's PC should be 1 less than the PC when this callback function is called.
assert(t->ar->pc+1 == pc);

// AR's PC should be at the first YIELD instruction
//assert(SInstrGetOP(*t->ar->pc) == S_OP_YIELD);
//assert(SInstrGetA(*t->ar->pc) == 1); // wait for timer
}

void test_control_flow(SVM* vm) {
// Covered: YIELD, JUMP, RETURN
// Not covered: CALL, SPAWN
SValue constants[] = {
SValueNumber(5),
SValueOpaque(&test_control_flow_on_yield),
SValueOpaque(&test_control_flow_on_timeout),
};
SInstr instructions[100] = {
SInstr_YIELD(0, 0, 0),
};
size_t instr_offs = 1;
SInstr* start_pc = instructions + instr_offs - 1;
SFunc* fun = SFuncCreate(constants, instructions);
SSched* sched = SSchedCreate();
STask* task = STaskCreate(fun, 0, 0);

// YIELD resources
assert(SchedExec(vm, sched, task) == STaskStatusYield);

// YIELD suspend, timeout RK(0) = 5
task->ar->pc = start_pc;
instructions[instr_offs] = SInstr_YIELD(0, 0, 0);
instructions[instr_offs+1] = SInstr_DBGCB(0, 1, 0); // call K(B)(vm, s, t, pc)
instructions[instr_offs+2] = SInstr_YIELD(1, S_INSTR_RK_k+0, 0);
instructions[instr_offs+3] = SInstr_DBGCB(0, 2, 0); // call K(B)(vm, s, t, pc)
instructions[instr_offs+4] = SInstr_RETURN(0, 0);
//assert(SchedExec(vm, sched, task) == STaskStatusSuspend);
assert(SchedExec(vm, sched, task) == STaskStatusYield);
assert(task->ar->pc == start_pc+1);

SSchedTask(sched, task);
SSchedRun(vm, sched);
STaskRelease(task);
SSchedDestroy(sched);
//SLogD("%p", ((STask*)task->wp)->wp); // Crash, pls.
exit(0);
}


void test_arithmetic(SVM* vm) {
// ADD, SUB, MUL, DIV
SValue constants[] = {
Expand All @@ -133,9 +71,9 @@ void test_arithmetic(SVM* vm) {
};
size_t instr_offs = 3;
SInstr* start_pc = instructions + instr_offs - 1;
SFunc* fun = SFuncCreate(constants, instructions);
SFunc* func = SFuncCreate(constants, instructions);
SSched* sched = SSchedCreate();
STask* task = STaskCreate(fun, 0, 0);
STask* task = STaskCreate(func, 0, 0);

// Load Ks to Rs
assert(SchedExec(vm, sched, task) == STaskStatusYield);
Expand Down Expand Up @@ -196,9 +134,9 @@ void test_logic_tests(SVM* vm) {
};
size_t instr_offs = 3;
SInstr* start_pc = instructions + instr_offs - 1;
SFunc* fun = SFuncCreate(constants, instructions);
SFunc* func = SFuncCreate(constants, instructions);
SSched* sched = SSchedCreate();
STask* task = STaskCreate(fun, 0, 0);
STask* task = STaskCreate(func, 0, 0);

// Load Ks to Rs
assert(SchedExec(vm, sched, task) == STaskStatusYield);
Expand Down Expand Up @@ -313,16 +251,46 @@ void test_logic_tests(SVM* vm) {
SSchedDestroy(sched);
}


void test_control_flow(SVM* vm) {
// Covered: YIELD(0), RETURN
SValue constants[] = {
SValueNumber(5),
};
SInstr instructions[100] = {
SInstr_YIELD(0, 0, 0),
SInstr_RETURN(0, 0),
};

SFunc* func = SFuncCreate(constants, instructions);
SSched* sched = SSchedCreate();
STask* task = STaskCreate(func, 0, 0);

// YIELD resources
assert(SchedExec(vm, sched, task) == STaskStatusYield);

// AR's PC should be 1 less than the PC when this callback function is called.
assert(task->ar->pc == instructions);

// AR's PC should be at the first YIELD instruction
assert(SInstrGetOP(*task->ar->pc) == S_OP_YIELD);
assert(SInstrGetA(*task->ar->pc) == 0); // yield resources

// RETURN
assert(SchedExec(vm, sched, task) == STaskStatusEnd);

SSchedTask(sched, task);
STaskRelease(task);
SSchedDestroy(sched);
}

int main(int argc, const char** argv) {
SVM vm = SVM_INIT;

test_data(&vm);
test_control_flow(&vm);
test_arithmetic(&vm);
test_logic_tests(&vm);
test_control_flow(&vm);

return 0;
}

// make -C ../sol DEBUG=1 libsol && \
// make /Users/rsms/src/sol3/build/test/test_prog_basics.c-smp
120 changes: 120 additions & 0 deletions test/test_prog_timer.c
@@ -0,0 +1,120 @@
// Tests programming with timers.
// Covers YIELD(1), DBGCB, event sequence, and run- & wait queue integrity.
#include "test.h"
#include <sol/vm.h>
#include <sol/sched.h>

STask* task1; // Schedules a timer, waits for it and is finally resumed
STask* task2; // Scheduled after task1 to inspect the VM state
int sequence = 0; // For verifying call sequence

void task1_on_resumed(SVM* vm, SSched* s, STask* t, SInstr* pc) {
// This is called as task1 is resumed after the timer triggered
STrace();
assert(t == task1);
assert(sequence++ == 2);

// AR's PC should be 1 less than the PC when this callback function is called.
assert(t->ar->pc+1 == pc);

// AR's PC should be at the first YIELD instruction
assert(SInstrGetOP(*t->ar->pc) == S_OP_YIELD);
assert(SInstrGetA(*t->ar->pc) == 1); // wait for timer

// Verify that task1 is the only task in the run queue (task2 has already
// ended)
assert(s->rhead == task1);
assert(s->rtail == task1);
assert(task1->next == 0);

// Verify that the wait queue is empty
assert(s->whead == 0);
assert(s->wtail == 0);

assert(sequence++ == 3);
}

void task2_on_task1_suspended(SVM* vm, SSched* s, STask* t, SInstr* pc) {
// This is called in task2 just after task1 is suspended, waiting for a timer
STrace();
assert(t == task2);
assert(sequence++ == 0);

// Verify that task2 is the only task in the run queue
assert(s->rhead == task2);
assert(s->rtail == task2);
assert(task2->next == 0);

// Verify that task1 is the only task in the wait queue
assert(s->whead == task1);
assert(s->wtail == task1);
assert(task1->next == 0);

// Verify that task1 is waiting for a timer
assert(task1->wtype == STaskWaitTimer);
assert(task1->wp != 0);

assert(sequence++ == 1);
}

void test_timer(SVM* vm) {
// Covered: YIELD(1)
SValue constants1[] = {
SValueNumber(5),
SValueOpaque(&task1_on_resumed),
};
SInstr instructions1[] = {
SInstr_YIELD(1, S_INSTR_RK_k+0, 0),
SInstr_DBGCB(0, 1, 0), // ccall K(B)(vm, s, t, pc)
SInstr_RETURN(0, 0),
};
SFunc* func1 = SFuncCreate(constants1, instructions1);
task1 = STaskCreate(func1, 0, 0);

// A task that is scheduled just after task1, inspecting VM state
SValue constants2[] = {
SValueOpaque(&task2_on_task1_suspended),
};
SInstr instructions2[] = {
SInstr_DBGCB(0, 0, 0), // ccall K(B)(vm, s, t, pc)
SInstr_RETURN(0, 0),
};
SFunc* func2 = SFuncCreate(constants2, instructions2);
task2 = STaskCreate(func2, 0, 0);

// Make a scheduler and add the tasks
SSched* sched = SSchedCreate();
SSchedTask(sched, task1);
SSchedTask(sched, task2);

// Verify run queue is: -> task1 <-> task2 <-
assert(sched->rhead == task1);
assert(task1->next == task2);
assert(task1->prev == 0);
assert(sched->rtail == task2);
assert(task2->prev == task1);
assert(task2->next == 0);

// Verify that wait queue is empty
assert(sched->whead == 0);
assert(sched->wtail == 0);

// Run
SSchedRun(vm, sched);

assert(sequence == 4);

SSchedDestroy(sched);
STaskRelease(task1);
STaskRelease(task2);
SFuncDestroy(func1);
SFuncDestroy(func2);
}

int main(int argc, const char** argv) {
SVM vm = SVM_INIT;

test_timer(&vm);

return 0;
}

0 comments on commit 7d9cb19

Please sign in to comment.