Coroutinen - Persitieren



  • Hallo,

    ich beschäftige mich gerade mit Coroutinen und nutze dafür "ucontext.h". Das schalten in verschiedene Coroutinen mittels "swapcontext" klappt auch gut. Ich möchte aber nun gern folgendes Problem lösen:

    Das Programm wird zu einem "beliebigen" Zeitpunkt angehalten (im Hauptcontext). Nun sollen alle vorhandenen contexte (irgendwie) in einer Datei gespeichert werden. Später wird das Programm erneut gestartet und die contexte sollen wieder ausgelesen werden und das Programm / die Coroutinen an der Stelle weiter ausgeführt werden, in der sie sich zuvor befunden haben.

    Ich bin mir nicht ganz sicher ob das überhaupt so funktioniert. Kann man dies so umsetzen und wenn ja, wie?

    Danke für eure Hilfe,

    BlahBlub



  • Hmm. Vielleicht noch mal anders, genauer. Ich bin immer noch am Basteln... . Ich teste gerade nicht mit dem Posix ucontext-swapcontext zeug, sondern mit setjmp und longjmp und manueller Stack-Speicherung.

    Dazu folgender Code (ein bisschen länglich ...):

    #include <stdio.h>
    #include <stdlib.h>
    #include <memory.h>
    
    typedef unsigned int uint;
    typedef unsigned short word;
    typedef unsigned char byte;
    
    #ifdef __GNUC__
    #define NOINLINE __attribute__((noinline))
    #else
    #define NOINLINE __declspec(noinline)
    #endif
    
    #include <setjmp.h>
    #include <fstream>
    #include <string>
    
    using namespace std;
    
    enum {
    	STKPAD = 1 << 16, STKSAV = 1 << 10
    };
    
    template<typename T>
    struct coroutine {
    
    	volatile uint state;
    	volatile char* stkptrH;
    	volatile char* stkptrL;
    	jmp_buf PointA, PointB;
    	char stack[STKSAV];
    
    	coroutine() {
    		state = 0;
    	}
    
    	NOINLINE // necessary for IntelC + my_setjmp.h
    	void yield(int value) {
    		char curtmp;
    		stkptrL = (&curtmp) - 16; // -16 is necessary for MSC
    		if (setjmp(PointB) == 0) {
    			state = value;
    			memcpy(stack, (char*) stkptrL, stkptrH - stkptrL);
    			longjmp(PointA, 1);
    		}
    	}
    
    	NOINLINE // necessary for MSC, to avoid allocation of stktmp before setjmp()
    	void call_do_process() {
    		char stktmp[STKPAD];
    		stkptrH = stktmp;
    		((T*) this)->do_process();
    	}
    
    	uint call(void) {
    		if (setjmp(PointA) == 0) {
    			if (state) {
    				memcpy((char*) stkptrL, stack, stkptrH - stkptrL);
    				longjmp(PointB, 1);
    			}
    			call_do_process();
    		}
    		return state;
    	}
    
    	void save(string pFileName) {
    		ofstream f;
    		f.open(pFileName.c_str(), ios::out | ios::binary | ios::trunc);
    		f.write((char*) &state, sizeof(state));
    		f.write((char*) &stkptrH, sizeof(stkptrH));
    		f.write((char*) &stkptrL, sizeof(stkptrH));
    		f.write((char*) &PointA[0], sizeof(__jmp_buf_tag ));
    		f.write((char*) &PointB[0], sizeof(__jmp_buf_tag ));
    		f.write(stack, STKSAV);
    		f.close();
    	}
    
    	void read(string pFileName) {
    		ifstream f;
    		f.open(pFileName.c_str(), ios::in | ios::binary);
    		f.read((char*) &state, sizeof(state));
    		f.read((char*) &stkptrH, sizeof(stkptrH));
    		f.read((char*) &stkptrL, sizeof(stkptrH));
    		f.read((char*) &PointA[0], sizeof(__jmp_buf_tag ));
    		f.read((char*) &PointB[0], sizeof(__jmp_buf_tag ));
    		f.read(stack, STKSAV);
    		f.close();
    	}
    
    };
    
    struct indexX: coroutine<indexX> {
    
    	void do_process(void) {
    		uint a = 1;
    		while (1) {
    			yield(a);
    			a++;
    		}
    	}
    
    };
    
    struct fibonacci: coroutine<fibonacci> {
    
    	void do_process(void) {
    		uint a = 0, b = 1;
    		while (1) {
    			yield(b);
    			b = b + a;
    			a = b - a;
    		}
    	}
    
    };
    
    int main(int argc, char** argv) {
    
    	indexX ind;
    	fibonacci fib;
    	printf("%p\n", &ind);
    
    	if (argc < 2) {
    		printf("too few args.\n");
    		return 0;
    	}
    
    	if (strcmp("write", argv[1]) == 0) {
    		printf("write.\n");
    
    		for (int i = 0; i < 20; i++) {
    			printf("%i:%i ", ind.call(), fib.call());
    		}
    
    		ind.save("ind");
    		fib.save("fib");
    
    		printf("\n");
    	} else {
    		printf("read.\n");
    
    		ind.read("ind");
    		fib.read("fib");
    
    		for (int i = 0; i < 20; i++) {
    			printf("%i:%i ", ind.call(), fib.call());
    			printf("\n");
    		}
    	}
    
    	printf("done.\n");
    	return 0;
    }
    

    Hier gibts zwei coroutinen, die eine zählt einfach und die andere gibt nacheinander die fibonacci zahlen aus. Das Beispiel hab ich von irgendwo her kopiert und um die save() und load() methoden erweitert.

    Der "write" fall klappt auch so wie er soll. Starte ich anschließend mit "read" (oder was auch immer), so werden die dateien eingelesen, jedoch stürzt des Programm beim ersten longjmp()-Aufruf ab.

    Ich starte das Programm dabei im Debugmodus, dabei ist die am anfang ausgegebene Adresse in beiden fällen gleich, d.h. der Stack, der bei beiden Aufrufen erstellt wird, liegt an der gleichen Stelle. Wenn ich das richtig sehe, werden jmp_buf Registerinhalte wie instruction-pointer, stack-pointer, framepointer gespeichert. Da der Stack beim "read" Aufruf an der gleichen Stelle im Speicher liegt, müsste es doch so eigentlich funktionieren?

    Tut es aber nicht. Weiß jemand, was das Problem ist?

    Bin über jede Hilfe Dankbar!

    Ciao,

    BlahBlub


Log in to reply