diff options
Diffstat (limited to 'include/ruby/internal/intern/select')
-rw-r--r-- | include/ruby/internal/intern/select/largesize.h | 141 | ||||
-rw-r--r-- | include/ruby/internal/intern/select/posix.h | 70 | ||||
-rw-r--r-- | include/ruby/internal/intern/select/win32.h | 153 |
3 files changed, 336 insertions, 28 deletions
diff --git a/include/ruby/internal/intern/select/largesize.h b/include/ruby/internal/intern/select/largesize.h index ba56a159b1..d65f088c06 100644 --- a/include/ruby/internal/intern/select/largesize.h +++ b/include/ruby/internal/intern/select/largesize.h @@ -17,7 +17,7 @@ * recursively included from extension libraries written in C++. * Do not expect for instance `__VA_ARGS__` is always available. * We assume C99 for ruby itself but we don't assume languages of - * extension libraries. They could be written in C++98. + * extension libraries. They could be written in C++98. * @brief Public APIs to provide ::rb_fd_select(). * * Several Unix platforms support file descriptors bigger than FD_SETSIZE in @@ -35,9 +35,6 @@ * `select(2)` documents how to allocate fd_set dynamically. * http://www.openbsd.org/cgi-bin/man.cgi?query=select&manpath=OpenBSD+4.4 * - * - HP-UX documents how to allocate fd_set dynamically. - * http://docs.hp.com/en/B2355-60105/select.2.html - * * - Solaris 8 has `select_large_fdset` * * - Mac OS X 10.7 (Lion) @@ -66,26 +63,134 @@ struct timeval; +/** + * The data structure which wraps the fd_set bitmap used by select(2). This + * allows Ruby to use FD sets larger than that allowed by historic limitations + * on modern platforms. + */ typedef struct { - int maxfd; - fd_set *fdset; + int maxfd; /**< Maximum allowed number of FDs. */ + fd_set *fdset; /**< File descriptors buffer */ } rb_fdset_t; RBIMPL_SYMBOL_EXPORT_BEGIN() -void rb_fd_init(rb_fdset_t *); -void rb_fd_term(rb_fdset_t *); -void rb_fd_zero(rb_fdset_t *); -void rb_fd_set(int, rb_fdset_t *); -void rb_fd_clr(int, rb_fdset_t *); -int rb_fd_isset(int, const rb_fdset_t *); -void rb_fd_copy(rb_fdset_t *, const fd_set *, int); +RBIMPL_ATTR_NONNULL(()) +/** + * (Re-)initialises a fdset. One must be initialised before other `rb_fd_*` + * operations. Analogous to calling `malloc(3)` to allocate an `fd_set`. + * + * @param[out] f An fdset to squash. + * @post `f` holds no file descriptors. + */ +void rb_fd_init(rb_fdset_t *f); + +RBIMPL_ATTR_NONNULL(()) +/** + * Destroys the ::rb_fdset_t, releasing any memory and resources it used. It + * must be reinitialised using rb_fd_init() before future use. Analogous to + * calling `free(3)` to release memory for an `fd_set`. + * + * @param[out] f An fdset to squash. + * @post `f` holds no file descriptors. + */ +void rb_fd_term(rb_fdset_t *f); + +RBIMPL_ATTR_NONNULL(()) +/** + * Wipes out the current set of FDs. + * + * @param[out] f The fdset to clear. + * @post `f` has no FDs. + */ +void rb_fd_zero(rb_fdset_t *f); + +RBIMPL_ATTR_NONNULL(()) +/** + * Sets an fd to a fdset. + * + * @param[in] fd A file descriptor. + * @param[out] f Target fdset. + * @post `f` holds `fd`. + */ +void rb_fd_set(int fd, rb_fdset_t *f); + +RBIMPL_ATTR_NONNULL(()) +/** + * Releases a specific FD from the given fdset. + * + * @param[in] fd Target FD. + * @param[out] f The fdset that holds `fd`. + * @post `f` doesn't hold n. + */ +void rb_fd_clr(int fd, rb_fdset_t *f); + +RBIMPL_ATTR_NONNULL(()) +RBIMPL_ATTR_PURE() +/** + * Queries if the given FD is in the given set. + * + * @param[in] fd Target FD. + * @param[in] f The fdset to scan. + * @retval 1 Yes there is. + * @retval 0 No there isn't. + * @see http://www.freebsd.org/cgi/query-pr.cgi?pr=91421 + */ +int rb_fd_isset(int fd, const rb_fdset_t *f); + +/** + * Destructively overwrites an fdset with another. + * + * @param[out] dst Target fdset. + * @param[in] src Source fdset. + * @param[in] max Maximum number of file descriptors to copy. + * @post `dst` is a copy of `src`. + */ +void rb_fd_copy(rb_fdset_t *dst, const fd_set *src, int max); + +/** + * Identical to rb_fd_copy(), except it copies unlimited number of file + * descriptors. + * + * @param[out] dst Target fdset. + * @param[in] src Source fdset. + * @post `dst` is a copy of `src`. + */ void rb_fd_dup(rb_fdset_t *dst, const rb_fdset_t *src); -int rb_fd_select(int, rb_fdset_t *, rb_fdset_t *, rb_fdset_t *, struct timeval *); + +/** + * Waits for multiple file descriptors at once. + * + * @param[in] nfds Max FD in everything passed, plus one. + * @param[in,out] rfds Set of FDs to wait for reads. + * @param[in,out] wfds Set of FDs to wait for writes. + * @param[in,out] efds Set of FDs to wait for OOBs. + * @param[in,out] timeout Max blocking duration. + * @retval -1 Failed, errno set. + * @retval 0 Timeout exceeded. + * @retval otherwise Total number of file descriptors returned. + * @post `rfds` contains readable FDs. + * @post `wfds` contains writable FDs. + * @post `efds` contains exceptional FDs. + * @post `timeout` is the time left. + * @note All pointers are allowed to be null pointers. + */ +int rb_fd_select(int nfds, rb_fdset_t *rfds, rb_fdset_t *wfds, rb_fdset_t *efds, struct timeval *timeout); RBIMPL_SYMBOL_EXPORT_END() RBIMPL_ATTR_NONNULL(()) RBIMPL_ATTR_PURE() -/* :TODO: can this function be __attribute__((returns_nonnull)) or not? */ +/** + * Raw pointer to `fd_set`. + * + * @param[in] f Target fdset. + * @retval NULL `f` is already terminated by rb_fd_term(). + * @retval otherwise Underlying fd_set. + * + * @internal + * + * Extension library must not touch raw pointers. It was a bad idea to let + * them use it. + */ static inline fd_set * rb_fd_ptr(const rb_fdset_t *f) { @@ -94,6 +199,12 @@ rb_fd_ptr(const rb_fdset_t *f) RBIMPL_ATTR_NONNULL(()) RBIMPL_ATTR_PURE() +/** + * It seems this function has no use. Maybe just remove? + * + * @param[in] f A set. + * @return Number of file descriptors stored. + */ static inline int rb_fd_max(const rb_fdset_t *f) { diff --git a/include/ruby/internal/intern/select/posix.h b/include/ruby/internal/intern/select/posix.h index 6c1092b39d..0a9b0b2e51 100644 --- a/include/ruby/internal/intern/select/posix.h +++ b/include/ruby/internal/intern/select/posix.h @@ -17,7 +17,7 @@ * recursively included from extension libraries written in C++. * Do not expect for instance `__VA_ARGS__` is always available. * We assume C99 for ruby itself but we don't assume languages of - * extension libraries. They could be written in C++98. + * extension libraries. They could be written in C++98. * @brief Public APIs to provide ::rb_fd_select(). */ #include "ruby/internal/config.h" @@ -30,17 +30,41 @@ # include <unistd.h> /* for select(2) (archaic UNIX) */ #endif -#include "ruby/internal/attr/pure.h" #include "ruby/internal/attr/const.h" +#include "ruby/internal/attr/noalias.h" +#include "ruby/internal/attr/nonnull.h" +#include "ruby/internal/attr/pure.h" +/** + * The data structure which wraps the fd_set bitmap used by `select(2)`. This + * allows Ruby to use FD sets larger than what has been historically allowed on + * modern platforms. + * + * @internal + * + * ... but because this header file is included only when the system is with + * that "historic restrictions", this is nothing more than an alias of fd_set. + */ typedef fd_set rb_fdset_t; +/** Clears the given ::rb_fdset_t. */ #define rb_fd_zero FD_ZERO + +/** Sets the given fd to the ::rb_fdset_t. */ #define rb_fd_set FD_SET + +/** Unsets the given fd from the ::rb_fdset_t. */ #define rb_fd_clr FD_CLR + +/** Queries if the given fd is in the ::rb_fdset_t. */ #define rb_fd_isset FD_ISSET + +/** Initialises the :given :rb_fdset_t. */ #define rb_fd_init FD_ZERO + +/** Waits for multiple file descriptors at once. */ #define rb_fd_select select + /**@cond INTERNAL_MACRO */ #define rb_fd_copy rb_fd_copy #define rb_fd_dup rb_fd_dup @@ -48,20 +72,50 @@ typedef fd_set rb_fdset_t; #define rb_fd_max rb_fd_max /** @endcond */ +RBIMPL_ATTR_NONNULL(()) +RBIMPL_ATTR_NOALIAS() +/** + * Destructively overwrites an fdset with another. + * + * @param[out] dst Target fdset. + * @param[in] src Source fdset. + * @param[in] n Unused parameter. + * @post `dst` is a copy of `src`. + */ static inline void rb_fd_copy(rb_fdset_t *dst, const fd_set *src, int n) { *dst = *src; } +RBIMPL_ATTR_NONNULL(()) +RBIMPL_ATTR_NOALIAS() +/** + * Destructively overwrites an fdset with another. + * + * @param[out] dst Target fdset. + * @param[in] src Source fdset. + * @post `dst` is a copy of `src`. + */ static inline void -rb_fd_dup(rb_fdset_t *dst, const fd_set *src, int n) +rb_fd_dup(rb_fdset_t *dst, const fd_set *src) { *dst = *src; } RBIMPL_ATTR_PURE() /* :TODO: can this function be __attribute__((returns_nonnull)) or not? */ +/** + * Raw pointer to `fd_set`. + * + * @param[in] f Target fdset. + * @return Underlying fd_set. + * + * @internal + * + * Extension library must not touch raw pointers. It was a bad idea to let + * them use it. + */ static inline fd_set * rb_fd_ptr(rb_fdset_t *f) { @@ -69,14 +123,22 @@ rb_fd_ptr(rb_fdset_t *f) } RBIMPL_ATTR_CONST() +/** + * It seems this function has no use. Maybe just remove? + * + * @param[in] f A set. + * @return Number of file descriptors stored. + */ static inline int rb_fd_max(const rb_fdset_t *f) { return FD_SETSIZE; } -/* :FIXME: What are these? They don't exist for shibling implementations. */ +/** @cond INTERNAL_MACRO */ +/* :FIXME: What are these? They don't exist for sibling implementations. */ #define rb_fd_init_copy(d, s) (*(d) = *(s)) #define rb_fd_term(f) ((void)(f)) +/** @endcond */ #endif /* RBIMPL_INTERN_SELECT_POSIX_H */ diff --git a/include/ruby/internal/intern/select/win32.h b/include/ruby/internal/intern/select/win32.h index ef75a0f760..edaf7a8523 100644 --- a/include/ruby/internal/intern/select/win32.h +++ b/include/ruby/internal/intern/select/win32.h @@ -17,7 +17,7 @@ * recursively included from extension libraries written in C++. * Do not expect for instance `__VA_ARGS__` is always available. * We assume C99 for ruby itself but we don't assume languages of - * extension libraries. They could be written in C++98. + * extension libraries. They could be written in C++98. * @brief Public APIs to provide ::rb_fd_select(). */ #include "ruby/internal/dosish.h" /* for rb_w32_select */ @@ -41,21 +41,83 @@ RBIMPL_SYMBOL_EXPORT_BEGIN() struct timeval; +/** + * The data structure which wraps the fd_set bitmap used by select(2). This + * allows Ruby to use FD sets larger than that allowed by historic limitations + * on modern platforms. + */ typedef struct { - int capa; - fd_set *fdset; + int capa; /**< Maximum allowed number of FDs. */ + fd_set *fdset; /**< File descriptors buffer. */ } rb_fdset_t; -void rb_fd_init(rb_fdset_t *); -void rb_fd_term(rb_fdset_t *); -void rb_fd_set(int, rb_fdset_t *); -void rb_w32_fd_copy(rb_fdset_t *, const fd_set *, int); +RBIMPL_ATTR_NONNULL(()) +/** + * (Re-)initialises a fdset. One must be initialised before other `rb_fd_*` + * operations. Analogous to calling `malloc(3)` to allocate an `fd_set`. + * + * @param[out] f An fdset to squash. + * @post `f` holds no file descriptors. + * + * @internal + * + * Can't this leak memory if the same `f` is passed twice...? + */ +void rb_fd_init(rb_fdset_t *f); + +RBIMPL_ATTR_NONNULL(()) +/** + * Destroys the ::rb_fdset_t, releasing any memory and resources it used. It + * must be reinitialised using rb_fd_init() before future use. Analogous to + * calling `free(3)` to release memory for an `fd_set`. + * + * @param[out] f An fdset to squash. + * @post `f` holds no file descriptors. + */ +void rb_fd_term(rb_fdset_t *f); + +RBIMPL_ATTR_NONNULL(()) +/** + * Sets an fd to a fdset. + * + * @param[in] fd A file descriptor. + * @param[out] f Target fdset. + * @post `f` holds `fd`. + */ +void rb_fd_set(int fd, rb_fdset_t *f); + +RBIMPL_ATTR_NONNULL(()) +/** + * Destructively overwrites an fdset with another. + * + * @param[out] dst Target fdset. + * @param[in] src Source fdset. + * @param[in] max Maximum number of file descriptors to copy. + * @post `dst` is a copy of `src`. + */ +void rb_w32_fd_copy(rb_fdset_t *dst, const fd_set *src, int max); + +RBIMPL_ATTR_NONNULL(()) +/** + * Identical to rb_w32_fd_copy(), except it copies unlimited number of file + * descriptors. + * + * @param[out] dst Target fdset. + * @param[in] src Source fdset. + * @post `dst` is a copy of `src`. + */ void rb_w32_fd_dup(rb_fdset_t *dst, const rb_fdset_t *src); RBIMPL_SYMBOL_EXPORT_END() RBIMPL_ATTR_NONNULL(()) RBIMPL_ATTR_NOALIAS() +/** + * Wipes out the current set of FDs. + * + * @param[out] f The fdset to clear. + * @post `f` has no FDs. + */ static inline void rb_fd_zero(rb_fdset_t *f) { @@ -63,6 +125,13 @@ rb_fd_zero(rb_fdset_t *f) } RBIMPL_ATTR_NONNULL(()) +/** + * Releases a specific FD from the given fdset. + * + * @param[in] n Target FD. + * @param[out] f The fdset that holds `n`. + * @post `f` doesn't hold n. + */ static inline void rb_fd_clr(int n, rb_fdset_t *f) { @@ -70,6 +139,14 @@ rb_fd_clr(int n, rb_fdset_t *f) } RBIMPL_ATTR_NONNULL(()) +/** + * Queries if the given FD is in the given set. + * + * @param[in] n Target FD. + * @param[in] f The fdset to scan. + * @retval 1 Yes there is. + * @retval 0 No there isn't. + */ static inline int rb_fd_isset(int n, rb_fdset_t *f) { @@ -77,6 +154,14 @@ rb_fd_isset(int n, rb_fdset_t *f) } RBIMPL_ATTR_NONNULL(()) +/** + * Destructively overwrites an fdset with another. + * + * @param[out] dst Target fdset. + * @param[in] src Source fdset. + * @param[in] n Maximum number of file descriptors to copy. + * @post `dst` is a copy of `src`. + */ static inline void rb_fd_copy(rb_fdset_t *dst, const fd_set *src, int n) { @@ -84,12 +169,45 @@ rb_fd_copy(rb_fdset_t *dst, const fd_set *src, int n) } RBIMPL_ATTR_NONNULL(()) +/** + * Identical to rb_fd_copy(), except it copies unlimited number of file + * descriptors. + * + * @param[out] dst Target fdset. + * @param[in] src Source fdset. + * @post `dst` is a copy of `src`. + */ static inline void rb_fd_dup(rb_fdset_t *dst, const rb_fdset_t *src) { rb_w32_fd_dup(dst, src); } +/** + * Waits for multiple file descriptors at once. + * + * @param[in] n Max FD in everything passed, plus one. + * @param[in,out] rfds Set of FDs to wait for reads. + * @param[in,out] wfds Set of FDs to wait for writes. + * @param[in,out] efds Set of FDs to wait for OOBs. + * @param[in,out] timeout Max blocking duration. + * @retval -1 Failed, errno set. + * @retval 0 Timeout exceeded. + * @retval otherwise Total number of file descriptors returned. + * @post `rfds` contains readable FDs. + * @post `wfds` contains writable FDs. + * @post `efds` contains exceptional FDs. + * @post `timeout` is the time left. + * @note All pointers are allowed to be null pointers. + * + * @internal + * + * This can wait for `SOCKET` and `HANDLE` at once. In order to achieve that + * property we heavily touch the internals of MSVCRT. We `CreateFile` a + * `"NUL"` alongside of a socket and directly manipulate its `struct ioinfo`. + * This is of course a very dirty hack. If we could design the API today we + * could use `CancellIoEx`. But we are older than that Win32 API. + */ static inline int rb_fd_select(int n, rb_fdset_t *rfds, rb_fdset_t *wfds, rb_fdset_t *efds, struct timeval *timeout) { @@ -103,7 +221,18 @@ rb_fd_select(int n, rb_fdset_t *rfds, rb_fdset_t *wfds, rb_fdset_t *efds, struct RBIMPL_ATTR_NONNULL(()) RBIMPL_ATTR_PURE() -/* :TODO: can this function be __attribute__((returns_nonnull)) or not? */ +/** + * Raw pointer to `fd_set`. + * + * @param[in] f Target fdset. + * @retval NULL `f` is already terminated by rb_fd_term(). + * @retval otherwise Underlying fd_set. + * + * @internal + * + * Extension library must not touch raw pointers. It was a bad idea to let + * them use it. + */ static inline fd_set * rb_fd_ptr(const rb_fdset_t *f) { @@ -111,7 +240,13 @@ rb_fd_ptr(const rb_fdset_t *f) } RBIMPL_ATTR_NONNULL(()) -RBIMPL_ATTR_PURE() +RBIMPL_ATTR_PURE_UNLESS_DEBUG() +/** + * It seems this function has no use. Maybe just remove? + * + * @param[in] f A set. + * @return Number of file descriptors stored. + */ static inline int rb_fd_max(const rb_fdset_t *f) { |