Eric Pollmann wrote:
> Hey Darin,
>
> Interesting! This sounds promising from my perspective as a potential
> consumer.
>
> A few questions (please forgive if these are self-evident, I'm not a
> networking guru. ;) :
>
> - What are the OnDataWriteable parameters "context", "offset", and "count"?
context is a user supplied parameter passed to AsyncWrite. It
is simply forwarded to nsIStreamWriter::OnDataWriteable().
offset indicates the current position in the stream. It is just
there to indicate the amount of data already written. In the case
of nsIStreamListener's OnDataAvailable method, offset indicates the
amount of data that has already been read. Most applications can
just ignore this parameter.
count indicates the maximum number of bytes that can be written
without blocking.
> - What would I use the channel for?
The channel parameter is the same channel that you called AsyncWrite
on. It tells the nsIStreamWriter implementation which channel is
being written to.
> - How many bytes can I write when I am notified? ("count" ?)
Between 0 and count bytes may be written without blocking.
> - Am I only allowed to write equal to (or less than) some exact number
> of bytes to the stream when notified, or will some form of buffering for
> extra data be built-in?
No form of buffering for extra data will be explicitly built in.
This allows us to optimize things. If you want buffering then
you have to supply the code for that yourself (though there may
be some convenience classes written for this). The point is that
with this model, the implementations of the channels become less
complex, and code like buffering is externalized... hence, the code
is made common to all channels independent of their underlying
protocol.
> - The following seem like they might be handled by the write method of
> the output stream, but just for completeness:
> - Can I indicate how many bytes I want to write if different than
> requested?
nsIOutputStream::Write(in string buf, in unsigned long count)
> - How do I indicate that I am done writing?
Call nsIOutputStream::Close() or it may be better if we added
a status return code to OnDataWriteable. That return code
would indicate whether or not all data had been written. Hmm..
something to think about.
Another important thing to think about with this design is that
it will be quite common for the channel to call OnDataWriteable
when no data is available for writing. To solve this, the
implementor of OnDataWriteable should Suspend the channel until
data is available for writing and then call Resume on the channel
to "wake up" the channel and have it continue the async write.
If we didn't do something like this, then the channel would most
likely end up busy waiting on the nsIStreamWriter, which would be
very bad.
>
> You might want to talk to Gagan about his "stream wrapper" stream idea
> to see how this jives with that and/or if it replaces it entirely.
Hmmm... I haven't heard anything about this yet.
>
> -Eric
>
> Darin Fisher wrote:
>
>> I have been thinking about the design of nsIChannel::AsyncWrite.
>> Currently, we expect the user to provide a nsIInputStream as the source
>> of data to be written to the channel. This effectively requires the
>> data to be buffered before it can be written to the channel. In the
>> case of form posting, this buffer is a temporary file.
>>
>> From a high level perspective this buffering of data seems unnecessary
>> and wasteful. It should be possible to write the form data directly to
>> the underlying transport instead of requiring the use of a temporary
>> buffer. That is, if the transport were given control over when data
>> is actually written to the channel, then buffering could easily be
>> eliminated.
>>
>> How would this work? Well... the transport can fire a callback when a
>> chunk of data could be written without blocking. This callback could be
>> propogated to the caller of AsyncWrite in some fashion. The implementor
>> of that callback would then supply the chunk of data.
>>
>> This new design is very much in line with the design of AsyncRead. For
>> AsyncRead, the transport fires a callback whenever there is a chunk of
>> data to be read. In this manner, the transport drives the entire
>> process of reading data from a channel. My proposal for AsyncWrite is
>> to make it similarly drive the entire process of writing data to a
>> channel.
>>
>> Currently AsyncWrite is declared as:
>>
>> void AsyncWrite(in nsIInputStream source,
>> in nsIStreamObserver observer,
>> in nsISupports context);
>>
>> The observer is notified when the write starts and stops. The context
>> is passed as an opaque parameter to the observer methods.
>>
>> My proposal is to change this to:
>>
>> void AsyncWrite(in nsIStreamWriter writer,
>> in nsISupports context);
>>
>> with
>>
>> interface nsIStreamWriter : nsIStreamObserver
>> {
>> void OnDataWriteable(in nsIChannel channel,
>> in nsISupports context,
>> in nsIOutputStream output,
>> in unsigned long offset,
>> in unsigned long count);
>> };
>>
>> This would provide the interface necessary to allow AsyncWrite to be
>> transport driven.
>>
>> BTW: the name nsIStreamWriter is totally up to debate. It isn't exactly
>> symmetric with nsIStreamListener used by AsyncRead. Perhaps
>> nsIStreamProvider would be better, or perhaps nsIStreamListener should
>> be changed to nsIStreamReader? Or, what about nsIAsyncWriter and
>> nsIAsyncReader?
>>
>> At any rate, I'd really like to get some feedback from people regarding
>> these changes. I believe that it is a good idea. To me, it is simply
>> more consistent with the concept of asynchronous channel io, and I
>> believe that it would lead to more efficient protocol and application
>> implementations.
>>
>> Darin
>>