From b3fac7be77d194c68a69c95c22834f67f92932ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Sun, 2 Jun 2013 11:50:14 +0000 Subject: [PATCH 01/35] Add the tealet project to the file structure --- PCbuild/pythoncore.vcproj | 139 +++- Stackless/core/stackless_impl.h | 2 + Stackless/tealet/platf_slp/mkswitch_stack.py | 71 ++ Stackless/tealet/platf_slp/slp_fallback.h | 67 ++ .../tealet/platf_slp/slp_platformselect.h | 86 ++ Stackless/tealet/platf_slp/slp_switch_stack.h | 279 +++++++ .../tealet/platf_slp/switch_amd64_unix.h | 142 ++++ Stackless/tealet/platf_slp/switch_arm32_gcc.h | 50 ++ .../tealet/platf_slp/switch_arm_thumb_gas.s | 70 ++ .../tealet/platf_slp/switch_arm_thumb_gcc.h | 10 + Stackless/tealet/platf_slp/switch_mips_unix.h | 56 ++ .../tealet/platf_slp/switch_ppc_macosx.h | 85 ++ Stackless/tealet/platf_slp/switch_ppc_unix.h | 73 ++ .../tealet/platf_slp/switch_ps3_SNTools.h | 15 + .../tealet/platf_slp/switch_ps3_SNTools.s | 228 ++++++ Stackless/tealet/platf_slp/switch_s390_unix.h | 54 ++ .../tealet/platf_slp/switch_sparc_sun_gcc.h | 85 ++ .../tealet/platf_slp/switch_x64_masm.asm | 105 +++ Stackless/tealet/platf_slp/switch_x64_msvc.h | 55 ++ Stackless/tealet/platf_slp/switch_x86_msvc.h | 94 +++ Stackless/tealet/platf_slp/switch_x86_unix.h | 70 ++ Stackless/tealet/slp_platformselect.h | 32 + Stackless/tealet/switch_x64_msvc.asm | 103 +++ Stackless/tealet/switch_x64_msvc.h | 7 + Stackless/tealet/switch_x86_64_gcc.h | 66 ++ Stackless/tealet/switch_x86_gcc.h | 53 ++ Stackless/tealet/switch_x86_msvc.asm | 44 ++ Stackless/tealet/switch_x86_msvc.h | 26 + Stackless/tealet/tealet.c | 747 ++++++++++++++++++ Stackless/tealet/tealet.h | 190 +++++ Stackless/tealet/tealet.sln | 36 + Stackless/tealet/tealet.vcproj | 454 +++++++++++ Stackless/tealet/tealet.vcxproj | 215 +++++ Stackless/tealet/tealetvs2008.sln | 39 + Stackless/tealet/test.vcproj | 358 +++++++++ Stackless/tealet/test.vcxproj | 184 +++++ Stackless/tealet/tests.c | 621 +++++++++++++++ 37 files changed, 5003 insertions(+), 8 deletions(-) create mode 100644 Stackless/tealet/platf_slp/mkswitch_stack.py create mode 100644 Stackless/tealet/platf_slp/slp_fallback.h create mode 100644 Stackless/tealet/platf_slp/slp_platformselect.h create mode 100644 Stackless/tealet/platf_slp/slp_switch_stack.h create mode 100644 Stackless/tealet/platf_slp/switch_amd64_unix.h create mode 100644 Stackless/tealet/platf_slp/switch_arm32_gcc.h create mode 100644 Stackless/tealet/platf_slp/switch_arm_thumb_gas.s create mode 100644 Stackless/tealet/platf_slp/switch_arm_thumb_gcc.h create mode 100644 Stackless/tealet/platf_slp/switch_mips_unix.h create mode 100644 Stackless/tealet/platf_slp/switch_ppc_macosx.h create mode 100644 Stackless/tealet/platf_slp/switch_ppc_unix.h create mode 100644 Stackless/tealet/platf_slp/switch_ps3_SNTools.h create mode 100644 Stackless/tealet/platf_slp/switch_ps3_SNTools.s create mode 100644 Stackless/tealet/platf_slp/switch_s390_unix.h create mode 100644 Stackless/tealet/platf_slp/switch_sparc_sun_gcc.h create mode 100644 Stackless/tealet/platf_slp/switch_x64_masm.asm create mode 100644 Stackless/tealet/platf_slp/switch_x64_msvc.h create mode 100644 Stackless/tealet/platf_slp/switch_x86_msvc.h create mode 100644 Stackless/tealet/platf_slp/switch_x86_unix.h create mode 100644 Stackless/tealet/slp_platformselect.h create mode 100644 Stackless/tealet/switch_x64_msvc.asm create mode 100644 Stackless/tealet/switch_x64_msvc.h create mode 100644 Stackless/tealet/switch_x86_64_gcc.h create mode 100644 Stackless/tealet/switch_x86_gcc.h create mode 100644 Stackless/tealet/switch_x86_msvc.asm create mode 100644 Stackless/tealet/switch_x86_msvc.h create mode 100644 Stackless/tealet/tealet.c create mode 100644 Stackless/tealet/tealet.h create mode 100644 Stackless/tealet/tealet.sln create mode 100644 Stackless/tealet/tealet.vcproj create mode 100644 Stackless/tealet/tealet.vcxproj create mode 100644 Stackless/tealet/tealetvs2008.sln create mode 100644 Stackless/tealet/test.vcproj create mode 100644 Stackless/tealet/test.vcxproj create mode 100644 Stackless/tealet/tests.c diff --git a/PCbuild/pythoncore.vcproj b/PCbuild/pythoncore.vcproj index 146d89baac291f..9868dbcb6a4149 100644 --- a/PCbuild/pythoncore.vcproj +++ b/PCbuild/pythoncore.vcproj @@ -16,6 +16,9 @@ /> + + @@ -105,6 +111,9 @@ + @@ -181,6 +190,9 @@ + @@ -258,6 +270,9 @@ + @@ -336,6 +351,9 @@ + @@ -410,6 +428,9 @@ + @@ -486,6 +507,9 @@ + @@ -560,6 +584,9 @@ + @@ -655,11 +682,11 @@ > @@ -2036,7 +2063,7 @@ @@ -2054,7 +2081,7 @@ @@ -2072,7 +2099,7 @@ @@ -2106,6 +2133,102 @@ > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Stackless/core/stackless_impl.h b/Stackless/core/stackless_impl.h index 6c9278c0d3c68d..3d4607907cd774 100644 --- a/Stackless/core/stackless_impl.h +++ b/Stackless/core/stackless_impl.h @@ -17,6 +17,8 @@ extern "C" { #include "core/stackless_structs.h" #include "pickling/prickelpit.h" +#include + #undef STACKLESS_SPY /* * if a platform wants to support self-inspection via _peek, diff --git a/Stackless/tealet/platf_slp/mkswitch_stack.py b/Stackless/tealet/platf_slp/mkswitch_stack.py new file mode 100644 index 00000000000000..5a047ef7f2998e --- /dev/null +++ b/Stackless/tealet/platf_slp/mkswitch_stack.py @@ -0,0 +1,71 @@ +""" + mkswitch_stack.py + + Purpose: + Generate an include file from the platform dependant + include files mentioned in slp_platformselect.h . + + The existing slp_switch implementations are calling + the macros slp_save_state and slp_restore_state. + Now I want to support real stack switching, that is, + the stack is not modified in place, but we jump to + a different stack, without copying anything. + This costs a lot of memory and should be used for + a few high-speed tasklets, only. + + In order to keep things simple, I'm not special-casing + the support macroes, but use a different macro set. + The machine code is the same, therefore the implementation + can be generated from the existing include files. + + We generate a new include file called slp_switch_stack.h . +""" + +def parse_platformselect(): + fin_name = "slp_platformselect.h" + fin = file(fin_name) + fout_name = "slp_switch_stack.h" + fout = file(fout_name, "w") + import sys + print>>fout, "/* this file is generated by mkswitch_stack.py, don't edit */" + print>>fout + for line in fin: + tokens = line.split() + if not tokens: continue + tok = tokens[0] + if tok == "#endif": + print>>fout, line + break # done + if tok in ("#if", "#elif"): + print>>fout, line + elif tok == "#include": + finc_name = tokens[1][1:-1] + txt = parse_switch(finc_name) + print>>fout, txt + +edits = ( + ("slp_switch", "slp_switch_stack"), + ("SLP_SAVE_STATE", "SLP_STACK_BEGIN"), + ("SLP_RESTORE_STATE", "SLP_STACK_END"), +) + +def parse_switch(fname): + f = file(fname) + res = [] + for line in f: + if line.strip() == "static int": + res.append(line) + break + for line in f: + res.append(line) + if line.rstrip() == "}": + break + # end of procedure. + # now substitute + s = "".join(res) + for txt, repl in edits: + s = s.replace(txt, repl) + return s + +if __name__ == "__main__": + parse_platformselect() \ No newline at end of file diff --git a/Stackless/tealet/platf_slp/slp_fallback.h b/Stackless/tealet/platf_slp/slp_fallback.h new file mode 100644 index 00000000000000..ef4687f3df025b --- /dev/null +++ b/Stackless/tealet/platf_slp/slp_fallback.h @@ -0,0 +1,67 @@ +/* slp_fallback.h + * Implements a fallback mechanism where the stacless python switching code be used. + * Stackless python switching code uses global variables to communicate and thus is not + * thread safe. We do not attempt to provide a lock for those global variables here, it + * is up to the user in each case to ensure that switching only occurs on one thread at + * a time. + */ + #include + + +/* rename the slp_switch function that the slp headers will define */ +#define slp_switch fallback_slp_switch + +/* sttic vars to pass inforamtion around. This is what makes this non-threadsafe */ +typedef void *(*save_restore_t)(void*, void*); +static save_restore_t fallback_save_state=NULL, fallback_restore_state=NULL; +static void *fallback_extra=NULL; +static void *fallback_newstack = NULL; + +/* call the provided save_satate method with its arguments. + * compute a proper stack delta and store the result for later + */ +#define SLP_SAVE_STATE(stackref, stsizediff) do {\ + intptr_t stsizeb; \ + void *newstack; \ + stackref += STACK_MAGIC; \ + newstack = fallback_save_state(stackref, fallback_extra); \ + fallback_newstack = newstack; /* store this for restore_state */ \ + if ((intptr_t)newstack & 1) \ + return (int)newstack; \ + /* compute the delta expected by old switching code */ \ + stsizediff = ((intptr_t*) newstack - (intptr_t*)stackref) * sizeof(intptr_t); \ +} while (0) + +/* call the restore_state using the stored data */ +#define SLP_RESTORE_STATE() do { \ + fallback_restore_state(fallback_newstack, fallback_extra); \ +} while(0) + +#define SLP_EVAL +#include "slp_platformselect.h" + +/* This is a wrapper that takes care of setting the appropriate globals */ +#undef slp_switch +static void *slp_switch(save_restore_t save_state, + save_restore_t restore_state, + void *extra) +{ + /* need to store the restore information on the stack */ + void *result; + fallback_save_state = save_state; + fallback_restore_state = restore_state; + fallback_extra = extra; + + fallback_slp_switch(); + result = fallback_newstack; + + /* clearing them again is prudent */ + fallback_save_state = fallback_restore_state = NULL; + fallback_extra = fallback_newstack = NULL; + + /* successful switch was indicated by save_state returning an even result */ + if (! ((intptr_t)result & 1)) + result = NULL; + /* otherwise it is 1 or -1 */ + return result; +} \ No newline at end of file diff --git a/Stackless/tealet/platf_slp/slp_platformselect.h b/Stackless/tealet/platf_slp/slp_platformselect.h new file mode 100644 index 00000000000000..90b46370c6e463 --- /dev/null +++ b/Stackless/tealet/platf_slp/slp_platformselect.h @@ -0,0 +1,86 @@ +/* + * Platform Selection for Stackless Python + */ + +#if defined(_M_IX86) +#include "switch_x86_msvc.h" /* MS Visual Studio on X86 */ +#elif defined(_M_X64) +#include "switch_x64_msvc.h" /* MS Visual Studio on X64 */ +#elif defined(__GNUC__) && defined(__i386__) +#include "switch_x86_unix.h" /* gcc on X86 */ +#elif defined(__GNUC__) && defined(__amd64__) +#include "switch_amd64_unix.h" /* gcc on amd64 */ +#elif defined(__GNUC__) && defined(__PPC__) && defined(__linux__) +#include "switch_ppc_unix.h" /* gcc on PowerPC */ +#elif defined(__GNUC__) && defined(__ppc__) && defined(__APPLE__) +#include "switch_ppc_macosx.h" /* Apple MacOS X on PowerPC */ +#elif defined(__GNUC__) && defined(sparc) && defined(sun) +#include "switch_sparc_sun_gcc.h" /* SunOS sparc with gcc */ +#elif defined(__GNUC__) && defined(__s390__) && defined(__linux__) +#include "switch_s390_unix.h" /* Linux/S390 */ +#elif defined(__GNUC__) && defined(__s390x__) && defined(__linux__) +#include "switch_s390_unix.h" /* Linux/S390 zSeries (identical) */ +#elif defined(__GNUC__) && defined(__arm__) && defined(__thumb__) +#include "switch_arm_thumb_gcc.h" /* gcc using arm thumb */ +#elif defined(__GNUC__) && defined(__arm32__) +#include "switch_arm32_gcc.h" /* gcc using arm32 */ +#elif defined(__GNUC__) && defined(__mips__) && defined(__linux__) +#include "switch_mips_unix.h" /* MIPS */ +#elif defined(SN_TARGET_PS3) +#include "switch_ps3_SNTools.h" /* Sony PS3 */ +#else +#error "platform unsupported by stackless python" +#endif + +/* default definitions if not defined in above files */ + +/* adjust slots to typical size of a few recursions on your system */ + +#ifndef CSTACK_SLOTS +#define CSTACK_SLOTS 1024 +#endif + +/* how many cstacks to cache at all */ + +#ifndef CSTACK_MAXCACHE +#define CSTACK_MAXCACHE 100 +#endif + +/* a good estimate how much the cstack level differs between + initialisation and main python code. Not critical, but saves time. + Note that this will vanish with the greenlet approach. */ + +#ifndef CSTACK_GOODGAP +#define CSTACK_GOODGAP 4096 +#endif + +/* stack size in pointer to trigger stack spilling */ + +#ifndef CSTACK_WATERMARK +#define CSTACK_WATERMARK 16384 +#endif + +/* define direction of stack growth */ + +#ifndef CSTACK_DOWNWARDS +#define CSTACK_DOWNWARDS 1 /* 0 for upwards */ +#endif + +/************************************************************** + + Don't change definitions below, please. + + **************************************************************/ + +#if CSTACK_DOWNWARDS == 1 +#define CSTACK_COMPARE(a, b) (a) < (b) +#define CSTACK_SUBTRACT(a, b) (a) - (b) +#else +#define CSTACK_COMPARE(a, b) (a) > (b) +#define CSTACK_SUBTRACT(a, b) (b) - (a) +#endif + +#define CSTACK_SAVE_NOW(tstate, stackvar) \ + ((tstate)->st.cstack_root != NULL ? \ + CSTACK_SUBTRACT((tstate)->st.cstack_root, \ + (intptr_t*)&(stackvar)) > CSTACK_WATERMARK : 1) diff --git a/Stackless/tealet/platf_slp/slp_switch_stack.h b/Stackless/tealet/platf_slp/slp_switch_stack.h new file mode 100644 index 00000000000000..4eb03f379a6f2f --- /dev/null +++ b/Stackless/tealet/platf_slp/slp_switch_stack.h @@ -0,0 +1,279 @@ +/* this file is generated by mkswitch_stack.py, don't edit */ + +#if defined(MS_WIN32) && !defined(MS_WIN64) && defined(_M_IX86) + +static int +slp_switch_stack(void) +{ + register int *stackref, stsizediff; + __asm mov stackref, esp; + /* modify EBX, ESI and EDI in order to get them preserved */ + __asm mov ebx, ebx; + __asm xchg esi, edi; + { + SLP_STACK_BEGIN(stackref, stsizediff); + __asm { + mov eax, stsizediff + add esp, eax + add ebp, eax + } + SLP_STACK_END(); + return 0; + } +#pragma warning(default:4731) +} + +#elif defined(MS_WIN64) && defined(_M_X64) + + +#elif defined(__GNUC__) && defined(__i386__) + +static int +slp_switch_stack(void) +{ + register int *stackref, stsizediff; +#if STACKLESS_FRHACK + __asm__ volatile ("" : : : "esi", "edi"); +#else + __asm__ volatile ("" : : : "ebx", "esi", "edi"); +#endif + __asm__ ("movl %%esp, %0" : "=g" (stackref)); + { + SLP_STACK_BEGIN(stackref, stsizediff); + __asm__ volatile ( + "addl %0, %%esp\n" + "addl %0, %%ebp\n" + : + : "r" (stsizediff) + ); + SLP_STACK_END(); + return 0; + } +#if STACKLESS_FRHACK + __asm__ volatile ("" : : : "esi", "edi"); +#else + __asm__ volatile ("" : : : "ebx", "esi", "edi"); +#endif +} + +#elif defined(__GNUC__) && defined(__amd64__) + +static int +slp_switch_stack(void) +{ + register long *stackref, stsizediff; + __asm__ volatile ("" : : : REGS_TO_SAVE); + __asm__ ("movq %%rsp, %0" : "=g" (stackref)); + { + SLP_STACK_BEGIN(stackref, stsizediff); + __asm__ volatile ( + "addq %0, %%rsp\n" + "addq %0, %%rbp\n" + : + : "r" (stsizediff) + ); + SLP_STACK_END(); + return 0; + } + __asm__ volatile ("" : : : REGS_TO_SAVE); +} + +#elif defined(__GNUC__) && defined(__PPC__) && defined(__linux__) + +static int +slp_switch_stack(void) +{ + register int *stackref, stsizediff; + __asm__ volatile ("" : : : REGS_TO_SAVE); + __asm__ ("mr %0, 1" : "=g" (stackref) : ); + { + SLP_STACK_BEGIN(stackref, stsizediff); + __asm__ volatile ( + "mr 11, %0\n" + "add 1, 1, 11\n" + "add 30, 30, 11\n" + : /* no outputs */ + : "g" (stsizediff) + : "11" + ); + SLP_STACK_END(); + return 0; + } + __asm__ volatile ("" : : : REGS_TO_SAVE); +} + +#elif defined(__GNUC__) && defined(__ppc__) && defined(__APPLE__) + +static int +slp_switch_stack(void) +{ + static int x = 0; + register intptr_t *stackref; + register int stsizediff; + __asm__ volatile ( + "; asm block 1\n" + : /* no outputs */ + : "r" (x) + : REGS_TO_SAVE + ); + __asm__ ("; asm block 2\n\tmr %0, r1" : "=g" (stackref) : ); + { + SLP_STACK_BEGIN(stackref, stsizediff); + __asm__ volatile ( + "; asm block 3\n" + "\tmr r11, %0\n" + "\tadd r1, r1, r11\n" + "\tadd r30, r30, r11\n" + : /* no outputs */ + : "g" (stsizediff) + : "r11" + ); + SLP_STACK_END(); + return 0; + } +} + +#elif defined(__GNUC__) && defined(sparc) && defined(sun) + +static int +slp_switch_stack(void) +{ + register int *stackref, stsizediff; + + /* Put the stack pointer into stackref */ + + /* Sparc special: at first, flush register windows + */ + __asm__ volatile ( + "ta %1\n\t" + "mov %%sp, %0" + : "=r" (stackref) : "i" (ST_FLUSH_WINDOWS)); + + { /* You shalt put SLP_STACK_BEGIN into a local block */ + + SLP_STACK_BEGIN(stackref, stsizediff); + + /* Increment stack and frame pointer by stsizediff */ + + /* Sparc special: at first load new return address. + This cannot be done later, because the stack + might be overwritten again just after SLP_STACK_END + has finished. BTW: All other registers (l0-l7 and i0-i5) + might be clobbered too. + */ + __asm__ volatile ( + "ld [%0+60], %%i7\n\t" + "add %1, %%sp, %%sp\n\t" + "add %1, %%fp, %%fp" + : : "r" (_cst->stack), "r" (stsizediff) + : "%l0", "%l1", "%l2", "%l3", "%l4", "%l5", "%l6", "%l7", + "%i0", "%i1", "%i2", "%i3", "%i4", "%i5"); + + SLP_STACK_END(); + + /* Run far away as fast as possible, don't look back at the sins. + * The LORD rained down burning sulfur on Sodom and Gomorra ... + */ + + /* Sparc special: Must make it *very* clear to the CPU that + it shouldn't look back into the register windows + */ + __asm__ volatile ( "ta %0" : : "i" (ST_CLEAN_WINDOWS)); + return 0; + } +} + +#elif defined(__GNUC__) && defined(__s390__) && defined(__linux__) + +static int +slp_switch_stack(void) +{ + register int *stackref, stsizediff; + __asm__ volatile ("" : : : REGS_TO_SAVE); + __asm__ ("lr %0, 15" : "=g" (stackref) : ); + { + SLP_STACK_BEGIN(stackref, stsizediff); + __asm__ volatile ( + "ar 15, %0" + : /* no outputs */ + : "g" (stsizediff) + ); + SLP_STACK_END(); + return 0; + } + __asm__ volatile ("" : : : REGS_TO_SAVE); +} + +#elif defined(__GNUC__) && defined(__s390x__) && defined(__linux__) + +static int +slp_switch_stack(void) +{ + register int *stackref, stsizediff; + __asm__ volatile ("" : : : REGS_TO_SAVE); + __asm__ ("lr %0, 15" : "=g" (stackref) : ); + { + SLP_STACK_BEGIN(stackref, stsizediff); + __asm__ volatile ( + "ar 15, %0" + : /* no outputs */ + : "g" (stsizediff) + ); + SLP_STACK_END(); + return 0; + } + __asm__ volatile ("" : : : REGS_TO_SAVE); +} + +#elif defined(__GNUC__) && defined(__arm__) && defined(__thumb__) + + +#elif defined(__GNUC__) && defined(__arm32__) + +static int +slp_switch_stack(void) +{ + register int *stackref, stsizediff; + __asm__ volatile ("" : : : REGS_TO_SAVE); + __asm__ ("mov %0,sp" : "=g" (stackref)); + { + SLP_STACK_BEGIN(stackref, stsizediff); + __asm__ volatile ( + "add sp,sp,%0\n" + "add fp,fp,%0\n" + : + : "r" (stsizediff) + ); + SLP_STACK_END(); + return 0; + } + __asm__ volatile ("" : : : REGS_TO_SAVE); +} + +#elif defined(__GNUC__) && defined(__mips__) && defined(__linux__) + +static int +slp_switch_stack(void) +{ + register int *stackref, stsizediff; + /* __asm__ __volatile__ ("" : : : REGS_TO_SAVE); */ + __asm__ ("move %0, $29" : "=r" (stackref) : ); + { + SLP_STACK_BEGIN(stackref, stsizediff); + __asm__ __volatile__ ( +#ifdef __mips64 + "daddu $29, %0\n" +#else + "addu $29, %0\n" +#endif + : /* no outputs */ + : "r" (stsizediff) + ); + SLP_STACK_END(); + } + /* __asm__ __volatile__ ("" : : : REGS_TO_SAVE); */ + return 0; +} + +#endif + diff --git a/Stackless/tealet/platf_slp/switch_amd64_unix.h b/Stackless/tealet/platf_slp/switch_amd64_unix.h new file mode 100644 index 00000000000000..1de0db17a54f48 --- /dev/null +++ b/Stackless/tealet/platf_slp/switch_amd64_unix.h @@ -0,0 +1,142 @@ +/* + * this is the internal transfer function. + * + * HISTORY + * 24-Oct-11 Anselm Kruis + * Reworked based on the AMD64 ABI spec. + * 26-Jul-10 Jeff Senn + * Got this to work (rather than crash consistently) + * on OS-X 10.6 by adding more registers to save set. + * Not sure what is the minimal set of regs, nor if + * this is completely stable and works for all compiler + * variations. + * 01-Apr-04 Hye-Shik Chang + * Ported from i386 to amd64. + * 24-Nov-02 Christian Tismer + * needed to add another magic constant to insure + * that f in slp_eval_frame(PyFrameObject *f) + * STACK_REFPLUS will probably be 1 in most cases. + * gets included into the saved stack area. + * 17-Sep-02 Christian Tismer + * after virtualizing stack save/restore, the + * stack size shrunk a bit. Needed to introduce + * an adjustment STACK_MAGIC per platform. + * 15-Sep-02 Gerd Woetzel + * slightly changed framework for spark + * 31-Avr-02 Armin Rigo + * Added ebx, esi and edi register-saves. + * 01-Mar-02 Samual M. Rushing + * Ported from i386. + */ + +#define STACK_REFPLUS 1 + +#ifdef SLP_EVAL + +/* #define STACK_MAGIC 3 */ +/* the above works fine with gcc 2.96, but 2.95.3 wants this */ +#define STACK_MAGIC 0 + + +/* + * In order to switch the stack, we use the fact, that the compiler + * already knows how to preserve registers accross function calls. + * + * The relevant AMD64 ABI specifigation pecisely defines which registers + * must be preserved and which registers may be modified. + * We use a gcc inline assembly feature to pretend that the inline + * assembly block modifies the registers to be preserved. As a result, + * the compiler emits code to preserve those registers. + * + * The AMD64 ABI Draft 0.99.5, Figure 3.4 "Register usage" + * defines the following registers as "Preserved across + * function calls": + * %rbx callee-saved register; optionally used as base pointer + * %rsp stack pointer + * %rbp callee-saved register; optional used as frame pointer + * %r12 - %r15 callee-saved registers + * %mxcsr SSE2 control and status word + * %fcw x87 control word + * + * The compiler always preserves the %rsp register accros a function call. + * + * Depending on the usage of a frame pointer, which is optional + * for the AMD64 ABI, the compiler already preserves the %rbp + * register. Unfortunately, we must not add "rbp" to the clobber list, if + * rbp is used as a frame pointer (won't compile). Therefore we save + * rbp manually. + * + * For the other registers (except %mxcsr and %fcw) we tell the compiler, + * that we are going to clobber the registers. The compiler will then save the registers + * for us. (However the compiler gives no guarantee, when it will restore + * the registers.) And the compiler only preserves those registers, that must + * be preserved according to the calling convention. It does not preserve any other + * register, that may be modified during a function call. Therefore specifying additional + * registers has no effect at all. Take a look at the generated assembly code! + * + * If we want more control, we need to preserve the + * registers explicitly similar to %mxcsr, %fcw and %rbp. + * + */ + +#if 1 +/* Registers marked as clobbered, minimum set according to the ABI spec. */ +#define REGS_CLOBBERED "rbx", "r12", "r13", "r14", "r15" +#else +/* Maximum possible clobber list. It gives the same assembly code as the minimum list. + If the ABI evolves, it might be necessary to add some of these registers */ +#define REGS_CLOBBERED "memory", "rax", "rbx", "rcx", "rdx", "rsi", "rdi", \ + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", \ + "st(1)", "st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)", \ + "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", \ + "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15" +#endif + +/* + * You may want to make the function static enable optimizations. + * However, the ABI SPEC does not apply to static functions. Therefore + * I make slp_switch a regular global function. + */ +#if 0 +static +#endif +int +slp_switch(void) +{ + register long *stackref, stsizediff; + void * rbp; int mxcsr; short x87cw; + __asm__ volatile ( + "fstcw %0\n\t" + "stmxcsr %1\n\t" + "movq %%rbp, %2\n\t" + : "=m" (x87cw), "=m" (mxcsr), "=m" (rbp) : : REGS_CLOBBERED ); + __asm__ ("movq %%rsp, %0" : "=g" (stackref)); + { + SLP_SAVE_STATE(stackref, stsizediff); + __asm__ volatile ( + "addq %0, %%rsp\n\t" + "addq %0, %%rbp\n\t" + : + : "r" (stsizediff) + ); + SLP_RESTORE_STATE(); + __asm__ volatile ( + "movq %2, %%rbp\n\t" + "ldmxcsr %1\n\t" + "fldcw %0\n\t" + : : "m" (x87cw), "m" (mxcsr), "m" (rbp)); + return 0; + } +} + +#endif +/* + * further self-processing support + */ + +/* + * if you want to add self-inspection tools, place them + * here. See the x86_msvc for the necessary defines. + * These features are highly experimental und not + * essential yet. + */ diff --git a/Stackless/tealet/platf_slp/switch_arm32_gcc.h b/Stackless/tealet/platf_slp/switch_arm32_gcc.h new file mode 100644 index 00000000000000..fda4b7d58536f9 --- /dev/null +++ b/Stackless/tealet/platf_slp/switch_arm32_gcc.h @@ -0,0 +1,50 @@ +/* + * this is the internal transfer function. + * + * HISTORY + * 14-Aug-06 File creation. Ported from Arm Thumb. Sylvain Baro + * 3-Sep-06 Commented out saving of r1-r3 (r4 already commented out) as I + * read that these do not need to be saved. Also added notes and + * errors related to the frame pointer. Richard Tew. + * + * NOTES + * + * It is not possible to detect if fp is used or not, so the supplied + * switch function needs to support it, so that you can remove it if + * it does not apply to you. + * + * POSSIBLE ERRORS + * + * "fp cannot be used in asm here" + * + * - Try commenting out "fp" in REGS_TO_SAVE. + * + */ + +#define STACK_REFPLUS 1 + +#ifdef SLP_EVAL +#define STACK_MAGIC 0 +#define REGS_TO_SAVE /*"r1", "r2", "r3", "r4",*/ "r5", "r6", "fp", "ip", "lr" + +static int +slp_switch(void) +{ + register int *stackref, stsizediff; + __asm__ volatile ("" : : : REGS_TO_SAVE); + __asm__ ("mov %0,sp" : "=g" (stackref)); + { + SLP_SAVE_STATE(stackref, stsizediff); + __asm__ volatile ( + "add sp,sp,%0\n" + "add fp,fp,%0\n" + : + : "r" (stsizediff) + ); + SLP_RESTORE_STATE(); + return 0; + } + __asm__ volatile ("" : : : REGS_TO_SAVE); +} + +#endif diff --git a/Stackless/tealet/platf_slp/switch_arm_thumb_gas.s b/Stackless/tealet/platf_slp/switch_arm_thumb_gas.s new file mode 100644 index 00000000000000..545ffe209cbdd0 --- /dev/null +++ b/Stackless/tealet/platf_slp/switch_arm_thumb_gas.s @@ -0,0 +1,70 @@ +@ The stack switching logic for the arm thumb instruction set. +@ Written by Richard Tew, 2nd May 2006. +@ +@ It is not possible to do this as inline assembler as gcc generates functions +@ where the stack register is preserved, so any changes we make to it will be +@ discarded. However, any stack copying we have done is left in place, which +@ results in corrupt state. +@ +@ This adapts the MASM approach written by Kristjan Jonsson, where early +@ returns from SLP_SAVE_STATE (within slp_save_state) are flagged by setting +@ the low bit of the return value. This either indicates that there was an +@ error (-1) or that we do not need to restore the stack (1) as we are only +@ here to save the current one. +@ +@ I know little arm thumb assembly, so in case anyone else wants to know the +@ approach I took, I will document it here. The first uncertainty was which +@ registers should be saved. I took this section of code from the assembler +@ generated by gcc with a command line of: +@ gcc -mthumb -S slp_transfer.c +@ where slp_transfer included the inline version of slp_switch for the arm +@ thumb. Another initial uncertainty were the instructions needed to: +@ a) obtain the stack pointer. (mov r0, sp) +@ b) restore the stack pointer. (add sp, sp, r0) +@ which were used in the original inlined assembly. The rest of the code was +@ just patched together based on Kristjan's existing masm source code. + +@ (int) slp_switch (void); + .thumb + .align 2 + .global slp_switch + .type slp_switch, %function @ Apparently useful for .map files. + .thumb_func + +slp_switch: + push {r4, r5, r6, r7, lr} + mov r7, fp + mov r6, sl + mov r5, r9 + mov r4, r8 + push {r4, r5, r6, r7} + + mov r0, sp + bl slp_save_state @ diff = slp_save_state([r0]stackref) + + mov r1, #0 @ r1 = -1 + mvn r1, r1 + + cmp r0, r1 @ if (diff == -1) + beq .exit @ return -1; + + cmp r0, #1 @ if (diff == 1) + beq .no_restore @ return 0; + +.restore: + add sp, sp, r0 @ Adjust the stack pointer for the state we are restoring. + + bl slp_restore_state @ slp_restore_state() + +.no_restore: + mov r0, #0 @ Switch successful (whether we restored or not). + +.exit: + pop {r2, r3, r4, r5} + mov r8, r2 + mov r9, r3 + mov sl, r4 + mov fp, r5 + pop {r4, r5, r6, r7, pc} + + .size slp_switch, .-slp_switch @ Apparently useful for .map files. diff --git a/Stackless/tealet/platf_slp/switch_arm_thumb_gcc.h b/Stackless/tealet/platf_slp/switch_arm_thumb_gcc.h new file mode 100644 index 00000000000000..489c920f14c2c4 --- /dev/null +++ b/Stackless/tealet/platf_slp/switch_arm_thumb_gcc.h @@ -0,0 +1,10 @@ + +#define STACK_REFPLUS 1 +#define STACK_MAGIC 0 + +/* Use the generic support for an external assembly language slp_switch function. */ +#define EXTERNAL_ASM + +#ifdef SLP_EVAL +/* This always uses the external masm assembly file. */ +#endif diff --git a/Stackless/tealet/platf_slp/switch_mips_unix.h b/Stackless/tealet/platf_slp/switch_mips_unix.h new file mode 100644 index 00000000000000..e507c42ec0990e --- /dev/null +++ b/Stackless/tealet/platf_slp/switch_mips_unix.h @@ -0,0 +1,56 @@ +/* + * this is the internal transfer function. + * + * HISTORY + * 05-Jan-08 Thiemo Seufer + * Ported from ppc. + */ + +#define STACK_REFPLUS 1 + +#ifdef SLP_EVAL + +#define STACK_MAGIC 0 + +#ifdef __mips64 +#define REGS_TO_SAVE "$16", "$17", "$18", "$19", "$20", "$21", "$22", \ + "$23", "$28", "$30" +#else +#define REGS_TO_SAVE "$16", "$17", "$18", "$19", "$20", "$21", "$22", \ + "$23", "$30" +#endif +static int +slp_switch(void) +{ + register int *stackref, stsizediff; + /* __asm__ __volatile__ ("" : : : REGS_TO_SAVE); */ + __asm__ ("move %0, $29" : "=r" (stackref) : ); + { + SLP_SAVE_STATE(stackref, stsizediff); + __asm__ __volatile__ ( +#ifdef __mips64 + "daddu $29, %0\n" +#else + "addu $29, %0\n" +#endif + : /* no outputs */ + : "r" (stsizediff) + ); + SLP_RESTORE_STATE(); + } + /* __asm__ __volatile__ ("" : : : REGS_TO_SAVE); */ + return 0; +} + +#endif + +/* + * further self-processing support + */ + +/* + * if you want to add self-inspection tools, place them + * here. See the x86_msvc for the necessary defines. + * These features are highly experimental und not + * essential yet. + */ diff --git a/Stackless/tealet/platf_slp/switch_ppc_macosx.h b/Stackless/tealet/platf_slp/switch_ppc_macosx.h new file mode 100644 index 00000000000000..d582d3125f43bf --- /dev/null +++ b/Stackless/tealet/platf_slp/switch_ppc_macosx.h @@ -0,0 +1,85 @@ +/* + * this is the internal transfer function. + * + * HISTORY + * 14-Jan-04 Bob Ippolito + * added cr2-cr4 to the registers to be saved. + * Open questions: Should we save FP registers? + * What about vector registers? + * Differences between darwin and unix? + * 24-Nov-02 Christian Tismer + * needed to add another magic constant to insure + * that f in slp_eval_frame(PyFrameObject *f) + * gets included into the saved stack area. + * STACK_REFPLUS will probably be 1 in most cases. + * 17-Sep-02 Christian Tismer + * after virtualizing stack save/restore, the + * stack size shrunk a bit. Needed to introduce + * an adjustment STACK_MAGIC per platform. + * 15-Sep-02 Gerd Woetzel + * slightly changed framework for sparc + * 29-Jun-02 Christian Tismer + * Added register 13-29, 31 saves. The same way as + * Armin Rigo did for the x86_unix version. + * This seems to be now fully functional! + * 04-Mar-02 Hye-Shik Chang + * Ported from i386. + */ + +#define STACK_REFPLUS 1 + +#ifdef SLP_EVAL + +#define STACK_MAGIC 3 + +#if STACKLESS_FRHACK +#define REGS_TO_SAVE "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", \ + "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", \ + "cr2", "cr3", "cr4" +#else +#define REGS_TO_SAVE "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", \ + "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", "r31", \ + "cr2", "cr3", "cr4" +#endif + +static int +slp_switch(void) +{ + static int x = 0; + register intptr_t *stackref; + register int stsizediff; + __asm__ volatile ( + "; asm block 1\n" + : /* no outputs */ + : "r" (x) + : REGS_TO_SAVE + ); + __asm__ ("; asm block 2\n\tmr %0, r1" : "=g" (stackref) : ); + { + SLP_SAVE_STATE(stackref, stsizediff); + __asm__ volatile ( + "; asm block 3\n" + "\tmr r11, %0\n" + "\tadd r1, r1, r11\n" + "\tadd r30, r30, r11\n" + : /* no outputs */ + : "g" (stsizediff) + : "r11" + ); + SLP_RESTORE_STATE(); + return 0; + } +} + +#endif + +/* + * further self-processing support + */ + +/* + * if you want to add self-inspection tools, place them + * here. See the x86_msvc for the necessary defines. + * These features are highly experimental und not + * essential yet. + */ diff --git a/Stackless/tealet/platf_slp/switch_ppc_unix.h b/Stackless/tealet/platf_slp/switch_ppc_unix.h new file mode 100644 index 00000000000000..f0bf07845f1dcf --- /dev/null +++ b/Stackless/tealet/platf_slp/switch_ppc_unix.h @@ -0,0 +1,73 @@ +/* + * this is the internal transfer function. + * + * HISTORY + * 14-Jan-04 Bob Ippolito + * added cr2-cr4 to the registers to be saved. + * Open questions: Should we save FP registers? + * What about vector registers? + * Differences between darwin and unix? + * 24-Nov-02 Christian Tismer + * needed to add another magic constant to insure + * that f in slp_eval_frame(PyFrameObject *f) + * gets included into the saved stack area. + * STACK_REFPLUS will probably be 1 in most cases. + * 04-Oct-02 Gustavo Niemeyer + * Ported from MacOS version. + * 17-Sep-02 Christian Tismer + * after virtualizing stack save/restore, the + * stack size shrunk a bit. Needed to introduce + * an adjustment STACK_MAGIC per platform. + * 15-Sep-02 Gerd Woetzel + * slightly changed framework for sparc + * 29-Jun-02 Christian Tismer + * Added register 13-29, 31 saves. The same way as + * Armin Rigo did for the x86_unix version. + * This seems to be now fully functional! + * 04-Mar-02 Hye-Shik Chang + * Ported from i386. + */ + +#define STACK_REFPLUS 1 + +#ifdef SLP_EVAL + +#define STACK_MAGIC 3 + +#define REGS_TO_SAVE "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", \ + "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", "r31", \ + "cr2", "cr3", "cr4" +static int +slp_switch(void) +{ + register int *stackref, stsizediff; + __asm__ volatile ("" : : : REGS_TO_SAVE); + __asm__ ("mr %0, 1" : "=g" (stackref) : ); + { + SLP_SAVE_STATE(stackref, stsizediff); + __asm__ volatile ( + "mr 11, %0\n" + "add 1, 1, 11\n" + "add 30, 30, 11\n" + : /* no outputs */ + : "g" (stsizediff) + : "11" + ); + SLP_RESTORE_STATE(); + return 0; + } + __asm__ volatile ("" : : : REGS_TO_SAVE); +} + +#endif + +/* + * further self-processing support + */ + +/* + * if you want to add self-inspection tools, place them + * here. See the x86_msvc for the necessary defines. + * These features are highly experimental und not + * essential yet. + */ diff --git a/Stackless/tealet/platf_slp/switch_ps3_SNTools.h b/Stackless/tealet/platf_slp/switch_ps3_SNTools.h new file mode 100644 index 00000000000000..ac305729b577ea --- /dev/null +++ b/Stackless/tealet/platf_slp/switch_ps3_SNTools.h @@ -0,0 +1,15 @@ +/* + * Dummy transfer function for PS3 + * Sets us up to use the assembler, which is in switch_ps3_SNTools.asm + */ + +#include + +#define STACK_REFPLUS 1 +#define STACK_MAGIC 0 /* in the assembler, we grab the stack pointer directly */ + +#define EXTERNAL_ASM + +/* use the c stack sparingly. No initial gap, and invoke stack spilling at 16k */ +#define CSTACK_GOODGAP 0 +#define CSTACK_WATERMARK (16*1024/sizeof(intptr_t)) \ No newline at end of file diff --git a/Stackless/tealet/platf_slp/switch_ps3_SNTools.s b/Stackless/tealet/platf_slp/switch_ps3_SNTools.s new file mode 100644 index 00000000000000..8597541b32731f --- /dev/null +++ b/Stackless/tealet/platf_slp/switch_ps3_SNTools.s @@ -0,0 +1,228 @@ +# +# Switching code for PS3. +# Save everyting that isn't volatile +# See the PPU ABI Specs for CELL OS lv-2, Low Level system information for details +# Kristjan Valur Jonsson, March 2010 +# + +###CODE SECTION### +.text + +##MAIN ENTRY POINT +.globl .slp_switch +.type .slp_switch,@function +.slp_switch: + #Save all 18 volatile GP registers, 18 volatile FP regs, and 12 volatile vector regs + #We need a stack frame of 144 bytes for FPR, 144 bytes for GPR, 192 bytes for VR + #plus 48 bytes for the standard stackframe = 528 bytes (which is quadword dividable) + mflr %r0 # Move LR into r0 + subi %r12,%r1,144 # Set r12 to general register save area (8*18) + bl _savegpr1_14 # Call routine to save general registers + bl _savefpr_14 # Call routine to save floating-point registers + subi %r0,%r1, 288 # Set r0 to first address beyond vector save area + bl _savevr_20 # Call routine to save vector registers + stdu %r1,(-528)(%r1) # Create stack frame + + #(save CR if necessary) + mfcr %r12 + std %r12,8(%r1) #save it in the condition save area + + #Call slp_save_state with stack pointer + addi %r3,%r1,0 # move stack pointer to r2 + bl .slp_save_state + nop# + extsw %r3,%r3 # extend sign bit to 64 + + #check the low order bit + addi %r4,%r0,1 # load 1 + and %r4,%r3,%r4 # and + cmpi 0,%r4,0 + bne- 0, NO_RESTORE # don't restore the stack (branch usually not taken) + + add %r1,%r1,%r3 # adjust stack pointer + + #Restore stack. + #now, we have to be careful. The function call will store the link register + #in the current frame (as the ABI) dictates. But it will then trample it + #with the restore! We fix this by creating a fake stack frame + stdu %r1,(-48)(%r1) # Create fake stack frame for the link register storage + bl .slp_restore_state # Restore state into new stack + nop# + addi %sp,%sp,48 # restore proper stack frame + + addi %r3,%r0,0 # set the return value (0) + + #restore the condition register + ld %r12,8(%r1) + mtcrf 0xff, %r12 + + #restore stack pointer + addi %sp,%sp,528 + + #restore vector registers + subi %r0,%r1, 288 # Set r0 to first address beyond vector save area + bl _savevr_20 # Call routine to save vector registers + + #restore general purporse registers + subi %r12,%r1,144 # Set r12 to general register save area (8*18) + bl _restgpr1_14 # Restore general registers + b _restfpr_14 # Restore floating point regs and return + +NO_RESTORE: + #are we -1 (error)? + cmpi 0,%r3,-1 + beq- 0, RETURN_NO_RESTORE # return with the -1 already in r3 + + #no error + addi %r3,%r0,0 + +RETURN_NO_RESTORE: + addi %sp,%sp,528 + ld %r0, 16(%r1) + mtlr %r0 + blr + + +#Stack saving and restoring functions from the ABI documentation. The ABI states that these should +#be part of the ABI, but the linker refuses to find them :) + +_savegpr1_14: std %r14,-144(%r12) +_savegpr1_15: std %r15,-136(%r12) +_savegpr1_16: std %r16,-128(%r12) +_savegpr1_17: std %r17,-120(%r12) +_savegpr1_18: std %r18,-112(%r12) +_savegpr1_19: std %r19,-104(%r12) +_savegpr1_20: std %r20,-96(%r12) +_savegpr1_21: std %r21,-88(%r12) +_savegpr1_22: std %r22,-80(%r12) +_savegpr1_23: std %r23,-72(%r12) +_savegpr1_24: std %r24,-64(%r12) +_savegpr1_25: std %r25,-56(%r12) +_savegpr1_26: std %r26,-48(%r12) +_savegpr1_27: std %r27,-40(%r12) +_savegpr1_28: std %r28,-32(%r12) +_savegpr1_29: std %r29,-24(%r12) +_savegpr1_30: std %r30,-16(%r12) +_savegpr1_31: std %r31,-8(%r12) + blr + +_restgpr1_14: ld %r14,-144(%r12) +_restgpr1_15: ld %r15,-136(%r12) +_restgpr1_16: ld %r16,-128(%r12) +_restgpr1_17: ld %r17,-120(%r12) +_restgpr1_18: ld %r18,-112(%r12) +_restgpr1_19: ld %r19,-104(%r12) +_restgpr1_20: ld %r20,-96(%r12) +_restgpr1_21: ld %r21,-88(%r12) +_restgpr1_22: ld %r22,-80(%r12) +_restgpr1_23: ld %r23,-72(%r12) +_restgpr1_24: ld %r24,-64(%r12) +_restgpr1_25: ld %r25,-56(%r12) +_restgpr1_26: ld %r26,-48(%r12) +_restgpr1_27: ld %r27,-40(%r12) +_restgpr1_28: ld %r28,-32(%r12) +_restgpr1_29: ld %r29,-24(%r12) +_restgpr1_30: ld %r30,-16(%r12) +_restgpr1_31: ld %r31,-8(%r12) + blr + +_savefpr_14: stfd %f14,-144(%r1) +_savefpr_15: stfd %f15,-136(%r1) +_savefpr_16: stfd %f16,-128(%r1) +_savefpr_17: stfd %f17,-120(%r1) +_savefpr_18: stfd %f18,-112(%r1) +_savefpr_19: stfd %f19,-104(%r1) +_savefpr_20: stfd %f20,-96(%r1) +_savefpr_21: stfd %f21,-88(%r1) +_savefpr_22: stfd %f22,-80(%r1) +_savefpr_23: stfd %f23,-72(%r1) +_savefpr_24: stfd %f24,-64(%r1) +_savefpr_25: stfd %f25,-56(%r1) +_savefpr_26: stfd %f26,-48(%r1) +_savefpr_27: stfd %f27,-40(%r1) +_savefpr_28: stfd %f28,-32(%r1) +_savefpr_29: stfd %f29,-24(%r1) +_savefpr_30: stfd %f30,-16(%r1) +_savefpr_31: stfd %f31,-8(%r1) + std %r0, 16(%r1) + blr + +_restfpr_14: lfd %f14,-144(%r1) +_restfpr_15: lfd %f15,-136(%r1) +_restfpr_16: lfd %f16,-128(%r1) +_restfpr_17: lfd %f17,-120(%r1) +_restfpr_18: lfd %f18,-112(%r1) +_restfpr_19: lfd %f19,-104(%r1) +_restfpr_20: lfd %f20,-96(%r1) +_restfpr_21: lfd %f21,-88(%r1) +_restfpr_22: lfd %f22,-80(%r1) +_restfpr_23: lfd %f23,-72(%r1) +_restfpr_24: lfd %f24,-64(%r1) +_restfpr_25: lfd %f25,-56(%r1) +_restfpr_26: lfd %f26,-48(%r1) +_restfpr_27: lfd %f27,-40(%r1) +_restfpr_28: lfd %f28,-32(%r1) +_restfpr_29: ld %r0, 16(%r1) + lfd %f29,-24(%r1) + mtlr %r0 + lfd %f30,-16(%r1) + lfd %f31,-8(%r1) + blr +_restfpr_30: lfd %f30,-16(%r1) +_restfpr_31: ld %r0, 16(%r1) + lfd %f31,-8(%r1) + mtlr %r0 + blr + +_savevr_20: addi %r12,%r0,-192 + stvx %v20,%r12,%r0 +_savevr_21: addi %r12,%r0,-176 + stvx %v21,%r12,%r0 +_savevr_22: addi %r12,%r0,-160 + stvx %v22,%r12,%r0 +_savevr_23: addi %r12,%r0,-144 + stvx %v23,%r12,%r0 +_savevr_24: addi %r12,%r0,-128 + stvx %v24,%r12,%r0 +_savevr_25: addi %r12,%r0,-112 + stvx %v25,%r12,%r0 +_savevr_26: addi %r12,%r0,-96 + stvx %v26,%r12,%r0 +_savevr_27: addi %r12,%r0,-80 + stvx %v27,%r12,%r0 +_savevr_28: addi %r12,%r0,-64 + stvx %v28,%r12,%r0 +_savevr_29: addi %r12,%r0,-48 + stvx %v29,%r12,%r0 +_savevr_30: addi %r12,%r0,-32 + stvx %v30,%r12,%r0 +_savevr_31: addi %r12,%r0,-16 + stvx %v31,%r12,%r0 + blr + +_restvr_20: addi %r12,%r0,-192 + lvx %v20,%r12,%r0 +_restvr_21: addi %r12,%r0,-176 + lvx %v21,%r12,%r0 +_restvr_22: addi %r12,%r0,-160 + lvx %v22,%r12,%r0 +_restvr_23: addi %r12,%r0,-144 + lvx %v23,%r12,%r0 +_restvr_24: addi %r12,%r0,-128 + lvx %v24,%r12,%r0 +_restvr_25: addi %r12,%r0,-112 + lvx %v25,%r12,%r0 +_restvr_26: addi %r12,%r0,-96 + lvx %v26,%r12,%r0 +_restvr_27: addi %r12,%r0,-80 + lvx %v27,%r12,%r0 +_restvr_28: addi %r12,%r0,-64 + lvx %v28,%r12,%r0 +_restvr_29: addi %r12,%r0,-48 + lvx %v29,%r12,%r0 +_restvr_30: addi %r12,%r0,-32 + lvx %v30,%r12,%r0 +_restvr_31: addi %r12,%r0,-16 + lvx %v31,%r12,%r0 + blr + diff --git a/Stackless/tealet/platf_slp/switch_s390_unix.h b/Stackless/tealet/platf_slp/switch_s390_unix.h new file mode 100644 index 00000000000000..8b8a1e363aab34 --- /dev/null +++ b/Stackless/tealet/platf_slp/switch_s390_unix.h @@ -0,0 +1,54 @@ +/* + * this is the internal transfer function. + * + * HISTORY + * 24-Nov-02 Christian Tismer + * needed to add another magic constant to insure + * that f in slp_eval_frame(PyFrameObject *f) + * gets included into the saved stack area. + * STACK_REFPLUS will probably be 1 in most cases. + * 06-Oct-02 Gustavo Niemeyer + * Ported to Linux/S390. + */ + +#define STACK_REFPLUS 1 + +#ifdef SLP_EVAL + +#define STACK_MAGIC 0 + +#define REGS_TO_SAVE "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r14", \ + "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", \ + "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15" + +static int +slp_switch(void) +{ + register int *stackref, stsizediff; + __asm__ volatile ("" : : : REGS_TO_SAVE); + __asm__ ("lr %0, 15" : "=g" (stackref) : ); + { + SLP_SAVE_STATE(stackref, stsizediff); + __asm__ volatile ( + "ar 15, %0" + : /* no outputs */ + : "g" (stsizediff) + ); + SLP_RESTORE_STATE(); + return 0; + } + __asm__ volatile ("" : : : REGS_TO_SAVE); +} + +#endif + +/* + * further self-processing support + */ + +/* + * if you want to add self-inspection tools, place them + * here. See the x86_msvc for the necessary defines. + * These features are highly experimental und not + * essential yet. + */ diff --git a/Stackless/tealet/platf_slp/switch_sparc_sun_gcc.h b/Stackless/tealet/platf_slp/switch_sparc_sun_gcc.h new file mode 100644 index 00000000000000..449e49faebfc9d --- /dev/null +++ b/Stackless/tealet/platf_slp/switch_sparc_sun_gcc.h @@ -0,0 +1,85 @@ +/* + * this is the internal transfer function. + * + * HISTORY + * 24-Nov-02 Christian Tismer + * needed to add another magic constant to insure + * that f in slp_eval_frame(PyFrameObject *f) + * gets included into the saved stack area. + * STACK_REFPLUS will probably be 1 in most cases. + * 17-Sep-02 Christian Tismer + * after virtualizing stack save/restore, the + * stack size shrunk a bit. Needed to introduce + * an adjustment STACK_MAGIC per platform. + * 15-Sep-02 Gerd Woetzel + * added support for SunOS sparc with gcc + */ + +#define STACK_REFPLUS 1 + +#ifdef SLP_EVAL + +#include + +#define STACK_MAGIC 0 + +static int +slp_switch(void) +{ + register int *stackref, stsizediff; + + /* Put the stack pointer into stackref */ + + /* Sparc special: at first, flush register windows + */ + __asm__ volatile ( + "ta %1\n\t" + "mov %%sp, %0" + : "=r" (stackref) : "i" (ST_FLUSH_WINDOWS)); + + { /* You shalt put SLP_SAVE_STATE into a local block */ + + SLP_SAVE_STATE(stackref, stsizediff); + + /* Increment stack and frame pointer by stsizediff */ + + /* Sparc special: at first load new return address. + This cannot be done later, because the stack + might be overwritten again just after SLP_RESTORE_STATE + has finished. BTW: All other registers (l0-l7 and i0-i5) + might be clobbered too. + */ + __asm__ volatile ( + "ld [%0+60], %%i7\n\t" + "add %1, %%sp, %%sp\n\t" + "add %1, %%fp, %%fp" + : : "r" (_cst->stack), "r" (stsizediff) + : "%l0", "%l1", "%l2", "%l3", "%l4", "%l5", "%l6", "%l7", + "%i0", "%i1", "%i2", "%i3", "%i4", "%i5"); + + SLP_RESTORE_STATE(); + + /* Run far away as fast as possible, don't look back at the sins. + * The LORD rained down burning sulfur on Sodom and Gomorra ... + */ + + /* Sparc special: Must make it *very* clear to the CPU that + it shouldn't look back into the register windows + */ + __asm__ volatile ( "ta %0" : : "i" (ST_CLEAN_WINDOWS)); + return 0; + } +} + +#endif + +/* + * further self-processing support + */ + +/* + * if you want to add self-inspection tools, place them + * here. See the x86_msvc for the necessary defines. + * These features are highly experimental und not + * essential yet. + */ diff --git a/Stackless/tealet/platf_slp/switch_x64_masm.asm b/Stackless/tealet/platf_slp/switch_x64_masm.asm new file mode 100644 index 00000000000000..5170157bc585cb --- /dev/null +++ b/Stackless/tealet/platf_slp/switch_x64_masm.asm @@ -0,0 +1,105 @@ +; +; stack switching code for MASM on x641 +; Kristjan Valur Jonsson, sept 2005 +; + + +;prototypes for our calls +slp_save_state PROTO +slp_restore_state PROTO + + +pushxmm MACRO reg + sub rsp, 16 + .allocstack 16 + movaps [rsp], reg ; faster than movups, but we must be aligned + ; .savexmm128 reg, offset (don't know what offset is, no documentation) +ENDM +popxmm MACRO reg + movaps reg, [rsp] ; faster than movups, but we must be aligned + add rsp, 16 +ENDM + +pushreg MACRO reg + push reg + .pushreg reg +ENDM +popreg MACRO reg + pop reg +ENDM + + +.code +slp_switch PROC FRAME + ;realign stack to 16 bytes after return address push, makes the following faster + sub rsp,8 + .allocstack 8 + + pushxmm xmm15 + pushxmm xmm14 + pushxmm xmm13 + pushxmm xmm12 + pushxmm xmm11 + pushxmm xmm10 + pushxmm xmm9 + pushxmm xmm8 + pushxmm xmm7 + pushxmm xmm6 + + pushreg r15 + pushreg r14 + pushreg r13 + pushreg r12 + + pushreg rbp + pushreg rbx + pushreg rdi + pushreg rsi + + sub rsp, 10h ;allocate the singlefunction argument (must be multiple of 16) + .allocstack 10h +.endprolog + + lea rcx, [rsp+10h] ;load stack base that we are saving + call slp_save_state ;pass stackpointer, return offset in eax + test rax, 1 ; an odd value means that we don't restore + jnz NORESTORE + ;actual stack switch: + add rsp, rax + call slp_restore_state + xor rax, rax ;return 0 + +EXIT: + + add rsp, 10h + popreg rsi + popreg rdi + popreg rbx + popreg rbp + + popreg r12 + popreg r13 + popreg r14 + popreg r15 + + popxmm xmm6 + popxmm xmm7 + popxmm xmm8 + popxmm xmm9 + popxmm xmm10 + popxmm xmm11 + popxmm xmm12 + popxmm xmm13 + popxmm xmm14 + popxmm xmm15 + + add rsp, 8 + ret + +NORESTORE: + sar rax, 1 ; return value is -1 for error + jmp EXIT + +slp_switch ENDP + +END \ No newline at end of file diff --git a/Stackless/tealet/platf_slp/switch_x64_msvc.h b/Stackless/tealet/platf_slp/switch_x64_msvc.h new file mode 100644 index 00000000000000..da413958d31e30 --- /dev/null +++ b/Stackless/tealet/platf_slp/switch_x64_msvc.h @@ -0,0 +1,55 @@ +/* + * this is the internal transfer function. + * + * HISTORY + * 24-Nov-02 Christian Tismer + * needed to add another magic constant to insure + * that f in slp_eval_frame(PyFrameObject *f) + * STACK_REFPLUS will probably be 1 in most cases. + * gets included into the saved stack area. + * 26-Sep-02 Christian Tismer + * again as a result of virtualized stack access, + * the compiler used less registers. Needed to + * explicit mention registers in order to get them saved. + * Thanks to Jeff Senn for pointing this out and help. + * 17-Sep-02 Christian Tismer + * after virtualizing stack save/restore, the + * stack size shrunk a bit. Needed to introduce + * an adjustment STACK_MAGIC per platform. + * 15-Sep-02 Gerd Woetzel + * slightly changed framework for sparc + * 01-Mar-02 Christian Tismer + * Initial final version after lots of iterations for i386. + */ + +#define alloca _alloca + +#define STACK_REFPLUS 1 +#define STACK_MAGIC 0 + +/* Use the generic support for an external assembly language slp_switch function. */ +#define EXTERNAL_ASM + +#ifdef SLP_EVAL +/* This always uses the external masm assembly file. */ +#endif + +/* + * further self-processing support + */ + +/* we have IsBadReadPtr available, so we can peek at objects */ +#define STACKLESS_SPY + +#ifdef IMPLEMENT_STACKLESSMODULE +#include "Windows.h" +#define CANNOT_READ_MEM(p, bytes) IsBadReadPtr(p, bytes) + +static int IS_ON_STACK(void*p) +{ + int stackref; + intptr_t stackbase = ((intptr_t)&stackref) & 0xfffff000; + return (intptr_t)p >= stackbase && (intptr_t)p < stackbase + 0x00100000; +} + +#endif diff --git a/Stackless/tealet/platf_slp/switch_x86_msvc.h b/Stackless/tealet/platf_slp/switch_x86_msvc.h new file mode 100644 index 00000000000000..bbb376733e3ef3 --- /dev/null +++ b/Stackless/tealet/platf_slp/switch_x86_msvc.h @@ -0,0 +1,94 @@ +/* + * this is the internal transfer function. + * + * HISTORY + * 24-Nov-02 Christian Tismer + * needed to add another magic constant to insure + * that f in slp_eval_frame(PyFrameObject *f) + * gets included into the saved stack area. + * STACK_REFPLUS will probably be 1 in most cases. + * 26-Sep-02 Christian Tismer + * again as a result of virtualized stack access, + * the compiler used less registers. Needed to + * explicit mention registers in order to get them saved. + * Thanks to Jeff Senn for pointing this out and help. + * 17-Sep-02 Christian Tismer + * after virtualizing stack save/restore, the + * stack size shrunk a bit. Needed to introduce + * an adjustment STACK_MAGIC per platform. + * 15-Sep-02 Gerd Woetzel + * slightly changed framework for sparc + * 01-Mar-02 Christian Tismer + * Initial final version after lots of iterations for i386. + */ + +/* for the SEH things */ +#ifndef _WINDOWS_ +#define WIN32_LEAN_AND_MEAN +#ifdef BYTE +#undef BYTE +#endif +#ifdef Yield +#undef Yield /* remove definition from Python_ast.h to avoid conflict */ +#endif +#include +#endif +#define _SEH32 + +#define alloca _alloca + +#define STACK_REFPLUS 1 + +/* use faster oparg fetch */ +#define STACKLESS_USE_ENDIAN + +/* switching related stuff */ +#ifdef SLP_EVAL + +#define STACK_MAGIC 0 + +#pragma optimize("", off) + +#pragma warning(disable:4731) /* disable ebp modification warning */ +static int +slp_switch(void) +{ + register int *stackref, stsizediff; + __asm mov stackref, esp; + /* modify EBX, ESI and EDI in order to get them preserved */ + __asm mov ebx, ebx; + __asm xchg esi, edi; + { + SLP_SAVE_STATE(stackref, stsizediff); + __asm { + mov eax, stsizediff + add esp, eax + add ebp, eax + } + SLP_RESTORE_STATE(); + return 0; + } +#pragma warning(default:4731) +} + +#endif + +/* + * further self-processing support + */ + +/* we have IsBadReadPtr available, so we can peek at objects */ +#define STACKLESS_SPY + +#ifdef IMPLEMENT_STACKLESSMODULE +#include "Windows.h" +#define CANNOT_READ_MEM(p, bytes) IsBadReadPtr(p, bytes) + +static int IS_ON_STACK(void*p) +{ + int stackref; + int stackbase = ((int)&stackref) & 0xfffff000; + return (int)p >= stackbase && (int)p < stackbase + 0x00100000; +} + +#endif diff --git a/Stackless/tealet/platf_slp/switch_x86_unix.h b/Stackless/tealet/platf_slp/switch_x86_unix.h new file mode 100644 index 00000000000000..c95362c0acbea5 --- /dev/null +++ b/Stackless/tealet/platf_slp/switch_x86_unix.h @@ -0,0 +1,70 @@ +/* + * this is the internal transfer function. + * + * HISTORY + * 24-Nov-02 Christian Tismer + * needed to add another magic constant to insure + * that f in slp_eval_frame(PyFrameObject *f) + * gets included into the saved stack area. + * STACK_REFPLUS will probably be 1 in most cases. + * 17-Sep-02 Christian Tismer + * after virtualizing stack save/restore, the + * stack size shrunk a bit. Needed to introduce + * an adjustment STACK_MAGIC per platform. + * 15-Sep-02 Gerd Woetzel + * slightly changed framework for spark + * 31-Avr-02 Armin Rigo + * Added ebx, esi and edi register-saves. + * 01-Mar-02 Samual M. Rushing + * Ported from i386. + */ + +#define STACK_REFPLUS 1 + +#ifdef SLP_EVAL + +/* #define STACK_MAGIC 3 */ +/* the above works fine with gcc 2.96, but 2.95.3 wants this */ +#define STACK_MAGIC 0 + +static int +slp_switch(void) +{ + register int *stackref, stsizediff; +#if STACKLESS_FRHACK + __asm__ volatile ("" : : : "esi", "edi"); +#else + __asm__ volatile ("" : : : "ebx", "esi", "edi", "ebp"); +#endif + __asm__ ("movl %%esp, %0" : "=g" (stackref)); + { + SLP_SAVE_STATE(stackref, stsizediff); + __asm__ volatile ( + "addl %0, %%esp\n" + "addl %0, %%ebp\n" + : + : "r" (stsizediff) + ); + SLP_RESTORE_STATE(); + return 0; + } +#if STACKLESS_FRHACK + __asm__ volatile ("" : : : "esi", "edi"); +#else + __asm__ volatile ("" : : : "ebx", "esi", "edi", "ebp"); +#endif +} + + +#endif + +/* + * further self-processing support + */ + +/* + * if you want to add self-inspection tools, place them + * here. See the x86_msvc for the necessary defines. + * These features are highly experimental und not + * essential yet. + */ diff --git a/Stackless/tealet/slp_platformselect.h b/Stackless/tealet/slp_platformselect.h new file mode 100644 index 00000000000000..e86580628f9e7d --- /dev/null +++ b/Stackless/tealet/slp_platformselect.h @@ -0,0 +1,32 @@ + +/* define USE_SLP_FALLBACK here if you want to test the SLP_FALLBACK mechanism */ +#define ENABLE_SLP_FALLBACK +/* #define USE_SLP_FALLBACK */ + +#if defined USE_SLP_FALLBACK && !defined ENABLE_SLP_FALLBACK +#define ENABLE_SLP_FALLBACK +#endif + +#ifndef USE_SLP_FALLBACK +#if defined(_M_IX86) +#include "switch_x86_msvc.h" /* MS Visual Studio on X86 */ +#elif defined(_M_X64) +#include "switch_x64_msvc.h" /* MS Visual Studio on X64 */ +#elif defined(__GNUC__) && defined(__amd64__) +#include "switch_x86_64_gcc.h" /* gcc on amd64 */ +#elif defined(__GNUC__) && defined(__i386__) +#include "switch_x86_gcc.h" /* gcc on X86 */ +#else +#ifdef ENABLE_SLP_FALLBACK +#define USE_SLP_FALLBACK +#else +#error "Unsupported platform!" +#endif +#endif +#endif + +#ifdef USE_SLP_FALLBACK +/* hope this is standard C */ +#pragma message("fallback to stackless platform support. Switching is not thread-safe") +#include "platf_slp/slp_fallback.h" +#endif diff --git a/Stackless/tealet/switch_x64_msvc.asm b/Stackless/tealet/switch_x64_msvc.asm new file mode 100644 index 00000000000000..a9ebd40409703e --- /dev/null +++ b/Stackless/tealet/switch_x64_msvc.asm @@ -0,0 +1,103 @@ +; +; stack switching code for MASM on x64 +; Kristjan Valur Jonsson, apr 2011 +; + +include macamd64.inc + +pop_reg MACRO reg + pop reg +ENDM + +load_xmm128 macro Reg, Offset + movdqa Reg, Offset[rsp] +endm + +.code + +;arguments save_state, restore_state, extra are passed in rcx, rdx, r8 respectively +;slp_switch PROC FRAME +NESTED_ENTRY slp_switch, _TEXT$00 + ; save all registers that the x64 ABI specifies as non-volatile. + ; This includes some mmx registers. May not always be necessary, + ; unless our application is doing 3D, but better safe than sorry. + alloc_stack 168; 10 * 16 bytes, plus 8 bytes to make stack 16 byte aligned + save_xmm128 xmm15, 144 + save_xmm128 xmm14, 128 + save_xmm128 xmm13, 112 + save_xmm128 xmm12, 96 + save_xmm128 xmm11, 80 + save_xmm128 xmm10, 64 + save_xmm128 xmm9, 48 + save_xmm128 xmm8, 32 + save_xmm128 xmm7, 16 + save_xmm128 xmm6, 0 + + push_reg r15 + push_reg r14 + push_reg r13 + push_reg r12 + + push_reg rbp + push_reg rbx + push_reg rdi + push_reg rsi + + sub rsp, 20h ;allocate shadow stack space for the arguments (must be multiple of 16) + .allocstack 20h +.endprolog + + ;save argments in nonvolatile registers + mov r12, rcx ;save_state + mov r13, rdx + mov r14, r8 + + ; load stack base that we are saving minus the callee argument + ; shadow stack. We don't want that clobbered + lea rcx, [rsp+20h] + mov rdx, r14 + call r12 ;pass stackpointer, return new stack pointer in eax + + ; an odd value means that we don't restore, could be + ; an error (e.g. -1) or indication that we only want + ; to save state (1). return that value to the caller. + test rax, 1 + jnz exit + + ;actual stack switch (and re-allocating the shadow stack): + lea rsp, [rax-20h] + + mov rcx, rax ;pass new stack pointer + mov rdx, r14 + call r13 + ;return the rax +EXIT: + + add rsp, 20h + pop_reg rsi + pop_reg rdi + pop_reg rbx + pop_reg rbp + + pop_reg r12 + pop_reg r13 + pop_reg r14 + pop_reg r15 + + load_xmm128 xmm15, 144 + load_xmm128 xmm14, 128 + load_xmm128 xmm13, 112 + load_xmm128 xmm12, 96 + load_xmm128 xmm11, 80 + load_xmm128 xmm10, 64 + load_xmm128 xmm9, 48 + load_xmm128 xmm8, 32 + load_xmm128 xmm7, 16 + load_xmm128 xmm6, 0 + add rsp, 168 + ret + +NESTED_END slp_switch, _TEXT$00 +;slp_switch ENDP + +END \ No newline at end of file diff --git a/Stackless/tealet/switch_x64_msvc.h b/Stackless/tealet/switch_x64_msvc.h new file mode 100644 index 00000000000000..1c0617121956b8 --- /dev/null +++ b/Stackless/tealet/switch_x64_msvc.h @@ -0,0 +1,7 @@ +/* The actual stack saving function, which just stores the stack, + * this declared in an .asm file + */ +extern void *slp_switch(void *(*save_state)(void*, void*), + void *(*restore_state)(void*, void*), + void *extra); + diff --git a/Stackless/tealet/switch_x86_64_gcc.h b/Stackless/tealet/switch_x86_64_gcc.h new file mode 100644 index 00000000000000..c240c1c261ee9b --- /dev/null +++ b/Stackless/tealet/switch_x86_64_gcc.h @@ -0,0 +1,66 @@ + +static void *slp_switch(void *(*save_state)(void*, void*), + void *(*restore_state)(void*, void*), + void *extra) +{ + void *result; + __asm__ volatile ( + "pushq %%rbp\n" + "pushq %%rbx\n" /* push the registers that may contain */ + "pushq %%rsi\n" /* some value that is meant to be saved */ + "pushq %%rdi\n" + "pushq %%rcx\n" + "pushq %%rdx\n" + "pushq %%r8\n" + "pushq %%r9\n" + "pushq %%r10\n" + "pushq %%r11\n" + "pushq %%r12\n" + "pushq %%r13\n" + "pushq %%r14\n" + "pushq %%r15\n" + + "movq %%rax, %%r12\n" /* save 'restore_state' for later */ + "movq %%rsi, %%r13\n" /* save 'extra' for later */ + + /* arg 2: extra */ + "movq %%rsp, %%rdi\n" /* arg 1: current (old) stack pointer */ + "call *%%rcx\n" /* call save_state() */ + + "testb $1, %%al\n" /* skip the rest if the return value is odd */ + "jnz 0f\n" + + "movq %%rax, %%rsp\n" /* change the stack pointer */ + + /* From now on, the stack pointer is modified, but the content of the + stack is not restored yet. It contains only garbage here. */ + + "movq %%r13, %%rsi\n" /* arg 2: extra */ + "movq %%rax, %%rdi\n" /* arg 1: current (new) stack pointer */ + "call *%%r12\n" /* call restore_state() */ + + /* The stack's content is now restored. */ + + "0:\n" + "popq %%r15\n" + "popq %%r14\n" + "popq %%r13\n" + "popq %%r12\n" + "popq %%r11\n" + "popq %%r10\n" + "popq %%r9\n" + "popq %%r8\n" + "popq %%rdx\n" + "popq %%rcx\n" + "popq %%rdi\n" + "popq %%rsi\n" + "popq %%rbx\n" + "popq %%rbp\n" + + : "=a"(result) /* output variables */ + : "a"(restore_state), /* input variables */ + "c"(save_state), + "S"(extra) + ); + return result; +} diff --git a/Stackless/tealet/switch_x86_gcc.h b/Stackless/tealet/switch_x86_gcc.h new file mode 100644 index 00000000000000..8621dcc584922f --- /dev/null +++ b/Stackless/tealet/switch_x86_gcc.h @@ -0,0 +1,53 @@ + +static void *slp_switch(void *(*save_state)(void*, void*), + void *(*restore_state)(void*, void*), + void *extra) +{ + void *result; + __asm__ volatile ( + "pushl %%ebp\n" + "pushl %%ebx\n" /* push the registers that may contain */ + "pushl %%esi\n" /* some value that is meant to be saved */ + "pushl %%edi\n" + "pushl %%ecx\n" + "pushl %%edx\n" + + "movl %%eax, %%esi\n" /* save 'restore_state' for later */ + "movl %%edx, %%edi\n" /* save 'extra' for later */ + + "movl %%esp, %%eax\n" + + "pushl %%edx\n" /* arg 2: extra */ + "pushl %%eax\n" /* arg 1: current (old) stack pointer */ + "call *%%ecx\n" /* call save_state() */ + + "testl $1, %%eax\n" /* skip the rest if the return value is odd */ + "jnz 0f\n" + + "movl %%eax, %%esp\n" /* change the stack pointer */ + + /* From now on, the stack pointer is modified, but the content of the + stack is not restored yet. It contains only garbage here. */ + + "pushl %%edi\n" /* arg 2: extra */ + "pushl %%eax\n" /* arg 1: current (new) stack pointer */ + "call *%%esi\n" /* call restore_state() */ + + /* The stack's content is now restored. */ + + "0:\n" + "addl $8, %%esp\n" + "popl %%edx\n" + "popl %%ecx\n" + "popl %%edi\n" + "popl %%esi\n" + "popl %%ebx\n" + "popl %%ebp\n" + + : "=a"(result) /* output variables */ + : "a"(restore_state), /* input variables */ + "c"(save_state), + "d"(extra) + ); + return result; +} diff --git a/Stackless/tealet/switch_x86_msvc.asm b/Stackless/tealet/switch_x86_msvc.asm new file mode 100644 index 00000000000000..628643e3975aec --- /dev/null +++ b/Stackless/tealet/switch_x86_msvc.asm @@ -0,0 +1,44 @@ + +.386 +.model flat, c + +.code + +slp_switch_raw PROC save_state:DWORD, restore_state:DWORD, extra:DWORD + + ;save registers. EAX ECX and EDX are available for function use and thus + ;do not have to be stored. + push ebx + push esi + push edi + push ebp + + mov esi, restore_state ; /* save 'restore_state' for later */ + mov edi, extra ; /* save 'extra' for later */ + + mov eax, esp + + push edi ; /* arg 2: extra */ + push eax ; /* arg 1: current (old) stack pointer */ + mov ecx, save_state + call ecx ; /* call save_state() */ + + test eax, 1; /* skip the restore if the return value is odd */ + jnz exit + + mov esp, eax; /* change the stack pointer */ + + push edi ; /* arg 2: extra */ + push eax ; /* arg 1: current (new) stack pointer */ + call esi ; /* call restore_state() */ + +exit: + add esp, 8 + pop ebp + pop edi + pop esi + pop ebx + ret +slp_switch_raw ENDP + +end \ No newline at end of file diff --git a/Stackless/tealet/switch_x86_msvc.h b/Stackless/tealet/switch_x86_msvc.h new file mode 100644 index 00000000000000..bd219271f8e281 --- /dev/null +++ b/Stackless/tealet/switch_x86_msvc.h @@ -0,0 +1,26 @@ +/* The actual stack saving function, which just stores the stack, + * this declared in an .asm file + */ +extern void *slp_switch_raw(void *(*save_state)(void*, void*), + void *(*restore_state)(void*, void*), + void *extra); + +#define WIN32_LEAN_AND_MEAN +#include + +/* Store any other runtime information on the local stack */ +#pragma optimize("", off) /* so that autos are stored on the stack */ +#pragma warning(disable:4733) /* disable warning about modifying FS[0] */ + +static void *slp_switch(void *(*save_state)(void*, void*), + void *(*restore_state)(void*, void*), + void *extra) +{ + /* store the structured exception state for this stack */ + DWORD seh_state = __readfsdword(FIELD_OFFSET(NT_TIB, ExceptionList)); + void * result = slp_switch_raw(save_state, restore_state, extra); + __writefsdword(FIELD_OFFSET(NT_TIB, ExceptionList), seh_state); + return result; +} +#pragma warning(default:4733) /* disable warning about modifying FS[0] */ +#pragma optimize("", on) diff --git a/Stackless/tealet/tealet.c b/Stackless/tealet/tealet.c new file mode 100644 index 00000000000000..f642d051e3afa7 --- /dev/null +++ b/Stackless/tealet/tealet.c @@ -0,0 +1,747 @@ +/********** A minimal coroutine package for C ********** + * By Armin Rigo + * Documentation: see the source code of the greenlet package from + * + * http://codespeak.net/svn/greenlet/trunk/c/_greenlet.c + */ + +#include "tealet.h" + +#include +#include +#include + +/************************************************************ + * platform specific code + */ + +/* The default stack direction is downwards, 0, but platforms + * can redefine it to upwards growing, 1. + * Since we support both architectures with descending and + * ascending stacks, we use the terms "near" and "far" + * to describe stack boundaries. In a typical architecture + * with descending stacks, "near" corresponds to a low + * address and "far" to a high address. + */ +#define STACK_DIRECTION 0 + +#include "slp_platformselect.h" + +#if STACK_DIRECTION == 0 +#define STACK_FAR_MAIN ((char*) -1) /* for stack_far */ +#define STACK_LE(a, b) ((a) <= (b)) /* to compare stack position */ +#define STACK_SUB(a, b) ((a) - (b)) /* to subtract stack pointers */ +#else +#define STACK_FAR_MAIN ((char*) 1) /* for stack_far */ +#define STACK_LE(a, b) ((b) <= (a)) /* to compare stack position */ +#define STACK_SUB(a, b) ((b) - (a)) /* to subtract stack pointers */ +#endif + +/************************************************************/ + +/* #define DEBUG_DUMP */ + +#ifdef DEBUG_DUMP +#include +#endif + +/************************************************************ + * Structures for maintaining copies of the C stack. + */ + +/* a chunk represents a single segment of saved stack */ +typedef struct tealet_chunk_t +{ + struct tealet_chunk_t *next; /* additional chunks */ + char *stack_near; /* near stack address */ + size_t size; /* amount of data saved */ + char data[1]; /* the data follows here */ +} tealet_chunk_t; + +/* The main stack structure, contains the initial chunk and a link to further + * segments. Stacks can be shared by different tealets, hence the reference + * count. They can also be linked into a list of partiallty unsaved + * stacks, that are saved only on demand. + */ +typedef struct tealet_stack_t +{ + int refcount; /* controls lifetime */ + struct tealet_stack_t **prev; /* previous 'next' pointer */ + struct tealet_stack_t *next; /* next unsaved stack */ + char *stack_far; /* the far boundary of this stack */ + size_t saved; /* total amount of memory saved in all chunks */ + struct tealet_chunk_t chunk; /* the initial chunk */ +} tealet_stack_t; + + +/* the actual tealet structure as used internally + * The main tealet will have stack_far set to the end of memory. + * "stack" is zero for a running tealet, otherwise it points + * to the saved stack, or is -1 if the sate is invalid. + * In addition, stack_far is set to NULL value to indicate + * that a tealet is exiting. + */ +typedef struct tealet_sub_t { + tealet_t base; /* the public part of the tealet */ + char *stack_far; /* the "far" end of the stack or NULL when exiting */ + tealet_stack_t *stack; /* saved stack or 0 if active or -1 if invalid*/ +#ifndef debug + int id; /* number of this tealet */ +#endif +} tealet_sub_t; + +/* The main tealet has additional fields for housekeeping */ +typedef struct tealet_main_t { + tealet_sub_t base; + void *g_user; /* user data pointer for main */ + tealet_sub_t *g_current; + tealet_sub_t *g_target; /* Temporary store when switching */ + void *g_arg; /* argument passed around when switching */ + tealet_alloc_t g_alloc; /* the allocation context used */ + tealet_stack_t *g_prev; /* previously active unsaved stacks */ +#ifndef NDEBUG + int g_tealets; /* number of active tealets excluding main */ + int g_counter; /* total number of tealets */ +#endif +} tealet_main_t; + +#define TEALET_IS_MAIN_STACK(t) (((tealet_sub_t *)(t))->stack_far == STACK_FAR_MAIN) +#define TEALET_GET_MAIN(t) ((tealet_main_t *)(((tealet_t *)(t))->main)) + +/************************************************************/ + +int (*_tealet_switchstack)(tealet_main_t*); +int (*_tealet_initialstub)(tealet_main_t*, tealet_run_t run, void*); + +/************************************************************ + * helpers to call the malloc functions provided by the user + */ +static void *tealet_int_malloc(tealet_main_t *main, size_t size) +{ + return main->g_alloc.malloc_p(size, main->g_alloc.context); +} +static void tealet_int_free(tealet_main_t *main, void *ptr) +{ + main->g_alloc.free_p(ptr, main->g_alloc.context); +} + +/************************************************************* + * actual stack management routines. Copying, growing + * restoring, duplicating, deleting + */ +static tealet_stack_t *tealet_stack_new(tealet_main_t *main, + char *stack_near, char *stack_far, size_t size) +{ + size_t tsize; + tealet_stack_t *s; + + tsize = offsetof(tealet_stack_t, chunk.data[0]) + size; + s = (tealet_stack_t*)tealet_int_malloc(main, tsize); + if (!s) + return NULL; + s->refcount = 1; + s->prev = NULL; + s->stack_far = stack_far; + s->saved = size; + + s->chunk.next = NULL; + s->chunk.stack_near = stack_near; + s->chunk.size = size; +#if STACK_DIRECTION == 0 + memcpy(&s->chunk.data[0], stack_near, size); +#else + memcpy(&s->chunk.data[0], stack_near-size, size); +#endif + return s; +} + +static int tealet_stack_grow(tealet_main_t *main, + tealet_stack_t *stack, size_t size) +{ + tealet_chunk_t *chunk; + size_t tsize, diff; + assert(size > stack->saved); + + diff = size - stack->saved; + tsize = offsetof(tealet_chunk_t, data[0]) + diff; + chunk = (tealet_chunk_t*)tealet_int_malloc(main, tsize); + if (!chunk) + return -1; +#if STACK_DIRECTION == 0 + chunk->stack_near = stack->chunk.stack_near + stack->saved; + memcpy(&chunk->data[0], chunk->stack_near, diff); +#else + chunk->stack_near = stack->chunk.stack_near - stack->saved; + memcpy(&chunk->data[0], chunk->stack_near - diff, diff); +#endif + chunk->size = diff; + chunk->next = stack->chunk.next; + stack->chunk.next = chunk; + stack->saved = size; + return 0; +} + +static void tealet_stack_restore(tealet_stack_t *stack) +{ + tealet_chunk_t *chunk = &stack->chunk; + do { +#if STACK_DIRECTION == 0 + memcpy(chunk->stack_near, &chunk->data[0], chunk->size); +#else + memcpy(chunk->stack_near - chunk->size, &chunk->data[0], chunk->size); +#endif + chunk = chunk->next; + } while(chunk); +} + +static tealet_stack_t *tealet_stack_dup(tealet_stack_t *stack) +{ + stack->refcount += 1; + return stack; +} + +static void tealet_stack_link(tealet_stack_t *stack, tealet_stack_t **head) +{ + assert(stack->prev == NULL); + assert(*head != stack); + if (*head) + assert((*head)->prev == head); + stack->next = *head; + if (stack->next) + stack->next->prev = &stack->next; + stack->prev = head; + *head = stack; +} + +static void tealet_stack_unlink(tealet_stack_t *stack) +{ + tealet_stack_t *next = stack->next; + assert(stack->prev); + assert(*stack->prev == stack); + if (next) + assert(next->prev == &stack->next); + + if (next) + next->prev = stack->prev; + *stack->prev = next; + stack->prev = NULL; +} + +static void tealet_stack_decref(tealet_main_t *main, tealet_stack_t *stack) +{ + tealet_chunk_t *chunk; + if (stack == NULL || --stack->refcount > 0) + return; + if (stack->prev) + tealet_stack_unlink(stack); + + chunk = stack->chunk.next; + tealet_int_free(main, (void*)stack); + while(chunk) { + tealet_chunk_t *next = chunk->next; + tealet_int_free(main, (void*)chunk); + chunk = next; + } +} + +static void tealet_stack_defunct(tealet_main_t *main, tealet_stack_t *stack) +{ + /* stack couldn't be grown. Release any extra chunks and mark stack as defunct */ + tealet_chunk_t *chunk; + chunk = stack->chunk.next; + stack->chunk.next = NULL; + stack->saved = (size_t)-1; + while(chunk) { + tealet_chunk_t *next = chunk->next; + tealet_int_free(main, (void*)chunk); + chunk = next; + } +} + + +/*************************************************************** + * utility functions for allocating and growing stacks + */ + +/* save a new stack, at least up to "saveto" */ +static tealet_stack_t *tealet_stack_saveto(tealet_main_t *main, + char *stack_near, char *stack_far, char *saveto, int *full) +{ + ptrdiff_t size; + if (STACK_LE(stack_far, saveto)) { + saveto = stack_far; + *full = 1; + } else + *full = 0; + assert(saveto != STACK_FAR_MAIN); /* can't save all of memory */ + size = STACK_SUB(saveto, stack_near); + if (size < 0) + size = 0; + return tealet_stack_new(main, (char*) stack_near, stack_far, size); +} + +static int tealet_stack_growto(tealet_main_t *main, tealet_stack_t *stack, char* saveto, + int *full, int fail_ok) +{ + /* Save more of g's stack into the heap -- at least up to 'saveto' + + g->stack_stop |________| + | | + | __ stop ......... + | | ==> : : + |________| :_______: + | | | | + | | | | + g->stack_start | | |_______| g->stack_copy + + */ + ptrdiff_t size, saved=(ptrdiff_t)stack->saved; + int fail; + + /* We shouldn't be completely saved already */ + if (stack->stack_far != STACK_FAR_MAIN) + assert(STACK_SUB(stack->stack_far, stack->chunk.stack_near) > saved); + + /* truncate the "stop" */ + if (STACK_LE(stack->stack_far, saveto)) { + saveto = stack->stack_far; + *full = 1; + } else + *full = 0; + + /* total saved size expected after this */ + assert(saveto != STACK_FAR_MAIN); /* can't save them all */ + size = STACK_SUB(saveto, stack->chunk.stack_near); + if (size <= saved) + return 0; /* nothing to do */ + + fail = tealet_stack_grow(main, stack, size); + if (fail == 0) + return 0; + + if (fail_ok) + return fail; /* caller can deal with failures */ + /* we cannot fail. Mark this stack as defunct and continue */ + tealet_stack_defunct(main, stack); + *full = 1; + return 0; +} + +/* Grow a list og stacks to a certain limit. Unlink those that + * become fully saved. + */ +static int tealet_stack_grow_list(tealet_main_t *main, tealet_stack_t *list, + char *saveto, tealet_stack_t *target, int fail_ok) +{ + while (list) { + int fail; + int full; + if (list == target) { + /* this is the stack we are switching to. We should stop here + * since previous stacks are already fully saved wrt. this. + * also, if this stack is not shared, it need not be saved + */ + if (list->refcount > 1) { + /* saving because the target stack is shared. If failure cannot + * be handled, the target will be marked invalid on error. But + * the caller of this function will have already checked that + * and will complete the switch despite such a flag. Only + * subsequent uses of this stack will fail. + */ + fail = tealet_stack_growto(main, list, saveto, &full, fail_ok); + if (fail) + return fail; + if (fail_ok) + assert(full); /* we saved it entirely */ + } + tealet_stack_unlink(list); + return 0; + } + + fail = tealet_stack_growto(main, list, saveto, &full, fail_ok); + if (fail) + return fail; + if (full) + tealet_stack_unlink(list); + list = list->next; + } + return 0; +} + +/********************************************************************* + * the save and restore callbacks. These implement all the stack + * save and restore logic using previously defined functions + */ + +/* main->g_target contains the tealet we are switching to: + * target->stack_far is the limit to which we must save the old stack + * target->stack can be NULL, indicating that the target stack + * needs not be restored. + */ +static void *tealet_save_state(void *old_stack_pointer, void *main) +{ + /* must free all the C stack up to target->stack_stop */ + tealet_main_t *g_main = (tealet_main_t *)main; + tealet_sub_t *g_target = g_main->g_target; + tealet_sub_t *g_current = g_main->g_current; + char* target_stop = g_target->stack_far; + int exiting, fail, fail_ok; + assert(target_stop != NULL); /* target is't exiting */ + assert(g_current != g_target); + + exiting = g_current->stack_far == NULL; + fail_ok = !exiting; + + /* save and unlink older stacks on demand */ + /* when coming from main, there should be no list of unsaved stacks */ + if (TEALET_IS_MAIN_STACK(g_main->g_current)) { + assert(!exiting); + assert(g_main->g_prev == NULL); + } + fail = tealet_stack_grow_list(g_main, g_main->g_prev, target_stop, g_target->stack, fail_ok); + if (fail) + return (void*) -1; + /* when returning to main, there should now be no list of unsaved stacks */ + if (TEALET_IS_MAIN_STACK(g_main->g_target)) + assert(g_main->g_prev == NULL); + + if (exiting) { + /* tealet is exiting. We don't save its stack. */ + assert(!TEALET_IS_MAIN_STACK(g_current)); + if (g_current->stack == NULL) { + /* auto-delete the tealet */ +#ifndef NDEBUG + g_main->g_tealets--; +#endif + tealet_int_free(g_main, g_current); + } else { + /* -1 means do-not-delete */ + assert(g_current->stack == (tealet_stack_t*)-1); + g_current->stack = NULL; + } + } else { + /* save the initial stack chunk */ + int full; + tealet_stack_t *stack = tealet_stack_saveto(g_main, (char*) old_stack_pointer, + g_current->stack_far, target_stop, &full); + if (!stack) { + if (fail_ok) + return (void*) -1; + assert(!TEALET_IS_MAIN_STACK(g_current)); + g_current->stack = (tealet_stack_t *)-1; /* signal invalid stack */ + } else { + g_current->stack = stack; + /* if it is partially saved, link it in to previous stacks */ + if (TEALET_IS_MAIN_STACK(g_current)) + assert(!full); /* always link in the main tealet's stack */ + if (!full) + tealet_stack_link(stack, &g_main->g_prev); + } + } + + if (g_target->stack == NULL) + return (void *) 1; /* don't restore */ + + /* return the stack pointer, it was saved here */ + return g_target->stack->chunk.stack_near; +} + +static void *tealet_restore_state(void *new_stack_pointer, void *main) +{ + tealet_main_t *g_main = (tealet_main_t *)main; + tealet_sub_t *g = g_main->g_target; + + /* Restore the heap copy back into the C stack */ + assert(g->stack != NULL); + tealet_stack_restore(g->stack); + tealet_stack_decref(g_main, g->stack); + g->stack = NULL; + return NULL; +} + +static int tealet_switchstack(tealet_main_t *g_main) +{ + /* note: we can't pass g_target simply as an argument here, because + of the mix between different call stacks: after slp_switch() it + might end up with a different value. But g_main is safe, because + it should have always the same value before and after the switch. */ + void *res; + assert(g_main->g_target); + assert(g_main->g_target != g_main->g_current); + /* if the target saved stack is invalid (due to a failure to save it + * during the exit of another tealet), we detect this here and + * report an error + * return value is: + * 0 = successful switch + * 1 = successful save only + * -1 = error, couldn't save state + * -2 = error, target tealet corrupt + */ + if (g_main->g_target->stack == (tealet_stack_t*)-1) + return -2; + { + /* make sure that optimizers, e.g. gcc -O2, won't assume that + * g_main->g_target stays unchanged across the switch and optimize it + * into a register + */ + tealet_sub_t * volatile *ptarget = &g_main->g_target; + res = slp_switch(tealet_save_state, tealet_restore_state, g_main); + g_main->g_target = *ptarget; + } + if ((int)res >= 0) + g_main->g_current = g_main->g_target; + g_main->g_target = NULL; + return (int)res; +} + +/* We are initializing and switching to a new stub, + * in order to immediately start a new tealet's execution. + * stack_far is the far end of this stack and must be + * far enough that local variables in this function get saved. + * A stack variable in the calling function is sufficient. + */ +static int tealet_initialstub(tealet_main_t *g_main, tealet_run_t run, void *stack_far) +{ + int result; + tealet_sub_t *g = g_main->g_current; + tealet_sub_t *g_target = g_main->g_target; + assert(g_target->stack == NULL); /* it is fresh */ + + assert(run); + g_target->stack_far = (char *)stack_far; + result = _tealet_switchstack(g_main); + if (result < 0) { + /* couldn't allocate stack */ + g_main->g_current = g; + return result; + } + if (result == 1) { + /* We successfully saved the source state (our caller) and initialized + * the current target without restoring state. We are the new tealet. + */ + g = g_main->g_current; + assert(g == g_target); + assert(g->stack == NULL); /* running */ + + #ifdef DEBUG_DUMP + printf("starting %p\n", g); + #endif + g_target = (tealet_sub_t *)(run((tealet_t *)g, g_main->g_arg)); + #ifdef DEBUG_DUMP + printf("ending %p -> %p\n", g, g_target); + #endif + if (tealet_exit((tealet_t*)g_target, NULL, TEALET_EXIT_DEFAULT)) + tealet_exit((tealet_t*)g_main, NULL, TEALET_EXIT_DEFAULT); /* failsafe */ + assert(!"This point should not be reached"); + } else { + /* this is a switch back into the calling tealet */ + assert(result == 0); + } + return 0; +} + +static tealet_sub_t *tealet_alloc(tealet_main_t *g_main, tealet_alloc_t *alloc) +{ + size_t size; + tealet_sub_t *g; + size = g_main == NULL ? sizeof(tealet_main_t) : sizeof(tealet_sub_t); + if (g_main != NULL) + alloc = &g_main->g_alloc; + g = (tealet_sub_t*) alloc->malloc_p(size, alloc->context); + if (g == NULL) + return NULL; + if (g_main == NULL) + g_main = (tealet_main_t *)g; + g->base.main = (tealet_t *)g_main; + g->base.data = NULL; + g->stack = NULL; + g->stack_far = NULL; +#ifndef NDEBUG + g->id = g_main->g_counter++; +#endif + return g; +} + + +/************************************************************/ + +tealet_t *tealet_initialize(tealet_alloc_t *alloc) +{ + tealet_sub_t *g; + tealet_main_t *g_main; + g = tealet_alloc(NULL, alloc); + if (g == NULL) + return NULL; + g_main = (tealet_main_t *)g; + g->stack = NULL; + g->stack_far = STACK_FAR_MAIN; + g_main->g_user = NULL; + g_main->g_current = g; + g_main->g_target = NULL; + g_main->g_arg = NULL; + g_main->g_alloc = *alloc; + g_main->g_prev = NULL; +#ifndef NDEBUG + g_main->g_tealets = 0; + g_main->g_counter = 0; +#endif + assert(TEALET_IS_MAIN_STACK(g_main)); + /* set up the following field with an indirection, which is needed + to prevent any inlining */ + _tealet_initialstub = tealet_initialstub; + _tealet_switchstack = tealet_switchstack; + return (tealet_t *)g_main; +} + +void tealet_finalize(tealet_t *tealet) +{ + tealet_main_t *g_main = TEALET_GET_MAIN(tealet); + assert(TEALET_IS_MAIN_STACK(g_main)); + assert(g_main->g_current == (tealet_sub_t *)g_main); + tealet_int_free(g_main, g_main); +} + +void *tealet_malloc(tealet_t *tealet, size_t s) +{ + tealet_main_t *g_main = TEALET_GET_MAIN(tealet); + return tealet_int_malloc(g_main, s); +} + +void tealet_free(tealet_t *tealet, void *p) +{ + tealet_main_t *g_main = TEALET_GET_MAIN(tealet); + tealet_int_free(g_main, p); +} + +tealet_t *tealet_new(tealet_t *tealet, tealet_run_t run, void **parg) +{ + tealet_sub_t *result; /* store this until we return */ + int fail; + tealet_main_t *g_main = TEALET_GET_MAIN(tealet); + assert(TEALET_IS_MAIN_STACK(g_main)); + assert(!g_main->g_target); + result = tealet_alloc(g_main, NULL); + if (result == NULL) + return NULL; /* Could not allocate */ + g_main->g_target = result; + g_main->g_arg = parg ? *parg : NULL; +#ifndef NDEBUG + g_main->g_tealets ++; +#endif + fail = _tealet_initialstub(g_main, run, (void*)&result); + if (fail) { + /* could not save stack */ + tealet_int_free(g_main, result); + g_main->g_target = NULL; +#ifndef NDEBUG + g_main->g_tealets --; +#endif + return NULL; + } + if (parg) + *parg = g_main->g_arg; + return (tealet_t*)result; +} + +int tealet_switch(tealet_t *stub, void **parg) +{ + tealet_sub_t *g_target = (tealet_sub_t *)stub; + tealet_main_t *g_main = TEALET_GET_MAIN(g_target); + int result; + if (g_target == g_main->g_current) + return 0; /* switch to self */ +#ifdef DEBUG_DUMP + printf("switch %p -> %p\n", g_main->g_current, g_target); +#endif + g_main->g_target = g_target; + g_main->g_arg = parg ? *parg : NULL; + result = _tealet_switchstack(g_main); + if (parg) + *parg = g_main->g_arg; +#ifdef DEBUG_DUMP + printf("done switch, res=%d, now in %p\n", result, g_main->g_current); +#endif + return result; +} + +int tealet_exit(tealet_t *target, void *arg, int flags) +{ + tealet_sub_t *g_target = (tealet_sub_t *)target; + tealet_main_t *g_main = TEALET_GET_MAIN(g_target); + tealet_sub_t *g_current = g_main->g_current; + char *stack_far = g_target->stack_far; + int result; + assert(g_current != (tealet_sub_t*)g_main); /* mustn't exit main */ + if (g_target == g_current) + return -2; /* invalid tealet */ + + g_current->stack_far = NULL; /* signal exit */ + assert (g_current->stack == NULL); + if (flags & TEALET_EXIT_NODELETE) + g_current->stack = (tealet_stack_t*) -1; /* signal do-not-delete */ + g_main->g_target = g_target; + g_main->g_arg = arg; + result = _tealet_switchstack(g_main); + assert(result < 0); /* only return here if there was failure */ + g_target->stack_far = stack_far; + g_current->stack = NULL; + return result; +} + +tealet_t *tealet_duplicate(tealet_t *tealet) +{ + tealet_sub_t *g_tealet = (tealet_sub_t *)tealet; + tealet_main_t *g_main = TEALET_GET_MAIN(g_tealet); + tealet_sub_t *g_copy; + assert(g_tealet != g_main->g_current && g_tealet != (tealet_sub_t*)g_main); + g_copy = tealet_alloc(g_main, NULL); + if (g_copy == NULL) + return NULL; +#ifndef NDEBUG + g_main->g_tealets++; +#endif + *g_copy = *g_tealet; + g_copy->stack = tealet_stack_dup(g_copy->stack); + return (tealet_t*)g_copy; +} + +void tealet_delete(tealet_t *target) +{ + tealet_sub_t *g_target = (tealet_sub_t *)target; + tealet_main_t *g_main = TEALET_GET_MAIN(g_target); + assert(!TEALET_IS_MAIN(target)); + tealet_stack_decref(g_main, g_target->stack); + tealet_int_free(g_main, g_target); +#ifndef NDEBUG + g_main->g_tealets--; +#endif +} + +tealet_t *tealet_current(tealet_t *tealet) +{ + tealet_main_t *g_main = TEALET_GET_MAIN(tealet); + return (tealet_t *)g_main->g_current; +} + +void **tealet_main_userpointer(tealet_t *tealet) +{ + tealet_main_t *g_main = TEALET_GET_MAIN(tealet); + return &g_main->g_user; +} + +int tealet_status(tealet_t *_tealet) +{ + tealet_sub_t *tealet = (tealet_sub_t *)_tealet; + if (tealet->stack_far == NULL) + return TEALET_STATUS_EXITED; + if (tealet->stack == (tealet_stack_t*)-1) + return TEALET_STATUS_DEFUNCT; + return TEALET_STATUS_ACTIVE; +} + +#ifndef NDEBUG +int tealet_get_count(tealet_t *tealet) +{ + return TEALET_GET_MAIN(tealet)->g_tealets; +} +#endif diff --git a/Stackless/tealet/tealet.h b/Stackless/tealet/tealet.h new file mode 100644 index 00000000000000..15f557ed9e27cd --- /dev/null +++ b/Stackless/tealet/tealet.h @@ -0,0 +1,190 @@ +/********** A minimal coroutine package for C **********/ +#ifndef _TEALET_H_ +#define _TEALET_H_ + +#include + +#ifdef WIN32 +#if defined TEALET_EXPORTS +#define TEALET_API __declspec(dllexport) +#elif defined TEALET_IMPORTS +#define TEALET_API __declspec(dllimport) +#else +#define TEALET_API +#endif +#else /* win32 */ +#define TEALET_API +#endif + + +/* A structure to define the memory allocation api used. + * the functions have C89 semantics and take an additional "context" + * pointer that they can use as they please + */ +typedef void*(*tealet_malloc_t)(size_t size, void *context); +typedef void*(*tealet_free_t)(void *ptr, void *context); +typedef struct tealet_alloc_t { + tealet_malloc_t malloc_p; + tealet_free_t free_p; + void *context; +} tealet_alloc_t; + +/* use the following macro to initialize a tealet_alloc_t + * structure with stdlib malloc functions, for convenience, e.g.: + * tealet_alloc_t stdalloc = TEALET_MALLOC; + */ +#define TEALET_MALLOC {\ + (tealet_malloc_t)&malloc, \ + (tealet_free_t)&free, \ + 0 \ +} + + +/* The user-visible tealet structure */ +typedef struct tealet_t { + struct tealet_t *main; /* pointer to the main tealet */ + void *data; /* general-purpose, store whatever you want here */ + /* private fields follow */ +} tealet_t; + +/* The "run" function of a tealet. It is called with the + * current tealet and the argument provided to its start function + */ +typedef tealet_t *(*tealet_run_t)(tealet_t *current, void *arg); + + +/* error codes. API functions that return int return a negative value + * to signal an error. + * Those that return tealet_t pointers return NULL to signal a memory + * error. + */ +#define TEALET_ERR_MEM -1 /* memory allocation failed */ +#define TEALET_ERR_DEFUNCT -2 /* the target tealet is corrupt */ + + +/* Initialize and return the main tealet. The main tealet contains the whole + * "normal" execution of the program; it starts when the program starts and + * ends when the program ends. This function and tealet_finalize() should + * be called together from the same (main) function which calls the rest of + * the program. It is fine to nest several uses of initialize/finalize, + * or to call them in multiple threads in case of multithreaded programs, + * as long as you don't try to switch between tealets created with a + * different main tealet. + */ +TEALET_API +tealet_t *tealet_initialize(tealet_alloc_t *alloc); + +/* Tear down the main tealet. Call e.g. after a thread finishes (including + * all its tealets). + */ +TEALET_API +void tealet_finalize(tealet_t *tealet); + +/* access to the tealet's allocator. This can be useful to use + * e.g. when passing data between tealets but such data cannot + * reside on the stack (except during the initial call to tealet_new) + */ +TEALET_API +void *tealet_malloc(tealet_t *tealet, size_t s); +TEALET_API +void tealet_free(tealet_t *tealet, void *p); + +/* Allocate a new tealet 'g', and call 'run(g, *arg)' in it. + * The return value of run() must be the next tealet in which to continue + * execution, which must be a different one, like for example the main tealet. + * When 'run(g)' returns, the tealet 'g' is freed. + * The return value is the new tealet, or NULL if memory allocation failed. + * Note that this tealet may have been alread freed should run(g) have + * returned by the time this function returns. + * On return, *arg contains the arg value passed in to the switch + * causing this return. + * 'arg' can be NULL, in which case NULL is passed to run and no result + * argument is passed. + */ +TEALET_API +tealet_t *tealet_new(tealet_t *tealet, tealet_run_t run, void **parg); + +/* Switch to another tealet. Execution continues there. The tealet + * passed in must not have been freed yet and must descend from + * the same main tealet as the current one. In multithreaded applications, + * it must also belong to the current thread (otherwise, segfaults). + * if 'arg' is non-NULL, the argument passed in *arg will be provided + * to the other tealet when it returns from its tealet_new() or + * tealet_switch(). + * On return, *arg contains whatever *arg was + * provided when switching back here. + * Take care to not have *arg point to stack allocated data because + * such data may be overwritten when the context switches. + */ +TEALET_API +int tealet_switch(tealet_t *target, void **parg); + +/* Exit the current tealet. Similar to tealet_switch except that it only + * ever returns if the target tealet is defunct. + * It also allows passing of an *arg to the target tealet, plus allows + * control over whether the tealet is automatically deleted or not. + * Returning with 'p' from the tealet's run funciton is equivalent to calling + * tealet_exit(p, NULL, 1). + * This function can be used as an emergency measure to return to the + * main tealet if tealet_switch() fails due to inability to save the stack. + * Note that exiting to the main tealet is always guaranteed to work. + */ +#define TEALET_EXIT_DEFAULT 0 +#define TEALET_EXIT_NODELETE 1 +TEALET_API +int tealet_exit(tealet_t *target, void *arg, int flags); + +/* Duplicate a tealet. The active tealet is duplicated + * along with its stack contents. + * This can be used, for example, to create "stubs" that can be duplicated + * and re-used to run with different arguments. + * Use this with care, because initially the duplicate will share the same + * stack data as the original. This includes any local variables, even the + * "current" argument passed to the original "run" function which may + * be obsolete by the time the duplicate is run. Use the argument passing + * mechanism to provide the copy with fresh data. + */ +TEALET_API +tealet_t *tealet_duplicate(tealet_t *tealet); + +/* Deallocate a tealet. Use this to delete a tealet that has exited + * with tealet_exit() with 'TEALET_EXIT_NODELETE', or defunct tealets. + * Active tealet can also be + * deleted, such as stubs that are no longer in use, but take care + * because any local resources in such tealets won't be freed. + */ +TEALET_API +void tealet_delete(tealet_t *target); + +/* Return the current tealet, i.e. the one in which the caller of this + * function currently is. "tealet" can be any tealet derived from the + * main tealet. + */ +TEALET_API +tealet_t *tealet_current(tealet_t *tealet); + +/* Get the address of a the tealet's main user pointer, a single + * void pointer associated with the main tealet. Use this to + * associate a (void*)value with the main tealet. + */ +TEALET_API +void **tealet_main_userpointer(tealet_t *tealet); + +/* get a tealet's status */ +#define TEALET_STATUS_ACTIVE 0 +#define TEALET_STATUS_EXITED 1 +#define TEALET_STATUS_DEFUNCT -2 +TEALET_API +int tealet_status(tealet_t *tealet); + +#ifndef NDEBUG +TEALET_API +int tealet_get_count(tealet_t *t); +#endif + +/* Convenience macros */ +#define TEALET_MAIN(t) ((t)->main) +#define TEALET_IS_MAIN(t) ((t) == TEALET_MAIN(t)) +#define TEALET_CURRENT_IS_MAIN(t) (tealet_current(t) == TEALET_MAIN(t)) + +#endif /* _TEALET_H_ */ diff --git a/Stackless/tealet/tealet.sln b/Stackless/tealet/tealet.sln new file mode 100644 index 00000000000000..7958176dfa8edd --- /dev/null +++ b/Stackless/tealet/tealet.sln @@ -0,0 +1,36 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tealet", "tealet.vcxproj", "{4D8AC602-C828-4EDC-B65A-9A439810F157}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test", "test.vcxproj", "{E03E9A66-978B-43DE-82D6-FE0F6367368F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4D8AC602-C828-4EDC-B65A-9A439810F157}.Debug|Win32.ActiveCfg = Debug|Win32 + {4D8AC602-C828-4EDC-B65A-9A439810F157}.Debug|Win32.Build.0 = Debug|Win32 + {4D8AC602-C828-4EDC-B65A-9A439810F157}.Debug|x64.ActiveCfg = Debug|x64 + {4D8AC602-C828-4EDC-B65A-9A439810F157}.Debug|x64.Build.0 = Debug|x64 + {4D8AC602-C828-4EDC-B65A-9A439810F157}.Release|Win32.ActiveCfg = Release|Win32 + {4D8AC602-C828-4EDC-B65A-9A439810F157}.Release|Win32.Build.0 = Release|Win32 + {4D8AC602-C828-4EDC-B65A-9A439810F157}.Release|x64.ActiveCfg = Release|x64 + {4D8AC602-C828-4EDC-B65A-9A439810F157}.Release|x64.Build.0 = Release|x64 + {E03E9A66-978B-43DE-82D6-FE0F6367368F}.Debug|Win32.ActiveCfg = Debug|Win32 + {E03E9A66-978B-43DE-82D6-FE0F6367368F}.Debug|Win32.Build.0 = Debug|Win32 + {E03E9A66-978B-43DE-82D6-FE0F6367368F}.Debug|x64.ActiveCfg = Debug|x64 + {E03E9A66-978B-43DE-82D6-FE0F6367368F}.Debug|x64.Build.0 = Debug|x64 + {E03E9A66-978B-43DE-82D6-FE0F6367368F}.Release|Win32.ActiveCfg = Release|Win32 + {E03E9A66-978B-43DE-82D6-FE0F6367368F}.Release|Win32.Build.0 = Release|Win32 + {E03E9A66-978B-43DE-82D6-FE0F6367368F}.Release|x64.ActiveCfg = Release|x64 + {E03E9A66-978B-43DE-82D6-FE0F6367368F}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Stackless/tealet/tealet.vcproj b/Stackless/tealet/tealet.vcproj new file mode 100644 index 00000000000000..c2dca5bb195ba2 --- /dev/null +++ b/Stackless/tealet/tealet.vcproj @@ -0,0 +1,454 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Stackless/tealet/tealet.vcxproj b/Stackless/tealet/tealet.vcxproj new file mode 100644 index 00000000000000..9be8abf16525d3 --- /dev/null +++ b/Stackless/tealet/tealet.vcxproj @@ -0,0 +1,215 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {4D8AC602-C828-4EDC-B65A-9A439810F157} + tealet + Win32Proj + + + + DynamicLibrary + Unicode + true + + + DynamicLibrary + Unicode + + + DynamicLibrary + Unicode + true + + + DynamicLibrary + Unicode + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + true + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + true + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + false + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + false + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + + + + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;TEALET_EXPORTS;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + EditAndContinue + + + true + Windows + MachineX86 + + + + + X64 + + + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;TEALET_EXPORTS;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + ProgramDatabase + + + true + Windows + MachineX64 + + + + + MaxSpeed + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;TEALET_EXPORTS;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + + + false + true + Windows + true + true + MachineX86 + + + + + X64 + + + MaxSpeed + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;TEALET_EXPORTS;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + + + true + Windows + true + true + MachineX64 + + + + + + + + + + + + + + + + + true + Assembling + ml64 /nologo /c /Zi /Fo "$(IntDir)%(Filename) .obj" "%(FullPath)" + + $(IntDir)%(Filename) .obj;%(Outputs) + true + Assembling + ml64 /nologo /c /Zi /Fo "$(IntDir)%(Filename) .obj" "%(FullPath)" + + $(IntDir)%(Filename) .obj;%(Outputs) + + + Assembling + ml -c -Zi "-Fl$(IntDir)%(Filename).lst" "-Fo$(IntDir)%(Filename).obj" "%(FullPath)" + + $(IntDir)%(Filename).obj;%(Outputs) + true + ml -c -Zi "-Fl$(IntDir)%(Filename).lst" "-Fo$(IntDir)%(Filename).obj" "%(FullPath)" + + $(IntDir)%(Filename).obj;%(Outputs) + Assembling + ml -c -Zi "-Fl$(IntDir)%(Filename).lst" "-Fo$(IntDir)%(Filename).obj" "%(FullPath)" + + $(IntDir)%(Filename).obj;%(Outputs) + true + + + + + + \ No newline at end of file diff --git a/Stackless/tealet/tealetvs2008.sln b/Stackless/tealet/tealetvs2008.sln new file mode 100644 index 00000000000000..eb422edf285782 --- /dev/null +++ b/Stackless/tealet/tealetvs2008.sln @@ -0,0 +1,39 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tealet", "tealet.vcproj", "{4D8AC602-C828-4EDC-B65A-9A439810F157}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test", "test.vcproj", "{E03E9A66-978B-43DE-82D6-FE0F6367368F}" + ProjectSection(ProjectDependencies) = postProject + {4D8AC602-C828-4EDC-B65A-9A439810F157} = {4D8AC602-C828-4EDC-B65A-9A439810F157} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4D8AC602-C828-4EDC-B65A-9A439810F157}.Debug|Win32.ActiveCfg = Debug|Win32 + {4D8AC602-C828-4EDC-B65A-9A439810F157}.Debug|Win32.Build.0 = Debug|Win32 + {4D8AC602-C828-4EDC-B65A-9A439810F157}.Debug|x64.ActiveCfg = Debug|x64 + {4D8AC602-C828-4EDC-B65A-9A439810F157}.Debug|x64.Build.0 = Debug|x64 + {4D8AC602-C828-4EDC-B65A-9A439810F157}.Release|Win32.ActiveCfg = Release|Win32 + {4D8AC602-C828-4EDC-B65A-9A439810F157}.Release|Win32.Build.0 = Release|Win32 + {4D8AC602-C828-4EDC-B65A-9A439810F157}.Release|x64.ActiveCfg = Release|x64 + {4D8AC602-C828-4EDC-B65A-9A439810F157}.Release|x64.Build.0 = Release|x64 + {E03E9A66-978B-43DE-82D6-FE0F6367368F}.Debug|Win32.ActiveCfg = Debug|Win32 + {E03E9A66-978B-43DE-82D6-FE0F6367368F}.Debug|Win32.Build.0 = Debug|Win32 + {E03E9A66-978B-43DE-82D6-FE0F6367368F}.Debug|x64.ActiveCfg = Debug|x64 + {E03E9A66-978B-43DE-82D6-FE0F6367368F}.Debug|x64.Build.0 = Debug|x64 + {E03E9A66-978B-43DE-82D6-FE0F6367368F}.Release|Win32.ActiveCfg = Release|Win32 + {E03E9A66-978B-43DE-82D6-FE0F6367368F}.Release|Win32.Build.0 = Release|Win32 + {E03E9A66-978B-43DE-82D6-FE0F6367368F}.Release|x64.ActiveCfg = Release|x64 + {E03E9A66-978B-43DE-82D6-FE0F6367368F}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Stackless/tealet/test.vcproj b/Stackless/tealet/test.vcproj new file mode 100644 index 00000000000000..3cf8e75d77f51c --- /dev/null +++ b/Stackless/tealet/test.vcproj @@ -0,0 +1,358 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Stackless/tealet/test.vcxproj b/Stackless/tealet/test.vcxproj new file mode 100644 index 00000000000000..95aedadedaa844 --- /dev/null +++ b/Stackless/tealet/test.vcxproj @@ -0,0 +1,184 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {E03E9A66-978B-43DE-82D6-FE0F6367368F} + test + Win32Proj + + + + Application + Unicode + true + + + Application + Unicode + + + Application + Unicode + true + + + Application + Unicode + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + true + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + true + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + false + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + false + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + + + + Disabled + WIN32;_DEBUG;_CONSOLE;TEALET_IMPORTS;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + EditAndContinue + + + true + Console + MachineX86 + + + + + X64 + + + Disabled + WIN32;_DEBUG;_CONSOLE;TEALET_IMPORTS;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + ProgramDatabase + + + true + Console + MachineX64 + + + + + MaxSpeed + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + + + true + Console + true + true + MachineX86 + + + + + X64 + + + MaxSpeed + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + + + true + Console + true + true + MachineX64 + + + + + + + + {4d8ac602-c828-4edc-b65a-9a439810f157} + false + + + + + + + \ No newline at end of file diff --git a/Stackless/tealet/tests.c b/Stackless/tealet/tests.c new file mode 100644 index 00000000000000..ff9c51774e8160 --- /dev/null +++ b/Stackless/tealet/tests.c @@ -0,0 +1,621 @@ +#include +#include +#include +#include "tealet.h" + +static int status = 0; +static tealet_t *g_main = NULL; +static tealet_t *the_stub = NULL; +static int newmode = 0; + + +static tealet_alloc_t talloc = TEALET_MALLOC; +void init_test() { + assert(g_main == NULL); + g_main = tealet_initialize(&talloc); + assert(tealet_current(g_main) == g_main); + status = 0; +} +void fini_test() { + assert(g_main != NULL); + assert(tealet_current(g_main) == g_main); + if (the_stub) + tealet_delete(the_stub); + the_stub = NULL; +#ifndef NDEBUG + assert(tealet_get_count(g_main) == 0); +#endif + tealet_finalize(g_main); + g_main = NULL; +} + +/**************************************************************** + *Implement copyable stubs by using a trampoline + */ +struct mystub_arg +{ + tealet_t *current; + tealet_run_t run; + void *runarg; +}; +static tealet_t *mystub_main(tealet_t *current, void *arg) +{ + void *myarg = 0; + /* the caller is in arg, return right back to him */ + tealet_switch((tealet_t*)arg, &myarg); + /* now we are back, myarg should contain the arg to the run function. + * We were possibly duplicated, so can't trust the original function args. + */ + { + struct mystub_arg sarg = *(struct mystub_arg*)myarg; + tealet_free(sarg.current, myarg); + return (sarg.run)(sarg.current, sarg.runarg); + } +} + +/* create a stub and return it */ +tealet_t *mystub_new(tealet_t *t) { + void *arg = (void*)tealet_current(t); + return tealet_new(t, mystub_main, &arg); +} + +/* run a stub */ +int mystub_run(tealet_t *stub, tealet_run_t run, void **parg) +{ + int result; + void *myarg; + /* we cannot pass arguments to a different tealet on the stack */ + struct mystub_arg *psarg = (struct mystub_arg*)tealet_malloc(stub, sizeof(struct mystub_arg)); + if (!psarg) + return TEALET_ERR_MEM; + psarg->current = stub; + psarg->run = run; + psarg->runarg = parg ? *parg : NULL; + myarg = (void*)psarg; + result = tealet_switch(stub, &myarg); + if (result) { + /* failure */ + tealet_free(stub, psarg); + return result; + } + /* pass back the arg value from the switch */ + if (parg) + *parg = myarg; + return 0; +} + + + +/**************************************/ + +/* create a tealet or stub low on the stack */ +static tealet_t * tealet_new_descend(tealet_t *t, int level, tealet_run_t run, void **parg) +{ + int boo[10]; + boo[9] = 0; + if (level > 0) + return tealet_new_descend(t, level-1, run, parg); + if (run) + return tealet_new(t, run, parg); + else + return mystub_new(t); +} + +/*************************************** + * methods for creating tealets in different ways + */ + +static tealet_t * tealet_new_rnd(tealet_t* t, tealet_run_t run, void **parg) +{ + return tealet_new_descend(t, rand() % 20, run, parg); +} + +static tealet_t * stub_new(tealet_t *t, tealet_run_t run, void **parg) +{ + tealet_t *stub = tealet_new_descend(t, rand() % 20, NULL, NULL); + int res; + if (stub == NULL) + return NULL; + if (run) + res = mystub_run(stub, run, parg); + else + res = 0; + if (res) { + tealet_delete(stub); + assert(res == TEALET_ERR_MEM); + return NULL; + } + return stub; +} + +static tealet_t * stub_new2(tealet_t *t, tealet_run_t run, void **parg) +{ + tealet_t *dup, *stub; + int res; + stub = tealet_new_descend(t, rand() % 20, NULL, NULL); + if (stub == NULL) + return NULL; + dup = tealet_duplicate(stub); + if (stub == NULL) { + tealet_delete(stub); + return NULL; + } + if (run) + res = mystub_run(dup, run, parg); + else + res = 0; + tealet_delete(stub); + if (res) { + tealet_delete(dup); + assert(res == TEALET_ERR_MEM); + return NULL; + } + return dup; +} + +static tealet_t * stub_new3(tealet_t *t, tealet_run_t run, void **parg) +{ + tealet_t *dup; + int res; + if ((rand()%10) == 0) + if (the_stub != NULL) { + tealet_delete(the_stub); + the_stub = NULL; + } + if (the_stub == NULL) + the_stub = tealet_new_descend(t, rand() % 20, NULL, NULL); + if (the_stub == NULL) + return NULL; + dup = tealet_duplicate(the_stub); + if (dup == NULL) + return NULL; + if (run) { + res = mystub_run(dup, run, parg); + if (res) { + tealet_delete(dup); + assert(res == TEALET_ERR_MEM); + return NULL; + } + } + return dup; +} + + +typedef tealet_t* (*t_new)(tealet_t *, tealet_run_t, void**); +static t_new newarray[] = {tealet_new_rnd, stub_new, stub_new2, stub_new3}; + +static t_new get_new(){ + if (newmode >= 0) + return newarray[newmode]; + return newarray[rand() % (sizeof(newarray)/sizeof(*newarray))]; +} +#define tealet_new get_new() + +/************************************************************/ + +void test_main_current(void) +{ + init_test(); + fini_test(); +} + +/************************************************************/ + +tealet_t *test_simple_run(tealet_t *t1, void *arg) +{ + assert(t1 != g_main); + status = 1; + return g_main; +} + +void test_simple(void) +{ + init_test(); + tealet_new(g_main, test_simple_run, NULL); + assert(status == 1); + fini_test(); +} + +/*************************************************************/ + +tealet_t *test_status_run(tealet_t *t1, void *arg) +{ + assert(t1 == tealet_current(t1)); + assert(!TEALET_IS_MAIN(t1)); + assert(tealet_status(t1) == TEALET_STATUS_ACTIVE); + return g_main; +} + +void test_status(void) +{ + tealet_t *stub1; + init_test(); + + assert(tealet_status(g_main) == TEALET_STATUS_ACTIVE); + assert(TEALET_IS_MAIN(g_main)); + + stub1 = tealet_new(g_main, NULL, NULL); + assert(tealet_status(stub1) == TEALET_STATUS_ACTIVE); + assert(!TEALET_IS_MAIN(stub1)); + mystub_run(stub1, test_status_run, NULL); + + fini_test(); +} + + +/*************************************************************/ +tealet_t *test_exit_run(tealet_t *t1, void *arg) +{ + int result; + assert(t1 != g_main); + status += 1; + result = tealet_exit(g_main, NULL, (int)arg); + assert(0); + return (tealet_t*)-1; +} + +void test_exit(void) +{ + tealet_t *stub1, *stub2; + int result; + void *arg; + init_test(); + stub1 = tealet_new(g_main, NULL, NULL); + stub2 = tealet_duplicate(stub1); + arg = (void*)TEALET_EXIT_NODELETE; + result = mystub_run(stub1, test_exit_run, &arg); + assert(result == 0); + assert(status == 1); + assert(tealet_status(stub1) == TEALET_STATUS_EXITED); + tealet_delete(stub1); + arg = (void*)TEALET_EXIT_DEFAULT; + result = mystub_run(stub2, test_exit_run, &arg); + assert(status == 2); + fini_test(); +} + + +/************************************************************/ + +static tealet_t *glob_t1; +static tealet_t *glob_t2; + +tealet_t *test_switch_2(tealet_t *t2, void *arg) +{ + assert(t2 != g_main); + assert(t2 != glob_t1); + glob_t2 = t2; + assert(status == 1); + status = 2; + assert(tealet_current(g_main) == t2); + tealet_switch(glob_t1, NULL); + assert(status == 3); + status = 4; + assert(tealet_current(g_main) == t2); + tealet_switch(glob_t1, NULL); + assert(status == 5); + status = 6; + assert(t2 == glob_t2); + assert(tealet_current(g_main) == t2); + tealet_switch(t2, NULL); + assert(status == 6); + status = 7; + assert(tealet_current(g_main) == t2); + return g_main; +} + +tealet_t *test_switch_1(tealet_t *t1, void *arg) +{ + assert(t1 != g_main); + glob_t1 = t1; + assert(status == 0); + status = 1; + assert(tealet_current(g_main) == t1); + tealet_new(g_main, test_switch_2, NULL); + assert(status == 2); + status = 3; + assert(tealet_current(g_main) == t1); + tealet_switch(glob_t2, NULL); + assert(status == 4); + status = 5; + assert(tealet_current(g_main) == t1); + return glob_t2; +} + +void test_switch(void) +{ + init_test(); + tealet_new(g_main, test_switch_1, NULL); + assert(status == 7); + fini_test(); +} + +/************************************************************/ + +/* 1 is high on the stack. We then create 2 lower on the stack */ +/* the execution is : m 1 m 2 1 m 2 m */ +tealet_t *test_switch_new_1(tealet_t *t1, void *arg) +{ + tealet_t *caller = (tealet_t*)arg; + tealet_t *stub; + /* switch back to the creator */ + tealet_switch(caller, NULL); + /* now we want to trample the stack */ + stub = tealet_new_descend(t1, 50, NULL, NULL); + tealet_delete(stub); + /* and back to main */ + return g_main; +} + +tealet_t *test_switch_new_2(tealet_t *t2, void *arg) { + tealet_t *target = (tealet_t*)arg; + /* switch to tealet 1 to trample the stack*/ + target->data = (void*)t2; + tealet_switch(target, NULL); + + /* and then return to main */ + return g_main; +} + +void test_switch_new(void) +{ + tealet_t *tealet1, *tealet2; + void *arg; + init_test(); + arg = (void *)tealet_current(g_main); + tealet1 = tealet_new(g_main, test_switch_new_1, &arg); + /* the tealet is now running */ + arg = (void*)tealet1; + tealet2 = tealet_new_descend(g_main, 4, test_switch_new_2, &arg); + assert(tealet_status(tealet2) == TEALET_STATUS_ACTIVE); + tealet_switch(tealet2, NULL); + /* tealet should be dead now */ + fini_test(); +} + +/************************************************************/ + +/* test argument passing with switch and exit */ +tealet_t *test_arg_1(tealet_t *t1, void *arg) +{ + void *myarg; + tealet_t *peer = (tealet_t*)arg; + myarg = (void*)1; + tealet_switch(peer, &myarg); + assert(myarg == (void*)2); + myarg = (void*)3; + tealet_exit(peer, myarg, TEALET_EXIT_DEFAULT); + return NULL; +} + +void test_arg(void) +{ + void *myarg; + tealet_t *t1; + init_test(); + myarg = (void*)g_main; + t1 = tealet_new(g_main, test_arg_1, &myarg); + assert(myarg == (void*)1); + myarg = (void*)2; + tealet_switch(t1, &myarg); + assert(myarg == (void*)3); + fini_test(); +} + +/************************************************************/ + +#define ARRAYSIZE 127 +#define MAX_STATUS 50000 + +static tealet_t *tealetarray[ARRAYSIZE] = {NULL}; +static int got_index; + +tealet_t *random_new_tealet(tealet_t*, void *arg); + +static void random_run(int index) +{ + int i, prevstatus; + void *arg; + tealet_t *cur = tealet_current(g_main); + assert(tealetarray[index] == cur); + do + { + i = rand() % (ARRAYSIZE + 1); + status += 1; + if (i == ARRAYSIZE) + break; + prevstatus = status; + got_index = i; + if (tealetarray[i] == NULL) + { + if (status >= MAX_STATUS) + break; + arg = (void*)i; + tealet_new(g_main, random_new_tealet, &arg); + } + else + { + tealet_switch(tealetarray[i], NULL); + } + assert(status >= prevstatus); + assert(tealet_current(g_main) == cur); + assert(tealetarray[index] == cur); + assert(got_index == index); + } + while (status < MAX_STATUS); +} + +tealet_t *random_new_tealet(tealet_t* cur, void *arg) +{ + int i = got_index; + assert(tealet_current(g_main) == cur); + assert(i == (int)(arg)); + assert(i > 0 && i < ARRAYSIZE); + assert(tealetarray[i] == NULL); + tealetarray[i] = cur; + random_run(i); + tealetarray[i] = NULL; + + i = rand() % ARRAYSIZE; + if (tealetarray[i] == NULL) + { + assert(tealetarray[0] != NULL); + i = 0; + } + got_index = i; + return tealetarray[i]; +} + +void test_random(void) +{ + int i; + init_test(); + for( i=0; i 0 && index < ARRAYSIZE); + assert(tealetarray[index] == NULL); + tealetarray[index] = cur; + random2_run(index); + tealetarray[index] = NULL; + return tealetarray[0]; /* switch to main */ +} +void random2_new(int index) { + void *arg = (void*)index; + tealet_new(g_main, random2_tealet, &arg); +} + +int random2_descend(int index, int level) { + int target; + if (level > 0) + return random2_descend(index, level-1); + + /* find target */ + target = rand() % ARRAYSIZE; + if (status Date: Mon, 8 Jul 2013 11:22:39 +0000 Subject: [PATCH 02/35] Initial replacement of switching. Remove CState structure. --- Stackless/core/slp_transfer.c | 31 +-- Stackless/core/stackless_impl.h | 4 +- Stackless/core/stackless_structs.h | 9 +- Stackless/core/stackless_tstate.h | 11 +- Stackless/core/stacklesseval.c | 302 +++++++++-------------------- Stackless/module/scheduling.c | 78 +++++--- Stackless/module/taskletobject.c | 73 +++---- Stackless/pickling/safe_pickle.c | 11 +- 8 files changed, 192 insertions(+), 327 deletions(-) diff --git a/Stackless/core/slp_transfer.c b/Stackless/core/slp_transfer.c index 7bba0d0c5c98f4..c4311759603419 100644 --- a/Stackless/core/slp_transfer.c +++ b/Stackless/core/slp_transfer.c @@ -80,29 +80,6 @@ extern int slp_switch(void); #endif -static int -climb_stack_and_transfer(PyCStackObject **cstprev, PyCStackObject *cst, - PyTaskletObject *prev) -{ - /* - * there are cases where we have been initialized - * in some deep stack recursion, but later on we - * need to switch from a higher stacklevel, and the - * needed stack size becomes *negative* :-)) - */ - PyThreadState *ts = PyThreadState_GET(); - intptr_t probe; - ptrdiff_t needed = &probe - ts->st.cstack_base; - /* in rare cases, the need might have vanished due to the recursion */ - intptr_t *goobledigoobs; - if (needed > 0) { - goobledigoobs = alloca(needed * sizeof(intptr_t)); - if (goobledigoobs == NULL) - return -1; - } - return slp_transfer(cstprev, cst, prev); -} - /* This function returns -1 on error, 1 if a switch occurred and 0 * if only a stack save was performed */ @@ -116,9 +93,7 @@ slp_transfer(PyCStackObject **cstprev, PyCStackObject *cst, /* since we change the stack we must assure that the protocol was met */ STACKLESS_ASSERT(); - if ((intptr_t *) &ts > ts->st.cstack_base) - return climb_stack_and_transfer(cstprev, cst, prev); - if (cst == NULL || cst->ob_size == 0) + if (cst == NULL) cst = ts->st.initial_stub; if (cst != NULL) { if (cst->tstate != ts) { @@ -139,6 +114,10 @@ slp_transfer(PyCStackObject **cstprev, PyCStackObject *cst, if (cstprev && *cstprev == cst && cst->ob_refcnt == 1) cst = NULL; } + /* allocate a fresh + if (!slp_cstack_new(cstprev, NULL, t)) + return NULL; + _cstprev = cstprev; _cst = cst; _prev = prev; diff --git a/Stackless/core/stackless_impl.h b/Stackless/core/stackless_impl.h index 3d4607907cd774..95fd137995a3f3 100644 --- a/Stackless/core/stackless_impl.h +++ b/Stackless/core/stackless_impl.h @@ -38,8 +38,9 @@ extern "C" { PyAPI_DATA(int) slp_enable_softswitch; PyAPI_DATA(int) slp_in_psyco; PyAPI_DATA(int) slp_try_stackless; -PyAPI_DATA(PyCStackObject *) slp_cstack_chain; +PyAPI_DATA(PyTaskletObject *) slp_tasklet_chain; +#if 0 PyAPI_FUNC(PyCStackObject *) slp_cstack_new(PyCStackObject **cst, intptr_t *stackref, PyTaskletObject *task); @@ -55,6 +56,7 @@ PyAPI_FUNC(int) slp_transfer_return(PyCStackObject *cst); #define slp_transfer_return(cst) \ slp_transfer(NULL, (cst), NULL) #endif +#endif PyAPI_FUNC(int) _PyStackless_InitTypes(void); PyAPI_FUNC(void) _PyStackless_Init(void); diff --git a/Stackless/core/stackless_structs.h b/Stackless/core/stackless_structs.h index e4c31505d680e3..60d6d3df6991d1 100644 --- a/Stackless/core/stackless_structs.h +++ b/Stackless/core/stackless_structs.h @@ -8,6 +8,8 @@ extern "C" { /* platform specific constants (mainly SEH stuff to store )*/ #include "platf/slp_platformselect.h" +#include + /*** important structures: tasklet ***/ @@ -85,7 +87,9 @@ typedef struct _tasklet { /* bits stuff */ struct _tasklet_flags flags; int recursion_depth; - struct _cstack *cstate; + int nesting_level; + PyThreadState *tstate; + struct tealet_t *cstate; PyObject *def_globals; PyObject *tsk_weakreflist; } PyTaskletObject; @@ -108,8 +112,7 @@ typedef struct _cstack { #ifdef _SEH32 DWORD exception_list; /* SEH handler on Win32 */ #endif - intptr_t *startaddr; - intptr_t stack[1]; + tealet_t *tealet; } PyCStackObject; diff --git a/Stackless/core/stackless_tstate.h b/Stackless/core/stackless_tstate.h index e69ebd46e60009..f60efe7b7118cb 100644 --- a/Stackless/core/stackless_tstate.h +++ b/Stackless/core/stackless_tstate.h @@ -2,7 +2,7 @@ typedef struct _sts { /* the blueprint for new stacks */ - struct _cstack *initial_stub; + struct tealet_t *initial_stub; /* "serial" is incremented each time we create a new stub. * (enter "stackless" from the outside) * and "serial_last_jump" indicates to which stub the current @@ -16,10 +16,7 @@ typedef struct _sts { long serial; long serial_last_jump; #endif - /* the base address for hijacking stacks. XXX deprecating */ - intptr_t *cstack_base; - /* stack overflow check and init flag */ - intptr_t *cstack_root; + struct tealet_t *tealet_main; /* main tasklet */ struct _tasklet *main; /* runnable tasklets */ @@ -54,8 +51,7 @@ typedef struct _sts { tstate->st.initial_stub = NULL; \ tstate->st.serial = 0; \ tstate->st.serial_last_jump = 0; \ - tstate->st.cstack_base = NULL; \ - tstate->st.cstack_root = NULL; \ + tstate->st.tealet_main = 0; \ tstate->st.ticker = 0; \ tstate->st.interval = 0; \ tstate->st.interrupt = NULL; \ @@ -77,6 +73,7 @@ typedef struct _sts { struct _ts; /* Forward */ +/* TODO, make this also call tealet_finalize */ void slp_kill_tasks_with_stacks(struct _ts *tstate); #define __STACKLESS_PYSTATE_CLEAR \ diff --git a/Stackless/core/stacklesseval.c b/Stackless/core/stacklesseval.c index fa8f924926b8eb..14b3f0cc7bd3c8 100644 --- a/Stackless/core/stacklesseval.c +++ b/Stackless/core/stacklesseval.c @@ -37,8 +37,8 @@ int slp_in_psyco = 0; */ int slp_try_stackless = 0; -/* the list of all stacks of all threads */ -struct _cstack *slp_cstack_chain = NULL; +/* the list of all tasklets of all threads */ +PyTaskletObject *slp_tasklet_chain = NULL; /****************************************************** @@ -47,231 +47,117 @@ struct _cstack *slp_cstack_chain = NULL; ******************************************************/ -static PyCStackObject *cstack_cache[CSTACK_SLOTS] = { NULL }; -static int cstack_cachecount = 0; /* this function will get called by PyStacklessEval_Fini */ -static void slp_cstack_cacheclear(void) -{ - int i; - PyCStackObject *stack; - - for (i=0; i < CSTACK_SLOTS; i++) { - while (cstack_cache[i] != NULL) { - stack = cstack_cache[i]; - cstack_cache[i] = (PyCStackObject *) stack->startaddr; - PyObject_Del(stack); - } - } - cstack_cachecount = 0; -} -static void -cstack_dealloc(PyCStackObject *cst) -{ - slp_cstack_chain = cst; - SLP_CHAIN_REMOVE(PyCStackObject, &slp_cstack_chain, cst, next, - prev); - if (cst->ob_size >= CSTACK_SLOTS) { - PyObject_Del(cst); - } - else { - if (cstack_cachecount >= CSTACK_MAXCACHE) - slp_cstack_cacheclear(); - cst->startaddr = (intptr_t *) cstack_cache[cst->ob_size]; - cstack_cache[cst->ob_size] = cst; - ++cstack_cachecount; - } -} -PyCStackObject * -slp_cstack_new(PyCStackObject **cst, intptr_t *stackref, PyTaskletObject *task) +/**************************************************************** + *Implement copyable stubs by using a trampoline + */ +struct slp_stub_arg { - PyThreadState *ts = PyThreadState_GET(); - intptr_t *stackbase = ts->st.cstack_base; - ptrdiff_t size = stackbase - stackref; - - assert(size >= 0); - - if (*cst != NULL) { - if ((*cst)->task == task) - (*cst)->task = NULL; - Py_DECREF(*cst); - } - if (size < CSTACK_SLOTS && ((*cst) = cstack_cache[size])) { - /* take stack from cache */ - cstack_cache[size] = (PyCStackObject *) (*cst)->startaddr; - --cstack_cachecount; - _Py_NewReference((PyObject *)(*cst)); - } - else - *cst = PyObject_NewVar(PyCStackObject, &PyCStack_Type, size); - if (*cst == NULL) return NULL; - - (*cst)->startaddr = stackbase; - (*cst)->next = (*cst)->prev = NULL; - SLP_CHAIN_INSERT(PyCStackObject, &slp_cstack_chain, *cst, next, prev); - (*cst)->serial = ts->st.serial_last_jump; - (*cst)->task = task; - (*cst)->tstate = ts; - (*cst)->nesting_level = ts->st.nesting_level; -#ifdef _SEH32 - //save the SEH handler - (*cst)->exception_list = (DWORD) - __readfsdword(FIELD_OFFSET(NT_TIB, ExceptionList)); -#endif - return *cst; -} + tealet_t *current; + tealet_run_t run; + void *runarg; +}; -size_t -slp_cstack_save(PyCStackObject *cstprev) +static tealet_t *slp_stub_main(tealet_t *current, void *arg) { - size_t stsizeb = (cstprev)->ob_size * sizeof(intptr_t); - - memcpy((cstprev)->stack, (cstprev)->startaddr - - (cstprev)->ob_size, stsizeb); -#ifdef _SEH32 - //save the SEH handler - cstprev->exception_list = (DWORD) - __readfsdword(FIELD_OFFSET(NT_TIB, ExceptionList)); -#endif - return stsizeb; + void *myarg = 0; + /* the caller is in arg, return right back to him */ + tealet_switch((tealet_t*)arg, &myarg); + /* now we are back, myarg should contain the arg to the run function. + * We were possibly duplicated, so can't trust the original function args. + */ + { + struct slp_stub_arg sarg = *(struct slp_stub_arg*)myarg; + tealet_free(sarg.current, myarg); + return (sarg.run)(sarg.current, sarg.runarg); + } } -void -#ifdef _SEH32 -#pragma warning(disable:4733) /* disable warning about modifying FS[0] */ -#endif -slp_cstack_restore(PyCStackObject *cst) -{ - cst->tstate->st.nesting_level = cst->nesting_level; - /* mark task as no longer responsible for cstack instance */ - cst->task = NULL; - memcpy(cst->startaddr - cst->ob_size, &cst->stack, - (cst->ob_size) * sizeof(intptr_t)); -#ifdef _SEH32 - //restore the SEH handler - __writefsdword(FIELD_OFFSET(NT_TIB, ExceptionList), (DWORD)(cst->exception_list)); - #pragma warning(default:4733) -#endif +/* create a stub and return it */ +tealet_t *slp_stub_new(tealet_t *t) { + void *arg = (void*)tealet_current(t); + return tealet_new(t, slp_stub_main, &arg); } - -static char cstack_doc[] = -"A CStack object serves to save the stack slice which is involved\n\ -during a recursive Python call. It will also be used for pickling\n\ -of program state. This structure is highly platform dependant.\n\ -Note: For inspection, str() can dump it as a string.\ -"; - -#if SIZEOF_VOIDP == SIZEOF_INT -#define T_ADDR T_UINT -#else -#define T_ADDR T_ULONG -#endif - - -static PyMemberDef cstack_members[] = { - {"size", T_INT, offsetof(PyCStackObject, ob_size), READONLY}, - {"next", T_OBJECT, offsetof(PyCStackObject, next), READONLY}, - {"prev", T_OBJECT, offsetof(PyCStackObject, prev), READONLY}, - {"task", T_OBJECT, offsetof(PyCStackObject, task), READONLY}, - {"startaddr", T_ADDR, offsetof(PyCStackObject, startaddr), READONLY}, - {0} -}; - -/* simple string interface for inspection */ - -static PyObject * -cstack_str(PyObject *o) +/* run a stub */ +int slp_stub_run(tealet_t *stub, tealet_run_t run, void **parg) { - PyCStackObject *cst = (PyCStackObject*)o; - return PyString_FromStringAndSize((char*)&cst->stack, - cst->ob_size*sizeof(cst->stack[0])); + int result; + void *myarg; + /* we cannot pass arguments to a different tealet on the stack */ + struct slp_stub_arg *psarg = (struct slp_stub_arg*)tealet_malloc(stub, sizeof(struct slp_stub_arg)); + if (!psarg) + return TEALET_ERR_MEM; + psarg->current = stub; + psarg->run = run; + psarg->runarg = parg ? *parg : NULL; + myarg = (void*)psarg; + result = tealet_switch(stub, &myarg); + if (result) { + /* failure */ + tealet_free(stub, psarg); + return result; + } + /* pass back the arg value from the switch */ + if (parg) + *parg = myarg; + return 0; } -PyTypeObject PyCStack_Type = { - PyObject_HEAD_INIT(&PyType_Type) - 0, - "stackless.cstack", - sizeof(PyCStackObject), - sizeof(PyObject *), - (destructor)cstack_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - (reprfunc)cstack_str, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - PyObject_GenericSetAttr, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - cstack_doc, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - cstack_members, /* tp_members */ -}; - - +/* the current mechanism is based on the generic callable stubs + * above. This can be simplified, TODO + */ static int make_initial_stub(void) { PyThreadState *ts = PyThreadState_GET(); - int result; - - if (ts->st.initial_stub != NULL) { - Py_DECREF(ts->st.initial_stub); - ts->st.initial_stub = NULL; + if (ts->st.tealet_main == NULL) { + tealet_alloc_t ta = { + (tealet_malloc_t)&PyMem_Malloc, + (tealet_free_t)&PyMem_Free, + 0}; + ts->st.tealet_main = tealet_initialize(&ta); + if (!ts->st.tealet_main) { + PyErr_NoMemory(); + return -1; + } } - ts->st.serial_last_jump = ++ts->st.serial; - result = slp_transfer(&ts->st.initial_stub, NULL, NULL); - if (result < 0) - return result; - /* - * from here, we always arrive with a compatible cstack - * that also can be used by main, if it is running - * in soft-switching mode. - * To insure that, it was necessary to re-create the - * initial stub for *every* run of a new main. - * This will vanish with greenlet-like stack management. - */ - - return result; + ts->st.initial_stub = slp_stub_new(ts->st.tealet_main); + if (!ts->st.initial_stub) { + PyErr_NoMemory(); + return -1; + } + return 0; } -static PyObject * -climb_stack_and_eval_frame(PyFrameObject *f) +static tealet_t *stub_func(tealet_t *me, void *arg) { - /* - * a similar case to climb_stack_and_transfer, - * but here we need to incorporate a gap in the - * stack into main and keep this gap on the stack. - * This way, initial_stub is always valid to be - * used to return to the main c stack. + PyThreadState *ts = PyThreadState_GET(); + PyFrameObject *f = ts->frame; + ts->frame = NULL; + slp_run_tasklet(f); + /* We should never return. The switch back is performed + * lower on the stack, in tasklet_end */ + assert(0); + return NULL; +} + +static int +run_initial_stub() +{ PyThreadState *ts = PyThreadState_GET(); - intptr_t probe; - ptrdiff_t needed = &probe - ts->st.cstack_base; - /* in rare cases, the need might have vanished due to the recursion */ - intptr_t *goobledigoobs; - if (needed > 0) { - goobledigoobs = alloca(needed * sizeof(intptr_t)); - if (goobledigoobs == NULL) - return NULL; + tealet_t *stub = tealet_duplicate(ts->st.initial_stub); + if (stub) { + if (! slp_stub_run(ts->st.initial_stub, &stub_func, NULL)) + return 0; } - return slp_eval_frame(f); + PyErr_NoMemory(); + return -1; } @@ -280,7 +166,6 @@ slp_eval_frame(PyFrameObject *f) { PyThreadState *ts = PyThreadState_GET(); PyFrameObject *fprev = f->f_back; - intptr_t * stackref; if (fprev == NULL && ts->st.main == NULL) { int returning; @@ -296,12 +181,6 @@ slp_eval_frame(PyFrameObject *f) * will run as a toplevel frame, with f_back == NULL! */ - stackref = STACK_REFPLUS + (intptr_t *) &f; - if (ts->st.cstack_base == NULL) - ts->st.cstack_base = stackref - CSTACK_GOODGAP; - if (stackref > ts->st.cstack_base) - return climb_stack_and_eval_frame(f); - returning = make_initial_stub(); if (returning < 0) return NULL; @@ -322,6 +201,7 @@ slp_eval_frame(PyFrameObject *f) void slp_kill_tasks_with_stacks(PyThreadState *target_ts) { +#if 0 /* todo, change wrt tasklet_chain PyThreadState *ts = PyThreadState_GET(); int count = 0; @@ -416,6 +296,7 @@ void slp_kill_tasks_with_stacks(PyThreadState *target_ts) Py_DECREF(t); } } +#endif } void PyStackless_kill_tasks_with_stacks(int allthreads) @@ -435,6 +316,8 @@ void PyStackless_kill_tasks_with_stacks(int allthreads) /* cstack spilling for recursive calls */ +#if 0 +/* re-enable stack spilling separately */ static PyObject * eval_frame_callback(PyFrameObject *f, int exc, PyObject *retval) @@ -513,7 +396,6 @@ slp_eval_frame_newstack(PyFrameObject *f, int exc, PyObject *retval) /* and reset it. We may reenter stackless at a completely different * depth later */ - ts->st.cstack_root = NULL; return retval; } @@ -543,6 +425,7 @@ slp_eval_frame_newstack(PyFrameObject *f, int exc, PyObject *retval) cur->cstate = cst; return retval; } +#endif /****************************************************** @@ -815,7 +698,6 @@ slp_frame_dispatch_top(PyObject *retval) void slp_stacklesseval_fini(void) { - slp_cstack_cacheclear(); } #endif /* STACKLESS */ diff --git a/Stackless/module/scheduling.c b/Stackless/module/scheduling.c index ec760399e2adbd..aac387dd9820a6 100644 --- a/Stackless/module/scheduling.c +++ b/Stackless/module/scheduling.c @@ -331,7 +331,44 @@ slp_schedule_hook_func *_slp_schedule_fasthook; PyObject *_slp_schedule_hook; static int -transfer_with_exc(PyCStackObject **cstprev, PyCStackObject *cst, PyTaskletObject *prev) +slp_transfer(tealet_t **cstprev, tealet_t *cst, PyTaskletObject *prev) +{ + int result; + int nesting_level; + assert(cstprev && *cstprev == NULL); + nesting_level = prev->tstate->st.nesting_level; + prev->nesting_level = nesting_level; + /* mark the old tasklet as having cstate */ + *cstprev = tealet_current(cst); + + result = tealet_switch(cst, NULL); + /* we are back, or failed, no cstate in tasklet */ + *cstprev = NULL; + prev->tstate->st.nesting_level = nesting_level; + if (!result) + return 0; + if (result == TEALET_ERR_MEM) + PyErr_NoMemory(); + else + PyErr_SetString(PyExc_RuntimeError, "Invalid tasklet"); + return -1; +} + +static int +slp_transfer_return(tealet_t *cst) +{ + int result = tealet_exit(cst, NULL, TEALET_EXIT_DEFAULT); + if (result) { + /* emergency switch back to main tealet */ + PyThreadState *ts = PyThreadState_GET(); + PyErr_SetString(PyExc_RuntimeError, "Invalid tealet"); + tealet_exit(ts->st.tealet_main, NULL, TEALET_EXIT_DEFAULT); + } + return 0; +} + +static int +transfer_with_exc(tealet_t **cstprev, tealet_t *cst, PyTaskletObject *prev) { PyThreadState *ts = PyThreadState_GET(); @@ -525,17 +562,6 @@ jump_soft_to_hard(PyFrameObject *f, int exc, PyObject *retval) /* combined soft/hard switching */ -int -slp_ensure_linkage(PyTaskletObject *t) -{ - if (t->cstate->task == t) - return 0; - if (!slp_cstack_new(&t->cstate, t->cstate->tstate->st.cstack_base, t)) - return -1; - t->cstate->nesting_level = 0; - return 0; -} - /* check whether a different thread can be run */ @@ -725,7 +751,7 @@ schedule_task_interthread(PyObject **result, int stackless, int *did_switch) { - PyThreadState *nts = next->cstate->tstate; + PyThreadState *nts = next->tstate; int fail; /* get myself ready, since the previous task is going to continue on the @@ -822,8 +848,7 @@ slp_schedule_task(PyObject **result, PyTaskletObject *prev, PyTaskletObject *nex return schedule_task_block(result, prev, stackless, did_switch); #ifdef WITH_THREAD - /* note that next->cstate is undefined if it is ourself */ - if (next->cstate != NULL && next->cstate->tstate != ts) { + if (next->tstate != ts) { return schedule_task_interthread(result, prev, next, stackless, did_switch); } #endif @@ -867,10 +892,10 @@ static int slp_schedule_task_prepared(PyThreadState *ts, PyObject **result, PyTaskletObject *prev, PyTaskletObject *next, int stackless, int *did_switch) { - PyCStackObject **cstprev; + tealet_t **cstprev; PyObject *retval; - int (*transfer)(PyCStackObject **, PyCStackObject *, PyTaskletObject *); + int (*transfer)(tealet_t **, tealet_t *, PyTaskletObject *); /* remove the no-soft-irq flag from the runflags */ int no_soft_irq = ts->st.runflags & PY_WATCHDOG_NO_SOFT_IRQ; @@ -898,17 +923,6 @@ slp_schedule_task_prepared(PyThreadState *ts, PyObject **result, PyTaskletObject /* start of soft switching code */ - if (prev->cstate != ts->st.initial_stub) { - Py_DECREF(prev->cstate); - prev->cstate = ts->st.initial_stub; - Py_INCREF(prev->cstate); - } - if (ts != slp_initial_tstate) { - /* ensure to get all tasklets into the other thread's chain */ - if (slp_ensure_linkage(prev) || slp_ensure_linkage(next)) - return -1; - } - /* handle exception */ if (ts->exc_type == Py_None) { Py_XDECREF(ts->exc_type); @@ -959,7 +973,7 @@ slp_schedule_task_prepared(PyThreadState *ts, PyObject **result, PyTaskletObject *did_switch = 1; assert(next->cstate != NULL); - if (next->cstate->nesting_level != 0) { + if (next->cstate) { /* create a helper frame to restore the target stack */ ts->frame = (PyFrameObject *) slp_cframe_new(jump_soft_to_hard, 1); @@ -993,7 +1007,7 @@ slp_schedule_task_prepared(PyThreadState *ts, PyObject **result, PyTaskletObject /* since we change the stack we must assure that the protocol was met */ STACKLESS_ASSERT(); - /* note: nesting_level is handled in cstack_new */ + /* note: nesting_level is handled in slp_transfer */ cstprev = &prev->cstate; ts->st.current = next; @@ -1192,8 +1206,8 @@ tasklet_end(PyObject *retval) * the original stub if necessary. (Meanwhile, task->cstate may be an old nesting state and not * the original stub, so we take the stub from the tstate) */ - if (ts->st.serial_last_jump != ts->st.initial_stub->serial) - slp_transfer_return(ts->st.initial_stub); + if (tealet_current(ts->st.tealet_main) != ts->st.tealet_main) + slp_transfer_return(ts->st.tealet_main); } /* remove from runnables */ diff --git a/Stackless/module/taskletobject.c b/Stackless/module/taskletobject.c index 4de5895b0baf1a..15b45d44217b4a 100644 --- a/Stackless/module/taskletobject.c +++ b/Stackless/module/taskletobject.c @@ -13,7 +13,7 @@ void slp_current_insert(PyTaskletObject *task) { - PyThreadState *ts = task->cstate->tstate; + PyThreadState *ts = task->tstate; PyTaskletObject **chain = &ts->st.current; SLP_CHAIN_INSERT(PyTaskletObject, chain, task, next, prev); @@ -23,7 +23,7 @@ slp_current_insert(PyTaskletObject *task) void slp_current_insert_after(PyTaskletObject *task) { - PyThreadState *ts = task->cstate->tstate; + PyThreadState *ts = task->tstate; PyTaskletObject *hold = ts->st.current; PyTaskletObject **chain = &ts->st.current; @@ -36,7 +36,7 @@ slp_current_insert_after(PyTaskletObject *task) void slp_current_uninsert(PyTaskletObject *task) { - PyThreadState *ts = task->cstate->tstate; + PyThreadState *ts = task->tstate; PyTaskletObject *hold = ts->st.current; PyTaskletObject **chain = &ts->st.current; @@ -74,7 +74,7 @@ slp_current_unremove(PyTaskletObject* task) static int tasklet_has_c_stack(PyTaskletObject *t) { - return t->f.frame && t->cstate && t->cstate->nesting_level != 0 ; + return t->f.frame && t->cstate; } static int @@ -118,11 +118,6 @@ tasklet_clear(PyTaskletObject *t) { tasklet_clear_frames(t); TASKLET_SETVAL(t, Py_None); /* always non-zero */ - - /* unlink task from cstate */ - if (t->cstate != NULL && t->cstate->task == t) - t->cstate->task = NULL; - Py_CLEAR(t->cstate); } /* @@ -141,7 +136,7 @@ kill_finally (PyObject *ob) { PyThreadState *ts = PyThreadState_GET(); PyTaskletObject *self = (PyTaskletObject *) ob; - int is_mine = ts == self->cstate->tstate; + int is_mine = ts == self->tstate; /* this could happen if we have a refcount bug, so catch it here. assert(self != ts->st.current); @@ -183,10 +178,6 @@ tasklet_dealloc(PyTaskletObject *t) tasklet_clear_frames(t); if (t->tsk_weakreflist != NULL) PyObject_ClearWeakRefs((PyObject *)t); - if (t->cstate != NULL) { - assert(t->cstate->task != t || t->cstate->ob_size == 0); - Py_DECREF(t->cstate); - } Py_DECREF(t->tempval); Py_XDECREF(t->def_globals); t->ob_type->tp_free((PyObject*)t); @@ -227,16 +218,11 @@ PyTasklet_New(PyTypeObject *type, PyObject *func) t->tempval = func; t->tsk_weakreflist = NULL; Py_INCREF(ts->st.initial_stub); - t->cstate = ts->st.initial_stub; + t->cstate = 0; + t->tstate = ts; + t->nesting_level = 0; t->def_globals = PyEval_GetGlobals(); Py_XINCREF(t->def_globals); - if (ts != slp_initial_tstate) { - /* make sure to kill tasklets with their thread */ - if (slp_ensure_linkage(t)) { - Py_DECREF(t); - return NULL; - } - } } return t; } @@ -330,7 +316,7 @@ tasklet_reduce(PyTaskletObject * t) t->ob_type, t->flags, t->tempval, - t->cstate->nesting_level, + t->nesting_level, lis ); err_exit: @@ -522,7 +508,7 @@ static TASKLET_INSERT_HEAD(impl_tasklet_insert) /* The tasklet may belong to a different thread, and that thread may * be blocked, waiting for something to do! */ - slp_thread_unblock(task->cstate->tstate); + slp_thread_unblock(task->tstate); } return 0; } @@ -711,20 +697,13 @@ tasklet_set_ignore_nesting(PyObject *self, PyObject *flag) static int bind_tasklet_to_frame(PyTaskletObject *task, PyFrameObject *frame) { - PyThreadState *ts = task->cstate->tstate; - + PyThreadState *ts = PyThreadState_GET(); + if (task->f.frame != NULL) RUNTIME_ERROR("tasklet is already bound to a frame", -1); task->f.frame = frame; - if (task->cstate != ts->st.initial_stub) { - PyCStackObject *hold = task->cstate; - task->cstate = ts->st.initial_stub; - Py_INCREF(task->cstate); - Py_DECREF(hold); - if (ts != slp_initial_tstate) - if (slp_ensure_linkage(task)) - return -1; - } + task->tstate = ts; + task->nesting_level = 0; return 0; /* note: We expect that f_back is NULL, or will be adjusted immediately */ } @@ -831,7 +810,7 @@ static TASKLET_THROW_HEAD(impl_tasklet_throw) * f.frame is null for the running tasklet and a dead tasklet * A new tasklet has a CFrame */ - if (self->f.frame == NULL && self != self->cstate->tstate->st.current) { + if (self->f.frame == NULL && self != self->tstate->st.current) { /* however, allow tasklet exit errors for already dead tasklets */ if (PyObject_IsSubclass(((PyBombObject*)bomb)->curexc_type, PyExc_TaskletExit)) { Py_DECREF(bomb); @@ -1147,7 +1126,7 @@ tasklet_get_recursion_depth(PyTaskletObject *task) PyThreadState *ts; assert(task->cstate != NULL); - ts = task->cstate->tstate; + ts = task->tstate; return PyInt_FromLong(ts->st.current == task ? ts->recursion_depth : task->recursion_depth); } @@ -1158,7 +1137,7 @@ PyTasklet_GetRecursionDepth(PyTaskletObject *task) PyThreadState *ts; assert(task->cstate != NULL); - ts = task->cstate->tstate; + ts = task->tstate; return ts->st.current == task ? ts->recursion_depth : task->recursion_depth; } @@ -1170,10 +1149,10 @@ tasklet_get_nesting_level(PyTaskletObject *task) PyThreadState *ts; assert(task->cstate != NULL); - ts = task->cstate->tstate; + ts = task->tstate; return PyInt_FromLong( ts->st.current == task ? ts->st.nesting_level - : task->cstate->nesting_level); + : task->nesting_level); } int @@ -1182,9 +1161,9 @@ PyTasklet_GetNestingLevel(PyTaskletObject *task) PyThreadState *ts; assert(task->cstate != NULL); - ts = task->cstate->tstate; + ts = task->tstate; return ts->st.current == task ? ts->st.nesting_level - : task->cstate->nesting_level; + : task->nesting_level; } @@ -1235,10 +1214,10 @@ tasklet_restorable(PyTaskletObject *task) PyThreadState *ts; assert(task->cstate != NULL); - ts = task->cstate->tstate; + ts = task->tstate; return PyBool_FromLong( 0 == (ts->st.current == task ? ts->st.nesting_level - : task->cstate->nesting_level) ); + : task->nesting_level) ); } int @@ -1247,9 +1226,9 @@ PyTasklet_Restorable(PyTaskletObject *task) PyThreadState *ts; assert(task->cstate != NULL); - ts = task->cstate->tstate; + ts = task->tstate; return 0 == (ts->st.current == task ? ts->st.nesting_level - : task->cstate->nesting_level); + : task->nesting_level); } static PyObject * @@ -1292,7 +1271,7 @@ tasklet_get_prev(PyTaskletObject *task) static PyObject * tasklet_thread_id(PyTaskletObject *task) { - return PyInt_FromLong(task->cstate->tstate->thread_id); + return PyInt_FromLong(task->tstate->thread_id); } static PyMemberDef tasklet_members[] = { diff --git a/Stackless/pickling/safe_pickle.c b/Stackless/pickling/safe_pickle.c index 964d2282ff1a31..209caf069cd430 100644 --- a/Stackless/pickling/safe_pickle.c +++ b/Stackless/pickling/safe_pickle.c @@ -9,7 +9,7 @@ /* safe pickling */ static int(*cPickle_save)(PyObject *, PyObject *, int) = NULL; - +#if 0 /* add stack spilling later */ static PyObject * pickle_callback(PyFrameObject *f, int exc, PyObject *retval) { @@ -44,6 +44,7 @@ pickle_callback(PyFrameObject *f, int exc, PyObject *retval) return NULL; } +#endif static int pickle_M(PyObject *self, PyObject *args, int pers_save); int @@ -53,6 +54,7 @@ slp_safe_pickling(int(*save)(PyObject *, PyObject *, int), PyThreadState *ts = PyThreadState_GET(); PyTaskletObject *cur = ts->st.current; int ret = -1; +#if 0 PyCFrameObject *cf = NULL; PyCStackObject *cst; @@ -67,7 +69,9 @@ slp_safe_pickling(int(*save)(PyObject *, PyObject *, int), cPickle_save = save; if (ts->st.main == NULL) +#endif return pickle_M(self, args, pers_save); +#if 0 cf = slp_cframe_new(pickle_callback, 1); if (cf == NULL) @@ -88,6 +92,7 @@ slp_safe_pickling(int(*save)(PyObject *, PyObject *, int), finally: Py_XDECREF(cf); return ret; +#endif } /* safe unpickling is not needed */ @@ -124,10 +129,14 @@ pickle_M(PyObject *self, PyObject *args, int pers_save) _self = self; _args = args; _pers_save = pers_save; +#if 0 old_root = ts->st.cstack_root; ts->st.cstack_root = STACK_REFPLUS + (intptr_t *) &self; +#endif ret = slp_int_wrapper(slp_eval_frame((PyFrameObject *)cf)); +#if 0 ts->st.cstack_root = old_root; +#endif return ret; } From 83a6b7f35cbd0d5b4cc8c41a2fb9cc36553846f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Mon, 8 Jul 2013 11:44:09 +0000 Subject: [PATCH 03/35] Initial running version --- Modules/cPickle.c | 3 ++- PCbuild/pythoncore.vcproj | 4 ---- Python/ceval.c | 2 ++ Stackless/module/channelobject.c | 4 ++-- Stackless/module/scheduling.c | 4 +++- Stackless/module/stacklessmodule.c | 5 +++-- 6 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Modules/cPickle.c b/Modules/cPickle.c index 0dbec407df11da..3f35b8f9d8b0c3 100644 --- a/Modules/cPickle.c +++ b/Modules/cPickle.c @@ -2538,7 +2538,8 @@ save(Picklerobject *self, PyObject *args, int pers_save) int res = -1; int tmp; -#ifdef STACKLESS + /* TODO: re-enable stack spilling */ +#if 0 && defined STACKLESS /* but we save the stack after a fixed watermark */ if (CSTACK_SAVE_NOW(PyThreadState_GET(), self)) { res = slp_safe_pickling((int(*)(PyObject *, PyObject *, int))&save, diff --git a/PCbuild/pythoncore.vcproj b/PCbuild/pythoncore.vcproj index 9868dbcb6a4149..19c2fcafb3ad9e 100644 --- a/PCbuild/pythoncore.vcproj +++ b/PCbuild/pythoncore.vcproj @@ -1964,10 +1964,6 @@ RelativePath="..\Stackless\core\cframeobject.c" > - - diff --git a/Python/ceval.c b/Python/ceval.c index d8c0510f3597c3..902bbd0eae28b6 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -906,8 +906,10 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) return NULL; #ifdef STACKLESS +#if 0 /* TODO - re-enable stack spilling */ if (CSTACK_SAVE_NOW(tstate, f)) return slp_eval_frame_newstack(f, throwflag, retval); +#endif /* push frame */ if (Py_EnterRecursiveCall("")) { diff --git a/Stackless/module/channelobject.c b/Stackless/module/channelobject.c index 4bfba83b84deed..c46a134db62c6b 100644 --- a/Stackless/module/channelobject.c +++ b/Stackless/module/channelobject.c @@ -450,7 +450,7 @@ generic_channel_action(PyChannelObject *self, PyObject *arg, int dir, int stackl PyTaskletObject *source = ts->st.current; PyTaskletObject *target = self->head; int cando = dir > 0 ? self->balance < 0 : self->balance > 0; - int interthread = cando ? target->cstate->tstate != ts : 0; + int interthread = cando ? target->tstate != ts : 0; PyObject *tmpval, *retval; int fail; @@ -496,7 +496,7 @@ generic_channel_cando(PyThreadState *ts, PyObject **result, PyChannelObject *sel /* swap data and perform necessary scheduling */ switchto = target = slp_channel_remove(self, NULL, NULL, &next); - interthread = target->cstate->tstate != ts; + interthread = target->tstate != ts; /* exchange data */ TASKLET_SWAPVAL(source, target); diff --git a/Stackless/module/scheduling.c b/Stackless/module/scheduling.c index aac387dd9820a6..31c9e75df5570f 100644 --- a/Stackless/module/scheduling.c +++ b/Stackless/module/scheduling.c @@ -1072,7 +1072,9 @@ initialize_main_and_current(void) &PyTasklet_Type, noargs, NULL); Py_DECREF(noargs); if (task == NULL) return -1; - assert(task->cstate != NULL); + task->cstate = 0; + task->tstate = ts; + task->nesting_level = 0; ts->st.main = task; Py_INCREF(task); slp_current_insert(task); diff --git a/Stackless/module/stacklessmodule.c b/Stackless/module/stacklessmodule.c index 7d1f226f6a0e25..32fb60530268f7 100644 --- a/Stackless/module/stacklessmodule.c +++ b/Stackless/module/stacklessmodule.c @@ -590,7 +590,7 @@ test_outside(PyObject *self) { PyThreadState *ts = PyThreadState_GET(); PyTaskletObject *stmain = ts->st.main; - PyCStackObject *cst = ts->st.initial_stub; + tealet_t *cst = ts->st.initial_stub; PyFrameObject *f = ts->frame; int recursion_depth = ts->recursion_depth; int nesting_level = ts->st.nesting_level; @@ -1147,6 +1147,7 @@ static PyObject * slpmodule_getuncollectables(PySlpModuleObject *mod, void *context) { PyObject *lis = PyList_New(0); +#if 0 /* todo: finish this */ PyCStackObject *cst = slp_cstack_chain; if (lis == NULL) @@ -1160,6 +1161,7 @@ slpmodule_getuncollectables(PySlpModuleObject *mod, void *context) } cst = cst->next; } while (cst != slp_cstack_chain); +#endif return lis; } @@ -1353,7 +1355,6 @@ _PyStackless_Init(void) INSERT("slpmodule", PySlpModule_TypePtr); INSERT("cframe", &PyCFrame_Type); - INSERT("cstack", &PyCStack_Type); INSERT("bomb", &PyBomb_Type); INSERT("tasklet", &PyTasklet_Type); INSERT("channel", &PyChannel_Type); From c6201ff468da47e3f177cb5abe9b4bf816cf8e97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Tue, 9 Jul 2013 13:50:31 +0000 Subject: [PATCH 04/35] Now runs testsuite but fails in test-external --- Stackless/core/stackless_impl.h | 3 ++ Stackless/core/stacklesseval.c | 65 ++++++++++++++++++++++---------- Stackless/module/scheduling.c | 44 ++++++++++++--------- Stackless/module/taskletobject.c | 7 ---- 4 files changed, 76 insertions(+), 43 deletions(-) diff --git a/Stackless/core/stackless_impl.h b/Stackless/core/stackless_impl.h index 95fd137995a3f3..652e1e3e4c0e1a 100644 --- a/Stackless/core/stackless_impl.h +++ b/Stackless/core/stackless_impl.h @@ -71,6 +71,9 @@ PyAPI_FUNC(void) PyStackless_Fini(void); PyAPI_FUNC(void) PyStackless_kill_tasks_with_stacks(int allthreads); +/* Runnin a new tealet */ +PyAPI_FUNC(int) slp_run_initial_stub(tealet_run_t func, void *arg); + /* the special version of eval_frame */ PyAPI_FUNC(PyObject *) slp_eval_frame(struct _frame *f); diff --git a/Stackless/core/stacklesseval.c b/Stackless/core/stacklesseval.c index 14b3f0cc7bd3c8..61377bc375042c 100644 --- a/Stackless/core/stacklesseval.c +++ b/Stackless/core/stacklesseval.c @@ -88,6 +88,7 @@ int slp_stub_run(tealet_t *stub, tealet_run_t run, void **parg) { int result; void *myarg; + /* we cannot pass arguments to a different tealet on the stack */ struct slp_stub_arg *psarg = (struct slp_stub_arg*)tealet_malloc(stub, sizeof(struct slp_stub_arg)); if (!psarg) @@ -108,6 +109,17 @@ int slp_stub_run(tealet_t *stub, tealet_run_t run, void **parg) return 0; } +static int +slp_tealet_error(int err) +{ + assert(err); + if (err == TEALET_ERR_MEM) + PyErr_NoMemory(); + assert(err == TEALET_ERR_DEFUNCT); + PyErr_SetString(PyExc_RuntimeError, "Tealet corrupt"); + return -1; +} + /* the current mechanism is based on the generic callable stubs * above. This can be simplified, TODO */ @@ -121,43 +133,56 @@ make_initial_stub(void) (tealet_free_t)&PyMem_Free, 0}; ts->st.tealet_main = tealet_initialize(&ta); - if (!ts->st.tealet_main) { - PyErr_NoMemory(); - return -1; - } + if (!ts->st.tealet_main) + goto err; + } ts->st.initial_stub = slp_stub_new(ts->st.tealet_main); - if (!ts->st.initial_stub) { - PyErr_NoMemory(); - return -1; - } + if (!ts->st.initial_stub) + goto err; return 0; +err: + PyErr_NoMemory(); + return -1; } -static tealet_t *stub_func(tealet_t *me, void *arg) +/* The function that runs tasklet loop in a tealet */ +static tealet_t *tasklet_stub_func(tealet_t *me, void *arg) { PyThreadState *ts = PyThreadState_GET(); PyFrameObject *f = ts->frame; ts->frame = NULL; + ts->st.nesting_level = 0; slp_run_tasklet(f); /* We should never return. The switch back is performed - * lower on the stack, in tasklet_end + * lower on the stack, in tasklet_endv */ assert(0); return NULL; } -static int -run_initial_stub() +/* Running a function in the top level stub. If NULL is provided, + * use the tasklet evaluation loop + */ + +/* Running the top level stub */ +int +slp_run_initial_stub(tealet_run_t func, void *arg) { PyThreadState *ts = PyThreadState_GET(); - tealet_t *stub = tealet_duplicate(ts->st.initial_stub); - if (stub) { - if (! slp_stub_run(ts->st.initial_stub, &stub_func, NULL)) - return 0; + tealet_t *stub; + int result; + if (func == NULL) + func = &tasklet_stub_func; + stub = tealet_duplicate(ts->st.initial_stub); + if (!stub) { + PyErr_NoMemory(); + return -1; } - PyErr_NoMemory(); - return -1; + result = slp_stub_run(stub, func, arg); + if (result) + return slp_tealet_error(result); + return 0; } @@ -201,8 +226,10 @@ slp_eval_frame(PyFrameObject *f) void slp_kill_tasks_with_stacks(PyThreadState *target_ts) { -#if 0 /* todo, change wrt tasklet_chain PyThreadState *ts = PyThreadState_GET(); + + +#if 0 /* todo, change wrt tasklet_chain int count = 0; /* a loop to kill tasklets on the local thread */ diff --git a/Stackless/module/scheduling.c b/Stackless/module/scheduling.c index 31c9e75df5570f..49e6117ea278d9 100644 --- a/Stackless/module/scheduling.c +++ b/Stackless/module/scheduling.c @@ -331,22 +331,38 @@ slp_schedule_hook_func *_slp_schedule_fasthook; PyObject *_slp_schedule_hook; static int -slp_transfer(tealet_t **cstprev, tealet_t *cst, PyTaskletObject *prev) +slp_transfer(PyThreadState *ts, tealet_t *cst, PyTaskletObject *prev) { int result; int nesting_level; - assert(cstprev && *cstprev == NULL); + assert(prev->cstate == NULL); + nesting_level = prev->tstate->st.nesting_level; prev->nesting_level = nesting_level; /* mark the old tasklet as having cstate */ - *cstprev = tealet_current(cst); + prev->cstate = tealet_current(ts->st.tealet_main); + + if (cst) { + /* make sure we are not trying to jump between threads */ + assert(TEALET_MAIN(cst) == ts->st.tealet_main); + result = tealet_switch(cst, NULL); + } else { + assert(TEALET_MAIN(ts->st.initial_stub) == ts->st.tealet_main); + result = slp_run_initial_stub(NULL, NULL); + } - result = tealet_switch(cst, NULL); /* we are back, or failed, no cstate in tasklet */ - *cstprev = NULL; + prev->cstate = NULL; prev->tstate->st.nesting_level = nesting_level; - if (!result) + if (!result) { + if (ts->st.del_post_switch) { + PyObject *tmp; + TASKLET_CLAIMVAL(ts->st.current, &tmp); + Py_CLEAR(ts->st.del_post_switch); + TASKLET_SETVAL_OWN(ts->st.current, tmp); + } return 0; + } if (result == TEALET_ERR_MEM) PyErr_NoMemory(); else @@ -368,10 +384,8 @@ slp_transfer_return(tealet_t *cst) } static int -transfer_with_exc(tealet_t **cstprev, tealet_t *cst, PyTaskletObject *prev) +transfer_with_exc(PyThreadState *ts, tealet_t *cst, PyTaskletObject *prev) { - PyThreadState *ts = PyThreadState_GET(); - int tracing = ts->tracing; int use_tracing = ts->use_tracing; @@ -394,7 +408,7 @@ transfer_with_exc(tealet_t **cstprev, tealet_t *cst, PyTaskletObject *prev) Py_XINCREF(c_profileobj); Py_XINCREF(c_traceobj); - ret = slp_transfer(cstprev, cst, prev); + ret = slp_transfer(ts, cst, prev); ts->tracing = tracing; ts->use_tracing = use_tracing; @@ -892,10 +906,8 @@ static int slp_schedule_task_prepared(PyThreadState *ts, PyObject **result, PyTaskletObject *prev, PyTaskletObject *next, int stackless, int *did_switch) { - tealet_t **cstprev; - PyObject *retval; - int (*transfer)(tealet_t **, tealet_t *, PyTaskletObject *); + int (*transfer)(PyThreadState *, tealet_t *, PyTaskletObject *); /* remove the no-soft-irq flag from the runflags */ int no_soft_irq = ts->st.runflags & PY_WATCHDOG_NO_SOFT_IRQ; @@ -972,7 +984,6 @@ slp_schedule_task_prepared(PyThreadState *ts, PyObject **result, PyTaskletObject if (did_switch) *did_switch = 1; - assert(next->cstate != NULL); if (next->cstate) { /* create a helper frame to restore the target stack */ ts->frame = (PyFrameObject *) @@ -1008,8 +1019,7 @@ slp_schedule_task_prepared(PyThreadState *ts, PyObject **result, PyTaskletObject STACKLESS_ASSERT(); /* note: nesting_level is handled in slp_transfer */ - cstprev = &prev->cstate; - + ts->st.current = next; if (ts->exc_type == Py_None) { @@ -1026,7 +1036,7 @@ slp_schedule_task_prepared(PyThreadState *ts, PyObject **result, PyTaskletObject else transfer = slp_transfer; - if (transfer(cstprev, next->cstate, prev) >= 0) { + if (transfer(ts, next->cstate, prev) >= 0) { --ts->st.nesting_level; TASKLET_CLAIMVAL(prev, &retval); if (PyBomb_Check(retval)) diff --git a/Stackless/module/taskletobject.c b/Stackless/module/taskletobject.c index 15b45d44217b4a..df296dbc954aa4 100644 --- a/Stackless/module/taskletobject.c +++ b/Stackless/module/taskletobject.c @@ -311,7 +311,6 @@ tasklet_reduce(PyTaskletObject * t) f = f->f_back; } if (PyList_Reverse(lis)) goto err_exit; - assert(t->cstate != NULL); tup = Py_BuildValue("(O()(" TASKLET_TUPLEFMT "))", t->ob_type, t->flags, @@ -1125,7 +1124,6 @@ tasklet_get_recursion_depth(PyTaskletObject *task) { PyThreadState *ts; - assert(task->cstate != NULL); ts = task->tstate; return PyInt_FromLong(ts->st.current == task ? ts->recursion_depth : task->recursion_depth); @@ -1136,7 +1134,6 @@ PyTasklet_GetRecursionDepth(PyTaskletObject *task) { PyThreadState *ts; - assert(task->cstate != NULL); ts = task->tstate; return ts->st.current == task ? ts->recursion_depth : task->recursion_depth; @@ -1148,7 +1145,6 @@ tasklet_get_nesting_level(PyTaskletObject *task) { PyThreadState *ts; - assert(task->cstate != NULL); ts = task->tstate; return PyInt_FromLong( ts->st.current == task ? ts->st.nesting_level @@ -1160,7 +1156,6 @@ PyTasklet_GetNestingLevel(PyTaskletObject *task) { PyThreadState *ts; - assert(task->cstate != NULL); ts = task->tstate; return ts->st.current == task ? ts->st.nesting_level : task->nesting_level; @@ -1213,7 +1208,6 @@ tasklet_restorable(PyTaskletObject *task) { PyThreadState *ts; - assert(task->cstate != NULL); ts = task->tstate; return PyBool_FromLong( 0 == (ts->st.current == task ? ts->st.nesting_level @@ -1225,7 +1219,6 @@ PyTasklet_Restorable(PyTaskletObject *task) { PyThreadState *ts; - assert(task->cstate != NULL); ts = task->tstate; return 0 == (ts->st.current == task ? ts->st.nesting_level : task->nesting_level); From 750a1e738b0b4623a5f9946551bbafe7d1b78005 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Wed, 10 Jul 2013 11:30:59 +0000 Subject: [PATCH 05/35] Clear del_post_switch in the new taskelt stub. --- Stackless/core/stacklesseval.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Stackless/core/stacklesseval.c b/Stackless/core/stacklesseval.c index 61377bc375042c..4b9a5250c20291 100644 --- a/Stackless/core/stacklesseval.c +++ b/Stackless/core/stacklesseval.c @@ -153,6 +153,7 @@ static tealet_t *tasklet_stub_func(tealet_t *me, void *arg) PyFrameObject *f = ts->frame; ts->frame = NULL; ts->st.nesting_level = 0; + Py_CLEAR(ts->st.del_post_switch); slp_run_tasklet(f); /* We should never return. The switch back is performed * lower on the stack, in tasklet_endv From a13f6e50f6a228360cf187652c651ae0b8799f26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Fri, 19 Jul 2013 10:37:09 +0000 Subject: [PATCH 06/35] Fix PyTasklet_New_M --- Stackless/module/stacklessmodule.c | 16 +++++++++++----- Stackless/module/taskletobject.c | 2 +- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Stackless/module/stacklessmodule.c b/Stackless/module/stacklessmodule.c index 32fb60530268f7..ae318259c14812 100644 --- a/Stackless/module/stacklessmodule.c +++ b/Stackless/module/stacklessmodule.c @@ -725,13 +725,19 @@ PyStackless_CallMethod_Main(PyObject *o, char *name, char *format, ...) va_list va; PyObject *args, *func = NULL, *retval; - if (o == NULL || name == NULL) + if (o == NULL) return slp_null_error(); - func = PyObject_GetAttrString(o, name); - if (func == NULL) { - PyErr_SetString(PyExc_AttributeError, name); - return NULL; + if (name != NULL) { + func = PyObject_GetAttrString(o, name); + if (func == NULL) { + PyErr_SetString(PyExc_AttributeError, name); + return NULL; + } + } else { + /* direct call to object */ + func = o; + Py_INCREF(func); } if (!PyCallable_Check(func)) diff --git a/Stackless/module/taskletobject.c b/Stackless/module/taskletobject.c index df296dbc954aa4..1c6ce4d822035d 100644 --- a/Stackless/module/taskletobject.c +++ b/Stackless/module/taskletobject.c @@ -191,7 +191,7 @@ PyTasklet_New_M(PyTypeObject *type, PyObject *func) if (func == NULL) fmt = NULL; return (PyTaskletObject *) PyStackless_CallMethod_Main( - (PyObject*)type, "__call__", fmt, func); + (PyObject*)type, NULL, fmt, func); } PyTaskletObject * From 79af2ab26096c232e629941be0ec4d8db00cc76f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Fri, 19 Jul 2013 10:56:05 +0000 Subject: [PATCH 07/35] don't treat cstate/initial stub as PyObjects --- Stackless/module/stacklessmodule.c | 3 ++- Stackless/module/taskletobject.c | 5 ++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Stackless/module/stacklessmodule.c b/Stackless/module/stacklessmodule.c index ae318259c14812..e96768e08152e8 100644 --- a/Stackless/module/stacklessmodule.c +++ b/Stackless/module/stacklessmodule.c @@ -612,7 +612,8 @@ test_outside(PyObject *self) } } ts->st.main = stmain; - Py_XDECREF(ts->st.initial_stub); + // TODO: destroy initial stub + //Py_XDECREF(ts->st.initial_stub); ts->st.initial_stub = cst; ts->frame = f; slp_current_insert(stmain); diff --git a/Stackless/module/taskletobject.c b/Stackless/module/taskletobject.c index 1c6ce4d822035d..b109ee2d6bbffa 100644 --- a/Stackless/module/taskletobject.c +++ b/Stackless/module/taskletobject.c @@ -94,7 +94,7 @@ tasklet_traverse(PyTaskletObject *t, visitproc visit, void *arg) Py_VISIT(f); } Py_VISIT(t->tempval); - Py_VISIT(t->cstate); + //Py_VISIT(t->cstate); return 0; } @@ -215,9 +215,8 @@ PyTasklet_New(PyTypeObject *type, PyObject *func) if (func == NULL) func = Py_None; Py_INCREF(func); - t->tempval = func; + t->tempval = func; t->tsk_weakreflist = NULL; - Py_INCREF(ts->st.initial_stub); t->cstate = 0; t->tstate = ts; t->nesting_level = 0; From bee3bf27b2a08b8cbb59f12f3c7bd585844574ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Fri, 19 Jul 2013 16:33:24 +0000 Subject: [PATCH 08/35] Fix the tealet model. The main tealet (the one calling in to python) must be suspended and the top level tasklet evaluation loop run in a worker, because soft switching may have the main tasklet run in an arbitrary tealet when it exits. It then needs to switch back to the 'main' tealet. The only way to do this is to have the 'main' tealet in a known state. In the old system, it was possible to switch back to a known state of the main stack, creating a duplicate on the fly. Tealets are more strict because a tealet are more like threads: each can only run a function, not extend back from its invocation point. --- Stackless/core/stackless_impl.h | 6 +- Stackless/core/stacklesseval.c | 92 +++++++++++++++++++----------- Stackless/module/scheduling.c | 13 +---- Stackless/module/stacklessmodule.c | 11 +++- Stackless/module/taskletobject.c | 2 +- 5 files changed, 73 insertions(+), 51 deletions(-) diff --git a/Stackless/core/stackless_impl.h b/Stackless/core/stackless_impl.h index 652e1e3e4c0e1a..c9f16d678fbac5 100644 --- a/Stackless/core/stackless_impl.h +++ b/Stackless/core/stackless_impl.h @@ -71,8 +71,10 @@ PyAPI_FUNC(void) PyStackless_Fini(void); PyAPI_FUNC(void) PyStackless_kill_tasks_with_stacks(int allthreads); -/* Runnin a new tealet */ -PyAPI_FUNC(int) slp_run_initial_stub(tealet_run_t func, void *arg); +/* Running a new tealet */ +PyAPI_FUNC(int) slp_run_initial_stub(PyThreadState *ts, tealet_run_t func, void **arg); +/* Running the evaluation loop in a new tealet */ +PyAPI_FUNC(int) slp_run_tasklet_stub(PyThreadState *ts); /* the special version of eval_frame */ PyAPI_FUNC(PyObject *) slp_eval_frame(struct _frame *f); diff --git a/Stackless/core/stacklesseval.c b/Stackless/core/stacklesseval.c index 4b9a5250c20291..a53cbb549f2165 100644 --- a/Stackless/core/stacklesseval.c +++ b/Stackless/core/stacklesseval.c @@ -124,9 +124,8 @@ slp_tealet_error(int err) * above. This can be simplified, TODO */ static int -make_initial_stub(void) +make_initial_stub(PyThreadState *ts) { - PyThreadState *ts = PyThreadState_GET(); if (ts->st.tealet_main == NULL) { tealet_alloc_t ta = { (tealet_malloc_t)&PyMem_Malloc, @@ -146,18 +145,29 @@ make_initial_stub(void) return -1; } +static void +destroy_initial_stub(PyThreadState *ts) +{ + tealet_delete(ts->st.initial_stub); + ts->st.initial_stub = NULL; +} + /* The function that runs tasklet loop in a tealet */ static tealet_t *tasklet_stub_func(tealet_t *me, void *arg) { PyThreadState *ts = PyThreadState_GET(); PyFrameObject *f = ts->frame; + PyObject *result; ts->frame = NULL; ts->st.nesting_level = 0; Py_CLEAR(ts->st.del_post_switch); - slp_run_tasklet(f); - /* We should never return. The switch back is performed - * lower on the stack, in tasklet_endv + result = slp_run_tasklet(f); + + /* this tealet is returning, which means that main is returning. Switch back + * to the main tealet. The result is passed to the target */ + tealet_exit(ts->st.tealet_main, (void*)result, TEALET_EXIT_DEFAULT); + /* this should never fail */ assert(0); return NULL; } @@ -166,15 +176,12 @@ static tealet_t *tasklet_stub_func(tealet_t *me, void *arg) * use the tasklet evaluation loop */ -/* Running the top level stub */ +/* Running a function in the top level stub */ int -slp_run_initial_stub(tealet_run_t func, void *arg) +slp_run_initial_stub(PyThreadState *ts, tealet_run_t func, void **arg) { - PyThreadState *ts = PyThreadState_GET(); tealet_t *stub; int result; - if (func == NULL) - func = &tasklet_stub_func; stub = tealet_duplicate(ts->st.initial_stub); if (!stub) { PyErr_NoMemory(); @@ -186,6 +193,28 @@ slp_run_initial_stub(tealet_run_t func, void *arg) return 0; } +/* run the top level loop from the main tasklet. This invocation expects\ + * a return value + */ +static PyObject * +slp_run_main_stub(PyThreadState *ts) +{ + void *arg; + /* switch into a stub duplicate. Run evaluation loop. Then switch back */ + int result = slp_run_initial_stub(ts, &tasklet_stub_func, &arg); + if (result) + return NULL; + return (PyObject*)arg; +} + +/* call the top level loop as a means of startin a new such loop, hardswitching out + * of a tasklet. This invocation does not expect a return value + */ +int +slp_run_tasklet_stub(PyThreadState *ts) +{ + return slp_run_initial_stub(ts, &tasklet_stub_func, NULL); +} PyObject * slp_eval_frame(PyFrameObject *f) @@ -194,33 +223,25 @@ slp_eval_frame(PyFrameObject *f) PyFrameObject *fprev = f->f_back; if (fprev == NULL && ts->st.main == NULL) { - int returning; - /* this is the initial frame, so mark the stack base */ - - /* - * careful, this caused me a major headache. - * it is *not* sufficient to just check for fprev == NULL. - * Reason: (observed with wxPython): - * A toplevel frame is run as a tasklet. When its frame - * is deallocated (in tasklet_end), a Python object - * with a __del__ method is destroyed. This __del__ - * will run as a toplevel frame, with f_back == NULL! - */ - - returning = make_initial_stub(); - if (returning < 0) - return NULL; - /* returning will be 1 if we "switched back" to this stub, and 0 - * if this is the original call that just created the stub. - * If the stub is being reused, the argument, i.e. the frame, - * is in ts->frame + /* this is the initial frame. From here we must + * run the evaluation loop on a separate tealet, so that + * all such tealets are equivalent and can jump back to main + * when they exit. */ - if (returning == 1) { - f = ts->frame; - ts->frame = NULL; + PyObject *result; + if (!ts->st.initial_stub) { + if (make_initial_stub(ts)) + return NULL; } - return slp_run_tasklet(f); + ts->frame = f; + result = slp_run_main_stub(ts); + /* TODO: We could leave the stub around in the tstate and save + * ourselves the effort of recreating it all the time + */ + destroy_initial_stub(ts); + return result; } + Py_INCREF(Py_None); return slp_frame_dispatch(f, fprev, 0, Py_None); } @@ -332,6 +353,9 @@ void PyStackless_kill_tasks_with_stacks(int allthreads) PyThreadState *ts = PyThreadState_Get(); if (ts->st.main == NULL) { + /* TODO: Must destroy main and current afterwards, if required. + * also, only do this is there is any work to be done. + */ if (initialize_main_and_current()) { PyObject *s = PyString_FromString("tasklet cleanup"); PyErr_WriteUnraisable(s); diff --git a/Stackless/module/scheduling.c b/Stackless/module/scheduling.c index 49e6117ea278d9..7d1c171d4b5883 100644 --- a/Stackless/module/scheduling.c +++ b/Stackless/module/scheduling.c @@ -348,7 +348,7 @@ slp_transfer(PyThreadState *ts, tealet_t *cst, PyTaskletObject *prev) result = tealet_switch(cst, NULL); } else { assert(TEALET_MAIN(ts->st.initial_stub) == ts->st.tealet_main); - result = slp_run_initial_stub(NULL, NULL); + result = slp_run_tasklet_stub(ts); } /* we are back, or failed, no cstate in tasklet */ @@ -1211,17 +1211,6 @@ tasklet_end(PyObject *retval) */ TASKLET_SETVAL(task, retval); - if (ismain) { - /* - * Because of soft switching, we may find ourself in the top level of a stack that was created - * using another stub (another entry point into stackless). If so, we need a final return to - * the original stub if necessary. (Meanwhile, task->cstate may be an old nesting state and not - * the original stub, so we take the stub from the tstate) - */ - if (tealet_current(ts->st.tealet_main) != ts->st.tealet_main) - slp_transfer_return(ts->st.tealet_main); - } - /* remove from runnables */ slp_current_remove(); diff --git a/Stackless/module/stacklessmodule.c b/Stackless/module/stacklessmodule.c index e96768e08152e8..67d18ed46db08a 100644 --- a/Stackless/module/stacklessmodule.c +++ b/Stackless/module/stacklessmodule.c @@ -589,13 +589,14 @@ PyObject * test_outside(PyObject *self) { PyThreadState *ts = PyThreadState_GET(); - PyTaskletObject *stmain = ts->st.main; + PyTaskletObject *stcurrent, *stmain = ts->st.main; tealet_t *cst = ts->st.initial_stub; PyFrameObject *f = ts->frame; int recursion_depth = ts->recursion_depth; int nesting_level = ts->st.nesting_level; PyObject *ret = Py_None; int jump = ts->st.serial_last_jump; + tealet_t *old_main = ts->st.tealet_main; Py_INCREF(ret); ts->st.main = NULL; @@ -603,7 +604,12 @@ test_outside(PyObject *self) ts->st.nesting_level = 0; ts->frame = NULL; ts->recursion_depth = 0; + stcurrent = ts->st.current; slp_current_remove(); + /* must reset the "main" tealet to be this one, so that a final + * switch back lands us here, rather than in the "real" main tealet. + */ + ts->st.tealet_main = tealet_current(old_main); while (ts->st.runcount > 0) { Py_DECREF(ret); ret = PyStackless_Schedule(Py_None, 0); @@ -615,8 +621,9 @@ test_outside(PyObject *self) // TODO: destroy initial stub //Py_XDECREF(ts->st.initial_stub); ts->st.initial_stub = cst; + ts->st.tealet_main = old_main; ts->frame = f; - slp_current_insert(stmain); + slp_current_insert(stcurrent); ts->recursion_depth = recursion_depth; ts->st.nesting_level = nesting_level; ts->st.serial_last_jump = jump; diff --git a/Stackless/module/taskletobject.c b/Stackless/module/taskletobject.c index b109ee2d6bbffa..ea9362ff08f780 100644 --- a/Stackless/module/taskletobject.c +++ b/Stackless/module/taskletobject.c @@ -200,7 +200,7 @@ PyTasklet_New(PyTypeObject *type, PyObject *func) PyThreadState *ts = PyThreadState_GET(); PyTaskletObject *t; - /* we always need a cstate, so be sure to initialize */ + /* TODO: initial stub is not needed anymore */ if (ts->st.initial_stub == NULL) return PyTasklet_New_M(type, func); if (func != NULL && !PyCallable_Check(func)) TYPE_ERROR("tasklet function must be a callable", NULL); From ca37c6ec04b2df5915b1bada24f3f55a1203e41b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Fri, 19 Jul 2013 19:25:41 +0000 Subject: [PATCH 09/35] Fix unittests to run. Correcly assert relatedness. --- Stackless/module/scheduling.c | 4 ++-- Stackless/tealet/tealet.h | 3 +++ Stackless/unittests/test_pickle.py | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Stackless/module/scheduling.c b/Stackless/module/scheduling.c index 7d1c171d4b5883..a0e26a700edec7 100644 --- a/Stackless/module/scheduling.c +++ b/Stackless/module/scheduling.c @@ -344,10 +344,10 @@ slp_transfer(PyThreadState *ts, tealet_t *cst, PyTaskletObject *prev) if (cst) { /* make sure we are not trying to jump between threads */ - assert(TEALET_MAIN(cst) == ts->st.tealet_main); + assert(TEALET_RELATED(ts->st.tealet_main, cst)); result = tealet_switch(cst, NULL); } else { - assert(TEALET_MAIN(ts->st.initial_stub) == ts->st.tealet_main); + assert(TEALET_RELATED(ts->st.tealet_main, ts->st.initial_stub)); result = slp_run_tasklet_stub(ts); } diff --git a/Stackless/tealet/tealet.h b/Stackless/tealet/tealet.h index 15f557ed9e27cd..5a9b387928c995 100644 --- a/Stackless/tealet/tealet.h +++ b/Stackless/tealet/tealet.h @@ -187,4 +187,7 @@ int tealet_get_count(tealet_t *t); #define TEALET_IS_MAIN(t) ((t) == TEALET_MAIN(t)) #define TEALET_CURRENT_IS_MAIN(t) (tealet_current(t) == TEALET_MAIN(t)) +/* see if two tealets share the same MAIN, and can therefore be switched between */ +#define TEALET_RELATED(t1, t2) (TEALET_MAIN(t1) == TEALET_MAIN(t2)) + #endif /* _TEALET_H_ */ diff --git a/Stackless/unittests/test_pickle.py b/Stackless/unittests/test_pickle.py index 30e951df2b068f..1c388dc1dc3392 100644 --- a/Stackless/unittests/test_pickle.py +++ b/Stackless/unittests/test_pickle.py @@ -221,6 +221,7 @@ def run_pickled(self, func, *args): except AttributeError: have_fromkeys = False +@unittest.skip("until safe pickling is done") class TestConcretePickledTasklets(TestPickledTasklets): def testClassPersistence(self): t1 = CustomTasklet(nothing)() From 896f9fb7fa43ec9f6ace5ee7badefd3d1025ac73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Fri, 19 Jul 2013 19:33:27 +0000 Subject: [PATCH 10/35] PyTasklet_New_M is no longer needed. --- Stackless/module/taskletobject.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/Stackless/module/taskletobject.c b/Stackless/module/taskletobject.c index ea9362ff08f780..b641bb185ba49e 100644 --- a/Stackless/module/taskletobject.c +++ b/Stackless/module/taskletobject.c @@ -184,24 +184,12 @@ tasklet_dealloc(PyTaskletObject *t) } -static PyTaskletObject * -PyTasklet_New_M(PyTypeObject *type, PyObject *func) -{ - char *fmt = "(O)"; - - if (func == NULL) fmt = NULL; - return (PyTaskletObject *) PyStackless_CallMethod_Main( - (PyObject*)type, NULL, fmt, func); -} - PyTaskletObject * PyTasklet_New(PyTypeObject *type, PyObject *func) { PyThreadState *ts = PyThreadState_GET(); PyTaskletObject *t; - /* TODO: initial stub is not needed anymore */ - if (ts->st.initial_stub == NULL) return PyTasklet_New_M(type, func); if (func != NULL && !PyCallable_Check(func)) TYPE_ERROR("tasklet function must be a callable", NULL); if (type == NULL) type = &PyTasklet_Type; From ddaa211d6dfd97f48d285c85c446fdf7e7bfa48a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Sat, 20 Jul 2013 10:30:29 +0000 Subject: [PATCH 11/35] Add feature to allocate extra data to tealets by giving an extra size. --- Stackless/core/stacklesseval.c | 6 +++--- Stackless/tealet/tealet.c | 30 ++++++++++++++++++++---------- Stackless/tealet/tealet.h | 18 +++++++++++++----- 3 files changed, 36 insertions(+), 18 deletions(-) diff --git a/Stackless/core/stacklesseval.c b/Stackless/core/stacklesseval.c index a53cbb549f2165..6021fcc90ba2e6 100644 --- a/Stackless/core/stacklesseval.c +++ b/Stackless/core/stacklesseval.c @@ -80,7 +80,7 @@ static tealet_t *slp_stub_main(tealet_t *current, void *arg) /* create a stub and return it */ tealet_t *slp_stub_new(tealet_t *t) { void *arg = (void*)tealet_current(t); - return tealet_new(t, slp_stub_main, &arg); + return tealet_new(t, slp_stub_main, &arg, 0); } /* run a stub */ @@ -131,7 +131,7 @@ make_initial_stub(PyThreadState *ts) (tealet_malloc_t)&PyMem_Malloc, (tealet_free_t)&PyMem_Free, 0}; - ts->st.tealet_main = tealet_initialize(&ta); + ts->st.tealet_main = tealet_initialize(&ta, 0); if (!ts->st.tealet_main) goto err; @@ -182,7 +182,7 @@ slp_run_initial_stub(PyThreadState *ts, tealet_run_t func, void **arg) { tealet_t *stub; int result; - stub = tealet_duplicate(ts->st.initial_stub); + stub = tealet_duplicate(ts->st.initial_stub, 0); if (!stub) { PyErr_NoMemory(); return -1; diff --git a/Stackless/tealet/tealet.c b/Stackless/tealet/tealet.c index f642d051e3afa7..c916e78d15bf5f 100644 --- a/Stackless/tealet/tealet.c +++ b/Stackless/tealet/tealet.c @@ -90,6 +90,12 @@ typedef struct tealet_sub_t { #endif } tealet_sub_t; +/* a structure incorporating extra data */ +typedef struct tealet_nonmain_t { + tealet_sub_t base; + double _extra[1]; /* start of any extra data */ +} tealet_nonmain_t; + /* The main tealet has additional fields for housekeeping */ typedef struct tealet_main_t { tealet_sub_t base; @@ -103,6 +109,7 @@ typedef struct tealet_main_t { int g_tealets; /* number of active tealets excluding main */ int g_counter; /* total number of tealets */ #endif + double _extra[1]; /* start of any extra data */ } tealet_main_t; #define TEALET_IS_MAIN_STACK(t) (((tealet_sub_t *)(t))->stack_far == STACK_FAR_MAIN) @@ -540,11 +547,11 @@ static int tealet_initialstub(tealet_main_t *g_main, tealet_run_t run, void *sta return 0; } -static tealet_sub_t *tealet_alloc(tealet_main_t *g_main, tealet_alloc_t *alloc) +static tealet_sub_t *tealet_alloc(tealet_main_t *g_main, tealet_alloc_t *alloc, size_t extrasize) { - size_t size; tealet_sub_t *g; - size = g_main == NULL ? sizeof(tealet_main_t) : sizeof(tealet_sub_t); + size_t basesize = g_main == NULL ? offsetof(tealet_main_t, _extra) : offsetof(tealet_nonmain_t, _extra); + size_t size = basesize + extrasize; if (g_main != NULL) alloc = &g_main->g_alloc; g = (tealet_sub_t*) alloc->malloc_p(size, alloc->context); @@ -553,7 +560,10 @@ static tealet_sub_t *tealet_alloc(tealet_main_t *g_main, tealet_alloc_t *alloc) if (g_main == NULL) g_main = (tealet_main_t *)g; g->base.main = (tealet_t *)g_main; - g->base.data = NULL; + if (extrasize) + g->base.extra = (void*)((char*)g + basesize); + else + g->base.extra = NULL; g->stack = NULL; g->stack_far = NULL; #ifndef NDEBUG @@ -565,11 +575,11 @@ static tealet_sub_t *tealet_alloc(tealet_main_t *g_main, tealet_alloc_t *alloc) /************************************************************/ -tealet_t *tealet_initialize(tealet_alloc_t *alloc) +tealet_t *tealet_initialize(tealet_alloc_t *alloc, size_t extrasize) { tealet_sub_t *g; tealet_main_t *g_main; - g = tealet_alloc(NULL, alloc); + g = tealet_alloc(NULL, alloc, extrasize); if (g == NULL) return NULL; g_main = (tealet_main_t *)g; @@ -613,14 +623,14 @@ void tealet_free(tealet_t *tealet, void *p) tealet_int_free(g_main, p); } -tealet_t *tealet_new(tealet_t *tealet, tealet_run_t run, void **parg) +tealet_t *tealet_new(tealet_t *tealet, tealet_run_t run, void **parg, size_t extrasize) { tealet_sub_t *result; /* store this until we return */ int fail; tealet_main_t *g_main = TEALET_GET_MAIN(tealet); assert(TEALET_IS_MAIN_STACK(g_main)); assert(!g_main->g_target); - result = tealet_alloc(g_main, NULL); + result = tealet_alloc(g_main, NULL, extrasize); if (result == NULL) return NULL; /* Could not allocate */ g_main->g_target = result; @@ -688,13 +698,13 @@ int tealet_exit(tealet_t *target, void *arg, int flags) return result; } -tealet_t *tealet_duplicate(tealet_t *tealet) +tealet_t *tealet_duplicate(tealet_t *tealet, size_t extrasize) { tealet_sub_t *g_tealet = (tealet_sub_t *)tealet; tealet_main_t *g_main = TEALET_GET_MAIN(g_tealet); tealet_sub_t *g_copy; assert(g_tealet != g_main->g_current && g_tealet != (tealet_sub_t*)g_main); - g_copy = tealet_alloc(g_main, NULL); + g_copy = tealet_alloc(g_main, NULL, extrasize); if (g_copy == NULL) return NULL; #ifndef NDEBUG diff --git a/Stackless/tealet/tealet.h b/Stackless/tealet/tealet.h index 5a9b387928c995..968c47bbc2d58e 100644 --- a/Stackless/tealet/tealet.h +++ b/Stackless/tealet/tealet.h @@ -40,10 +40,13 @@ typedef struct tealet_alloc_t { } -/* The user-visible tealet structure */ +/* The user-visible tealet structure. If an "extrasize" is provided when + * the tealet is created, "extra" points to a block of that size, otherwise + * it is initialized to NULL + */ typedef struct tealet_t { struct tealet_t *main; /* pointer to the main tealet */ - void *data; /* general-purpose, store whatever you want here */ + void *extra; /* private fields follow */ } tealet_t; @@ -72,7 +75,7 @@ typedef tealet_t *(*tealet_run_t)(tealet_t *current, void *arg); * different main tealet. */ TEALET_API -tealet_t *tealet_initialize(tealet_alloc_t *alloc); +tealet_t *tealet_initialize(tealet_alloc_t *alloc, size_t extrasize); /* Tear down the main tealet. Call e.g. after a thread finishes (including * all its tealets). @@ -100,9 +103,11 @@ void tealet_free(tealet_t *tealet, void *p); * causing this return. * 'arg' can be NULL, in which case NULL is passed to run and no result * argument is passed. + * If 'extrasize' is non-zero, extra data will be allocated and the tealet's + * 'extra' member points to it, otherwise it is set to NULL */ TEALET_API -tealet_t *tealet_new(tealet_t *tealet, tealet_run_t run, void **parg); +tealet_t *tealet_new(tealet_t *tealet, tealet_run_t run, void **parg, size_t extrasize); /* Switch to another tealet. Execution continues there. The tealet * passed in must not have been freed yet and must descend from @@ -145,7 +150,7 @@ int tealet_exit(tealet_t *target, void *arg, int flags); * mechanism to provide the copy with fresh data. */ TEALET_API -tealet_t *tealet_duplicate(tealet_t *tealet); +tealet_t *tealet_duplicate(tealet_t *tealet, size_t extrasize); /* Deallocate a tealet. Use this to delete a tealet that has exited * with tealet_exit() with 'TEALET_EXIT_NODELETE', or defunct tealets. @@ -190,4 +195,7 @@ int tealet_get_count(tealet_t *t); /* see if two tealets share the same MAIN, and can therefore be switched between */ #define TEALET_RELATED(t1, t2) (TEALET_MAIN(t1) == TEALET_MAIN(t2)) +/* convenience access to a typecast extra pointer */ +#define TEALET_EXTRA(t, tp) ((tp*)((t)->extra)) + #endif /* _TEALET_H_ */ From 73398f67483bf2335d15d7ab3c7ed7415ecefe02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Sat, 20 Jul 2013 11:42:25 +0000 Subject: [PATCH 12/35] rename struct _tasklet to struct PyTaskletObject. This is safe and recommended, structs have their own namespace and their names don't clash with typedefs. --- Stackless/core/stackless_structs.h | 16 ++++++++-------- Stackless/core/stackless_tstate.h | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Stackless/core/stackless_structs.h b/Stackless/core/stackless_structs.h index 60d6d3df6991d1..8ea6174e49aa8f 100644 --- a/Stackless/core/stackless_structs.h +++ b/Stackless/core/stackless_structs.h @@ -64,7 +64,7 @@ extern "C" { ***************************************************************************/ -typedef struct _tasklet_flags { +typedef struct PyTaskletObject_flags { int blocked: 2; unsigned int atomic: 1; unsigned int ignore_nesting: 1; @@ -75,17 +75,17 @@ typedef struct _tasklet_flags { } PyTaskletFlagStruc; -typedef struct _tasklet { +typedef struct PyTaskletObject { PyObject_HEAD - struct _tasklet *next; - struct _tasklet *prev; + struct PyTaskletObject *next; + struct PyTaskletObject *prev; union { struct _frame *frame; struct _cframe *cframe; } f; PyObject *tempval; /* bits stuff */ - struct _tasklet_flags flags; + struct PyTaskletObject_flags flags; int recursion_depth; int nesting_level; PyThreadState *tstate; @@ -106,7 +106,7 @@ typedef struct _cstack { #else long serial; #endif - struct _tasklet *task; + struct PyTaskletObject *task; int nesting_level; PyThreadState *tstate; #ifdef _SEH32 @@ -159,8 +159,8 @@ typedef struct _channel_flags { typedef struct _channel { PyObject_HEAD /* make sure that these fit tasklet's next/prev */ - struct _tasklet *head; - struct _tasklet *tail; + struct PyTaskletObject *head; + struct PyTaskletObject *tail; int balance; struct _channel_flags flags; PyObject *chan_weakreflist; diff --git a/Stackless/core/stackless_tstate.h b/Stackless/core/stackless_tstate.h index f60efe7b7118cb..1d81312b4d263d 100644 --- a/Stackless/core/stackless_tstate.h +++ b/Stackless/core/stackless_tstate.h @@ -18,9 +18,9 @@ typedef struct _sts { #endif struct tealet_t *tealet_main; /* main tasklet */ - struct _tasklet *main; + struct PyTaskletObject *main; /* runnable tasklets */ - struct _tasklet *current; + struct PyTaskletObject *current; int runcount; /* scheduling */ From 082c2723c2c62ba519e17c9940137c9de4d7a0fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Sat, 20 Jul 2013 11:47:22 +0000 Subject: [PATCH 13/35] Reorganize. Put the tealet switching code into stackless_tealet.c/.h --- PCbuild/pythoncore.vcproj | 8 + Stackless/core/stackless_impl.h | 16 -- Stackless/core/stackless_structs.h | 3 +- Stackless/core/stackless_tealet.c | 274 +++++++++++++++++++++++++++++ Stackless/core/stackless_tealet.h | 20 +++ Stackless/core/stacklesseval.c | 170 +----------------- Stackless/module/scheduling.c | 99 +---------- 7 files changed, 311 insertions(+), 279 deletions(-) create mode 100644 Stackless/core/stackless_tealet.c create mode 100644 Stackless/core/stackless_tealet.h diff --git a/PCbuild/pythoncore.vcproj b/PCbuild/pythoncore.vcproj index 19c2fcafb3ad9e..ecc68abaa6a73a 100644 --- a/PCbuild/pythoncore.vcproj +++ b/PCbuild/pythoncore.vcproj @@ -1976,6 +1976,14 @@ RelativePath="..\Stackless\core\stackless_structs.h" > + + + + diff --git a/Stackless/core/stackless_impl.h b/Stackless/core/stackless_impl.h index c9f16d678fbac5..c2d0ff77b2893e 100644 --- a/Stackless/core/stackless_impl.h +++ b/Stackless/core/stackless_impl.h @@ -17,8 +17,6 @@ extern "C" { #include "core/stackless_structs.h" #include "pickling/prickelpit.h" -#include - #undef STACKLESS_SPY /* * if a platform wants to support self-inspection via _peek, @@ -47,15 +45,6 @@ PyAPI_FUNC(PyCStackObject *) slp_cstack_new(PyCStackObject **cst, PyAPI_FUNC(size_t) slp_cstack_save(PyCStackObject *cstprev); PyAPI_FUNC(void) slp_cstack_restore(PyCStackObject *cst); -PyAPI_FUNC(int) slp_transfer(PyCStackObject **cstprev, PyCStackObject *cst, - PyTaskletObject *prev); - -#ifdef Py_DEBUG -PyAPI_FUNC(int) slp_transfer_return(PyCStackObject *cst); -#else -#define slp_transfer_return(cst) \ - slp_transfer(NULL, (cst), NULL) -#endif #endif PyAPI_FUNC(int) _PyStackless_InitTypes(void); @@ -71,11 +60,6 @@ PyAPI_FUNC(void) PyStackless_Fini(void); PyAPI_FUNC(void) PyStackless_kill_tasks_with_stacks(int allthreads); -/* Running a new tealet */ -PyAPI_FUNC(int) slp_run_initial_stub(PyThreadState *ts, tealet_run_t func, void **arg); -/* Running the evaluation loop in a new tealet */ -PyAPI_FUNC(int) slp_run_tasklet_stub(PyThreadState *ts); - /* the special version of eval_frame */ PyAPI_FUNC(PyObject *) slp_eval_frame(struct _frame *f); diff --git a/Stackless/core/stackless_structs.h b/Stackless/core/stackless_structs.h index 8ea6174e49aa8f..98ebc88d242353 100644 --- a/Stackless/core/stackless_structs.h +++ b/Stackless/core/stackless_structs.h @@ -6,9 +6,10 @@ extern "C" { #endif /* platform specific constants (mainly SEH stuff to store )*/ +/* TODO: remove vestiges of old switching */ #include "platf/slp_platformselect.h" -#include +#include "stackless_tealet.h" diff --git a/Stackless/core/stackless_tealet.c b/Stackless/core/stackless_tealet.c new file mode 100644 index 00000000000000..fdf1d39b3fe5ce --- /dev/null +++ b/Stackless/core/stackless_tealet.c @@ -0,0 +1,274 @@ + +#include "stackless_tealet.h" + +#include +#include "frameobject.h" +#include "stackless_impl.h" + +/**************************************************************** + *Implement copyable tealet stubs by using a trampoline + */ +struct slp_stub_arg +{ + tealet_t *current; + tealet_run_t run; + void *runarg; +}; + +static tealet_t *slp_stub_main(tealet_t *current, void *arg) +{ + void *myarg = 0; + /* the caller is in arg, return right back to him */ + tealet_switch((tealet_t*)arg, &myarg); + /* now we are back, myarg should contain the arg to the run function. + * We were possibly duplicated, so can't trust the original function args. + */ + { + struct slp_stub_arg sarg = *(struct slp_stub_arg*)myarg; + tealet_free(sarg.current, myarg); + return (sarg.run)(sarg.current, sarg.runarg); + } +} + +/* create a stub and return it */ +tealet_t *slp_stub_new(tealet_t *t) { + void *arg = (void*)tealet_current(t); + return tealet_new(t, slp_stub_main, &arg, 0); +} + +/* run a stub */ +int slp_stub_run(tealet_t *stub, tealet_run_t run, void **parg) +{ + int result; + void *myarg; + + /* we cannot pass arguments to a different tealet on the stack */ + struct slp_stub_arg *psarg = (struct slp_stub_arg*)tealet_malloc(stub, sizeof(struct slp_stub_arg)); + if (!psarg) + return TEALET_ERR_MEM; + psarg->current = stub; + psarg->run = run; + psarg->runarg = parg ? *parg : NULL; + myarg = (void*)psarg; + result = tealet_switch(stub, &myarg); + if (result) { + /* failure */ + tealet_free(stub, psarg); + return result; + } + /* pass back the arg value from the switch */ + if (parg) + *parg = myarg; + return 0; +} + +/* end of generic tealet stub code */ + +/* translate tealet errors into python errors */ +static int +slp_tealet_error(int err) +{ + assert(err); + if (err == TEALET_ERR_MEM) + PyErr_NoMemory(); + assert(err == TEALET_ERR_DEFUNCT); + PyErr_SetString(PyExc_RuntimeError, "Tealet corrupt"); + return -1; +} + +/* the current mechanism is based on the generic callable stubs + * above. This can be simplified, TODO + */ +int +slp_make_initial_stub(PyThreadState *ts) +{ + if (ts->st.tealet_main == NULL) { + tealet_alloc_t ta = { + (tealet_malloc_t)&PyMem_Malloc, + (tealet_free_t)&PyMem_Free, + 0}; + ts->st.tealet_main = tealet_initialize(&ta, 0); + if (!ts->st.tealet_main) + goto err; + + } + ts->st.initial_stub = slp_stub_new(ts->st.tealet_main); + if (!ts->st.initial_stub) + goto err; + return 0; +err: + PyErr_NoMemory(); + return -1; +} + +void +slp_destroy_initial_stub(PyThreadState *ts) +{ + tealet_delete(ts->st.initial_stub); + ts->st.initial_stub = NULL; +} + +/* The function that runs tasklet loop in a tealet */ +static tealet_t *tasklet_stub_func(tealet_t *me, void *arg) +{ + PyThreadState *ts = PyThreadState_GET(); + PyFrameObject *f = ts->frame; + PyObject *result; + ts->frame = NULL; + ts->st.nesting_level = 0; + Py_CLEAR(ts->st.del_post_switch); + result = slp_run_tasklet(f); + + /* this tealet is returning, which means that main is returning. Switch back + * to the main tealet. The result is passed to the target + */ + tealet_exit(ts->st.tealet_main, (void*)result, TEALET_EXIT_DEFAULT); + /* this should never fail */ + assert(0); + return NULL; +} + +/* Running a function in the top level stub. If NULL is provided, + * use the tasklet evaluation loop + */ + +/* Running a function in the top level stub */ +int +slp_run_initial_stub(PyThreadState *ts, tealet_run_t func, void **arg) +{ + tealet_t *stub; + int result; + stub = tealet_duplicate(ts->st.initial_stub, 0); + if (!stub) { + PyErr_NoMemory(); + return -1; + } + result = slp_stub_run(stub, func, arg); + if (result) + return slp_tealet_error(result); + return 0; +} + +/* run the top level loop from the main tasklet. This invocation expects + * a return value + */ +PyObject * +slp_run_stub_from_main(PyThreadState *ts) +{ + void *arg; + /* switch into a stub duplicate. Run evaluation loop. Then switch back */ + int result = slp_run_initial_stub(ts, &tasklet_stub_func, &arg); + if (result) + return NULL; + return (PyObject*)arg; +} + +/* call the top level loop as a means of startin a new such loop, hardswitching out + * of a tasklet. This invocation does not expect a return value + */ +int +slp_run_stub_from_worker(PyThreadState *ts) +{ + return slp_run_initial_stub(ts, &tasklet_stub_func, NULL); +} + +/* hard switching of tasklets */ + +int +slp_transfer(PyThreadState *ts, tealet_t *cst, PyTaskletObject *prev) +{ + int result; + int nesting_level; + assert(prev->cstate == NULL); + + nesting_level = prev->tstate->st.nesting_level; + prev->nesting_level = nesting_level; + /* mark the old tasklet as having cstate */ + prev->cstate = tealet_current(ts->st.tealet_main); + + if (cst) { + /* make sure we are not trying to jump between threads */ + assert(TEALET_RELATED(ts->st.tealet_main, cst)); + result = tealet_switch(cst, NULL); + } else { + assert(TEALET_RELATED(ts->st.tealet_main, ts->st.initial_stub)); + result = slp_run_stub_from_worker(ts); + } + + /* we are back, or failed, no cstate in tasklet */ + prev->cstate = NULL; + prev->tstate->st.nesting_level = nesting_level; + if (!result) { + if (ts->st.del_post_switch) { + PyObject *tmp; + TASKLET_CLAIMVAL(ts->st.current, &tmp); + Py_CLEAR(ts->st.del_post_switch); + TASKLET_SETVAL_OWN(ts->st.current, tmp); + } + return 0; + } + if (result == TEALET_ERR_MEM) + PyErr_NoMemory(); + else + PyErr_SetString(PyExc_RuntimeError, "Invalid tasklet"); + return -1; +} + +int +slp_transfer_return(tealet_t *cst) +{ + int result = tealet_exit(cst, NULL, TEALET_EXIT_DEFAULT); + if (result) { + /* emergency switch back to main tealet */ + PyThreadState *ts = PyThreadState_GET(); + PyErr_SetString(PyExc_RuntimeError, "Invalid tealet"); + tealet_exit(ts->st.tealet_main, NULL, TEALET_EXIT_DEFAULT); + /* emergency!. Transfer to the _real_ main */ + tealet_exit(TEALET_MAIN(ts->st.tealet_main), NULL, TEALET_EXIT_DEFAULT); + } + assert(0); /* never returns */ + return 0; +} + +int +slp_transfer_with_exc(PyThreadState *ts, tealet_t *cst, PyTaskletObject *prev) +{ + int tracing = ts->tracing; + int use_tracing = ts->use_tracing; + + Py_tracefunc c_profilefunc = ts->c_profilefunc; + Py_tracefunc c_tracefunc = ts->c_tracefunc; + PyObject *c_profileobj = ts->c_profileobj; + PyObject *c_traceobj = ts->c_traceobj; + + PyObject *exc_type = ts->exc_type; + PyObject *exc_value = ts->exc_value; + PyObject *exc_traceback = ts->exc_traceback; + int ret; + + ts->exc_type = ts->exc_value = ts->exc_traceback = NULL; + ts->c_profilefunc = ts->c_tracefunc = NULL; + ts->c_profileobj = ts->c_traceobj = NULL; + ts->use_tracing = ts->tracing = 0; + + /* note that trace/profile are set without ref */ + Py_XINCREF(c_profileobj); + Py_XINCREF(c_traceobj); + + ret = slp_transfer(ts, cst, prev); + + ts->tracing = tracing; + ts->use_tracing = use_tracing; + + ts->c_profilefunc = c_profilefunc; + ts->c_tracefunc = c_tracefunc; + ts->c_profileobj = c_profileobj; + ts->c_traceobj = c_traceobj; + Py_XDECREF(c_profileobj); + Py_XDECREF(c_traceobj); + + ts->exc_type = exc_type; + ts->exc_value = exc_value; + ts->exc_traceback = exc_traceback; + return ret; +} diff --git a/Stackless/core/stackless_tealet.h b/Stackless/core/stackless_tealet.h new file mode 100644 index 00000000000000..4dfa2d8f59d1a5 --- /dev/null +++ b/Stackless/core/stackless_tealet.h @@ -0,0 +1,20 @@ +/* interface to the stack switching routines used by stackless */ +#ifndef _STACKLESS_TEALET_H +#define _STACKLESS_TEALET_H + +#include +#include + +int slp_make_initial_stub(PyThreadState *ts); +void slp_destroy_initial_stub(PyThreadState *ts); +int slp_run_initial_stub(PyThreadState *ts, tealet_run_t func, void **arg); +PyObject * slp_run_stub_from_main(PyThreadState *ts); +int slp_run_stub_from_worker(PyThreadState *ts); + +/* hard switching of tasklets */ +int slp_transfer(PyThreadState *ts, tealet_t *cst, struct PyTaskletObject *prev); +int slp_transfer_with_exc(PyThreadState *ts, tealet_t *cst, struct PyTaskletObject *prev); +int slp_transfer_return(tealet_t *cst); + + +#endif \ No newline at end of file diff --git a/Stackless/core/stacklesseval.c b/Stackless/core/stacklesseval.c index 6021fcc90ba2e6..b0df890b05338f 100644 --- a/Stackless/core/stacklesseval.c +++ b/Stackless/core/stacklesseval.c @@ -11,6 +11,8 @@ /* platform specific constants */ #include "platf/slp_platformselect.h" +#include "stackless_tealet.h" + /* Stackless extension for ceval.c */ /****************************************************** @@ -52,169 +54,7 @@ PyTaskletObject *slp_tasklet_chain = NULL; -/**************************************************************** - *Implement copyable stubs by using a trampoline - */ -struct slp_stub_arg -{ - tealet_t *current; - tealet_run_t run; - void *runarg; -}; - -static tealet_t *slp_stub_main(tealet_t *current, void *arg) -{ - void *myarg = 0; - /* the caller is in arg, return right back to him */ - tealet_switch((tealet_t*)arg, &myarg); - /* now we are back, myarg should contain the arg to the run function. - * We were possibly duplicated, so can't trust the original function args. - */ - { - struct slp_stub_arg sarg = *(struct slp_stub_arg*)myarg; - tealet_free(sarg.current, myarg); - return (sarg.run)(sarg.current, sarg.runarg); - } -} - -/* create a stub and return it */ -tealet_t *slp_stub_new(tealet_t *t) { - void *arg = (void*)tealet_current(t); - return tealet_new(t, slp_stub_main, &arg, 0); -} - -/* run a stub */ -int slp_stub_run(tealet_t *stub, tealet_run_t run, void **parg) -{ - int result; - void *myarg; - - /* we cannot pass arguments to a different tealet on the stack */ - struct slp_stub_arg *psarg = (struct slp_stub_arg*)tealet_malloc(stub, sizeof(struct slp_stub_arg)); - if (!psarg) - return TEALET_ERR_MEM; - psarg->current = stub; - psarg->run = run; - psarg->runarg = parg ? *parg : NULL; - myarg = (void*)psarg; - result = tealet_switch(stub, &myarg); - if (result) { - /* failure */ - tealet_free(stub, psarg); - return result; - } - /* pass back the arg value from the switch */ - if (parg) - *parg = myarg; - return 0; -} - -static int -slp_tealet_error(int err) -{ - assert(err); - if (err == TEALET_ERR_MEM) - PyErr_NoMemory(); - assert(err == TEALET_ERR_DEFUNCT); - PyErr_SetString(PyExc_RuntimeError, "Tealet corrupt"); - return -1; -} - -/* the current mechanism is based on the generic callable stubs - * above. This can be simplified, TODO - */ -static int -make_initial_stub(PyThreadState *ts) -{ - if (ts->st.tealet_main == NULL) { - tealet_alloc_t ta = { - (tealet_malloc_t)&PyMem_Malloc, - (tealet_free_t)&PyMem_Free, - 0}; - ts->st.tealet_main = tealet_initialize(&ta, 0); - if (!ts->st.tealet_main) - goto err; - - } - ts->st.initial_stub = slp_stub_new(ts->st.tealet_main); - if (!ts->st.initial_stub) - goto err; - return 0; -err: - PyErr_NoMemory(); - return -1; -} -static void -destroy_initial_stub(PyThreadState *ts) -{ - tealet_delete(ts->st.initial_stub); - ts->st.initial_stub = NULL; -} - -/* The function that runs tasklet loop in a tealet */ -static tealet_t *tasklet_stub_func(tealet_t *me, void *arg) -{ - PyThreadState *ts = PyThreadState_GET(); - PyFrameObject *f = ts->frame; - PyObject *result; - ts->frame = NULL; - ts->st.nesting_level = 0; - Py_CLEAR(ts->st.del_post_switch); - result = slp_run_tasklet(f); - - /* this tealet is returning, which means that main is returning. Switch back - * to the main tealet. The result is passed to the target - */ - tealet_exit(ts->st.tealet_main, (void*)result, TEALET_EXIT_DEFAULT); - /* this should never fail */ - assert(0); - return NULL; -} - -/* Running a function in the top level stub. If NULL is provided, - * use the tasklet evaluation loop - */ - -/* Running a function in the top level stub */ -int -slp_run_initial_stub(PyThreadState *ts, tealet_run_t func, void **arg) -{ - tealet_t *stub; - int result; - stub = tealet_duplicate(ts->st.initial_stub, 0); - if (!stub) { - PyErr_NoMemory(); - return -1; - } - result = slp_stub_run(stub, func, arg); - if (result) - return slp_tealet_error(result); - return 0; -} - -/* run the top level loop from the main tasklet. This invocation expects\ - * a return value - */ -static PyObject * -slp_run_main_stub(PyThreadState *ts) -{ - void *arg; - /* switch into a stub duplicate. Run evaluation loop. Then switch back */ - int result = slp_run_initial_stub(ts, &tasklet_stub_func, &arg); - if (result) - return NULL; - return (PyObject*)arg; -} - -/* call the top level loop as a means of startin a new such loop, hardswitching out - * of a tasklet. This invocation does not expect a return value - */ -int -slp_run_tasklet_stub(PyThreadState *ts) -{ - return slp_run_initial_stub(ts, &tasklet_stub_func, NULL); -} PyObject * slp_eval_frame(PyFrameObject *f) @@ -230,15 +70,15 @@ slp_eval_frame(PyFrameObject *f) */ PyObject *result; if (!ts->st.initial_stub) { - if (make_initial_stub(ts)) + if (slp_make_initial_stub(ts)) return NULL; } ts->frame = f; - result = slp_run_main_stub(ts); + result = slp_run_stub_from_main(ts); /* TODO: We could leave the stub around in the tstate and save * ourselves the effort of recreating it all the time */ - destroy_initial_stub(ts); + slp_destroy_initial_stub(ts); return result; } diff --git a/Stackless/module/scheduling.c b/Stackless/module/scheduling.c index a0e26a700edec7..64417ee12a8fe6 100644 --- a/Stackless/module/scheduling.c +++ b/Stackless/module/scheduling.c @@ -2,6 +2,7 @@ #ifdef STACKLESS #include "core/stackless_impl.h" +#include "core/stackless_tealet.h" #ifdef WITH_THREAD #include "pythread.h" @@ -330,102 +331,6 @@ PyTypeObject PyBomb_Type = { slp_schedule_hook_func *_slp_schedule_fasthook; PyObject *_slp_schedule_hook; -static int -slp_transfer(PyThreadState *ts, tealet_t *cst, PyTaskletObject *prev) -{ - int result; - int nesting_level; - assert(prev->cstate == NULL); - - nesting_level = prev->tstate->st.nesting_level; - prev->nesting_level = nesting_level; - /* mark the old tasklet as having cstate */ - prev->cstate = tealet_current(ts->st.tealet_main); - - if (cst) { - /* make sure we are not trying to jump between threads */ - assert(TEALET_RELATED(ts->st.tealet_main, cst)); - result = tealet_switch(cst, NULL); - } else { - assert(TEALET_RELATED(ts->st.tealet_main, ts->st.initial_stub)); - result = slp_run_tasklet_stub(ts); - } - - /* we are back, or failed, no cstate in tasklet */ - prev->cstate = NULL; - prev->tstate->st.nesting_level = nesting_level; - if (!result) { - if (ts->st.del_post_switch) { - PyObject *tmp; - TASKLET_CLAIMVAL(ts->st.current, &tmp); - Py_CLEAR(ts->st.del_post_switch); - TASKLET_SETVAL_OWN(ts->st.current, tmp); - } - return 0; - } - if (result == TEALET_ERR_MEM) - PyErr_NoMemory(); - else - PyErr_SetString(PyExc_RuntimeError, "Invalid tasklet"); - return -1; -} - -static int -slp_transfer_return(tealet_t *cst) -{ - int result = tealet_exit(cst, NULL, TEALET_EXIT_DEFAULT); - if (result) { - /* emergency switch back to main tealet */ - PyThreadState *ts = PyThreadState_GET(); - PyErr_SetString(PyExc_RuntimeError, "Invalid tealet"); - tealet_exit(ts->st.tealet_main, NULL, TEALET_EXIT_DEFAULT); - } - return 0; -} - -static int -transfer_with_exc(PyThreadState *ts, tealet_t *cst, PyTaskletObject *prev) -{ - int tracing = ts->tracing; - int use_tracing = ts->use_tracing; - - Py_tracefunc c_profilefunc = ts->c_profilefunc; - Py_tracefunc c_tracefunc = ts->c_tracefunc; - PyObject *c_profileobj = ts->c_profileobj; - PyObject *c_traceobj = ts->c_traceobj; - - PyObject *exc_type = ts->exc_type; - PyObject *exc_value = ts->exc_value; - PyObject *exc_traceback = ts->exc_traceback; - int ret; - - ts->exc_type = ts->exc_value = ts->exc_traceback = NULL; - ts->c_profilefunc = ts->c_tracefunc = NULL; - ts->c_profileobj = ts->c_traceobj = NULL; - ts->use_tracing = ts->tracing = 0; - - /* note that trace/profile are set without ref */ - Py_XINCREF(c_profileobj); - Py_XINCREF(c_traceobj); - - ret = slp_transfer(ts, cst, prev); - - ts->tracing = tracing; - ts->use_tracing = use_tracing; - - ts->c_profilefunc = c_profilefunc; - ts->c_tracefunc = c_tracefunc; - ts->c_profileobj = c_profileobj; - ts->c_traceobj = c_traceobj; - Py_XDECREF(c_profileobj); - Py_XDECREF(c_traceobj); - - ts->exc_type = exc_type; - ts->exc_value = exc_value; - ts->exc_traceback = exc_traceback; - return ret; -} - /* scheduler monitoring */ int @@ -1032,7 +937,7 @@ slp_schedule_task_prepared(PyThreadState *ts, PyObject **result, PyTaskletObject ++ts->st.nesting_level; if (ts->exc_type != NULL || ts->use_tracing || ts->tracing) - transfer = transfer_with_exc; + transfer = slp_transfer_with_exc; else transfer = slp_transfer; From c366b482b4bb0cfdbb1ba4f4dec44b6a4d609ada Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Sat, 20 Jul 2013 11:53:49 +0000 Subject: [PATCH 14/35] Make the running of the stub from main explicit about setting the 'main' tealet to return to. This also removes then need to tweak it in the test_outside function. --- Stackless/core/stackless_tealet.c | 13 +++++++++++-- Stackless/module/stacklessmodule.c | 8 +------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/Stackless/core/stackless_tealet.c b/Stackless/core/stackless_tealet.c index fdf1d39b3fe5ce..013791df7156be 100644 --- a/Stackless/core/stackless_tealet.c +++ b/Stackless/core/stackless_tealet.c @@ -155,9 +155,18 @@ slp_run_initial_stub(PyThreadState *ts, tealet_run_t func, void **arg) PyObject * slp_run_stub_from_main(PyThreadState *ts) { + int result; void *arg; - /* switch into a stub duplicate. Run evaluation loop. Then switch back */ - int result = slp_run_initial_stub(ts, &tasklet_stub_func, &arg); + tealet_t *old_main; + + /* switch into a stub duplicate. Run evaluation loop. Then switch back. + * Set the "main" to be us, so that a switch out of the tasklet_stub_func + * lands us here + */ + old_main = ts->st.tealet_main; + ts->st.tealet_main = tealet_current(old_main); + result = slp_run_initial_stub(ts, &tasklet_stub_func, &arg); + ts->st.tealet_main = old_main; if (result) return NULL; return (PyObject*)arg; diff --git a/Stackless/module/stacklessmodule.c b/Stackless/module/stacklessmodule.c index 67d18ed46db08a..1d08d65de7dc41 100644 --- a/Stackless/module/stacklessmodule.c +++ b/Stackless/module/stacklessmodule.c @@ -596,8 +596,7 @@ test_outside(PyObject *self) int nesting_level = ts->st.nesting_level; PyObject *ret = Py_None; int jump = ts->st.serial_last_jump; - tealet_t *old_main = ts->st.tealet_main; - + Py_INCREF(ret); ts->st.main = NULL; ts->st.initial_stub = NULL; @@ -606,10 +605,6 @@ test_outside(PyObject *self) ts->recursion_depth = 0; stcurrent = ts->st.current; slp_current_remove(); - /* must reset the "main" tealet to be this one, so that a final - * switch back lands us here, rather than in the "real" main tealet. - */ - ts->st.tealet_main = tealet_current(old_main); while (ts->st.runcount > 0) { Py_DECREF(ret); ret = PyStackless_Schedule(Py_None, 0); @@ -621,7 +616,6 @@ test_outside(PyObject *self) // TODO: destroy initial stub //Py_XDECREF(ts->st.initial_stub); ts->st.initial_stub = cst; - ts->st.tealet_main = old_main; ts->frame = f; slp_current_insert(stcurrent); ts->recursion_depth = recursion_depth; From cba21c7c9042f7515f007f45c26e09e0ed5b4697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Sun, 21 Jul 2013 11:38:58 +0000 Subject: [PATCH 15/35] Fix handling of extra_data (and id) when duplicating tealets --- Stackless/tealet/tealet.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Stackless/tealet/tealet.c b/Stackless/tealet/tealet.c index c916e78d15bf5f..da07c04a5444fe 100644 --- a/Stackless/tealet/tealet.c +++ b/Stackless/tealet/tealet.c @@ -703,6 +703,7 @@ tealet_t *tealet_duplicate(tealet_t *tealet, size_t extrasize) tealet_sub_t *g_tealet = (tealet_sub_t *)tealet; tealet_main_t *g_main = TEALET_GET_MAIN(g_tealet); tealet_sub_t *g_copy; + /* can't dup the current or the main tealet */ assert(g_tealet != g_main->g_current && g_tealet != (tealet_sub_t*)g_main); g_copy = tealet_alloc(g_main, NULL, extrasize); if (g_copy == NULL) @@ -710,8 +711,11 @@ tealet_t *tealet_duplicate(tealet_t *tealet, size_t extrasize) #ifndef NDEBUG g_main->g_tealets++; #endif - *g_copy = *g_tealet; - g_copy->stack = tealet_stack_dup(g_copy->stack); + /* copy the relevant bits. extra data is not copied since we don't + * know how large it was in the source + */ + g_copy->stack_far = g_tealet->stack_far; + g_copy->stack = tealet_stack_dup(g_tealet->stack); return (tealet_t*)g_copy; } From d176dca4da9e246fdbd40aebfd98df8d7114b7cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Sun, 21 Jul 2013 12:24:17 +0000 Subject: [PATCH 16/35] Link susptended tasklet tealets in a chain. re-implement stackless.uncollectable. --- Stackless/core/stackless_tealet.c | 51 ++++++++++++++++++++++++----- Stackless/core/stackless_tealet.h | 12 ++++++- Stackless/module/stacklessmodule.c | 24 +++++++------- Stackless/unittests/test_miscell.py | 38 +++++++++++++++++++++ 4 files changed, 104 insertions(+), 21 deletions(-) diff --git a/Stackless/core/stackless_tealet.c b/Stackless/core/stackless_tealet.c index 013791df7156be..5839691f76dd58 100644 --- a/Stackless/core/stackless_tealet.c +++ b/Stackless/core/stackless_tealet.c @@ -5,6 +5,35 @@ #include "frameobject.h" #include "stackless_impl.h" +PyTealet_data *slp_tealet_list = NULL; + +static void +slp_tealet_link(tealet_t *t, PyTaskletObject *tasklet) +{ + PyTealet_data *td = TEALET_EXTRA(t, PyTealet_data); + if (slp_tealet_list) { + td->next = slp_tealet_list; + td->prev = td->next->prev; + td->next->prev = td; + td->prev->next = td; + } else + td->next = td->prev = td; + slp_tealet_list = td; + td->tasklet = tasklet; +} + +static void +slp_tealet_unlink(tealet_t *t) +{ + PyTealet_data *td = TEALET_EXTRA(t, PyTealet_data); + if (td->next != td) { + slp_tealet_list = td->next; + td->next->prev = td->prev; + td->prev->next = td->next; + } else + slp_tealet_list = NULL; +} + /**************************************************************** *Implement copyable tealet stubs by using a trampoline */ @@ -31,9 +60,9 @@ static tealet_t *slp_stub_main(tealet_t *current, void *arg) } /* create a stub and return it */ -tealet_t *slp_stub_new(tealet_t *t) { +tealet_t *slp_stub_new(tealet_t *t, size_t extrasize) { void *arg = (void*)tealet_current(t); - return tealet_new(t, slp_stub_main, &arg, 0); + return tealet_new(t, slp_stub_main, &arg, extrasize); } /* run a stub */ @@ -87,12 +116,12 @@ slp_make_initial_stub(PyThreadState *ts) (tealet_malloc_t)&PyMem_Malloc, (tealet_free_t)&PyMem_Free, 0}; - ts->st.tealet_main = tealet_initialize(&ta, 0); + ts->st.tealet_main = tealet_initialize(&ta, sizeof(PyTealet_data)); if (!ts->st.tealet_main) goto err; } - ts->st.initial_stub = slp_stub_new(ts->st.tealet_main); + ts->st.initial_stub = slp_stub_new(ts->st.tealet_main, sizeof(PyTealet_data)); if (!ts->st.initial_stub) goto err; return 0; @@ -138,7 +167,7 @@ slp_run_initial_stub(PyThreadState *ts, tealet_run_t func, void **arg) { tealet_t *stub; int result; - stub = tealet_duplicate(ts->st.initial_stub, 0); + stub = tealet_duplicate(ts->st.initial_stub, sizeof(PyTealet_data)); if (!stub) { PyErr_NoMemory(); return -1; @@ -188,12 +217,16 @@ slp_transfer(PyThreadState *ts, tealet_t *cst, PyTaskletObject *prev) { int result; int nesting_level; + tealet_t *current, *current2; + assert(prev->cstate == NULL); - nesting_level = prev->tstate->st.nesting_level; prev->nesting_level = nesting_level; - /* mark the old tasklet as having cstate */ - prev->cstate = tealet_current(ts->st.tealet_main); + + /* mark the old tasklet as having cstate, and link it in */ + current = tealet_current(ts->st.tealet_main); + prev->cstate = current; + slp_tealet_link(current, prev); if (cst) { /* make sure we are not trying to jump between threads */ @@ -205,6 +238,8 @@ slp_transfer(PyThreadState *ts, tealet_t *cst, PyTaskletObject *prev) } /* we are back, or failed, no cstate in tasklet */ + current2 = tealet_current(ts->st.tealet_main); + slp_tealet_unlink(current); prev->cstate = NULL; prev->tstate->st.nesting_level = nesting_level; if (!result) { diff --git a/Stackless/core/stackless_tealet.h b/Stackless/core/stackless_tealet.h index 4dfa2d8f59d1a5..b3ef0b8d429391 100644 --- a/Stackless/core/stackless_tealet.h +++ b/Stackless/core/stackless_tealet.h @@ -5,6 +5,17 @@ #include #include +/* a list of tealets. Currently these are suspended tealets with their + * suspended tasklets indicated. + */ +typedef struct PyTealet_data { + struct PyTealet_data *prev; + struct PyTealet_data *next; + struct PyTaskletObject *tasklet; +} PyTealet_data; + +PyTealet_data *slp_tealet_list; /* head of the tealet list */ + int slp_make_initial_stub(PyThreadState *ts); void slp_destroy_initial_stub(PyThreadState *ts); int slp_run_initial_stub(PyThreadState *ts, tealet_run_t func, void **arg); @@ -16,5 +27,4 @@ int slp_transfer(PyThreadState *ts, tealet_t *cst, struct PyTaskletObject *prev) int slp_transfer_with_exc(PyThreadState *ts, tealet_t *cst, struct PyTaskletObject *prev); int slp_transfer_return(tealet_t *cst); - #endif \ No newline at end of file diff --git a/Stackless/module/stacklessmodule.c b/Stackless/module/stacklessmodule.c index 1d08d65de7dc41..372290629f1062 100644 --- a/Stackless/module/stacklessmodule.c +++ b/Stackless/module/stacklessmodule.c @@ -1155,21 +1155,21 @@ static PyObject * slpmodule_getuncollectables(PySlpModuleObject *mod, void *context) { PyObject *lis = PyList_New(0); -#if 0 /* todo: finish this */ - PyCStackObject *cst = slp_cstack_chain; - + PyTealet_data *data = slp_tealet_list; + if (lis == NULL) return NULL; - do { - if (cst->task != NULL) { - if (PyList_Append(lis, (PyObject *) cst->task)) { - Py_DECREF(lis); - return NULL; + if (data) { + do { + if (data->tasklet) { + if (PyList_Append(lis, (PyObject*)data->tasklet)) { + Py_DECREF(lis); + return NULL; + } } - } - cst = cst->next; - } while (cst != slp_cstack_chain); -#endif + data = data->next; + } while (data != slp_tealet_list); + } return lis; } diff --git a/Stackless/unittests/test_miscell.py b/Stackless/unittests/test_miscell.py index 6603a6a0f047b6..813b66c08c389c 100644 --- a/Stackless/unittests/test_miscell.py +++ b/Stackless/unittests/test_miscell.py @@ -475,6 +475,44 @@ def test_getcurrent(self): s.run() self.assertTrue(self.handled_tasklet is s) +class TestUncollectables(StacklessTestCase): + def _testNone(self): + self.assertEqual(stackless.uncollectables, []) + + def testOne(self): + before = set(stackless.uncollectables) + c = stackless.channel() + def func1(): + stackless.test_cstate(func2) # force hard switch + def func2(): + c.receive() + t = stackless.tasklet(func1)() + stackless.run() + after = set(stackless.uncollectables) + self.assertEqual(after-before, set([t])) + c.send(None) + stackless.run() + after = set(stackless.uncollectables) + self.assertEqual(after, before) + + def testTwo(self): + before = set(stackless.uncollectables) + c = stackless.channel() + def func1(): + stackless.test_cstate(func2) # force hard switch + def func2(): + c.receive() + t1 = stackless.tasklet(func1)() + t2 = stackless.tasklet(func1)() + stackless.run() + after = set(stackless.uncollectables) + self.assertEqual(after - before, set([t1, t2])) + c.send(None) + c.send(None) + stackless.run() + after = set(stackless.uncollectables) + self.assertEqual(after, before) + #/////////////////////////////////////////////////////////////////////////////// From 3ae9c9fb5d0e6d2cb37eb214b2fee7d574335c4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Sun, 21 Jul 2013 12:35:53 +0000 Subject: [PATCH 17/35] fix slp_kill_tasks_with_stacks() --- Stackless/core/stacklesseval.c | 45 ++++++++++++++++------------------ 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/Stackless/core/stacklesseval.c b/Stackless/core/stacklesseval.c index b0df890b05338f..d818a5e1c4af5a 100644 --- a/Stackless/core/stacklesseval.c +++ b/Stackless/core/stacklesseval.c @@ -91,38 +91,34 @@ void slp_kill_tasks_with_stacks(PyThreadState *target_ts) PyThreadState *ts = PyThreadState_GET(); -#if 0 /* todo, change wrt tasklet_chain int count = 0; /* a loop to kill tasklets on the local thread */ while (1) { - PyCStackObject *csfirst = slp_cstack_chain, *cs; + PyTealet_data *tdfirst = slp_tealet_list, *td; PyTaskletObject *t, *task; PyTaskletObject **chain; - if (csfirst == NULL) + if (tdfirst == NULL) break; - for (cs = csfirst; ; cs = cs->next) { - if (count && cs == csfirst) { + for (td = tdfirst; ; td = td->next) { + if (count && td == tdfirst) { /* nothing found */ return; } ++count; - if (cs->task == NULL) + if (td->tasklet == NULL) continue; /* can't kill tasklets from other threads here */ - if (cs->tstate != ts) - continue; - /* were we asked to kill tasklet on our thread? */ - if (target_ts != NULL && cs->tstate != target_ts) + if (td->tasklet->tstate != ts) continue; /* Not killable, another thread's frameless main? */ - if (slp_get_frame(cs->task) == NULL) + if (slp_get_frame(td->tasklet) == NULL) continue; break; } count = 0; - t = cs->task; + t = td->tasklet; Py_INCREF(t); /* cs->task is a borrowed ref */ /* We need to ensure that the tasklet 't' is in the scheduler @@ -135,7 +131,7 @@ void slp_kill_tasks_with_stacks(PyThreadState *target_ts) * killed, they will be implicitly placed before this one, * leaving it to run next. */ - if (!t->flags.blocked && t != cs->tstate->st.current) { + if (!t->flags.blocked && t != t->tstate->st.current) { PyTaskletObject *tmp; /* unlink from runnable queue if it wasn't previously remove()'d */ if (t->next && t->prev) { @@ -145,7 +141,7 @@ void slp_kill_tasks_with_stacks(PyThreadState *target_ts) } else Py_INCREF(t); /* a new reference for the runnable queue */ /* insert into the 'current' chain without modifying 'current' */ - tmp = cs->tstate->st.current; + tmp = t->tstate->st.current; chain = &tmp; task = t; SLP_CHAIN_INSERT(PyTaskletObject, chain, task, next, prev); @@ -154,10 +150,6 @@ void slp_kill_tasks_with_stacks(PyThreadState *target_ts) PyTasklet_Kill(t); PyErr_Clear(); - if (t->cstate != NULL) { - /* ensure a valid tstate */ - t->cstate->tstate = slp_initial_tstate; - } Py_DECREF(t); } @@ -167,25 +159,30 @@ void slp_kill_tasks_with_stacks(PyThreadState *target_ts) * exit this function */ { - PyCStackObject *csfirst = slp_cstack_chain, *cs; + PyTealet_data *tdfirst = slp_tealet_list, *td; PyTaskletObject *t; - if (csfirst == NULL) + if (tdfirst == NULL) return; count = 0; - for (cs = csfirst; ; cs = cs->next) { - if (count && cs == csfirst) { + for (td = tdfirst; ; td = td->next) { + if (count && td == tdfirst) { return; } + if (td->tasklet == NULL) + continue; + t = td->tasklet; + if (t->tstate == ts) + continue; /* ignore this thread's tasklets */ + if (target_ts && t->tstate != target_ts) + continue; /* want a specific thread */ count++; - t = cs->task; Py_INCREF(t); /* cs->task is a borrowed ref */ PyTasklet_Kill(t); PyErr_Clear(); Py_DECREF(t); } } -#endif } void PyStackless_kill_tasks_with_stacks(int allthreads) From 1add5d3b10e5e347695dea827f637f91a6327ab4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Sun, 21 Jul 2013 18:08:00 +0000 Subject: [PATCH 18/35] clean up unused stuff. --- Stackless/core/stackless_impl.h | 9 --------- Stackless/core/stackless_structs.h | 31 ------------------------------ Stackless/core/stackless_tealet.c | 3 +-- Stackless/core/stackless_tstate.h | 22 +++------------------ Stackless/core/stacklesseval.c | 2 -- Stackless/module/stacklessmodule.c | 4 +--- 6 files changed, 5 insertions(+), 66 deletions(-) diff --git a/Stackless/core/stackless_impl.h b/Stackless/core/stackless_impl.h index c2d0ff77b2893e..ceac00f7e56c6b 100644 --- a/Stackless/core/stackless_impl.h +++ b/Stackless/core/stackless_impl.h @@ -38,15 +38,6 @@ PyAPI_DATA(int) slp_in_psyco; PyAPI_DATA(int) slp_try_stackless; PyAPI_DATA(PyTaskletObject *) slp_tasklet_chain; -#if 0 -PyAPI_FUNC(PyCStackObject *) slp_cstack_new(PyCStackObject **cst, - intptr_t *stackref, - PyTaskletObject *task); -PyAPI_FUNC(size_t) slp_cstack_save(PyCStackObject *cstprev); -PyAPI_FUNC(void) slp_cstack_restore(PyCStackObject *cst); - -#endif - PyAPI_FUNC(int) _PyStackless_InitTypes(void); PyAPI_FUNC(void) _PyStackless_Init(void); diff --git a/Stackless/core/stackless_structs.h b/Stackless/core/stackless_structs.h index 98ebc88d242353..8bf5325a9e48d4 100644 --- a/Stackless/core/stackless_structs.h +++ b/Stackless/core/stackless_structs.h @@ -5,14 +5,8 @@ extern "C" { #endif -/* platform specific constants (mainly SEH stuff to store )*/ -/* TODO: remove vestiges of old switching */ -#include "platf/slp_platformselect.h" - #include "stackless_tealet.h" - - /*** important structures: tasklet ***/ @@ -95,28 +89,6 @@ typedef struct PyTaskletObject { PyObject *tsk_weakreflist; } PyTaskletObject; - -/*** important structures: cstack ***/ - -typedef struct _cstack { - PyObject_VAR_HEAD - struct _cstack *next; - struct _cstack *prev; -#ifdef have_long_long - long_long serial; -#else - long serial; -#endif - struct PyTaskletObject *task; - int nesting_level; - PyThreadState *tstate; -#ifdef _SEH32 - DWORD exception_list; /* SEH handler on Win32 */ -#endif - tealet_t *tealet; -} PyCStackObject; - - /*** important structures: bomb ***/ typedef struct _bomb { @@ -208,9 +180,6 @@ typedef struct _slpmodule { PyAPI_DATA(PyTypeObject) PyCFrame_Type; #define PyCFrame_Check(op) ((op)->ob_type == &PyCFrame_Type) -PyAPI_DATA(PyTypeObject) PyCStack_Type; -#define PyCStack_Check(op) ((op)->ob_type == &PyCStack_Type) - PyAPI_DATA(PyTypeObject) PyBomb_Type; #define PyBomb_Check(op) ((op)->ob_type == &PyBomb_Type) diff --git a/Stackless/core/stackless_tealet.c b/Stackless/core/stackless_tealet.c index 5839691f76dd58..555225a345633f 100644 --- a/Stackless/core/stackless_tealet.c +++ b/Stackless/core/stackless_tealet.c @@ -217,7 +217,7 @@ slp_transfer(PyThreadState *ts, tealet_t *cst, PyTaskletObject *prev) { int result; int nesting_level; - tealet_t *current, *current2; + tealet_t *current; assert(prev->cstate == NULL); nesting_level = prev->tstate->st.nesting_level; @@ -238,7 +238,6 @@ slp_transfer(PyThreadState *ts, tealet_t *cst, PyTaskletObject *prev) } /* we are back, or failed, no cstate in tasklet */ - current2 = tealet_current(ts->st.tealet_main); slp_tealet_unlink(current); prev->cstate = NULL; prev->tstate->st.nesting_level = nesting_level; diff --git a/Stackless/core/stackless_tstate.h b/Stackless/core/stackless_tstate.h index 1d81312b4d263d..cf833a738d1e5c 100644 --- a/Stackless/core/stackless_tstate.h +++ b/Stackless/core/stackless_tstate.h @@ -1,22 +1,8 @@ /*** addition to tstate ***/ typedef struct _sts { - /* the blueprint for new stacks */ - struct tealet_t *initial_stub; - /* "serial" is incremented each time we create a new stub. - * (enter "stackless" from the outside) - * and "serial_last_jump" indicates to which stub the current - * stack belongs. This is used to find out if a stack switch - * is required when the main tasklet exits - */ -#ifdef have_long_long - long_long serial; - long_long serial_last_jump; -#else - long serial; - long serial_last_jump; -#endif - struct tealet_t *tealet_main; + struct tealet_t *initial_stub; /* stub for creating new tealets */ + struct tealet_t *tealet_main; /* the current "main" tealet */ /* main tasklet */ struct PyTaskletObject *main; /* runnable tasklets */ @@ -49,9 +35,7 @@ typedef struct _sts { /* these macros go into pystate.c */ #define __STACKLESS_PYSTATE_NEW \ tstate->st.initial_stub = NULL; \ - tstate->st.serial = 0; \ - tstate->st.serial_last_jump = 0; \ - tstate->st.tealet_main = 0; \ + tstate->st.tealet_main = NULL; \ tstate->st.ticker = 0; \ tstate->st.interval = 0; \ tstate->st.interrupt = NULL; \ diff --git a/Stackless/core/stacklesseval.c b/Stackless/core/stacklesseval.c index d818a5e1c4af5a..2dadfee75b6f0c 100644 --- a/Stackless/core/stacklesseval.c +++ b/Stackless/core/stacklesseval.c @@ -89,8 +89,6 @@ slp_eval_frame(PyFrameObject *f) void slp_kill_tasks_with_stacks(PyThreadState *target_ts) { PyThreadState *ts = PyThreadState_GET(); - - int count = 0; /* a loop to kill tasklets on the local thread */ diff --git a/Stackless/module/stacklessmodule.c b/Stackless/module/stacklessmodule.c index 372290629f1062..78a6d81c7a3d08 100644 --- a/Stackless/module/stacklessmodule.c +++ b/Stackless/module/stacklessmodule.c @@ -595,8 +595,7 @@ test_outside(PyObject *self) int recursion_depth = ts->recursion_depth; int nesting_level = ts->st.nesting_level; PyObject *ret = Py_None; - int jump = ts->st.serial_last_jump; - + Py_INCREF(ret); ts->st.main = NULL; ts->st.initial_stub = NULL; @@ -620,7 +619,6 @@ test_outside(PyObject *self) slp_current_insert(stcurrent); ts->recursion_depth = recursion_depth; ts->st.nesting_level = nesting_level; - ts->st.serial_last_jump = jump; return ret; } From ffcb696ba0f3837aef8e33ae37c3eb2fd69ae6b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Sun, 21 Jul 2013 19:12:09 +0000 Subject: [PATCH 19/35] Remove the platf/ files from the solution, and from being included. The platf/ files are still present in the repo. --- Modules/cPickle.c | 1 - PCbuild/pythoncore.vcproj | 124 ----------------------------- Python/ceval.c | 1 - Stackless/core/stacklesseval.c | 3 - Stackless/module/stacklessmodule.c | 4 +- Stackless/pickling/prickelpit.c | 3 - Stackless/pickling/safe_pickle.c | 1 - 7 files changed, 3 insertions(+), 134 deletions(-) diff --git a/Modules/cPickle.c b/Modules/cPickle.c index 3f35b8f9d8b0c3..dcfcf0a658a1a3 100644 --- a/Modules/cPickle.c +++ b/Modules/cPickle.c @@ -2,7 +2,6 @@ #include "cStringIO.h" #include "structmember.h" #include "core/stackless_impl.h" -#include "platf/slp_platformselect.h" PyDoc_STRVAR(cPickle_module_documentation, "C implementation and optimization of the Python pickle module."); diff --git a/PCbuild/pythoncore.vcproj b/PCbuild/pythoncore.vcproj index ecc68abaa6a73a..0afc880e286c36 100644 --- a/PCbuild/pythoncore.vcproj +++ b/PCbuild/pythoncore.vcproj @@ -1997,130 +1997,6 @@ > - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Python/ceval.c b/Python/ceval.c index 902bbd0eae28b6..fa303209ae30d6 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -17,7 +17,6 @@ #include "opcode.h" #include "structmember.h" #include "core/stackless_impl.h" -#include "platf/slp_platformselect.h" /* for stack saving */ #include diff --git a/Stackless/core/stacklesseval.c b/Stackless/core/stacklesseval.c index 2dadfee75b6f0c..3704051079b190 100644 --- a/Stackless/core/stacklesseval.c +++ b/Stackless/core/stacklesseval.c @@ -8,9 +8,6 @@ #include "stackless_impl.h" #include "pickling/prickelpit.h" -/* platform specific constants */ -#include "platf/slp_platformselect.h" - #include "stackless_tealet.h" /* Stackless extension for ceval.c */ diff --git a/Stackless/module/stacklessmodule.c b/Stackless/module/stacklessmodule.c index 78a6d81c7a3d08..8db17093027f51 100644 --- a/Stackless/module/stacklessmodule.c +++ b/Stackless/module/stacklessmodule.c @@ -6,7 +6,6 @@ #include "core/stackless_impl.h" #define IMPLEMENT_STACKLESSMODULE -#include "platf/slp_platformselect.h" #include "core/cframeobject.h" #include "taskletobject.h" #include "channelobject.h" @@ -543,6 +542,9 @@ This can be used to measure the execution time of 1.000.000 switches."); #define STACK_MAX_USEFUL 64000 #define STACK_MAX_USESTR "64000" +#ifdef MS_WINDOWS +#define alloca _alloca +#endif static PyObject * diff --git a/Stackless/pickling/prickelpit.c b/Stackless/pickling/prickelpit.c index a2d154efe11b66..56959996a938a5 100644 --- a/Stackless/pickling/prickelpit.c +++ b/Stackless/pickling/prickelpit.c @@ -7,9 +7,6 @@ #include "pickling/prickelpit.h" #include "module/channelobject.h" -/* platform specific constants */ -#include "platf/slp_platformselect.h" - /****************************************************** type template and support for pickle helper types diff --git a/Stackless/pickling/safe_pickle.c b/Stackless/pickling/safe_pickle.c index 209caf069cd430..10d0c7e843dc14 100644 --- a/Stackless/pickling/safe_pickle.c +++ b/Stackless/pickling/safe_pickle.c @@ -4,7 +4,6 @@ #include "compile.h" #include "core/stackless_impl.h" -#include "platf/slp_platformselect.h" /* safe pickling */ From 29394d7eade2eec0860afdcb80401408563e58c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Sun, 21 Jul 2013 19:43:02 +0000 Subject: [PATCH 20/35] Clean up tealet state when python thread state is cleared. --- Stackless/core/stackless_tealet.c | 13 +++++++++++++ Stackless/core/stackless_tealet.h | 1 + Stackless/core/stackless_tstate.h | 6 +++--- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/Stackless/core/stackless_tealet.c b/Stackless/core/stackless_tealet.c index 555225a345633f..9d3752828f9cf2 100644 --- a/Stackless/core/stackless_tealet.c +++ b/Stackless/core/stackless_tealet.c @@ -130,6 +130,19 @@ slp_make_initial_stub(PyThreadState *ts) return -1; } +/* clean up tealet state for this thread */ +void slp_tealet_cleanup(PyThreadState *ts) +{ + if (ts->st.initial_stub) + tealet_delete(ts->st.initial_stub); + ts->st.initial_stub = NULL; + if (ts->st.tealet_main) { + assert(TEALET_IS_MAIN(ts->st.tealet_main)); + tealet_finalize(ts->st.tealet_main); + } + ts->st.initial_stub = NULL; +} + void slp_destroy_initial_stub(PyThreadState *ts) { diff --git a/Stackless/core/stackless_tealet.h b/Stackless/core/stackless_tealet.h index b3ef0b8d429391..c19bc0c450116e 100644 --- a/Stackless/core/stackless_tealet.h +++ b/Stackless/core/stackless_tealet.h @@ -21,6 +21,7 @@ void slp_destroy_initial_stub(PyThreadState *ts); int slp_run_initial_stub(PyThreadState *ts, tealet_run_t func, void **arg); PyObject * slp_run_stub_from_main(PyThreadState *ts); int slp_run_stub_from_worker(PyThreadState *ts); +void slp_tealet_cleanup(PyThreadState *ts); /* hard switching of tasklets */ int slp_transfer(PyThreadState *ts, tealet_t *cst, struct PyTaskletObject *prev); diff --git a/Stackless/core/stackless_tstate.h b/Stackless/core/stackless_tstate.h index cf833a738d1e5c..d3aeff2ac3a606 100644 --- a/Stackless/core/stackless_tstate.h +++ b/Stackless/core/stackless_tstate.h @@ -57,14 +57,14 @@ typedef struct _sts { struct _ts; /* Forward */ -/* TODO, make this also call tealet_finalize */ void slp_kill_tasks_with_stacks(struct _ts *tstate); +void slp_tealet_cleanup(struct _ts *tstate); #define __STACKLESS_PYSTATE_CLEAR \ slp_kill_tasks_with_stacks(tstate); \ - Py_CLEAR(tstate->st.initial_stub); \ Py_CLEAR(tstate->st.del_post_switch); \ - Py_CLEAR(tstate->st.interrupted); + Py_CLEAR(tstate->st.interrupted); \ + slp_tealet_cleanup(tstate); #ifdef WITH_THREAD From 781bb423e3449d1210488e0607ec45000d28af14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Sun, 21 Jul 2013 19:45:19 +0000 Subject: [PATCH 21/35] Clean up initial stub for test_outside() --- Stackless/core/stackless_tealet.c | 3 ++- Stackless/module/stacklessmodule.c | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Stackless/core/stackless_tealet.c b/Stackless/core/stackless_tealet.c index 9d3752828f9cf2..0b0047246a5b76 100644 --- a/Stackless/core/stackless_tealet.c +++ b/Stackless/core/stackless_tealet.c @@ -146,7 +146,8 @@ void slp_tealet_cleanup(PyThreadState *ts) void slp_destroy_initial_stub(PyThreadState *ts) { - tealet_delete(ts->st.initial_stub); + if (ts->st.initial_stub) + tealet_delete(ts->st.initial_stub); ts->st.initial_stub = NULL; } diff --git a/Stackless/module/stacklessmodule.c b/Stackless/module/stacklessmodule.c index 8db17093027f51..3f97b2cd1a2331 100644 --- a/Stackless/module/stacklessmodule.c +++ b/Stackless/module/stacklessmodule.c @@ -614,8 +614,7 @@ test_outside(PyObject *self) } } ts->st.main = stmain; - // TODO: destroy initial stub - //Py_XDECREF(ts->st.initial_stub); + slp_destroy_initial_stub(ts); ts->st.initial_stub = cst; ts->frame = f; slp_current_insert(stcurrent); From 440a93d0e8b9e4dc42def7cfea829a1400f60c62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Mon, 22 Jul 2013 10:30:57 +0000 Subject: [PATCH 22/35] Fix 'runcount' during tasklet kill and cleanup. Fix cleanup to work properly. --- Stackless/core/stacklesseval.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/Stackless/core/stacklesseval.c b/Stackless/core/stacklesseval.c index 3704051079b190..8234bf336fe5ec 100644 --- a/Stackless/core/stacklesseval.c +++ b/Stackless/core/stacklesseval.c @@ -133,6 +133,7 @@ void slp_kill_tasks_with_stacks(PyThreadState *target_ts) task = t; chain = &task; SLP_CHAIN_REMOVE(PyTaskletObject, chain, task, next, prev); + ts->st.runcount--; } else Py_INCREF(t); /* a new reference for the runnable queue */ /* insert into the 'current' chain without modifying 'current' */ @@ -140,6 +141,7 @@ void slp_kill_tasks_with_stacks(PyThreadState *target_ts) chain = &tmp; task = t; SLP_CHAIN_INSERT(PyTaskletObject, chain, task, next, prev); + ts->st.runcount++; } PyTasklet_Kill(t); @@ -183,19 +185,28 @@ void slp_kill_tasks_with_stacks(PyThreadState *target_ts) void PyStackless_kill_tasks_with_stacks(int allthreads) { PyThreadState *ts = PyThreadState_Get(); + int init = 0; + if (slp_tealet_list == NULL) + return; if (ts->st.main == NULL) { - /* TODO: Must destroy main and current afterwards, if required. - * also, only do this is there is any work to be done. - */ if (initialize_main_and_current()) { PyObject *s = PyString_FromString("tasklet cleanup"); PyErr_WriteUnraisable(s); Py_XDECREF(s); return; } + init = 1; } slp_kill_tasks_with_stacks(allthreads ? NULL : ts); + + if (init) { + PyTaskletObject *m = ts->st.main, *c; + ts->st.main = NULL; + c = slp_current_remove(); + Py_XDECREF(m); + Py_XDECREF(c); + } } From 04cf3822e3fe45b794b567438a0661879606b82f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Mon, 29 Jul 2013 10:19:50 +0000 Subject: [PATCH 23/35] Don't recreate a 'stub' every time stackless is entered, just keep the original one. --- Stackless/core/stackless_tealet.c | 4 +--- Stackless/core/stacklesseval.c | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/Stackless/core/stackless_tealet.c b/Stackless/core/stackless_tealet.c index 0b0047246a5b76..edd45d35751ef1 100644 --- a/Stackless/core/stackless_tealet.c +++ b/Stackless/core/stackless_tealet.c @@ -133,9 +133,7 @@ slp_make_initial_stub(PyThreadState *ts) /* clean up tealet state for this thread */ void slp_tealet_cleanup(PyThreadState *ts) { - if (ts->st.initial_stub) - tealet_delete(ts->st.initial_stub); - ts->st.initial_stub = NULL; + slp_destroy_initial_stub(ts); if (ts->st.tealet_main) { assert(TEALET_IS_MAIN(ts->st.tealet_main)); tealet_finalize(ts->st.tealet_main); diff --git a/Stackless/core/stacklesseval.c b/Stackless/core/stacklesseval.c index 8234bf336fe5ec..a4cd13988c3b02 100644 --- a/Stackless/core/stacklesseval.c +++ b/Stackless/core/stacklesseval.c @@ -72,10 +72,20 @@ slp_eval_frame(PyFrameObject *f) } ts->frame = f; result = slp_run_stub_from_main(ts); - /* TODO: We could leave the stub around in the tstate and save - * ourselves the effort of recreating it all the time - */ +#if 0 + /* clean up initial stub and recreate it every time */ slp_destroy_initial_stub(ts); +#else + /* experimental. + * no need to have new stubs, because of how tealets + * work. This stub continues to be valid no matter from + * what level we enter. + * The only way this might cause problems is if the initial + * entry is unusually deep on the stack. We could modify this by + * testing the stack position. + */ + /* TODO: recreate the stub if we are in a "shallower" position */ +#endif return result; } From 4abd89c046b36d5615ae5b4dfc67b5d55896f828 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Mon, 29 Jul 2013 12:05:09 +0000 Subject: [PATCH 24/35] Add stack arithmetic support to tealets, to measure stack pos. --- Stackless/tealet/tealet.c | 24 ++++++++++++++++++++++++ Stackless/tealet/tealet.h | 26 ++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/Stackless/tealet/tealet.c b/Stackless/tealet/tealet.c index da07c04a5444fe..9744fa65639d47 100644 --- a/Stackless/tealet/tealet.c +++ b/Stackless/tealet/tealet.c @@ -759,3 +759,27 @@ int tealet_get_count(tealet_t *tealet) return TEALET_GET_MAIN(tealet)->g_tealets; } #endif + +ptrdiff_t tealet_stack_diff(void *a, void *b) +{ + return STACK_SUB((ptrdiff_t)a, (ptrdiff_t)(b)); +} + +void *tealet_get_far(tealet_t *_tealet) +{ + tealet_sub_t *tealet = (tealet_sub_t *)_tealet; + return tealet->stack_far; +} + +void *tealet_new_far(tealet_t *d1, tealet_run_t d2, void **d3, size_t d4) +{ + tealet_sub_t *result; + void *r; + (void)d1; + (void)d2; + (void)d3; + (void)d4; + /* avoid compiler warnings about returning tmp addr */ + r = (void*)&result; + return r; +} diff --git a/Stackless/tealet/tealet.h b/Stackless/tealet/tealet.h index 968c47bbc2d58e..beeebcfec3d411 100644 --- a/Stackless/tealet/tealet.h +++ b/Stackless/tealet/tealet.h @@ -175,6 +175,32 @@ tealet_t *tealet_current(tealet_t *tealet); TEALET_API void **tealet_main_userpointer(tealet_t *tealet); +/* functions for stack arithmetic. Useful when deciding + * if you want to start a newa tealet, or when doing stack + * spilling + */ + +/* subtract two stack positions, taking into account if the + * local stack grows up or down in memory. + * The result is positive if 'b' is 'deeper' on the stack + * than 'a' + */ +TEALET_API +ptrdiff_t tealet_stack_diff(void *a, void *b); + +/* Get a tealet's "far" position on the stack. This is an + * indicator of its creation position on the stack. The main + * tealet extends until the beginning of stack + */ +TEALET_API +void *tealet_get_far(tealet_t *_tealet); + +/* this is used to get the "far address _if_ a tealet were initialized here + * The arguments must match the real tealet_new() but are dummies. + */ +TEALET_API +void *tealet_new_far(tealet_t *dummy1, tealet_run_t dummy2, void **dummy3, size_t dummy4); + /* get a tealet's status */ #define TEALET_STATUS_ACTIVE 0 #define TEALET_STATUS_EXITED 1 From 3098540f15832d32d7280b08238acf3306b1a188 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Mon, 29 Jul 2013 12:05:53 +0000 Subject: [PATCH 25/35] Re-implement stack spilling for pickling. We now just call a callback for the initial stub. There is no need to mess with cframes and such stuff. --- Modules/cPickle.c | 5 +- Stackless/core/stackless_tealet.c | 30 +++++- Stackless/core/stackless_tealet.h | 3 + Stackless/pickling/safe_pickle.c | 165 +++++++---------------------- Stackless/unittests/test_pickle.py | 1 - 5 files changed, 73 insertions(+), 131 deletions(-) diff --git a/Modules/cPickle.c b/Modules/cPickle.c index dcfcf0a658a1a3..ae093b9ac003cb 100644 --- a/Modules/cPickle.c +++ b/Modules/cPickle.c @@ -2537,10 +2537,9 @@ save(Picklerobject *self, PyObject *args, int pers_save) int res = -1; int tmp; - /* TODO: re-enable stack spilling */ -#if 0 && defined STACKLESS +#if defined STACKLESS /* but we save the stack after a fixed watermark */ - if (CSTACK_SAVE_NOW(PyThreadState_GET(), self)) { + if (slp_cstack_save_now(PyThreadState_GET())) { res = slp_safe_pickling((int(*)(PyObject *, PyObject *, int))&save, (PyObject *)self, args, pers_save); goto finally; diff --git a/Stackless/core/stackless_tealet.c b/Stackless/core/stackless_tealet.c index edd45d35751ef1..d993c462d56620 100644 --- a/Stackless/core/stackless_tealet.c +++ b/Stackless/core/stackless_tealet.c @@ -135,10 +135,15 @@ void slp_tealet_cleanup(PyThreadState *ts) { slp_destroy_initial_stub(ts); if (ts->st.tealet_main) { - assert(TEALET_IS_MAIN(ts->st.tealet_main)); - tealet_finalize(ts->st.tealet_main); + /* we may be calling Py_Exit from a tasklet or otherwise + * from a sub-tealet. Therefore we can't make sanity checks + * like testing that we are the main tealet. Accept that we + * may leak suspended tealets in this case + */ + if (TEALET_CURRENT_IS_MAIN(ts->st.tealet_main)) + tealet_finalize(ts->st.tealet_main); + ts->st.tealet_main = NULL; } - ts->st.initial_stub = NULL; } void @@ -327,3 +332,22 @@ slp_transfer_with_exc(PyThreadState *ts, tealet_t *cst, PyTaskletObject *prev) ts->exc_traceback = exc_traceback; return ret; } + +#ifndef CSTACK_WATERMARK +#define CSTACK_WATERMARK 16384 +#endif +int slp_cstack_save_now(PyThreadState *ts) +{ + void *a, *b; + ptrdiff_t diff; + if (!ts->st.initial_stub) + return 0; + assert(ts->st.initial_stub); + a = tealet_get_far(ts->st.initial_stub); + b = tealet_new_far(NULL, NULL, NULL, 0); + diff = tealet_stack_diff(a, b); +#if 0 /* enable this for testing, a low threshold */ + return diff > 1000; +#endif + return diff > CSTACK_WATERMARK; +} \ No newline at end of file diff --git a/Stackless/core/stackless_tealet.h b/Stackless/core/stackless_tealet.h index c19bc0c450116e..3c6e8a3e9622be 100644 --- a/Stackless/core/stackless_tealet.h +++ b/Stackless/core/stackless_tealet.h @@ -28,4 +28,7 @@ int slp_transfer(PyThreadState *ts, tealet_t *cst, struct PyTaskletObject *prev) int slp_transfer_with_exc(PyThreadState *ts, tealet_t *cst, struct PyTaskletObject *prev); int slp_transfer_return(tealet_t *cst); +/* stack spilling */ +int slp_cstack_save_now(PyThreadState *ts); + #endif \ No newline at end of file diff --git a/Stackless/pickling/safe_pickle.c b/Stackless/pickling/safe_pickle.c index 10d0c7e843dc14..ff02112c7cbd71 100644 --- a/Stackless/pickling/safe_pickle.c +++ b/Stackless/pickling/safe_pickle.c @@ -1,142 +1,59 @@ #include "Python.h" #ifdef STACKLESS -#include "compile.h" - -#include "core/stackless_impl.h" +#include "core/stackless_tealet.h" /* safe pickling */ -static int(*cPickle_save)(PyObject *, PyObject *, int) = NULL; -#if 0 /* add stack spilling later */ -static PyObject * -pickle_callback(PyFrameObject *f, int exc, PyObject *retval) +typedef struct pickle_args { - PyThreadState *ts = PyThreadState_GET(); - PyTaskletObject *cur = ts->st.current; - PyCStackObject *cst; - PyCFrameObject *cf = (PyCFrameObject *) f; - intptr_t *saved_base; - - /* store and update thread state */ - ts->st.nesting_level = 1; /* always hard-switch from this one now */ - /* swap the cstates, hold on to the return one */ - cst = cur->cstate; - cur->cstate = ts->st.initial_stub; - Py_INCREF(cur->cstate); - - /* We must base our new stack from here, because oterwise we might find - * ourselves in an infinite loop of stack spilling. - */ - saved_base = ts->st.cstack_root; - ts->st.cstack_root = STACK_REFPLUS + (intptr_t *) &f; - Py_DECREF(retval); - cf->i = cPickle_save(cf->ob1, cf->ob2, cf->n); - ts->st.cstack_root = saved_base; - - /* jump back. No decref, frame contains result. */ - Py_DECREF(cur->cstate); - cur->cstate = cst; - ts->frame = cf->f_back; - slp_transfer_return(cst); - /* never come here */ - return NULL; + PyThreadState *ts; + tealet_t *return_to; + int (*save)(PyObject *, PyObject *, int); + PyObject *self; + PyObject *args; + int pers_save; + int result; +} pickle_args; + + +static tealet_t * +pickle_callback(tealet_t *current, void *arg) +{ + pickle_args *args = (pickle_args*)arg; + args->ts->st.nesting_level++; /* always hard-switch from this one now */ + args->result = (*args->save)(args->self, args->args, args->pers_save); + args->ts->st.nesting_level--; + return args->return_to; } -#endif -static int pickle_M(PyObject *self, PyObject *args, int pers_save); - int slp_safe_pickling(int(*save)(PyObject *, PyObject *, int), PyObject *self, PyObject *args, int pers_save) { PyThreadState *ts = PyThreadState_GET(); - PyTaskletObject *cur = ts->st.current; - int ret = -1; -#if 0 - PyCFrameObject *cf = NULL; - PyCStackObject *cst; - - if (ts->st.cstack_root == NULL) { - /* mark the stack spilling base */ - ts->st.cstack_root = STACK_REFPLUS + (intptr_t *) &save; - ret = (*save)(self, args, pers_save); - ts->st.cstack_root = NULL; - return ret; + pickle_args *pargs = PyMem_MALLOC(sizeof(pickle_args)); + void *ppargs; + int result; + if (!pargs) { + PyErr_NoMemory(); + return -1; } - - cPickle_save = save; - - if (ts->st.main == NULL) -#endif - return pickle_M(self, args, pers_save); -#if 0 - - cf = slp_cframe_new(pickle_callback, 1); - if (cf == NULL) - goto finally; - Py_INCREF(self); - cf->ob1 = self; - Py_INCREF(args); - cf->ob2 = args; - cf->n = pers_save; - ts->frame = (PyFrameObject *) cf; - cst = cur->cstate; - cur->cstate = NULL; - if (slp_transfer(&cur->cstate, NULL, cur) < 0) - return -1; /* fatal */ - Py_XDECREF(cur->cstate); - cur->cstate = cst; - ret = cf->i; -finally: - Py_XDECREF(cf); - return ret; -#endif + pargs->ts = ts; + pargs->return_to = tealet_current(ts->st.tealet_main); + pargs->save = save; + pargs->self = self; + pargs->args = args; + pargs->pers_save = pers_save; + + ppargs = (void*)pargs; + result = slp_run_initial_stub(ts, pickle_callback, &ppargs); + if (result == 0) + result = pargs->result; + PyMem_FREE(pargs); + return result; } -/* safe unpickling is not needed */ - -/* - * the following stuff is only needed in the rare case that we are - * run without any initialisation. In this case, we don't save stack - * but use slp_eval_frame, which initializes everything. - */ - -static PyObject *_self, *_args; -static int _pers_save; - -static PyObject * -pickle_runmain(PyFrameObject *f, int exc, PyObject *retval) -{ - PyThreadState *ts = PyThreadState_GET(); - Py_XDECREF(retval); - ts->frame = f->f_back; - Py_DECREF(f); - return PyInt_FromLong(cPickle_save(_self, _args, _pers_save)); -} - -static int -pickle_M(PyObject *self, PyObject *args, int pers_save) -{ - PyThreadState *ts = PyThreadState_GET(); - PyCFrameObject *cf = slp_cframe_new(pickle_runmain, 0); - int ret; - intptr_t *old_root; - - if (cf == NULL) return -1; - _self = self; - _args = args; - _pers_save = pers_save; -#if 0 - old_root = ts->st.cstack_root; - ts->st.cstack_root = STACK_REFPLUS + (intptr_t *) &self; -#endif - ret = slp_int_wrapper(slp_eval_frame((PyFrameObject *)cf)); -#if 0 - ts->st.cstack_root = old_root; -#endif - return ret; -} - -#endif +/* safe unpickling is not needed */ +#endif \ No newline at end of file diff --git a/Stackless/unittests/test_pickle.py b/Stackless/unittests/test_pickle.py index 1c388dc1dc3392..30e951df2b068f 100644 --- a/Stackless/unittests/test_pickle.py +++ b/Stackless/unittests/test_pickle.py @@ -221,7 +221,6 @@ def run_pickled(self, func, *args): except AttributeError: have_fromkeys = False -@unittest.skip("until safe pickling is done") class TestConcretePickledTasklets(TestPickledTasklets): def testClassPersistence(self): t1 = CustomTasklet(nothing)() From 9694d4951053fab4752ceaeca321143c2bc0d558 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Mon, 29 Jul 2013 12:48:37 +0000 Subject: [PATCH 26/35] Re-enable stack spilling for eval loop. --- Python/ceval.c | 4 +- Stackless/core/stacklesseval.c | 139 ++++++++++----------------------- 2 files changed, 42 insertions(+), 101 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index fa303209ae30d6..2566419db8a891 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -905,10 +905,8 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) return NULL; #ifdef STACKLESS -#if 0 /* TODO - re-enable stack spilling */ - if (CSTACK_SAVE_NOW(tstate, f)) + if (slp_cstack_save_now(tstate)) return slp_eval_frame_newstack(f, throwflag, retval); -#endif /* push frame */ if (Py_EnterRecursiveCall("")) { diff --git a/Stackless/core/stacklesseval.c b/Stackless/core/stacklesseval.c index a4cd13988c3b02..cafb8d01f3acbd 100644 --- a/Stackless/core/stacklesseval.c +++ b/Stackless/core/stacklesseval.c @@ -221,116 +221,59 @@ void PyStackless_kill_tasks_with_stacks(int allthreads) /* cstack spilling for recursive calls */ -#if 0 -/* re-enable stack spilling separately */ - -static PyObject * -eval_frame_callback(PyFrameObject *f, int exc, PyObject *retval) +typedef struct eval_args { - PyThreadState *ts = PyThreadState_GET(); - PyTaskletObject *cur = ts->st.current; - PyCStackObject *cst; - PyCFrameObject *cf = (PyCFrameObject *) f; - intptr_t *saved_base; - - //make sure we don't try softswitching out of this callstack - ts->st.nesting_level = cf->n + 1; - ts->frame = f->f_back; - - //this tasklet now runs in this tstate. - cst = cur->cstate; //The calling cstate - cur->cstate = ts->st.initial_stub; - Py_INCREF(cur->cstate); - - /* We must base our new stack from here, because otherwise we might find - * ourselves in an infinite loop of stack spilling. - */ - saved_base = ts->st.cstack_root; - ts->st.cstack_root = STACK_REFPLUS + (intptr_t *) &f; - - /* pull in the right retval and tempval from the arguments */ - Py_DECREF(retval); - retval = cf->ob1; - cf->ob1 = NULL; - TASKLET_SETVAL_OWN(cur, cf->ob2); - cf->ob2 = NULL; + PyThreadState *ts; + tealet_t *return_to; + PyFrameObject *f; + int exc; + PyObject *retval; +} eval_args; - retval = PyEval_EvalFrameEx_slp(ts->frame, exc, retval); - ts->st.cstack_root = saved_base; - - /* store retval back into the cstate object */ - if (retval == NULL) - retval = slp_curexc_to_bomb(); - if (retval == NULL) - goto fatal; - cf->ob1 = retval; - - /* jump back */ - Py_DECREF(cur->cstate); - cur->cstate = cst; - slp_transfer_return(cst); - /* should never come here */ -fatal: - Py_DECREF(cf); /* since the caller won't do it */ - return NULL; +static tealet_t * +eval_frame_callback(tealet_t *current, void *arg) +{ + eval_args *args = (eval_args*)arg; + PyThreadState *ts = args->ts; + + /*make sure we don't try softswitching out of this callstack + /* TODO: find a way to keep this tasklet pickleable, but not softswitchable */ + ts->st.nesting_level++; + + /* perform the call */ + args->retval = PyEval_EvalFrameEx_slp(args->f, args->exc, args->retval); + ts->st.nesting_level--; + return args->return_to; } PyObject * slp_eval_frame_newstack(PyFrameObject *f, int exc, PyObject *retval) { PyThreadState *ts = PyThreadState_GET(); - PyTaskletObject *cur = ts->st.current; - PyCFrameObject *cf = NULL; - PyCStackObject *cst; - - if (cur == NULL || PyErr_Occurred()) { - /* Bypass this during early initialization or if we have a pending - * exception, such as the one set via gen_close(). Doing the stack - * magic here will clear that exception. - */ - intptr_t *old = ts->st.cstack_root; - ts->st.cstack_root = STACK_REFPLUS + (intptr_t *) &f; - retval = PyEval_EvalFrameEx_slp(f,exc, retval); - ts->st.cstack_root = old; - return retval; - } - if (ts->st.cstack_root == NULL) { - /* this is a toplevel call. Store the root of stack spilling */ - ts->st.cstack_root = STACK_REFPLUS + (intptr_t *) &f; - retval = PyEval_EvalFrameEx_slp(f, exc, retval); - /* and reset it. We may reenter stackless at a completely different - * depth later - */ - return retval; + eval_args *args = PyMem_MALLOC(sizeof(eval_args)); + int result; + void *argp; + if (!args) { + PyErr_NoMemory(); + return NULL; } - - ts->frame = f; - cf = slp_cframe_new(eval_frame_callback, 1); - if (cf == NULL) + args->ts = ts; + args->return_to = tealet_current(ts->st.tealet_main); + args->f = f; + args->exc = exc; + args->retval = retval; + + argp = (void*)args; + result = slp_run_initial_stub(ts, eval_frame_callback, &argp); + if (result) { + Py_XDECREF(retval); + PyMem_FREE(args); return NULL; - cf->n = ts->st.nesting_level; - cf->ob1 = retval; - /* store the tmpval here so that it won't get clobbered - * by slp_run_tasklet() - */ - TASKLET_CLAIMVAL(cur, &(cf->ob2)); - ts->frame = (PyFrameObject *) cf; - cst = cur->cstate; - cur->cstate = NULL; - if (slp_transfer(&cur->cstate, NULL, cur) < 0) - goto finally; /* fatal */ - Py_XDECREF(cur->cstate); - - retval = cf->ob1; - cf->ob1 = NULL; - if (PyBomb_Check(retval)) - retval = slp_bomb_explode(retval); -finally: - Py_DECREF(cf); - cur->cstate = cst; + } + retval = args->retval; + PyMem_FREE(args); return retval; } -#endif /****************************************************** From e9249093cd2fadadf77ba2ac6f3c82c4b2fe6798 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Mon, 29 Jul 2013 14:02:20 +0000 Subject: [PATCH 27/35] Recreate initial stub if found higher on the stack. --- Stackless/core/stackless_tealet.c | 45 +++++++++++++++++++++++++++---- Stackless/core/stacklesseval.c | 20 ++------------ 2 files changed, 42 insertions(+), 23 deletions(-) diff --git a/Stackless/core/stackless_tealet.c b/Stackless/core/stackless_tealet.c index d993c462d56620..74cbd08eab8740 100644 --- a/Stackless/core/stackless_tealet.c +++ b/Stackless/core/stackless_tealet.c @@ -60,11 +60,20 @@ static tealet_t *slp_stub_main(tealet_t *current, void *arg) } /* create a stub and return it */ -tealet_t *slp_stub_new(tealet_t *t, size_t extrasize) { +tealet_t *slp_stub_new(tealet_t *t, size_t extrasize) +{ void *arg = (void*)tealet_current(t); return tealet_new(t, slp_stub_main, &arg, extrasize); } +/* find the stack far point if a stub were created here */ +void * slp_stub_far(tealet_t *dummy1, size_t dummy2) +{ + void *arg = 0; + return tealet_new_far(dummy1, NULL, &arg, dummy2); +} + + /* run a stub */ int slp_stub_run(tealet_t *stub, tealet_run_t run, void **parg) { @@ -121,9 +130,35 @@ slp_make_initial_stub(PyThreadState *ts) goto err; } - ts->st.initial_stub = slp_stub_new(ts->st.tealet_main, sizeof(PyTealet_data)); - if (!ts->st.initial_stub) - goto err; + if (ts->st.initial_stub) { + /* recreate stub, if it was deeper on the stack, otherwise, we just leave it */ + /* note, this approach needs to be subject to tuning. Maybe the absolute position + * of the stub makes no difference. But for stack spilling, a "high" stub makes + * perfect sense. + */ + void *a; + void *b; + a = tealet_get_far(ts->st.initial_stub); + b = slp_stub_far(ts->st.tealet_main, 0); + if (tealet_stack_diff(a, b) < 0) { + /* new stub will be higher up, recreate it */ + tealet_delete(ts->st.initial_stub); + ts->st.initial_stub = NULL; + } + } + + if (!ts->st.initial_stub) { + ts->st.initial_stub = slp_stub_new(ts->st.tealet_main, sizeof(PyTealet_data)); + if (!ts->st.initial_stub) + goto err; +#if 0 /* just to verify predicted pos */ + { + void *c + c = slp_stub_far(ts->st.tealet_main, 0); + c = c; + } +#endif + } return 0; err: PyErr_NoMemory(); @@ -344,7 +379,7 @@ int slp_cstack_save_now(PyThreadState *ts) return 0; assert(ts->st.initial_stub); a = tealet_get_far(ts->st.initial_stub); - b = tealet_new_far(NULL, NULL, NULL, 0); + b = slp_stub_far(NULL, 0); diff = tealet_stack_diff(a, b); #if 0 /* enable this for testing, a low threshold */ return diff > 1000; diff --git a/Stackless/core/stacklesseval.c b/Stackless/core/stacklesseval.c index cafb8d01f3acbd..2a2735bbf39e0c 100644 --- a/Stackless/core/stacklesseval.c +++ b/Stackless/core/stacklesseval.c @@ -66,26 +66,10 @@ slp_eval_frame(PyFrameObject *f) * when they exit. */ PyObject *result; - if (!ts->st.initial_stub) { - if (slp_make_initial_stub(ts)) - return NULL; - } + if (slp_make_initial_stub(ts)) + return NULL; ts->frame = f; result = slp_run_stub_from_main(ts); -#if 0 - /* clean up initial stub and recreate it every time */ - slp_destroy_initial_stub(ts); -#else - /* experimental. - * no need to have new stubs, because of how tealets - * work. This stub continues to be valid no matter from - * what level we enter. - * The only way this might cause problems is if the initial - * entry is unusually deep on the stack. We could modify this by - * testing the stack position. - */ - /* TODO: recreate the stub if we are in a "shallower" position */ -#endif return result; } From 5a01335417af9741757c2b71af2e131bce160159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Mon, 29 Jul 2013 14:03:20 +0000 Subject: [PATCH 28/35] Change a todo comment. --- Stackless/core/stackless_tealet.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Stackless/core/stackless_tealet.c b/Stackless/core/stackless_tealet.c index 74cbd08eab8740..4a9b710dba2824 100644 --- a/Stackless/core/stackless_tealet.c +++ b/Stackless/core/stackless_tealet.c @@ -115,7 +115,8 @@ slp_tealet_error(int err) } /* the current mechanism is based on the generic callable stubs - * above. This can be simplified, TODO + * above. This is useful, because it allows us to call arbitrary + * callbacks, e.g. when doing stack spilling. */ int slp_make_initial_stub(PyThreadState *ts) From 7bb7ab6c94c8af5ef669fd6d7cf9471241fd9c2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Mon, 29 Jul 2013 16:09:14 +0000 Subject: [PATCH 29/35] Minor cleanup --- Stackless/module/scheduling.c | 2 +- Stackless/module/taskletobject.c | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/Stackless/module/scheduling.c b/Stackless/module/scheduling.c index 64417ee12a8fe6..0bb29e28e15e78 100644 --- a/Stackless/module/scheduling.c +++ b/Stackless/module/scheduling.c @@ -987,7 +987,7 @@ initialize_main_and_current(void) &PyTasklet_Type, noargs, NULL); Py_DECREF(noargs); if (task == NULL) return -1; - task->cstate = 0; + task->cstate = NULL; task->tstate = ts; task->nesting_level = 0; ts->st.main = task; diff --git a/Stackless/module/taskletobject.c b/Stackless/module/taskletobject.c index b641bb185ba49e..56be351cbca341 100644 --- a/Stackless/module/taskletobject.c +++ b/Stackless/module/taskletobject.c @@ -94,7 +94,6 @@ tasklet_traverse(PyTaskletObject *t, visitproc visit, void *arg) Py_VISIT(f); } Py_VISIT(t->tempval); - //Py_VISIT(t->cstate); return 0; } @@ -1255,10 +1254,6 @@ tasklet_thread_id(PyTaskletObject *task) } static PyMemberDef tasklet_members[] = { - {"cstate", T_OBJECT, offsetof(PyTaskletObject, cstate), READONLY, - PyDoc_STR("the C stack object associated with the tasklet.\n\ - Every tasklet has a cstate, even if it is a trivial one.\n\ - Please see the cstate doc and the stackless documentation.")}, {"tempval", T_OBJECT, offsetof(PyTaskletObject, tempval), 0}, /* blocked, slicing_lock, atomic and such are treated by tp_getset */ {0} From 627c5f6ee51ffe802ff18a9a5649a378a52e0cbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Mon, 29 Jul 2013 16:10:03 +0000 Subject: [PATCH 30/35] Remove the old "platf" directory. This is a lot of legacy, it should all work through tealets now. --- Stackless/platf/mkswitch_stack.py | 71 ------ Stackless/platf/slp_platformselect.h | 84 ------- Stackless/platf/slp_switch_stack.h | 279 ------------------------ Stackless/platf/switch_amd64_unix.h | 142 ------------ Stackless/platf/switch_amd64_unix_gas.s | 56 ----- Stackless/platf/switch_arm32_gcc.h | 50 ----- Stackless/platf/switch_arm_thumb_gas.s | 70 ------ Stackless/platf/switch_arm_thumb_gcc.h | 10 - Stackless/platf/switch_mips_unix.h | 56 ----- Stackless/platf/switch_ppc_macosx.h | 85 -------- Stackless/platf/switch_ppc_unix.h | 73 ------- Stackless/platf/switch_ps3_SNTools.h | 15 -- Stackless/platf/switch_ps3_SNTools.s | 228 ------------------- Stackless/platf/switch_s390_unix.h | 54 ----- Stackless/platf/switch_sparc_sun_gcc.h | 85 -------- Stackless/platf/switch_x64_masm.asm | 105 --------- Stackless/platf/switch_x64_msvc.h | 55 ----- Stackless/platf/switch_x86_msvc.h | 94 -------- Stackless/platf/switch_x86_unix.h | 70 ------ 19 files changed, 1682 deletions(-) delete mode 100644 Stackless/platf/mkswitch_stack.py delete mode 100644 Stackless/platf/slp_platformselect.h delete mode 100644 Stackless/platf/slp_switch_stack.h delete mode 100644 Stackless/platf/switch_amd64_unix.h delete mode 100644 Stackless/platf/switch_amd64_unix_gas.s delete mode 100644 Stackless/platf/switch_arm32_gcc.h delete mode 100644 Stackless/platf/switch_arm_thumb_gas.s delete mode 100644 Stackless/platf/switch_arm_thumb_gcc.h delete mode 100644 Stackless/platf/switch_mips_unix.h delete mode 100644 Stackless/platf/switch_ppc_macosx.h delete mode 100644 Stackless/platf/switch_ppc_unix.h delete mode 100644 Stackless/platf/switch_ps3_SNTools.h delete mode 100644 Stackless/platf/switch_ps3_SNTools.s delete mode 100644 Stackless/platf/switch_s390_unix.h delete mode 100644 Stackless/platf/switch_sparc_sun_gcc.h delete mode 100644 Stackless/platf/switch_x64_masm.asm delete mode 100644 Stackless/platf/switch_x64_msvc.h delete mode 100644 Stackless/platf/switch_x86_msvc.h delete mode 100644 Stackless/platf/switch_x86_unix.h diff --git a/Stackless/platf/mkswitch_stack.py b/Stackless/platf/mkswitch_stack.py deleted file mode 100644 index 5a047ef7f2998e..00000000000000 --- a/Stackless/platf/mkswitch_stack.py +++ /dev/null @@ -1,71 +0,0 @@ -""" - mkswitch_stack.py - - Purpose: - Generate an include file from the platform dependant - include files mentioned in slp_platformselect.h . - - The existing slp_switch implementations are calling - the macros slp_save_state and slp_restore_state. - Now I want to support real stack switching, that is, - the stack is not modified in place, but we jump to - a different stack, without copying anything. - This costs a lot of memory and should be used for - a few high-speed tasklets, only. - - In order to keep things simple, I'm not special-casing - the support macroes, but use a different macro set. - The machine code is the same, therefore the implementation - can be generated from the existing include files. - - We generate a new include file called slp_switch_stack.h . -""" - -def parse_platformselect(): - fin_name = "slp_platformselect.h" - fin = file(fin_name) - fout_name = "slp_switch_stack.h" - fout = file(fout_name, "w") - import sys - print>>fout, "/* this file is generated by mkswitch_stack.py, don't edit */" - print>>fout - for line in fin: - tokens = line.split() - if not tokens: continue - tok = tokens[0] - if tok == "#endif": - print>>fout, line - break # done - if tok in ("#if", "#elif"): - print>>fout, line - elif tok == "#include": - finc_name = tokens[1][1:-1] - txt = parse_switch(finc_name) - print>>fout, txt - -edits = ( - ("slp_switch", "slp_switch_stack"), - ("SLP_SAVE_STATE", "SLP_STACK_BEGIN"), - ("SLP_RESTORE_STATE", "SLP_STACK_END"), -) - -def parse_switch(fname): - f = file(fname) - res = [] - for line in f: - if line.strip() == "static int": - res.append(line) - break - for line in f: - res.append(line) - if line.rstrip() == "}": - break - # end of procedure. - # now substitute - s = "".join(res) - for txt, repl in edits: - s = s.replace(txt, repl) - return s - -if __name__ == "__main__": - parse_platformselect() \ No newline at end of file diff --git a/Stackless/platf/slp_platformselect.h b/Stackless/platf/slp_platformselect.h deleted file mode 100644 index 59ad0ef371404e..00000000000000 --- a/Stackless/platf/slp_platformselect.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Platform Selection for Stackless Python - */ - -#if defined(MS_WIN32) && !defined(MS_WIN64) && defined(_M_IX86) -#include "switch_x86_msvc.h" /* MS Visual Studio on X86 */ -#elif defined(MS_WIN64) && defined(_M_X64) -#include "switch_x64_msvc.h" /* MS Visual Studio on X64 */ -#elif defined(__GNUC__) && defined(__i386__) -#include "switch_x86_unix.h" /* gcc on X86 */ -#elif defined(__GNUC__) && defined(__amd64__) -#include "switch_amd64_unix.h" /* gcc on amd64 */ -#elif defined(__GNUC__) && defined(__PPC__) && defined(__linux__) -#include "switch_ppc_unix.h" /* gcc on PowerPC */ -#elif defined(__GNUC__) && defined(__ppc__) && defined(__APPLE__) -#include "switch_ppc_macosx.h" /* Apple MacOS X on PowerPC */ -#elif defined(__GNUC__) && defined(sparc) && defined(sun) -#include "switch_sparc_sun_gcc.h" /* SunOS sparc with gcc */ -#elif defined(__GNUC__) && defined(__s390__) && defined(__linux__) -#include "switch_s390_unix.h" /* Linux/S390 */ -#elif defined(__GNUC__) && defined(__s390x__) && defined(__linux__) -#include "switch_s390_unix.h" /* Linux/S390 zSeries (identical) */ -#elif defined(__GNUC__) && defined(__arm__) && defined(__thumb__) -#include "switch_arm_thumb_gcc.h" /* gcc using arm thumb */ -#elif defined(__GNUC__) && defined(__arm32__) -#include "switch_arm32_gcc.h" /* gcc using arm32 */ -#elif defined(__GNUC__) && defined(__mips__) && defined(__linux__) -#include "switch_mips_unix.h" /* MIPS */ -#elif defined(SN_TARGET_PS3) -#include "switch_ps3_SNTools.h" /* Sony PS3 */ -#endif - -/* default definitions if not defined in above files */ - -/* adjust slots to typical size of a few recursions on your system */ - -#ifndef CSTACK_SLOTS -#define CSTACK_SLOTS 1024 -#endif - -/* how many cstacks to cache at all */ - -#ifndef CSTACK_MAXCACHE -#define CSTACK_MAXCACHE 100 -#endif - -/* a good estimate how much the cstack level differs between - initialisation and main python code. Not critical, but saves time. - Note that this will vanish with the greenlet approach. */ - -#ifndef CSTACK_GOODGAP -#define CSTACK_GOODGAP 4096 -#endif - -/* stack size in pointer to trigger stack spilling */ - -#ifndef CSTACK_WATERMARK -#define CSTACK_WATERMARK 16384 -#endif - -/* define direction of stack growth */ - -#ifndef CSTACK_DOWNWARDS -#define CSTACK_DOWNWARDS 1 /* 0 for upwards */ -#endif - -/************************************************************** - - Don't change definitions below, please. - - **************************************************************/ - -#if CSTACK_DOWNWARDS == 1 -#define CSTACK_COMPARE(a, b) (a) < (b) -#define CSTACK_SUBTRACT(a, b) (a) - (b) -#else -#define CSTACK_COMPARE(a, b) (a) > (b) -#define CSTACK_SUBTRACT(a, b) (b) - (a) -#endif - -#define CSTACK_SAVE_NOW(tstate, stackvar) \ - ((tstate)->st.cstack_root != NULL ? \ - CSTACK_SUBTRACT((tstate)->st.cstack_root, \ - (intptr_t*)&(stackvar)) > CSTACK_WATERMARK : 1) diff --git a/Stackless/platf/slp_switch_stack.h b/Stackless/platf/slp_switch_stack.h deleted file mode 100644 index 4eb03f379a6f2f..00000000000000 --- a/Stackless/platf/slp_switch_stack.h +++ /dev/null @@ -1,279 +0,0 @@ -/* this file is generated by mkswitch_stack.py, don't edit */ - -#if defined(MS_WIN32) && !defined(MS_WIN64) && defined(_M_IX86) - -static int -slp_switch_stack(void) -{ - register int *stackref, stsizediff; - __asm mov stackref, esp; - /* modify EBX, ESI and EDI in order to get them preserved */ - __asm mov ebx, ebx; - __asm xchg esi, edi; - { - SLP_STACK_BEGIN(stackref, stsizediff); - __asm { - mov eax, stsizediff - add esp, eax - add ebp, eax - } - SLP_STACK_END(); - return 0; - } -#pragma warning(default:4731) -} - -#elif defined(MS_WIN64) && defined(_M_X64) - - -#elif defined(__GNUC__) && defined(__i386__) - -static int -slp_switch_stack(void) -{ - register int *stackref, stsizediff; -#if STACKLESS_FRHACK - __asm__ volatile ("" : : : "esi", "edi"); -#else - __asm__ volatile ("" : : : "ebx", "esi", "edi"); -#endif - __asm__ ("movl %%esp, %0" : "=g" (stackref)); - { - SLP_STACK_BEGIN(stackref, stsizediff); - __asm__ volatile ( - "addl %0, %%esp\n" - "addl %0, %%ebp\n" - : - : "r" (stsizediff) - ); - SLP_STACK_END(); - return 0; - } -#if STACKLESS_FRHACK - __asm__ volatile ("" : : : "esi", "edi"); -#else - __asm__ volatile ("" : : : "ebx", "esi", "edi"); -#endif -} - -#elif defined(__GNUC__) && defined(__amd64__) - -static int -slp_switch_stack(void) -{ - register long *stackref, stsizediff; - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ ("movq %%rsp, %0" : "=g" (stackref)); - { - SLP_STACK_BEGIN(stackref, stsizediff); - __asm__ volatile ( - "addq %0, %%rsp\n" - "addq %0, %%rbp\n" - : - : "r" (stsizediff) - ); - SLP_STACK_END(); - return 0; - } - __asm__ volatile ("" : : : REGS_TO_SAVE); -} - -#elif defined(__GNUC__) && defined(__PPC__) && defined(__linux__) - -static int -slp_switch_stack(void) -{ - register int *stackref, stsizediff; - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ ("mr %0, 1" : "=g" (stackref) : ); - { - SLP_STACK_BEGIN(stackref, stsizediff); - __asm__ volatile ( - "mr 11, %0\n" - "add 1, 1, 11\n" - "add 30, 30, 11\n" - : /* no outputs */ - : "g" (stsizediff) - : "11" - ); - SLP_STACK_END(); - return 0; - } - __asm__ volatile ("" : : : REGS_TO_SAVE); -} - -#elif defined(__GNUC__) && defined(__ppc__) && defined(__APPLE__) - -static int -slp_switch_stack(void) -{ - static int x = 0; - register intptr_t *stackref; - register int stsizediff; - __asm__ volatile ( - "; asm block 1\n" - : /* no outputs */ - : "r" (x) - : REGS_TO_SAVE - ); - __asm__ ("; asm block 2\n\tmr %0, r1" : "=g" (stackref) : ); - { - SLP_STACK_BEGIN(stackref, stsizediff); - __asm__ volatile ( - "; asm block 3\n" - "\tmr r11, %0\n" - "\tadd r1, r1, r11\n" - "\tadd r30, r30, r11\n" - : /* no outputs */ - : "g" (stsizediff) - : "r11" - ); - SLP_STACK_END(); - return 0; - } -} - -#elif defined(__GNUC__) && defined(sparc) && defined(sun) - -static int -slp_switch_stack(void) -{ - register int *stackref, stsizediff; - - /* Put the stack pointer into stackref */ - - /* Sparc special: at first, flush register windows - */ - __asm__ volatile ( - "ta %1\n\t" - "mov %%sp, %0" - : "=r" (stackref) : "i" (ST_FLUSH_WINDOWS)); - - { /* You shalt put SLP_STACK_BEGIN into a local block */ - - SLP_STACK_BEGIN(stackref, stsizediff); - - /* Increment stack and frame pointer by stsizediff */ - - /* Sparc special: at first load new return address. - This cannot be done later, because the stack - might be overwritten again just after SLP_STACK_END - has finished. BTW: All other registers (l0-l7 and i0-i5) - might be clobbered too. - */ - __asm__ volatile ( - "ld [%0+60], %%i7\n\t" - "add %1, %%sp, %%sp\n\t" - "add %1, %%fp, %%fp" - : : "r" (_cst->stack), "r" (stsizediff) - : "%l0", "%l1", "%l2", "%l3", "%l4", "%l5", "%l6", "%l7", - "%i0", "%i1", "%i2", "%i3", "%i4", "%i5"); - - SLP_STACK_END(); - - /* Run far away as fast as possible, don't look back at the sins. - * The LORD rained down burning sulfur on Sodom and Gomorra ... - */ - - /* Sparc special: Must make it *very* clear to the CPU that - it shouldn't look back into the register windows - */ - __asm__ volatile ( "ta %0" : : "i" (ST_CLEAN_WINDOWS)); - return 0; - } -} - -#elif defined(__GNUC__) && defined(__s390__) && defined(__linux__) - -static int -slp_switch_stack(void) -{ - register int *stackref, stsizediff; - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ ("lr %0, 15" : "=g" (stackref) : ); - { - SLP_STACK_BEGIN(stackref, stsizediff); - __asm__ volatile ( - "ar 15, %0" - : /* no outputs */ - : "g" (stsizediff) - ); - SLP_STACK_END(); - return 0; - } - __asm__ volatile ("" : : : REGS_TO_SAVE); -} - -#elif defined(__GNUC__) && defined(__s390x__) && defined(__linux__) - -static int -slp_switch_stack(void) -{ - register int *stackref, stsizediff; - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ ("lr %0, 15" : "=g" (stackref) : ); - { - SLP_STACK_BEGIN(stackref, stsizediff); - __asm__ volatile ( - "ar 15, %0" - : /* no outputs */ - : "g" (stsizediff) - ); - SLP_STACK_END(); - return 0; - } - __asm__ volatile ("" : : : REGS_TO_SAVE); -} - -#elif defined(__GNUC__) && defined(__arm__) && defined(__thumb__) - - -#elif defined(__GNUC__) && defined(__arm32__) - -static int -slp_switch_stack(void) -{ - register int *stackref, stsizediff; - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ ("mov %0,sp" : "=g" (stackref)); - { - SLP_STACK_BEGIN(stackref, stsizediff); - __asm__ volatile ( - "add sp,sp,%0\n" - "add fp,fp,%0\n" - : - : "r" (stsizediff) - ); - SLP_STACK_END(); - return 0; - } - __asm__ volatile ("" : : : REGS_TO_SAVE); -} - -#elif defined(__GNUC__) && defined(__mips__) && defined(__linux__) - -static int -slp_switch_stack(void) -{ - register int *stackref, stsizediff; - /* __asm__ __volatile__ ("" : : : REGS_TO_SAVE); */ - __asm__ ("move %0, $29" : "=r" (stackref) : ); - { - SLP_STACK_BEGIN(stackref, stsizediff); - __asm__ __volatile__ ( -#ifdef __mips64 - "daddu $29, %0\n" -#else - "addu $29, %0\n" -#endif - : /* no outputs */ - : "r" (stsizediff) - ); - SLP_STACK_END(); - } - /* __asm__ __volatile__ ("" : : : REGS_TO_SAVE); */ - return 0; -} - -#endif - diff --git a/Stackless/platf/switch_amd64_unix.h b/Stackless/platf/switch_amd64_unix.h deleted file mode 100644 index 1de0db17a54f48..00000000000000 --- a/Stackless/platf/switch_amd64_unix.h +++ /dev/null @@ -1,142 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 24-Oct-11 Anselm Kruis - * Reworked based on the AMD64 ABI spec. - * 26-Jul-10 Jeff Senn - * Got this to work (rather than crash consistently) - * on OS-X 10.6 by adding more registers to save set. - * Not sure what is the minimal set of regs, nor if - * this is completely stable and works for all compiler - * variations. - * 01-Apr-04 Hye-Shik Chang - * Ported from i386 to amd64. - * 24-Nov-02 Christian Tismer - * needed to add another magic constant to insure - * that f in slp_eval_frame(PyFrameObject *f) - * STACK_REFPLUS will probably be 1 in most cases. - * gets included into the saved stack area. - * 17-Sep-02 Christian Tismer - * after virtualizing stack save/restore, the - * stack size shrunk a bit. Needed to introduce - * an adjustment STACK_MAGIC per platform. - * 15-Sep-02 Gerd Woetzel - * slightly changed framework for spark - * 31-Avr-02 Armin Rigo - * Added ebx, esi and edi register-saves. - * 01-Mar-02 Samual M. Rushing - * Ported from i386. - */ - -#define STACK_REFPLUS 1 - -#ifdef SLP_EVAL - -/* #define STACK_MAGIC 3 */ -/* the above works fine with gcc 2.96, but 2.95.3 wants this */ -#define STACK_MAGIC 0 - - -/* - * In order to switch the stack, we use the fact, that the compiler - * already knows how to preserve registers accross function calls. - * - * The relevant AMD64 ABI specifigation pecisely defines which registers - * must be preserved and which registers may be modified. - * We use a gcc inline assembly feature to pretend that the inline - * assembly block modifies the registers to be preserved. As a result, - * the compiler emits code to preserve those registers. - * - * The AMD64 ABI Draft 0.99.5, Figure 3.4 "Register usage" - * defines the following registers as "Preserved across - * function calls": - * %rbx callee-saved register; optionally used as base pointer - * %rsp stack pointer - * %rbp callee-saved register; optional used as frame pointer - * %r12 - %r15 callee-saved registers - * %mxcsr SSE2 control and status word - * %fcw x87 control word - * - * The compiler always preserves the %rsp register accros a function call. - * - * Depending on the usage of a frame pointer, which is optional - * for the AMD64 ABI, the compiler already preserves the %rbp - * register. Unfortunately, we must not add "rbp" to the clobber list, if - * rbp is used as a frame pointer (won't compile). Therefore we save - * rbp manually. - * - * For the other registers (except %mxcsr and %fcw) we tell the compiler, - * that we are going to clobber the registers. The compiler will then save the registers - * for us. (However the compiler gives no guarantee, when it will restore - * the registers.) And the compiler only preserves those registers, that must - * be preserved according to the calling convention. It does not preserve any other - * register, that may be modified during a function call. Therefore specifying additional - * registers has no effect at all. Take a look at the generated assembly code! - * - * If we want more control, we need to preserve the - * registers explicitly similar to %mxcsr, %fcw and %rbp. - * - */ - -#if 1 -/* Registers marked as clobbered, minimum set according to the ABI spec. */ -#define REGS_CLOBBERED "rbx", "r12", "r13", "r14", "r15" -#else -/* Maximum possible clobber list. It gives the same assembly code as the minimum list. - If the ABI evolves, it might be necessary to add some of these registers */ -#define REGS_CLOBBERED "memory", "rax", "rbx", "rcx", "rdx", "rsi", "rdi", \ - "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", \ - "st(1)", "st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)", \ - "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", \ - "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15" -#endif - -/* - * You may want to make the function static enable optimizations. - * However, the ABI SPEC does not apply to static functions. Therefore - * I make slp_switch a regular global function. - */ -#if 0 -static -#endif -int -slp_switch(void) -{ - register long *stackref, stsizediff; - void * rbp; int mxcsr; short x87cw; - __asm__ volatile ( - "fstcw %0\n\t" - "stmxcsr %1\n\t" - "movq %%rbp, %2\n\t" - : "=m" (x87cw), "=m" (mxcsr), "=m" (rbp) : : REGS_CLOBBERED ); - __asm__ ("movq %%rsp, %0" : "=g" (stackref)); - { - SLP_SAVE_STATE(stackref, stsizediff); - __asm__ volatile ( - "addq %0, %%rsp\n\t" - "addq %0, %%rbp\n\t" - : - : "r" (stsizediff) - ); - SLP_RESTORE_STATE(); - __asm__ volatile ( - "movq %2, %%rbp\n\t" - "ldmxcsr %1\n\t" - "fldcw %0\n\t" - : : "m" (x87cw), "m" (mxcsr), "m" (rbp)); - return 0; - } -} - -#endif -/* - * further self-processing support - */ - -/* - * if you want to add self-inspection tools, place them - * here. See the x86_msvc for the necessary defines. - * These features are highly experimental und not - * essential yet. - */ diff --git a/Stackless/platf/switch_amd64_unix_gas.s b/Stackless/platf/switch_amd64_unix_gas.s deleted file mode 100644 index 6f61106572728b..00000000000000 --- a/Stackless/platf/switch_amd64_unix_gas.s +++ /dev/null @@ -1,56 +0,0 @@ -# NOTE: This is not yet safe to use. Checked in for the same of reference. -# -# (int) slp_switch (void); - - .text - .type slp_switch, @function - # This next line is required to the C code can find and - # link against this function. - .global slp_switch -slp_switch: - pushq %rbp - pushq %r15 - pushq %r14 - pushq %r13 - pushq %r12 - pushq %rbx - - # Disabled for now, which should ideally give us identical - # behaviour to the inline C version. Can add this when we - # are ready for it. - #subq $8, %rsp - #stmxcsr (%rsp) - - movq %rsp, %rdi - - call slp_save_state # diff = slp_save_state([?]stackref) - - cmp $-1, %rax # if (diff == -1) - je .exit # return -1; - - cmp $1, %rax # if (diff == 1) - je .no_restore # return 0; - -.restore: - add %rax, %rsp # Adjust the stack pointer for the state we are restoring. - - call slp_restore_state # slp_restore_state() - -.no_restore: - xor %rax, %rax # Switch successful (whether we restored or not). - -.exit: - #ldmxcsr (%rsp) - #addq $8, %rsp - - popq %rbx - popq %r12 - popq %r13 - popq %r14 - popq %r15 - # rbp gets popped by the leave statement - - leave - ret -.LFE11: - .size slp_switch, .-slp_switch diff --git a/Stackless/platf/switch_arm32_gcc.h b/Stackless/platf/switch_arm32_gcc.h deleted file mode 100644 index fda4b7d58536f9..00000000000000 --- a/Stackless/platf/switch_arm32_gcc.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 14-Aug-06 File creation. Ported from Arm Thumb. Sylvain Baro - * 3-Sep-06 Commented out saving of r1-r3 (r4 already commented out) as I - * read that these do not need to be saved. Also added notes and - * errors related to the frame pointer. Richard Tew. - * - * NOTES - * - * It is not possible to detect if fp is used or not, so the supplied - * switch function needs to support it, so that you can remove it if - * it does not apply to you. - * - * POSSIBLE ERRORS - * - * "fp cannot be used in asm here" - * - * - Try commenting out "fp" in REGS_TO_SAVE. - * - */ - -#define STACK_REFPLUS 1 - -#ifdef SLP_EVAL -#define STACK_MAGIC 0 -#define REGS_TO_SAVE /*"r1", "r2", "r3", "r4",*/ "r5", "r6", "fp", "ip", "lr" - -static int -slp_switch(void) -{ - register int *stackref, stsizediff; - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ ("mov %0,sp" : "=g" (stackref)); - { - SLP_SAVE_STATE(stackref, stsizediff); - __asm__ volatile ( - "add sp,sp,%0\n" - "add fp,fp,%0\n" - : - : "r" (stsizediff) - ); - SLP_RESTORE_STATE(); - return 0; - } - __asm__ volatile ("" : : : REGS_TO_SAVE); -} - -#endif diff --git a/Stackless/platf/switch_arm_thumb_gas.s b/Stackless/platf/switch_arm_thumb_gas.s deleted file mode 100644 index 545ffe209cbdd0..00000000000000 --- a/Stackless/platf/switch_arm_thumb_gas.s +++ /dev/null @@ -1,70 +0,0 @@ -@ The stack switching logic for the arm thumb instruction set. -@ Written by Richard Tew, 2nd May 2006. -@ -@ It is not possible to do this as inline assembler as gcc generates functions -@ where the stack register is preserved, so any changes we make to it will be -@ discarded. However, any stack copying we have done is left in place, which -@ results in corrupt state. -@ -@ This adapts the MASM approach written by Kristjan Jonsson, where early -@ returns from SLP_SAVE_STATE (within slp_save_state) are flagged by setting -@ the low bit of the return value. This either indicates that there was an -@ error (-1) or that we do not need to restore the stack (1) as we are only -@ here to save the current one. -@ -@ I know little arm thumb assembly, so in case anyone else wants to know the -@ approach I took, I will document it here. The first uncertainty was which -@ registers should be saved. I took this section of code from the assembler -@ generated by gcc with a command line of: -@ gcc -mthumb -S slp_transfer.c -@ where slp_transfer included the inline version of slp_switch for the arm -@ thumb. Another initial uncertainty were the instructions needed to: -@ a) obtain the stack pointer. (mov r0, sp) -@ b) restore the stack pointer. (add sp, sp, r0) -@ which were used in the original inlined assembly. The rest of the code was -@ just patched together based on Kristjan's existing masm source code. - -@ (int) slp_switch (void); - .thumb - .align 2 - .global slp_switch - .type slp_switch, %function @ Apparently useful for .map files. - .thumb_func - -slp_switch: - push {r4, r5, r6, r7, lr} - mov r7, fp - mov r6, sl - mov r5, r9 - mov r4, r8 - push {r4, r5, r6, r7} - - mov r0, sp - bl slp_save_state @ diff = slp_save_state([r0]stackref) - - mov r1, #0 @ r1 = -1 - mvn r1, r1 - - cmp r0, r1 @ if (diff == -1) - beq .exit @ return -1; - - cmp r0, #1 @ if (diff == 1) - beq .no_restore @ return 0; - -.restore: - add sp, sp, r0 @ Adjust the stack pointer for the state we are restoring. - - bl slp_restore_state @ slp_restore_state() - -.no_restore: - mov r0, #0 @ Switch successful (whether we restored or not). - -.exit: - pop {r2, r3, r4, r5} - mov r8, r2 - mov r9, r3 - mov sl, r4 - mov fp, r5 - pop {r4, r5, r6, r7, pc} - - .size slp_switch, .-slp_switch @ Apparently useful for .map files. diff --git a/Stackless/platf/switch_arm_thumb_gcc.h b/Stackless/platf/switch_arm_thumb_gcc.h deleted file mode 100644 index 489c920f14c2c4..00000000000000 --- a/Stackless/platf/switch_arm_thumb_gcc.h +++ /dev/null @@ -1,10 +0,0 @@ - -#define STACK_REFPLUS 1 -#define STACK_MAGIC 0 - -/* Use the generic support for an external assembly language slp_switch function. */ -#define EXTERNAL_ASM - -#ifdef SLP_EVAL -/* This always uses the external masm assembly file. */ -#endif diff --git a/Stackless/platf/switch_mips_unix.h b/Stackless/platf/switch_mips_unix.h deleted file mode 100644 index e507c42ec0990e..00000000000000 --- a/Stackless/platf/switch_mips_unix.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 05-Jan-08 Thiemo Seufer - * Ported from ppc. - */ - -#define STACK_REFPLUS 1 - -#ifdef SLP_EVAL - -#define STACK_MAGIC 0 - -#ifdef __mips64 -#define REGS_TO_SAVE "$16", "$17", "$18", "$19", "$20", "$21", "$22", \ - "$23", "$28", "$30" -#else -#define REGS_TO_SAVE "$16", "$17", "$18", "$19", "$20", "$21", "$22", \ - "$23", "$30" -#endif -static int -slp_switch(void) -{ - register int *stackref, stsizediff; - /* __asm__ __volatile__ ("" : : : REGS_TO_SAVE); */ - __asm__ ("move %0, $29" : "=r" (stackref) : ); - { - SLP_SAVE_STATE(stackref, stsizediff); - __asm__ __volatile__ ( -#ifdef __mips64 - "daddu $29, %0\n" -#else - "addu $29, %0\n" -#endif - : /* no outputs */ - : "r" (stsizediff) - ); - SLP_RESTORE_STATE(); - } - /* __asm__ __volatile__ ("" : : : REGS_TO_SAVE); */ - return 0; -} - -#endif - -/* - * further self-processing support - */ - -/* - * if you want to add self-inspection tools, place them - * here. See the x86_msvc for the necessary defines. - * These features are highly experimental und not - * essential yet. - */ diff --git a/Stackless/platf/switch_ppc_macosx.h b/Stackless/platf/switch_ppc_macosx.h deleted file mode 100644 index d582d3125f43bf..00000000000000 --- a/Stackless/platf/switch_ppc_macosx.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 14-Jan-04 Bob Ippolito - * added cr2-cr4 to the registers to be saved. - * Open questions: Should we save FP registers? - * What about vector registers? - * Differences between darwin and unix? - * 24-Nov-02 Christian Tismer - * needed to add another magic constant to insure - * that f in slp_eval_frame(PyFrameObject *f) - * gets included into the saved stack area. - * STACK_REFPLUS will probably be 1 in most cases. - * 17-Sep-02 Christian Tismer - * after virtualizing stack save/restore, the - * stack size shrunk a bit. Needed to introduce - * an adjustment STACK_MAGIC per platform. - * 15-Sep-02 Gerd Woetzel - * slightly changed framework for sparc - * 29-Jun-02 Christian Tismer - * Added register 13-29, 31 saves. The same way as - * Armin Rigo did for the x86_unix version. - * This seems to be now fully functional! - * 04-Mar-02 Hye-Shik Chang - * Ported from i386. - */ - -#define STACK_REFPLUS 1 - -#ifdef SLP_EVAL - -#define STACK_MAGIC 3 - -#if STACKLESS_FRHACK -#define REGS_TO_SAVE "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", \ - "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", \ - "cr2", "cr3", "cr4" -#else -#define REGS_TO_SAVE "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", \ - "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", "r31", \ - "cr2", "cr3", "cr4" -#endif - -static int -slp_switch(void) -{ - static int x = 0; - register intptr_t *stackref; - register int stsizediff; - __asm__ volatile ( - "; asm block 1\n" - : /* no outputs */ - : "r" (x) - : REGS_TO_SAVE - ); - __asm__ ("; asm block 2\n\tmr %0, r1" : "=g" (stackref) : ); - { - SLP_SAVE_STATE(stackref, stsizediff); - __asm__ volatile ( - "; asm block 3\n" - "\tmr r11, %0\n" - "\tadd r1, r1, r11\n" - "\tadd r30, r30, r11\n" - : /* no outputs */ - : "g" (stsizediff) - : "r11" - ); - SLP_RESTORE_STATE(); - return 0; - } -} - -#endif - -/* - * further self-processing support - */ - -/* - * if you want to add self-inspection tools, place them - * here. See the x86_msvc for the necessary defines. - * These features are highly experimental und not - * essential yet. - */ diff --git a/Stackless/platf/switch_ppc_unix.h b/Stackless/platf/switch_ppc_unix.h deleted file mode 100644 index ccc893b2c2022b..00000000000000 --- a/Stackless/platf/switch_ppc_unix.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 14-Jan-04 Bob Ippolito - * added cr2-cr4 to the registers to be saved. - * Open questions: Should we save FP registers? - * What about vector registers? - * Differences between darwin and unix? - * 24-Nov-02 Christian Tismer - * needed to add another magic constant to insure - * that f in slp_eval_frame(PyFrameObject *f) - * gets included into the saved stack area. - * STACK_REFPLUS will probably be 1 in most cases. - * 04-Oct-02 Gustavo Niemeyer - * Ported from MacOS version. - * 17-Sep-02 Christian Tismer - * after virtualizing stack save/restore, the - * stack size shrunk a bit. Needed to introduce - * an adjustment STACK_MAGIC per platform. - * 15-Sep-02 Gerd Woetzel - * slightly changed framework for sparc - * 29-Jun-02 Christian Tismer - * Added register 13-29, 31 saves. The same way as - * Armin Rigo did for the x86_unix version. - * This seems to be now fully functional! - * 04-Mar-02 Hye-Shik Chang - * Ported from i386. - */ - -#define STACK_REFPLUS 1 - -#ifdef SLP_EVAL - -#define STACK_MAGIC 3 - -#define REGS_TO_SAVE "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", \ - "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", "r31", \ - "cr2", "cr3", "cr4" -static int -slp_switch(void) -{ - register int *stackref, stsizediff; - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ ("mr %0, 1" : "=g" (stackref) : ); - { - SLP_SAVE_STATE(stackref, stsizediff); - __asm__ volatile ( - "mr 11, %0\n" - "add 1, 1, 11\n" - "add 30, 30, 11\n" - : /* no outputs */ - : "g" (stsizediff) - : "11" - ); - SLP_RESTORE_STATE(); - return 0; - } - __asm__ volatile ("" : : : REGS_TO_SAVE); -} - -#endif - -/* - * further self-processing support - */ - -/* - * if you want to add self-inspection tools, place them - * here. See the x86_msvc for the necessary defines. - * These features are highly experimental und not - * essential yet. - */ diff --git a/Stackless/platf/switch_ps3_SNTools.h b/Stackless/platf/switch_ps3_SNTools.h deleted file mode 100644 index ac305729b577ea..00000000000000 --- a/Stackless/platf/switch_ps3_SNTools.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Dummy transfer function for PS3 - * Sets us up to use the assembler, which is in switch_ps3_SNTools.asm - */ - -#include - -#define STACK_REFPLUS 1 -#define STACK_MAGIC 0 /* in the assembler, we grab the stack pointer directly */ - -#define EXTERNAL_ASM - -/* use the c stack sparingly. No initial gap, and invoke stack spilling at 16k */ -#define CSTACK_GOODGAP 0 -#define CSTACK_WATERMARK (16*1024/sizeof(intptr_t)) \ No newline at end of file diff --git a/Stackless/platf/switch_ps3_SNTools.s b/Stackless/platf/switch_ps3_SNTools.s deleted file mode 100644 index 8597541b32731f..00000000000000 --- a/Stackless/platf/switch_ps3_SNTools.s +++ /dev/null @@ -1,228 +0,0 @@ -# -# Switching code for PS3. -# Save everyting that isn't volatile -# See the PPU ABI Specs for CELL OS lv-2, Low Level system information for details -# Kristjan Valur Jonsson, March 2010 -# - -###CODE SECTION### -.text - -##MAIN ENTRY POINT -.globl .slp_switch -.type .slp_switch,@function -.slp_switch: - #Save all 18 volatile GP registers, 18 volatile FP regs, and 12 volatile vector regs - #We need a stack frame of 144 bytes for FPR, 144 bytes for GPR, 192 bytes for VR - #plus 48 bytes for the standard stackframe = 528 bytes (which is quadword dividable) - mflr %r0 # Move LR into r0 - subi %r12,%r1,144 # Set r12 to general register save area (8*18) - bl _savegpr1_14 # Call routine to save general registers - bl _savefpr_14 # Call routine to save floating-point registers - subi %r0,%r1, 288 # Set r0 to first address beyond vector save area - bl _savevr_20 # Call routine to save vector registers - stdu %r1,(-528)(%r1) # Create stack frame - - #(save CR if necessary) - mfcr %r12 - std %r12,8(%r1) #save it in the condition save area - - #Call slp_save_state with stack pointer - addi %r3,%r1,0 # move stack pointer to r2 - bl .slp_save_state - nop# - extsw %r3,%r3 # extend sign bit to 64 - - #check the low order bit - addi %r4,%r0,1 # load 1 - and %r4,%r3,%r4 # and - cmpi 0,%r4,0 - bne- 0, NO_RESTORE # don't restore the stack (branch usually not taken) - - add %r1,%r1,%r3 # adjust stack pointer - - #Restore stack. - #now, we have to be careful. The function call will store the link register - #in the current frame (as the ABI) dictates. But it will then trample it - #with the restore! We fix this by creating a fake stack frame - stdu %r1,(-48)(%r1) # Create fake stack frame for the link register storage - bl .slp_restore_state # Restore state into new stack - nop# - addi %sp,%sp,48 # restore proper stack frame - - addi %r3,%r0,0 # set the return value (0) - - #restore the condition register - ld %r12,8(%r1) - mtcrf 0xff, %r12 - - #restore stack pointer - addi %sp,%sp,528 - - #restore vector registers - subi %r0,%r1, 288 # Set r0 to first address beyond vector save area - bl _savevr_20 # Call routine to save vector registers - - #restore general purporse registers - subi %r12,%r1,144 # Set r12 to general register save area (8*18) - bl _restgpr1_14 # Restore general registers - b _restfpr_14 # Restore floating point regs and return - -NO_RESTORE: - #are we -1 (error)? - cmpi 0,%r3,-1 - beq- 0, RETURN_NO_RESTORE # return with the -1 already in r3 - - #no error - addi %r3,%r0,0 - -RETURN_NO_RESTORE: - addi %sp,%sp,528 - ld %r0, 16(%r1) - mtlr %r0 - blr - - -#Stack saving and restoring functions from the ABI documentation. The ABI states that these should -#be part of the ABI, but the linker refuses to find them :) - -_savegpr1_14: std %r14,-144(%r12) -_savegpr1_15: std %r15,-136(%r12) -_savegpr1_16: std %r16,-128(%r12) -_savegpr1_17: std %r17,-120(%r12) -_savegpr1_18: std %r18,-112(%r12) -_savegpr1_19: std %r19,-104(%r12) -_savegpr1_20: std %r20,-96(%r12) -_savegpr1_21: std %r21,-88(%r12) -_savegpr1_22: std %r22,-80(%r12) -_savegpr1_23: std %r23,-72(%r12) -_savegpr1_24: std %r24,-64(%r12) -_savegpr1_25: std %r25,-56(%r12) -_savegpr1_26: std %r26,-48(%r12) -_savegpr1_27: std %r27,-40(%r12) -_savegpr1_28: std %r28,-32(%r12) -_savegpr1_29: std %r29,-24(%r12) -_savegpr1_30: std %r30,-16(%r12) -_savegpr1_31: std %r31,-8(%r12) - blr - -_restgpr1_14: ld %r14,-144(%r12) -_restgpr1_15: ld %r15,-136(%r12) -_restgpr1_16: ld %r16,-128(%r12) -_restgpr1_17: ld %r17,-120(%r12) -_restgpr1_18: ld %r18,-112(%r12) -_restgpr1_19: ld %r19,-104(%r12) -_restgpr1_20: ld %r20,-96(%r12) -_restgpr1_21: ld %r21,-88(%r12) -_restgpr1_22: ld %r22,-80(%r12) -_restgpr1_23: ld %r23,-72(%r12) -_restgpr1_24: ld %r24,-64(%r12) -_restgpr1_25: ld %r25,-56(%r12) -_restgpr1_26: ld %r26,-48(%r12) -_restgpr1_27: ld %r27,-40(%r12) -_restgpr1_28: ld %r28,-32(%r12) -_restgpr1_29: ld %r29,-24(%r12) -_restgpr1_30: ld %r30,-16(%r12) -_restgpr1_31: ld %r31,-8(%r12) - blr - -_savefpr_14: stfd %f14,-144(%r1) -_savefpr_15: stfd %f15,-136(%r1) -_savefpr_16: stfd %f16,-128(%r1) -_savefpr_17: stfd %f17,-120(%r1) -_savefpr_18: stfd %f18,-112(%r1) -_savefpr_19: stfd %f19,-104(%r1) -_savefpr_20: stfd %f20,-96(%r1) -_savefpr_21: stfd %f21,-88(%r1) -_savefpr_22: stfd %f22,-80(%r1) -_savefpr_23: stfd %f23,-72(%r1) -_savefpr_24: stfd %f24,-64(%r1) -_savefpr_25: stfd %f25,-56(%r1) -_savefpr_26: stfd %f26,-48(%r1) -_savefpr_27: stfd %f27,-40(%r1) -_savefpr_28: stfd %f28,-32(%r1) -_savefpr_29: stfd %f29,-24(%r1) -_savefpr_30: stfd %f30,-16(%r1) -_savefpr_31: stfd %f31,-8(%r1) - std %r0, 16(%r1) - blr - -_restfpr_14: lfd %f14,-144(%r1) -_restfpr_15: lfd %f15,-136(%r1) -_restfpr_16: lfd %f16,-128(%r1) -_restfpr_17: lfd %f17,-120(%r1) -_restfpr_18: lfd %f18,-112(%r1) -_restfpr_19: lfd %f19,-104(%r1) -_restfpr_20: lfd %f20,-96(%r1) -_restfpr_21: lfd %f21,-88(%r1) -_restfpr_22: lfd %f22,-80(%r1) -_restfpr_23: lfd %f23,-72(%r1) -_restfpr_24: lfd %f24,-64(%r1) -_restfpr_25: lfd %f25,-56(%r1) -_restfpr_26: lfd %f26,-48(%r1) -_restfpr_27: lfd %f27,-40(%r1) -_restfpr_28: lfd %f28,-32(%r1) -_restfpr_29: ld %r0, 16(%r1) - lfd %f29,-24(%r1) - mtlr %r0 - lfd %f30,-16(%r1) - lfd %f31,-8(%r1) - blr -_restfpr_30: lfd %f30,-16(%r1) -_restfpr_31: ld %r0, 16(%r1) - lfd %f31,-8(%r1) - mtlr %r0 - blr - -_savevr_20: addi %r12,%r0,-192 - stvx %v20,%r12,%r0 -_savevr_21: addi %r12,%r0,-176 - stvx %v21,%r12,%r0 -_savevr_22: addi %r12,%r0,-160 - stvx %v22,%r12,%r0 -_savevr_23: addi %r12,%r0,-144 - stvx %v23,%r12,%r0 -_savevr_24: addi %r12,%r0,-128 - stvx %v24,%r12,%r0 -_savevr_25: addi %r12,%r0,-112 - stvx %v25,%r12,%r0 -_savevr_26: addi %r12,%r0,-96 - stvx %v26,%r12,%r0 -_savevr_27: addi %r12,%r0,-80 - stvx %v27,%r12,%r0 -_savevr_28: addi %r12,%r0,-64 - stvx %v28,%r12,%r0 -_savevr_29: addi %r12,%r0,-48 - stvx %v29,%r12,%r0 -_savevr_30: addi %r12,%r0,-32 - stvx %v30,%r12,%r0 -_savevr_31: addi %r12,%r0,-16 - stvx %v31,%r12,%r0 - blr - -_restvr_20: addi %r12,%r0,-192 - lvx %v20,%r12,%r0 -_restvr_21: addi %r12,%r0,-176 - lvx %v21,%r12,%r0 -_restvr_22: addi %r12,%r0,-160 - lvx %v22,%r12,%r0 -_restvr_23: addi %r12,%r0,-144 - lvx %v23,%r12,%r0 -_restvr_24: addi %r12,%r0,-128 - lvx %v24,%r12,%r0 -_restvr_25: addi %r12,%r0,-112 - lvx %v25,%r12,%r0 -_restvr_26: addi %r12,%r0,-96 - lvx %v26,%r12,%r0 -_restvr_27: addi %r12,%r0,-80 - lvx %v27,%r12,%r0 -_restvr_28: addi %r12,%r0,-64 - lvx %v28,%r12,%r0 -_restvr_29: addi %r12,%r0,-48 - lvx %v29,%r12,%r0 -_restvr_30: addi %r12,%r0,-32 - lvx %v30,%r12,%r0 -_restvr_31: addi %r12,%r0,-16 - lvx %v31,%r12,%r0 - blr - diff --git a/Stackless/platf/switch_s390_unix.h b/Stackless/platf/switch_s390_unix.h deleted file mode 100644 index 8b8a1e363aab34..00000000000000 --- a/Stackless/platf/switch_s390_unix.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 24-Nov-02 Christian Tismer - * needed to add another magic constant to insure - * that f in slp_eval_frame(PyFrameObject *f) - * gets included into the saved stack area. - * STACK_REFPLUS will probably be 1 in most cases. - * 06-Oct-02 Gustavo Niemeyer - * Ported to Linux/S390. - */ - -#define STACK_REFPLUS 1 - -#ifdef SLP_EVAL - -#define STACK_MAGIC 0 - -#define REGS_TO_SAVE "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r14", \ - "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", \ - "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15" - -static int -slp_switch(void) -{ - register int *stackref, stsizediff; - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ ("lr %0, 15" : "=g" (stackref) : ); - { - SLP_SAVE_STATE(stackref, stsizediff); - __asm__ volatile ( - "ar 15, %0" - : /* no outputs */ - : "g" (stsizediff) - ); - SLP_RESTORE_STATE(); - return 0; - } - __asm__ volatile ("" : : : REGS_TO_SAVE); -} - -#endif - -/* - * further self-processing support - */ - -/* - * if you want to add self-inspection tools, place them - * here. See the x86_msvc for the necessary defines. - * These features are highly experimental und not - * essential yet. - */ diff --git a/Stackless/platf/switch_sparc_sun_gcc.h b/Stackless/platf/switch_sparc_sun_gcc.h deleted file mode 100644 index 449e49faebfc9d..00000000000000 --- a/Stackless/platf/switch_sparc_sun_gcc.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 24-Nov-02 Christian Tismer - * needed to add another magic constant to insure - * that f in slp_eval_frame(PyFrameObject *f) - * gets included into the saved stack area. - * STACK_REFPLUS will probably be 1 in most cases. - * 17-Sep-02 Christian Tismer - * after virtualizing stack save/restore, the - * stack size shrunk a bit. Needed to introduce - * an adjustment STACK_MAGIC per platform. - * 15-Sep-02 Gerd Woetzel - * added support for SunOS sparc with gcc - */ - -#define STACK_REFPLUS 1 - -#ifdef SLP_EVAL - -#include - -#define STACK_MAGIC 0 - -static int -slp_switch(void) -{ - register int *stackref, stsizediff; - - /* Put the stack pointer into stackref */ - - /* Sparc special: at first, flush register windows - */ - __asm__ volatile ( - "ta %1\n\t" - "mov %%sp, %0" - : "=r" (stackref) : "i" (ST_FLUSH_WINDOWS)); - - { /* You shalt put SLP_SAVE_STATE into a local block */ - - SLP_SAVE_STATE(stackref, stsizediff); - - /* Increment stack and frame pointer by stsizediff */ - - /* Sparc special: at first load new return address. - This cannot be done later, because the stack - might be overwritten again just after SLP_RESTORE_STATE - has finished. BTW: All other registers (l0-l7 and i0-i5) - might be clobbered too. - */ - __asm__ volatile ( - "ld [%0+60], %%i7\n\t" - "add %1, %%sp, %%sp\n\t" - "add %1, %%fp, %%fp" - : : "r" (_cst->stack), "r" (stsizediff) - : "%l0", "%l1", "%l2", "%l3", "%l4", "%l5", "%l6", "%l7", - "%i0", "%i1", "%i2", "%i3", "%i4", "%i5"); - - SLP_RESTORE_STATE(); - - /* Run far away as fast as possible, don't look back at the sins. - * The LORD rained down burning sulfur on Sodom and Gomorra ... - */ - - /* Sparc special: Must make it *very* clear to the CPU that - it shouldn't look back into the register windows - */ - __asm__ volatile ( "ta %0" : : "i" (ST_CLEAN_WINDOWS)); - return 0; - } -} - -#endif - -/* - * further self-processing support - */ - -/* - * if you want to add self-inspection tools, place them - * here. See the x86_msvc for the necessary defines. - * These features are highly experimental und not - * essential yet. - */ diff --git a/Stackless/platf/switch_x64_masm.asm b/Stackless/platf/switch_x64_masm.asm deleted file mode 100644 index 5170157bc585cb..00000000000000 --- a/Stackless/platf/switch_x64_masm.asm +++ /dev/null @@ -1,105 +0,0 @@ -; -; stack switching code for MASM on x641 -; Kristjan Valur Jonsson, sept 2005 -; - - -;prototypes for our calls -slp_save_state PROTO -slp_restore_state PROTO - - -pushxmm MACRO reg - sub rsp, 16 - .allocstack 16 - movaps [rsp], reg ; faster than movups, but we must be aligned - ; .savexmm128 reg, offset (don't know what offset is, no documentation) -ENDM -popxmm MACRO reg - movaps reg, [rsp] ; faster than movups, but we must be aligned - add rsp, 16 -ENDM - -pushreg MACRO reg - push reg - .pushreg reg -ENDM -popreg MACRO reg - pop reg -ENDM - - -.code -slp_switch PROC FRAME - ;realign stack to 16 bytes after return address push, makes the following faster - sub rsp,8 - .allocstack 8 - - pushxmm xmm15 - pushxmm xmm14 - pushxmm xmm13 - pushxmm xmm12 - pushxmm xmm11 - pushxmm xmm10 - pushxmm xmm9 - pushxmm xmm8 - pushxmm xmm7 - pushxmm xmm6 - - pushreg r15 - pushreg r14 - pushreg r13 - pushreg r12 - - pushreg rbp - pushreg rbx - pushreg rdi - pushreg rsi - - sub rsp, 10h ;allocate the singlefunction argument (must be multiple of 16) - .allocstack 10h -.endprolog - - lea rcx, [rsp+10h] ;load stack base that we are saving - call slp_save_state ;pass stackpointer, return offset in eax - test rax, 1 ; an odd value means that we don't restore - jnz NORESTORE - ;actual stack switch: - add rsp, rax - call slp_restore_state - xor rax, rax ;return 0 - -EXIT: - - add rsp, 10h - popreg rsi - popreg rdi - popreg rbx - popreg rbp - - popreg r12 - popreg r13 - popreg r14 - popreg r15 - - popxmm xmm6 - popxmm xmm7 - popxmm xmm8 - popxmm xmm9 - popxmm xmm10 - popxmm xmm11 - popxmm xmm12 - popxmm xmm13 - popxmm xmm14 - popxmm xmm15 - - add rsp, 8 - ret - -NORESTORE: - sar rax, 1 ; return value is -1 for error - jmp EXIT - -slp_switch ENDP - -END \ No newline at end of file diff --git a/Stackless/platf/switch_x64_msvc.h b/Stackless/platf/switch_x64_msvc.h deleted file mode 100644 index da413958d31e30..00000000000000 --- a/Stackless/platf/switch_x64_msvc.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 24-Nov-02 Christian Tismer - * needed to add another magic constant to insure - * that f in slp_eval_frame(PyFrameObject *f) - * STACK_REFPLUS will probably be 1 in most cases. - * gets included into the saved stack area. - * 26-Sep-02 Christian Tismer - * again as a result of virtualized stack access, - * the compiler used less registers. Needed to - * explicit mention registers in order to get them saved. - * Thanks to Jeff Senn for pointing this out and help. - * 17-Sep-02 Christian Tismer - * after virtualizing stack save/restore, the - * stack size shrunk a bit. Needed to introduce - * an adjustment STACK_MAGIC per platform. - * 15-Sep-02 Gerd Woetzel - * slightly changed framework for sparc - * 01-Mar-02 Christian Tismer - * Initial final version after lots of iterations for i386. - */ - -#define alloca _alloca - -#define STACK_REFPLUS 1 -#define STACK_MAGIC 0 - -/* Use the generic support for an external assembly language slp_switch function. */ -#define EXTERNAL_ASM - -#ifdef SLP_EVAL -/* This always uses the external masm assembly file. */ -#endif - -/* - * further self-processing support - */ - -/* we have IsBadReadPtr available, so we can peek at objects */ -#define STACKLESS_SPY - -#ifdef IMPLEMENT_STACKLESSMODULE -#include "Windows.h" -#define CANNOT_READ_MEM(p, bytes) IsBadReadPtr(p, bytes) - -static int IS_ON_STACK(void*p) -{ - int stackref; - intptr_t stackbase = ((intptr_t)&stackref) & 0xfffff000; - return (intptr_t)p >= stackbase && (intptr_t)p < stackbase + 0x00100000; -} - -#endif diff --git a/Stackless/platf/switch_x86_msvc.h b/Stackless/platf/switch_x86_msvc.h deleted file mode 100644 index bbb376733e3ef3..00000000000000 --- a/Stackless/platf/switch_x86_msvc.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 24-Nov-02 Christian Tismer - * needed to add another magic constant to insure - * that f in slp_eval_frame(PyFrameObject *f) - * gets included into the saved stack area. - * STACK_REFPLUS will probably be 1 in most cases. - * 26-Sep-02 Christian Tismer - * again as a result of virtualized stack access, - * the compiler used less registers. Needed to - * explicit mention registers in order to get them saved. - * Thanks to Jeff Senn for pointing this out and help. - * 17-Sep-02 Christian Tismer - * after virtualizing stack save/restore, the - * stack size shrunk a bit. Needed to introduce - * an adjustment STACK_MAGIC per platform. - * 15-Sep-02 Gerd Woetzel - * slightly changed framework for sparc - * 01-Mar-02 Christian Tismer - * Initial final version after lots of iterations for i386. - */ - -/* for the SEH things */ -#ifndef _WINDOWS_ -#define WIN32_LEAN_AND_MEAN -#ifdef BYTE -#undef BYTE -#endif -#ifdef Yield -#undef Yield /* remove definition from Python_ast.h to avoid conflict */ -#endif -#include -#endif -#define _SEH32 - -#define alloca _alloca - -#define STACK_REFPLUS 1 - -/* use faster oparg fetch */ -#define STACKLESS_USE_ENDIAN - -/* switching related stuff */ -#ifdef SLP_EVAL - -#define STACK_MAGIC 0 - -#pragma optimize("", off) - -#pragma warning(disable:4731) /* disable ebp modification warning */ -static int -slp_switch(void) -{ - register int *stackref, stsizediff; - __asm mov stackref, esp; - /* modify EBX, ESI and EDI in order to get them preserved */ - __asm mov ebx, ebx; - __asm xchg esi, edi; - { - SLP_SAVE_STATE(stackref, stsizediff); - __asm { - mov eax, stsizediff - add esp, eax - add ebp, eax - } - SLP_RESTORE_STATE(); - return 0; - } -#pragma warning(default:4731) -} - -#endif - -/* - * further self-processing support - */ - -/* we have IsBadReadPtr available, so we can peek at objects */ -#define STACKLESS_SPY - -#ifdef IMPLEMENT_STACKLESSMODULE -#include "Windows.h" -#define CANNOT_READ_MEM(p, bytes) IsBadReadPtr(p, bytes) - -static int IS_ON_STACK(void*p) -{ - int stackref; - int stackbase = ((int)&stackref) & 0xfffff000; - return (int)p >= stackbase && (int)p < stackbase + 0x00100000; -} - -#endif diff --git a/Stackless/platf/switch_x86_unix.h b/Stackless/platf/switch_x86_unix.h deleted file mode 100644 index 6668e659bb5ff3..00000000000000 --- a/Stackless/platf/switch_x86_unix.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 24-Nov-02 Christian Tismer - * needed to add another magic constant to insure - * that f in slp_eval_frame(PyFrameObject *f) - * gets included into the saved stack area. - * STACK_REFPLUS will probably be 1 in most cases. - * 17-Sep-02 Christian Tismer - * after virtualizing stack save/restore, the - * stack size shrunk a bit. Needed to introduce - * an adjustment STACK_MAGIC per platform. - * 15-Sep-02 Gerd Woetzel - * slightly changed framework for spark - * 31-Avr-02 Armin Rigo - * Added ebx, esi and edi register-saves. - * 01-Mar-02 Samual M. Rushing - * Ported from i386. - */ - -#define STACK_REFPLUS 1 - -#ifdef SLP_EVAL - -/* #define STACK_MAGIC 3 */ -/* the above works fine with gcc 2.96, but 2.95.3 wants this */ -#define STACK_MAGIC 0 - -static int -slp_switch(void) -{ - register int *stackref, stsizediff; -#if STACKLESS_FRHACK - __asm__ volatile ("" : : : "esi", "edi"); -#else - __asm__ volatile ("" : : : "ebx", "esi", "edi"); -#endif - __asm__ ("movl %%esp, %0" : "=g" (stackref)); - { - SLP_SAVE_STATE(stackref, stsizediff); - __asm__ volatile ( - "addl %0, %%esp\n" - "addl %0, %%ebp\n" - : - : "r" (stsizediff) - ); - SLP_RESTORE_STATE(); - return 0; - } -#if STACKLESS_FRHACK - __asm__ volatile ("" : : : "esi", "edi"); -#else - __asm__ volatile ("" : : : "ebx", "esi", "edi"); -#endif -} - - -#endif - -/* - * further self-processing support - */ - -/* - * if you want to add self-inspection tools, place them - * here. See the x86_msvc for the necessary defines. - * These features are highly experimental und not - * essential yet. - */ From 561f49ae06dc108deeb8c3bba99e33989448589d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Mon, 29 Jul 2013 16:25:47 +0000 Subject: [PATCH 31/35] Create new branch From d5d606ab0d3e2776e9a2761eac790491ef66b630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Mon, 29 Jul 2013 16:52:20 +0000 Subject: [PATCH 32/35] fix incorrect merge --- Stackless/core/stacklesseval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Stackless/core/stacklesseval.c b/Stackless/core/stacklesseval.c index ff41a0c9bdcac9..183e395b8beba7 100644 --- a/Stackless/core/stacklesseval.c +++ b/Stackless/core/stacklesseval.c @@ -128,7 +128,7 @@ void slp_kill_tasks_with_stacks(PyThreadState *target_ts) chain = &t; SLP_CHAIN_REMOVE(PyTaskletObject, chain, tmp, next, prev); t = tmp; - ts.st.runcount--; + ts->st.runcount--; } else Py_INCREF(t); /* a new reference for the runnable queue */ /* insert into the 'current' chain without modifying 'current' */ From 2f8941a9296576661af34f5a1fa4e990538fa1fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Sun, 18 Aug 2013 12:11:11 +0000 Subject: [PATCH 33/35] Simplify the main tasklet handling. Instead of having to deal with soft-swiching of the main tasklet, and the consequence of the main tasklet being executed on a different stack when it exits, it is much simpler to simply force hard switching for the main tasklet. This means that the main tasklet always runs on the stack provided by the caller into the python library, including whatever stack exception handlers they provided. This removes the need to have the main tealet suspended while stackless runs. --- Stackless/core/stackless_tealet.c | 25 +------------------------ Stackless/core/stackless_tealet.h | 1 - Stackless/core/stacklesseval.c | 4 +--- Stackless/module/scheduling.c | 9 +++++++++ 4 files changed, 11 insertions(+), 28 deletions(-) diff --git a/Stackless/core/stackless_tealet.c b/Stackless/core/stackless_tealet.c index 4a9b710dba2824..a1e1ddd5e33d29 100644 --- a/Stackless/core/stackless_tealet.c +++ b/Stackless/core/stackless_tealet.c @@ -231,30 +231,7 @@ slp_run_initial_stub(PyThreadState *ts, tealet_run_t func, void **arg) return 0; } -/* run the top level loop from the main tasklet. This invocation expects - * a return value - */ -PyObject * -slp_run_stub_from_main(PyThreadState *ts) -{ - int result; - void *arg; - tealet_t *old_main; - - /* switch into a stub duplicate. Run evaluation loop. Then switch back. - * Set the "main" to be us, so that a switch out of the tasklet_stub_func - * lands us here - */ - old_main = ts->st.tealet_main; - ts->st.tealet_main = tealet_current(old_main); - result = slp_run_initial_stub(ts, &tasklet_stub_func, &arg); - ts->st.tealet_main = old_main; - if (result) - return NULL; - return (PyObject*)arg; -} - -/* call the top level loop as a means of startin a new such loop, hardswitching out +/* call the top level loop as a means of starting a new such loop, hardswitching out * of a tasklet. This invocation does not expect a return value */ int diff --git a/Stackless/core/stackless_tealet.h b/Stackless/core/stackless_tealet.h index 3c6e8a3e9622be..1baf871ba5fc64 100644 --- a/Stackless/core/stackless_tealet.h +++ b/Stackless/core/stackless_tealet.h @@ -19,7 +19,6 @@ PyTealet_data *slp_tealet_list; /* head of the tealet list */ int slp_make_initial_stub(PyThreadState *ts); void slp_destroy_initial_stub(PyThreadState *ts); int slp_run_initial_stub(PyThreadState *ts, tealet_run_t func, void **arg); -PyObject * slp_run_stub_from_main(PyThreadState *ts); int slp_run_stub_from_worker(PyThreadState *ts); void slp_tealet_cleanup(PyThreadState *ts); diff --git a/Stackless/core/stacklesseval.c b/Stackless/core/stacklesseval.c index 183e395b8beba7..ec330be2574fac 100644 --- a/Stackless/core/stacklesseval.c +++ b/Stackless/core/stacklesseval.c @@ -68,9 +68,7 @@ slp_eval_frame(PyFrameObject *f) PyObject *result; if (slp_make_initial_stub(ts)) return NULL; - ts->frame = f; - result = slp_run_stub_from_main(ts); - return result; + return slp_run_tasklet(f); } Py_INCREF(Py_None); diff --git a/Stackless/module/scheduling.c b/Stackless/module/scheduling.c index 90de716ac1661a..9a0b56e700b8ee 100644 --- a/Stackless/module/scheduling.c +++ b/Stackless/module/scheduling.c @@ -838,6 +838,15 @@ slp_schedule_task_prepared(PyThreadState *ts, PyObject **result, PyTaskletObject if (!stackless || ts->st.nesting_level != 0) goto hard_switching; + /* We never soft switch out of the main tasklet. Otherwise, we might end up soft switching + * back to the mani tasklet from another stack, but that screws up with assumptions + * of the caller. e.g. the main tasklet might make callbacks back into the program, but + * it really shouln't do that from a different stack. For example, the main stack might + * have structured exception handlers installed. + */ + if (prev == ts->st.main) + goto hard_switching; + /* start of soft switching code */ /* handle exception */ From 5e0532680aac710b11236cba7f61f71e9af55a92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Tue, 3 Sep 2013 20:02:52 +0000 Subject: [PATCH 34/35] Cosmetic cleanup after merge Unittest test_thread are broken here and need fixing. --- Stackless/unittests/test_miscell.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/Stackless/unittests/test_miscell.py b/Stackless/unittests/test_miscell.py index ae453abbc8562c..91c34410fa4088 100644 --- a/Stackless/unittests/test_miscell.py +++ b/Stackless/unittests/test_miscell.py @@ -608,8 +608,6 @@ def testTwo(self): c = stackless.channel() def func1(): stackless.test_cstate(func2) # force hard switch - - def func2(): c.receive() t1 = stackless.tasklet(func1)() @@ -624,7 +622,6 @@ def func2(): self.assertEqual(after, before) - #/////////////////////////////////////////////////////////////////////////////// if __name__ == '__main__': From acdc04433f99eecca258a024ff6808fc493052b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Tue, 3 Sep 2013 20:45:04 +0000 Subject: [PATCH 35/35] Fix merge. Tasklet's thread state is now in task->tstate --- Stackless/module/taskletobject.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Stackless/module/taskletobject.c b/Stackless/module/taskletobject.c index 4685e52d7e62b6..13c739c732388c 100644 --- a/Stackless/module/taskletobject.c +++ b/Stackless/module/taskletobject.c @@ -487,7 +487,7 @@ static TASKLET_INSERT_HEAD(impl_tasklet_insert) if (task->next == NULL) { if (task->f.frame == NULL && task != ts->st.current) RUNTIME_ERROR("You cannot run an unbound(dead) tasklet", -1); - if (task->cstate->tstate->st.main == NULL) + if (task->tstate->st.main == NULL) RUNTIME_ERROR("Target thread isn't initialized", -1); Py_INCREF(task); slp_current_insert(task); @@ -555,7 +555,7 @@ static TASKLET_RUN_HEAD(impl_tasklet_run) if (ts->st.main == NULL) return PyTasklet_Run_M(task); inserted = task->next == NULL; - if (ts == task->cstate->tstate) { + if (ts == task->tstate) { /* same thread behaviour. Insert at the end of the queue and then * switch to that task. Notice that this behaviour upsets FIFO * order @@ -563,7 +563,7 @@ static TASKLET_RUN_HEAD(impl_tasklet_run) fail = impl_tasklet_insert(task); } else { /* interthread. */ - PyThreadState *rts = task->cstate->tstate; + PyThreadState *rts = task->tstate; PyTaskletObject *current = rts->st.current; if (rts->st.thread.is_idle) { /* remote thread is blocked, or unblocked and hasn't got the GIL yet.