Skip to content

Finalize pull/pullInto behavior #423

@tyoshino

Description

@tyoshino

(Branched from #379 (comment))

Current situation

Based on the result of the discussion at #363, we've introduced pullInto for ReadableByteStream.

The pull and pullInto in the ReadableByteStream were designed to be invoked in response to read() and read(view) call on the stream. It was temporary design.

Now, in the PR #418, I'm converging ReadableStream and ReadableByteStream. Based on the conclusion on #353, ReadableByteStream has a queue in it as well as ReadableStream. I've also ported the strategy based queuing control mechanism to ReadableByteStream for converging. So, now the 1:1 correspondence between read()/read(view) and pull/pullInto is impossible.

I'm trying to give a reasonable semantics to them.


Role of pullInto

Currently, pullInto has a role of notifying the underlying byte source of the outstanding TypedArray (and region on it) to which the underlying byte source should write generated data. So, whenever:

  • a new TypedArray comes
  • or the outstanding TypedArray is partially filled
  • or filled the outstanding TypedArray but there're more TypedArrays waiting to get filled

the controller should invoke a pullInto.

Unlike pullInto, the signal of pull is just that "the amount in the queue has changed". This is probable by looking at desiredSize.


Plan taken in the PR

My plan was:

  • pull is invoked when:
    • read() is called when the queue is empty
    • read() is called and it consumed all or part of the queue
    • read(view) is called and it consumed all or part of the queue
  • pullInto is invoked when:
    • read(view) is called when the queue is empty
    • read(view) is called and it consumed part of the queue but needs to fill more bytes to return to the consumer with at least 1 element (e.g. 2 byte for Uint16Array) filled.

I planned to do nothing on invocation of controller methods. For example, after the source calls controller.enqueue() in pull (or outside pull), the source can probe how much more data is required to fill the queue by accessing controller.desiredSize. But I realized it's more complicated. After calling enqueue() in response to pullInto (either in pullInto or async to pullInto), it's possible there's more read(view)s are waiting.

It seems some more redesign should be made.


Invocation timing and repetition

ReadableByteStream has a new pull/pullInto invocation scheme.

  • pull is required when the current call stack contains any pull/pullInto (check by _pulling), schedule by setting _pullAgain
  • pull/pullInto invocation code is in (or followed by) a while loop (see ReadableByteStreamControllerCallPullOrPullIntoRepeatedlyIfNeeded()) to repeat pull/pullInto invocation synchronously while _pullAgain is set in the last pull/pullInto
  • when respond() or enqueue() fail to fill enough bytes (only 1 byte filled in Uint16Array), asynchronously invoke pullInto with the remaining region again.
    • this depends on how we resolve the issue above (addition of desiredSize like API?)

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions