chromium/content/browser/byte_stream.h

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CONTENT_BROWSER_BYTE_STREAM_H_
#define CONTENT_BROWSER_BYTE_STREAM_H_

#include <stddef.h>

#include <memory>

#include "base/functional/callback.h"
#include "base/memory/ref_counted.h"
#include "content/common/content_export.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "net/base/io_buffer.h"

namespace base {
class SequencedTaskRunner;
}

namespace content {

// A byte stream is a pipe to transfer bytes between a source and a
// sink, which may be on different threads.  It is intended to be the
// only connection between source and sink; they need have no
// direct awareness of each other aside from the byte stream.  The source and
// the sink have different interfaces to a byte stream, |ByteStreamWriter|
// and |ByteStreamReader|.  A pair of connected interfaces is generated by
// calling |CreateByteStream|.
//
// The source adds bytes to the bytestream via |ByteStreamWriter::Write|
// and the sink retrieves bytes already written via |ByteStreamReader::Read|.
//
// When the source has no more data to add, it will call
// |ByteStreamWriter::Close| to indicate that.  Operation status at the source
// is indicated to the sink via an int passed to the Close() method and returned
// from the GetStatus() method. Source and sink must agree on the interpretation
// of this int.
//
// Normally the source is not managed after the relationship is setup;
// it is expected to provide data and then close itself.  If an error
// occurs on the sink, it is not signalled to the source via this
// mechanism; instead, the source will write data until it exausts the
// available space.  If the source needs to be aware of errors occuring
// on the sink, this must be signalled in some other fashion (usually
// through whatever controller setup the relationship).
//
// Callback lifetime management: No lifetime management is done in this
// class to prevent registered callbacks from being called after any
// objects to which they may refer have been destroyed.  It is the
// responsibility of the callers to avoid use-after-free references.
// This may be done by any of several mechanisms, including weak
// pointers, scoped_refptr references, or calling the registration
// function with a null callback from a destructor.  To enable the null
// callback strategy, callbacks will not be stored between retrieval and
// evaluation, so setting a null callback will guarantee that the
// previous callback will not be executed after setting.
//
// Class methods are virtual to allow mocking for tests; these classes
// aren't intended to be base classes for other classes.
//
// Sample usage (note that this does not show callback usage):
//
//    void OriginatingClass::Initialize() {
//      // Create a stream for sending bytes from IO->FILE threads.
//      std::unique_ptr<ByteStreamWriter> writer;
//      std::unique_ptr<ByteStreamReader> reader;
//      CreateByteStream(
//          GetIOThreadTaskRunner({}),
//          base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock, ...}),
//          kStreamBufferSize /* e.g. 10240.  */,
//          &writer,
//          &reader);         // Presumed passed to FILE thread for reading.
//
//      // Setup callback for writing.
//      writer->RegisterCallback(base::BindRepeating(&SpaceAvailable, this));
//
//      // Do initial round of writing.
//      SpaceAvailable();
//    }
//
//    // May only be run on first argument task runner, in this case the IO
//    // thread.
//    void OriginatingClass::SpaceAvailable() {
//      while (<data available>) {
//        std::unique_ptr<net::IOBuffer> buffer;
//        size_t buffer_length;
//        // Create IOBuffer, fill in with data, and set buffer_length.
//        if (!writer->Write(buffer, buffer_length)) {
//          // No more space; return and we'll be called again
//          // when there is space.
//          return;
//        }
//      }
//      writer->Close(<operation status>);
//      writer.reset(NULL);
//    }
//
//    // On File thread; containing class setup not shown.
//
//    void ReceivingClass::Initialize() {
//      // Initialization
//      reader->RegisterCallback(base::BindRepeating(&DataAvailable, obj));
//    }
//
//    // Called whenever there's something to read.
//    void ReceivingClass::DataAvailable() {
//      scoped_refptr<net::IOBuffer> data;
//      size_t length = 0;
//
//      while (ByteStreamReader::STREAM_HAS_DATA ==
//             (state = reader->Read(&data, &length))) {
//        // Process |data|.
//      }
//
//      if (ByteStreamReader::STREAM_COMPLETE == state) {
//        int status = reader->GetStatus();
//        // Process error or successful completion in |status|.
//      }
//
//      // if |state| is STREAM_EMPTY, we're done for now; we'll be called
//      // again when there's more data.
//    }
class CONTENT_EXPORT ByteStreamWriter {};

class CONTENT_EXPORT ByteStreamReader {};

CONTENT_EXPORT void CreateByteStream(
    scoped_refptr<base::SequencedTaskRunner> input_task_runner,
    scoped_refptr<base::SequencedTaskRunner> output_task_runner,
    size_t buffer_size,
    std::unique_ptr<ByteStreamWriter>* input,
    std::unique_ptr<ByteStreamReader>* output);

}  // namespace content

#endif  // CONTENT_BROWSER_BYTE_STREAM_H_