Augustin Cavalier f66d2b46a8 kernel: Add event queue implementation to wait for objects efficiently.
Based on hamishm's original patch from 2015, but heavily modified,
refactored, and reworked.

From the original commit message:

> When an object is deleted, a B_EVENT_INVALID event is delivered,
> and the object is unregistered from the queue.
>
> The special event flag B_EVENT_ONE_SHOT can be passed in when adding
> an object so that the object is automatically unregistered when an
> event is delivered.

Modifications to the original change include:

 * Removed the public interface (syscalls remain private for the moment)

 * Event list queueing/dequeueing almost entirely rewritten, including:
  - Clear events field when dequeueing.

  - Have B_EVENT_QUEUED actually indicate whether the event has been
    appended to the linked list (or not), based around lock state.
    The previous logic was prone to races and double-insertions.

  - "Modify" is now just "Deselect + Select" performed at once;
    previously it could cause use-after-frees.

  - Unlock for deselect only once at the end of dequeue.

  - Handle INVALID events still in the queue upon destruction,
    fixing memory leaks.

 * Deduplified code with wait_for_objects.

 * Use of C++ virtual dispatch instead of C-style enum + function calls,
   and BReferenceable plus destructors for teardown.

 * Removed select/modify/delete flags. Select/Modify are now the same
   operation on the syscall interface, and "Delete" is done when 0
   is passed for "events". Additionally, the events selected can be fetched
   by passing -1 for "events".

 * Implemented level-triggered mode.

 * Use of BStackOrHeapArray and other convenience routines in syscalls.

Change-Id: I1d2f094fd981c95215a59adbc087523c7bbbe40b
Reviewed-on: https://review.haiku-os.org/c/haiku/+/6745
Tested-by: Commit checker robot <no-reply+buildbot@haiku-os.org>
Reviewed-by: Jérôme Duval <jerome.duval@gmail.com>
2023-07-29 15:53:15 +00:00

131 lines
3.7 KiB
C

/*
* Copyright 2002-2018, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*/
#ifndef _FD_H
#define _FD_H
#include <vfs.h>
#include <team.h>
#include <thread.h>
#ifdef __cplusplus
extern "C" {
#endif
struct event_queue;
struct file_descriptor;
struct io_context;
struct net_socket;
struct selectsync;
struct select_info;
struct fd_ops {
status_t (*fd_read)(struct file_descriptor *, off_t pos, void *buffer,
size_t *length);
status_t (*fd_write)(struct file_descriptor *, off_t pos,
const void *buffer, size_t *length);
off_t (*fd_seek)(struct file_descriptor *, off_t pos, int seekType);
status_t (*fd_ioctl)(struct file_descriptor *, ulong op, void *buffer,
size_t length);
status_t (*fd_set_flags)(struct file_descriptor *, int flags);
status_t (*fd_select)(struct file_descriptor *, uint8 event,
struct selectsync *sync);
status_t (*fd_deselect)(struct file_descriptor *, uint8 event,
struct selectsync *sync);
status_t (*fd_read_dir)(struct io_context* ioContext,
struct file_descriptor *, struct dirent *buffer,
size_t bufferSize, uint32 *_count);
status_t (*fd_rewind_dir)(struct file_descriptor *);
status_t (*fd_read_stat)(struct file_descriptor *, struct stat *);
status_t (*fd_write_stat)(struct file_descriptor *, const struct stat *,
int statMask);
status_t (*fd_close)(struct file_descriptor *);
void (*fd_free)(struct file_descriptor *);
};
struct file_descriptor {
int32 type; /* descriptor type */
int32 ref_count;
int32 open_count;
struct fd_ops *ops;
union {
struct vnode *vnode;
struct fs_mount *mount;
struct net_socket *socket;
struct event_queue *queue;
} u;
void *cookie;
int32 open_mode;
off_t pos;
};
/* Types of file descriptors we can create */
enum fd_types {
FDTYPE_FILE = 1,
FDTYPE_ATTR,
FDTYPE_DIR,
FDTYPE_ATTR_DIR,
FDTYPE_INDEX,
FDTYPE_INDEX_DIR,
FDTYPE_QUERY,
FDTYPE_SOCKET,
FDTYPE_EVENT_QUEUE
};
// additional open mode - kernel special
#define O_DISCONNECTED 0x80000000
/* Prototypes */
extern struct file_descriptor *alloc_fd(void);
extern int new_fd_etc(struct io_context *, struct file_descriptor *,
int firstIndex);
extern int new_fd(struct io_context *, struct file_descriptor *);
extern struct file_descriptor *get_fd(struct io_context *, int);
extern struct file_descriptor *get_open_fd(struct io_context *, int);
extern void close_fd(struct io_context *context,
struct file_descriptor *descriptor);
extern status_t close_fd_index(struct io_context *context, int fd);
extern void put_fd(struct file_descriptor *descriptor);
extern void disconnect_fd(struct file_descriptor *descriptor);
extern void inc_fd_ref_count(struct file_descriptor *descriptor);
extern int dup_foreign_fd(team_id fromTeam, int fd, bool kernel);
extern status_t select_fd(int32 fd, struct select_info *info, bool kernel);
extern status_t deselect_fd(int32 fd, struct select_info *info, bool kernel);
extern bool fd_is_valid(int fd, bool kernel);
extern struct vnode *fd_vnode(struct file_descriptor *descriptor);
extern bool fd_close_on_exec(struct io_context *context, int fd);
extern void fd_set_close_on_exec(struct io_context *context, int fd,
bool closeFD);
static struct io_context *get_current_io_context(bool kernel);
extern status_t user_fd_kernel_ioctl(int fd, ulong op, void *buffer,
size_t length);
/* The prototypes of the (sys|user)_ functions are currently defined in vfs.h */
/* Inlines */
static inline struct io_context *
get_current_io_context(bool kernel)
{
if (kernel)
return (struct io_context *)team_get_kernel_team()->io_context;
return (struct io_context *)thread_get_current_thread()->team->io_context;
}
#ifdef __cplusplus
}
#endif
#endif /* _FD_H */