Bugs item #1069160, was opened at 2004-11-19 01:48 Message generated for change (Comment added) made by arigo You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=105470&aid=1069160&group_id=5470
Category: Threads Group: Python 2.4 Status: Open Resolution: None Priority: 6 Submitted By: Tim Peters (tim_one) Assigned to: Just van Rossum (jvr) Summary: PyThreadState_SetAsyncExc segfault Initial Comment: PyThreadState_SetAsyncExc() crawls over the list of tstates without holding a mutex. Python implementation code has tried to get away with this kind of thing before (& more than once <wink>: and segfaults are always eventually reported. A common cause is that PyThreadState_DeleteCurrent() is typically called *without* the GIL held, so any thread can try to delete its own tstate *while* PyThreadState_SetAsyncExc() is trying to muck with that tstate. That the GIL is held by PyThreadState_SetAsyncExc's caller is no protection. Instead the code has to serialize against tstate chain mutations by acquiring head_mutex for the duration. Of course this is rare and can be virtually impossible to reproduce, but that makes the bug worse, not "better". ---------------------------------------------------------------------- >Comment By: Armin Rigo (arigo) Date: 2005-02-07 11:27 Message: Logged In: YES user_id=4771 I didn't worry too much about ZAP deadlocks precisely because this problem was already there elsewhere. I guess that if this is a problem, it should be discussed and fixed in another bug report, after we apply the obvious two-liner patch of the current tracker. ---------------------------------------------------------------------- Comment By: Just van Rossum (jvr) Date: 2005-02-07 09:04 Message: Logged In: YES user_id=92689 I'm not able to judge the patch. I'm only the one who had the feature request (together with Alex Martelli), but I know next to nothing about the implementation and all the subtle details. If you need a volunteer to apply a patch, sure, I'll gladly help, but the discussion is way over my head here. ---------------------------------------------------------------------- Comment By: Tim Peters (tim_one) Date: 2005-02-07 01:51 Message: Logged In: YES user_id=31435 It's already documented that PyThreadState_SetAsyncExc() must be called with the GIL held. head_mutex is only used to protect traversal/mutation of the threadstate chain, so is only held internally by functions that create, or destroy, thread states or interpreter states. It's certainly possible to get a deadlock then after the patch, not due to the GIL, but due to head_mutex alone. I doubt that any __del__ method would create/destroy thread/interpreter states on its own, the bigger danger is that once a __del__ method is invoked, other threads can run too, and they may do anything (including calling PyThreadState_SetAsyncExc() again! that seem the most likely way to deadlock). It would be good to have a clue about "I have no idea why this function warns about return values greater than 1". If in fact it should be impossible to have a count greater than 1 given that concurrent mutations to the tstate chain are locked out after the patch, then the patch could be rewritten to release head_mutex when it found p->thread_id == id the first time, before the ZAP. After the ZAP, the function would just return: if (p->thread_id == id) { PyObject *temp = p->async_exc; Py_XINCREF(exc); p->async_exc = exc; HEAD_UNLOCK(); Py_XDECREF(temp); return 1; } Then deadlock couldn't occur. I should note that PyInterpreterState_Clear() is prone to the same kind of deadlocks on head_mutex (it calls PyThreadState_Clear() with head_mutex held, and the latter does a lot of ZAPping). ---------------------------------------------------------------------- Comment By: Guido van Rossum (gvanrossum) Date: 2005-02-07 00:44 Message: Logged In: YES user_id=6380 But it was Just's idea, so assigning to him. Q for Just: would applying the patch cause any problems in your code that's using this feature? (Q. for Tim: hoes does the head_mutex in this file relate to the GIL? The ZAP() call would seem to require that the GIL is already head when this fn. is called, because it could invoke arbitrary Python __del__ code. Is there any other call that could acquire these locks in the opposite order, causing deadlocks?) ---------------------------------------------------------------------- Comment By: Tim Peters (tim_one) Date: 2005-02-03 16:41 Message: Logged In: YES user_id=31435 Yup, that patch is all I had in mind. It's a shame that nobody cares enough about this function to test it (which isn't a dig at you, Armin -- I don't care enough either <0.5 wink>). BTW, I have no idea why this function warns about return values greater than 1. It's possible that the original author realized the tstate chain could mutate while the loop was running, and that's where "greater than 1" came from. If so, locking head_mutex should make that impossible ... OK, some quality time with CVS shows that Guido checked this function in, so assigning the report to him. ---------------------------------------------------------------------- Comment By: Armin Rigo (arigo) Date: 2005-02-02 11:19 Message: Logged In: YES user_id=4771 I guess that the patch you have in mind is just to protect the loop with HEAD_LOCK/HEAD_UNLOCK. I cannot test this patch (attached), though, as I don't have any code that uses this function. I can only tell that it also looks like a reasonable thing to do to me. ---------------------------------------------------------------------- Comment By: Tim Peters (tim_one) Date: 2004-11-19 15:32 Message: Logged In: YES user_id=31435 Changed Category to Threads. ---------------------------------------------------------------------- You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=105470&aid=1069160&group_id=5470 _______________________________________________ Python-bugs-list mailing list Unsubscribe: http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com