blob: d7c58db8dcd1e5d4ac7d39a9fad0641318fb8cc9 [file] [log] [blame]
// Copyright 2015 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef LIBBRILLO_BRILLO_STREAMS_STREAM_H_
#define LIBBRILLO_BRILLO_STREAMS_STREAM_H_
#include <cstdint>
#include <memory>
#include <base/callback.h>
#include <base/macros.h>
#include <base/memory/weak_ptr.h>
#include <base/time/time.h>
#include <brillo/brillo_export.h>
#include <brillo/errors/error.h>
namespace brillo {
// Stream is a base class that specific stream storage implementations must
// derive from to provide I/O facilities.
// The stream class provides general streaming I/O primitives to read, write and
// seek within a stream. It has methods for asynchronous (callback-based) as
// well as synchronous (both blocking and non-blocking) operations.
// The Stream class is abstract and cannot be created by itself.
// In order to construct a stream, you must use one of the derived classes'
// factory methods which return a stream smart pointer (StreamPtr):
//
// StreamPtr input_stream = FileStream::Open(path, AccessMode::READ);
// StreamPtr output_stream = MemoryStream::Create();
// uint8_t buf[1000];
// size_t read = 0;
// while (input_stream->ReadBlocking(buf, sizeof(buf), &read, nullptr)) {
// if (read == 0) break;
// output_stream->WriteAllBlocking(buf, read, nullptr);
// }
//
// NOTE ABOUT ASYNCHRONOUS OPERATIONS: Asynchronous I/O relies on a MessageLoop
// instance to be present on the current thread. Using Stream::ReadAsync(),
// Stream::WriteAsync() and similar will call MessageLoop::current() to access
// the current message loop and abort if there isn't one for the current thread.
// Also, only one outstanding asynchronous operation of particular kind (reading
// or writing) at a time is supported. Trying to call ReadAsync() while another
// asynchronous read operation is pending will fail with an error
// ("operation_not_supported").
//
// NOTE ABOUT READING FROM/WRITING TO STREAMS: In many cases underlying streams
// use buffered I/O. Using all read/write methods other than ReadAllAsync(),
// ReadAllBlocking(), WriteAllAsync(), WriteAllBlocking() will return
// immediately if there is any data available in the underlying buffer. That is,
// trying to read 1000 bytes while the internal buffer contains only 100 will
// return immediately with just those 100 bytes and no blocking or other I/O
// traffic will be incurred. This guarantee is important for efficient and
// correct implementation of duplex communication over pipes and sockets.
//
// NOTE TO IMPLEMENTERS: When creating new stream types, you must derive
// from this class and provide the implementation for its pure virtual methods.
// For operations that do not apply to your stream, make sure the corresponding
// methods return "false" and set the error to "operation_not_supported".
// You should use stream_utils::ErrorOperationNotSupported() for this. Also
// Make sure the stream capabilities functions like CanRead(), etc return
// correct values:
//
// bool MyReadOnlyStream::CanRead() const { return true; }
// bool MyReadOnlyStream::CanWrite() const { return false; }
// bool MyReadOnlyStream::WriteBlocking(const void* buffer,
// size_t size_to_write,
// size_t* size_written,
// ErrorPtr* error) {
// return stream_utils::ErrorOperationNotSupported(error);
// }
//
// The class should also provide a static factory methods to create/open
// a new stream:
//
// static StreamPtr MyReadOnlyStream::Open(..., ErrorPtr* error) {
// auto my_stream = std::make_unique<MyReadOnlyStream>(...);
// if (!my_stream->Initialize(..., error))
// my_stream.reset();
// }
// return my_stream;
// }
//
class BRILLO_EXPORT Stream {
public:
// When seeking in streams, whence specifies the origin of the seek operation.
enum class Whence { FROM_BEGIN, FROM_CURRENT, FROM_END };
// Stream access mode for open operations (used in derived classes).
enum class AccessMode { READ, WRITE, READ_WRITE };
// Standard error callback for asynchronous operations.
using ErrorCallback = base::Callback<void(const Error*)>;
virtual ~Stream() = default;
// == Stream capabilities ===================================================
// Returns true while stream is open. Closing the last reference to the stream
// will make this method return false.
virtual bool IsOpen() const = 0;
// Called to determine if read operations are supported on the stream (stream
// is readable). This method does not check if there is actually any data to
// read, only the fact that the stream is open in read mode and can be read
// from in general.
// If CanRead() returns false, it is guaranteed that the stream can't be
// read from. However, if it returns true, there is no guarantee that the
// subsequent read operation will actually succeed (for example, the stream
// position could be at the end of the data stream, or the access mode of
// the stream is unknown beforehand).
virtual bool CanRead() const = 0;
// Called to determine if write operations are supported on the stream (stream
// is writable).
// If CanWrite() returns false, it is guaranteed that the stream can't be
// written to. However, if it returns true, the subsequent write operation
// is not guaranteed to succeed (e.g. the output media could be out of free
// space or a transport error could occur).
virtual bool CanWrite() const = 0;
// Called to determine if random access I/O operations are supported on
// the stream. Sequential streams should return false.
// If CanSeek() returns false, it is guaranteed that the stream can't use
// Seek(). However, if it returns true, it might be possible to seek, but this
// is not guaranteed since the actual underlying stream capabilities might
// not be known.
// Note that non-seekable streams might still maintain the current stream
// position and GetPosition method might still be used even if CanSeek()
// returns false. However SetPosition() will almost always fail in such
// a case.
virtual bool CanSeek() const = 0;
// Called to determine if the size of the stream is known. Size of some
// sequential streams (e.g. based on pipes) is unknown beforehand, so this
// method can be used to check how reliable a call to GetSize() is.
virtual bool CanGetSize() const = 0;
// == Stream size operations ================================================
// Returns the size of stream data.
// If the stream size is unavailable/unknown, it returns 0.
virtual uint64_t GetSize() const = 0;
// Resizes the stream storage to |size|. Stream must be writable and support
// this operation.
virtual bool SetSizeBlocking(uint64_t size, ErrorPtr* error) = 0;
// Truncates the stream at the current stream pointer.
// Calls SetSizeBlocking(GetPosition(), ...).
bool TruncateBlocking(ErrorPtr* error);
// Returns the amount of data remaining in the stream. If the size of the
// stream is unknown, or if the stream pointer is at or past the end of the
// stream, the function returns 0.
virtual uint64_t GetRemainingSize() const = 0;
// == Seek operations =======================================================
// Gets the position of the stream I/O pointer from the beginning of the
// stream. If the stream position is unavailable/unknown, it returns 0.
virtual uint64_t GetPosition() const = 0;
// Moves the stream pointer to the specified position, relative to the
// beginning of the stream. This calls Seek(position, Whence::FROM_BEGIN),
// however it also provides proper |position| validation to ensure that
// it doesn't overflow the range of signed int64_t used by Seek.
bool SetPosition(uint64_t position, ErrorPtr* error);
// Moves the stream pointer by |offset| bytes relative to |whence|.
// When successful, returns true and sets the new pointer position from the
// beginning of the stream to |new_position|. If |new_position| is nullptr,
// new stream position is not returned.
// On error, returns false and specifies additional details in |error| if it
// is not nullptr.
virtual bool Seek(int64_t offset,
Whence whence,
uint64_t* new_position,
ErrorPtr* error) = 0;
// == Read operations =======================================================
// -- Asynchronous ----------------------------------------------------------
// Reads up to |size_to_read| bytes from the stream asynchronously. It is not
// guaranteed that all requested data will be read. It is not an error for
// this function to read fewer bytes than requested. If the function reads
// zero bytes, it means that the end of stream is reached.
// Upon successful read, the |success_callback| will be invoked with the
// actual number of bytes read.
// If an error occurs during the asynchronous operation, the |error_callback|
// is invoked with the error details. The error object pointer passed in as a
// parameter to the |error_callback| is valid only for the duration of that
// callback.
// If this function successfully schedules an asynchronous operation, it
// returns true. If it fails immediately, it will return false and set the
// error details to |error| object and will not call the success or error
// callbacks.
// The |buffer| must be at least |size_to_read| in size and must remain
// valid for the duration of the asynchronous operation (until either
// |success_callback| or |error_callback| is called).
// Only one asynchronous operation at a time is allowed on the stream (read
// and/or write)
// Uses ReadNonBlocking() and MonitorDataAvailable().
virtual bool ReadAsync(void* buffer,
size_t size_to_read,
const base::Callback<void(size_t)>& success_callback,
const ErrorCallback& error_callback,
ErrorPtr* error);
// Similar to ReadAsync() operation above but reads exactly |size_to_read|
// bytes from the stream into the |buffer|. Attempt to read past the end of
// the stream is considered an error in this case and will trigger the
// |error_callback|. The rest of restrictions and conditions of ReadAsync()
// method applies to ReadAllAsync() as well.
// Uses ReadNonBlocking() and MonitorDataAvailable().
virtual bool ReadAllAsync(void* buffer,
size_t size_to_read,
const base::Closure& success_callback,
const ErrorCallback& error_callback,
ErrorPtr* error);
// -- Synchronous non-blocking ----------------------------------------------
// Reads up to |size_to_read| bytes from the stream without blocking.
// The |buffer| must be at least |size_to_read| in size. It is not an error
// for this function to return without reading all (or any) the data.
// The actual amount of data read (which could be 0 bytes) is returned in
// |size_read|.
// On error, the function returns false and specifies additional error details
// in |error|.
// If end of stream is reached or if no data is currently available to be read
// without blocking, |size_read| will contain 0 and the function will still
// return true (success). In case of end-of-stream scenario, |end_of_stream|
// will also be set to true to indicate that no more data is available.
virtual bool ReadNonBlocking(void* buffer,
size_t size_to_read,
size_t* size_read,
bool* end_of_stream,
ErrorPtr* error) = 0;
// -- Synchronous blocking --------------------------------------------------
// Reads up to |size_to_read| bytes from the stream. This function will block
// until at least one byte is read or the end of stream is reached or until
// the stream is closed.
// The |buffer| must be at least |size_to_read| in size. It is not an error
// for this function to return without reading all the data. The actual amount
// of data read (which could be 0 bytes) is returned in |size_read|.
// On error, the function returns false and specifies additional error details
// in |error|. In this case, the state of the stream pointer is undefined,
// since some bytes might have been read successfully (and the pointer moved)
// before the error has occurred and |size_read| is not updated.
// If end of stream is reached, |size_read| will contain 0 and the function
// will still return true (success).
virtual bool ReadBlocking(void* buffer,
size_t size_to_read,
size_t* size_read,
ErrorPtr* error);
// Reads exactly |size_to_read| bytes to |buffer|. Returns false on error
// (reading fewer than requested bytes is treated as an error as well).
// Calls ReadAllBlocking() repeatedly until all the data is read.
virtual bool ReadAllBlocking(void* buffer,
size_t size_to_read,
ErrorPtr* error);
// == Write operations ======================================================
// -- Asynchronous ----------------------------------------------------------
// Writes up to |size_to_write| bytes from |buffer| to the stream
// asynchronously. It is not guaranteed that all requested data will be
// written. It is not an error for this function to write fewer bytes than
// requested.
// Upon successful write, the |success_callback| will be invoked with the
// actual number of bytes written.
// If an error occurs during the asynchronous operation, the |error_callback|
// is invoked with the error details. The error object pointer is valid only
// for the duration of the error callback.
// If this function successfully schedules an asynchronous operation, it
// returns true. If it fails immediately, it will return false and set the
// error details to |error| object and will not call the success or error
// callbacks.
// The |buffer| must be at least |size_to_write| in size and must remain
// valid for the duration of the asynchronous operation (until either
// |success_callback| or |error_callback| is called).
// Only one asynchronous operation at a time is allowed on the stream (read
// and/or write).
// Uses WriteNonBlocking() and MonitorDataAvailable().
virtual bool WriteAsync(const void* buffer,
size_t size_to_write,
const base::Callback<void(size_t)>& success_callback,
const ErrorCallback& error_callback,
ErrorPtr* error);
// Similar to WriteAsync() operation above but writes exactly |size_to_write|
// bytes from |buffet| to the stream. When all the data is written
// successfully, the |success_callback| is invoked.
// The rest of restrictions and conditions of WriteAsync() method applies to
// WriteAllAsync() as well.
// Uses WriteNonBlocking() and MonitorDataAvailable().
virtual bool WriteAllAsync(const void* buffer,
size_t size_to_write,
const base::Closure& success_callback,
const ErrorCallback& error_callback,
ErrorPtr* error);
// -- Synchronous non-blocking ----------------------------------------------
// Writes up to |size_to_write| bytes to the stream. The |buffer| must be at
// least |size_to_write| in size. It is not an error for this function to
// return without writing all the data requested (or any data at all).
// The actual amount of data written is returned in |size_written|.
// On error, the function returns false and specifies additional error details
// in |error|.
virtual bool WriteNonBlocking(const void* buffer,
size_t size_to_write,
size_t* size_written,
ErrorPtr* error) = 0;
// -- Synchronous blocking --------------------------------------------------
// Writes up to |size_to_write| bytes to the stream. The |buffer| must be at
// least |size_to_write| in size. It is not an error for this function to
// return without writing all the data requested. The actual amount of data
// written is returned in |size_written|.
// On error, the function returns false and specifies additional error details
// in |error|.
virtual bool WriteBlocking(const void* buffer,
size_t size_to_write,
size_t* size_written,
ErrorPtr* error);
// Writes exactly |size_to_write| bytes to |buffer|. Returns false on error
// (writing fewer than requested bytes is treated as an error as well).
// Calls WriteBlocking() repeatedly until all the data is written.
virtual bool WriteAllBlocking(const void* buffer,
size_t size_to_write,
ErrorPtr* error);
// == Finalizing/closing streams ===========================================
// Flushes all the user-space data from cache output buffers to storage
// medium. For read-only streams this is a no-op, however it is still valid
// to call this method on read-only streams.
// If an error occurs, the function returns false and specifies additional
// error details in |error|.
virtual bool FlushBlocking(ErrorPtr* error) = 0;
// Flushes all the user-space data from the cache output buffer
// asynchronously. When all the data is successfully flushed, the
// |success_callback| is invoked. If an error occurs while flushing, partial
// data might be flushed and |error_callback| is invoked. If there's an error
// scheduling the flush operation, it returns false and neither callback will
// be called.
virtual bool FlushAsync(const base::Closure& success_callback,
const ErrorCallback& error_callback,
ErrorPtr* error);
// Closes the underlying stream. The stream is also automatically closed
// when the stream object is destroyed, but since closing a stream is
// an operation that may fail, in situations when it is important to detect
// the failure to close the stream, CloseBlocking() should be used explicitly
// before destroying the stream object.
virtual bool CloseBlocking(ErrorPtr* error) = 0;
// == Data availability monitoring ==========================================
// Overloaded by derived classes to provide stream monitoring for read/write
// data availability for the stream. Calls |callback| when data can be read
// and/or written without blocking.
// |mode| specifies the type of operation to monitor for (read, write, both).
virtual bool WaitForData(AccessMode mode,
const base::Callback<void(AccessMode)>& callback,
ErrorPtr* error) = 0;
// Helper function for implementing blocking I/O. Blocks until the
// non-blocking operation specified by |in_mode| can be performed.
// If |out_mode| is not nullptr, it receives the actual operation that can be
// performed. For example, watching a stream for READ_WRITE while only
// READ can be performed, |out_mode| would contain READ even though |in_mode|
// was set to READ_WRITE.
// |timeout| is the maximum amount of time to wait. Set it to TimeDelta::Max()
// to wait indefinitely.
virtual bool WaitForDataBlocking(AccessMode in_mode,
base::TimeDelta timeout,
AccessMode* out_mode,
ErrorPtr* error) = 0;
// Cancels pending asynchronous read/write operations.
virtual void CancelPendingAsyncOperations();
protected:
Stream() = default;
private:
// Simple wrapper to call the externally exposed |success_callback| that only
// receives a size_t.
BRILLO_PRIVATE static void IgnoreEOSCallback(
const base::Callback<void(size_t)>& success_callback,
size_t read,
bool eos);
// The internal implementation of ReadAsync() and ReadAllAsync().
// Calls ReadNonBlocking and if there's no data available waits for it calling
// WaitForData(). The extra |force_async_callback| tell whether the success
// callback should be called from the main loop instead of directly from this
// method. This method only calls WaitForData() if ReadNonBlocking() returns a
// situation in which it would block (bytes_read = 0 and eos = false),
// preventing us from calling WaitForData() on streams that don't support such
// feature.
BRILLO_PRIVATE bool ReadAsyncImpl(
void* buffer,
size_t size_to_read,
const base::Callback<void(size_t, bool)>& success_callback,
const ErrorCallback& error_callback,
ErrorPtr* error,
bool force_async_callback);
// Called from the main loop when the ReadAsyncImpl finished right away
// without waiting for data. We use this callback to call the
// |sucess_callback| but invalidate the callback if the Stream is destroyed
// while this call is waiting in the main loop.
BRILLO_PRIVATE void OnReadAsyncDone(
const base::Callback<void(size_t, bool)>& success_callback,
size_t bytes_read,
bool eos);
// Called from WaitForData() when read operations can be performed
// without blocking (the type of operation is provided in |mode|).
BRILLO_PRIVATE void OnReadAvailable(
void* buffer,
size_t size_to_read,
const base::Callback<void(size_t, bool)>& success_callback,
const ErrorCallback& error_callback,
AccessMode mode);
// The internal implementation of WriteAsync() and WriteAllAsync().
// Calls WriteNonBlocking and if the write would block for it to not block
// calling WaitForData(). The extra |force_async_callback| tell whether the
// success callback should be called from the main loop instead of directly
// from this method. This method only calls WaitForData() if
// WriteNonBlocking() returns a situation in which it would block
// (size_written = 0 and eos = false), preventing us from calling
// WaitForData() on streams that don't support such feature.
BRILLO_PRIVATE bool WriteAsyncImpl(
const void* buffer,
size_t size_to_write,
const base::Callback<void(size_t)>& success_callback,
const ErrorCallback& error_callback,
ErrorPtr* error,
bool force_async_callback);
// Called from the main loop when the WriteAsyncImpl finished right away
// without waiting for data. We use this callback to call the
// |sucess_callback| but invalidate the callback if the Stream is destroyed
// while this call is waiting in the main loop.
BRILLO_PRIVATE void OnWriteAsyncDone(
const base::Callback<void(size_t)>& success_callback,
size_t size_written);
// Called from WaitForData() when write operations can be performed
// without blocking (the type of operation is provided in |mode|).
BRILLO_PRIVATE void OnWriteAvailable(
const void* buffer,
size_t size,
const base::Callback<void(size_t)>& success_callback,
const ErrorCallback& error_callback,
AccessMode mode);
// Helper callbacks to implement ReadAllAsync/WriteAllAsync.
BRILLO_PRIVATE void ReadAllAsyncCallback(
void* buffer,
size_t size_to_read,
const base::Closure& success_callback,
const ErrorCallback& error_callback,
size_t size_read,
bool eos);
BRILLO_PRIVATE void WriteAllAsyncCallback(
const void* buffer,
size_t size_to_write,
const base::Closure& success_callback,
const ErrorCallback& error_callback,
size_t size_written);
// Helper callbacks to implement FlushAsync().
BRILLO_PRIVATE void FlushAsyncCallback(
const base::Closure& success_callback,
const ErrorCallback& error_callback);
// Data members for asynchronous read operations.
bool is_async_read_pending_{false};
// Data members for asynchronous write operations.
bool is_async_write_pending_{false};
base::WeakPtrFactory<Stream> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(Stream);
};
// A smart pointer to the stream used to pass the stream object around.
using StreamPtr = std::unique_ptr<Stream>;
} // namespace brillo
#endif // LIBBRILLO_BRILLO_STREAMS_STREAM_H_