diff --git a/stix/kernel/test-005.st b/stix/kernel/test-005.st index 413460c..58f33ab 100644 --- a/stix/kernel/test-005.st +++ b/stix/kernel/test-005.st @@ -245,7 +245,7 @@ #method(#class) getABlock { - ^ [ 'a block returned by getABlock' dump. ^ self] + ^ [ 'a block returned by getABlock' dump. "^ self"] } #method(#class) main diff --git a/stix/lib/exec.c b/stix/lib/exec.c index 18df39a..dc43124 100644 --- a/stix/lib/exec.c +++ b/stix/lib/exec.c @@ -26,6 +26,12 @@ #include "stix-prv.h" +#define PROCESS_STATE_RUNNING 3 +#define PROCESS_STATE_BLOCKED 2 +#define PROCESS_STATE_SUSPENDED 1 +#define PROCESS_STATE_CREATED 0 +#define PROCESS_STATE_TERMINATED -1 + #if defined(USE_DYNCALL) /* TODO: defined dcAllocMem and dcFreeMeme before builing the dynload and dyncall library */ # include /* TODO: remove this. make dyXXXX calls to callbacks */ @@ -141,7 +147,7 @@ static stix_oop_process_t make_process (stix_t* stix, stix_oop_context_t c) stix_poptmp (stix); if (!proc) return STIX_NULL; - proc->state = STIX_SMOOI_TO_OOP(0); + proc->state = STIX_SMOOI_TO_OOP(PROCESS_STATE_CREATED); proc->initial_context = c; proc->sp = STIX_SMOOI_TO_OOP(-1); @@ -155,6 +161,9 @@ static void switch_process (stix_t* stix, stix_oop_process_t proc) { if (stix->processor->active != proc) { + STIX_ASSERT (proc->state == STIX_SMOOI_TO_OOP(PROCESS_STATE_SUSPENDED) || + proc->state == STIX_SMOOI_TO_OOP(PROCESS_STATE_BLOCKED)); + #if defined(STIX_DEBUG_PROCESSOR) printf ("ACTUAL PROCESS SWITCHING BF...%d %p\n", (int)stix->ip, stix->active_context); #endif @@ -165,12 +174,14 @@ printf ("ACTUAL PROCESS SWITCHING BF...%d %p\n", (int)stix->ip, stix->active_con /* nothing special */ #endif - /* store the active context to the active process */ + /* store the current active context to the current process. + * it is the suspended context of the process to be suspended */ STIX_ASSERT ((stix_oop_t)stix->processor->active != stix->_nil); - stix->processor->active->active_context = stix->active_context; + stix->processor->active->suspended_context = stix->active_context; + stix->processor->active->state = STIX_SMOOI_TO_OOP(PROCESS_STATE_SUSPENDED); - /* switch the active process */ - /*TODO: set the state to RUNNING */ + /* activate the given process */ + proc->state = STIX_SMOOI_TO_OOP(PROCESS_STATE_RUNNING); stix->processor->active = proc; #if defined(STIX_USE_PROCSTK) @@ -179,8 +190,8 @@ printf ("ACTUAL PROCESS SWITCHING BF...%d %p\n", (int)stix->ip, stix->active_con /* nothing special */ #endif - /* switch the active context */ - SWITCH_ACTIVE_CONTEXT (stix, proc->active_context); + /* activate the suspended context of the new process */ + SWITCH_ACTIVE_CONTEXT (stix, proc->suspended_context); #if defined(STIX_DEBUG_PROCESSOR) printf ("ACTUAL PROCESS SWITCHING AF...%d %p\n", (int)stix->ip, stix->active_context); @@ -213,10 +224,11 @@ static STIX_INLINE int register_new_process (stix_t* stix, stix_oop_process_t pr * link it to the processor's process list. */ stix_ooi_t tally; - STIX_ASSERT (proc->state == STIX_SMOOI_TO_OOP(0)); STIX_ASSERT ((stix_oop_t)proc->prev == stix->_nil); STIX_ASSERT ((stix_oop_t)proc->next == stix->_nil); - STIX_ASSERT ((stix_oop_t)proc->active_context == stix->_nil); + + STIX_ASSERT (proc->state == STIX_SMOOI_TO_OOP(PROCESS_STATE_CREATED)); + STIX_ASSERT ((stix_oop_t)proc->suspended_context == stix->_nil); tally = STIX_OOP_TO_SMOOI(stix->processor->tally); if (tally <= 0) @@ -252,8 +264,8 @@ printf ("ADDED NEW PROCESS - %d\n", (int)tally + 1); #endif } - proc->state = STIX_SMOOI_TO_OOP(1); /* TODO: change the code properly... changing state alone doesn't help */ - proc->active_context = proc->initial_context; + proc->state = STIX_SMOOI_TO_OOP(PROCESS_STATE_SUSPENDED); + proc->suspended_context = proc->initial_context; return 0; } @@ -263,12 +275,13 @@ static void terminate_process (stix_t* stix, stix_oop_process_t proc) * can a main process be killed? * can the only process be killed? if so, terminate VM??? */ - if (proc->state == STIX_SMOOI_TO_OOP(1)) + if (proc->state == STIX_SMOOI_TO_OOP(PROCESS_STATE_RUNNING) || + proc->state == STIX_SMOOI_TO_OOP(PROCESS_STATE_SUSPENDED) || + proc->state == STIX_SMOOI_TO_OOP(PROCESS_STATE_BLOCKED)) { stix_ooi_t tally; tally = STIX_OOP_TO_SMOOI(stix->processor->tally); - STIX_ASSERT (tally >= 2); /* the main process must not reach here */ /* the state must be alive */ if ((stix_oop_t)proc->prev != stix->_nil) proc->prev->next = proc->next; @@ -276,14 +289,15 @@ static void terminate_process (stix_t* stix, stix_oop_process_t proc) if ((stix_oop_t)proc->next != stix->_nil) proc->next->prev = proc->prev; else stix->processor->tail = proc->prev; - proc->state = STIX_SMOOI_TO_OOP(-1); /* killed */ + proc->state = STIX_SMOOI_TO_OOP(PROCESS_STATE_TERMINATED); + if (proc == stix->processor->active) proc->suspended_context = stix->active_context; /* not needed but just in case */ proc->sp = STIX_SMOOI_TO_OOP(-1); /* invalidate the process stack */ tally--; stix->processor->tally = STIX_SMOOI_TO_OOP(tally); -/* TODO: allow the last process to be killed like this??? */ if (tally <= 0) { + /* no more process left in the system */ stix->processor->active = stix->nil_process; } else @@ -304,13 +318,13 @@ static void terminate_process (stix_t* stix, stix_oop_process_t proc) static int schedule_process (stix_t* stix, stix_oop_process_t proc) { - if (proc->state == STIX_SMOOI_TO_OOP(-1)) + if (proc->state == STIX_SMOOI_TO_OOP(PROCESS_STATE_TERMINATED)) { /* the process is terminated already */ stix->errnum = STIX_EINVAL; /* TODO: more specialized error code? */ return -1; } - else if (proc->state == STIX_SMOOI_TO_OOP(0)) + else if (proc->state == STIX_SMOOI_TO_OOP(PROCESS_STATE_CREATED)) { /* the process is not scheduled at all. it must not exist in the * process list of the process scheduler. */ @@ -342,9 +356,9 @@ static stix_oop_process_t start_initial_process (stix_t* stix, stix_oop_context_ stix->processor->active = proc; /* do somthing that schedule_process() would do with less overhead */ - STIX_ASSERT ((stix_oop_t)proc->active_context != stix->_nil); - STIX_ASSERT (proc->active_context == proc->initial_context); - SWITCH_ACTIVE_CONTEXT (stix, proc->active_context); + STIX_ASSERT ((stix_oop_t)proc->suspended_context != stix->_nil); + STIX_ASSERT (proc->suspended_context == proc->initial_context); + SWITCH_ACTIVE_CONTEXT (stix, proc->initial_context); return proc; } @@ -394,7 +408,7 @@ static STIX_INLINE int activate_new_method (stix_t* stix, stix_oop_method_t mth) stix_poptmp (stix); if (!ctx) return -1; - ctx->sender = (stix_oop_t)stix->active_context; + ctx->sender = stix->active_context; ctx->ip = STIX_SMOOI_TO_OOP(0); /* the front part of a stack has temporary variables including arguments. * @@ -616,7 +630,7 @@ TODO: overcome this problem STIX_ASSERT (stix->processor->active == proc); STIX_ASSERT (stix->processor->active->initial_context == ctx); - STIX_ASSERT (stix->processor->active->active_context == ctx); + STIX_ASSERT (stix->processor->active->suspended_context == ctx); STIX_ASSERT (stix->active_context == ctx); /* emulate the message sending */ @@ -1086,7 +1100,7 @@ printf ("PRIM BlockContext value FAIL - NARGS MISMATCH\n"); STIX_ASSERT (blkctx->home != stix->_nil); blkctx->sp = STIX_SMOOI_TO_OOP(local_ntmprs - 1); - blkctx->sender = (stix_oop_t)stix->active_context; + blkctx->sender = stix->active_context; *pblkctx = blkctx; return 1; @@ -2285,13 +2299,13 @@ int stix_execute (stix_t* stix) while (1) { -#if 0 -printf ("IP => %d\n", (int)stix->ip); -#endif -switch_to_next_process (stix); -#if 0 -printf ("IP => %d\n", (int)stix->ip); -#endif + if (stix->processor->active == stix->nil_process) + { + /* no more process in the system */ + STIX_ASSERT (stix->processor->tally = STIX_SMOOI_TO_OOP(0)); + break; + } + switch_to_next_process (stix); FETCH_BYTE_CODE_TO (stix, bcode); /*while (bcode == BCODE_NOOP) FETCH_BYTE_CODE_TO (stix, bcode);*/ @@ -2874,6 +2888,7 @@ fflush (stdout); printf ("<> SP=%d\n", (int)stix->sp); #endif + #if 0 /* put the instruction pointer back to the return * instruction (RETURN_RECEIVER or RETURN_RECEIVER) * if a context returns into this context again, @@ -2915,25 +2930,101 @@ printf ("<> SP=%d\n", (int)stix->sp); * */ stix->ip--; + #else + if (stix->active_context->origin == stix->processor->active->initial_context->origin) + { + /* method return from a processified block + * + * #method(#class) main + * { + * [^100] newProcess resume. + * '1111' dump. + * '1111' dump. + * '1111' dump. + * ^300. + * } + * + * ^100 doesn't terminate a main process as the block + * has been processified. on the other hand, ^100 + * in the following program causes main to exit. + * + * #method(#class) main + * { + * [^100] value. + * '1111' dump. + * '1111' dump. + * '1111' dump. + * ^300. + * } + */ - if (stix->processor->active->initial_context == stix->active_context) - { -/* TODO: terminate a process... */ -printf ("TERMINATING A PROCESS RETURNING\n"); + STIX_ASSERT (STIX_CLASSOF(stix, stix->active_context) == stix->_block_context); + STIX_ASSERT (STIX_CLASSOF(stix, stix->processor->active->initial_context) == stix->_block_context); + + /* place the instruction pointer back at the return instruction. + * even if the context is reentered, it will just return. + *stix->ip--;*/ + + +#if defined(STIX_DEBUG_EXEC_002) +printf ("TERMINATING A PROCESS RETURNING old_active context %p\n", stix->active_context); +#endif terminate_process (stix, stix->processor->active); +#if defined(STIX_DEBUG_EXEC_002) +printf ("TERMINATED A PROCESS RETURNING %lld new active_context %p\n", (long long int)stix->ip, stix->active_context); +#endif } - else + else { - SWITCH_ACTIVE_CONTEXT (stix, (stix_oop_context_t)stix->active_context->origin->sender); + if (stix->active_context->origin->ip == STIX_SMOOI_TO_OOP(STIX_SMOOI_MIN)) + { +printf ("ERROR: CAN'T RETURN FROM DEAD METHOD CONTEXT orgin->ip %ld origin->sender->ip %ld\n", + (long int)STIX_OOP_TO_SMOOI(stix->active_context->origin->ip), (long int)STIX_OOP_TO_SMOOI(stix->active_context->origin->sender->ip)); +printf ("ERROR: CAN'T RETURN FROM DEAD METHOD CONTEXT origin %p origin->sender %p\n", stix->active_context->origin, stix->active_context->origin->sender); +printf ("ERROR: CAN'T RETURN FROM DEAD METHOD CONTEXT\n"); + + /* TODO: proper error handling */ + stix->errnum = STIX_EINTERN; /* TODO: this should be caughtable at the stix level... */ + return -1; + } + + /* set the instruction pointer to an invalid value. + * this is stored into the current method context + * before context switching and marks a dead context */ + if (stix->active_context->origin == stix->active_context) + { + /* returning from a method */ +#if defined(STIX_DEBUG_EXEC_002) +printf (">>>>>>>>>>>>> METHOD RETURN...\n"); +#endif + STIX_ASSERT (STIX_CLASSOF(stix, stix->active_context) == stix->_method_context); + stix->ip = STIX_SMOOI_MIN; + } + else + { + /* method return from within a block(including a non-local return) */ + STIX_ASSERT (STIX_CLASSOF(stix, stix->active_context) == stix->_block_context); +#if defined(STIX_DEBUG_EXEC_002) +printf (">>>>>>>>>>>>>>>> METHOD RETURN FROM WITHIN A BLOCK. NON-LOCAL RETURN.. RESETTUBG IP OF CONTEXT %p.\n", stix->active_context->origin); +#endif + stix->active_context->origin->ip = STIX_SMOOI_TO_OOP(STIX_SMOOI_MIN); + } + + SWITCH_ACTIVE_CONTEXT (stix, stix->active_context->origin->sender); /* push the return value to the stack of the new active context */ ACTIVE_STACK_PUSH (stix, return_value); - if (stix->active_context->sender == stix->_nil) + if ((stix_oop_t)stix->active_context->sender == stix->_nil) { /* the sender of the intial context is nil. * use this fact to tell an initial context from a normal context. */ STIX_ASSERT (stix->active_context->receiver_or_source == stix->_nil); + + /* when sender is nil, the following condition must be true. + * but it's not always true the other way around */ + STIX_ASSERT (stix->active_context == stix->processor->active->initial_context); + #if defined(STIX_DEBUG_EXEC_001) printf ("<<>>\n"); #endif @@ -2946,10 +3037,20 @@ printf ("TERMINATING SP.... %ld\n", (long int)stix->sp); /* the stack contains the final return value so the stack pointer must be 0. */ STIX_ASSERT (stix->sp == 0); #endif - goto done; + + if (stix->option.trait & STIX_AWAIT_PROCS) + terminate_process (stix, stix->processor->active); + else + goto done; + + + /* TODO: store the return value to the VM register. + * the caller to stix_execute() can fetch it to return it to the system */ } } + #endif + break; case BCODE_RETURN_FROM_BLOCK: @@ -2959,10 +3060,14 @@ printf ("TERMINATING SP.... %ld\n", (long int)stix->sp); if (stix->active_context == stix->processor->active->initial_context) { - /* TODO: terminate the process. */ +#if defined(STIX_DEBUG_EXEC_002) printf ("TERMINATE A PROCESS RETURNING FROM BLOCK\n"); +#endif terminate_process (stix, stix->processor->active); -/* **************************************** */ +#if defined(STIX_DEBUG_EXEC_002) + +printf ("TERMINATED A PROCESS RETURNING FROM BLOCK %lld new active_context %p\n", (long long int)stix->ip, stix->active_context); +#endif } else { diff --git a/stix/lib/main.c b/stix/lib/main.c index 1240702..73cd83d 100644 --- a/stix/lib/main.c +++ b/stix/lib/main.c @@ -457,6 +457,7 @@ int main (int argc, char* argv[]) int trait = 0; /*trait |= STIX_NOGC;*/ + trait |= STIX_AWAIT_PROCS; stix_setoption (stix, STIX_TRAIT, &trait); } diff --git a/stix/lib/stix-prv.h b/stix/lib/stix-prv.h index bf19870..c25fa71 100644 --- a/stix/lib/stix-prv.h +++ b/stix/lib/stix-prv.h @@ -54,7 +54,7 @@ /* this is for gc debugging */ /*#define STIX_DEBUG_PROCESSOR*/ -#define STIX_DEBUG_GC_001 +/*#define STIX_DEBUG_GC_001*/ /*#define STIX_DEBUG_GC_002*/ #define STIX_DEBUG_COMP_001 /*#define STIX_DEBUG_COMP_002*/ diff --git a/stix/lib/stix.h b/stix/lib/stix.h index 8111e9e..4fcb029 100644 --- a/stix/lib/stix.h +++ b/stix/lib/stix.h @@ -83,7 +83,10 @@ enum stix_trait_t { /* perform no garbage collection when the heap is full. * you still can use stix_gc() explicitly. */ - STIX_NOGC = (1 << 0) + STIX_NOGC = (1 << 0), + + /* wait for running process when exiting from the main method */ + STIX_AWAIT_PROCS = (1 << 1), }; typedef enum stix_trait_t stix_trait_t; @@ -492,35 +495,35 @@ struct stix_context_t * is activated as a result of normal message sending and a block * context is activated when it is sent 'value'. it's set to * nil if a block context created hasn't received 'value'. */ - stix_oop_t sender; + stix_oop_context_t sender; /* SmallInteger, instruction pointer */ - stix_oop_t ip; + stix_oop_t ip; /* SmallInteger, stack pointer */ - stix_oop_t sp; + stix_oop_t sp; /* SmallInteger. Number of temporaries. * For a block context, it's inclusive of the temporaries * defined its 'home'. */ - stix_oop_t ntmprs; + stix_oop_t ntmprs; /* CompiledMethod for a method context, * SmallInteger for a block context */ - stix_oop_t method_or_nargs; + stix_oop_t method_or_nargs; /* it points to the receiver of the message for a method context. * a base block context(created but not yet activated) has nil in this * field. if a block context is activated by 'value', it points * to the block context object used as a base for shallow-copy. */ - stix_oop_t receiver_or_source; + stix_oop_t receiver_or_source; /* it is set to nil for a method context. * for a block context, it points to the active context at the * moment the block context was created. that is, it points to * a method context where the base block has been defined. * an activated block context copies this field from the source. */ - stix_oop_t home; + stix_oop_t home; /* when a method context is created, it is set to itself. no change is * made when the method context is activated. when a block context is @@ -528,10 +531,10 @@ struct stix_context_t * origin of the active context. when the block context is shallow-copied * for activation (when it is sent 'value'), it is set to the origin of * the source block context. */ - stix_oop_context_t origin; + stix_oop_context_t origin; /* variable indexed part */ - stix_oop_t slot[1]; /* stack */ + stix_oop_t slot[1]; /* stack */ }; #define STIX_PROCESS_NAMED_INSTVARS 6 @@ -541,7 +544,7 @@ struct stix_process_t { STIX_OBJ_HEADER; stix_oop_context_t initial_context; - stix_oop_context_t active_context; + stix_oop_context_t suspended_context; stix_oop_t state; /* SmallInteger */ stix_oop_process_t prev; stix_oop_process_t next;