Ticket UUID: | b9bfaf5fdb2cb794475fa70121c88859a8fc8496 | |||
Title: | EAGAIN throw in reflected channel in blocking mode causes eof (data loss) | |||
Type: | Bug | Version: | 8.5 | |
Submitter: | sebres | Created on: | 2016-06-23 20:03:38 | |
Subsystem: | 25. Channel System | Assigned To: | sebres | |
Priority: | 5 Medium | Severity: | Minor | |
Status: | Open | Last Modified: | 2016-07-12 07:32:59 | |
Resolution: | None | Closed By: | nobody | |
Closed on: | ||||
Description: |
Th-1 get a reflected channel "rch" for real non-blocking channel "ch" and do the processing of real channel totally asynchronously in thread Th-2. If Th-1 switches the reflected channel to blocking mode and then does `set data [read $rch]` And if currently no data for "ch" (but still no eof), the Th-2 executes "return -code error EAGAIN" to signal it should repeat later. Then Th-1 get eof on "rch" and stop read. I know that Th-2 could wait with something like `vwait`, etc., but imho, reflected channel should also accept return code EAGAIN in blocking mode also (and wait then in the main thread of channel Th-1, not in reflected one Th-2, because if it asynchronous, it can serve several channels at once)... | |||
User Comments: |
sebres added on 2016-07-12 07:32:59:
Oh yeah, forgotten... My argument is - the worker that serves reflected channel, should decide to wait or just to send an EAGAIN. But not another thread, that by setting of blocking mode, currently leaves him no choice. sebres added on 2016-07-12 06:59:09: > If the transferred channel is set to blocking mode, EAGAIN should result in an EOF. - Where it is so described? - Why it should be comprehensible? If someone will produce EOF, the callback should return zero bytes. Imho, EAGAIN stands clearly for repeat again. Once again, as I already wrote, of course the worker can although wait inside read callback (2nd run), but there are situations, where it can be impossible or undesirable - just for example if the worker asynchronously serves multiple channels (multi-channel factory), the entering of wait cycle tcl-side (vwait, etc.) makes sometimes too large overhead (in addition to growing of the call / frame stack). Apart from the fact, that another thread has made blocking mode (so it will and can wait also). > would break existing implementations. The solution will be an easy-fix, so I don't think it will break something existing. apnadkarni added on 2016-07-12 02:24:43: OK, I misunderstood your original post. I thought you had a reflected channel in the main thread that wrapped another reflected channel in the worker thread as opposed to transferring the channel created in the worker thread to the main thread. The behaviour exhibited in your demo code does not seem like a bug to me. If the transferred channel is set to blocking mode, EAGAIN should result in an EOF. In the common case of a single thread, how would you expect the channel code to behave on seeing a EAGAIN? It cannot return to the caller because it is supposed to be a blocking call. It cannot go to the event loop via vwait or similar because that is not supposed to happen in a blocking call where no other code is supposed to execute until the blocking call returns. And I seriously doubt we would want differing behaviour depending on whether the app is using multiple threads or not (whether the channel code to track that is a different matter). I also suspect that changing this documented behaviour, even if possible, would break existing implementations. sebres added on 2016-07-11 19:22:00:
Example demo-code see in attachment "reflected channel - multi-threaded demo-test.tcl"
1468262804.306 | tid21E8 | =MTH | ================================================================================ 1468262804.307 | tid21E8 | =MTH | ==== Blocking: 0, Wait-in-worker: 0 =================== 1468262804.307 | tid21E8 | =MTH | ================================================================================ 1468262804.325 | tid0FF0 | -WRK | -- channel initialize rc69 ... read 1468262804.326 | tid0FF0 | -WRK | ++ channel wait-in-worker 1 blocking 0 eof 0 content {1st data} 1468262804.326 | tid21E8 | =MTH | CREATED ... rc69 1468262804.328 | tid0FF0 | -WRK | -- channel blocking rc69 ... 0 1468262804.328 | tid0FF0 | -WRK | .. channel configure rc69 ... -wait-in-worker 1 1468262804.328 | tid0FF0 | -WRK | -> channel read rc69 ... 4096 1468262804.328 | tid0FF0 | -WRK | <- channel read rc69 ... 8 1468262804.329 | tid21E8 | =MTH | !!BUF:1st data 1468262804.329 | tid0FF0 | -WRK | -> channel read rc69 ... 4096 1468262804.329 | tid0FF0 | -WRK | <- channel read rc69 ... EAGAIN 1468262804.379 | tid0FF0 | -WRK | -> channel read rc69 ... 4096 1468262804.379 | tid0FF0 | -WRK | <- channel read rc69 ... EAGAIN 1468262804.429 | tid0FF0 | -WRK | -> channel read rc69 ... 4096 1468262804.430 | tid0FF0 | -WRK | <- channel read rc69 ... EAGAIN 1468262804.481 | tid0FF0 | -WRK | -> channel read rc69 ... 4096 1468262804.482 | tid0FF0 | -WRK | <- channel read rc69 ... EAGAIN 1468262804.534 | tid0FF0 | -WRK | -> channel read rc69 ... 4096 1468262804.534 | tid0FF0 | -WRK | <- channel read rc69 ... EAGAIN 1468262804.579 | tid0FF0 | -WRK | ** last content ... 1468262804.585 | tid0FF0 | -WRK | -> channel read rc69 ... 4096 1468262804.585 | tid0FF0 | -WRK | <- channel read rc69 ... 10 1468262804.585 | tid21E8 | =MTH | !!BUF:, 2nd data 1468262804.636 | tid0FF0 | -WRK | -> channel read rc69 ... 4096 1468262804.637 | tid0FF0 | -WRK | <- channel read rc69 ... 0 - EOF 1468262804.687 | tid21E8 | =MTH | OK**READ:18--1st data, 2nd data 1468262804.687 | tid21E8 | =MTH | CLOSE ... rc69 1468262804.688 | tid0FF0 | -WRK | -- channel finalize rc69 ... 1468262804.688 | tid0FF0 | -WRK | -- channel wait-in-worker 1 blocking 0 eof 1 content {} 1468262804.689 | tid21E8 | =MTH | ================================================================================ 1468262804.689 | tid21E8 | =MTH | ==== Blocking: 1, Wait-in-worker: 1 =================== 1468262804.689 | tid21E8 | =MTH | ================================================================================ 1468262804.690 | tid0FF0 | -WRK | -- channel initialize rc70 ... read 1468262804.690 | tid0FF0 | -WRK | ++ channel wait-in-worker 1 blocking 0 eof 0 content {1st data} 1468262804.690 | tid21E8 | =MTH | CREATED ... rc70 1468262804.691 | tid0FF0 | -WRK | -- channel blocking rc70 ... 1 1468262804.691 | tid0FF0 | -WRK | .. channel configure rc70 ... -wait-in-worker 1 1468262804.691 | tid0FF0 | -WRK | -> channel read rc70 ... 4096 1468262804.691 | tid0FF0 | -WRK | <- channel read rc70 ... 8 1468262804.692 | tid0FF0 | -WRK | -> channel read rc70 ... 4096 1468262804.941 | tid0FF0 | -WRK | ** last content ... 1468262804.942 | tid0FF0 | -WRK | <- channel read rc70 ... 10 1468262804.942 | tid0FF0 | -WRK | -> channel read rc70 ... 4096 1468262804.942 | tid0FF0 | -WRK | <- channel read rc70 ... 0 - EOF 1468262804.942 | tid21E8 | =MTH | !!BUF:1st data, 2nd data 1468262804.942 | tid21E8 | =MTH | OK**READ:18--1st data, 2nd data 1468262804.943 | tid21E8 | =MTH | CLOSE ... rc70 1468262804.943 | tid0FF0 | -WRK | -- channel finalize rc70 ... 1468262804.943 | tid0FF0 | -WRK | -- channel wait-in-worker 1 blocking 1 eof 1 content {} 1468262804.943 | tid21E8 | =MTH | ================================================================================ 1468262804.943 | tid21E8 | =MTH | ==== Blocking: 1, Wait-in-worker: 0 =================== 1468262804.943 | tid21E8 | =MTH | ================================================================================ 1468262804.944 | tid0FF0 | -WRK | -- channel initialize rc71 ... read 1468262804.944 | tid0FF0 | -WRK | ++ channel wait-in-worker 1 blocking 0 eof 0 content {1st data} 1468262804.944 | tid21E8 | =MTH | CREATED ... rc71 1468262804.945 | tid0FF0 | -WRK | -- channel blocking rc71 ... 1 1468262804.945 | tid0FF0 | -WRK | .. channel configure rc71 ... -wait-in-worker 0 1468262804.945 | tid0FF0 | -WRK | -> channel read rc71 ... 4096 1468262804.945 | tid0FF0 | -WRK | <- channel read rc71 ... 8 1468262804.946 | tid0FF0 | -WRK | -> channel read rc71 ... 4096 1468262804.946 | tid0FF0 | -WRK | <- channel read rc71 ... EAGAIN 1468262804.946 | tid21E8 | =MTH | !!BUF:1st data 1468262804.947 | tid21E8 | =ERR | ERROR**READ:8--1st data 1468262804.947 | tid21E8 | =MTH | CLOSE ... rc71 1468262804.948 | tid0FF0 | -WRK | -- channel finalize rc71 ... 1468262804.948 | tid0FF0 | -WRK | -- channel wait-in-worker 0 blocking 1 eof 0 content {} 1468262804.948 | tid21E8 | =MTH | ================================================================================ apnadkarni added on 2016-07-11 16:25:38: When you say reflected channel, which particular reflected channel implementation are you referring to? Or are you saying the reflected channel infrastructure in the core is converting the EAGAIN to eof (which I find hard to believe since the return value from the second thread should happen outside any core channel code). sebres added on 2016-07-11 15:59:48: Not so easy, but I'll try to make a demo... dgp added on 2016-07-11 15:45:53: I can't make sense of this. Can this description be converted into a demo script? |
Attachments:
- reflected channel - multi-threaded demo-test.tcl [download] added by sebres on 2016-07-11 19:18:20. [details]