In a POSIX environment, like Linux, file descriptors (fds) are used for most process I/O. Many components need to be notified when one or more fds are ready to read from or write to, or if there's an error or hang-up.
Although it's common to block a thread on a call to read()
, write()
, accept()
, select()
, poll()
(or some variantion of these), if that's done in a thread shared with other components, the other components won't run when needed. To avoid this, Legato has methods to monitor fds reporting related events so they won't interfere with other software sharing the same thread.
le_fdMonitor_Create() creates a File Descriptor Monitor and starts monitoring an fd. A handler function and set of events is also provided to le_fdMonitor_Create().
When an fd no longer needs to be monitored, the File Descriptor Monitor object is deleted by calling le_fdMonitor_Delete().
Events that can be handled:
POLLIN
= Data available to read.POLLPRI
= Urgent data available to read (e.g., out-of-band data on a socket).POLLOUT
= Writing to the fd should accept some data now.POLLRDHUP
= Other end of stream socket closed or shutdown.POLLERR
= Error occurred.POLLHUP
= Hang up.These are bitmask values and can be combined using the bit-wise OR operator ('|') and tested for using the bit-wise and ('&') operator.
POLLRDHUP
, POLLERR
and POLLHUP
can't be disabled. Monitoring these events is always enabled as soon as the File Descriptor Monitor is created regardless of the set of events given to le_fdMonitor_Create().The fd type affects how events are monitored:
Pipe fd events indicate two conditions for reading from a pipe and two conditions for writing to a pipe.
Event | Condition | |
---|---|---|
READING from a pipe | POLLHUP | NO DATA in the pipe and the WRITE END is closed |
POLLIN | DATA in the pipe and the WRITE END is open | |
POLLIN + POLLHUP | DATA in the pipe BUT the WRITE END is closed | |
WRITING to the pipe | POLLERR | NO SPACE in the pipe and the READ END is closed |
POLLOUT | SPACE in the pipe and the READ END is open | |
POLLOUT + POLLERR | SPACE in the pipe BUT the READ END is closed |
Socket activity (establishing/closing) is monitored for connection-orientated sockets including SOCK_STREAM and SOCK_SEQPACKET. Input and output data availability for all socket types is monitored.
Event | Condition |
---|---|
POLLIN | Input is available from the socket |
POLLOUT | Possible to send data on the socket |
POLLIN | Incoming connection being established on the listen port |
POLLPRI | Out of band data received only on TCP |
POLLIN + POLLOUT + POLLRDHUP | Peer closed the connection in a connection-orientated socket |
Terminals and pseudo-terminals operate in pairs. When one terminal pair closes, an event is generated to indicate the closure. POLLIN, POLLOUT and POLLPRI are the event indicators related to terminal status.
Event | Condition |
---|---|
POLLIN | Ready to receive data |
POLLOUT | Ready to send data |
POLLPRI | Master/pseudo terminal detects slave state has changed (in packet mode only). |
POLLHUP | Either half of the terminal pair has closed. |
Parameters to the fd event handler functions are the fd and the events active for the fd. The events are passed as a bit mask; the bit-wise AND operator ('&') must be used to check for specific events.
The set of fd events being monitored can be adjusted using le_fdMonitor_Enable() and le_fdMonitor_Disable(). However, POLLRDHUP
, POLLERR
and POLLHUP
can't be disabled.
CPU cycles (and power) can be saved by disabling monitoring when not needed. For example, POLLOUT
monitoring should be disabled while nothing needs to be written to the fd, so that the event handler doesn't keep getting called with a POLLOUT
event because the fd is writeable.
If an event occurs on an fd while monitoring of that event is disabled, the event will be ignored. If that event is later enabled, and that event's trigger condition is still true (e.g., the fd still has data available to be read), then the event will be reported to the handler at that time. If the event trigger condition is gone (e.g., the fd no longer has data available to read), then the event will not be reported until its trigger condition becomes true again.
If events occur on different fds at the same time, the order in which the handlers are called is implementation-dependent.
Calling le_fdMonitor_GetMonitor() inside the handler function fetches a reference to the File Descriptor Monitor object for the event being handled. This is handy to enable and disable event monitoring from inside the handler.
If additional data needs to be passed to the handler function, the context pointer can be set to use le_fdMonitor_SetContextPtr() and retrieved inside the handler function with le_fdMonitor_GetContextPtr(). le_event_GetContextPtr() can also be used, but le_fdMonitor_GetContextPtr() is preferred as it double checks it's being called inside a File Descriptor Monitor's handler function.
If your process has the privilege of being able to block the system from going to sleep, whenever the fd that is being monitored has a pending event, the system will be kept awake. To allow the system to go to sleep while this fd has a pending event, you can call le_fdMonitor_SetDeferrable() with isDeferrable
flag set to 'true'.
fd monitoring is performed by the Event Loop of the thread that created the Monitor object for that fd. If that the is blocked, events won't be detected for that fd until the thread is unblocked and returns to its Event Loop. Similarly, if the thread that creates a File Descriptor Monitor object doesn't run an Event Loop at all, no events will be detected for that fd.
It's not recommended to monitor the same fd in two threads at the same time, because the threads will race to handle any events on that fd.
The "fdMonitor" logging keyword can be enabled to view fd monitoring activity.
Copyright (C) Sierra Wireless Inc. Use of this work is subject to license.