first add files

This commit is contained in:
2023-10-08 20:59:00 +08:00
parent b494be364b
commit 1dac226337
991 changed files with 368151 additions and 40 deletions

63
lib/greenlet/__init__.py Normal file
View File

@@ -0,0 +1,63 @@
# -*- coding: utf-8 -*-
"""
The root of the greenlet package.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
__all__ = [
'__version__',
'_C_API',
'GreenletExit',
'error',
'getcurrent',
'greenlet',
'gettrace',
'settrace',
]
# pylint:disable=no-name-in-module
###
# Metadata
###
__version__ = '1.1.3'
from ._greenlet import _C_API # pylint:disable=no-name-in-module
###
# Exceptions
###
from ._greenlet import GreenletExit
from ._greenlet import error
###
# greenlets
###
from ._greenlet import getcurrent
from ._greenlet import greenlet
###
# tracing
###
try:
from ._greenlet import gettrace
from ._greenlet import settrace
except ImportError:
# Tracing wasn't supported.
# XXX: The option to disable it was removed in 1.0,
# so this branch should be dead code.
pass
###
# Constants
# These constants aren't documented and aren't recommended.
# In 1.0, USE_GC and USE_TRACING are always true, and USE_CONTEXT_VARS
# is the same as ``sys.version_info[:2] >= 3.7``
###
from ._greenlet import GREENLET_USE_CONTEXT_VARS # pylint:disable=unused-import
from ._greenlet import GREENLET_USE_GC # pylint:disable=unused-import
from ._greenlet import GREENLET_USE_TRACING # pylint:disable=unused-import

Binary file not shown.

2170
lib/greenlet/greenlet.c Normal file

File diff suppressed because it is too large Load Diff

161
lib/greenlet/greenlet.h Normal file
View File

@@ -0,0 +1,161 @@
/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
/* Greenlet object interface */
#ifndef Py_GREENLETOBJECT_H
#define Py_GREENLETOBJECT_H
#include <Python.h>
#ifdef __cplusplus
extern "C" {
#endif
/* This is deprecated and undocumented. It does not change. */
#define GREENLET_VERSION "1.0.0"
#if PY_VERSION_HEX >= 0x30B00A6
# define GREENLET_PY311 1
/* _PyInterpreterFrame moved to the internal C API in Python 3.11 */
# include <internal/pycore_frame.h>
#else
# define GREENLET_PY311 0
# define _PyCFrame CFrame
#endif
typedef struct _greenlet {
PyObject_HEAD
char* stack_start;
char* stack_stop;
char* stack_copy;
intptr_t stack_saved;
struct _greenlet* stack_prev;
struct _greenlet* parent;
PyObject* run_info;
struct _frame* top_frame;
int recursion_depth;
#if GREENLET_PY311
_PyInterpreterFrame *current_frame;
_PyStackChunk *datastack_chunk;
PyObject **datastack_top;
PyObject **datastack_limit;
#endif
PyObject* weakreflist;
#if PY_VERSION_HEX >= 0x030700A3
_PyErr_StackItem* exc_info;
_PyErr_StackItem exc_state;
#else
PyObject* exc_type;
PyObject* exc_value;
PyObject* exc_traceback;
#endif
PyObject* dict;
#if PY_VERSION_HEX >= 0x030700A3
PyObject* context;
#endif
#if PY_VERSION_HEX >= 0x30A00B1
_PyCFrame* cframe;
#endif
} PyGreenlet;
#define PyGreenlet_Check(op) PyObject_TypeCheck(op, &PyGreenlet_Type)
#define PyGreenlet_MAIN(op) (((PyGreenlet*)(op))->stack_stop == (char*)-1)
#define PyGreenlet_STARTED(op) (((PyGreenlet*)(op))->stack_stop != NULL)
#define PyGreenlet_ACTIVE(op) (((PyGreenlet*)(op))->stack_start != NULL)
#define PyGreenlet_GET_PARENT(op) (((PyGreenlet*)(op))->parent)
/* C API functions */
/* Total number of symbols that are exported */
#define PyGreenlet_API_pointers 8
#define PyGreenlet_Type_NUM 0
#define PyExc_GreenletError_NUM 1
#define PyExc_GreenletExit_NUM 2
#define PyGreenlet_New_NUM 3
#define PyGreenlet_GetCurrent_NUM 4
#define PyGreenlet_Throw_NUM 5
#define PyGreenlet_Switch_NUM 6
#define PyGreenlet_SetParent_NUM 7
#ifndef GREENLET_MODULE
/* This section is used by modules that uses the greenlet C API */
static void** _PyGreenlet_API = NULL;
# define PyGreenlet_Type \
(*(PyTypeObject*)_PyGreenlet_API[PyGreenlet_Type_NUM])
# define PyExc_GreenletError \
((PyObject*)_PyGreenlet_API[PyExc_GreenletError_NUM])
# define PyExc_GreenletExit \
((PyObject*)_PyGreenlet_API[PyExc_GreenletExit_NUM])
/*
* PyGreenlet_New(PyObject *args)
*
* greenlet.greenlet(run, parent=None)
*/
# define PyGreenlet_New \
(*(PyGreenlet * (*)(PyObject * run, PyGreenlet * parent)) \
_PyGreenlet_API[PyGreenlet_New_NUM])
/*
* PyGreenlet_GetCurrent(void)
*
* greenlet.getcurrent()
*/
# define PyGreenlet_GetCurrent \
(*(PyGreenlet * (*)(void)) _PyGreenlet_API[PyGreenlet_GetCurrent_NUM])
/*
* PyGreenlet_Throw(
* PyGreenlet *greenlet,
* PyObject *typ,
* PyObject *val,
* PyObject *tb)
*
* g.throw(...)
*/
# define PyGreenlet_Throw \
(*(PyObject * (*)(PyGreenlet * self, \
PyObject * typ, \
PyObject * val, \
PyObject * tb)) \
_PyGreenlet_API[PyGreenlet_Throw_NUM])
/*
* PyGreenlet_Switch(PyGreenlet *greenlet, PyObject *args)
*
* g.switch(*args, **kwargs)
*/
# define PyGreenlet_Switch \
(*(PyObject * \
(*)(PyGreenlet * greenlet, PyObject * args, PyObject * kwargs)) \
_PyGreenlet_API[PyGreenlet_Switch_NUM])
/*
* PyGreenlet_SetParent(PyObject *greenlet, PyObject *new_parent)
*
* g.parent = new_parent
*/
# define PyGreenlet_SetParent \
(*(int (*)(PyGreenlet * greenlet, PyGreenlet * nparent)) \
_PyGreenlet_API[PyGreenlet_SetParent_NUM])
/* Macro that imports greenlet and initializes C API */
/* NOTE: This has actually moved to ``greenlet._greenlet._C_API``, but we
keep the older definition to be sure older code that might have a copy of
the header still works. */
# define PyGreenlet_Import() \
{ \
_PyGreenlet_API = (void**)PyCapsule_Import("greenlet._C_API", 0); \
}
#endif /* GREENLET_MODULE */
#ifdef __cplusplus
}
#endif
#endif /* !Py_GREENLETOBJECT_H */

View File

@@ -0,0 +1,2 @@
call "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\vcvarsall.bat" amd64
ml64 /nologo /c /Fo switch_x64_masm.obj switch_x64_masm.asm

View File

@@ -0,0 +1,69 @@
/*
* this is the internal transfer function.
*
* HISTORY
* 07-Sep-16 Add clang support using x register naming. Fredrik Fornwall
* 13-Apr-13 Add support for strange GCC caller-save decisions
* 08-Apr-13 File creation. Michael Matz
*
* NOTES
*
* Simply save all callee saved registers
*
*/
#define STACK_REFPLUS 1
#ifdef SLP_EVAL
#define STACK_MAGIC 0
#define REGS_TO_SAVE "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", \
"x27", "x28", "x30" /* aka lr */, \
"v8", "v9", "v10", "v11", \
"v12", "v13", "v14", "v15"
static int
slp_switch(void)
{
int err;
void *fp;
register long *stackref, stsizediff;
__asm__ volatile ("" : : : REGS_TO_SAVE);
__asm__ volatile ("str x29, %0" : "=m"(fp) : : );
__asm__ ("mov %0, sp" : "=r" (stackref));
{
SLP_SAVE_STATE(stackref, stsizediff);
__asm__ volatile (
"add sp,sp,%0\n"
"add x29,x29,%0\n"
:
: "r" (stsizediff)
);
SLP_RESTORE_STATE();
/* SLP_SAVE_STATE macro contains some return statements
(of -1 and 1). It falls through only when
the return value of slp_save_state() is zero, which
is placed in x0.
In that case we (slp_switch) also want to return zero
(also in x0 of course).
Now, some GCC versions (seen with 4.8) think it's a
good idea to save/restore x0 around the call to
slp_restore_state(), instead of simply zeroing it
at the return below. But slp_restore_state
writes random values to the stack slot used for this
save/restore (from when it once was saved above in
SLP_SAVE_STATE, when it was still uninitialized), so
"restoring" that precious zero actually makes us
return random values. There are some ways to make
GCC not use that zero value in the normal return path
(e.g. making err volatile, but that costs a little
stack space), and the simplest is to call a function
that returns an unknown value (which happens to be zero),
so the saved/restored value is unused. */
__asm__ volatile ("mov %0, #0" : "=r" (err));
}
__asm__ volatile ("ldr x29, %0" : : "m" (fp) :);
__asm__ volatile ("" : : : REGS_TO_SAVE);
return err;
}
#endif

View File

@@ -0,0 +1,30 @@
#define STACK_REFPLUS 1
#ifdef SLP_EVAL
#define STACK_MAGIC 0
#define REGS_TO_SAVE "$9", "$10", "$11", "$12", "$13", "$14", "$15", \
"$f2", "$f3", "$f4", "$f5", "$f6", "$f7", "$f8", "$f9"
static int
slp_switch(void)
{
register int ret;
register long *stackref, stsizediff;
__asm__ volatile ("" : : : REGS_TO_SAVE);
__asm__ volatile ("mov $30, %0" : "=r" (stackref) : );
{
SLP_SAVE_STATE(stackref, stsizediff);
__asm__ volatile (
"addq $30, %0, $30\n\t"
: /* no outputs */
: "r" (stsizediff)
);
SLP_RESTORE_STATE();
}
__asm__ volatile ("" : : : REGS_TO_SAVE);
__asm__ volatile ("mov $31, %0" : "=r" (ret) : );
return ret;
}
#endif

View File

@@ -0,0 +1,84 @@
/*
* this is the internal transfer function.
*
* HISTORY
* 3-May-13 Ralf Schmitt <ralf@systemexit.de>
* Add support for strange GCC caller-save decisions
* (ported from switch_aarch64_gcc.h)
* 18-Aug-11 Alexey Borzenkov <snaury@gmail.com>
* Correctly save rbp, csr and cw
* 01-Apr-04 Hye-Shik Chang <perky@FreeBSD.org>
* Ported from i386 to amd64.
* 24-Nov-02 Christian Tismer <tismer@tismer.com>
* 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 <tismer@tismer.com>
* 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 <gerd.woetzel@GMD.DE>
* slightly changed framework for spark
* 31-Avr-02 Armin Rigo <arigo@ulb.ac.be>
* Added ebx, esi and edi register-saves.
* 01-Mar-02 Samual M. Rushing <rushing@ironport.com>
* 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
#define REGS_TO_SAVE "r12", "r13", "r14", "r15"
static int
slp_switch(void)
{
int err;
void* rbp;
void* rbx;
unsigned int csr;
unsigned short cw;
register long *stackref, stsizediff;
__asm__ volatile ("" : : : REGS_TO_SAVE);
__asm__ volatile ("fstcw %0" : "=m" (cw));
__asm__ volatile ("stmxcsr %0" : "=m" (csr));
__asm__ volatile ("movq %%rbp, %0" : "=m" (rbp));
__asm__ volatile ("movq %%rbx, %0" : "=m" (rbx));
__asm__ ("movq %%rsp, %0" : "=g" (stackref));
{
SLP_SAVE_STATE(stackref, stsizediff);
__asm__ volatile (
"addq %0, %%rsp\n"
"addq %0, %%rbp\n"
:
: "r" (stsizediff)
);
SLP_RESTORE_STATE();
__asm__ volatile ("xorq %%rax, %%rax" : "=a" (err));
}
__asm__ volatile ("movq %0, %%rbx" : : "m" (rbx));
__asm__ volatile ("movq %0, %%rbp" : : "m" (rbp));
__asm__ volatile ("ldmxcsr %0" : : "m" (csr));
__asm__ volatile ("fldcw %0" : : "m" (cw));
__asm__ volatile ("" : : : REGS_TO_SAVE);
return err;
}
#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.
*/

View File

@@ -0,0 +1,79 @@
/*
* 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 REG_SP "sp"
#define REG_SPSP "sp,sp"
#ifdef __thumb__
#define REG_FP "r7"
#define REG_FPFP "r7,r7"
#define REGS_TO_SAVE_GENERAL "r4", "r5", "r6", "r8", "r9", "r10", "r11", "lr"
#else
#define REG_FP "fp"
#define REG_FPFP "fp,fp"
#define REGS_TO_SAVE_GENERAL "r4", "r5", "r6", "r7", "r8", "r9", "r10", "lr"
#endif
#if defined(__SOFTFP__)
#define REGS_TO_SAVE REGS_TO_SAVE_GENERAL
#elif defined(__VFP_FP__)
#define REGS_TO_SAVE REGS_TO_SAVE_GENERAL, "d8", "d9", "d10", "d11", \
"d12", "d13", "d14", "d15"
#elif defined(__MAVERICK__)
#define REGS_TO_SAVE REGS_TO_SAVE_GENERAL, "mvf4", "mvf5", "mvf6", "mvf7", \
"mvf8", "mvf9", "mvf10", "mvf11", \
"mvf12", "mvf13", "mvf14", "mvf15"
#else
#define REGS_TO_SAVE REGS_TO_SAVE_GENERAL, "f4", "f5", "f6", "f7"
#endif
static int
#ifdef __GNUC__
__attribute__((optimize("no-omit-frame-pointer")))
#endif
slp_switch(void)
{
void *fp;
register int *stackref, stsizediff;
int result;
__asm__ volatile ("" : : : REGS_TO_SAVE);
__asm__ volatile ("mov r0," REG_FP "\n\tstr r0,%0" : "=m" (fp) : : "r0");
__asm__ ("mov %0," REG_SP : "=r" (stackref));
{
SLP_SAVE_STATE(stackref, stsizediff);
__asm__ volatile (
"add " REG_SPSP ",%0\n"
"add " REG_FPFP ",%0\n"
:
: "r" (stsizediff)
);
SLP_RESTORE_STATE();
}
__asm__ volatile ("ldr r0,%1\n\tmov " REG_FP ",r0\n\tmov %0, #0" : "=r" (result) : "m" (fp) : "r0");
__asm__ volatile ("" : : : REGS_TO_SAVE);
return result;
}
#endif

View File

@@ -0,0 +1,67 @@
/*
* this is the internal transfer function.
*
* HISTORY
* 31-May-15 iOS support. Ported from arm32. Proton <feisuzhu@163.com>
*
* 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 REG_SP "sp"
#define REG_SPSP "sp,sp"
#define REG_FP "r7"
#define REG_FPFP "r7,r7"
#define REGS_TO_SAVE_GENERAL "r4", "r5", "r6", "r8", "r10", "r11", "lr"
#define REGS_TO_SAVE REGS_TO_SAVE_GENERAL, "d8", "d9", "d10", "d11", \
"d12", "d13", "d14", "d15"
static int
#ifdef __GNUC__
__attribute__((optimize("no-omit-frame-pointer")))
#endif
slp_switch(void)
{
void *fp;
register int *stackref, stsizediff, result;
__asm__ volatile ("" : : : REGS_TO_SAVE);
__asm__ volatile ("str " REG_FP ",%0" : "=m" (fp));
__asm__ ("mov %0," REG_SP : "=r" (stackref));
{
SLP_SAVE_STATE(stackref, stsizediff);
__asm__ volatile (
"add " REG_SPSP ",%0\n"
"add " REG_FPFP ",%0\n"
:
: "r" (stsizediff)
: REGS_TO_SAVE /* Clobber registers, force compiler to
* recalculate address of void *fp from REG_SP or REG_FP */
);
SLP_RESTORE_STATE();
}
__asm__ volatile (
"ldr " REG_FP ", %1\n\t"
"mov %0, #0"
: "=r" (result)
: "m" (fp)
: REGS_TO_SAVE /* Force compiler to restore saved registers after this */
);
return result;
}
#endif

View File

@@ -0,0 +1,48 @@
#ifdef SLP_EVAL
#define STACK_MAGIC 0
#define REG_FP "r8"
#ifdef __CSKYABIV2__
#define REGS_TO_SAVE_GENERAL "r4", "r5", "r6", "r7", "r9", "r10", "r11", "r15",\
"r16", "r17", "r18", "r19", "r20", "r21", "r22",\
"r23", "r24", "r25"
#if defined (__CSKY_HARD_FLOAT__) || (__CSKY_VDSP__)
#define REGS_TO_SAVE REGS_TO_SAVE_GENERAL, "vr8", "vr9", "vr10", "vr11", "vr12",\
"vr13", "vr14", "vr15"
#else
#define REGS_TO_SAVE REGS_TO_SAVE_GENERAL
#endif
#else
#define REGS_TO_SAVE "r9", "r10", "r11", "r12", "r13", "r15"
#endif
static int
#ifdef __GNUC__
__attribute__((optimize("no-omit-frame-pointer")))
#endif
slp_switch(void)
{
register int *stackref, stsizediff;
int result;
__asm__ volatile ("" : : : REGS_TO_SAVE);
__asm__ ("mov %0, sp" : "=r" (stackref));
{
SLP_SAVE_STATE(stackref, stsizediff);
__asm__ volatile (
"addu sp,%0\n"
"addu "REG_FP",%0\n"
:
: "r" (stsizediff)
);
SLP_RESTORE_STATE();
}
__asm__ volatile ("movi %0, 0" : "=r" (result));
__asm__ volatile ("" : : : REGS_TO_SAVE);
return result;
}
#endif

View File

@@ -0,0 +1,38 @@
/*
* this is the internal transfer function.
*
* HISTORY
* 2014-01-06 Andreas Schwab <schwab@linux-m68k.org>
* File created.
*/
#ifdef SLP_EVAL
#define STACK_MAGIC 0
#define REGS_TO_SAVE "%d2", "%d3", "%d4", "%d5", "%d6", "%d7", \
"%a2", "%a3", "%a4"
static int
slp_switch(void)
{
int err;
int *stackref, stsizediff;
void *fp, *a5;
__asm__ volatile ("" : : : REGS_TO_SAVE);
__asm__ volatile ("move.l %%fp, %0" : "=m"(fp));
__asm__ volatile ("move.l %%a5, %0" : "=m"(a5));
__asm__ ("move.l %%sp, %0" : "=r"(stackref));
{
SLP_SAVE_STATE(stackref, stsizediff);
__asm__ volatile ("add.l %0, %%sp; add.l %0, %%fp" : : "r"(stsizediff));
SLP_RESTORE_STATE();
__asm__ volatile ("clr.l %0" : "=g" (err));
}
__asm__ volatile ("move.l %0, %%a5" : : "m"(a5));
__asm__ volatile ("move.l %0, %%fp" : : "m"(fp));
__asm__ volatile ("" : : : REGS_TO_SAVE);
return err;
}
#endif

View File

@@ -0,0 +1,64 @@
/*
* this is the internal transfer function.
*
* HISTORY
* 20-Sep-14 Matt Madison <madison@bliss-m.org>
* Re-code the saving of the gp register for MIPS64.
* 05-Jan-08 Thiemo Seufer <ths@debian.org>
* Ported from ppc.
*/
#define STACK_REFPLUS 1
#ifdef SLP_EVAL
#define STACK_MAGIC 0
#define REGS_TO_SAVE "$16", "$17", "$18", "$19", "$20", "$21", "$22", \
"$23", "$30"
static int
slp_switch(void)
{
register int err;
register int *stackref, stsizediff;
#ifdef __mips64
uint64_t gpsave;
#endif
__asm__ __volatile__ ("" : : : REGS_TO_SAVE);
#ifdef __mips64
__asm__ __volatile__ ("sd $28,%0" : "=m" (gpsave) : : );
#endif
__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();
}
#ifdef __mips64
__asm__ __volatile__ ("ld $28,%0" : : "m" (gpsave) : );
#endif
__asm__ __volatile__ ("" : : : REGS_TO_SAVE);
__asm__ __volatile__ ("move %0, $0" : "=r" (err));
return err;
}
#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.
*/

View File

@@ -0,0 +1,103 @@
/*
* this is the internal transfer function.
*
* HISTORY
* 16-Oct-20 Jesse Gorzinski <jgorzins@us.ibm.com>
* Copied from Linux PPC64 implementation
* 04-Sep-18 Alexey Borzenkov <snaury@gmail.com>
* Workaround a gcc bug using manual save/restore of r30
* 21-Mar-18 Tulio Magno Quites Machado Filho <tuliom@linux.vnet.ibm.com>
* Added r30 to the list of saved registers in order to fully comply with
* both ppc64 ELFv1 ABI and the ppc64le ELFv2 ABI, that classify this
* register as a nonvolatile register used for local variables.
* 21-Mar-18 Laszlo Boszormenyi <gcs@debian.org>
* Save r2 (TOC pointer) manually.
* 10-Dec-13 Ulrich Weigand <uweigand@de.ibm.com>
* Support ELFv2 ABI. Save float/vector registers.
* 09-Mar-12 Michael Ellerman <michael@ellerman.id.au>
* 64-bit implementation, copied from 32-bit.
* 07-Sep-05 (py-dev mailing list discussion)
* removed 'r31' from the register-saved. !!!! WARNING !!!!
* It means that this file can no longer be compiled statically!
* It is now only suitable as part of a dynamic library!
* 14-Jan-04 Bob Ippolito <bob@redivi.com>
* 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 <tismer@tismer.com>
* 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.
* 04-Oct-02 Gustavo Niemeyer <niemeyer@conectiva.com>
* Ported from MacOS version.
* 17-Sep-02 Christian Tismer <tismer@tismer.com>
* 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 <gerd.woetzel@GMD.DE>
* slightly changed framework for sparc
* 29-Jun-02 Christian Tismer <tismer@tismer.com>
* 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 <perky@fallin.lv>
* Ported from i386.
* 31-Jul-12 Trevor Bowen <trevorbowen@gmail.com>
* Changed memory constraints to register only.
*/
#define STACK_REFPLUS 1
#ifdef SLP_EVAL
#define STACK_MAGIC 6
#if defined(__ALTIVEC__)
#define ALTIVEC_REGS \
"v20", "v21", "v22", "v23", "v24", "v25", "v26", "v27", \
"v28", "v29", "v30", "v31",
#else
#define ALTIVEC_REGS
#endif
#define REGS_TO_SAVE "r14", "r15", "r16", "r17", "r18", "r19", "r20", \
"r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", \
"r31", \
"fr14", "fr15", "fr16", "fr17", "fr18", "fr19", "fr20", "fr21", \
"fr22", "fr23", "fr24", "fr25", "fr26", "fr27", "fr28", "fr29", \
"fr30", "fr31", \
ALTIVEC_REGS \
"cr2", "cr3", "cr4"
static int
slp_switch(void)
{
register int err;
register long *stackref, stsizediff;
void * toc;
void * r30;
__asm__ volatile ("" : : : REGS_TO_SAVE);
__asm__ volatile ("std 2, %0" : "=m" (toc));
__asm__ volatile ("std 30, %0" : "=m" (r30));
__asm__ ("mr %0, 1" : "=r" (stackref) : );
{
SLP_SAVE_STATE(stackref, stsizediff);
__asm__ volatile (
"mr 11, %0\n"
"add 1, 1, 11\n"
: /* no outputs */
: "r" (stsizediff)
: "11"
);
SLP_RESTORE_STATE();
}
__asm__ volatile ("ld 30, %0" : : "m" (r30));
__asm__ volatile ("ld 2, %0" : : "m" (toc));
__asm__ volatile ("" : : : REGS_TO_SAVE);
__asm__ volatile ("li %0, 0" : "=r" (err));
return err;
}
#endif

View File

@@ -0,0 +1,105 @@
/*
* this is the internal transfer function.
*
* HISTORY
* 04-Sep-18 Alexey Borzenkov <snaury@gmail.com>
* Workaround a gcc bug using manual save/restore of r30
* 21-Mar-18 Tulio Magno Quites Machado Filho <tuliom@linux.vnet.ibm.com>
* Added r30 to the list of saved registers in order to fully comply with
* both ppc64 ELFv1 ABI and the ppc64le ELFv2 ABI, that classify this
* register as a nonvolatile register used for local variables.
* 21-Mar-18 Laszlo Boszormenyi <gcs@debian.org>
* Save r2 (TOC pointer) manually.
* 10-Dec-13 Ulrich Weigand <uweigand@de.ibm.com>
* Support ELFv2 ABI. Save float/vector registers.
* 09-Mar-12 Michael Ellerman <michael@ellerman.id.au>
* 64-bit implementation, copied from 32-bit.
* 07-Sep-05 (py-dev mailing list discussion)
* removed 'r31' from the register-saved. !!!! WARNING !!!!
* It means that this file can no longer be compiled statically!
* It is now only suitable as part of a dynamic library!
* 14-Jan-04 Bob Ippolito <bob@redivi.com>
* 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 <tismer@tismer.com>
* 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.
* 04-Oct-02 Gustavo Niemeyer <niemeyer@conectiva.com>
* Ported from MacOS version.
* 17-Sep-02 Christian Tismer <tismer@tismer.com>
* 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 <gerd.woetzel@GMD.DE>
* slightly changed framework for sparc
* 29-Jun-02 Christian Tismer <tismer@tismer.com>
* 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 <perky@fallin.lv>
* Ported from i386.
* 31-Jul-12 Trevor Bowen <trevorbowen@gmail.com>
* Changed memory constraints to register only.
*/
#define STACK_REFPLUS 1
#ifdef SLP_EVAL
#if _CALL_ELF == 2
#define STACK_MAGIC 4
#else
#define STACK_MAGIC 6
#endif
#if defined(__ALTIVEC__)
#define ALTIVEC_REGS \
"v20", "v21", "v22", "v23", "v24", "v25", "v26", "v27", \
"v28", "v29", "v30", "v31",
#else
#define ALTIVEC_REGS
#endif
#define REGS_TO_SAVE "r14", "r15", "r16", "r17", "r18", "r19", "r20", \
"r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", \
"r31", \
"fr14", "fr15", "fr16", "fr17", "fr18", "fr19", "fr20", "fr21", \
"fr22", "fr23", "fr24", "fr25", "fr26", "fr27", "fr28", "fr29", \
"fr30", "fr31", \
ALTIVEC_REGS \
"cr2", "cr3", "cr4"
static int
slp_switch(void)
{
register int err;
register long *stackref, stsizediff;
void * toc;
void * r30;
__asm__ volatile ("" : : : REGS_TO_SAVE);
__asm__ volatile ("std 2, %0" : "=m" (toc));
__asm__ volatile ("std 30, %0" : "=m" (r30));
__asm__ ("mr %0, 1" : "=r" (stackref) : );
{
SLP_SAVE_STATE(stackref, stsizediff);
__asm__ volatile (
"mr 11, %0\n"
"add 1, 1, 11\n"
: /* no outputs */
: "r" (stsizediff)
: "11"
);
SLP_RESTORE_STATE();
}
__asm__ volatile ("ld 30, %0" : : "m" (r30));
__asm__ volatile ("ld 2, %0" : : "m" (toc));
__asm__ volatile ("" : : : REGS_TO_SAVE);
__asm__ volatile ("li %0, 0" : "=r" (err));
return err;
}
#endif

View File

@@ -0,0 +1,87 @@
/*
* this is the internal transfer function.
*
* HISTORY
* 07-Mar-11 Floris Bruynooghe <flub@devork.be>
* Do not add stsizediff to general purpose
* register (GPR) 30 as this is a non-volatile and
* unused by the PowerOpen Environment, therefore
* this was modifying a user register instead of the
* frame pointer (which does not seem to exist).
* 07-Sep-05 (py-dev mailing list discussion)
* removed 'r31' from the register-saved. !!!! WARNING !!!!
* It means that this file can no longer be compiled statically!
* It is now only suitable as part of a dynamic library!
* 14-Jan-04 Bob Ippolito <bob@redivi.com>
* 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 <tismer@tismer.com>
* 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.
* 04-Oct-02 Gustavo Niemeyer <niemeyer@conectiva.com>
* Ported from MacOS version.
* 17-Sep-02 Christian Tismer <tismer@tismer.com>
* 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 <gerd.woetzel@GMD.DE>
* slightly changed framework for sparc
* 29-Jun-02 Christian Tismer <tismer@tismer.com>
* 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 <perky@fallin.lv>
* Ported from i386.
*/
#define STACK_REFPLUS 1
#ifdef SLP_EVAL
#define STACK_MAGIC 3
/* !!!!WARNING!!!! need to add "r31" in the next line if this header file
* is meant to be compiled non-dynamically!
*/
#define REGS_TO_SAVE "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", \
"r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", \
"cr2", "cr3", "cr4"
static int
slp_switch(void)
{
register int err;
register int *stackref, stsizediff;
__asm__ volatile ("" : : : REGS_TO_SAVE);
__asm__ ("mr %0, 1" : "=r" (stackref) : );
{
SLP_SAVE_STATE(stackref, stsizediff);
__asm__ volatile (
"mr 11, %0\n"
"add 1, 1, 11\n"
: /* no outputs */
: "r" (stsizediff)
: "11"
);
SLP_RESTORE_STATE();
}
__asm__ volatile ("" : : : REGS_TO_SAVE);
__asm__ volatile ("li %0, 0" : "=r" (err));
return err;
}
#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.
*/

View File

@@ -0,0 +1,84 @@
/*
* this is the internal transfer function.
*
* HISTORY
* 07-Sep-05 (py-dev mailing list discussion)
* removed 'r31' from the register-saved. !!!! WARNING !!!!
* It means that this file can no longer be compiled statically!
* It is now only suitable as part of a dynamic library!
* 14-Jan-04 Bob Ippolito <bob@redivi.com>
* 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 <tismer@tismer.com>
* 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.
* 04-Oct-02 Gustavo Niemeyer <niemeyer@conectiva.com>
* Ported from MacOS version.
* 17-Sep-02 Christian Tismer <tismer@tismer.com>
* 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 <gerd.woetzel@GMD.DE>
* slightly changed framework for sparc
* 29-Jun-02 Christian Tismer <tismer@tismer.com>
* 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 <perky@fallin.lv>
* Ported from i386.
* 31-Jul-12 Trevor Bowen <trevorbowen@gmail.com>
* Changed memory constraints to register only.
*/
#define STACK_REFPLUS 1
#ifdef SLP_EVAL
#define STACK_MAGIC 3
/* !!!!WARNING!!!! need to add "r31" in the next line if this header file
* is meant to be compiled non-dynamically!
*/
#define REGS_TO_SAVE "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", \
"r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", \
"cr2", "cr3", "cr4"
static int
slp_switch(void)
{
register int err;
register int *stackref, stsizediff;
__asm__ volatile ("" : : : REGS_TO_SAVE);
__asm__ ("mr %0, 1" : "=r" (stackref) : );
{
SLP_SAVE_STATE(stackref, stsizediff);
__asm__ volatile (
"mr 11, %0\n"
"add 1, 1, 11\n"
"add 30, 30, 11\n"
: /* no outputs */
: "r" (stsizediff)
: "11"
);
SLP_RESTORE_STATE();
}
__asm__ volatile ("" : : : REGS_TO_SAVE);
__asm__ volatile ("li %0, 0" : "=r" (err));
return err;
}
#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.
*/

View File

@@ -0,0 +1,82 @@
/*
* this is the internal transfer function.
*
* HISTORY
* 07-Sep-05 (py-dev mailing list discussion)
* removed 'r31' from the register-saved. !!!! WARNING !!!!
* It means that this file can no longer be compiled statically!
* It is now only suitable as part of a dynamic library!
* 14-Jan-04 Bob Ippolito <bob@redivi.com>
* 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 <tismer@tismer.com>
* 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 <tismer@tismer.com>
* 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 <gerd.woetzel@GMD.DE>
* slightly changed framework for sparc
* 29-Jun-02 Christian Tismer <tismer@tismer.com>
* 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 <perky@fallin.lv>
* Ported from i386.
*/
#define STACK_REFPLUS 1
#ifdef SLP_EVAL
#define STACK_MAGIC 3
/* !!!!WARNING!!!! need to add "r31" in the next line if this header file
* is meant to be compiled non-dynamically!
*/
#define REGS_TO_SAVE "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", \
"r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", \
"cr2", "cr3", "cr4"
static int
slp_switch(void)
{
register int err;
register int *stackref, stsizediff;
__asm__ volatile ("" : : : 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();
}
__asm__ volatile ("" : : : REGS_TO_SAVE);
__asm__ volatile ("li %0, 0" : "=r" (err));
return err;
}
#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.
*/

View File

@@ -0,0 +1,82 @@
/*
* this is the internal transfer function.
*
* HISTORY
* 07-Sep-05 (py-dev mailing list discussion)
* removed 'r31' from the register-saved. !!!! WARNING !!!!
* It means that this file can no longer be compiled statically!
* It is now only suitable as part of a dynamic library!
* 14-Jan-04 Bob Ippolito <bob@redivi.com>
* 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 <tismer@tismer.com>
* 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.
* 04-Oct-02 Gustavo Niemeyer <niemeyer@conectiva.com>
* Ported from MacOS version.
* 17-Sep-02 Christian Tismer <tismer@tismer.com>
* 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 <gerd.woetzel@GMD.DE>
* slightly changed framework for sparc
* 29-Jun-02 Christian Tismer <tismer@tismer.com>
* 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 <perky@fallin.lv>
* Ported from i386.
*/
#define STACK_REFPLUS 1
#ifdef SLP_EVAL
#define STACK_MAGIC 3
/* !!!!WARNING!!!! need to add "r31" in the next line if this header file
* is meant to be compiled non-dynamically!
*/
#define REGS_TO_SAVE "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", \
"r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", \
"cr2", "cr3", "cr4"
static int
slp_switch(void)
{
register int err;
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();
}
__asm__ volatile ("" : : : REGS_TO_SAVE);
__asm__ volatile ("li %0, 0" : "=r" (err));
return err;
}
#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.
*/

View File

@@ -0,0 +1,32 @@
#define STACK_REFPLUS 1
#ifdef SLP_EVAL
#define STACK_MAGIC 0
#define REGS_TO_SAVE "s0", "s1", "s2", "s3", "s4", "s5", \
"s6", "s7", "s8", "s9", "s10", "s11", "fs0", "fs1", \
"fs2", "fs3", "fs4", "fs5", "fs6", "fs7", "fs8", "fs9", \
"fs10", "fs11"
static int
slp_switch(void)
{
register int ret;
register long *stackref, stsizediff;
__asm__ volatile ("" : : : REGS_TO_SAVE);
__asm__ volatile ("mv %0, sp" : "=r" (stackref) : );
{
SLP_SAVE_STATE(stackref, stsizediff);
__asm__ volatile (
"add sp, sp, %0\n\t"
: /* no outputs */
: "r" (stsizediff)
);
SLP_RESTORE_STATE();
}
__asm__ volatile ("" : : : REGS_TO_SAVE);
__asm__ volatile ("mv %0, zero" : "=r" (ret) : );
return ret;
}
#endif

View File

@@ -0,0 +1,87 @@
/*
* this is the internal transfer function.
*
* HISTORY
* 25-Jan-12 Alexey Borzenkov <snaury@gmail.com>
* Fixed Linux/S390 port to work correctly with
* different optimization options both on 31-bit
* and 64-bit. Thanks to Stefan Raabe for lots
* of testing.
* 24-Nov-02 Christian Tismer <tismer@tismer.com>
* 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.
* 06-Oct-02 Gustavo Niemeyer <niemeyer@conectiva.com>
* Ported to Linux/S390.
*/
#define STACK_REFPLUS 1
#ifdef SLP_EVAL
#ifdef __s390x__
#define STACK_MAGIC 20 /* 20 * 8 = 160 bytes of function call area */
#else
#define STACK_MAGIC 24 /* 24 * 4 = 96 bytes of function call area */
#endif
/* Technically, r11-r13 also need saving, but function prolog starts
with stm(g) and since there are so many saved registers already
it won't be optimized, resulting in all r6-r15 being saved */
#define REGS_TO_SAVE "r6", "r7", "r8", "r9", "r10", "r14", \
"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", \
"f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15"
static int
slp_switch(void)
{
register int ret;
register long *stackref, stsizediff;
__asm__ volatile ("" : : : REGS_TO_SAVE);
#ifdef __s390x__
__asm__ volatile ("lgr %0, 15" : "=r" (stackref) : );
#else
__asm__ volatile ("lr %0, 15" : "=r" (stackref) : );
#endif
{
SLP_SAVE_STATE(stackref, stsizediff);
/* N.B.
r11 may be used as the frame pointer, and in that case it cannot be
clobbered and needs offsetting just like the stack pointer (but in cases
where frame pointer isn't used we might clobber it accidentally). What's
scary is that r11 is 2nd (and even 1st when GOT is used) callee saved
register that gcc would chose for surviving function calls. However,
since r6-r10 are clobbered above, their cost for reuse is reduced, so
gcc IRA will chose them over r11 (not seeing r11 is implicitly saved),
making it relatively safe to offset in all cases. :) */
__asm__ volatile (
#ifdef __s390x__
"agr 15, %0\n\t"
"agr 11, %0"
#else
"ar 15, %0\n\t"
"ar 11, %0"
#endif
: /* no outputs */
: "r" (stsizediff)
);
SLP_RESTORE_STATE();
}
__asm__ volatile ("" : : : REGS_TO_SAVE);
__asm__ volatile ("lhi %0, 0" : "=r" (ret) : );
return ret;
}
#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.
*/

View File

@@ -0,0 +1,92 @@
/*
* this is the internal transfer function.
*
* HISTORY
* 16-May-15 Alexey Borzenkov <snaury@gmail.com>
* Move stack spilling code inside save/restore functions
* 30-Aug-13 Floris Bruynooghe <flub@devork.be>
Clean the register windows again before returning.
This does not clobber the PIC register as it leaves
the current window intact and is required for multi-
threaded code to work correctly.
* 08-Mar-11 Floris Bruynooghe <flub@devork.be>
* No need to set return value register explicitly
* before the stack and framepointer are adjusted
* as none of the other registers are influenced by
* this. Also don't needlessly clean the windows
* ('ta %0" :: "i" (ST_CLEAN_WINDOWS)') as that
* clobbers the gcc PIC register (%l7).
* 24-Nov-02 Christian Tismer <tismer@tismer.com>
* 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 <tismer@tismer.com>
* 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 <gerd.woetzel@GMD.DE>
* added support for SunOS sparc with gcc
*/
#define STACK_REFPLUS 1
#ifdef SLP_EVAL
#define STACK_MAGIC 0
#if defined(__sparcv9)
#define SLP_FLUSHW __asm__ volatile ("flushw")
#else
#define SLP_FLUSHW __asm__ volatile ("ta 3") /* ST_FLUSH_WINDOWS */
#endif
/* On sparc we need to spill register windows inside save/restore functions */
#define SLP_BEFORE_SAVE_STATE() SLP_FLUSHW
#define SLP_BEFORE_RESTORE_STATE() SLP_FLUSHW
static int
slp_switch(void)
{
register int err;
register int *stackref, stsizediff;
/* Put current stack pointer into stackref.
* Register spilling is done in save/restore.
*/
__asm__ volatile ("mov %%sp, %0" : "=r" (stackref));
{
/* Thou shalt put SLP_SAVE_STATE into a local block */
/* Copy the current stack onto the heap */
SLP_SAVE_STATE(stackref, stsizediff);
/* Increment stack and frame pointer by stsizediff */
__asm__ volatile (
"add %0, %%sp, %%sp\n\t"
"add %0, %%fp, %%fp"
: : "r" (stsizediff));
/* Copy new stack from it's save store on the heap */
SLP_RESTORE_STATE();
__asm__ volatile ("mov %1, %0" : "=r" (err) : "i" (0));
return err;
}
}
#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.
*/

View File

@@ -0,0 +1,63 @@
/*
* this is the internal transfer function.
*
* HISTORY
* 17-Aug-12 Fantix King <fantix.king@gmail.com>
* Ported from amd64.
*/
#define STACK_REFPLUS 1
#ifdef SLP_EVAL
#define STACK_MAGIC 0
#define REGS_TO_SAVE "r12", "r13", "r14", "r15"
static int
slp_switch(void)
{
void* ebp;
void* ebx;
unsigned int csr;
unsigned short cw;
register int err;
register int *stackref, stsizediff;
__asm__ volatile ("" : : : REGS_TO_SAVE);
__asm__ volatile ("fstcw %0" : "=m" (cw));
__asm__ volatile ("stmxcsr %0" : "=m" (csr));
__asm__ volatile ("movl %%ebp, %0" : "=m" (ebp));
__asm__ volatile ("movl %%ebx, %0" : "=m" (ebx));
__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();
}
__asm__ volatile ("movl %0, %%ebx" : : "m" (ebx));
__asm__ volatile ("movl %0, %%ebp" : : "m" (ebp));
__asm__ volatile ("ldmxcsr %0" : : "m" (csr));
__asm__ volatile ("fldcw %0" : : "m" (cw));
__asm__ volatile ("" : : : REGS_TO_SAVE);
__asm__ volatile ("xorl %%eax, %%eax" : "=a" (err));
return err;
}
#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.
*/

View File

@@ -0,0 +1,111 @@
;
; stack switching code for MASM on x641
; Kristjan Valur Jonsson, sept 2005
;
;prototypes for our calls
slp_save_state_asm PROTO
slp_restore_state_asm 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_asm ;pass stackpointer, return offset in eax
cmp rax, 1
je EXIT1
cmp rax, -1
je EXIT2
;actual stack switch:
add rsp, rax
call slp_restore_state_asm
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
EXIT1:
mov rax, 1
jmp EXIT
EXIT2:
sar rax, 1
jmp EXIT
slp_switch ENDP
END

Binary file not shown.

View File

@@ -0,0 +1,60 @@
/*
* this is the internal transfer function.
*
* HISTORY
* 24-Nov-02 Christian Tismer <tismer@tismer.com>
* 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 <tismer@tismer.com>
* 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 <tismer@tismer.com>
* 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 <gerd.woetzel@GMD.DE>
* slightly changed framework for sparc
* 01-Mar-02 Christian Tismer <tismer@tismer.com>
* Initial final version after lots of iterations for i386.
*/
/* Avoid alloca redefined warning on mingw64 */
#ifndef alloca
#define alloca _alloca
#endif
#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
*/

View File

@@ -0,0 +1,88 @@
/*
* this is the internal transfer function.
*
* HISTORY
* 24-Nov-02 Christian Tismer <tismer@tismer.com>
* 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 <tismer@tismer.com>
* 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 <tismer@tismer.com>
* 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 <gerd.woetzel@GMD.DE>
* slightly changed framework for sparc
* 01-Mar-02 Christian Tismer <tismer@tismer.com>
* Initial final version after lots of iterations for i386.
*/
#define alloca _alloca
#define STACK_REFPLUS 1
#ifdef SLP_EVAL
#define STACK_MAGIC 0
/* Some magic to quell warnings and keep slp_switch() from crashing when built
with VC90. Disable global optimizations, and the warning: frame pointer
register 'ebp' modified by inline assembly code */
#pragma optimize("g", off)
#pragma warning(disable:4731)
static int
slp_switch(void)
{
void* seh;
register int *stackref, stsizediff;
__asm mov eax, fs:[0]
__asm mov [seh], eax
__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();
}
__asm mov eax, [seh]
__asm mov fs:[0], eax
return 0;
}
/* re-enable ebp warning and global optimizations. */
#pragma optimize("g", on)
#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

View File

@@ -0,0 +1,105 @@
/*
* this is the internal transfer function.
*
* HISTORY
* 3-May-13 Ralf Schmitt <ralf@systemexit.de>
* Add support for strange GCC caller-save decisions
* (ported from switch_aarch64_gcc.h)
* 19-Aug-11 Alexey Borzenkov <snaury@gmail.com>
* Correctly save ebp, ebx and cw
* 07-Sep-05 (py-dev mailing list discussion)
* removed 'ebx' from the register-saved. !!!! WARNING !!!!
* It means that this file can no longer be compiled statically!
* It is now only suitable as part of a dynamic library!
* 24-Nov-02 Christian Tismer <tismer@tismer.com>
* 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 <tismer@tismer.com>
* 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 <gerd.woetzel@GMD.DE>
* slightly changed framework for spark
* 31-Avr-02 Armin Rigo <arigo@ulb.ac.be>
* Added ebx, esi and edi register-saves.
* 01-Mar-02 Samual M. Rushing <rushing@ironport.com>
* 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
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
# define ATTR_NOCLONE __attribute__((noclone))
#else
# define ATTR_NOCLONE
#endif
static int
slp_switch(void)
{
int err;
#ifdef _WIN32
void *seh;
#endif
void *ebp, *ebx;
unsigned short cw;
register int *stackref, stsizediff;
__asm__ volatile ("" : : : "esi", "edi");
__asm__ volatile ("fstcw %0" : "=m" (cw));
__asm__ volatile ("movl %%ebp, %0" : "=m" (ebp));
__asm__ volatile ("movl %%ebx, %0" : "=m" (ebx));
#ifdef _WIN32
__asm__ volatile (
"movl %%fs:0x0, %%eax\n"
"movl %%eax, %0\n"
: "=m" (seh)
:
: "eax");
#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();
__asm__ volatile ("xorl %%eax, %%eax" : "=a" (err));
}
#ifdef _WIN32
__asm__ volatile (
"movl %0, %%eax\n"
"movl %%eax, %%fs:0x0\n"
:
: "m" (seh)
: "eax");
#endif
__asm__ volatile ("movl %0, %%ebx" : : "m" (ebx));
__asm__ volatile ("movl %0, %%ebp" : : "m" (ebp));
__asm__ volatile ("fldcw %0" : : "m" (cw));
__asm__ volatile ("" : : : "esi", "edi");
return err;
}
#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.
*/

View File

@@ -0,0 +1,58 @@
/*
* Platform Selection for Stackless Python
*/
#if defined(MS_WIN32) && !defined(MS_WIN64) && defined(_M_IX86) && defined(_MSC_VER)
#include "platform/switch_x86_msvc.h" /* MS Visual Studio on X86 */
#elif defined(MS_WIN64) && defined(_M_X64) && defined(_MSC_VER) || defined(__MINGW64__)
#include "platform/switch_x64_msvc.h" /* MS Visual Studio on X64 */
#elif defined(__GNUC__) && defined(__amd64__) && defined(__ILP32__)
#include "platform/switch_x32_unix.h" /* gcc on amd64 with x32 ABI */
#elif defined(__GNUC__) && defined(__amd64__)
#include "platform/switch_amd64_unix.h" /* gcc on amd64 */
#elif defined(__GNUC__) && defined(__i386__)
#include "platform/switch_x86_unix.h" /* gcc on X86 */
#elif defined(__GNUC__) && defined(__powerpc64__) && (defined(__linux__) || defined(__FreeBSD__))
#include "platform/switch_ppc64_linux.h" /* gcc on PowerPC 64-bit */
#elif defined(__GNUC__) && defined(__PPC__) && (defined(__linux__) || defined(__FreeBSD__))
#include "platform/switch_ppc_linux.h" /* gcc on PowerPC */
#elif defined(__GNUC__) && defined(__ppc__) && defined(__APPLE__)
#include "platform/switch_ppc_macosx.h" /* Apple MacOS X on PowerPC */
#elif defined(__GNUC__) && defined(__powerpc64__) && defined(_AIX)
#include "platform/switch_ppc64_aix.h" /* gcc on AIX/PowerPC 64-bit */
#elif defined(__GNUC__) && defined(_ARCH_PPC) && defined(_AIX)
#include "platform/switch_ppc_aix.h" /* gcc on AIX/PowerPC */
#elif defined(__GNUC__) && defined(sparc)
#include "platform/switch_sparc_sun_gcc.h" /* SunOS sparc with gcc */
#elif defined(__SUNPRO_C) && defined(sparc) && defined(sun)
#include "platform/switch_sparc_sun_gcc.h" /* SunStudio on amd64 */
#elif defined(__SUNPRO_C) && defined(__amd64__) && defined(sun)
#include "platform/switch_amd64_unix.h" /* SunStudio on amd64 */
#elif defined(__SUNPRO_C) && defined(__i386__) && defined(sun)
#include "platform/switch_x86_unix.h" /* SunStudio on x86 */
#elif defined(__GNUC__) && defined(__s390__) && defined(__linux__)
#include "platform/switch_s390_unix.h" /* Linux/S390 */
#elif defined(__GNUC__) && defined(__s390x__) && defined(__linux__)
#include "platform/switch_s390_unix.h" /* Linux/S390 zSeries (64-bit) */
#elif defined(__GNUC__) && defined(__arm__)
#ifdef __APPLE__
#include <TargetConditionals.h>
#endif
#if TARGET_OS_IPHONE
#include "platform/switch_arm32_ios.h" /* iPhone OS on arm32 */
#else
#include "platform/switch_arm32_gcc.h" /* gcc using arm32 */
#endif
#elif defined(__GNUC__) && defined(__mips__) && defined(__linux__)
#include "platform/switch_mips_unix.h" /* Linux/MIPS */
#elif defined(__GNUC__) && defined(__aarch64__)
#include "platform/switch_aarch64_gcc.h" /* Aarch64 ABI */
#elif defined(__GNUC__) && defined(__mc68000__)
#include "platform/switch_m68k_gcc.h" /* gcc on m68k */
#elif defined(__GNUC__) && defined(__csky__)
#include "platform/switch_csky_gcc.h" /* gcc on csky */
#elif defined(__GNUC__) && defined(__riscv)
#include "platform/switch_riscv_unix.h" /* gcc on RISC-V */
#elif defined(__GNUC__) && defined(__alpha__)
#include "platform/switch_alpha_unix.h" /* gcc on DEC Alpha */
#endif

View File

View File

@@ -0,0 +1,216 @@
/* This is a set of functions used by test_extension_interface.py to test the
* Greenlet C API.
*/
#include "../greenlet.h"
#ifndef Py_RETURN_NONE
# define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
#endif
#define TEST_MODULE_NAME "_test_extension"
static PyObject*
test_switch(PyObject* self, PyObject* greenlet)
{
PyObject* result = NULL;
if (greenlet == NULL || !PyGreenlet_Check(greenlet)) {
PyErr_BadArgument();
return NULL;
}
result = PyGreenlet_Switch((PyGreenlet*)greenlet, NULL, NULL);
if (result == NULL) {
if (!PyErr_Occurred()) {
PyErr_SetString(PyExc_AssertionError,
"greenlet.switch() failed for some reason.");
}
return NULL;
}
Py_INCREF(result);
return result;
}
static PyObject*
test_switch_kwargs(PyObject* self, PyObject* args, PyObject* kwargs)
{
PyGreenlet* g = NULL;
PyObject* result = NULL;
PyArg_ParseTuple(args, "O!", &PyGreenlet_Type, &g);
if (g == NULL || !PyGreenlet_Check(g)) {
PyErr_BadArgument();
return NULL;
}
result = PyGreenlet_Switch(g, NULL, kwargs);
if (result == NULL) {
if (!PyErr_Occurred()) {
PyErr_SetString(PyExc_AssertionError,
"greenlet.switch() failed for some reason.");
}
return NULL;
}
Py_XINCREF(result);
return result;
}
static PyObject*
test_getcurrent(PyObject* self)
{
PyGreenlet* g = PyGreenlet_GetCurrent();
if (g == NULL || !PyGreenlet_Check(g) || !PyGreenlet_ACTIVE(g)) {
PyErr_SetString(PyExc_AssertionError,
"getcurrent() returned an invalid greenlet");
Py_XDECREF(g);
return NULL;
}
Py_DECREF(g);
Py_RETURN_NONE;
}
static PyObject*
test_setparent(PyObject* self, PyObject* arg)
{
PyGreenlet* current;
PyGreenlet* greenlet = NULL;
if (arg == NULL || !PyGreenlet_Check(arg)) {
PyErr_BadArgument();
return NULL;
}
if ((current = PyGreenlet_GetCurrent()) == NULL) {
return NULL;
}
greenlet = (PyGreenlet*)arg;
if (PyGreenlet_SetParent(greenlet, current)) {
Py_DECREF(current);
return NULL;
}
Py_DECREF(current);
if (PyGreenlet_Switch(greenlet, NULL, NULL) == NULL) {
return NULL;
}
Py_RETURN_NONE;
}
static PyObject*
test_new_greenlet(PyObject* self, PyObject* callable)
{
PyObject* result = NULL;
PyGreenlet* greenlet = PyGreenlet_New(callable, NULL);
if (!greenlet) {
return NULL;
}
result = PyGreenlet_Switch(greenlet, NULL, NULL);
if (result == NULL) {
return NULL;
}
Py_INCREF(result);
return result;
}
static PyObject*
test_raise_dead_greenlet(PyObject* self)
{
PyErr_SetString(PyExc_GreenletExit, "test GreenletExit exception.");
return NULL;
}
static PyObject*
test_raise_greenlet_error(PyObject* self)
{
PyErr_SetString(PyExc_GreenletError, "test greenlet.error exception");
return NULL;
}
static PyObject*
test_throw(PyObject* self, PyGreenlet* g)
{
const char msg[] = "take that sucka!";
PyObject* msg_obj = Py_BuildValue("s", msg);
PyGreenlet_Throw(g, PyExc_ValueError, msg_obj, NULL);
Py_DECREF(msg_obj);
Py_RETURN_NONE;
}
static PyMethodDef test_methods[] = {
{"test_switch",
(PyCFunction)test_switch,
METH_O,
"Switch to the provided greenlet sending provided arguments, and \n"
"return the results."},
{"test_switch_kwargs",
(PyCFunction)test_switch_kwargs,
METH_VARARGS | METH_KEYWORDS,
"Switch to the provided greenlet sending the provided keyword args."},
{"test_getcurrent",
(PyCFunction)test_getcurrent,
METH_NOARGS,
"Test PyGreenlet_GetCurrent()"},
{"test_setparent",
(PyCFunction)test_setparent,
METH_O,
"Se the parent of the provided greenlet and switch to it."},
{"test_new_greenlet",
(PyCFunction)test_new_greenlet,
METH_O,
"Test PyGreenlet_New()"},
{"test_raise_dead_greenlet",
(PyCFunction)test_raise_dead_greenlet,
METH_NOARGS,
"Just raise greenlet.GreenletExit"},
{"test_raise_greenlet_error",
(PyCFunction)test_raise_greenlet_error,
METH_NOARGS,
"Just raise greenlet.error"},
{"test_throw",
(PyCFunction)test_throw,
METH_O,
"Throw a ValueError at the provided greenlet"},
{NULL, NULL, 0, NULL}};
#if PY_MAJOR_VERSION >= 3
# define INITERROR return NULL
static struct PyModuleDef moduledef = {PyModuleDef_HEAD_INIT,
TEST_MODULE_NAME,
NULL,
0,
test_methods,
NULL,
NULL,
NULL,
NULL};
PyMODINIT_FUNC
PyInit__test_extension(void)
#else
# define INITERROR return
PyMODINIT_FUNC
init_test_extension(void)
#endif
{
PyObject* module = NULL;
#if PY_MAJOR_VERSION >= 3
module = PyModule_Create(&moduledef);
#else
module = Py_InitModule(TEST_MODULE_NAME, test_methods);
#endif
if (module == NULL) {
INITERROR;
}
PyGreenlet_Import();
#if PY_MAJOR_VERSION >= 3
return module;
#endif
}

View File

@@ -0,0 +1,121 @@
/* This is a set of functions used to test C++ exceptions are not
* broken during greenlet switches
*/
#include "../greenlet.h"
struct exception_t {
int depth;
exception_t(int depth) : depth(depth) {}
};
/* Functions are called via pointers to prevent inlining */
static void (*p_test_exception_throw)(int depth);
static PyObject* (*p_test_exception_switch_recurse)(int depth, int left);
static void
test_exception_throw(int depth)
{
throw exception_t(depth);
}
static PyObject*
test_exception_switch_recurse(int depth, int left)
{
if (left > 0) {
return p_test_exception_switch_recurse(depth, left - 1);
}
PyObject* result = NULL;
PyGreenlet* self = PyGreenlet_GetCurrent();
if (self == NULL)
return NULL;
try {
PyGreenlet_Switch(self->parent, NULL, NULL);
p_test_exception_throw(depth);
PyErr_SetString(PyExc_RuntimeError,
"throwing C++ exception didn't work");
}
catch (exception_t& e) {
if (e.depth != depth)
PyErr_SetString(PyExc_AssertionError, "depth mismatch");
else
result = PyLong_FromLong(depth);
}
catch (...) {
PyErr_SetString(PyExc_RuntimeError, "unexpected C++ exception");
}
Py_DECREF(self);
return result;
}
/* test_exception_switch(int depth)
* - recurses depth times
* - switches to parent inside try/catch block
* - throws an exception that (expected to be caught in the same function)
* - verifies depth matches (exceptions shouldn't be caught in other greenlets)
*/
static PyObject*
test_exception_switch(PyObject* self, PyObject* args)
{
int depth;
if (!PyArg_ParseTuple(args, "i", &depth))
return NULL;
return p_test_exception_switch_recurse(depth, depth);
}
static PyMethodDef test_methods[] = {
{"test_exception_switch",
(PyCFunction)&test_exception_switch,
METH_VARARGS,
"Switches to parent twice, to test exception handling and greenlet "
"switching."},
{NULL, NULL, 0, NULL}};
#if PY_MAJOR_VERSION >= 3
# define INITERROR return NULL
static struct PyModuleDef moduledef = {PyModuleDef_HEAD_INIT,
"greenlet.tests._test_extension_cpp",
NULL,
0,
test_methods,
NULL,
NULL,
NULL,
NULL};
PyMODINIT_FUNC
PyInit__test_extension_cpp(void)
#else
# define INITERROR return
PyMODINIT_FUNC
init_test_extension_cpp(void)
#endif
{
PyObject* module = NULL;
#if PY_MAJOR_VERSION >= 3
module = PyModule_Create(&moduledef);
#else
module = Py_InitModule("greenlet.tests._test_extension_cpp", test_methods);
#endif
if (module == NULL) {
INITERROR;
}
PyGreenlet_Import();
if (_PyGreenlet_API == NULL) {
INITERROR;
}
p_test_exception_throw = test_exception_throw;
p_test_exception_switch_recurse = test_exception_switch_recurse;
#if PY_MAJOR_VERSION >= 3
return module;
#endif
}

View File

@@ -0,0 +1,266 @@
import unittest
import gc
import sys
from functools import partial
from greenlet import greenlet
from greenlet import getcurrent
try:
from contextvars import Context
from contextvars import ContextVar
from contextvars import copy_context
except ImportError:
Context = ContextVar = copy_context = None
# We don't support testing if greenlet's built-in context var support is disabled.
@unittest.skipUnless(Context is not None, "ContextVar not supported")
class ContextVarsTests(unittest.TestCase):
def _new_ctx_run(self, *args, **kwargs):
return copy_context().run(*args, **kwargs)
def _increment(self, greenlet_id, ctx_var, callback, counts, expect):
if expect is None:
self.assertIsNone(ctx_var.get())
else:
self.assertEqual(ctx_var.get(), expect)
ctx_var.set(greenlet_id)
for _ in range(2):
counts[ctx_var.get()] += 1
callback()
def _test_context(self, propagate_by):
id_var = ContextVar("id", default=None)
id_var.set(0)
callback = getcurrent().switch
counts = dict((i, 0) for i in range(5))
lets = [
greenlet(partial(
partial(
copy_context().run,
self._increment
) if propagate_by == "run" else self._increment,
greenlet_id=i,
ctx_var=id_var,
callback=callback,
counts=counts,
expect=(
i - 1 if propagate_by == "share" else
0 if propagate_by in ("set", "run") else None
)
))
for i in range(1, 5)
]
for let in lets:
if propagate_by == "set":
let.gr_context = copy_context()
elif propagate_by == "share":
let.gr_context = getcurrent().gr_context
for i in range(2):
counts[id_var.get()] += 1
for let in lets:
let.switch()
if propagate_by == "run":
# Must leave each context.run() in reverse order of entry
for let in reversed(lets):
let.switch()
else:
# No context.run(), so fine to exit in any order.
for let in lets:
let.switch()
for let in lets:
self.assertTrue(let.dead)
# When using run(), we leave the run() as the greenlet dies,
# and there's no context "underneath". When not using run(),
# gr_context still reflects the context the greenlet was
# running in.
self.assertEqual(let.gr_context is None, propagate_by == "run")
if propagate_by == "share":
self.assertEqual(counts, {0: 1, 1: 1, 2: 1, 3: 1, 4: 6})
else:
self.assertEqual(set(counts.values()), set([2]))
def test_context_propagated_by_context_run(self):
self._new_ctx_run(self._test_context, "run")
def test_context_propagated_by_setting_attribute(self):
self._new_ctx_run(self._test_context, "set")
def test_context_not_propagated(self):
self._new_ctx_run(self._test_context, None)
def test_context_shared(self):
self._new_ctx_run(self._test_context, "share")
def test_break_ctxvars(self):
let1 = greenlet(copy_context().run)
let2 = greenlet(copy_context().run)
let1.switch(getcurrent().switch)
let2.switch(getcurrent().switch)
# Since let2 entered the current context and let1 exits its own, the
# interpreter emits:
# RuntimeError: cannot exit context: thread state references a different context object
let1.switch()
def test_not_broken_if_using_attribute_instead_of_context_run(self):
let1 = greenlet(getcurrent().switch)
let2 = greenlet(getcurrent().switch)
let1.gr_context = copy_context()
let2.gr_context = copy_context()
let1.switch()
let2.switch()
let1.switch()
let2.switch()
def test_context_assignment_while_running(self):
id_var = ContextVar("id", default=None)
def target():
self.assertIsNone(id_var.get())
self.assertIsNone(gr.gr_context)
# Context is created on first use
id_var.set(1)
self.assertIsInstance(gr.gr_context, Context)
self.assertEqual(id_var.get(), 1)
self.assertEqual(gr.gr_context[id_var], 1)
# Clearing the context makes it get re-created as another
# empty context when next used
old_context = gr.gr_context
gr.gr_context = None # assign None while running
self.assertIsNone(id_var.get())
self.assertIsNone(gr.gr_context)
id_var.set(2)
self.assertIsInstance(gr.gr_context, Context)
self.assertEqual(id_var.get(), 2)
self.assertEqual(gr.gr_context[id_var], 2)
new_context = gr.gr_context
getcurrent().parent.switch((old_context, new_context))
# parent switches us back to old_context
self.assertEqual(id_var.get(), 1)
gr.gr_context = new_context # assign non-None while running
self.assertEqual(id_var.get(), 2)
getcurrent().parent.switch()
# parent switches us back to no context
self.assertIsNone(id_var.get())
self.assertIsNone(gr.gr_context)
gr.gr_context = old_context
self.assertEqual(id_var.get(), 1)
getcurrent().parent.switch()
# parent switches us back to no context
self.assertIsNone(id_var.get())
self.assertIsNone(gr.gr_context)
gr = greenlet(target)
with self.assertRaisesRegex(AttributeError, "can't delete attr"):
del gr.gr_context
self.assertIsNone(gr.gr_context)
old_context, new_context = gr.switch()
self.assertIs(new_context, gr.gr_context)
self.assertEqual(old_context[id_var], 1)
self.assertEqual(new_context[id_var], 2)
self.assertEqual(new_context.run(id_var.get), 2)
gr.gr_context = old_context # assign non-None while suspended
gr.switch()
self.assertIs(gr.gr_context, new_context)
gr.gr_context = None # assign None while suspended
gr.switch()
self.assertIs(gr.gr_context, old_context)
gr.gr_context = None
gr.switch()
self.assertIsNone(gr.gr_context)
# Make sure there are no reference leaks
gr = None
gc.collect()
self.assertEqual(sys.getrefcount(old_context), 2)
self.assertEqual(sys.getrefcount(new_context), 2)
def test_context_assignment_different_thread(self):
import threading
ctx = Context()
var = ContextVar("var", default=None)
is_running = threading.Event()
should_suspend = threading.Event()
did_suspend = threading.Event()
should_exit = threading.Event()
holder = []
def greenlet_in_thread_fn():
var.set(1)
is_running.set()
should_suspend.wait()
var.set(2)
getcurrent().parent.switch()
holder.append(var.get())
def thread_fn():
gr = greenlet(greenlet_in_thread_fn)
gr.gr_context = ctx
holder.append(gr)
gr.switch()
did_suspend.set()
should_exit.wait()
gr.switch()
thread = threading.Thread(target=thread_fn, daemon=True)
thread.start()
is_running.wait()
gr = holder[0]
# Can't access or modify context if the greenlet is running
# in a different thread
with self.assertRaisesRegex(ValueError, "running in a different"):
getattr(gr, 'gr_context')
with self.assertRaisesRegex(ValueError, "running in a different"):
gr.gr_context = None
should_suspend.set()
did_suspend.wait()
# OK to access and modify context if greenlet is suspended
self.assertIs(gr.gr_context, ctx)
self.assertEqual(gr.gr_context[var], 2)
gr.gr_context = None
should_exit.set()
thread.join()
self.assertEqual(holder, [gr, None])
# Context can still be accessed/modified when greenlet is dead:
self.assertIsNone(gr.gr_context)
gr.gr_context = ctx
self.assertIs(gr.gr_context, ctx)
@unittest.skipIf(Context is not None, "ContextVar supported")
class NoContextVarsTests(unittest.TestCase):
def test_contextvars_errors(self):
let1 = greenlet(getcurrent().switch)
self.assertFalse(hasattr(let1, 'gr_context'))
with self.assertRaises(AttributeError):
getattr(let1, 'gr_context')
with self.assertRaises(AttributeError):
let1.gr_context = None
let1.switch()
with self.assertRaises(AttributeError):
getattr(let1, 'gr_context')
with self.assertRaises(AttributeError):
let1.gr_context = None

View File

@@ -0,0 +1,18 @@
from __future__ import print_function
from __future__ import absolute_import
import unittest
import greenlet
from . import _test_extension_cpp
class CPPTests(unittest.TestCase):
def test_exception_switch(self):
greenlets = []
for i in range(4):
g = greenlet.greenlet(_test_extension_cpp.test_exception_switch)
g.switch(i)
greenlets.append(g)
for i, g in enumerate(greenlets):
self.assertEqual(g.switch(), i)

View File

@@ -0,0 +1,77 @@
from __future__ import print_function
from __future__ import absolute_import
import sys
import unittest
import greenlet
from . import _test_extension
class CAPITests(unittest.TestCase):
def test_switch(self):
self.assertEqual(
50, _test_extension.test_switch(greenlet.greenlet(lambda: 50)))
def test_switch_kwargs(self):
def foo(x, y):
return x * y
g = greenlet.greenlet(foo)
self.assertEqual(6, _test_extension.test_switch_kwargs(g, x=3, y=2))
def test_setparent(self):
def foo():
def bar():
greenlet.getcurrent().parent.switch()
# This final switch should go back to the main greenlet, since
# the test_setparent() function in the C extension should have
# reparented this greenlet.
greenlet.getcurrent().parent.switch()
raise AssertionError("Should never have reached this code")
child = greenlet.greenlet(bar)
child.switch()
greenlet.getcurrent().parent.switch(child)
greenlet.getcurrent().parent.throw(
AssertionError("Should never reach this code"))
foo_child = greenlet.greenlet(foo).switch()
self.assertEqual(None, _test_extension.test_setparent(foo_child))
def test_getcurrent(self):
_test_extension.test_getcurrent()
def test_new_greenlet(self):
self.assertEqual(-15, _test_extension.test_new_greenlet(lambda: -15))
def test_raise_greenlet_dead(self):
self.assertRaises(
greenlet.GreenletExit, _test_extension.test_raise_dead_greenlet)
def test_raise_greenlet_error(self):
self.assertRaises(
greenlet.error, _test_extension.test_raise_greenlet_error)
def test_throw(self):
seen = []
def foo():
try:
greenlet.getcurrent().parent.switch()
except ValueError:
seen.append(sys.exc_info()[1])
except greenlet.GreenletExit:
raise AssertionError
g = greenlet.greenlet(foo)
g.switch()
_test_extension.test_throw(g)
self.assertEqual(len(seen), 1)
self.assertTrue(
isinstance(seen[0], ValueError),
"ValueError was not raised in foo()")
self.assertEqual(
str(seen[0]),
'take that sucka!',
"message doesn't match")
if __name__ == '__main__':
unittest.main()

View File

@@ -0,0 +1,77 @@
import gc
import sys
import unittest
import weakref
import greenlet
class GCTests(unittest.TestCase):
def test_dead_circular_ref(self):
o = weakref.ref(greenlet.greenlet(greenlet.getcurrent).switch())
gc.collect()
self.assertTrue(o() is None)
self.assertFalse(gc.garbage, gc.garbage)
if greenlet.GREENLET_USE_GC:
# These only work with greenlet gc support
def test_circular_greenlet(self):
class circular_greenlet(greenlet.greenlet):
pass
o = circular_greenlet()
o.self = o
o = weakref.ref(o)
gc.collect()
self.assertTrue(o() is None)
self.assertFalse(gc.garbage, gc.garbage)
def test_inactive_ref(self):
class inactive_greenlet(greenlet.greenlet):
def __init__(self):
greenlet.greenlet.__init__(self, run=self.run)
def run(self):
pass
o = inactive_greenlet()
o = weakref.ref(o)
gc.collect()
self.assertTrue(o() is None)
self.assertFalse(gc.garbage, gc.garbage)
def test_finalizer_crash(self):
# This test is designed to crash when active greenlets
# are made garbage collectable, until the underlying
# problem is resolved. How does it work:
# - order of object creation is important
# - array is created first, so it is moved to unreachable first
# - we create a cycle between a greenlet and this array
# - we create an object that participates in gc, is only
# referenced by a greenlet, and would corrupt gc lists
# on destruction, the easiest is to use an object with
# a finalizer
# - because array is the first object in unreachable it is
# cleared first, which causes all references to greenlet
# to disappear and causes greenlet to be destroyed, but since
# it is still live it causes a switch during gc, which causes
# an object with finalizer to be destroyed, which causes stack
# corruption and then a crash
class object_with_finalizer(object):
def __del__(self):
pass
array = []
parent = greenlet.getcurrent()
def greenlet_body():
greenlet.getcurrent().object = object_with_finalizer()
try:
parent.switch()
finally:
del greenlet.getcurrent().object
g = greenlet.greenlet(greenlet_body)
g.array = array
array.append(g)
g.switch()
del array
del g
greenlet.getcurrent()
gc.collect()

View File

@@ -0,0 +1,59 @@
import unittest
from greenlet import greenlet
class genlet(greenlet):
def __init__(self, *args, **kwds):
self.args = args
self.kwds = kwds
def run(self):
fn, = self.fn
fn(*self.args, **self.kwds)
def __iter__(self):
return self
def __next__(self):
self.parent = greenlet.getcurrent()
result = self.switch()
if self:
return result
else:
raise StopIteration
# Hack: Python < 2.6 compatibility
next = __next__
def Yield(value):
g = greenlet.getcurrent()
while not isinstance(g, genlet):
if g is None:
raise RuntimeError('yield outside a genlet')
g = g.parent
g.parent.switch(value)
def generator(func):
class generator(genlet):
fn = (func,)
return generator
# ____________________________________________________________
class GeneratorTests(unittest.TestCase):
def test_generator(self):
seen = []
def g(n):
for i in range(n):
seen.append(i)
Yield(i)
g = generator(g)
for k in range(3):
for j in g(5):
seen.append(j)
self.assertEqual(seen, 3 * [0, 0, 1, 1, 2, 2, 3, 3, 4, 4])

View File

@@ -0,0 +1,165 @@
import unittest
from greenlet import greenlet
class genlet(greenlet):
def __init__(self, *args, **kwds):
self.args = args
self.kwds = kwds
self.child = None
def run(self):
fn, = self.fn
fn(*self.args, **self.kwds)
def __iter__(self):
return self
def set_child(self, child):
self.child = child
def __next__(self):
if self.child:
child = self.child
while child.child:
tmp = child
child = child.child
tmp.child = None
result = child.switch()
else:
self.parent = greenlet.getcurrent()
result = self.switch()
if self:
return result
else:
raise StopIteration
# Hack: Python < 2.6 compatibility
next = __next__
def Yield(value, level=1):
g = greenlet.getcurrent()
while level != 0:
if not isinstance(g, genlet):
raise RuntimeError('yield outside a genlet')
if level > 1:
g.parent.set_child(g)
g = g.parent
level -= 1
g.switch(value)
def Genlet(func):
class Genlet(genlet):
fn = (func,)
return Genlet
# ____________________________________________________________
def g1(n, seen):
for i in range(n):
seen.append(i + 1)
yield i
def g2(n, seen):
for i in range(n):
seen.append(i + 1)
Yield(i)
g2 = Genlet(g2)
def nested(i):
Yield(i)
def g3(n, seen):
for i in range(n):
seen.append(i + 1)
nested(i)
g3 = Genlet(g3)
def a(n):
if n == 0:
return
for ii in ax(n - 1):
Yield(ii)
Yield(n)
ax = Genlet(a)
def perms(l):
if len(l) > 1:
for e in l:
# No syntactical sugar for generator expressions
[Yield([e] + p) for p in perms([x for x in l if x != e])]
else:
Yield(l)
perms = Genlet(perms)
def gr1(n):
for ii in range(1, n):
Yield(ii)
Yield(ii * ii, 2)
gr1 = Genlet(gr1)
def gr2(n, seen):
for ii in gr1(n):
seen.append(ii)
gr2 = Genlet(gr2)
class NestedGeneratorTests(unittest.TestCase):
def test_layered_genlets(self):
seen = []
for ii in gr2(5, seen):
seen.append(ii)
self.assertEqual(seen, [1, 1, 2, 4, 3, 9, 4, 16])
def test_permutations(self):
gen_perms = perms(list(range(4)))
permutations = list(gen_perms)
self.assertEqual(len(permutations), 4 * 3 * 2 * 1)
self.assertTrue([0, 1, 2, 3] in permutations)
self.assertTrue([3, 2, 1, 0] in permutations)
res = []
for ii in zip(perms(list(range(4))), perms(list(range(3)))):
res.append(ii)
self.assertEqual(
res,
[([0, 1, 2, 3], [0, 1, 2]), ([0, 1, 3, 2], [0, 2, 1]),
([0, 2, 1, 3], [1, 0, 2]), ([0, 2, 3, 1], [1, 2, 0]),
([0, 3, 1, 2], [2, 0, 1]), ([0, 3, 2, 1], [2, 1, 0])])
# XXX Test to make sure we are working as a generator expression
def test_genlet_simple(self):
for g in [g1, g2, g3]:
seen = []
for k in range(3):
for j in g(5, seen):
seen.append(j)
self.assertEqual(seen, 3 * [1, 0, 2, 1, 3, 2, 4, 3, 5, 4])
def test_genlet_bad(self):
try:
Yield(10)
except RuntimeError:
pass
def test_nested_genlets(self):
seen = []
for ii in ax(5):
seen.append(ii)

View File

@@ -0,0 +1,728 @@
import gc
import sys
import time
import threading
import unittest
from abc import ABCMeta, abstractmethod
from greenlet import greenlet
# We manually manage locks in many tests
# pylint:disable=consider-using-with
class SomeError(Exception):
pass
def fmain(seen):
try:
greenlet.getcurrent().parent.switch()
except:
seen.append(sys.exc_info()[0])
raise
raise SomeError
def send_exception(g, exc):
# note: send_exception(g, exc) can be now done with g.throw(exc).
# the purpose of this test is to explicitely check the propagation rules.
def crasher(exc):
raise exc
g1 = greenlet(crasher, parent=g)
g1.switch(exc)
class TestGreenlet(unittest.TestCase):
def test_simple(self):
lst = []
def f():
lst.append(1)
greenlet.getcurrent().parent.switch()
lst.append(3)
g = greenlet(f)
lst.append(0)
g.switch()
lst.append(2)
g.switch()
lst.append(4)
self.assertEqual(lst, list(range(5)))
def test_parent_equals_None(self):
g = greenlet(parent=None)
self.assertIsNotNone(g)
self.assertIs(g.parent, greenlet.getcurrent())
def test_run_equals_None(self):
g = greenlet(run=None)
self.assertIsNotNone(g)
self.assertIsNone(g.run)
def test_two_children(self):
lst = []
def f():
lst.append(1)
greenlet.getcurrent().parent.switch()
lst.extend([1, 1])
g = greenlet(f)
h = greenlet(f)
g.switch()
self.assertEqual(len(lst), 1)
h.switch()
self.assertEqual(len(lst), 2)
h.switch()
self.assertEqual(len(lst), 4)
self.assertEqual(h.dead, True)
g.switch()
self.assertEqual(len(lst), 6)
self.assertEqual(g.dead, True)
def test_two_recursive_children(self):
lst = []
def f():
lst.append(1)
greenlet.getcurrent().parent.switch()
def g():
lst.append(1)
g = greenlet(f)
g.switch()
lst.append(1)
g = greenlet(g)
g.switch()
self.assertEqual(len(lst), 3)
self.assertEqual(sys.getrefcount(g), 2)
def test_threads(self):
success = []
def f():
self.test_simple()
success.append(True)
ths = [threading.Thread(target=f) for i in range(10)]
for th in ths:
th.start()
for th in ths:
th.join()
self.assertEqual(len(success), len(ths))
def test_exception(self):
seen = []
g1 = greenlet(fmain)
g2 = greenlet(fmain)
g1.switch(seen)
g2.switch(seen)
g2.parent = g1
self.assertEqual(seen, [])
self.assertRaises(SomeError, g2.switch)
self.assertEqual(seen, [SomeError])
g2.switch()
self.assertEqual(seen, [SomeError])
def test_send_exception(self):
seen = []
g1 = greenlet(fmain)
g1.switch(seen)
self.assertRaises(KeyError, send_exception, g1, KeyError)
self.assertEqual(seen, [KeyError])
def test_dealloc(self):
seen = []
g1 = greenlet(fmain)
g2 = greenlet(fmain)
g1.switch(seen)
g2.switch(seen)
self.assertEqual(seen, [])
del g1
gc.collect()
self.assertEqual(seen, [greenlet.GreenletExit])
del g2
gc.collect()
self.assertEqual(seen, [greenlet.GreenletExit, greenlet.GreenletExit])
def test_dealloc_other_thread(self):
seen = []
someref = []
lock = threading.Lock()
lock.acquire()
lock2 = threading.Lock()
lock2.acquire()
def f():
g1 = greenlet(fmain)
g1.switch(seen)
someref.append(g1)
del g1
gc.collect()
lock.release()
lock2.acquire()
greenlet() # trigger release
lock.release()
lock2.acquire()
t = threading.Thread(target=f)
t.start()
lock.acquire()
self.assertEqual(seen, [])
self.assertEqual(len(someref), 1)
del someref[:]
gc.collect()
# g1 is not released immediately because it's from another thread
self.assertEqual(seen, [])
lock2.release()
lock.acquire()
self.assertEqual(seen, [greenlet.GreenletExit])
lock2.release()
t.join()
def test_frame(self):
def f1():
f = sys._getframe(0) # pylint:disable=protected-access
self.assertEqual(f.f_back, None)
greenlet.getcurrent().parent.switch(f)
return "meaning of life"
g = greenlet(f1)
frame = g.switch()
self.assertTrue(frame is g.gr_frame)
self.assertTrue(g)
from_g = g.switch()
self.assertFalse(g)
self.assertEqual(from_g, 'meaning of life')
self.assertEqual(g.gr_frame, None)
def test_thread_bug(self):
def runner(x):
g = greenlet(lambda: time.sleep(x))
g.switch()
t1 = threading.Thread(target=runner, args=(0.2,))
t2 = threading.Thread(target=runner, args=(0.3,))
t1.start()
t2.start()
t1.join()
t2.join()
def test_switch_kwargs(self):
def run(a, b):
self.assertEqual(a, 4)
self.assertEqual(b, 2)
return 42
x = greenlet(run).switch(a=4, b=2)
self.assertEqual(x, 42)
def test_switch_kwargs_to_parent(self):
def run(x):
greenlet.getcurrent().parent.switch(x=x)
greenlet.getcurrent().parent.switch(2, x=3)
return x, x ** 2
g = greenlet(run)
self.assertEqual({'x': 3}, g.switch(3))
self.assertEqual(((2,), {'x': 3}), g.switch())
self.assertEqual((3, 9), g.switch())
def test_switch_to_another_thread(self):
data = {}
error = None
created_event = threading.Event()
done_event = threading.Event()
def run():
data['g'] = greenlet(lambda: None)
created_event.set()
done_event.wait()
thread = threading.Thread(target=run)
thread.start()
created_event.wait()
try:
data['g'].switch()
except greenlet.error:
error = sys.exc_info()[1]
self.assertIsNotNone(error, "greenlet.error was not raised!")
done_event.set()
thread.join()
def test_exc_state(self):
def f():
try:
raise ValueError('fun')
except: # pylint:disable=bare-except
exc_info = sys.exc_info()
greenlet(h).switch()
self.assertEqual(exc_info, sys.exc_info())
def h():
self.assertEqual(sys.exc_info(), (None, None, None))
greenlet(f).switch()
def test_instance_dict(self):
def f():
greenlet.getcurrent().test = 42
def deldict(g):
del g.__dict__
def setdict(g, value):
g.__dict__ = value
g = greenlet(f)
self.assertEqual(g.__dict__, {})
g.switch()
self.assertEqual(g.test, 42)
self.assertEqual(g.__dict__, {'test': 42})
g.__dict__ = g.__dict__
self.assertEqual(g.__dict__, {'test': 42})
self.assertRaises(TypeError, deldict, g)
self.assertRaises(TypeError, setdict, g, 42)
def test_threaded_reparent(self):
data = {}
created_event = threading.Event()
done_event = threading.Event()
def run():
data['g'] = greenlet(lambda: None)
created_event.set()
done_event.wait()
def blank():
greenlet.getcurrent().parent.switch()
def setparent(g, value):
g.parent = value
thread = threading.Thread(target=run)
thread.start()
created_event.wait()
g = greenlet(blank)
g.switch()
self.assertRaises(ValueError, setparent, g, data['g'])
done_event.set()
thread.join()
def test_deepcopy(self):
import copy
self.assertRaises(TypeError, copy.copy, greenlet())
self.assertRaises(TypeError, copy.deepcopy, greenlet())
def test_parent_restored_on_kill(self):
hub = greenlet(lambda: None)
main = greenlet.getcurrent()
result = []
def worker():
try:
# Wait to be killed
main.switch()
except greenlet.GreenletExit:
# Resurrect and switch to parent
result.append(greenlet.getcurrent().parent)
result.append(greenlet.getcurrent())
hub.switch()
g = greenlet(worker, parent=hub)
g.switch()
del g
self.assertTrue(result)
self.assertEqual(result[0], main)
self.assertEqual(result[1].parent, hub)
def test_parent_return_failure(self):
# No run causes AttributeError on switch
g1 = greenlet()
# Greenlet that implicitly switches to parent
g2 = greenlet(lambda: None, parent=g1)
# AttributeError should propagate to us, no fatal errors
self.assertRaises(AttributeError, g2.switch)
def test_throw_exception_not_lost(self):
class mygreenlet(greenlet):
def __getattribute__(self, name):
try:
raise Exception()
except: # pylint:disable=bare-except
pass
return greenlet.__getattribute__(self, name)
g = mygreenlet(lambda: None)
self.assertRaises(SomeError, g.throw, SomeError())
def test_throw_doesnt_crash(self):
result = []
def worker():
greenlet.getcurrent().parent.switch()
def creator():
g = greenlet(worker)
g.switch()
result.append(g)
t = threading.Thread(target=creator)
t.start()
t.join()
self.assertRaises(greenlet.error, result[0].throw, SomeError())
def test_recursive_startup(self):
class convoluted(greenlet):
def __init__(self):
greenlet.__init__(self)
self.count = 0
def __getattribute__(self, name):
if name == 'run' and self.count == 0:
self.count = 1
self.switch(43)
return greenlet.__getattribute__(self, name)
def run(self, value):
while True:
self.parent.switch(value)
g = convoluted()
self.assertEqual(g.switch(42), 43)
def test_unexpected_reparenting(self):
another = []
def worker():
g = greenlet(lambda: None)
another.append(g)
g.switch()
t = threading.Thread(target=worker)
t.start()
t.join()
class convoluted(greenlet):
def __getattribute__(self, name):
if name == 'run':
self.parent = another[0] # pylint:disable=attribute-defined-outside-init
return greenlet.__getattribute__(self, name)
g = convoluted(lambda: None)
self.assertRaises(greenlet.error, g.switch)
def test_threaded_updatecurrent(self):
# released when main thread should execute
lock1 = threading.Lock()
lock1.acquire()
# released when another thread should execute
lock2 = threading.Lock()
lock2.acquire()
class finalized(object):
def __del__(self):
# happens while in green_updatecurrent() in main greenlet
# should be very careful not to accidentally call it again
# at the same time we must make sure another thread executes
lock2.release()
lock1.acquire()
# now ts_current belongs to another thread
def deallocator():
greenlet.getcurrent().parent.switch()
def fthread():
lock2.acquire()
greenlet.getcurrent()
del g[0]
lock1.release()
lock2.acquire()
greenlet.getcurrent()
lock1.release()
main = greenlet.getcurrent()
g = [greenlet(deallocator)]
g[0].bomb = finalized()
g[0].switch()
t = threading.Thread(target=fthread)
t.start()
# let another thread grab ts_current and deallocate g[0]
lock2.release()
lock1.acquire()
# this is the corner stone
# getcurrent() will notice that ts_current belongs to another thread
# and start the update process, which would notice that g[0] should
# be deallocated, and that will execute an object's finalizer. Now,
# that object will let another thread run so it can grab ts_current
# again, which would likely crash the interpreter if there's no
# check for this case at the end of green_updatecurrent(). This test
# passes if getcurrent() returns correct result, but it's likely
# to randomly crash if it's not anyway.
self.assertEqual(greenlet.getcurrent(), main)
# wait for another thread to complete, just in case
t.join()
def test_dealloc_switch_args_not_lost(self):
seen = []
def worker():
# wait for the value
value = greenlet.getcurrent().parent.switch()
# delete all references to ourself
del worker[0]
initiator.parent = greenlet.getcurrent().parent
# switch to main with the value, but because
# ts_current is the last reference to us we
# return immediately
try:
greenlet.getcurrent().parent.switch(value)
finally:
seen.append(greenlet.getcurrent())
def initiator():
return 42 # implicitly falls thru to parent
worker = [greenlet(worker)]
worker[0].switch() # prime worker
initiator = greenlet(initiator, worker[0])
value = initiator.switch()
self.assertTrue(seen)
self.assertEqual(value, 42)
def test_tuple_subclass(self):
if sys.version_info[0] > 2:
# There's no apply in Python 3.x
def _apply(func, a, k):
func(*a, **k)
else:
_apply = apply # pylint:disable=undefined-variable
class mytuple(tuple):
def __len__(self):
greenlet.getcurrent().switch()
return tuple.__len__(self)
args = mytuple()
kwargs = dict(a=42)
def switchapply():
_apply(greenlet.getcurrent().parent.switch, args, kwargs)
g = greenlet(switchapply)
self.assertEqual(g.switch(), kwargs)
def test_abstract_subclasses(self):
AbstractSubclass = ABCMeta(
'AbstractSubclass',
(greenlet,),
{'run': abstractmethod(lambda self: None)})
class BadSubclass(AbstractSubclass):
pass
class GoodSubclass(AbstractSubclass):
def run(self):
pass
GoodSubclass() # should not raise
self.assertRaises(TypeError, BadSubclass)
def test_implicit_parent_with_threads(self):
if not gc.isenabled():
return # cannot test with disabled gc
N = gc.get_threshold()[0]
if N < 50:
return # cannot test with such a small N
def attempt():
lock1 = threading.Lock()
lock1.acquire()
lock2 = threading.Lock()
lock2.acquire()
recycled = [False]
def another_thread():
lock1.acquire() # wait for gc
greenlet.getcurrent() # update ts_current
lock2.release() # release gc
t = threading.Thread(target=another_thread)
t.start()
class gc_callback(object):
def __del__(self):
lock1.release()
lock2.acquire()
recycled[0] = True
class garbage(object):
def __init__(self):
self.cycle = self
self.callback = gc_callback()
l = []
x = range(N*2)
current = greenlet.getcurrent()
g = garbage()
for _ in x:
g = None # lose reference to garbage
if recycled[0]:
# gc callback called prematurely
t.join()
return False
last = greenlet()
if recycled[0]:
break # yes! gc called in green_new
l.append(last) # increase allocation counter
else:
# gc callback not called when expected
gc.collect()
if recycled[0]:
t.join()
return False
self.assertEqual(last.parent, current)
for g in l:
self.assertEqual(g.parent, current)
return True
for _ in range(5):
if attempt():
break
def test_issue_245_reference_counting_subclass_no_threads(self):
# https://github.com/python-greenlet/greenlet/issues/245
# Before the fix, this crashed pretty reliably on
# Python 3.10, at least on macOS; but much less reliably on other
# interpreters (memory layout must have changed).
# The threaded test crashed more reliably on more interpreters.
from greenlet import getcurrent
from greenlet import GreenletExit
class Greenlet(greenlet):
pass
initial_refs = sys.getrefcount(Greenlet)
# This has to be an instance variable because
# Python 2 raises a SyntaxError if we delete a local
# variable referenced in an inner scope.
self.glets = [] # pylint:disable=attribute-defined-outside-init
def greenlet_main():
try:
getcurrent().parent.switch()
except GreenletExit:
self.glets.append(getcurrent())
# Before the
for _ in range(10):
Greenlet(greenlet_main).switch()
del self.glets
self.assertEqual(sys.getrefcount(Greenlet), initial_refs)
def test_issue_245_reference_counting_subclass_threads(self):
# https://github.com/python-greenlet/greenlet/issues/245
from threading import Thread
from threading import Event
from greenlet import getcurrent
class MyGreenlet(greenlet):
pass
glets = []
ref_cleared = Event()
def greenlet_main():
getcurrent().parent.switch()
def thread_main(greenlet_running_event):
mine = MyGreenlet(greenlet_main)
glets.append(mine)
# The greenlets being deleted must be active
mine.switch()
# Don't keep any reference to it in this thread
del mine
# Let main know we published our greenlet.
greenlet_running_event.set()
# Wait for main to let us know the references are
# gone and the greenlet objects no longer reachable
ref_cleared.wait()
# The creating thread must call getcurrent() (or a few other
# greenlet APIs) because that's when the thread-local list of dead
# greenlets gets cleared.
getcurrent()
# We start with 3 references to the subclass:
# - This module
# - Its __mro__
# - The __subclassess__ attribute of greenlet
# - (If we call gc.get_referents(), we find four entries, including
# some other tuple ``(greenlet)`` that I'm not sure about but must be part
# of the machinery.)
#
# On Python 3.10 it's often enough to just run 3 threads; on Python 2.7,
# more threads are needed, and the results are still
# non-deterministic. Presumably the memory layouts are different
initial_refs = sys.getrefcount(MyGreenlet)
thread_ready_events = []
for _ in range(
initial_refs + 45
):
event = Event()
thread = Thread(target=thread_main, args=(event,))
thread_ready_events.append(event)
thread.start()
for done_event in thread_ready_events:
done_event.wait()
del glets[:]
ref_cleared.set()
# Let any other thread run; it will crash the interpreter
# if not fixed (or silently corrupt memory and we possibly crash
# later).
time.sleep(1)
self.assertEqual(sys.getrefcount(MyGreenlet), initial_refs)
class TestRepr(unittest.TestCase):
def assertEndsWith(self, got, suffix):
self.assertTrue(got.endswith(suffix), (got, suffix))
def test_main_while_running(self):
r = repr(greenlet.getcurrent())
self.assertEndsWith(r, " current active started main>")
def test_main_in_background(self):
main = greenlet.getcurrent()
def run():
return repr(main)
g = greenlet(run)
r = g.switch()
self.assertEndsWith(r, ' suspended active started main>')
def test_initial(self):
r = repr(greenlet())
self.assertEndsWith(r, ' pending>')
def test_main_from_other_thread(self):
main = greenlet.getcurrent()
class T(threading.Thread):
original_main = thread_main = None
main_glet = None
def run(self):
self.original_main = repr(main)
self.main_glet = greenlet.getcurrent()
self.thread_main = repr(self.main_glet)
t = T()
t.start()
t.join(10)
self.assertEndsWith(t.original_main, ' suspended active started main>')
self.assertEndsWith(t.thread_main, ' current active started main>')
r = repr(t.main_glet)
# main greenlets, even from dead threads, never really appear dead
# TODO: Can we find a better way to differentiate that?
assert not t.main_glet.dead
self.assertEndsWith(r, ' suspended active started main>')
def test_dead(self):
g = greenlet(lambda: None)
g.switch()
self.assertEndsWith(repr(g), ' dead>')
self.assertNotIn('suspended', repr(g))
self.assertNotIn('started', repr(g))
self.assertNotIn('active', repr(g))
def test_formatting_produces_native_str(self):
# https://github.com/python-greenlet/greenlet/issues/218
# %s formatting on Python 2 was producing unicode, not str.
g_dead = greenlet(lambda: None)
g_not_started = greenlet(lambda: None)
g_cur = greenlet.getcurrent()
for g in g_dead, g_not_started, g_cur:
self.assertIsInstance(
'%s' % (g,),
str
)
self.assertIsInstance(
'%r' % (g,),
str,
)
if __name__ == '__main__':
unittest.main()

View File

@@ -0,0 +1,178 @@
import unittest
import sys
import gc
import time
import weakref
import threading
import greenlet
class TestLeaks(unittest.TestCase):
def test_arg_refs(self):
args = ('a', 'b', 'c')
refcount_before = sys.getrefcount(args)
# pylint:disable=unnecessary-lambda
g = greenlet.greenlet(
lambda *args: greenlet.getcurrent().parent.switch(*args))
for _ in range(100):
g.switch(*args)
self.assertEqual(sys.getrefcount(args), refcount_before)
def test_kwarg_refs(self):
kwargs = {}
# pylint:disable=unnecessary-lambda
g = greenlet.greenlet(
lambda **kwargs: greenlet.getcurrent().parent.switch(**kwargs))
for _ in range(100):
g.switch(**kwargs)
self.assertEqual(sys.getrefcount(kwargs), 2)
assert greenlet.GREENLET_USE_GC # Option to disable this was removed in 1.0
def recycle_threads(self):
# By introducing a thread that does sleep we allow other threads,
# that have triggered their __block condition, but did not have a
# chance to deallocate their thread state yet, to finally do so.
# The way it works is by requiring a GIL switch (different thread),
# which does a GIL release (sleep), which might do a GIL switch
# to finished threads and allow them to clean up.
def worker():
time.sleep(0.001)
t = threading.Thread(target=worker)
t.start()
time.sleep(0.001)
t.join()
def test_threaded_leak(self):
gg = []
def worker():
# only main greenlet present
gg.append(weakref.ref(greenlet.getcurrent()))
for _ in range(2):
t = threading.Thread(target=worker)
t.start()
t.join()
del t
greenlet.getcurrent() # update ts_current
self.recycle_threads()
greenlet.getcurrent() # update ts_current
gc.collect()
greenlet.getcurrent() # update ts_current
for g in gg:
self.assertIsNone(g())
def test_threaded_adv_leak(self):
gg = []
def worker():
# main and additional *finished* greenlets
ll = greenlet.getcurrent().ll = []
def additional():
ll.append(greenlet.getcurrent())
for _ in range(2):
greenlet.greenlet(additional).switch()
gg.append(weakref.ref(greenlet.getcurrent()))
for _ in range(2):
t = threading.Thread(target=worker)
t.start()
t.join()
del t
greenlet.getcurrent() # update ts_current
self.recycle_threads()
greenlet.getcurrent() # update ts_current
gc.collect()
greenlet.getcurrent() # update ts_current
for g in gg:
self.assertIsNone(g())
def test_issue251_killing_cross_thread_leaks_list(self, manually_collect_background=True):
# See https://github.com/python-greenlet/greenlet/issues/251
# Killing a greenlet (probably not the main one)
# in one thread from another thread would
# result in leaking a list (the ts_delkey list).
# For the test to be valid, even empty lists have to be tracked by the
# GC
assert gc.is_tracked([])
def count_objects(kind=list):
# pylint:disable=unidiomatic-typecheck
# Collect the garbage.
for _ in range(3):
gc.collect()
gc.collect()
return sum(
1
for x in gc.get_objects()
if type(x) is kind
)
# XXX: The main greenlet of a dead thread is only released
# when one of the proper greenlet APIs is used from a different
# running thread. See #252 (https://github.com/python-greenlet/greenlet/issues/252)
greenlet.getcurrent()
greenlets_before = count_objects(greenlet.greenlet)
background_glet_running = threading.Event()
background_glet_killed = threading.Event()
background_greenlets = []
def background_greenlet():
# Throw control back to the main greenlet.
greenlet.getcurrent().parent.switch()
def background_thread():
glet = greenlet.greenlet(background_greenlet)
background_greenlets.append(glet)
glet.switch() # Be sure it's active.
# Control is ours again.
del glet # Delete one reference from the thread it runs in.
background_glet_running.set()
background_glet_killed.wait()
# To trigger the background collection of the dead
# greenlet, thus clearing out the contents of the list, we
# need to run some APIs. See issue 252.
if manually_collect_background:
greenlet.getcurrent()
t = threading.Thread(target=background_thread)
t.start()
background_glet_running.wait()
lists_before = count_objects()
assert len(background_greenlets) == 1
self.assertFalse(background_greenlets[0].dead)
# Delete the last reference to the background greenlet
# from a different thread. This puts it in the background thread's
# ts_delkey list.
del background_greenlets[:]
background_glet_killed.set()
# Now wait for the background thread to die.
t.join(10)
del t
# Free the background main greenlet by forcing greenlet to notice a difference.
greenlet.getcurrent()
greenlets_after = count_objects(greenlet.greenlet)
lists_after = count_objects()
# On 2.7, we observe that lists_after is smaller than
# lists_before. No idea what lists got cleaned up. All the
# Python 3 versions match exactly.
self.assertLessEqual(lists_after, lists_before)
self.assertEqual(greenlets_before, greenlets_after)
@unittest.expectedFailure
def test_issue251_issue252_need_to_collect_in_background(self):
# This still fails because the leak of the list
# still exists when we don't call a greenlet API before exiting the
# thread. The proximate cause is that neither of the two greenlets
# from the background thread are actually being destroyed, even though
# the GC is in fact visiting both objects.
# It's not clear where that leak is? For some reason the thread-local dict
# holding it isn't being cleaned up.
self.test_issue251_killing_cross_thread_leaks_list(manually_collect_background=False)

View File

@@ -0,0 +1,19 @@
import greenlet
import unittest
class Test(unittest.TestCase):
def test_stack_saved(self):
main = greenlet.getcurrent()
self.assertEqual(main._stack_saved, 0)
def func():
main.switch(main._stack_saved)
g = greenlet.greenlet(func)
x = g.switch()
assert x > 0, x
assert g._stack_saved > 0, g._stack_saved
g.switch()
assert g._stack_saved == 0, g._stack_saved

View File

@@ -0,0 +1,100 @@
import sys
import unittest
from greenlet import greenlet
def switch(*args):
return greenlet.getcurrent().parent.switch(*args)
class ThrowTests(unittest.TestCase):
def test_class(self):
def f():
try:
switch("ok")
except RuntimeError:
switch("ok")
return
switch("fail")
g = greenlet(f)
res = g.switch()
self.assertEqual(res, "ok")
res = g.throw(RuntimeError)
self.assertEqual(res, "ok")
def test_val(self):
def f():
try:
switch("ok")
except RuntimeError:
val = sys.exc_info()[1]
if str(val) == "ciao":
switch("ok")
return
switch("fail")
g = greenlet(f)
res = g.switch()
self.assertEqual(res, "ok")
res = g.throw(RuntimeError("ciao"))
self.assertEqual(res, "ok")
g = greenlet(f)
res = g.switch()
self.assertEqual(res, "ok")
res = g.throw(RuntimeError, "ciao")
self.assertEqual(res, "ok")
def test_kill(self):
def f():
switch("ok")
switch("fail")
g = greenlet(f)
res = g.switch()
self.assertEqual(res, "ok")
res = g.throw()
self.assertTrue(isinstance(res, greenlet.GreenletExit))
self.assertTrue(g.dead)
res = g.throw() # immediately eaten by the already-dead greenlet
self.assertTrue(isinstance(res, greenlet.GreenletExit))
def test_throw_goes_to_original_parent(self):
main = greenlet.getcurrent()
def f1():
try:
main.switch("f1 ready to catch")
except IndexError:
return "caught"
else:
return "normal exit"
def f2():
main.switch("from f2")
g1 = greenlet(f1)
g2 = greenlet(f2, parent=g1)
self.assertRaises(IndexError, g2.throw, IndexError)
self.assertTrue(g2.dead)
self.assertTrue(g1.dead)
g1 = greenlet(f1)
g2 = greenlet(f2, parent=g1)
res = g1.switch()
self.assertEqual(res, "f1 ready to catch")
res = g2.throw(IndexError)
self.assertEqual(res, "caught")
self.assertTrue(g2.dead)
self.assertTrue(g1.dead)
g1 = greenlet(f1)
g2 = greenlet(f2, parent=g1)
res = g1.switch()
self.assertEqual(res, "f1 ready to catch")
res = g2.switch()
self.assertEqual(res, "from f2")
res = g2.throw(IndexError)
self.assertEqual(res, "caught")
self.assertTrue(g2.dead)
self.assertTrue(g1.dead)

View File

@@ -0,0 +1,267 @@
import sys
import unittest
import greenlet
class SomeError(Exception):
pass
class GreenletTracer(object):
oldtrace = None
def __init__(self, error_on_trace=False):
self.actions = []
self.error_on_trace = error_on_trace
def __call__(self, *args):
self.actions.append(args)
if self.error_on_trace:
raise SomeError
def __enter__(self):
self.oldtrace = greenlet.settrace(self)
return self.actions
def __exit__(self, *args):
greenlet.settrace(self.oldtrace)
class TestGreenletTracing(unittest.TestCase):
"""
Tests of ``greenlet.settrace()``
"""
def test_greenlet_tracing(self):
main = greenlet.getcurrent()
def dummy():
pass
def dummyexc():
raise SomeError()
with GreenletTracer() as actions:
g1 = greenlet.greenlet(dummy)
g1.switch()
g2 = greenlet.greenlet(dummyexc)
self.assertRaises(SomeError, g2.switch)
self.assertEqual(actions, [
('switch', (main, g1)),
('switch', (g1, main)),
('switch', (main, g2)),
('throw', (g2, main)),
])
def test_exception_disables_tracing(self):
main = greenlet.getcurrent()
def dummy():
main.switch()
g = greenlet.greenlet(dummy)
g.switch()
with GreenletTracer(error_on_trace=True) as actions:
self.assertRaises(SomeError, g.switch)
self.assertEqual(greenlet.gettrace(), None)
self.assertEqual(actions, [
('switch', (main, g)),
])
class PythonTracer(object):
oldtrace = None
def __init__(self):
self.actions = []
def __call__(self, frame, event, arg):
# Record the co_name so we have an idea what function we're in.
self.actions.append((event, frame.f_code.co_name))
def __enter__(self):
self.oldtrace = sys.setprofile(self)
return self.actions
def __exit__(self, *args):
sys.setprofile(self.oldtrace)
def tpt_callback():
return 42
class TestPythonTracing(unittest.TestCase):
"""
Tests of the interaction of ``sys.settrace()``
with greenlet facilities.
NOTE: Most of this is probably CPython specific.
"""
maxDiff = None
def test_trace_events_trivial(self):
with PythonTracer() as actions:
tpt_callback()
# If we use the sys.settrace instead of setprofile, we get
# this:
# self.assertEqual(actions, [
# ('call', 'tpt_callback'),
# ('call', '__exit__'),
# ])
self.assertEqual(actions, [
('return', '__enter__'),
('call', 'tpt_callback'),
('return', 'tpt_callback'),
('call', '__exit__'),
('c_call', '__exit__'),
])
def _trace_switch(self, glet):
with PythonTracer() as actions:
glet.switch()
return actions
def _check_trace_events_func_already_set(self, glet):
actions = self._trace_switch(glet)
self.assertEqual(actions, [
('return', '__enter__'),
('c_call', '_trace_switch'),
('call', 'run'),
('call', 'tpt_callback'),
('return', 'tpt_callback'),
('return', 'run'),
('c_return', '_trace_switch'),
('call', '__exit__'),
('c_call', '__exit__'),
])
def test_trace_events_into_greenlet_func_already_set(self):
def run():
return tpt_callback()
self._check_trace_events_func_already_set(greenlet.greenlet(run))
def test_trace_events_into_greenlet_subclass_already_set(self):
class X(greenlet.greenlet):
def run(self):
return tpt_callback()
self._check_trace_events_func_already_set(X())
def _check_trace_events_from_greenlet_sets_profiler(self, g, tracer):
g.switch()
tpt_callback()
tracer.__exit__()
self.assertEqual(tracer.actions, [
('return', '__enter__'),
('call', 'tpt_callback'),
('return', 'tpt_callback'),
('return', 'run'),
('call', 'tpt_callback'),
('return', 'tpt_callback'),
('call', '__exit__'),
('c_call', '__exit__'),
])
def test_trace_events_from_greenlet_func_sets_profiler(self):
tracer = PythonTracer()
def run():
tracer.__enter__()
return tpt_callback()
self._check_trace_events_from_greenlet_sets_profiler(greenlet.greenlet(run),
tracer)
def test_trace_events_from_greenlet_subclass_sets_profiler(self):
tracer = PythonTracer()
class X(greenlet.greenlet):
def run(self):
tracer.__enter__()
return tpt_callback()
self._check_trace_events_from_greenlet_sets_profiler(X(), tracer)
def test_trace_events_multiple_greenlets_switching(self):
tracer = PythonTracer()
g1 = None
g2 = None
def g1_run():
tracer.__enter__()
tpt_callback()
g2.switch()
tpt_callback()
return 42
def g2_run():
tpt_callback()
tracer.__exit__()
tpt_callback()
g1.switch()
g1 = greenlet.greenlet(g1_run)
g2 = greenlet.greenlet(g2_run)
x = g1.switch()
self.assertEqual(x, 42)
tpt_callback() # ensure not in the trace
self.assertEqual(tracer.actions, [
('return', '__enter__'),
('call', 'tpt_callback'),
('return', 'tpt_callback'),
('c_call', 'g1_run'),
('call', 'g2_run'),
('call', 'tpt_callback'),
('return', 'tpt_callback'),
('call', '__exit__'),
('c_call', '__exit__'),
])
def test_trace_events_multiple_greenlets_switching_siblings(self):
# Like the first version, but get both greenlets running first
# as "siblings" and then establish the tracing.
tracer = PythonTracer()
g1 = None
g2 = None
def g1_run():
greenlet.getcurrent().parent.switch()
tracer.__enter__()
tpt_callback()
g2.switch()
tpt_callback()
return 42
def g2_run():
greenlet.getcurrent().parent.switch()
tpt_callback()
tracer.__exit__()
tpt_callback()
g1.switch()
g1 = greenlet.greenlet(g1_run)
g2 = greenlet.greenlet(g2_run)
# Start g1
g1.switch()
# And it immediately returns control to us.
# Start g2
g2.switch()
# Which also returns. Now kick of the real part of the
# test.
x = g1.switch()
self.assertEqual(x, 42)
tpt_callback() # ensure not in the trace
self.assertEqual(tracer.actions, [
('return', '__enter__'),
('call', 'tpt_callback'),
('return', 'tpt_callback'),
('c_call', 'g1_run'),
('call', 'tpt_callback'),
('return', 'tpt_callback'),
('call', '__exit__'),
('c_call', '__exit__'),
])

View File

@@ -0,0 +1,39 @@
#! /usr/bin/env python
from __future__ import absolute_import
from __future__ import print_function
import sys
import os
import unittest
import greenlet
class VersionTests(unittest.TestCase):
def test_version(self):
def find_dominating_file(name):
if os.path.exists(name):
return name
tried = []
here = os.path.abspath(os.path.dirname(__file__))
for i in range(10):
up = ['..'] * i
path = [here] + up + [name]
fname = os.path.join(*path)
fname = os.path.abspath(fname)
tried.append(fname)
if os.path.exists(fname):
return fname
raise AssertionError("Could not find file " + name + "; checked " + str(tried))
try:
setup_py = find_dominating_file('setup.py')
except AssertionError as e:
raise unittest.SkipTest("Unable to find setup.py; must be out of tree. " + str(e))
invoke_setup = "%s %s --version" % (sys.executable, setup_py)
with os.popen(invoke_setup) as f:
sversion = f.read().strip()
self.assertEqual(sversion, greenlet.__version__)

View File

@@ -0,0 +1,34 @@
import gc
import greenlet
import weakref
import unittest
class WeakRefTests(unittest.TestCase):
def test_dead_weakref(self):
def _dead_greenlet():
g = greenlet.greenlet(lambda: None)
g.switch()
return g
o = weakref.ref(_dead_greenlet())
gc.collect()
self.assertEqual(o(), None)
def test_inactive_weakref(self):
o = weakref.ref(greenlet.greenlet())
gc.collect()
self.assertEqual(o(), None)
def test_dealloc_weakref(self):
seen = []
def worker():
try:
greenlet.getcurrent().parent.switch()
finally:
seen.append(g())
g = greenlet.greenlet(worker)
g.switch()
g2 = greenlet.greenlet(lambda: None, g)
g = weakref.ref(g2)
g2 = None
self.assertEqual(seen, [None])