This issue tracker has been migrated to GitHub ,
and is currently read-only.
For more information,
see the GitHub FAQs in the Python's Developer Guide.
Created on 2013年05月07日 13:45 by Pierrick.Koch, last changed 2022年04月11日 14:57 by admin. This issue is now closed.
| Files | ||||
|---|---|---|---|---|
| File name | Uploaded | Description | Edit | |
| asynchat.async_chat.initiate_send.deldeque.patch | Pierrick.Koch, 2013年05月07日 13:45 | |||
| test_initiate_send.py | xdegaye, 2013年05月09日 19:19 | |||
| cpython.asyncore.patch | Pierrick.Koch, 2013年06月11日 13:27 | review | ||
| cpython.asyncore_2.patch | xdegaye, 2013年06月13日 10:39 | review | ||
| cpython.asyncore_3.patch | Pierrick.Koch, 2013年06月16日 20:54 | review | ||
| cpython.asyncore_4.patch | Pierrick.Koch, 2014年02月04日 10:48 | review | ||
| Messages (17) | |||
|---|---|---|---|
| msg188652 - (view) | Author: Pierrick Koch (Pierrick.Koch) | Date: 2013年05月07日 13:45 | |
Dear, del deque[0] is not safe, see the attached patch for the asynchat.async_chat.initiate_send method. fix the "IndexError: deque index out of range" of "del self.producer_fifo[0]" Best, Pierrick Koch |
|||
| msg188789 - (view) | Author: Xavier de Gaye (xdegaye) * (Python triager) | Date: 2013年05月09日 19:19 | |
The attached script, test_initiate_send.py, tests initiate_send with
threads, causing duplicate writes and an IndexError. This is the
script output using python on the default branch:
$ python test_initiate_send.py
--- Test: duplicate data sent ---
chat.send('thread data')
chat.send('thread data')
--- Test: IndexError ---
chat.send('thread data')
chat.send('thread data')
Exception in thread Thread-2:
Traceback (most recent call last):
File "Lib/threading.py", line 644, in _bootstrap_inner
self.run()
File "Lib/threading.py", line 601, in run
self._target(*self._args, **self._kwargs)
File "test_initiate_send.py", line 25, in <lambda>
thread = threading.Thread(target=lambda : chat.push('thread data'))
File "Lib/asynchat.py", line 194, in push
self.initiate_send()
File "Lib/asynchat.py", line 254, in initiate_send
del self.producer_fifo[0]
IndexError: deque index out of range
The script does not fail with Pierrick patch:
$ python test_initiate_send.py
--- Test: duplicate data sent ---
chat.send('main data')
chat.send('thread data')
--- Test: IndexError ---
chat.send('thread data')
The patch misses to also appendleft() 'first' when 'num_sent' is zero,
which may happen on getting EWOULDBLOCK on send().
|
|||
| msg190232 - (view) | Author: Andrew Stormont (andy_js) | Date: 2013年05月28日 18:00 | |
Looks like a reasonable fix to me. What needs to be done to get this integrated? |
|||
| msg190233 - (view) | Author: Giampaolo Rodola' (giampaolo.rodola) * (Python committer) | Date: 2013年05月28日 18:15 | |
After applying the patch I get these 2 failures on Linux: ====================================================================== FAIL: test_simple_producer (__main__.TestAsynchat) ---------------------------------------------------------------------- Traceback (most recent call last): File "Lib/test/test_asynchat.py", line 198, in test_simple_producer self.fail("join() timed out") AssertionError: join() timed out ====================================================================== FAIL: test_simple_producer (__main__.TestAsynchat_WithPoll) ---------------------------------------------------------------------- Traceback (most recent call last): File "Lib/test/test_asynchat.py", line 198, in test_simple_producer self.fail("join() timed out") AssertionError: join() timed out |
|||
| msg190299 - (view) | Author: Xavier de Gaye (xdegaye) * (Python triager) | Date: 2013年05月29日 12:40 | |
The problem is that when the fifo contains a producer and the more() method of the producer returns a non-empty bytes sequence, then the producer must be put back in the fifo first. This is what does the following change made to Pierrick patch: diff --git a/Lib/asynchat.py b/Lib/asynchat.py --- a/Lib/asynchat.py +++ b/Lib/asynchat.py @@ -229,6 +229,7 @@ except TypeError: data = first.more() if data: + self.producer_fifo.appendleft(first) self.producer_fifo.appendleft(data) continue The asynchat test is OK when the patch is modified with the above change. However, then the patch does not make initiate_send() thread safe. There is now a race condition: another thread may be allowed to run between the two appendleft() calls, this other thread may then call the more() method of 'first' and send the returned bytes. When that happens, the sent data is mis-ordered. |
|||
| msg190300 - (view) | Author: Giampaolo Rodola' (giampaolo.rodola) * (Python committer) | Date: 2013年05月29日 12:44 | |
I think we shouldn't expect asynchat to be thread safe in this regard. |
|||
| msg190305 - (view) | Author: Andrew Stormont (andy_js) | Date: 2013年05月29日 13:01 | |
What about changing: self.producer_fifo.appendleft(first) self.producer_fifo.appendleft(data) To self.producer_fifo.extendleft([data, first]) Assuming deque's extendleft is actually thread safe. |
|||
| msg190318 - (view) | Author: Xavier de Gaye (xdegaye) * (Python triager) | Date: 2013年05月29日 16:40 | |
extendleft is an extension module C function (in the _collections module) that does not release the GIL, so it is thread safe. |
|||
| msg190532 - (view) | Author: Andrew Stormont (andy_js) | Date: 2013年06月03日 10:22 | |
Great. Everybody's happy now, surely? |
|||
| msg190535 - (view) | Author: Giampaolo Rodola' (giampaolo.rodola) * (Python committer) | Date: 2013年06月03日 10:35 | |
patch plus "self.producer_fifo.extendleft([data, first])" seems legit and I verified pyftpdlib tests pass. Last thing missing from the patch is a test case. Pierrick can you merge test_initiate_send.py into Lib/test_asynchat.py and provide a new patch? |
|||
| msg190964 - (view) | Author: Pierrick Koch (Pierrick.Koch) | Date: 2013年06月11日 13:27 | |
sorry for the delay, here is the updated patch, shall I add a new class in Lib/test/test_asynchat.py ? |
|||
| msg191071 - (view) | Author: Xavier de Gaye (xdegaye) * (Python triager) | Date: 2013年06月13日 10:39 | |
Attached are two test cases for this patch. test_simple_producer still fails with the new patch because it should be: self.producer_fifo.extendleft([first, data]) instead of: self.producer_fifo.appendleft([data, first]) The order of the items in the list is reversed, as documented in deque.extendleft. So the attachment also includes the corrected patch with this single change. I still think that if num_sent == 0, then 'first' should be put back in the queue. This means that initiate_send should not attempt anymore to send an empty string, which is confusing anyway, and therefore at the beginning of initiate_send, when 'if not first', then we should return in all cases and not only when 'first' is None. |
|||
| msg191075 - (view) | Author: Andrew Stormont (andy_js) | Date: 2013年06月13日 10:45 | |
I think you mean: self.producer_fifo.extendleft([data, first]) Instead of: self.producer_fifo.extendleft([first, data]) No? |
|||
| msg191288 - (view) | Author: Pierrick Koch (Pierrick.Koch) | Date: 2013年06月16日 20:54 | |
last patch, replaced: self.producer_fifo.appendleft([data, first]) by: self.producer_fifo.extendleft([data, first]) |
|||
| msg191318 - (view) | Author: Xavier de Gaye (xdegaye) * (Python triager) | Date: 2013年06月17日 07:10 | |
> I think you mean: > > self.producer_fifo.extendleft([data, first]) > > Instead of: > > self.producer_fifo.extendleft([first, data]) > > No? No, I do mean: self.producer_fifo.extendleft([first, data]) See the documentation. Also: >>> from collections import deque >>> a = deque([0, 1, 2, 3]) >>> a.extendleft([11, 22]) >>> a deque([22, 11, 0, 1, 2, 3]) |
|||
| msg191319 - (view) | Author: Xavier de Gaye (xdegaye) * (Python triager) | Date: 2013年06月17日 07:14 | |
After applying the last patch cpython.asyncore_3.patch (while cpython.asyncore_2.patch does not fail): ====================================================================== FAIL: test_simple_producer (test.test_asynchat.TestAsynchat) ---------------------------------------------------------------------- Traceback (most recent call last): File "Lib/test/test_asynchat.py", line 198, in test_simple_producer self.fail("join() timed out") AssertionError: join() timed out ====================================================================== FAIL: test_simple_producer (test.test_asynchat.TestAsynchat_WithPoll) ---------------------------------------------------------------------- Traceback (most recent call last): File "Lib/test/test_asynchat.py", line 198, in test_simple_producer self.fail("join() timed out") AssertionError: join() timed out |
|||
| msg210196 - (view) | Author: Pierrick Koch (Pierrick.Koch) | Date: 2014年02月04日 10:48 | |
Fix patch from Xavier's comment, sorry for the delay. Lib/test/test_asynchat.py passes (Ran 27 tests in 1.431s) |
|||
| History | |||
|---|---|---|---|
| Date | User | Action | Args |
| 2022年04月11日 14:57:45 | admin | set | github: 62125 |
| 2021年10月21日 11:28:49 | iritkatriel | set | status: open -> closed superseder: Close asyncore/asynchat/smtpd issues and list them here resolution: wont fix stage: resolved |
| 2018年07月11日 07:34:49 | serhiy.storchaka | set | type: crash -> behavior |
| 2014年02月04日 10:48:29 | Pierrick.Koch | set | files:
+ cpython.asyncore_4.patch messages: + msg210196 |
| 2013年06月17日 07:14:39 | xdegaye | set | messages: + msg191319 |
| 2013年06月17日 07:10:38 | xdegaye | set | messages: + msg191318 |
| 2013年06月16日 20:54:44 | Pierrick.Koch | set | files:
+ cpython.asyncore_3.patch messages: + msg191288 |
| 2013年06月13日 10:45:21 | andy_js | set | messages: + msg191075 |
| 2013年06月13日 10:39:37 | xdegaye | set | files:
+ cpython.asyncore_2.patch messages: + msg191071 |
| 2013年06月11日 13:27:41 | Pierrick.Koch | set | files:
+ cpython.asyncore.patch messages: + msg190964 |
| 2013年06月03日 10:35:32 | giampaolo.rodola | set | messages: + msg190535 |
| 2013年06月03日 10:22:56 | andy_js | set | messages: + msg190532 |
| 2013年05月29日 16:40:59 | xdegaye | set | messages: + msg190318 |
| 2013年05月29日 13:01:09 | andy_js | set | messages: + msg190305 |
| 2013年05月29日 12:44:30 | giampaolo.rodola | set | messages: + msg190300 |
| 2013年05月29日 12:40:25 | xdegaye | set | messages: + msg190299 |
| 2013年05月28日 18:15:56 | giampaolo.rodola | set | messages: + msg190233 |
| 2013年05月28日 18:00:10 | andy_js | set | nosy:
+ andy_js messages: + msg190232 |
| 2013年05月09日 19:19:55 | xdegaye | set | files:
+ test_initiate_send.py nosy: + xdegaye messages: + msg188789 |
| 2013年05月07日 15:00:44 | r.david.murray | set | nosy:
+ giampaolo.rodola |
| 2013年05月07日 13:45:56 | Pierrick.Koch | create | |