TSDuck v3.45-4766
MPEG Transport Stream Toolkit
Loading...
Searching...
No Matches
ts::NonBlockingDevice Class Reference

Base class for devices, files, or sockets which can work in non-blocking mode. More...

#include <tsNonBlockingDevice.h>

Inheritance diagram for ts::NonBlockingDevice:
Collaboration diagram for ts::NonBlockingDevice:

Classes

class  IOSB
 This structure indicates the status of a non-blocking I/O. More...
 

Public Member Functions

 NonBlockingDevice (Report *report, bool non_blocking=false)
 Constructor.
 
 NonBlockingDevice (ReporterBase *delegate, bool non_blocking=false)
 Constructor.
 
virtual ~NonBlockingDevice () override
 Destructor.
 
SysHandleType getHandle () const
 Get the underlying file descriptor or device handle.
 
virtual SysHandleType getReadHandle () const
 Get the underlying file descriptor or device handle for read operations.
 
SysSocketType getReadSocket () const
 Get the underlying file descriptor or device handle as a system socket handle for read operations.
 
SysSocketType getSocket () const
 Get the underlying file descriptor or device handle as a system socket handle.
 
virtual SysHandleType getWriteHandle () const
 Get the underlying file descriptor or device handle for write operations.
 
SysSocketType getWriteSocket () const
 Get the underlying file descriptor or device handle as a system socket handle for write operations.
 
bool isNonBlocking () const
 Check if the device is in non-blocking mode.
 
bool isSupportedByReactor (bool recheck=false)
 Check if the device is supported by a reactor for non-blocking or asynchronous I/O.
 
bool muteReport (bool mute)
 Temporarily mute the associated report.
 
virtual Reportreport () const override
 Access the Report which is associated with this object.
 
bool setNonBlocking (bool non_blocking)
 Set the device in non-blocking mode.
 
ReportsetReport (Report *report)
 Associate this object with another Report to log errors.
 
ReporterBasesetReport (ReporterBase *delegate)
 Associate this object with another ReporterBase to log errors.
 

Static Public Member Functions

static bool IsPendingStatus (int error_code)
 This static method checks if a system error code means "I/O in progress" (asynchronous I/O) or "I/O would block" (non-blocking I/O).
 
static int SilentLevel (bool silent, int default_severity=Severity::Error)
 Compute a log severity level from a "silent" parameter.
 

Protected Member Functions

virtual bool allowSetNonBlocking () const
 Check that the non-blocking mode can be set.
 
bool checkNonBlocking (bool non_blocking, const UChar *opname)
 Check the blocking mode of a device.
 
bool checkNonBlocking (IOSB *iosb, const UChar *opname)
 Check the blocking mode of a device.
 
int genericSystemRead (void *addr, size_t max_size, size_t &ret_size, const AbortInterface *abort, NonBlockingDevice::IOSB *iosb, uint64_t position)
 Generic system read operation.
 
int genericSystemWrite (const void *addr, size_t size, size_t &written_size, NonBlockingDevice::IOSB *iosb, uint64_t position)
 Generic system write operation.
 
bool setSystemNonBlocking (bool non_blocking)
 Low-level method to set the system file or socket descriptor in non-blocking mode.
 

Detailed Description

Base class for devices, files, or sockets which can work in non-blocking mode.

The methods from this class should not be used by applications. They should be used only by "reactive classes", which work in combination with an event dispatcher.

The exact meaning of "non-blocking" depends on the type of device and the operating system. This is why this class shall be used by specialized classes which exactly know what they are doing.

There are two distinct I/O models:

  • Non-blocking I/O (UNIX).
  • Asynchronous I/O (Windows).

Differences in semantics:

  • On UNIX systems (Linux, macOS, BSD), non-blocking means that a read or write operation fails if it cannot be immediately served. The corresponding error is EAGAIN.
  • On Windows systems, non-blocking means using "overlapped" I/O. For anyone with a basic system culture, this means "asynchronous" I/O. An asynchronous I/O operation can either immediately succeed or fail with error ERROR_IO_PENDING, meaning that the operation executes in the background.

Differences in usage:

  • Execution logic: On UNIX systems, an event dispatcher notifies the applications when an I/O may be "possible". When notified, the application shall repeatedly read or write all possible data, until an EAGAIN error indicates that no more I/O is immediately possible. On Windows systems, the application starts an I/O and control immediately returns. The I/O data exchange continues in the background. The event dispatcher notifies the application when the I/O completes.
  • Data buffer usage: On UNIX, an I/O is either immediate or failed. On Windows, the I/O is in progress as long as it is not completed. This means that, on Windows, I/O buffers which are used for read or write must be available during the asynchronous phase of the I/O, because the data can come in or go out at any time. On UNIX systems, on the contrary, no buffer is used while waiting for some I/O to become possible.

Important differences in canceling I/O and closing file descriptors or handles:

  • On UNIX, a non-blocking I/O is either immediate or failed. No I/O is ever "in progress". Closing a file descriptor is possible at any time without restriction.
  • On Windows, a pending I/O can be canceled either explicitly or as the result of closing the device handle. In that case, the I/O completion is notified later with an I/O error status. This means that the I/O data buffer and IOSB (see below) shall remain valid as long as this corresponding I/O completion has not been received. It is of the utmost importance that the application keeps track of all pending asynchronous I/O and always waits for the reception of all corresponding I/O completions before releasing the memory for the data buffers, including when closing the device handle.

Constructor & Destructor Documentation

◆ NonBlockingDevice() [1/2]

ts::NonBlockingDevice::NonBlockingDevice ( Report report,
bool  non_blocking = false 
)
inlineexplicit

Constructor.

Parameters
[in]reportWhere to report errors. The report object must remain valid as long as this object exists or setReport() is used with another Report object. If report is null, log messages are discarded.
[in]non_blockingIt true, the device is initially set in non-blocking mode.

◆ NonBlockingDevice() [2/2]

ts::NonBlockingDevice::NonBlockingDevice ( ReporterBase delegate,
bool  non_blocking = false 
)
inlineexplicit

Constructor.

Parameters
[in]delegateUse the report of another ReporterBase. If delegate is null, log messages are discarded.
[in]non_blockingIt true, the device is initially set in non-blocking mode.

Member Function Documentation

◆ setNonBlocking()

bool ts::NonBlockingDevice::setNonBlocking ( bool  non_blocking)

Set the device in non-blocking mode.

Important: Usually, this method must be called before opening the device, whatever it means. Otherwise it is ignored and the device blocking mode is unchanged.

Parameters
[in]non_blockingIt true, the device is set in non-blocking mode.
Returns
True on success, false if the device is already open and the non-blocking mode is unchanged.

◆ isNonBlocking()

bool ts::NonBlockingDevice::isNonBlocking ( ) const
inline

Check if the device is in non-blocking mode.

Returns
True if the device is in non-blocking mode, false otherwise.
See also
setNonBlocking()

◆ isSupportedByReactor()

bool ts::NonBlockingDevice::isSupportedByReactor ( bool  recheck = false)

Check if the device is supported by a reactor for non-blocking or asynchronous I/O.

Parameters
[in]recheckIf true, force a recheck. If false and the device was previously checked, return the previous cached value.
Returns
True if the device is supported by a reactor for non-blocking or asynchronous I/O, false otherwise.

◆ IsPendingStatus()

static bool ts::NonBlockingDevice::IsPendingStatus ( int  error_code)
inlinestatic

This static method checks if a system error code means "I/O in progress" (asynchronous I/O) or "I/O would block" (non-blocking I/O).

Parameters
[in]error_codeSystem error code.
Returns
True if error_code is an in-progress/would-block one.

◆ getHandle()

SysHandleType ts::NonBlockingDevice::getHandle ( ) const

Get the underlying file descriptor or device handle.

This method is reserved for low-level operations and should not be used by normal applications.

On UNIX systems, sockets are standard file descriptors. On Windows systems, sockets and devices handles are two distinct types (SOCKET, an integer type, and HANDLE, a pointer type). However, SOCKET and HANDLE have the same size and can be converted between each other. In practice, all Windows device handles are pointers. When Microsoft decided to implement the BSD socket API, they needed to represent sockets as integers. The integer is simply a cast of the HANDLE pointer.

In practice, the methods getHandle() and getSocket() return the same value, represented as two different portable types, SysHandleType and SysSocketType. Note that this types are defined as their real representation. On UNIX systems, both are defined as int and are compatible. However, on Windows systems, they are defined as HANDLE and SOCKET and are not compatible. So, accidentally mixing the two compiles on UNIX but not on Windows. Be careful to use the right type for the right usage.

There is one important difference between getHandle() and getSocket(): the error values, SYS_HANDLE_INVALID and SYS_SOCKET_INVALID. Be careful, these constants have distinct binary values on Windows. This is the only case were it is not possible to cast between a SysHandleType value and a SysSocketType value. This is why it is recommended to always use getHandle() when a SysHandleType is required and getSocket() when a SysSocketType is required.

A subclass which uses file descriptors or device handles for read, write, or both, must override the methods getReadHandle(), getWriteHandle(), or both. If the two types of operations use the same file descriptor, the two methods must return the same value.

An application should call getReadHandle() for read operations and getWriteHandle() for write operations. The method getHandle() returns getReadHandle() if its returns a valid value, and getWriteHandle() otherwise.

Returns
The underlying file descriptor or device handler. Return SYS_HANDLE_INVALID if the socket is not open.

◆ getReadHandle()

virtual SysHandleType ts::NonBlockingDevice::getReadHandle ( ) const
virtual

Get the underlying file descriptor or device handle for read operations.

Subclasses should override this method.

Returns
The underlying file descriptor for read operations or SYS_HANDLE_INVALID if the device is not open for read.
See also
getHandle()

Reimplemented in ts::Socket, ts::BinaryFile, and ts::ForkPipe.

◆ getWriteHandle()

virtual SysHandleType ts::NonBlockingDevice::getWriteHandle ( ) const
virtual

Get the underlying file descriptor or device handle for write operations.

Subclasses should override this method.

Returns
The underlying file descriptor for write operations or SYS_HANDLE_INVALID if the device is not open for write.
See also
getHandle()

Reimplemented in ts::Socket, ts::BinaryFile, and ts::ForkPipe.

◆ getSocket()

SysSocketType ts::NonBlockingDevice::getSocket ( ) const

Get the underlying file descriptor or device handle as a system socket handle.

Returns
The underlying socket descriptor. Return SYS_SOCKET_INVALID if the socket is not open.
See also
getHandle()

◆ getReadSocket()

SysSocketType ts::NonBlockingDevice::getReadSocket ( ) const

Get the underlying file descriptor or device handle as a system socket handle for read operations.

Returns
The underlying file descriptor for read operations or SYS_SOCKET_INVALID if the socket is not open for read.
See also
getHandle()

◆ getWriteSocket()

SysSocketType ts::NonBlockingDevice::getWriteSocket ( ) const

Get the underlying file descriptor or device handle as a system socket handle for write operations.

Returns
The underlying file descriptor for read operations or SYS_SOCKET_INVALID if the socket is not open for write.
See also
getHandle()

◆ checkNonBlocking() [1/2]

bool ts::NonBlockingDevice::checkNonBlocking ( bool  non_blocking,
const UChar opname 
)
protected

Check the blocking mode of a device.

Called by subclass methods which are explicitly called in blocking or non-blocking mode.

Parameters
[in]non_blockingThe required non-blocking mode.
[in]opnameName of the operation, for the error message.
Returns
True on success, false on error.

◆ checkNonBlocking() [2/2]

bool ts::NonBlockingDevice::checkNonBlocking ( IOSB iosb,
const UChar opname 
)
protected

Check the blocking mode of a device.

Called by subclass methods which are explicitly called in blocking or non-blocking mode.

Parameters
[in,out]iosbAddress of an IOSB structure. If non-null, we are in non-blocking mode. When null, we are in blocking mode. When non-null, pending is reset to false and overlap is zeroed.
[in]opnameName of the operation, for the error message.
Returns
True on success, false on error.

◆ allowSetNonBlocking()

virtual bool ts::NonBlockingDevice::allowSetNonBlocking ( ) const
protectedvirtual

Check that the non-blocking mode can be set.

Must be implemented by subclasses which do not support setting the non-blocking in certain states, such as after being opened. The default implementation always allows setting the non-blocking mode.

Returns
True if setting the non-blocking mode is allowed, false otherwise.

Reimplemented in ts::Socket, ts::BinaryFile, and ts::ForkPipe.

◆ setSystemNonBlocking()

bool ts::NonBlockingDevice::setSystemNonBlocking ( bool  non_blocking)
protected

Low-level method to set the system file or socket descriptor in non-blocking mode.

Parameters
[in]non_blockingIt true, the device is set in non-blocking mode.
Returns
True on success, false on error.

Summary: Do not use this method unless you exactly know what you are doing.

UNIX: Depending on the way a file descriptor is created, it may be possible to specify the non-blocking mode from the beginning. Or it can be somehow inherited. However, this is not portable.

Examples:

  • On Linux and FreeBSD, a socket can be directly created in non-blocking mode using the flag SOCK_NONBLOCK in the 'type' parameter of the socket() system call. However, it does not work on macOS.
  • On macOS (and maybe FreeBSD), when a server socket is in non-blocking mode, all client session sockets which are created by accept() are also in non-blocking mode. However, on Linux, they are in blocking mode.

In all cases, it is possible to set a file descriptor in non-blocking mode at any time using the method setSystemNonBlocking(). This method uses fcntl(F_SETFL) to alter the file descriptor's flags.

Windows: The natural way of not being blocked on I/O on Windows is asynchronous I/O. To increase the general confusion, there is some form of non-blocking mode on Windows sockets, and only sockets, not other forms of file handles. This mode is activated using "ioctlsocket(fd, FIONBIO, &mode)". When this mode is active, socket I/O become similar to UNIX: they immediately either succeed or fail, but never block. However, there is no way to get notified when the I/O becomes possible. There is no equivalent to epoll (Linux) or kqueue (macOS and BSD). The Windows I/O Completion Ports can only work on asynchronous I/O, using OVERLAPPED structures. Because this form of non-blocking mode is mostly useless in practice, we do not use it and the method setSystemNonBlocking() does nothing on Windows.

See also
https://learn.microsoft.com/en-us/archive/blogs/csliu/io-concept-blockingnon-blocking-vs-syncasync

◆ genericSystemWrite()

int ts::NonBlockingDevice::genericSystemWrite ( const void *  addr,
size_t  size,
size_t &  written_size,
NonBlockingDevice::IOSB iosb,
uint64_t  position 
)
protected

Generic system write operation.

This is a convenience method which can be used (or not) by subclasses when the system calls write() (UNIX) or WriteFile() (Windows) are appropriate.

Parameters
[in]addrAddress of the data to write.
[in]sizeSize in bytes of the data to write.
[out]written_sizeActually written size in bytes. Can be less than size in case of error in the middle of the write.
[in,out]iosbAddress of an IOSB structure. If non-null, the stream must be in non-blocking mode. When null, the stream must be in blocking mode (the default). See the description of ts::NonBlockingDevice::IOSB.
[in]positionThis value is only used on Windows with asynchronous I/O on disk file. On Windows, when asynchronous I/O are used on random access files, the file position is not maintained. Each read or write operation is performed at the specified absolute position.
Returns
Error code. When the I/O is non-blocking/asynchronous and pending, return SYS_SUCCESS and iosb->pending is true. Return SYS_EOF when it is no longer possible to write (e.g. broken pipe).

◆ genericSystemRead()

int ts::NonBlockingDevice::genericSystemRead ( void *  addr,
size_t  max_size,
size_t &  ret_size,
const AbortInterface abort,
NonBlockingDevice::IOSB iosb,
uint64_t  position 
)
protected

Generic system read operation.

This is a convenience method which can be used (or not) by subclasses when the system calls read() (UNIX) or ReadFile() (Windows) are appropriate.

Parameters
[out]addrAddress of the buffer for the incoming data.
[in]max_sizeMaximum size in bytes of the buffer.
[out]ret_sizeReturned input size in bytes. If zero, end of file has been reached or an error occurred.
[in]abortIf non-zero, invoked when I/O is interrupted (in case of user-interrupt, return, otherwise retry).
[in,out]iosbAddress of an IOSB structure. If non-null, the stream must be in non-blocking mode. When null, the stream must be in blocking mode (the default). See the description of ts::NonBlockingDevice::IOSB.
[in]positionThis value is only used on Windows with asynchronous I/O on disk file. On Windows, when asynchronous I/O are used on random access files, the file position is not maintained. Each read or write operation is performed at the specified absolute position.
Returns
Error code. When the I/O is non-blocking/asynchronous and pending, return SYS_SUCCESS and iosb->pending is true. Return SYS_EOF when it is no longer possible to read.

◆ report()

virtual Report & ts::ReporterBase::report ( ) const
overridevirtualinherited

Access the Report which is associated with this object.

Can be called from another thread only if the Report object is thread-safe.

Returns
A reference to the associated report.

Implements ts::ReporterInterface.

◆ setReport() [1/2]

Report * ts::ReporterBase::setReport ( Report report)
inherited

Associate this object with another Report to log errors.

Parameters
[in]reportWhere to report errors. The report object must remain valid as long as this object exists or setReport() is used with another Report object. If report is null, log messages are discarded.
Returns
The address of the previous Report object or a null pointer if there was none.

◆ setReport() [2/2]

ReporterBase * ts::ReporterBase::setReport ( ReporterBase delegate)
inherited

Associate this object with another ReporterBase to log errors.

Parameters
[in]delegateUse the report of another ReporterBase. If delegate is null, the previous explicit Report is used..
Returns
The address of the previous ReporterBase object or a null pointer if there was none.

◆ muteReport()

bool ts::ReporterBase::muteReport ( bool  mute)
inherited

Temporarily mute the associated report.

Parameters
[in]muteIt true, report() will return a null report (log messages are discarded), until muteReport() is invoked again with mute set to false.
Returns
Previous state of the mute field.

◆ SilentLevel()

static int ts::ReporterBase::SilentLevel ( bool  silent,
int  default_severity = Severity::Error 
)
inlinestaticinherited

Compute a log severity level from a "silent" parameter.

Some subclass methods have a "silent" parameter to avoid reporting errors which may be insignificant, typically when closing a device after an error, in which case the close operation may produce other errors if the previous error left the device in an inconsistent state. While those errors should not be displayed as errors, we still display them at debug level.

Parameters
[in]silentIf true, do not report errors, report debug messages instead.
[in]default_severityDefault severity, in non-silent mode (error by default).
Returns
Error when silent is false, Debug otherwise.

The documentation for this class was generated from the following file: