/*#io Map ioDoc( docCopyright("Steve Dekorte", 2002) docLicense("BSD revised") docDescription("Object wrapper for an Io coroutine.") docCategory("Core") */ #include "IoCoroutine.h" #include "IoObject.h" #include "IoState.h" #include "IoCFunction.h" #include "IoSeq.h" #include "IoNumber.h" #include "IoList.h" #include "IoBlock.h" //#define DEBUG #define DATA(self) ((IoCoroutineData *)IoObject_dataPointer(self)) IoCoroutine *IoMessage_locals_coroutineArgAt_(IoMessage *self, void *locals, int n) { IoObject *v = IoMessage_locals_valueArgAt_(self, (IoObject *)locals, n); if (!ISCOROUTINE(v)) IoMessage_locals_numberArgAt_errorForType_(self, (IoObject *)locals, n, "Coroutine"); return v; } void IoCoroutine_writeToStore_stream_(IoCoroutine *self, IoStore *store, BStream *stream) { //IoCoroutineData *data = DATA(self); } void IoCoroutine_readFromStore_stream_(IoCoroutine *self, IoStore *store, BStream *stream) { //IoCoroutineData *data = DATA(self); } IoTag *IoCoroutine_tag(void *state) { IoTag *tag = IoTag_newWithName_("Coroutine"); tag->state = state; tag->freeFunc = (TagFreeFunc *)IoCoroutine_free; tag->cloneFunc = (TagCloneFunc *)IoCoroutine_rawClone; tag->markFunc = (TagMarkFunc *)IoCoroutine_mark; tag->writeToStoreOnStreamFunc = (TagWriteToStoreOnStreamFunc *)IoCoroutine_writeToStore_stream_; tag->readFromStoreOnStreamFunc = (TagReadFromStoreOnStreamFunc *)IoCoroutine_readFromStore_stream_; return tag; } IoCoroutine *IoCoroutine_proto(void *state) { IoObject *self = IoObject_new(state); self->tag = IoCoroutine_tag(state); IoObject_setDataPointer_(self, calloc(1, sizeof(IoCoroutineData))); DATA(self)->ioStack = Stack_new(); IoState_registerProtoWithFunc_((IoState *)state, self, IoCoroutine_proto); // init Coroutine proto's coro as the main one { Coro *coro = Coro_new(); DATA(self)->cid = coro; Coro_initializeMainCoro(coro); } return self; } void IoCoroutine_protoFinish(IoCoroutine *self) { IoMethodTable methodTable[] = { {"ioStack", IoCoroutine_ioStack}, {"run", IoCoroutine_run}, {"main", IoCoroutine_main}, {"resume", IoCoroutine_resume}, {"isCurrent", IoCoroutine_isCurrent}, {"currentCoroutine", IoCoroutine_currentCoroutine}, {"implementation", IoCoroutine_implementation}, {"setMessageDebugging", IoCoroutine_setMessageDebugging}, {NULL, NULL}, }; IoObject_addMethodTable_(self, methodTable); } IoCoroutine *IoCoroutine_rawClone(IoCoroutine *proto) { IoObject *self = IoObject_rawClonePrimitive(proto); IoObject_setDataPointer_(self, calloc(1, sizeof(IoCoroutineData))); DATA(self)->ioStack = Stack_new(); DATA(self)->cid = (Coro *)NULL; return self; } IoCoroutine *IoCoroutine_new(void *state) { IoObject *proto = IoState_protoWithInitFunction_((IoState *)state, IoCoroutine_proto); IoObject *self = IOCLONE(proto); return self; } void IoCoroutine_free(IoCoroutine *self) { Coro *coro = DATA(self)->cid; if (coro) Coro_free(coro); Stack_free(DATA(self)->ioStack); free(DATA(self)); } void IoCoroutine_mark(IoCoroutine *self) { //printf("Coroutine_%p mark\n", (void *)self); Stack_do_(DATA(self)->ioStack, (ListDoCallback *)IoObject_shouldMark); } // raw Stack *IoCoroutine_rawIoStack(IoCoroutine *self) { return DATA(self)->ioStack; } void IoCoroutine_rawShow(IoCoroutine *self) { Stack_do_(DATA(self)->ioStack, (StackDoCallback *)IoObject_show); printf("\n"); } void *IoCoroutine_cid(IoObject *self) { return DATA(self)->cid; } // runTarget void IoCoroutine_rawSetRunTarget_(IoObject *self, IoObject *v) { IoObject_setSlot_to_(self, IOSYMBOL("runTarget"), v); } IoObject *IoCoroutine_rawRunTarget(IoObject *self) { return IoObject_rawGetSlot_(self, IOSYMBOL("runTarget")); } // runMessage void IoCoroutine_rawSetRunMessage_(IoObject *self, IoObject *v) { IoObject_setSlot_to_(self, IOSYMBOL("runMessage"), v); } IoObject *IoCoroutine_rawRunMessage(IoObject *self) { return IoObject_rawGetSlot_(self, IOSYMBOL("runMessage")); } // runLocals void IoCoroutine_rawSetRunLocals_(IoObject *self, IoObject *v) { IoObject_setSlot_to_(self, IOSYMBOL("runLocals"), v); } IoObject *IoCoroutine_rawRunLocals(IoObject *self) { return IoObject_rawGetSlot_(self, IOSYMBOL("runLocals")); } // parent void IoCoroutine_rawSetParentCoroutine_(IoObject *self, IoObject *v) { IoObject_setSlot_to_(self, IOSYMBOL("parentCoroutine"), v); } IoObject *IoCoroutine_rawParentCoroutine(IoObject *self) { return IoObject_getSlot_(self, IOSYMBOL("parentCoroutine")); } // result void IoCoroutine_rawSetResult_(IoObject *self, IoObject *v) { IoObject_setSlot_to_(self, IOSYMBOL("result"), v); } IoObject *IoCoroutine_rawResult(IoObject *self) { return IoObject_getSlot_(self, IOSYMBOL("result")); } // exception void IoCoroutine_rawRemoveException(IoObject *self) { IoObject_removeSlot_(self, IOSYMBOL("exception")); } void IoCoroutine_rawSetException_(IoObject *self, IoObject *v) { IoObject_setSlot_to_(self, IOSYMBOL("exception"), v); } IoObject *IoCoroutine_rawException(IoObject *self) { return IoObject_getSlot_(self, IOSYMBOL("exception")); } // ioStack IoObject *IoCoroutine_ioStack(IoCoroutine *self, IoObject *locals, IoMessage *m) { /*#io docSlot("ioStack", "Returns List of values on this coroutine's stack.") */ return IoList_newWithList_(IOSTATE, Stack_asList(DATA(self)->ioStack)); } void IoCoroutine_rawReturnToParent(IoCoroutine *self) { IoCoroutine *parent = IoCoroutine_rawParentCoroutine(self); if (parent && ISCOROUTINE(parent)) { IoCoroutine_rawResume(parent); } else { if (self == IOSTATE->mainCoroutine) { printf("IoCoroutine error: attempt to return from main coro\n"); exit(-1); } } if (!ISNIL(IoCoroutine_rawException(self))) { IoCoroutine_rawPrintBackTrace(self); } printf("IoCoroutine error: unable to auto abort coro %p by resuming parent coro %s_%p\n", (void *)self, IoObject_name(parent), (void *)parent); exit(-1); } void IoCoroutine_coroStart(void *context) // Called by Coro_Start() { IoCoroutine *self = (IoCoroutine *)context; IoObject *result; IoState_setCurrentCoroutine_(IOSTATE, self); //printf("%p-%p start\n", (void *)self, (void *)DATA(self)->cid); result = IoMessage_locals_performOn_(IOSTATE->mainMessage, self, self); IoCoroutine_rawSetResult_(self, result); IoCoroutine_rawReturnToParent(self); } IoObject *IoCoroutine_main(IoObject *self, IoObject *locals, IoMessage *m) { IoObject *runTarget = IoCoroutine_rawRunTarget(self); IoObject *runLocals = IoCoroutine_rawRunLocals(self); IoObject *runMessage = IoCoroutine_rawRunMessage(self); if (runTarget && runLocals && runMessage) { return IoMessage_locals_performOn_(runMessage, runLocals, runTarget); } else { printf("IoCoroutine_main() missing needed parameters\n"); } return IONIL(self); } Coro *IoCoroutine_rawCoro(IoObject *self) { return DATA(self)->cid; } void IoCoroutine_clearStack(IoObject *self) { Stack_clear(DATA(self)->ioStack); } void IoCoroutine_rawRun(IoObject *self) { Coro *coro = DATA(self)->cid; //Stack_clear(DATA(self)->ioStack); if (coro) { //printf("error: attempt to run same coro %p twice\n", (void *)self); //exit(-1); } else { coro = Coro_new(); DATA(self)->cid = coro; } { IoCoroutine *current = IoState_currentCoroutine(IOSTATE); Coro *currentCoro = IoCoroutine_rawCoro(current); Coro_startCoro_(currentCoro, coro, self, (CoroStartCallback *)IoCoroutine_coroStart); //IoState_setCurrentCoroutine_(IOSTATE, current); } } IoObject *IoCoroutine_run(IoObject *self, IoObject *locals, IoMessage *m) { IoCoroutine_rawRun(self); return IoCoroutine_rawResult(self); } void IoCoroutine_try(IoObject *self, IoObject *target, IoObject *locals, IoMessage *message) { IoCoroutine *currentCoro = (IoCoroutine *)IoState_currentCoroutine((IoState *)IOSTATE); IoCoroutine_rawSetRunTarget_(self, target); IoCoroutine_rawSetRunLocals_(self, locals); IoCoroutine_rawSetRunMessage_(self, message); IoCoroutine_rawSetParentCoroutine_(self, currentCoro); IoCoroutine_rawRun(self); } IoCoroutine *IoCoroutine_newWithTry(void *state, IoObject *target, IoObject *locals, IoMessage *message) { IoCoroutine *self = IoCoroutine_new(state); IoCoroutine_try(self, target, locals, message); return self; } void IoCoroutine_raiseError(IoCoroutine *self, IoSymbol *description, IoMessage *m) { IoObject *e = IoObject_rawGetSlot_(self, IOSYMBOL("Exception")); if (e) { e = IOCLONE(e); IoObject_setSlot_to_(e, IOSYMBOL("error"), description); if (m) IoObject_setSlot_to_(e, IOSYMBOL("caughtMessage"), m); IoObject_setSlot_to_(e, IOSYMBOL("coroutine"), self); IoCoroutine_rawSetException_(self, e); } IoCoroutine_rawReturnToParent(self); } // methods IoObject *IoCoroutine_rawResume(IoObject *self) { IoCoroutine *current = IoState_currentCoroutine(IOSTATE); IoState_setCurrentCoroutine_(IOSTATE, self); //printf("IoCoroutine resuming %p\n", (void *)self); Coro_switchTo_(IoCoroutine_rawCoro(current), IoCoroutine_rawCoro(self)); //IoState_setCurrentCoroutine_(IOSTATE, current); return self; } IoObject *IoCoroutine_resume(IoObject *self, IoObject *locals, IoMessage *m) { //printf("IoCoroutine_resume()\n"); return IoCoroutine_rawResume(self); } IoObject *IoCoroutine_implementation(IoObject *self, IoObject *locals, IoMessage *m) { return IOSYMBOL(CORO_IMPLEMENTATION); } IoObject *IoCoroutine_isCurrent(IoObject *self, IoObject *locals, IoMessage *m) { IoObject *v = IOBOOL(self, self == IoState_currentCoroutine(IOSTATE)); return v; } IoObject *IoCoroutine_currentCoroutine(IoObject *self, IoObject *locals, IoMessage *m) { return IoState_currentCoroutine(IOSTATE); } // stack trace int IoCoroutine_rawIoStackSize(IoObject *self) { return Stack_count(DATA(self)->ioStack); } void IoCoroutine_rawPrint(IoObject *self) { Coro *coro = DATA(self)->cid; if (coro) { printf("Coroutine_%p with cid %p ioStackSize %i\n", (void *)self, (void *)coro, (int)Stack_count(DATA(self)->ioStack)); } } // debugging int IoCoroutine_rawDebuggingOn(IoObject *self) { return DATA(self)->debuggingOn; } IoObject *IoCoroutine_setMessageDebugging(IoObject *self, IoObject *locals, IoMessage *m) { /*#io docSlot("setMessageDebugging(aBoolean)", "Turns on message level debugging for this coro. When on, this coro will send a vmWillSendMessage message to the Debugger object before each message send and pause itself. See the Debugger object documentation for more information. ") */ IoObject *v = IoMessage_locals_valueArgAt_(m, locals, 0); DATA(self)->debuggingOn = ISTRUE(v); IoState_updateDebuggingMode(IOSTATE); return self; } IoObject *IoObject_performWithDebugger(IoObject *self, IoObject *locals, IoMessage *m) { IoState *state = IOSTATE; IoObject *currentCoroutine = IoState_currentCoroutine(state); if (IoCoroutine_rawDebuggingOn(currentCoroutine)) { IoObject *debugger = state->debugger; // stack retain it? if (debugger) { IoObject_setSlot_to_(debugger, IOSYMBOL("messageCoroutine"), currentCoroutine); IoObject_setSlot_to_(debugger, IOSYMBOL("messageSelf"), self); IoObject_setSlot_to_(debugger, IOSYMBOL("messageLocals"), locals); IoObject_setSlot_to_(debugger, IOSYMBOL("message"), m); { IoObject *context; IoCoroutine *c = IoObject_rawGetSlot_context_(debugger, IOSYMBOL("debuggerCoroutine"), &context); IOASSERT(c, "Debugger needs a debuggerCoroutine slot"); IoCoroutine_rawResume(c); } } } return IoObject_perform(self, locals, m); } void IoCoroutine_rawPrintBackTrace(IoObject *self) { IoObject *e = IoCoroutine_rawException(self); IoMessage *caughtMessage = IoObject_rawGetSlot_(e, IOSYMBOL("caughtMessage")); if (IoObject_rawGetSlot_(e, IOSYMBOL("showStack"))) // sanity check { IoState_on_doCString_withLabel_(IOSTATE, e, "showStack", "[Coroutine]"); } else { IoSymbol *error = IoObject_rawGetSlot_(e, IOSYMBOL("error")); if (error) { fputs(CSTRING(error), stderr); fputs("\n", stderr); } else { fputs("error: [missing error slot in Exception object]\n", stderr); } if (caughtMessage) { ByteArray *ba = IoMessage_asMinimalStackEntryDescription(caughtMessage); fputs(ByteArray_asCString(ba), stderr); fputs("\n", stderr); ByteArray_free(ba); } } }