-
Notifications
You must be signed in to change notification settings - Fork 246
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
greenlets leak thread local data when used with threads #12
Comments
Yes, I can confirm that. The leak is not because of _thead._localdummy, however, but because main greenlets themselves are leaking. The problem is that reference to current greenlet is kept in a global variable, and it's only switched when current greenlet is needed in another thread. The problem is that when greenlet is deallocated, it realizes that it's on another thread already, so it schedules itself for deallocation on its own thread, but that thread is gone and will never come back, that's why it's leaking. I'm not sure how to fix this, the problem is there's no reliable way to detect when thread goes away, except using weak references all over the place. |
It actually never even tries to deallocate itself. I changed the gc callback to force collection if the thread is dead, but it never results in dealloc being called: static int green_is_gc(PyGreenlet* self)
{
/* The owning thread died, return */
if (PyThreadState_GET()->dict == NULL)
return 1;
/* Main and alive greenlets are not garbage collectable */
return !(self->stack_stop == (char *)-1 || self->stack_start != NULL);
} |
Oh, and here's the twist, I cannot create weak references to thread state dict: TypeError: cannot create weak reference to 'dict' object |
The path should be something like this: green_dealloc -> check active -> kill_greenlet -> thread mismatch -> add to ts_delkey. But I'll double check, of course. |
Isn't the problem that green_dealloc is not called in the first place? No matter what I do, i can't get the GC to actually consider that thing garbage and kick in deallocation. |
Oh, you are right, it doesn't even go to green_dealloc, because ts_current is saved in threadstate dict, so there's an early cycle. |
The thing is, it was designed that way, because when greenlet is running on some random thread, ts_current might be the only reference to current greenlet. When it comes back to that thread it should be restored. If I could have a callback just before thread dies, this would be as easy as Py_XDECREF(ts_current) with additional checks for ts_current == NULL, but I can't see how to do that. |
Well, you can easily do that by instead of storing the dict, you store the threadstate instead. Then every time you get the dict you check if the dict is null. if the dict is null the thread went away and is waiting for the cycle to cleanup -> decref. |
You cannot store thread state, because thread state is not reference counted. After PyThreadState_DeleteCurrent() call in threadmodule.c the pointer would no longer be valid and might point to any arbitrary location. You cannot check anything after that. |
Ok, I have a proof of concept patch that fixes your particular sample, but it's a hack and I won't commit it to the main repo. But it might help you privately: diff --git a/greenlet.c b/greenlet.c
index 68caa4e..2683e89 100644
--- a/greenlet.c
+++ b/greenlet.c
@@ -214,8 +214,11 @@ static int green_updatecurrent(void)
/* save ts_current as the current greenlet of its own thread */
previous = ts_current;
- if (PyDict_SetItem(previous->run_info, ts_curkey, (PyObject*) previous))
- return -1;
+ /* HACK: only store ts_current if run_info is not abandoned */
+ if (Py_REFCNT(previous->run_info) != 1) {
+ if (PyDict_SetItem(previous->run_info, ts_curkey, (PyObject*) previous))
+ return -1;
+ }
/* get ts_current from the active tstate */
tstate = PyThreadState_GET();
@@ -673,6 +676,11 @@ static int green_init(PyGreenlet *self, PyObject *args, PyObject *kwargs)
static int kill_greenlet(PyGreenlet* self)
{
+ if (self->stack_start == (char*)1 && self->stack_stop == (char*)-1) {
+ /* HACK: main greenlet, just abandon! */
+ self->stack_start = NULL;
+ return 0;
+ }
/* Cannot raise an exception to kill the greenlet if
it is not running in the same thread! */
if (self->run_info == PyThreadState_GET()->dict) { |
Bear in mind that it would only work if by the time thread exits there's only main greenlet active in that thread. If you have multiple greenlets, and you switch to main greenlet and cause a thread exit, this hack would no longer work. |
The proper solution would be a helper object that is stored in the tls that on deallocation notifys the greenlet that the thread went away. that helper object would have the run state instead of the greenlet itself. If the reference from the greenlet to the helper object is weak that should work (idea by Antoine Pitrou). |
Hmm, at first I thought that would work, but then I realized that it wouldn't. That would mean we can't keep references to thread dict, but we need it to detect cross-thread switches, and if helper object keeps a reference to it, then it wouldn't work, because then situation wouldn't change at all, thread dict wouldn't go away, helper object wouldn't be deallocated. Even if it could work, it would be way too complicated, and I speed would likely suffer a lot. In the mean time, I worked a little more on the hack, it seems I managed to make it a little better (and fixed a glaring bug), so I'm more confident with it. Please see if this helps you with leaks: https://github.com/snaury/greenlet/tree/exp-thread-leaks If it does, I would commit it to the main repo. |
That's why it would keep a reference but would not own it. |
See above, when I tried, I got: TypeError: cannot create weak reference to 'dict' object |
On the c level you can represent weak references by just not incrementing the refcount and not decrementing it alter. That of course means you wouldn't be able to figure out if the reference died but that is not a problem in this situation because you know the order of destruction. |
Ok, I understand what you mean now. But that's:
After a monstrosity of a rewrite like that, I doubt you would gain much, but you are free to try. |
If only there was a global interpreter lock :D That being said, I will try to come up with a patch for that. |
What I meant is that some calls from C to Python might execute Python code and/or release the GIL, which might switch threads, so it adds a lot of complexity especially closer to the core of the switching code, where switch arguments are transferred via global variables. Just saying. |
The patch behind the pull request fixes the common issue. Some notes on it:
|
I realized what worries me. The problem is that it might be possible tp_clear would cause greenlet switches, which is a big no and can lead to crashes. What if Py_CLEAR(self->parent) causes parent greenlet to deallocate, although I can't prove it's possible yet, but if parent is alive and kill_greenlet kicks in, then stack switching might corrupt gc's unreachable lists, which are on the stack. This is actually why I made sure that active greenlets would not be garbage collected, because I proved crashes before with a test case in my wip-full-gc branch. |
Then that greenlet will not have a thread either which means that the cleanup will not ever attempt to switch. Since all greenlets will be owned by the same thread the same cleanup logic is applied to all: which is mostly just deallocate and otherwise noop. |
You might miss the fact that your lack of tp_is_gc means that it's not just about missing thread anymore. Ordinary greenlets that would not be considered by gc, are considered now, which means active greenlets in cycles might garbage collect now. It's late and I can't figure out how to properly make a cycle from g2 to g1, but when I add tp_dictoffset to greenlet, then this sample shows that there's a switch inside gc, which under right conditions might crash: import gc
import sys
import weakref
from greenlet import greenlet, GreenletExit
main = greenlet.getcurrent()
def func(seen):
try:
main.switch()
except:
print sys.exc_info()
seen.append(sys.exc_info()[0])
raise
raise RuntimeError
seen = []
g1 = greenlet(func)
g2 = greenlet(func)
g1.parent = g2
g1.switch(seen)
g2.switch(seen)
g2.ref = g1
print 'g1', g1
print 'g2', g2
del g2
del g1
print 'gc.collect start'
gc.collect()
print 'gc.collect end'
print seen Output:
|
The problem there is that the whole idea of having |
I think it's ok, as long as there are strong possibilities that is_gc won't change during gc. Changing your mind in traverse is actually worse, because unlike is_gc it's called multiple times, and that would certainly confuse gc. But anyway, I managed to make your idea work, see https://github.com/snaury/greenlet/tree/wip-next (it's kind of a post-midnight braindump at the moment though, with multiple unrelated patches, @schmir if you're reading this, please look if you don't like any of those). Thanks a lot for your help, using gc certainly makes it a lot cleaner. :) |
Oops, I was wrong, is_gc is called more than once. :) But it doesn't change the fact that anyone switching greenlets (which may flip is_gc) during gc is shooting themselves in the foot... |
Ouch, it seems my logic is faulty, it's perfectly possible to switch to active greenlets in gc, as easy as this: import gc
import weakref
from greenlet import greenlet
def alive():
try:
greenlet.getcurrent().parent.switch()
finally:
print 'Dying!'
g = greenlet(lambda: None)
g.ref = g
g.alive = greenlet(alive)
g.alive.switch()
g = weakref.ref(g)
assert g() is not None
print 'Before collect'
gc.collect()
print 'After collect'
assert g() is None This prints:
Which means that it's possible to switch in gc, which means even current releases are not as safe as I thought they are... :( |
sigh Ok, it's not that bad, actually. If I'm thinking straight, then since active greenlets are not collectible, then they won't appear in unreachable nor finalizers lists, neither anything they reference will appear in those. As a result, after the switch, and until that switch returns, no deallocations should touch unreachable/finalizers in gcmodule.c/collect, because, if I'm not mistaken here, greenlet can only ever deallocate what it can reach, and that's by definition reachable from it. So as long as active greenlets are not garbage collectible we're good. ;) |
\o/ |
I'm sick at the moment and didn't follow most of the discussion in depth. @snaury, feel free to apply your changes as you see fit. |
Today I realized that your sample script was still leaking with new code and found the culprit (deallocation order could bypass greenlet's tp_clear, because threadstate->dict might be cleared first). The changes are now in master, so I'm closing this issue. |
That's good to know. It might be easier just to take 0.4.0 and package that up instead. Thanks for your reply. |
…opagate to another greenlet and corrupt memory Hello up there. While working on Pygolang I've faced the following problem: a C++ exception thrown in one greenlet, without any try/catch block on that greenlet, might become propagated and "handled" by try/catch block established at another greenlet that happened to switch to the greenlet in question. "Handled" comes in quotes because the program usually segfaults after that. I've also observed segfaults before exception reaches user-level try/catch block - internally in __cxa_throw. Both this issues are likely of the same origin - due to the fact that C-level stack of a greenlet does not start from scratch and starts from C-level stack of the program state when the greenlet is switched to the first time. We already have one test for C++ exception handling in test_cpp that @snaury initially added in d9cb12a. However in that test all greenlets that throw C++ exceptions also put try/catch at the top of their C-stack. In the problem, that I describe, and that added test reproduces, there is no intended top-level try/catch block in C-stack of the greenlet in question that throws. As the test shows the exception becomes propagated to switcher's greenlet context and the program then dies with SIGSEGV: test_exception_switch_and_throw (greenlet.tests.test_cpp.CPPTests) ... terminate called after throwing an instance of 'exception_t' C++ exception unexpectedly caught in g1 <-- NOTE FAIL ====================================================================== FAIL: test_exception_switch_and_throw (greenlet.tests.test_cpp.CPPTests) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/kirr/src/tools/py/gevent/greenlet/src/greenlet/tests/test_cpp.py", line 62, in test_exception_switch_and_throw (ret, sig, " (core dumped)" if core else "")) AssertionError: failed with ret=0 sig=11 (core dumped) The C-level backtrace from the dumped core is attached in Appendix I. There the program dies somehow after running the code from _test_extension_cpp.cpp module. However with the following dirty-patch the situation becomes more clear: --- a/src/greenlet/tests/_test_extension_cpp.cpp +++ b/src/greenlet/tests/_test_extension_cpp.cpp @@ -70,6 +70,7 @@ test_exception_switch(PyObject* self, PyObject* args) static PyObject* py_test_exception_throw(PyObject* self, PyObject* args) { + abort(); if (!PyArg_ParseTuple(args, "")) return NULL; p_test_exception_throw(0); The C-level backtrace of this abort when, run from under g2, shows that g2 C-stack starts from what was there before for first greenlet with try/catch block: #0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50 python-greenlet#1 0x00007f8be31e9537 in __GI_abort () at abort.c:79 python-greenlet#2 0x00007f8be3529423 in py_test_exception_throw (self=0x0, args=0x7f8be317f050) at src/greenlet/tests/_test_extension_cpp.cpp:73 python-greenlet#3 0x00005584d0389903 in PyObject_Call (func=0x7f8be2cdd690, arg=<optimized out>, kw=<optimized out>) at ../Objects/abstract.c:2544 python-greenlet#4 0x00007f8be3530d63 in g_initialstub (mark=0x7ffe6d1b1c88) at src/greenlet/greenlet.c:931 python-greenlet#5 0x00007f8be3530290 in g_switch (target=0x7f8be2cd6230, args=0x7f8be317f050, kwargs=0x0) at src/greenlet/greenlet.c:692 python-greenlet#6 0x00007f8be35329e2 in PyGreenlet_Switch (g=0x7f8be2cd6230, args=0x7f8be317f050, kwargs=0x0) at src/greenlet/greenlet.c:1806 <- first switch to G2 python-greenlet#7 0x00007f8be35294da in test_exception_switch_and_do_in_g2 (self=0x0, args=0x7f8be2ce33d0) <- this code runs in G1 at src/greenlet/tests/_test_extension_cpp.cpp:105 python-greenlet#8 0x00005584d039de7a in call_function (oparg=<optimized out>, pp_stack=0x7ffe6d1b1e28) at ../Python/ceval.c:4376 python-greenlet#9 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3013 python-greenlet#10 0x00005584d039c3cc in PyEval_EvalCodeEx (co=0x7f8be2cd9cb0, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kws=<optimized out>, kwcount=0, defs=0x0, defcount=0, closure=0x0) at ../Python/ceval.c:3608 python-greenlet#11 0x00005584d03b6b9b in function_call (func=func@entry=0x7f8be2cde650, arg=0x7f8be317f050, kw=0x0) at ../Objects/funcobject.c:523 python-greenlet#12 0x00005584d0389903 in PyObject_Call (func=0x7f8be2cde650, arg=<optimized out>, kw=<optimized out>) at ../Objects/abstract.c:2544 python-greenlet#13 0x00007f8be3530d63 in g_initialstub (mark=0x7ffe6d1b20b8) at src/greenlet/greenlet.c:931 python-greenlet#14 0x00007f8be3530290 in g_switch (target=0x7f8be2cd6410, args=0x7f8be317f050, kwargs=0x0) at src/greenlet/greenlet.c:692 python-greenlet#15 0x00007f8be3531c4c in green_switch (self=0x7f8be2cd6410, args=0x7f8be317f050, kwargs=0x0) at src/greenlet/greenlet.c:1321 <- switch to G1 ... The problem might be worked-around with putting try/catch on C-stack for every greenlet, but a more proper fix would be to unlink return address and C-stack-frame of created greenlet from other greenlets completely (i.e. rewrite first frame of C-stack for created greenlet to return to NULL). Thanks beforehand, Kirill P.S. The problem described at python-greenlet#197 (comment) and python-greenlet#205 might be related to hereby issue (/cc @ThePrez, @kadler, @jamadden). P.P.S. The test does not fail on master. I see there @jamadden switched the codebase to C++ almost completely and there is some explicit attempts to save/restore exception state added in python-greenlet@87edf955. However that exception save/restore is specific to Win32 and the test succeeds for me on Linux. I still see the following comment in UserGreenlet::inner_bootstrap // C++ exceptions cannot propagate to the parent greenlet from // here. (TODO: Do we need a catch(...) clause, perhaps on the // function itself? ALl we could do is terminate the program.) ( https://github.com/python-greenlet/greenlet/blob/3e534d6b/src/greenlet/greenlet.cpp#L1085-L1104 ) probably this indeed works only by luck, or due to G_NOEXCEPT modifier of UserGreenlet::inner_bootstrap upon seeing which __cxa_throw stops unwinding C-stack and just calls std::terminate. -------- Appendix I. C-level backtrace after the test died with SIGSEGV #0 0x0000556bdc30d394 in PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3351 python-greenlet#1 0x0000556bdc30b3cc in PyEval_EvalCodeEx (co=0x7f0b8a03acb0, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kws=<optimized out>, kwcount=0, defs=0x0, defcount=0, closure=0x0) at ../Python/ceval.c:3608 python-greenlet#2 0x0000556bdc325b9b in function_call (func=func@entry=0x7f0b8a03f650, arg=0x7f0b8a4e0050, kw=0x0) at ../Objects/funcobject.c:523 python-greenlet#3 0x0000556bdc2f8903 in PyObject_Call (func=0x7f0b8a03f650, arg=<optimized out>, kw=<optimized out>) at ../Objects/abstract.c:2544 python-greenlet#4 0x00007f0b8a891d63 in g_initialstub (mark=0x7ffc3ef97988) at src/greenlet/greenlet.c:931 python-greenlet#5 0x00007f0b8a891290 in g_switch (target=0x7f0b8a037410, args=0x7f0b8a4e0050, kwargs=0x0) at src/greenlet/greenlet.c:692 python-greenlet#6 0x00007f0b8a892c4c in green_switch (self=0x7f0b8a037410, args=0x7f0b8a4e0050, kwargs=0x0) at src/greenlet/greenlet.c:1321 python-greenlet#7 0x0000556bdc30ce7a in call_function (oparg=<optimized out>, pp_stack=0x7ffc3ef97ac8) at ../Python/ceval.c:4376 python-greenlet#8 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3013 python-greenlet#9 0x0000556bdc30b3cc in PyEval_EvalCodeEx (co=0x7f0b8a03ad30, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kws=<optimized out>, kwcount=0, defs=0x0, defcount=0, closure=0x0) at ../Python/ceval.c:3608 python-greenlet#10 0x0000556bdc312953 in fast_function (nk=<optimized out>, na=<optimized out>, n=<optimized out>, pp_stack=0x7ffc3ef97ca8, func=0x7f0b8a03aed0) at ../Python/ceval.c:4471 python-greenlet#11 call_function (oparg=<optimized out>, pp_stack=0x7ffc3ef97ca8) at ../Python/ceval.c:4396 python-greenlet#12 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3013 python-greenlet#13 0x0000556bdc30b3cc in PyEval_EvalCodeEx (co=0x7f0b8a03ac30, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kws=<optimized out>, kwcount=0, defs=0x0, defcount=0, closure=0x0) at ../Python/ceval.c:3608 python-greenlet#14 0x0000556bdc312953 in fast_function (nk=<optimized out>, na=<optimized out>, n=<optimized out>, pp_stack=0x7ffc3ef97e88, func=0x7f0b8a03af50) at ../Python/ceval.c:4471 python-greenlet#15 call_function (oparg=<optimized out>, pp_stack=0x7ffc3ef97e88) at ../Python/ceval.c:4396 python-greenlet#16 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3013 python-greenlet#17 0x0000556bdc30b3cc in PyEval_EvalCodeEx (co=0x7f0b8a03adb0, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kws=<optimized out>, kwcount=0, defs=0x0, defcount=0, closure=0x0) at ../Python/ceval.c:3608 python-greenlet#18 0x0000556bdc312953 in fast_function (nk=<optimized out>, na=<optimized out>, n=<optimized out>, pp_stack=0x7ffc3ef98068, func=0x7f0b8a03f250) at ../Python/ceval.c:4471 python-greenlet#19 call_function (oparg=<optimized out>, pp_stack=0x7ffc3ef98068) at ../Python/ceval.c:4396 python-greenlet#20 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3013 python-greenlet#21 0x0000556bdc30b3cc in PyEval_EvalCodeEx (co=0x7f0b8a04bd30, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kws=<optimized out>, kwcount=0, defs=0x7f0b8a0752a8, defcount=1, closure=0x0) at ../Python/ceval.c:3608 python-greenlet#22 0x0000556bdc325cc2 in function_call (func=0x7f0b8a0771d0, arg=0x7f0b8a3bd5a0, kw=0x7f0b8a042dd0) at ../Objects/funcobject.c:523 python-greenlet#23 0x0000556bdc30fa96 in PyObject_Call (kw=0x7f0b8a042dd0, arg=0x7f0b8a3bd5a0, func=0x7f0b8a0771d0) at ../Objects/abstract.c:2544 python-greenlet#24 ext_do_call (nk=<optimized out>, na=1, flags=<optimized out>, pp_stack=0x7ffc3ef982a0, func=0x7f0b8a0771d0) at ../Python/ceval.c:4690 python-greenlet#25 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3052 python-greenlet#26 0x0000556bdc30b3cc in PyEval_EvalCodeEx (co=0x7f0b8a04bf30, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kws=<optimized out>, kwcount=0, defs=0x0, defcount=0, closure=0x0) at ../Python/ceval.c:3608 python-greenlet#27 0x0000556bdc325b9b in function_call (func=0x7f0b8a0772d0, arg=arg@entry=0x7f0b8a3bd550, kw=kw@entry=0x0) at ../Objects/funcobject.c:523 python-greenlet#28 0x0000556bdc33f553 in PyObject_Call (kw=0x0, arg=0x7f0b8a3bd550, func=<optimized out>) at ../Objects/abstract.c:2544 python-greenlet#29 instancemethod_call (func=<optimized out>, func@entry=0x7f0b8a3c6a00, arg=0x7f0b8a3bd550, kw=0x0) at ../Objects/classobject.c:2600 python-greenlet#30 0x0000556bdc2f8903 in PyObject_Call (func=0x7f0b8a3c6a00, arg=<optimized out>, kw=<optimized out>) at ../Objects/abstract.c:2544 python-greenlet#31 0x0000556bdc37eff8 in slot_tp_call (self=self@entry=0x7f0b8a03cfd0, args=0x7f0b8a044210, kwds=0x0) at ../Objects/typeobject.c:5609 python-greenlet#32 0x0000556bdc2f8903 in PyObject_Call (func=0x7f0b8a03cfd0, arg=<optimized out>, kw=<optimized out>) at ../Objects/abstract.c:2544 python-greenlet#33 0x0000556bdc3128dd in do_call (nk=<optimized out>, na=<optimized out>, pp_stack=0x7ffc3ef98798, func=0x7f0b8a03cfd0) at ../Python/ceval.c:4593 python-greenlet#34 call_function (oparg=<optimized out>, pp_stack=0x7ffc3ef98798) at ../Python/ceval.c:4398 python-greenlet#35 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3013 python-greenlet#36 0x0000556bdc30b3cc in PyEval_EvalCodeEx (co=0x7f0b8a07d530, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kws=<optimized out>, kwcount=0, defs=0x7f0b8a076ee8, defcount=1, closure=0x0) at ../Python/ceval.c:3608 python-greenlet#37 0x0000556bdc325cc2 in function_call (func=0x7f0b8a07f950, arg=0x7f0b8a3bd780, kw=0x7f0b8a042cb0) at ../Objects/funcobject.c:523 python-greenlet#38 0x0000556bdc30fa96 in PyObject_Call (kw=0x7f0b8a042cb0, arg=0x7f0b8a3bd780, func=0x7f0b8a07f950) at ../Objects/abstract.c:2544 python-greenlet#39 ext_do_call (nk=<optimized out>, na=1, flags=<optimized out>, pp_stack=0x7ffc3ef989d0, func=0x7f0b8a07f950) at ../Python/ceval.c:4690 python-greenlet#40 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3052 python-greenlet#41 0x0000556bdc30b3cc in PyEval_EvalCodeEx (co=0x7f0b8a07d2b0, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kws=<optimized out>, kwcount=0, defs=0x0, defcount=0, closure=0x0) at ../Python/ceval.c:3608 python-greenlet#42 0x0000556bdc325b9b in function_call (func=0x7f0b8a07f850, arg=arg@entry=0x7f0b8a3bd730, kw=kw@entry=0x0) at ../Objects/funcobject.c:523 python-greenlet#43 0x0000556bdc33f553 in PyObject_Call (kw=0x0, arg=0x7f0b8a3bd730, func=<optimized out>) at ../Objects/abstract.c:2544 python-greenlet#44 instancemethod_call (func=<optimized out>, func@entry=0x7f0b8a427190, arg=0x7f0b8a3bd730, kw=0x0) at ../Objects/classobject.c:2600 python-greenlet#45 0x0000556bdc2f8903 in PyObject_Call (func=0x7f0b8a427190, arg=<optimized out>, kw=<optimized out>) at ../Objects/abstract.c:2544 python-greenlet#46 0x0000556bdc37eff8 in slot_tp_call (self=self@entry=0x7f0b8a03cf90, args=0x7f0b8a044150, kwds=0x0) at ../Objects/typeobject.c:5609 python-greenlet#47 0x0000556bdc2f8903 in PyObject_Call (func=0x7f0b8a03cf90, arg=<optimized out>, kw=<optimized out>) at ../Objects/abstract.c:2544 python-greenlet#48 0x0000556bdc3128dd in do_call (nk=<optimized out>, na=<optimized out>, pp_stack=0x7ffc3ef98ec8, func=0x7f0b8a03cf90) at ../Python/ceval.c:4593 python-greenlet#49 call_function (oparg=<optimized out>, pp_stack=0x7ffc3ef98ec8) at ../Python/ceval.c:4398 python-greenlet#50 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3013 python-greenlet#51 0x0000556bdc30b3cc in PyEval_EvalCodeEx (co=0x7f0b8a07d530, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kws=<optimized out>, kwcount=0, defs=0x7f0b8a076ee8, defcount=1, closure=0x0) at ../Python/ceval.c:3608 python-greenlet#52 0x0000556bdc325cc2 in function_call (func=0x7f0b8a07f950, arg=0x7f0b8a3bdaf0, kw=0x7f0b8a042b90) at ../Objects/funcobject.c:523 python-greenlet#53 0x0000556bdc30fa96 in PyObject_Call (kw=0x7f0b8a042b90, arg=0x7f0b8a3bdaf0, func=0x7f0b8a07f950) at ../Objects/abstract.c:2544 python-greenlet#54 ext_do_call (nk=<optimized out>, na=1, flags=<optimized out>, pp_stack=0x7ffc3ef99100, func=0x7f0b8a07f950) at ../Python/ceval.c:4690 python-greenlet#55 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3052 python-greenlet#56 0x0000556bdc30b3cc in PyEval_EvalCodeEx (co=0x7f0b8a07d2b0, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kws=<optimized out>, kwcount=0, defs=0x0, defcount=0, closure=0x0) at ../Python/ceval.c:3608 python-greenlet#57 0x0000556bdc325b9b in function_call (func=0x7f0b8a07f850, arg=arg@entry=0x7f0b8a3bdb40, kw=kw@entry=0x0) at ../Objects/funcobject.c:523 python-greenlet#58 0x0000556bdc33f553 in PyObject_Call (kw=0x0, arg=0x7f0b8a3bdb40, func=<optimized out>) at ../Objects/abstract.c:2544 python-greenlet#59 instancemethod_call (func=<optimized out>, func@entry=0x7f0b8a3c6820, arg=0x7f0b8a3bdb40, kw=0x0) at ../Objects/classobject.c:2600 python-greenlet#60 0x0000556bdc2f8903 in PyObject_Call (func=0x7f0b8a3c6820, arg=<optimized out>, kw=<optimized out>) at ../Objects/abstract.c:2544 python-greenlet#61 0x0000556bdc37eff8 in slot_tp_call (self=self@entry=0x7f0b8a033c50, args=0x7f0b8a03c0d0, kwds=0x0) at ../Objects/typeobject.c:5609 python-greenlet#62 0x0000556bdc2f8903 in PyObject_Call (func=0x7f0b8a033c50, arg=<optimized out>, kw=<optimized out>) at ../Objects/abstract.c:2544 python-greenlet#63 0x0000556bdc3128dd in do_call (nk=<optimized out>, na=<optimized out>, pp_stack=0x7ffc3ef995f8, func=0x7f0b8a033c50) at ../Python/ceval.c:4593 python-greenlet#64 call_function (oparg=<optimized out>, pp_stack=0x7ffc3ef995f8) at ../Python/ceval.c:4398 python-greenlet#65 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3013 python-greenlet#66 0x0000556bdc30b3cc in PyEval_EvalCodeEx (co=0x7f0b8a07d530, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kws=<optimized out>, kwcount=0, defs=0x7f0b8a076ee8, defcount=1, closure=0x0) at ../Python/ceval.c:3608 python-greenlet#67 0x0000556bdc325cc2 in function_call (func=0x7f0b8a07f950, arg=0x7f0b8a3bd500, kw=0x7f0b8a042a70) at ../Objects/funcobject.c:523 python-greenlet#68 0x0000556bdc30fa96 in PyObject_Call (kw=0x7f0b8a042a70, arg=0x7f0b8a3bd500, func=0x7f0b8a07f950) at ../Objects/abstract.c:2544 python-greenlet#69 ext_do_call (nk=<optimized out>, na=1, flags=<optimized out>, pp_stack=0x7ffc3ef99830, func=0x7f0b8a07f950) at ../Python/ceval.c:4690 python-greenlet#70 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3052 python-greenlet#71 0x0000556bdc30b3cc in PyEval_EvalCodeEx (co=0x7f0b8a07d2b0, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kws=<optimized out>, kwcount=0, defs=0x0, defcount=0, closure=0x0) at ../Python/ceval.c:3608 python-greenlet#72 0x0000556bdc325b9b in function_call (func=0x7f0b8a07f850, arg=arg@entry=0x7f0b8a3bd9b0, kw=kw@entry=0x0) at ../Objects/funcobject.c:523 python-greenlet#73 0x0000556bdc33f553 in PyObject_Call (kw=0x0, arg=0x7f0b8a3bd9b0, func=<optimized out>) at ../Objects/abstract.c:2544 python-greenlet#74 instancemethod_call (func=<optimized out>, func@entry=0x7f0b8a4310a0, arg=0x7f0b8a3bd9b0, kw=0x0) at ../Objects/classobject.c:2600 python-greenlet#75 0x0000556bdc2f8903 in PyObject_Call (func=0x7f0b8a4310a0, arg=<optimized out>, kw=<optimized out>) at ../Objects/abstract.c:2544 python-greenlet#76 0x0000556bdc37eff8 in slot_tp_call (self=self@entry=0x7f0b8a033c10, args=0x7f0b8a033fd0, kwds=0x0) at ../Objects/typeobject.c:5609 python-greenlet#77 0x0000556bdc2f8903 in PyObject_Call (func=0x7f0b8a033c10, arg=<optimized out>, kw=<optimized out>) at ../Objects/abstract.c:2544 python-greenlet#78 0x0000556bdc3128dd in do_call (nk=<optimized out>, na=<optimized out>, pp_stack=0x7ffc3ef99d28, func=0x7f0b8a033c10) at ../Python/ceval.c:4593 python-greenlet#79 call_function (oparg=<optimized out>, pp_stack=0x7ffc3ef99d28) at ../Python/ceval.c:4398 python-greenlet#80 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3013 python-greenlet#81 0x0000556bdc3125fe in fast_function (nk=<optimized out>, na=<optimized out>, n=<optimized out>, pp_stack=0x7ffc3ef99e38, func=0x7f0b8a01ecd0) at ../Python/ceval.c:4461 python-greenlet#82 call_function (oparg=<optimized out>, pp_stack=0x7ffc3ef99e38) at ../Python/ceval.c:4396 python-greenlet#83 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3013 python-greenlet#84 0x0000556bdc3125fe in fast_function (nk=<optimized out>, na=<optimized out>, n=<optimized out>, pp_stack=0x7ffc3ef99f48, func=0x7f0b8a01edd0) at ../Python/ceval.c:4461 python-greenlet#85 call_function (oparg=<optimized out>, pp_stack=0x7ffc3ef99f48) at ../Python/ceval.c:4396 python-greenlet#86 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3013 python-greenlet#87 0x0000556bdc30b3cc in PyEval_EvalCodeEx (co=0x7f0b8a081d30, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kws=<optimized out>, kwcount=1, defs=0x7f0b8a008ba8, defcount=10, closure=0x0) at ../Python/ceval.c:3608 python-greenlet#88 0x0000556bdc325cc2 in function_call (func=0x7f0b8a00ddd0, arg=arg@entry=0x7f0b8a033610, kw=kw@entry=0x7f0b8a014a70) at ../Objects/funcobject.c:523 python-greenlet#89 0x0000556bdc33f553 in PyObject_Call (kw=0x7f0b8a014a70, arg=0x7f0b8a033610, func=<optimized out>) at ../Objects/abstract.c:2544 python-greenlet#90 instancemethod_call (func=<optimized out>, func@entry=0x7f0b8a427140, arg=0x7f0b8a033610, arg@entry=0x7f0b8a4e0050, kw=kw@entry=0x7f0b8a014a70) at ../Objects/classobject.c:2600 python-greenlet#91 0x0000556bdc33f10f in PyObject_Call (kw=0x7f0b8a014a70, arg=0x7f0b8a4e0050, func=0x7f0b8a427140) at ../Objects/abstract.c:2544 python-greenlet#92 slot_tp_init (self=self@entry=0x7f0b8a3ebb10, args=args@entry=0x7f0b8a4e0050, kwds=kwds@entry=0x7f0b8a014a70) at ../Objects/typeobject.c:5869 python-greenlet#93 0x0000556bdc2feb87 in type_call (type=<optimized out>, type@entry=0x556bdd2a85c0, args=0x7f0b8a4e0050, kwds=0x7f0b8a014a70) at ../Objects/typeobject.c:765 python-greenlet#94 0x0000556bdc2f8903 in PyObject_Call (func=0x556bdd2a85c0, arg=<optimized out>, kw=<optimized out>) at ../Objects/abstract.c:2544 python-greenlet#95 0x0000556bdc3128dd in do_call (nk=<optimized out>, na=<optimized out>, pp_stack=0x7ffc3ef9a458, func=0x556bdd2a85c0) at ../Python/ceval.c:4593 python-greenlet#96 call_function (oparg=<optimized out>, pp_stack=0x7ffc3ef9a458) at ../Python/ceval.c:4398 python-greenlet#97 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3013 python-greenlet#98 0x0000556bdc30b3cc in PyEval_EvalCodeEx (co=0x7f0b8a37d830, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kws=<optimized out>, kwcount=0, defs=0x0, defcount=0, closure=0x0) at ../Python/ceval.c:3608 python-greenlet#99 0x0000556bdc311886 in PyEval_EvalCode (locals=0x7f0b8a4ae170, globals=0x7f0b8a4ae170, co=<optimized out>) at ../Python/ceval.c:669 python-greenlet#100 exec_statement (locals=0x7f0b8a4ae170, globals=0x7f0b8a4ae170, prog=<optimized out>, f=<optimized out>) at ../Python/ceval.c:5093 python-greenlet#101 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:2122 python-greenlet#102 0x0000556bdc30b3cc in PyEval_EvalCodeEx (co=0x7f0b8a3ea4b0, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kws=<optimized out>, kwcount=0, defs=0x7f0b8a381068, defcount=5, closure=0x0) at ../Python/ceval.c:3608 python-greenlet#103 0x0000556bdc312953 in fast_function (nk=<optimized out>, na=<optimized out>, n=<optimized out>, pp_stack=0x7ffc3ef9a818, func=0x7f0b8a37d8d0) at ../Python/ceval.c:4471 python-greenlet#104 call_function (oparg=<optimized out>, pp_stack=0x7ffc3ef9a818) at ../Python/ceval.c:4396 python-greenlet#105 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3013 python-greenlet#106 0x0000556bdc30b3cc in PyEval_EvalCodeEx (co=0x7f0b8a37d4b0, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kws=<optimized out>, kwcount=0, defs=0x7f0b8a379ce8, defcount=1, closure=0x0) at ../Python/ceval.c:3608 python-greenlet#107 0x0000556bdc325b9b in function_call (func=func@entry=0x7f0b8a37de50, arg=0x7f0b8a3b5be0, kw=0x0) at ../Objects/funcobject.c:523 python-greenlet#108 0x0000556bdc2f8903 in PyObject_Call (func=0x7f0b8a37de50, arg=<optimized out>, kw=<optimized out>) at ../Objects/abstract.c:2544 python-greenlet#109 0x0000556bdc3b87e1 in RunModule (module=<optimized out>, set_argv0=1) at ../Modules/main.c:197 python-greenlet#110 0x0000556bdc3a68ed in Py_Main (argc=<optimized out>, argv=<optimized out>) at ../Modules/main.c:592#111 0x00007f0b8a54bd0a in __libc_start_main (main=0x556bdc3a6530 <main>, argc=5, argv=0x7ffc3ef9ac08, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7ffc3ef9abf8) at ../csu/libc-start.c:308 python-greenlet#112 0x0000556bdc3a646a in _start ()
…opagate to another greenlet and corrupt memory Hello up there. While working on Pygolang I've faced the following problem: a C++ exception thrown in one greenlet, without any try/catch block on that greenlet, might become propagated and "handled" by try/catch block established at another greenlet that happened to switch to the greenlet in question. "Handled" comes in quotes because the program usually segfaults after that. I've also observed segfaults before exception reaches user-level try/catch block - internally in __cxa_throw. Both this issues are likely of the same origin - due to the fact that C-level stack of a greenlet does not start from scratch and starts from C-level stack of the program state when the greenlet is switched to the first time. We already have one test for C++ exception handling in test_cpp that @snaury initially added in d9cb12a. However in that test all greenlets that throw C++ exceptions also put try/catch at the top of their C-stack. In the problem, that I describe, and that added test reproduces, there is no intended top-level try/catch block in C-stack of the greenlet in question that throws. As the test shows the exception becomes propagated to switcher's greenlet context and the program then dies with SIGSEGV: test_exception_switch_and_throw (greenlet.tests.test_cpp.CPPTests) ... terminate called after throwing an instance of 'exception_t' C++ exception unexpectedly caught in g1 <-- NOTE FAIL ====================================================================== FAIL: test_exception_switch_and_throw (greenlet.tests.test_cpp.CPPTests) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/kirr/src/tools/py/gevent/greenlet/src/greenlet/tests/test_cpp.py", line 62, in test_exception_switch_and_throw (ret, sig, " (core dumped)" if core else "")) AssertionError: failed with ret=0 sig=11 (core dumped) The C-level backtrace from the dumped core is attached in Appendix I. There the program dies somehow after running the code from _test_extension_cpp.cpp module. However with the following dirty-patch the situation becomes more clear: --- a/src/greenlet/tests/_test_extension_cpp.cpp +++ b/src/greenlet/tests/_test_extension_cpp.cpp @@ -70,6 +70,7 @@ test_exception_switch(PyObject* self, PyObject* args) static PyObject* py_test_exception_throw(PyObject* self, PyObject* args) { + abort(); if (!PyArg_ParseTuple(args, "")) return NULL; p_test_exception_throw(0); The C-level backtrace of this abort when, run from under g2, shows that g2 C-stack starts from what was there before for first greenlet with try/catch block: #0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50 #1 0x00007f8be31e9537 in __GI_abort () at abort.c:79 #2 0x00007f8be3529423 in py_test_exception_throw (self=0x0, args=0x7f8be317f050) at src/greenlet/tests/_test_extension_cpp.cpp:73 #3 0x00005584d0389903 in PyObject_Call (func=0x7f8be2cdd690, arg=<optimized out>, kw=<optimized out>) at ../Objects/abstract.c:2544 #4 0x00007f8be3530d63 in g_initialstub (mark=0x7ffe6d1b1c88) at src/greenlet/greenlet.c:931 #5 0x00007f8be3530290 in g_switch (target=0x7f8be2cd6230, args=0x7f8be317f050, kwargs=0x0) at src/greenlet/greenlet.c:692 #6 0x00007f8be35329e2 in PyGreenlet_Switch (g=0x7f8be2cd6230, args=0x7f8be317f050, kwargs=0x0) at src/greenlet/greenlet.c:1806 <- first switch to G2 #7 0x00007f8be35294da in test_exception_switch_and_do_in_g2 (self=0x0, args=0x7f8be2ce33d0) <- this code runs in G1 at src/greenlet/tests/_test_extension_cpp.cpp:105 #8 0x00005584d039de7a in call_function (oparg=<optimized out>, pp_stack=0x7ffe6d1b1e28) at ../Python/ceval.c:4376 #9 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3013 #10 0x00005584d039c3cc in PyEval_EvalCodeEx (co=0x7f8be2cd9cb0, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kws=<optimized out>, kwcount=0, defs=0x0, defcount=0, closure=0x0) at ../Python/ceval.c:3608 #11 0x00005584d03b6b9b in function_call (func=func@entry=0x7f8be2cde650, arg=0x7f8be317f050, kw=0x0) at ../Objects/funcobject.c:523 #12 0x00005584d0389903 in PyObject_Call (func=0x7f8be2cde650, arg=<optimized out>, kw=<optimized out>) at ../Objects/abstract.c:2544 #13 0x00007f8be3530d63 in g_initialstub (mark=0x7ffe6d1b20b8) at src/greenlet/greenlet.c:931 #14 0x00007f8be3530290 in g_switch (target=0x7f8be2cd6410, args=0x7f8be317f050, kwargs=0x0) at src/greenlet/greenlet.c:692 #15 0x00007f8be3531c4c in green_switch (self=0x7f8be2cd6410, args=0x7f8be317f050, kwargs=0x0) at src/greenlet/greenlet.c:1321 <- switch to G1 ... The problem might be worked-around with putting try/catch on C-stack for every greenlet, but a more proper fix would be to unlink return address and C-stack-frame of created greenlet from other greenlets completely (i.e. rewrite first frame of C-stack for created greenlet to return to NULL). Thanks beforehand, Kirill P.S. The problem described at #197 (comment) and #205 might be related to hereby issue (/cc @ThePrez, @kadler, @jamadden). P.P.S. The test does not fail on master. I see there @jamadden switched the codebase to C++ almost completely and there is some explicit attempts to save/restore exception state added in 87edf955. However that exception save/restore is specific to Win32 and the test succeeds for me on Linux. I still see the following comment in UserGreenlet::inner_bootstrap // C++ exceptions cannot propagate to the parent greenlet from // here. (TODO: Do we need a catch(...) clause, perhaps on the // function itself? ALl we could do is terminate the program.) ( https://github.com/python-greenlet/greenlet/blob/3e534d6b/src/greenlet/greenlet.cpp#L1085-L1104 ) probably this indeed works only by luck, or due to G_NOEXCEPT modifier of UserGreenlet::inner_bootstrap upon seeing which __cxa_throw stops unwinding C-stack and just calls std::terminate. -------- Appendix I. C-level backtrace after the test died with SIGSEGV #0 0x0000556bdc30d394 in PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3351 #1 0x0000556bdc30b3cc in PyEval_EvalCodeEx (co=0x7f0b8a03acb0, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kws=<optimized out>, kwcount=0, defs=0x0, defcount=0, closure=0x0) at ../Python/ceval.c:3608 #2 0x0000556bdc325b9b in function_call (func=func@entry=0x7f0b8a03f650, arg=0x7f0b8a4e0050, kw=0x0) at ../Objects/funcobject.c:523 #3 0x0000556bdc2f8903 in PyObject_Call (func=0x7f0b8a03f650, arg=<optimized out>, kw=<optimized out>) at ../Objects/abstract.c:2544 #4 0x00007f0b8a891d63 in g_initialstub (mark=0x7ffc3ef97988) at src/greenlet/greenlet.c:931 #5 0x00007f0b8a891290 in g_switch (target=0x7f0b8a037410, args=0x7f0b8a4e0050, kwargs=0x0) at src/greenlet/greenlet.c:692 #6 0x00007f0b8a892c4c in green_switch (self=0x7f0b8a037410, args=0x7f0b8a4e0050, kwargs=0x0) at src/greenlet/greenlet.c:1321 #7 0x0000556bdc30ce7a in call_function (oparg=<optimized out>, pp_stack=0x7ffc3ef97ac8) at ../Python/ceval.c:4376 #8 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3013 #9 0x0000556bdc30b3cc in PyEval_EvalCodeEx (co=0x7f0b8a03ad30, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kws=<optimized out>, kwcount=0, defs=0x0, defcount=0, closure=0x0) at ../Python/ceval.c:3608 #10 0x0000556bdc312953 in fast_function (nk=<optimized out>, na=<optimized out>, n=<optimized out>, pp_stack=0x7ffc3ef97ca8, func=0x7f0b8a03aed0) at ../Python/ceval.c:4471 #11 call_function (oparg=<optimized out>, pp_stack=0x7ffc3ef97ca8) at ../Python/ceval.c:4396 #12 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3013 #13 0x0000556bdc30b3cc in PyEval_EvalCodeEx (co=0x7f0b8a03ac30, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kws=<optimized out>, kwcount=0, defs=0x0, defcount=0, closure=0x0) at ../Python/ceval.c:3608 #14 0x0000556bdc312953 in fast_function (nk=<optimized out>, na=<optimized out>, n=<optimized out>, pp_stack=0x7ffc3ef97e88, func=0x7f0b8a03af50) at ../Python/ceval.c:4471 #15 call_function (oparg=<optimized out>, pp_stack=0x7ffc3ef97e88) at ../Python/ceval.c:4396 #16 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3013 #17 0x0000556bdc30b3cc in PyEval_EvalCodeEx (co=0x7f0b8a03adb0, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kws=<optimized out>, kwcount=0, defs=0x0, defcount=0, closure=0x0) at ../Python/ceval.c:3608 #18 0x0000556bdc312953 in fast_function (nk=<optimized out>, na=<optimized out>, n=<optimized out>, pp_stack=0x7ffc3ef98068, func=0x7f0b8a03f250) at ../Python/ceval.c:4471 #19 call_function (oparg=<optimized out>, pp_stack=0x7ffc3ef98068) at ../Python/ceval.c:4396 #20 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3013 #21 0x0000556bdc30b3cc in PyEval_EvalCodeEx (co=0x7f0b8a04bd30, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kws=<optimized out>, kwcount=0, defs=0x7f0b8a0752a8, defcount=1, closure=0x0) at ../Python/ceval.c:3608 #22 0x0000556bdc325cc2 in function_call (func=0x7f0b8a0771d0, arg=0x7f0b8a3bd5a0, kw=0x7f0b8a042dd0) at ../Objects/funcobject.c:523 #23 0x0000556bdc30fa96 in PyObject_Call (kw=0x7f0b8a042dd0, arg=0x7f0b8a3bd5a0, func=0x7f0b8a0771d0) at ../Objects/abstract.c:2544 #24 ext_do_call (nk=<optimized out>, na=1, flags=<optimized out>, pp_stack=0x7ffc3ef982a0, func=0x7f0b8a0771d0) at ../Python/ceval.c:4690 #25 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3052 #26 0x0000556bdc30b3cc in PyEval_EvalCodeEx (co=0x7f0b8a04bf30, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kws=<optimized out>, kwcount=0, defs=0x0, defcount=0, closure=0x0) at ../Python/ceval.c:3608 #27 0x0000556bdc325b9b in function_call (func=0x7f0b8a0772d0, arg=arg@entry=0x7f0b8a3bd550, kw=kw@entry=0x0) at ../Objects/funcobject.c:523 #28 0x0000556bdc33f553 in PyObject_Call (kw=0x0, arg=0x7f0b8a3bd550, func=<optimized out>) at ../Objects/abstract.c:2544 #29 instancemethod_call (func=<optimized out>, func@entry=0x7f0b8a3c6a00, arg=0x7f0b8a3bd550, kw=0x0) at ../Objects/classobject.c:2600 #30 0x0000556bdc2f8903 in PyObject_Call (func=0x7f0b8a3c6a00, arg=<optimized out>, kw=<optimized out>) at ../Objects/abstract.c:2544 #31 0x0000556bdc37eff8 in slot_tp_call (self=self@entry=0x7f0b8a03cfd0, args=0x7f0b8a044210, kwds=0x0) at ../Objects/typeobject.c:5609 #32 0x0000556bdc2f8903 in PyObject_Call (func=0x7f0b8a03cfd0, arg=<optimized out>, kw=<optimized out>) at ../Objects/abstract.c:2544 #33 0x0000556bdc3128dd in do_call (nk=<optimized out>, na=<optimized out>, pp_stack=0x7ffc3ef98798, func=0x7f0b8a03cfd0) at ../Python/ceval.c:4593 #34 call_function (oparg=<optimized out>, pp_stack=0x7ffc3ef98798) at ../Python/ceval.c:4398 #35 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3013 #36 0x0000556bdc30b3cc in PyEval_EvalCodeEx (co=0x7f0b8a07d530, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kws=<optimized out>, kwcount=0, defs=0x7f0b8a076ee8, defcount=1, closure=0x0) at ../Python/ceval.c:3608 #37 0x0000556bdc325cc2 in function_call (func=0x7f0b8a07f950, arg=0x7f0b8a3bd780, kw=0x7f0b8a042cb0) at ../Objects/funcobject.c:523 #38 0x0000556bdc30fa96 in PyObject_Call (kw=0x7f0b8a042cb0, arg=0x7f0b8a3bd780, func=0x7f0b8a07f950) at ../Objects/abstract.c:2544 #39 ext_do_call (nk=<optimized out>, na=1, flags=<optimized out>, pp_stack=0x7ffc3ef989d0, func=0x7f0b8a07f950) at ../Python/ceval.c:4690 #40 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3052 #41 0x0000556bdc30b3cc in PyEval_EvalCodeEx (co=0x7f0b8a07d2b0, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kws=<optimized out>, kwcount=0, defs=0x0, defcount=0, closure=0x0) at ../Python/ceval.c:3608 #42 0x0000556bdc325b9b in function_call (func=0x7f0b8a07f850, arg=arg@entry=0x7f0b8a3bd730, kw=kw@entry=0x0) at ../Objects/funcobject.c:523 #43 0x0000556bdc33f553 in PyObject_Call (kw=0x0, arg=0x7f0b8a3bd730, func=<optimized out>) at ../Objects/abstract.c:2544 #44 instancemethod_call (func=<optimized out>, func@entry=0x7f0b8a427190, arg=0x7f0b8a3bd730, kw=0x0) at ../Objects/classobject.c:2600 #45 0x0000556bdc2f8903 in PyObject_Call (func=0x7f0b8a427190, arg=<optimized out>, kw=<optimized out>) at ../Objects/abstract.c:2544 #46 0x0000556bdc37eff8 in slot_tp_call (self=self@entry=0x7f0b8a03cf90, args=0x7f0b8a044150, kwds=0x0) at ../Objects/typeobject.c:5609 #47 0x0000556bdc2f8903 in PyObject_Call (func=0x7f0b8a03cf90, arg=<optimized out>, kw=<optimized out>) at ../Objects/abstract.c:2544 #48 0x0000556bdc3128dd in do_call (nk=<optimized out>, na=<optimized out>, pp_stack=0x7ffc3ef98ec8, func=0x7f0b8a03cf90) at ../Python/ceval.c:4593 #49 call_function (oparg=<optimized out>, pp_stack=0x7ffc3ef98ec8) at ../Python/ceval.c:4398 #50 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3013 #51 0x0000556bdc30b3cc in PyEval_EvalCodeEx (co=0x7f0b8a07d530, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kws=<optimized out>, kwcount=0, defs=0x7f0b8a076ee8, defcount=1, closure=0x0) at ../Python/ceval.c:3608 #52 0x0000556bdc325cc2 in function_call (func=0x7f0b8a07f950, arg=0x7f0b8a3bdaf0, kw=0x7f0b8a042b90) at ../Objects/funcobject.c:523 #53 0x0000556bdc30fa96 in PyObject_Call (kw=0x7f0b8a042b90, arg=0x7f0b8a3bdaf0, func=0x7f0b8a07f950) at ../Objects/abstract.c:2544 #54 ext_do_call (nk=<optimized out>, na=1, flags=<optimized out>, pp_stack=0x7ffc3ef99100, func=0x7f0b8a07f950) at ../Python/ceval.c:4690 #55 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3052 #56 0x0000556bdc30b3cc in PyEval_EvalCodeEx (co=0x7f0b8a07d2b0, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kws=<optimized out>, kwcount=0, defs=0x0, defcount=0, closure=0x0) at ../Python/ceval.c:3608 #57 0x0000556bdc325b9b in function_call (func=0x7f0b8a07f850, arg=arg@entry=0x7f0b8a3bdb40, kw=kw@entry=0x0) at ../Objects/funcobject.c:523 #58 0x0000556bdc33f553 in PyObject_Call (kw=0x0, arg=0x7f0b8a3bdb40, func=<optimized out>) at ../Objects/abstract.c:2544 #59 instancemethod_call (func=<optimized out>, func@entry=0x7f0b8a3c6820, arg=0x7f0b8a3bdb40, kw=0x0) at ../Objects/classobject.c:2600 #60 0x0000556bdc2f8903 in PyObject_Call (func=0x7f0b8a3c6820, arg=<optimized out>, kw=<optimized out>) at ../Objects/abstract.c:2544 #61 0x0000556bdc37eff8 in slot_tp_call (self=self@entry=0x7f0b8a033c50, args=0x7f0b8a03c0d0, kwds=0x0) at ../Objects/typeobject.c:5609 #62 0x0000556bdc2f8903 in PyObject_Call (func=0x7f0b8a033c50, arg=<optimized out>, kw=<optimized out>) at ../Objects/abstract.c:2544 #63 0x0000556bdc3128dd in do_call (nk=<optimized out>, na=<optimized out>, pp_stack=0x7ffc3ef995f8, func=0x7f0b8a033c50) at ../Python/ceval.c:4593 #64 call_function (oparg=<optimized out>, pp_stack=0x7ffc3ef995f8) at ../Python/ceval.c:4398 #65 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3013 #66 0x0000556bdc30b3cc in PyEval_EvalCodeEx (co=0x7f0b8a07d530, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kws=<optimized out>, kwcount=0, defs=0x7f0b8a076ee8, defcount=1, closure=0x0) at ../Python/ceval.c:3608 #67 0x0000556bdc325cc2 in function_call (func=0x7f0b8a07f950, arg=0x7f0b8a3bd500, kw=0x7f0b8a042a70) at ../Objects/funcobject.c:523 #68 0x0000556bdc30fa96 in PyObject_Call (kw=0x7f0b8a042a70, arg=0x7f0b8a3bd500, func=0x7f0b8a07f950) at ../Objects/abstract.c:2544 #69 ext_do_call (nk=<optimized out>, na=1, flags=<optimized out>, pp_stack=0x7ffc3ef99830, func=0x7f0b8a07f950) at ../Python/ceval.c:4690 #70 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3052 #71 0x0000556bdc30b3cc in PyEval_EvalCodeEx (co=0x7f0b8a07d2b0, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kws=<optimized out>, kwcount=0, defs=0x0, defcount=0, closure=0x0) at ../Python/ceval.c:3608 #72 0x0000556bdc325b9b in function_call (func=0x7f0b8a07f850, arg=arg@entry=0x7f0b8a3bd9b0, kw=kw@entry=0x0) at ../Objects/funcobject.c:523 #73 0x0000556bdc33f553 in PyObject_Call (kw=0x0, arg=0x7f0b8a3bd9b0, func=<optimized out>) at ../Objects/abstract.c:2544 #74 instancemethod_call (func=<optimized out>, func@entry=0x7f0b8a4310a0, arg=0x7f0b8a3bd9b0, kw=0x0) at ../Objects/classobject.c:2600 #75 0x0000556bdc2f8903 in PyObject_Call (func=0x7f0b8a4310a0, arg=<optimized out>, kw=<optimized out>) at ../Objects/abstract.c:2544 #76 0x0000556bdc37eff8 in slot_tp_call (self=self@entry=0x7f0b8a033c10, args=0x7f0b8a033fd0, kwds=0x0) at ../Objects/typeobject.c:5609 #77 0x0000556bdc2f8903 in PyObject_Call (func=0x7f0b8a033c10, arg=<optimized out>, kw=<optimized out>) at ../Objects/abstract.c:2544 #78 0x0000556bdc3128dd in do_call (nk=<optimized out>, na=<optimized out>, pp_stack=0x7ffc3ef99d28, func=0x7f0b8a033c10) at ../Python/ceval.c:4593 #79 call_function (oparg=<optimized out>, pp_stack=0x7ffc3ef99d28) at ../Python/ceval.c:4398 #80 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3013 #81 0x0000556bdc3125fe in fast_function (nk=<optimized out>, na=<optimized out>, n=<optimized out>, pp_stack=0x7ffc3ef99e38, func=0x7f0b8a01ecd0) at ../Python/ceval.c:4461 #82 call_function (oparg=<optimized out>, pp_stack=0x7ffc3ef99e38) at ../Python/ceval.c:4396 #83 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3013 #84 0x0000556bdc3125fe in fast_function (nk=<optimized out>, na=<optimized out>, n=<optimized out>, pp_stack=0x7ffc3ef99f48, func=0x7f0b8a01edd0) at ../Python/ceval.c:4461 #85 call_function (oparg=<optimized out>, pp_stack=0x7ffc3ef99f48) at ../Python/ceval.c:4396 #86 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3013 #87 0x0000556bdc30b3cc in PyEval_EvalCodeEx (co=0x7f0b8a081d30, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kws=<optimized out>, kwcount=1, defs=0x7f0b8a008ba8, defcount=10, closure=0x0) at ../Python/ceval.c:3608 #88 0x0000556bdc325cc2 in function_call (func=0x7f0b8a00ddd0, arg=arg@entry=0x7f0b8a033610, kw=kw@entry=0x7f0b8a014a70) at ../Objects/funcobject.c:523 #89 0x0000556bdc33f553 in PyObject_Call (kw=0x7f0b8a014a70, arg=0x7f0b8a033610, func=<optimized out>) at ../Objects/abstract.c:2544 #90 instancemethod_call (func=<optimized out>, func@entry=0x7f0b8a427140, arg=0x7f0b8a033610, arg@entry=0x7f0b8a4e0050, kw=kw@entry=0x7f0b8a014a70) at ../Objects/classobject.c:2600 #91 0x0000556bdc33f10f in PyObject_Call (kw=0x7f0b8a014a70, arg=0x7f0b8a4e0050, func=0x7f0b8a427140) at ../Objects/abstract.c:2544 #92 slot_tp_init (self=self@entry=0x7f0b8a3ebb10, args=args@entry=0x7f0b8a4e0050, kwds=kwds@entry=0x7f0b8a014a70) at ../Objects/typeobject.c:5869 #93 0x0000556bdc2feb87 in type_call (type=<optimized out>, type@entry=0x556bdd2a85c0, args=0x7f0b8a4e0050, kwds=0x7f0b8a014a70) at ../Objects/typeobject.c:765 #94 0x0000556bdc2f8903 in PyObject_Call (func=0x556bdd2a85c0, arg=<optimized out>, kw=<optimized out>) at ../Objects/abstract.c:2544 #95 0x0000556bdc3128dd in do_call (nk=<optimized out>, na=<optimized out>, pp_stack=0x7ffc3ef9a458, func=0x556bdd2a85c0) at ../Python/ceval.c:4593 #96 call_function (oparg=<optimized out>, pp_stack=0x7ffc3ef9a458) at ../Python/ceval.c:4398 #97 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3013 #98 0x0000556bdc30b3cc in PyEval_EvalCodeEx (co=0x7f0b8a37d830, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kws=<optimized out>, kwcount=0, defs=0x0, defcount=0, closure=0x0) at ../Python/ceval.c:3608 #99 0x0000556bdc311886 in PyEval_EvalCode (locals=0x7f0b8a4ae170, globals=0x7f0b8a4ae170, co=<optimized out>) at ../Python/ceval.c:669 #100 exec_statement (locals=0x7f0b8a4ae170, globals=0x7f0b8a4ae170, prog=<optimized out>, f=<optimized out>) at ../Python/ceval.c:5093 #101 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:2122 #102 0x0000556bdc30b3cc in PyEval_EvalCodeEx (co=0x7f0b8a3ea4b0, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kws=<optimized out>, kwcount=0, defs=0x7f0b8a381068, defcount=5, closure=0x0) at ../Python/ceval.c:3608 #103 0x0000556bdc312953 in fast_function (nk=<optimized out>, na=<optimized out>, n=<optimized out>, pp_stack=0x7ffc3ef9a818, func=0x7f0b8a37d8d0) at ../Python/ceval.c:4471 #104 call_function (oparg=<optimized out>, pp_stack=0x7ffc3ef9a818) at ../Python/ceval.c:4396 #105 PyEval_EvalFrameEx (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3013 #106 0x0000556bdc30b3cc in PyEval_EvalCodeEx (co=0x7f0b8a37d4b0, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kws=<optimized out>, kwcount=0, defs=0x7f0b8a379ce8, defcount=1, closure=0x0) at ../Python/ceval.c:3608 #107 0x0000556bdc325b9b in function_call (func=func@entry=0x7f0b8a37de50, arg=0x7f0b8a3b5be0, kw=0x0) at ../Objects/funcobject.c:523 #108 0x0000556bdc2f8903 in PyObject_Call (func=0x7f0b8a37de50, arg=<optimized out>, kw=<optimized out>) at ../Objects/abstract.c:2544 #109 0x0000556bdc3b87e1 in RunModule (module=<optimized out>, set_argv0=1) at ../Modules/main.c:197 #110 0x0000556bdc3a68ed in Py_Main (argc=<optimized out>, argv=<optimized out>) at ../Modules/main.c:592#111 0x00007f0b8a54bd0a in __libc_start_main (main=0x556bdc3a6530 <main>, argc=5, argv=0x7ffc3ef9ac08, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7ffc3ef9abf8) at ../csu/libc-start.c:308 #112 0x0000556bdc3a646a in _start ()
If greenlets are used with threads they will leak weak references to
_thread._localdummy
objects. These objects seem to be internally used in the interpreter to implement thread local storage.Simple test case:
I can reproduce this on OS X 32/64 bit in the latest 2.7 version as well as linux 32 bit, both running the 0.3.4 version of greenlet.
The text was updated successfully, but these errors were encountered: