This commit is contained in:
2024-08-05 20:57:09 +08:00
commit 46d9ee7795
3020 changed files with 1725767 additions and 0 deletions

View File

@@ -0,0 +1,17 @@
# RT-Thread building script for component
from building import *
cwd = GetCurrentDir()
src = []
CPPPATH = [cwd]
if GetDepend('RT_USING_POSIX_POLL'):
src += ['poll.c']
if GetDepend('RT_USING_POSIX_SELECT'):
src += ['select.c']
group = DefineGroup('POSIX', src, depend = [''], CPPPATH = CPPPATH)
Return('group')

View File

@@ -0,0 +1,359 @@
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2016-12-28 Bernard first version
* 2018-03-09 Bernard Add protection for pt->triggered.
* 2023-12-04 Shell Fix return code and error verification
* 2023-12-14 Shell When poll goes to sleep before the waitqueue has added a
* record and finished enumerating all the fd's, it may be
* incorrectly woken up. This is basically because the poll
* mechanism wakeup algorithm does not correctly distinguish
* the current wait state.
* 2024-03-29 TroyMitchelle Add all function comments and comments to structure members
*/
#include <stdint.h>
#include <rthw.h>
#include <rtthread.h>
#include <dfs_file.h>
#include "poll.h"
enum rt_poll_status
{
RT_POLL_STAT_INIT, /**< Poll operation initialization status. */
RT_POLL_STAT_TRIG, /**< Poll operation triggered status. */
RT_POLL_STAT_WAITING /**< Poll operation waiting status. */
};
struct rt_poll_table
{
rt_pollreq_t req; /**< Poll request. */
enum rt_poll_status status; /**< Status of the poll operation. */
rt_thread_t polling_thread; /**< Polling thread associated with the table. */
struct rt_poll_node *nodes; /**< Linked list of poll nodes. */
};
struct rt_poll_node
{
struct rt_wqueue_node wqn; /**< Wait queue node for the poll node. */
struct rt_poll_table *pt; /**< Pointer to the parent poll table. */
struct rt_poll_node *next; /**< Pointer to the next poll node. */
};
static RT_DEFINE_SPINLOCK(_spinlock);
/**
* @brief Wake-up function for the wait queue.
*
* This function is invoked when a node in the wait queue needs to be woken up.
*
* @param wait Pointer to the wait queue node.
* @param key Key associated with the wake-up operation.
* @return Upon successful wake-up, returns 0; otherwise, -1 is returned.
*/
static int __wqueue_pollwake(struct rt_wqueue_node *wait, void *key)
{
rt_ubase_t level;
struct rt_poll_node *pn;
int is_waiting;
if (key && !((rt_ubase_t)key & wait->key))
return -1;
pn = rt_container_of(wait, struct rt_poll_node, wqn);
level = rt_spin_lock_irqsave(&_spinlock);
is_waiting = (pn->pt->status == RT_POLL_STAT_WAITING);
pn->pt->status = RT_POLL_STAT_TRIG;
rt_spin_unlock_irqrestore(&_spinlock, level);
if (is_waiting)
return __wqueue_default_wake(wait, key);
return -1;
}
/**
* @brief Adds a poll request to the wait queue.
*
* This function adds a poll request to the wait queue associated with the specified
* wait queue and poll request.
*
* @param wq Pointer to the wait queue.
* @param req Pointer to the poll request.
*/
static void _poll_add(rt_wqueue_t *wq, rt_pollreq_t *req)
{
struct rt_poll_table *pt;
struct rt_poll_node *node;
node = (struct rt_poll_node *)rt_malloc(sizeof(struct rt_poll_node));
if (node == RT_NULL)
return;
pt = rt_container_of(req, struct rt_poll_table, req);
node->wqn.key = req->_key;
rt_list_init(&(node->wqn.list));
node->wqn.polling_thread = pt->polling_thread;
node->wqn.wakeup = __wqueue_pollwake;
node->next = pt->nodes;
node->pt = pt;
pt->nodes = node;
rt_wqueue_add(wq, &node->wqn);
}
/**
* @brief Initializes a poll table.
*
* This function initializes a poll table with the provided poll request, status,
* and polling thread.
*
* @param pt Pointer to the poll table to be initialized.
*/
static void poll_table_init(struct rt_poll_table *pt)
{
pt->req._proc = _poll_add;
pt->status = RT_POLL_STAT_INIT;
pt->nodes = RT_NULL;
pt->polling_thread = rt_thread_self();
}
/**
* @brief Waits for events on the poll table with a specified timeout.
*
* This function waits for events on the poll table with the specified timeout
* in milliseconds.
*
* @param pt Pointer to the poll table.
* @param msec Timeout value in milliseconds.
* @return Upon successful completion, returns 0. If the timeout expires, -RT_ETIMEOUT
* is returned. If the operation is interrupted by a signal, -RT_EINTR is
* returned.
*/
static int poll_wait_timeout(struct rt_poll_table *pt, int msec)
{
rt_int32_t timeout;
int ret = 0;
struct rt_thread *thread;
rt_base_t level;
thread = pt->polling_thread;
timeout = rt_tick_from_millisecond(msec);
level = rt_spin_lock_irqsave(&_spinlock);
if (timeout != 0 && pt->status != RT_POLL_STAT_TRIG)
{
if (rt_thread_suspend_with_flag(thread, RT_INTERRUPTIBLE) == RT_EOK)
{
if (timeout > 0)
{
rt_timer_control(&(thread->thread_timer),
RT_TIMER_CTRL_SET_TIME,
&timeout);
rt_timer_start(&(thread->thread_timer));
rt_set_errno(RT_ETIMEOUT);
}
else
{
rt_set_errno(0);
}
pt->status = RT_POLL_STAT_WAITING;
rt_spin_unlock_irqrestore(&_spinlock, level);
rt_schedule();
level = rt_spin_lock_irqsave(&_spinlock);
if (pt->status == RT_POLL_STAT_WAITING)
pt->status = RT_POLL_STAT_INIT;
}
}
ret = rt_get_errno();
if (ret == RT_EINTR)
ret = -RT_EINTR;
else if (pt->status == RT_POLL_STAT_TRIG)
ret = RT_EOK;
else
ret = -RT_ETIMEOUT;
rt_spin_unlock_irqrestore(&_spinlock, level);
return ret;
}
/**
* @brief Performs poll operation for a single file descriptor.
*
* This function performs a poll operation for a single file descriptor and updates
* the revents field of the pollfd structure accordingly.
*
* @param pollfd Pointer to the pollfd structure.
* @param req Pointer to the poll request.
* @return Upon successful completion, returns the bitmask of events that occurred.
* If an error occurs, -1 is returned.
*/
static int do_pollfd(struct pollfd *pollfd, rt_pollreq_t *req)
{
int mask = 0;
int fd;
fd = pollfd->fd;
if (fd >= 0)
{
struct dfs_file *f = fd_get(fd);
mask = POLLNVAL;
if (f)
{
mask = POLLMASK_DEFAULT;
if (f->vnode->fops->poll)
{
req->_key = pollfd->events | POLLERR | POLLHUP;
mask = f->vnode->fops->poll(f, req);
/* dealwith the device return error -1*/
if (mask < 0)
{
pollfd->revents = 0;
return mask;
}
}
/* Mask out unneeded events. */
mask &= pollfd->events | POLLERR | POLLHUP;
}
}
pollfd->revents = mask;
return mask;
}
/**
* @brief Performs the poll operation on an array of file descriptors.
*
* This function performs the poll operation on an array of file descriptors and
* waits for events with the specified timeout.
*
* @param fds Pointer to the array of pollfd structures.
* @param nfds Number of file descriptors in the array.
* @param pt Pointer to the poll table.
* @param msec Timeout value in milliseconds.
* @return Upon successful completion, returns the number of file descriptors
* for which events were received. If the timeout expires, -RT_ETIMEOUT
* is returned. If the operation is interrupted by a signal, -RT_EINTR is
* returned.
*/
static int poll_do(struct pollfd *fds, nfds_t nfds, struct rt_poll_table *pt, int msec)
{
int num;
int istimeout = 0;
nfds_t n;
struct pollfd *pf;
int ret = 0;
if (msec == 0)
{
pt->req._proc = RT_NULL;
istimeout = 1;
}
while (1)
{
pf = fds;
num = 0;
pt->status = RT_POLL_STAT_INIT;
for (n = 0; n < nfds; n ++)
{
ret = do_pollfd(pf, &pt->req);
if(ret < 0)
{
/*dealwith the device return error -1 */
pt->req._proc = RT_NULL;
return ret;
}
else if(ret > 0)
{
num ++;
pt->req._proc = RT_NULL;
}
pf ++;
}
pt->req._proc = RT_NULL;
if (num || istimeout)
break;
ret = poll_wait_timeout(pt, msec);
if (ret == -RT_EINTR)
return -EINTR;
else if (ret == -RT_ETIMEOUT)
istimeout = 1;
else
istimeout = 0;
}
return num;
}
/**
* @brief Tears down the poll table.
*
* This function tears down the poll table by removing all poll nodes associated
* with it.
*
* @param pt Pointer to the poll table.
*/
static void poll_teardown(struct rt_poll_table *pt)
{
struct rt_poll_node *node, *next;
next = pt->nodes;
while (next)
{
node = next;
rt_wqueue_remove(&node->wqn);
next = node->next;
rt_free(node);
}
}
/**
* @brief Performs the poll operation on a set of file descriptors.
*
* This function performs the poll operation on a set of file descriptors and
* waits for events with the specified timeout.
*
* @param fds Pointer to the array of pollfd structures.
* @param nfds Number of file descriptors in the array.
* @param timeout Timeout value in milliseconds.
* @return Upon successful completion, returns the number of file descriptors
* for which events were received. If the timeout expires, 0 is returned.
* If an error occurs, -1 is returned.
*/
int poll(struct pollfd *fds, nfds_t nfds, int timeout)
{
int num;
struct rt_poll_table table;
poll_table_init(&table);
num = poll_do(fds, nfds, &table, timeout);
poll_teardown(&table);
return num;
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2021-09-11 Meco Man First version
* 2024-03-29 TroyMitchelle Add all macro comments and comments to structure members
*/
#ifndef __POLL_H__
#define __POLL_H__
#ifdef __cplusplus
extern "C" {
#endif
#ifdef RT_USING_MUSLLIBC
#if !defined(POLLIN) && !defined(POLLOUT)
#define POLLIN 0x001 /**< There is data to read. */
#define POLLPRI 0x002 /**< There is urgent data to read. */
#define POLLOUT 0x004 /**< Writing is now possible. */
#define POLLERR 0x008 /**< Error condition. */
#define POLLHUP 0x010 /**< Hang up. */
#define POLLNVAL 0x020 /**< Invalid polling request. */
#define POLLRDNORM 0x040 /**< Normal data may be read. */
#define POLLRDBAND 0x080 /**< Priority data may be read. */
#define POLLWRNORM 0x100 /**< Writing normal data is possible. */
#define POLLWRBAND 0x200 /**< Writing priority data is possible. */
typedef unsigned int nfds_t;
struct pollfd
{
int fd; /**< File descriptor. */
short events; /**< Requested events. */
short revents; /**< Returned events. */
};
#endif
#else
#if !defined(POLLIN) && !defined(POLLOUT)
#define POLLIN 0x1 /**< There is data to read. */
#define POLLOUT 0x2 /**< Writing is now possible. */
#define POLLERR 0x4 /**< Error condition. */
#define POLLNVAL 0x8 /**< Invalid polling request. */
/* Below values are unimplemented */
#define POLLRDNORM 0x10 /**< Normal data may be read. */
#define POLLRDBAND 0x20 /**< Priority data may be read. */
#define POLLPRI 0x40 /**< There is urgent data to read. */
#define POLLWRNORM 0x80 /**< Writing normal data is possible. */
#define POLLWRBAND 0x100 /**< Writing priority data is possible. */
#define POLLHUP 0x200 /**< Hang up. */
typedef unsigned int nfds_t;
struct pollfd
{
int fd; /**< File descriptor. */
short events; /**< Requested events. */
short revents; /**< Returned events. */
};
#endif
#endif /* !defined(POLLIN) && !defined(POLLOUT) */
#define POLLMASK_DEFAULT (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
#ifdef __cplusplus
}
#endif
#endif /* __POLL_H__ */

View File

@@ -0,0 +1,193 @@
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2016-12-28 Bernard first version
* 2024-04-08 TroyMitchell Add all function comments
*/
#include <rtthread.h>
#include <poll.h>
#include <sys/select.h>
/**
* @brief Initialize the file descriptor set to have zero bits for all file descriptors.
* @param set Pointer to the file descriptor set to be initialized.
* @param nfds The maximum file descriptor in the set plus one.
* @note The actual size of the 'fd_set' is determined based on the parameter 'nfds'.
*/
static void fdszero(fd_set *set, int nfds)
{
fd_mask *m;
int n;
/*
The 'sizeof(fd_set)' of the system space may differ from user space,
so the actual size of the 'fd_set' is determined here with the parameter 'nfds'
*/
m = (fd_mask *)set;
for (n = 0; n < nfds; n += (sizeof(fd_mask) * 8))
{
rt_memset(m, 0, sizeof(fd_mask));
m ++;
}
}
/**
* @brief Synchronous I/O multiplexing: multiplex input/output over a set of file descriptors.
* @param nfds The highest-numbered file descriptor in any of the three sets, plus 1.
* @param readfds A pointer to a set of file descriptors to be checked for read readiness.
* @param writefds A pointer to a set of file descriptors to be checked for write readiness.
* @param exceptfds A pointer to a set of file descriptors to be checked for exceptions.
* @param timeout The maximum time to wait for any of the specified file descriptors to become ready.
* @return Upon successful completion, the total number of file descriptors in all the sets that are ready for the requested operation is returned; otherwise, -1 is returned on error.
*/
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
{
int fd;
int npfds;
int msec;
int ndx;
int ret;
struct pollfd *pollset = RT_NULL;
/* How many pollfd structures do we need to allocate? */
for (fd = 0, npfds = 0; fd < nfds; fd++)
{
/* Check if any monitor operation is requested on this fd */
if ((readfds && FD_ISSET(fd, readfds)) ||
(writefds && FD_ISSET(fd, writefds)) ||
(exceptfds && FD_ISSET(fd, exceptfds)))
{
npfds++;
}
}
/* Allocate the descriptor list for poll() */
if (npfds > 0)
{
pollset = (struct pollfd *)rt_calloc(npfds, sizeof(struct pollfd));
if (!pollset)
{
return -1;
}
}
/* Initialize the descriptor list for poll() */
for (fd = 0, ndx = 0; fd < nfds; fd++)
{
int incr = 0;
/* The readfs set holds the set of FDs that the caller can be assured
* of reading from without blocking. Note that POLLHUP is included as
* a read-able condition. POLLHUP will be reported at the end-of-file
* or when a connection is lost. In either case, the read() can then
* be performed without blocking.
*/
if (readfds && FD_ISSET(fd, readfds))
{
pollset[ndx].fd = fd;
pollset[ndx].events |= POLLIN;
incr = 1;
}
if (writefds && FD_ISSET(fd, writefds))
{
pollset[ndx].fd = fd;
pollset[ndx].events |= POLLOUT;
incr = 1;
}
if (exceptfds && FD_ISSET(fd, exceptfds))
{
pollset[ndx].fd = fd;
incr = 1;
}
ndx += incr;
}
RT_ASSERT(ndx == npfds);
/* Convert the timeout to milliseconds */
if (timeout)
{
msec = (int)timeout->tv_sec * 1000 + (int)timeout->tv_usec / 1000;
}
else
{
msec = -1;
}
/* Then let poll do all of the real work. */
ret = poll(pollset, npfds, msec);
/* Now set up the return values */
if (readfds)
{
fdszero(readfds, nfds);
}
if (writefds)
{
fdszero(writefds, nfds);
}
if (exceptfds)
{
fdszero(exceptfds, nfds);
}
/* Convert the poll descriptor list back into selects 3 bitsets */
if (ret > 0)
{
ret = 0;
for (ndx = 0; ndx < npfds; ndx++)
{
/* Check for read conditions. Note that POLLHUP is included as a
* read condition. POLLHUP will be reported when no more data will
* be available (such as when a connection is lost). In either
* case, the read() can then be performed without blocking.
*/
if (readfds)
{
if (pollset[ndx].revents & (POLLIN | POLLHUP))
{
FD_SET(pollset[ndx].fd, readfds);
ret++;
}
}
/* Check for write conditions */
if (writefds)
{
if (pollset[ndx].revents & POLLOUT)
{
FD_SET(pollset[ndx].fd, writefds);
ret++;
}
}
/* Check for exceptions */
if (exceptfds)
{
if (pollset[ndx].revents & POLLERR)
{
FD_SET(pollset[ndx].fd, exceptfds);
ret++;
}
}
}
}
if (pollset) rt_free(pollset);
return ret;
}