Tcl Source Code

View Ticket
Login
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"

Here is formated result of the execution (see below):

  1. run - non-blocking - OK, just to indicate, that the return of EAGAIN works at all
  2. run - blocking and wait in worker - OK also (you see the main thread waits in read operation)
  3. run - blocking and no-wait in worker so EAGAIN (for example the wait there is not possible) - ERROR: the main thread gets eof (and stop read), but "** last content" still not available and no real eof in worker.
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: