diff --git a/headers/os/drivers/Select.h b/headers/os/drivers/Select.h index 465385bd80..f08971cd5e 100644 --- a/headers/os/drivers/Select.h +++ b/headers/os/drivers/Select.h @@ -31,11 +31,7 @@ enum select_events { extern "C" { #endif -#ifdef COMPILE_FOR_R5 -extern void notify_select_event(struct selectsync *sync, uint32 ref); -#else -extern status_t notify_select_event(struct selectsync *sync, uint32 ref, uint8 event); -#endif +extern status_t notify_select_event(struct selectsync *sync, uint8 event); #ifdef __cplusplus } diff --git a/headers/os/kernel/OS.h b/headers/os/kernel/OS.h index 7a37dbca92..e5accd98a4 100644 --- a/headers/os/kernel/OS.h +++ b/headers/os/kernel/OS.h @@ -658,6 +658,52 @@ extern status_t _get_system_info(system_info *info, size_t size); extern int32 is_computer_on(void); extern double is_computer_on_fire(void); + +// WARNING: Experimental API! + +enum { + B_OBJECT_TYPE_FD = 0, + B_OBJECT_TYPE_SEMAPHORE = 1, + B_OBJECT_TYPE_PORT = 2, + B_OBJECT_TYPE_THREAD = 3 +}; + +enum { + B_EVENT_READ = 0x0001, // FD/port readable + B_EVENT_WRITE = 0x0002, // FD/port writable + B_EVENT_ERROR = 0x0004, // FD error + B_EVENT_PRIORITY_READ = 0x0008, // FD priority readable + B_EVENT_PRIORITY_WRITE = 0x0010, // FD priority writable + B_EVENT_HIGH_PRIORITY_READ = 0x0020, // FD high priority readable + B_EVENT_HIGH_PRIORITY_WRITE = 0x0040, // FD high priority writable + B_EVENT_DISCONNECTED = 0x0080, // FD disconnected + + B_EVENT_ACQUIRE_SEMAPHORE = 0x0001, // semaphore can be acquired + + B_EVENT_INVALID = 0x1000 // FD/port/sem/thread ID not or + // no longer valid (e.g. has been + // close/deleted) +}; + +typedef struct object_wait_info { + int32 object; // ID of the object + uint16 type; // type of the object + uint16 events; // events mask +} object_wait_info; + +// wait_for_objects[_etc]() waits until at least one of the specified events or, +// if given, the timeout occurred. When entering the function the +// object_wait_info::events field specifies the events for each object the +// caller is interested in. When the function returns the fields reflect the +// events that actually occurred. The events B_EVENT_INVALID, B_EVENT_ERROR, +// and B_EVENT_DISCONNECTED don't need to be specified. They will always be +// reported, when they occur. + +extern ssize_t wait_for_objects(object_wait_info* infos, int numInfos); +extern ssize_t wait_for_objects_etc(object_wait_info* infos, int numInfos, + uint32 flags, bigtime_t timeout); + + #ifdef __cplusplus } #endif diff --git a/headers/private/kernel/fs/fd.h b/headers/private/kernel/fs/fd.h index ce2c6972d1..a8f45be019 100644 --- a/headers/private/kernel/fs/fd.h +++ b/headers/private/kernel/fs/fd.h @@ -17,14 +17,14 @@ extern "C" { struct file_descriptor; struct selectsync; -struct select_sync; +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_select)(struct file_descriptor *, uint8 event, uint32 ref, + status_t (*fd_select)(struct file_descriptor *, uint8 event, struct selectsync *sync); status_t (*fd_deselect)(struct file_descriptor *, uint8 event, struct selectsync *sync); @@ -77,10 +77,8 @@ extern void close_fd(struct file_descriptor *descriptor); 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 status_t select_fd(int fd, struct select_sync *sync, uint32 ref, - bool kernel); -extern status_t deselect_fd(int fd, struct select_sync *sync, uint32 ref, - 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); diff --git a/headers/private/kernel/fs/select_sync_pool.h b/headers/private/kernel/fs/select_sync_pool.h index c99479d6c1..7cd96c1615 100644 --- a/headers/private/kernel/fs/select_sync_pool.h +++ b/headers/private/kernel/fs/select_sync_pool.h @@ -17,7 +17,7 @@ extern "C" { status_t add_select_sync_pool_entry(select_sync_pool **pool, selectsync *sync, - uint32 ref, uint8 event); + uint8 event); status_t remove_select_sync_pool_entry(select_sync_pool **pool, selectsync *sync, uint8 event); void delete_select_sync_pool(select_sync_pool *pool); diff --git a/headers/private/kernel/port.h b/headers/private/kernel/port.h index 56ef2ac3a2..d20d64c2c5 100644 --- a/headers/private/kernel/port.h +++ b/headers/private/kernel/port.h @@ -10,6 +10,7 @@ #include struct kernel_args; +struct select_info; #define PORT_FLAG_USE_USER_MEMCPY 0x80000000 @@ -23,6 +24,9 @@ int delete_owned_ports(team_id owner); int32 port_max_ports(void); int32 port_used_ports(void); +status_t select_port(int32 object, struct select_info *info, bool kernel); +status_t deselect_port(int32 object, struct select_info *info, bool kernel); + // currently private API status_t writev_port_etc(port_id id, int32 msgCode, const iovec *msgVecs, size_t vecCount, size_t bufferSize, uint32 flags, bigtime_t timeout); diff --git a/headers/private/kernel/sem.h b/headers/private/kernel/sem.h index 684ec9db43..756b53cdb4 100644 --- a/headers/private/kernel/sem.h +++ b/headers/private/kernel/sem.h @@ -13,6 +13,7 @@ #include struct kernel_args; +struct select_info; #ifdef __cplusplus @@ -25,6 +26,10 @@ extern status_t sem_interrupt_thread(struct thread *t); extern int32 sem_used_sems(void); extern int32 sem_max_sems(void); +extern status_t select_sem(int32 object, struct select_info *info, bool kernel); +extern status_t deselect_sem(int32 object, struct select_info *info, + bool kernel); + extern sem_id create_sem_etc(int32 count, const char *name, team_id owner); /* user calls */ diff --git a/headers/private/kernel/syscalls.h b/headers/private/kernel/syscalls.h index 4736ad1f43..1fd2c01a03 100644 --- a/headers/private/kernel/syscalls.h +++ b/headers/private/kernel/syscalls.h @@ -53,6 +53,10 @@ extern int _kern_setrlimit(int resource, const struct rlimit * rlp); extern status_t _kern_shutdown(bool reboot); extern status_t _kern_get_safemode_option(const char *parameter, char *buffer, size_t *_bufferSize); +extern ssize_t _kern_wait_for_objects(object_wait_info* infos, int numInfos, + uint32 flags, bigtime_t timeout); + + /* sem functions */ extern sem_id _kern_create_sem(int count, const char *name); extern status_t _kern_delete_sem(sem_id id); diff --git a/headers/private/kernel/thread.h b/headers/private/kernel/thread.h index 1c417aa681..65e4c60fa6 100644 --- a/headers/private/kernel/thread.h +++ b/headers/private/kernel/thread.h @@ -14,6 +14,7 @@ #include struct kernel_args; +struct select_info; #ifdef __cplusplus @@ -65,6 +66,9 @@ thread_id spawn_kernel_thread_etc(thread_func, const char *name, int32 priority, status_t wait_for_thread_etc(thread_id id, uint32 flags, bigtime_t timeout, status_t *_returnCode); +status_t select_thread(int32 object, struct select_info *info, bool kernel); +status_t deselect_thread(int32 object, struct select_info *info, bool kernel); + // used in syscalls.c status_t _user_set_thread_priority(thread_id thread, int32 newPriority); status_t _user_rename_thread(thread_id thread, const char *name); diff --git a/headers/private/kernel/thread_types.h b/headers/private/kernel/thread_types.h index 98a04e69fa..22af8d2cb5 100644 --- a/headers/private/kernel/thread_types.h +++ b/headers/private/kernel/thread_types.h @@ -54,6 +54,7 @@ typedef enum job_control_state { struct image; // defined in image.c +struct select_info; struct death_entry { struct list_link link; @@ -234,6 +235,8 @@ struct thread { struct list waiters; } exit; + struct select_info *select_infos; + struct thread_debug_info debug_info; // stack diff --git a/headers/private/kernel/wait_for_objects.h b/headers/private/kernel/wait_for_objects.h new file mode 100644 index 0000000000..7f331e87c3 --- /dev/null +++ b/headers/private/kernel/wait_for_objects.h @@ -0,0 +1,52 @@ +/* + * Copyright 2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ + +#ifndef _KERNEL_WAIT_FOR_OBJECTS_H +#define _KERNEL_WAIT_FOR_OBJECTS_H + +#include + +#include + + +struct select_sync; + + +typedef struct select_info { + struct select_info* next; // next in the object's list + struct select_sync* sync; + uint16 selected_events; + uint16 events; +} select_info; + +typedef struct select_sync { + vint32 ref_count; + benaphore lock; + sem_id sem; + uint32 count; + struct select_info* set; +} select_sync; + +#define SELECT_FLAG(type) (1L << (type - 1)) + + +#ifdef __cplusplus +extern "C" { +#endif + + +extern void put_select_sync(select_sync* sync); +extern status_t notify_select_events(select_info* info, uint16 events); +extern void notify_select_events_list(select_info* list, uint16 events); + +extern ssize_t _user_wait_for_objects(object_wait_info* userInfos, + int numInfos, uint32 flags, bigtime_t timeout); + + +#ifdef __cplusplus +} +#endif + +#endif // _KERNEL_WAIT_FOR_OBJECTS_H diff --git a/headers/private/userlandfs/private/Requests.h b/headers/private/userlandfs/private/Requests.h index 4753dd5d0b..b21c558c72 100644 --- a/headers/private/userlandfs/private/Requests.h +++ b/headers/private/userlandfs/private/Requests.h @@ -547,7 +547,6 @@ public: SelectRequest() : FileRequest(SELECT_REQUEST) {} uint8 event; - uint32 ref; selectsync* sync; }; @@ -1394,7 +1393,6 @@ public: NotifySelectEventRequest() : Request(NOTIFY_SELECT_EVENT_REQUEST) {} selectsync* sync; - uint32 ref; uint8 event; bool unspecifiedEvent; }; diff --git a/src/add-ons/kernel/drivers/random/driver.c b/src/add-ons/kernel/drivers/random/driver.c index 97c6ac5d5f..5b10cd4f86 100644 --- a/src/add-ons/kernel/drivers/random/driver.c +++ b/src/add-ons/kernel/drivers/random/driver.c @@ -466,7 +466,7 @@ random_select(void *cookie, uint8 event, uint32 ref, selectsync *sync) #ifdef COMPILE_FOR_R5 notify_select_event(sync, ref); #else - notify_select_event(sync, ref, event); + notify_select_event(sync, event); #endif } else if (event == B_SELECT_WRITE) { /* we're not writable */ diff --git a/src/add-ons/kernel/drivers/tty/tty.cpp b/src/add-ons/kernel/drivers/tty/tty.cpp index 55f5e60d88..4375c428d6 100644 --- a/src/add-ons/kernel/drivers/tty/tty.cpp +++ b/src/add-ons/kernel/drivers/tty/tty.cpp @@ -1676,7 +1676,7 @@ tty_select(tty_cookie *cookie, uint8 event, uint32 ref, selectsync *sync) if (!ttyReference.IsLocked()) { TRACE(("tty_select() done: cookie %p already closed\n", cookie)); - notify_select_event(sync, ref, event); + notify_select_event(sync, event); return B_OK; } @@ -1690,8 +1690,7 @@ tty_select(tty_cookie *cookie, uint8 event, uint32 ref, selectsync *sync) otherTTY = NULL; // add the event to the TTY's pool - status_t error = add_select_sync_pool_entry(&tty->select_pool, sync, ref, - event); + status_t error = add_select_sync_pool_entry(&tty->select_pool, sync, event); if (error != B_OK) { TRACE(("tty_select() done: add_select_sync_pool_entry() failed: %lx\n", error)); @@ -1708,7 +1707,7 @@ tty_select(tty_cookie *cookie, uint8 event, uint32 ref, selectsync *sync) case B_SELECT_READ: if (tty->reader_queue.IsEmpty() && line_buffer_readable(tty->input_buffer) > 0) { - notify_select_event(sync, ref, event); + notify_select_event(sync, event); } break; @@ -1716,7 +1715,7 @@ tty_select(tty_cookie *cookie, uint8 event, uint32 ref, selectsync *sync) { // writes go to the other TTY if (!otherTTY) { - notify_select_event(sync, ref, event); + notify_select_event(sync, event); break; } @@ -1730,7 +1729,7 @@ tty_select(tty_cookie *cookie, uint8 event, uint32 ref, selectsync *sync) if (!echo || (tty->writer_queue.IsEmpty() && line_buffer_writable(tty->input_buffer) > 0)) { - notify_select_event(sync, ref, event); + notify_select_event(sync, event); } } diff --git a/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/KernelRequestHandler.cpp b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/KernelRequestHandler.cpp index 7c2d204019..2217796dbf 100644 --- a/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/KernelRequestHandler.cpp +++ b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/KernelRequestHandler.cpp @@ -211,13 +211,13 @@ KernelRequestHandler::_HandleRequest(NotifySelectEventRequest* request) if (request->unspecifiedEvent) { // old style add-ons can't provide an event argument; we shoot // all events - notify_select_event(request->sync, request->ref, B_SELECT_READ); - notify_select_event(request->sync, request->ref, B_SELECT_WRITE); - notify_select_event(request->sync, request->ref, B_SELECT_ERROR); + notify_select_event(request->sync, B_SELECT_READ); + notify_select_event(request->sync, B_SELECT_WRITE); + notify_select_event(request->sync, B_SELECT_ERROR); } else { - PRINT(("notify_select_event(%p, %lu, %d)\n", request->sync, - request->ref, (int)request->event)); - notify_select_event(request->sync, request->ref, request->event); + PRINT(("notify_select_event(%p, %d)\n", request->sync, + (int)request->event)); + notify_select_event(request->sync, request->event); } } else result = B_BAD_VALUE; diff --git a/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/Volume.cpp b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/Volume.cpp index 4f41e4f513..654648f3f0 100644 --- a/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/Volume.cpp +++ b/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/Volume.cpp @@ -736,7 +736,7 @@ Volume::Select(fs_vnode node, fs_cookie cookie, uint8 event, uint32 ref, { // check capability if (!fFileSystem->HasCapability(FS_CAPABILITY_SELECT)) { - notify_select_event(sync, ref, event); + notify_select_event(sync, event); return B_OK; } @@ -757,7 +757,6 @@ Volume::Select(fs_vnode node, fs_cookie cookie, uint8 event, uint32 ref, request->node = node; request->fileCookie = cookie; request->event = event; - request->ref = ref; request->sync = sync; // add a selectsync entry diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/BeOSKernelVolume.cpp b/src/add-ons/kernel/file_systems/userlandfs/server/BeOSKernelVolume.cpp index 1159efc1cc..62303c7d38 100644 --- a/src/add-ons/kernel/file_systems/userlandfs/server/BeOSKernelVolume.cpp +++ b/src/add-ons/kernel/file_systems/userlandfs/server/BeOSKernelVolume.cpp @@ -224,7 +224,7 @@ BeOSKernelVolume::Select(fs_vnode node, fs_cookie cookie, uint8 event, uint32 ref, selectsync* sync) { if (!fFSOps->select) { - UserlandFS::KernelEmu::notify_select_event(sync, ref, event, false); + UserlandFS::KernelEmu::notify_select_event(sync, event, false); return B_OK; } return fFSOps->select(fVolumeCookie, node, cookie, event, ref, sync); diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/HaikuKernelVolume.cpp b/src/add-ons/kernel/file_systems/userlandfs/server/HaikuKernelVolume.cpp index 66e76a35ad..3e8778ecca 100644 --- a/src/add-ons/kernel/file_systems/userlandfs/server/HaikuKernelVolume.cpp +++ b/src/add-ons/kernel/file_systems/userlandfs/server/HaikuKernelVolume.cpp @@ -194,7 +194,7 @@ HaikuKernelVolume::Select(fs_vnode node, fs_cookie cookie, uint8 event, uint32 ref, selectsync* sync) { if (!fFSModule->select) { - UserlandFS::KernelEmu::notify_select_event(sync, ref, event, false); + UserlandFS::KernelEmu::notify_select_event(sync, event, false); return B_OK; } return fFSModule->select(fVolumeCookie, node, cookie, event, ref, sync); diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/UserlandRequestHandler.cpp b/src/add-ons/kernel/file_systems/userlandfs/server/UserlandRequestHandler.cpp index 247b6cf208..8c00acfa90 100644 --- a/src/add-ons/kernel/file_systems/userlandfs/server/UserlandRequestHandler.cpp +++ b/src/add-ons/kernel/file_systems/userlandfs/server/UserlandRequestHandler.cpp @@ -627,7 +627,7 @@ UserlandRequestHandler::_HandleRequest(SelectRequest* request) if (result == B_OK) { RequestThreadContext context(volume); result = volume->Select(request->node, request->fileCookie, - request->event, request->ref, request->sync); + request->event, 0, request->sync); } // prepare the reply diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/beos_kernel_emu.cpp b/src/add-ons/kernel/file_systems/userlandfs/server/beos_kernel_emu.cpp index e15f99257d..553b1902bd 100644 --- a/src/add-ons/kernel/file_systems/userlandfs/server/beos_kernel_emu.cpp +++ b/src/add-ons/kernel/file_systems/userlandfs/server/beos_kernel_emu.cpp @@ -82,7 +82,7 @@ notify_listener(int op, nspace_id nsid, ino_t vnida, ino_t vnidb, void notify_select_event(selectsync *sync, uint32 ref) { - UserlandFS::KernelEmu::notify_select_event(sync, ref, 0, true); + UserlandFS::KernelEmu::notify_select_event(sync, 0, true); } // send_notification diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/haiku_kernel_emu.cpp b/src/add-ons/kernel/file_systems/userlandfs/server/haiku_kernel_emu.cpp index c226962831..57554e8d7a 100644 --- a/src/add-ons/kernel/file_systems/userlandfs/server/haiku_kernel_emu.cpp +++ b/src/add-ons/kernel/file_systems/userlandfs/server/haiku_kernel_emu.cpp @@ -76,9 +76,9 @@ notify_attribute_changed(dev_t device, ino_t node, const char *attribute, // notify_select_event status_t -notify_select_event(selectsync *sync, uint32 ref, uint8 event) +notify_select_event(selectsync *sync, uint8 event) { - return UserlandFS::KernelEmu::notify_select_event(sync, ref, event, false); + return UserlandFS::KernelEmu::notify_select_event(sync, event, false); } // notify_query_entry_created diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/kernel_emu.cpp b/src/add-ons/kernel/file_systems/userlandfs/server/kernel_emu.cpp index b755bc7dce..fdb40e21b3 100644 --- a/src/add-ons/kernel/file_systems/userlandfs/server/kernel_emu.cpp +++ b/src/add-ons/kernel/file_systems/userlandfs/server/kernel_emu.cpp @@ -170,8 +170,8 @@ UserlandFS::KernelEmu::notify_listener(int32 operation, uint32 details, // notify_select_event status_t -UserlandFS::KernelEmu::notify_select_event(selectsync *sync, uint32 ref, - uint8 event, bool unspecifiedEvent) +UserlandFS::KernelEmu::notify_select_event(selectsync *sync, uint8 event, + bool unspecifiedEvent) { // get the request port and the file system RequestPort* port; @@ -188,7 +188,6 @@ UserlandFS::KernelEmu::notify_select_event(selectsync *sync, uint32 ref, return error; request->sync = sync; - request->ref = ref; request->event = event; request->unspecifiedEvent = unspecifiedEvent; diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/kernel_emu.h b/src/add-ons/kernel/file_systems/userlandfs/server/kernel_emu.h index ab71cb170a..8d4e795f37 100644 --- a/src/add-ons/kernel/file_systems/userlandfs/server/kernel_emu.h +++ b/src/add-ons/kernel/file_systems/userlandfs/server/kernel_emu.h @@ -17,7 +17,7 @@ void free_path(char *p); status_t notify_listener(int32 operation, uint32 details, dev_t device, ino_t oldDirectory, ino_t directory, ino_t node, const char* oldName, const char* name); -status_t notify_select_event(selectsync *sync, uint32 ref, uint8 event, +status_t notify_select_event(selectsync *sync, uint8 event, bool unspecifiedEvent); status_t notify_query(port_id port, int32 token, int32 operation, dev_t device, ino_t directory, const char* name, ino_t node); diff --git a/src/add-ons/kernel/network/stack/net_socket.cpp b/src/add-ons/kernel/network/stack/net_socket.cpp index 443be52b32..c9292196d1 100644 --- a/src/add-ons/kernel/network/stack/net_socket.cpp +++ b/src/add-ons/kernel/network/stack/net_socket.cpp @@ -470,7 +470,7 @@ socket_request_notification(net_socket *_socket, uint8 event, uint32 ref, benaphore_lock(&socket->lock); status_t status = add_select_sync_pool_entry(&socket->select_pool, sync, - ref, event); + event); benaphore_unlock(&socket->lock); @@ -486,7 +486,7 @@ socket_request_notification(net_socket *_socket, uint8 event, uint32 ref, ssize_t available = socket_read_avail(socket); if ((ssize_t)socket->receive.low_water_mark <= available || available < B_OK) - notify_select_event(sync, ref, event); + notify_select_event(sync, event); break; } case B_SELECT_WRITE: @@ -494,7 +494,7 @@ socket_request_notification(net_socket *_socket, uint8 event, uint32 ref, ssize_t available = socket_send_avail(socket); if ((ssize_t)socket->send.low_water_mark <= available || available < B_OK) - notify_select_event(sync, ref, event); + notify_select_event(sync, event); break; } case B_SELECT_ERROR: diff --git a/src/system/kernel/Jamfile b/src/system/kernel/Jamfile index 1665d85f36..8be1b226c9 100644 --- a/src/system/kernel/Jamfile +++ b/src/system/kernel/Jamfile @@ -40,6 +40,7 @@ KernelMergeObject kernel_core.o : team.cpp thread.cpp timer.c + wait_for_objects.cpp : $(TARGET_KERNEL_PIC_CCFLAGS) ; diff --git a/src/system/kernel/fs/Jamfile b/src/system/kernel/fs/Jamfile index 1eaba4ec0d..2a719e0629 100644 --- a/src/system/kernel/fs/Jamfile +++ b/src/system/kernel/fs/Jamfile @@ -13,7 +13,6 @@ KernelMergeObject kernel_fs.o : vfs.cpp vfs_boot.cpp vfs_net_boot.cpp - vfs_select.cpp node_monitor.cpp IOScheduler.cpp KPath.cpp diff --git a/src/system/kernel/fs/devfs.cpp b/src/system/kernel/fs/devfs.cpp index 2f40e522e3..649fe920c0 100644 --- a/src/system/kernel/fs/devfs.cpp +++ b/src/system/kernel/fs/devfs.cpp @@ -1839,7 +1839,7 @@ devfs_select(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie, uint8 event, // If the device has no select() hook, notify select() now. if (!vnode->stream.u.dev.info->select) - return notify_select_event((selectsync*)sync, ref, event); + return notify_select_event((selectsync*)sync, event); return vnode->stream.u.dev.info->select(cookie->device_cookie, event, ref, (selectsync*)sync); diff --git a/src/system/kernel/fs/fd.cpp b/src/system/kernel/fs/fd.cpp index 778c3877d7..6fb24be5df 100644 --- a/src/system/kernel/fs/fd.cpp +++ b/src/system/kernel/fs/fd.cpp @@ -4,7 +4,6 @@ * Distributed under the terms of the MIT License. */ - #include #include @@ -15,8 +14,7 @@ #include #include #include - -#include "vfs_select.h" +#include //#define TRACE_FD @@ -448,6 +446,7 @@ deselect_select_infos(file_descriptor* descriptor, select_info* infos) } } + notify_select_events(info, B_EVENT_INVALID); info = info->next; put_select_sync(sync); } @@ -455,9 +454,10 @@ deselect_select_infos(file_descriptor* descriptor, select_info* infos) status_t -select_fd(int fd, struct select_sync* sync, uint32 ref, bool kernel) +select_fd(int32 fd, struct select_info* info, bool kernel) { - TRACE(("select_fd(fd = %d, selectsync = %p, ref = %lu, 0x%x)\n", fd, sync, ref, sync->set[ref].selected_events)); + TRACE(("select_fd(fd = %d, info = %p (%p), 0x%x)\n", fd, info, + info->sync, info.selected_events)); FDGetter fdGetter; // define before the context locker, so it will be destroyed after it @@ -469,7 +469,6 @@ select_fd(int fd, struct select_sync* sync, uint32 ref, bool kernel) if (descriptor == NULL) return B_FILE_ERROR; - select_info* info = &sync->set[ref]; if (info->selected_events == 0) return B_OK; @@ -485,7 +484,7 @@ select_fd(int fd, struct select_sync* sync, uint32 ref, bool kernel) // as long as the info is in the list, we keep a reference to the sync // object - atomic_add(&sync->ref_count, 1); + atomic_add(&info->sync->ref_count, 1); locker.Unlock(); @@ -494,7 +493,7 @@ select_fd(int fd, struct select_sync* sync, uint32 ref, bool kernel) for (uint16 event = 1; event < 16; event++) { if (info->selected_events & SELECT_FLAG(event) - && descriptor->ops->fd_select(descriptor, event, ref, + && descriptor->ops->fd_select(descriptor, event, (selectsync*)info) == B_OK) { selectedEvents |= SELECT_FLAG(event); } @@ -503,18 +502,18 @@ select_fd(int fd, struct select_sync* sync, uint32 ref, bool kernel) // if nothing has been selected, we deselect immediately if (selectedEvents == 0) - deselect_fd(fd, sync, ref, kernel); + deselect_fd(fd, info, kernel); return B_OK; } status_t -deselect_fd(int fd, struct select_sync* sync, uint32 ref, bool kernel) +deselect_fd(int32 fd, struct select_info* info, bool kernel) { - TRACE(("deselect_fd(fd = %d, selectsync = %p, ref = %lu)\n", fd, sync, ref)); + TRACE(("deselect_fd(fd = %d, info = %p (%p), 0x%x)\n", fd, info, + info->sync, info.selected_events)); - select_info* info = &sync->set[ref]; if (info->selected_events == 0) return B_OK; @@ -552,7 +551,7 @@ deselect_fd(int fd, struct select_sync* sync, uint32 ref, bool kernel) } } - put_select_sync(sync); + put_select_sync(info->sync); return B_OK; } diff --git a/src/system/kernel/fs/pipefs.cpp b/src/system/kernel/fs/pipefs.cpp index 69748ce426..4a0d8ee95f 100644 --- a/src/system/kernel/fs/pipefs.cpp +++ b/src/system/kernel/fs/pipefs.cpp @@ -12,20 +12,19 @@ #include #include +#include #include -#include +#include +#include +#include +#include +#include #include #include #include #include -#include "vfs_select.h" -#include -#include -#include -#include #include -#include //#define TRACE_PIPEFS @@ -960,7 +959,7 @@ Inode::Select(uint8 event, uint32 ref, selectsync *sync, int openMode) } else return B_NOT_ALLOWED; - if (add_select_sync_pool_entry(pool, sync, ref, event) != B_OK) + if (add_select_sync_pool_entry(pool, sync, event) != B_OK) return B_ERROR; // signal right away, if the condition holds already @@ -968,12 +967,12 @@ Inode::Select(uint8 event, uint32 ref, selectsync *sync, int openMode) if (event == B_SELECT_WRITE && (fBuffer.Writable() > 0 || fReaderCount == 0) || event == B_SELECT_ERROR && fReaderCount == 0) { - return notify_select_event(sync, ref, event); + return notify_select_event(sync, event); } } else { if (event == B_SELECT_READ && (fBuffer.Readable() > 0 || fWriterCount == 0)) { - return notify_select_event(sync, ref, event); + return notify_select_event(sync, event); } } diff --git a/src/system/kernel/fs/vfs.cpp b/src/system/kernel/fs/vfs.cpp index 1a2dab74a9..291e612816 100644 --- a/src/system/kernel/fs/vfs.cpp +++ b/src/system/kernel/fs/vfs.cpp @@ -201,7 +201,7 @@ static status_t file_write(struct file_descriptor *, off_t pos, const void *buff static off_t file_seek(struct file_descriptor *, off_t pos, int seek_type); static void file_free_fd(struct file_descriptor *); static status_t file_close(struct file_descriptor *); -static status_t file_select(struct file_descriptor *, uint8 event, uint32 ref, +static status_t file_select(struct file_descriptor *, uint8 event, struct selectsync *sync); static status_t file_deselect(struct file_descriptor *, uint8 event, struct selectsync *sync); @@ -3894,7 +3894,7 @@ file_seek(struct file_descriptor *descriptor, off_t pos, int seekType) static status_t -file_select(struct file_descriptor *descriptor, uint8 event, uint32 ref, +file_select(struct file_descriptor *descriptor, uint8 event, struct selectsync *sync) { FUNCTION(("file_select(%p, %u, %lu, %p)\n", descriptor, event, ref, sync)); @@ -3903,10 +3903,10 @@ file_select(struct file_descriptor *descriptor, uint8 event, uint32 ref, // If the FS has no select() hook, notify select() now. if (FS_CALL(vnode, select) == NULL) - return notify_select_event(sync, ref, event); + return notify_select_event(sync, event); return FS_CALL(vnode, select)(vnode->mount->cookie, vnode->private_node, - descriptor->cookie, event, ref, sync); + descriptor->cookie, event, 0, sync); } diff --git a/src/system/kernel/fs/vfs_select.h b/src/system/kernel/fs/vfs_select.h deleted file mode 100644 index 407eaa5298..0000000000 --- a/src/system/kernel/fs/vfs_select.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2002-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved. - * Distributed under the terms of the MIT License. - */ -#ifndef VFS_SELECT_H -#define VFS_SELECT_H - - -#include - -#include -#include - -#include - - -struct select_sync; - -typedef struct select_info { - select_info* next; // next in the IO context's list - select_sync* sync; - uint16 selected_events; - uint16 events; -} select_info; - -typedef struct select_sync { - vint32 ref_count; - benaphore lock; - sem_id sem; - uint32 count; - select_info* set; -} select_sync; - -#define SELECT_FLAG(type) (1L << (type - 1)) - -struct select_sync_pool_entry - : DoublyLinkedListLinkImpl { - selectsync *sync; - uint32 ref; - uint16 events; -}; - -typedef DoublyLinkedList SelectSyncPoolEntryList; - -struct select_sync_pool { - SelectSyncPoolEntryList entries; -}; - -void put_select_sync(select_sync* sync); - -status_t notify_select_events(select_info* info, uint16 events); - -#endif /* VFS_SELECT_H */ diff --git a/src/system/kernel/port.c b/src/system/kernel/port.c index 5cc669c347..9b3b3bf33f 100644 --- a/src/system/kernel/port.c +++ b/src/system/kernel/port.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -49,6 +50,7 @@ struct port_entry { sem_id read_sem; sem_id write_sem; int32 total_count; // messages read from port since creation + select_info *select_infos; struct list msg_queue; }; @@ -312,6 +314,14 @@ dump_port_info(int argc, char **argv) } +static void +notify_port_select_events(int slot, uint16 events) +{ + if (sPorts[slot].select_infos) + notify_select_events_list(sPorts[slot].select_infos, events); +} + + /** this function cycles through the ports table, deleting all * the ports that are owned by the passed team_id */ @@ -499,6 +509,7 @@ create_port(int32 queueLength, const char *name) list_init(&sPorts[i].msg_queue); sPorts[i].total_count = 0; + sPorts[i].select_infos = NULL; id = sPorts[i].id; RELEASE_PORT_LOCK(sPorts[i]); @@ -565,6 +576,9 @@ close_port(port_id id) readSem = sPorts[slot].read_sem; writeSem = sPorts[slot].write_sem; + notify_port_select_events(slot, B_EVENT_INVALID); + sPorts[slot].select_infos = NULL; + RELEASE_PORT_LOCK(sPorts[slot]); restore_interrupts(state); @@ -611,6 +625,9 @@ delete_port(port_id id) sPorts[slot].name = NULL; list_move_to_list(&sPorts[slot].msg_queue, &list); + notify_port_select_events(slot, B_EVENT_INVALID); + sPorts[slot].select_infos = NULL; + RELEASE_PORT_LOCK(sPorts[slot]); // update the first free slot hint in the array @@ -639,6 +656,93 @@ delete_port(port_id id) } +status_t +select_port(int32 id, struct select_info *info, bool kernel) +{ + cpu_status state; + int32 slot; + status_t error = B_OK; + + if (id < 0) + return B_BAD_PORT_ID; + + slot = id % sMaxPorts; + + state = disable_interrupts(); + GRAB_PORT_LOCK(sPorts[slot]); + + if (sPorts[slot].id != id || is_port_closed(slot)) { + // bad port ID + error = B_BAD_SEM_ID; + } else if (!kernel && sPorts[slot].owner == team_get_kernel_team_id()) { + // kernel port, but call from userland + error = B_NOT_ALLOWED; + } else { + info->selected_events &= B_EVENT_READ | B_EVENT_WRITE | B_EVENT_INVALID; + + if (info->selected_events != 0) { + uint16 events = 0; + int32 writeCount = 0; + + info->next = sPorts[slot].select_infos; + sPorts[slot].select_infos = info; + + // check for events + if ((info->selected_events & B_EVENT_READ) != 0 + && !list_is_empty(&sPorts[slot].msg_queue)) { + events |= B_EVENT_READ; + } + + if (get_sem_count(sPorts[slot].write_sem, &writeCount) == B_OK + && writeCount > 0) { + events |= B_EVENT_WRITE; + } + + if (events != 0) + notify_select_events(info, events); + } + } + + RELEASE_PORT_LOCK(sPorts[slot]); + restore_interrupts(state); + + return error; +} + + +status_t +deselect_port(int32 id, struct select_info *info, bool kernel) +{ + cpu_status state; + int32 slot; + + if (id < 0) + return B_BAD_PORT_ID; + + if (info->selected_events == 0) + return B_OK; + + slot = id % sMaxPorts; + + state = disable_interrupts(); + GRAB_PORT_LOCK(sPorts[slot]); + + if (sPorts[slot].id == id) { + select_info** infoLocation = &sPorts[slot].select_infos; + while (*infoLocation != NULL && *infoLocation != info) + infoLocation = &(*infoLocation)->next; + + if (*infoLocation == info) + *infoLocation = info->next; + } + + RELEASE_PORT_LOCK(sPorts[slot]); + restore_interrupts(state); + + return B_OK; +} + + port_id find_port(const char *name) { @@ -988,6 +1092,8 @@ read_port_etc(port_id id, int32 *_msgCode, void *msgBuffer, size_t bufferSize, sPorts[slot].total_count++; + notify_port_select_events(slot, B_EVENT_WRITE); + cachedSem = sPorts[slot].write_sem; RELEASE_PORT_LOCK(sPorts[slot]); @@ -1151,6 +1257,8 @@ writev_port_etc(port_id id, int32 msgCode, const iovec *msgVecs, list_add_item(&sPorts[slot].msg_queue, msg); + notify_port_select_events(slot, B_EVENT_READ); + // store sem_id in local variable cachedSem = sPorts[slot].read_sem; diff --git a/src/system/kernel/sem.c b/src/system/kernel/sem.c index 71672b294d..5edc2b0400 100644 --- a/src/system/kernel/sem.c +++ b/src/system/kernel/sem.c @@ -25,10 +25,12 @@ #include #include #include +#include #include #include + //#define TRACE_SEM #ifdef TRACE_SEM # define TRACE(x) dprintf x @@ -48,6 +50,7 @@ struct sem_entry { struct thread_queue queue; char *name; team_id owner; // if set to -1, means owned by a port + select_info *select_infos; #ifdef DEBUG_LAST_ACQUIRER thread_id last_acquirer; int32 last_acquire_count; @@ -81,7 +84,8 @@ static spinlock sem_spinlock = 0; #define RELEASE_SEM_LOCK(s) release_spinlock(&(s).lock) static int remove_thread_from_sem(struct thread *thread, struct sem_entry *sem, - struct thread_queue *queue, status_t acquireStatus); + struct thread_queue *queue, status_t acquireStatus, + bool hasThreadLock); struct sem_timeout_args { thread_id blocked_thread; @@ -255,6 +259,14 @@ free_sem_slot(int slot, sem_id nextID) } +static void +notify_sem_select_events(struct sem_entry* sem, uint16 events) +{ + if (sem->u.used.select_infos) + notify_select_events_list(sem->u.used.select_infos, events); +} + + status_t sem_init(kernel_args *args) { @@ -358,6 +370,7 @@ create_sem_etc(int32 count, const char *name, team_id owner) clear_thread_queue(&sem->u.used.queue); sem->u.used.name = tempName; sem->u.used.owner = owner; + sem->u.used.select_infos = NULL; id = sem->id; RELEASE_SEM_LOCK(*sem); @@ -408,6 +421,9 @@ delete_sem(sem_id id) return B_BAD_SEM_ID; } + notify_sem_select_events(&sSems[slot], B_EVENT_INVALID); + sSems[slot].u.used.select_infos = NULL; + releasedThreads = 0; clear_thread_queue(&releaseQueue); @@ -449,6 +465,80 @@ delete_sem(sem_id id) } +status_t +select_sem(int32 id, struct select_info* info, bool kernel) +{ + cpu_status state; + int32 slot; + status_t error = B_OK; + + if (id < 0) + return B_BAD_SEM_ID; + + slot = id % sMaxSems; + + state = disable_interrupts(); + GRAB_SEM_LOCK(sSems[slot]); + + if (sSems[slot].id != id) { + // bad sem ID + error = B_BAD_SEM_ID; + } else if (!kernel + && sSems[slot].u.used.owner == team_get_kernel_team_id()) { + // kernel semaphore, but call from userland + error = B_NOT_ALLOWED; + } else { + info->selected_events &= B_EVENT_ACQUIRE_SEMAPHORE | B_EVENT_INVALID; + + if (info->selected_events != 0) { + info->next = sSems[slot].u.used.select_infos; + sSems[slot].u.used.select_infos = info; + + if (sSems[slot].u.used.count > 0) + notify_select_events(info, B_EVENT_ACQUIRE_SEMAPHORE); + } + } + + RELEASE_SEM_LOCK(sSems[slot]); + restore_interrupts(state); + + return error; +} + + +status_t +deselect_sem(int32 id, struct select_info* info, bool kernel) +{ + cpu_status state; + int32 slot; + + if (id < 0) + return B_BAD_SEM_ID; + + if (info->selected_events == 0) + return B_OK; + + slot = id % sMaxSems; + + state = disable_interrupts(); + GRAB_SEM_LOCK(sSems[slot]); + + if (sSems[slot].id == id) { + select_info** infoLocation = &sSems[slot].u.used.select_infos; + while (*infoLocation != NULL && *infoLocation != info) + infoLocation = &(*infoLocation)->next; + + if (*infoLocation == info) + *infoLocation = info->next; + } + + RELEASE_SEM_LOCK(sSems[slot]); + restore_interrupts(state); + + return B_OK; +} + + /** Called from a timer handler. Wakes up a semaphore */ static int32 @@ -481,7 +571,8 @@ sem_timeout(timer *data) } clear_thread_queue(&wakeupQueue); - remove_thread_from_sem(thread, &sSems[slot], &wakeupQueue, B_TIMED_OUT); + remove_thread_from_sem(thread, &sSems[slot], &wakeupQueue, B_TIMED_OUT, + false); RELEASE_SEM_LOCK(sSems[slot]); @@ -635,7 +726,8 @@ switch_sem_etc(sem_id semToBeReleased, sem_id id, int32 count, clear_thread_queue(&wakeupQueue); GRAB_SEM_LOCK(sSems[slot]); if (sSems[slot].id == id) { - remove_thread_from_sem(thread, &sSems[slot], &wakeupQueue, B_INTERRUPTED); + remove_thread_from_sem(thread, &sSems[slot], &wakeupQueue, + B_INTERRUPTED, true); } RELEASE_SEM_LOCK(sSems[slot]); while ((thread = thread_dequeue(&wakeupQueue)) != NULL) { @@ -770,6 +862,10 @@ release_sem_etc(sem_id id, int32 count, uint32 flags) sSems[slot].u.used.count += delta; count -= delta; } + + if (sSems[slot].u.used.count > 0) + notify_sem_select_events(&sSems[slot], B_EVENT_ACQUIRE_SEMAPHORE); + RELEASE_SEM_LOCK(sSems[slot]); // pull off any items in the release queue and put them in the run queue @@ -1022,7 +1118,8 @@ sem_interrupt_thread(struct thread *thread) } clear_thread_queue(&wakeupQueue); - if (remove_thread_from_sem(thread, &sSems[slot], &wakeupQueue, B_INTERRUPTED) != B_OK) { + if (remove_thread_from_sem(thread, &sSems[slot], &wakeupQueue, + B_INTERRUPTED, true) != B_OK) { panic("sem_interrupt_thread: thread 0x%lx not found in sem 0x%lx's wait queue\n", thread->id, thread->sem.blocking); } @@ -1045,7 +1142,7 @@ sem_interrupt_thread(struct thread *thread) static int remove_thread_from_sem(struct thread *thread, struct sem_entry *sem, - struct thread_queue *queue, status_t acquireStatus) + struct thread_queue *queue, status_t acquireStatus, bool hasThreadLock) { // remove the thread from the queue and place it in the supplied queue if (thread_dequeue_id(&sem->u.used.queue, thread->id) != thread) @@ -1070,6 +1167,16 @@ remove_thread_from_sem(struct thread *thread, struct sem_entry *sem, sem->u.used.count -= delta; } + if (sem->u.used.count > 0 && sem->u.used.select_infos != NULL) { + if (hasThreadLock) + RELEASE_THREAD_LOCK(); + + notify_sem_select_events(sem, B_EVENT_ACQUIRE_SEMAPHORE); + + if (hasThreadLock) + GRAB_THREAD_LOCK(); + } + return B_OK; } diff --git a/src/system/kernel/syscalls.c b/src/system/kernel/syscalls.c index 4abc2ce4cf..d8de065858 100644 --- a/src/system/kernel/syscalls.c +++ b/src/system/kernel/syscalls.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include diff --git a/src/system/kernel/thread.cpp b/src/system/kernel/thread.cpp index 0b71203ab3..f89ee772b4 100644 --- a/src/system/kernel/thread.cpp +++ b/src/system/kernel/thread.cpp @@ -9,8 +9,19 @@ /*! Threading routines */ +#include + +#include +#include +#include +#include + #include +#include +#include + +#include #include #include #include @@ -20,20 +31,13 @@ #include #include #include -#include #include #include #include #include #include +#include -#include -#include - -#include -#include -#include -#include //#define TRACE_THREAD #ifdef TRACE_THREAD @@ -249,6 +253,7 @@ create_thread_struct(struct thread *inthread, const char *name, thread->exit.reason = 0; thread->exit.signal = 0; list_init(&thread->exit.waiters); + thread->select_infos = NULL; sprintf(temp, "thread_0x%lx_retcode_sem", thread->id); thread->exit.sem = create_sem(0, temp); @@ -1223,11 +1228,25 @@ thread_exit(void) debugInfo = thread->debug_info; clear_thread_debug_info(&thread->debug_info, true); + // Remove the select infos. We notify them a little later. + select_info* selectInfos = thread->select_infos; + thread->select_infos = NULL; + RELEASE_THREAD_LOCK(); restore_interrupts(state); destroy_thread_debug_info(&debugInfo); + // notify select infos + select_info* info = selectInfos; + while (info != NULL) { + select_sync* sync = info->sync; + + notify_select_events(info, B_EVENT_INVALID); + info = info->next; + put_select_sync(sync); + } + // shutdown the thread messaging status = acquire_sem_etc(thread->msg.write_sem, 1, B_RELATIVE_TIMEOUT, 0); @@ -1613,6 +1632,61 @@ wait_for_thread_etc(thread_id id, uint32 flags, bigtime_t timeout, } +status_t +select_thread(int32 id, struct select_info* info, bool kernel) +{ + InterruptsSpinLocker locker(thread_spinlock); + + // get thread + struct thread* thread = thread_get_thread_struct_locked(id); + if (thread == NULL) + return B_BAD_THREAD_ID; + + // We support only B_EVENT_INVALID at the moment. + info->selected_events &= B_EVENT_INVALID; + + // add info to list + if (info->selected_events != 0) { + info->next = thread->select_infos; + thread->select_infos = info; + + // we need a sync reference + atomic_add(&info->sync->ref_count, 1); + } + + return B_OK; +} + + +status_t +deselect_thread(int32 id, struct select_info* info, bool kernel) +{ + InterruptsSpinLocker locker(thread_spinlock); + + // get thread + struct thread* thread = thread_get_thread_struct_locked(id); + if (thread == NULL) + return B_BAD_THREAD_ID; + + // remove info from list + select_info** infoLocation = &thread->select_infos; + while (*infoLocation != NULL && *infoLocation != info) + infoLocation = &(*infoLocation)->next; + + if (*infoLocation != info) + return B_OK; + + *infoLocation = info->next; + + locker.Unlock(); + + // surrender sync reference + put_select_sync(info->sync); + + return B_OK; +} + + int32 thread_max_threads(void) { diff --git a/src/system/kernel/fs/vfs_select.cpp b/src/system/kernel/wait_for_objects.cpp similarity index 73% rename from src/system/kernel/fs/vfs_select.cpp rename to src/system/kernel/wait_for_objects.cpp index df001038cc..2706f64fcc 100644 --- a/src/system/kernel/fs/vfs_select.cpp +++ b/src/system/kernel/wait_for_objects.cpp @@ -4,8 +4,8 @@ * Distributed under the terms of the MIT License. */ - -#include "vfs_select.h" +#include +#include #include @@ -15,16 +15,23 @@ #include #include +#include +#include + #include -#include -#include +#include +#include +#include #include +#include #include +#include #include -//#define TRACE_VFS_SELECT -#ifdef TRACE_VFS_SELECT + +//#define TRACE_WAIT_FOR_OBJECTS +#ifdef TRACE_WAIT_FOR_OBJECTS # define PRINT(x) dprintf x # define FUNCTION(x) dprintf x #else @@ -36,6 +43,54 @@ using std::nothrow; +struct select_sync_pool_entry + : DoublyLinkedListLinkImpl { + selectsync *sync; + uint16 events; +}; + +typedef DoublyLinkedList SelectSyncPoolEntryList; + +struct select_sync_pool { + SelectSyncPoolEntryList entries; +}; + + +struct select_ops { + status_t (*select)(int32 object, struct select_info* info, bool kernel); + status_t (*deselect)(int32 object, struct select_info* info, bool kernel); +}; + + +static const select_ops kSelectOps[] = { + // B_OBJECT_TYPE_FD + { + select_fd, + deselect_fd + }, + + // B_OBJECT_TYPE_SEMAPHORE + { + select_sem, + deselect_sem + }, + + // B_OBJECT_TYPE_PORT + { + select_port, + deselect_port + }, + + // B_OBJECT_TYPE_THREAD + { + select_thread, + deselect_thread + } +}; + +static const uint32 kSelectOpsCount = sizeof(kSelectOps) / sizeof(select_ops); + + /*! Clears all bits in the fd_set - since we are using variable sized arrays in the kernel, we can't use the FD_ZERO() macro provided by @@ -150,7 +205,7 @@ common_select(int numFDs, fd_set *readSet, fd_set *writeSet, fd_set *errorSet, sync->set[fd].selected_events |= SELECT_FLAG(B_SELECT_ERROR); if (sync->set[fd].selected_events != 0) { - select_fd(fd, sync, fd, kernel); + select_fd(fd, sync->set + fd, kernel); // array position is the same as the fd for select() } } @@ -177,7 +232,7 @@ common_select(int numFDs, fd_set *readSet, fd_set *writeSet, fd_set *errorSet, locker.Lock(); for (fd = 0; fd < numFDs; fd++) - deselect_fd(fd, sync, fd, kernel); + deselect_fd(fd, sync->set + fd, kernel); PRINT(("common_select(): events deselected\n")); @@ -253,7 +308,7 @@ common_poll(struct pollfd *fds, nfds_t numFDs, bigtime_t timeout, bool kernel) | POLLERR | POLLHUP; sync->set[i].events = 0; - if (select_fd(fd, sync, i, kernel) == B_OK) { + if (select_fd(fd, sync->set + i, kernel) == B_OK) { fds[i].revents = 0; if (sync->set[i].selected_events != 0) count++; @@ -276,7 +331,7 @@ common_poll(struct pollfd *fds, nfds_t numFDs, bigtime_t timeout, bool kernel) locker.Lock(); for (i = 0; i < numFDs; i++) - deselect_fd(fds[i].fd, sync, i, kernel); + deselect_fd(fds[i].fd, sync->set + i, kernel); // collect the events that are happened in the meantime @@ -309,7 +364,94 @@ err: } -// #pragma mark - VFS private +static ssize_t +common_wait_for_objects(object_wait_info* infos, int numInfos, uint32 flags, + bigtime_t timeout, bool kernel) +{ + status_t status = B_OK; + + // allocate sync object + select_sync* sync; + status = create_select_sync(numInfos, sync); + if (status != B_OK) + return status; + + // start selecting objects + + BenaphoreLocker locker(sync->lock); + + ssize_t count = 0; + bool invalid = false; + for (int i = 0; i < numInfos; i++) { + uint16 type = infos[i].type; + int32 object = infos[i].object; + + // initialize events masks + sync->set[i].selected_events = infos[i].events + | B_EVENT_INVALID | B_EVENT_ERROR | B_EVENT_DISCONNECTED; + sync->set[i].events = 0; + + if (type < kSelectOpsCount + && kSelectOps[type].select(object, sync->set + i, kernel) == B_OK) { + infos[i].events = 0; + if (sync->set[i].selected_events != 0) + count++; + } else { + sync->set[i].selected_events = B_EVENT_INVALID; + infos[i].events = B_EVENT_INVALID; + invalid = true; + } + } + + locker.Unlock(); + + if (count < 1) { + put_select_sync(sync); + return B_BAD_VALUE; + } + + if (!invalid) { + status = acquire_sem_etc(sync->sem, 1, B_CAN_INTERRUPT | flags, + timeout); + } + + // deselect objects + + locker.Lock(); + + for (int i = 0; i < numInfos; i++) { + uint16 type = infos[i].type; + + if (type < kSelectOpsCount && (infos[i].events & B_EVENT_INVALID) == 0) + kSelectOps[type].deselect(infos[i].object, sync->set + i, kernel); + } + + // collect the events that are happened in the meantime + + switch (status) { + case B_OK: + count = 0; + for (int i = 0; i < numInfos; i++) { + infos[i].events = sync->set[i].events + & sync->set[i].selected_events; + if (infos[i].events != 0) + count++; + } + break; + default: + // B_INTERRUPTED, B_TIMED_OUT, and B_WOULD_BLOCK + count = status; + break; + } + + locker.Unlock(); + put_select_sync(sync); + + return count; +} + + +// #pragma mark - kernel private status_t @@ -328,17 +470,28 @@ notify_select_events(select_info* info, uint16 events) // only wake up the waiting select()/poll() call if the events // match one of the selected ones if (info->selected_events & events) - return release_sem(info->sync->sem); + return release_sem_etc(info->sync->sem, 1, B_DO_NOT_RESCHEDULE); return B_OK; } +void +notify_select_events_list(select_info* list, uint16 events) +{ + struct select_info* info = list; + while (info != NULL) { + notify_select_events(info, events); + info = info->next; + } +} + + // #pragma mark - public kernel API status_t -notify_select_event(struct selectsync *sync, uint32 /*ref*/, uint8 event) +notify_select_event(struct selectsync *sync, uint8 event) { return notify_select_events((select_info*)sync, SELECT_FLAG(event)); } @@ -348,13 +501,12 @@ notify_select_event(struct selectsync *sync, uint32 /*ref*/, uint8 event) static select_sync_pool_entry * -find_select_sync_pool_entry(select_sync_pool *pool, selectsync *sync, - uint32 ref) +find_select_sync_pool_entry(select_sync_pool *pool, selectsync *sync) { for (SelectSyncPoolEntryList::Iterator it = pool->entries.GetIterator(); it.HasNext();) { select_sync_pool_entry *entry = it.Next(); - if (entry->sync == sync && entry->ref == ref) + if (entry->sync == sync) return entry; } @@ -364,18 +516,16 @@ find_select_sync_pool_entry(select_sync_pool *pool, selectsync *sync, static status_t add_select_sync_pool_entry(select_sync_pool *pool, selectsync *sync, - uint32 ref, uint8 event) + uint8 event) { // check, whether the entry does already exist - select_sync_pool_entry *entry = find_select_sync_pool_entry(pool, sync, - ref); + select_sync_pool_entry *entry = find_select_sync_pool_entry(pool, sync); if (!entry) { entry = new (std::nothrow) select_sync_pool_entry; if (!entry) return B_NO_MEMORY; entry->sync = sync; - entry->ref = ref; entry->events = 0; pool->entries.Add(entry); @@ -389,7 +539,7 @@ add_select_sync_pool_entry(select_sync_pool *pool, selectsync *sync, status_t add_select_sync_pool_entry(select_sync_pool **_pool, selectsync *sync, - uint32 ref, uint8 event) + uint8 event) { // create the pool, if necessary select_sync_pool *pool = *_pool; @@ -402,7 +552,7 @@ add_select_sync_pool_entry(select_sync_pool **_pool, selectsync *sync, } // add the entry - status_t error = add_select_sync_pool_entry(pool, sync, ref, event); + status_t error = add_select_sync_pool_entry(pool, sync, event); // cleanup if (pool->entries.IsEmpty()) { @@ -479,7 +629,7 @@ notify_select_event_pool(select_sync_pool *pool, uint8 event) it.HasNext();) { select_sync_pool_entry *entry = it.Next(); if (entry->events & SELECT_FLAG(event)) - notify_select_event(entry->sync, entry->ref, event); + notify_select_event(entry->sync, event); } } @@ -503,6 +653,14 @@ _kern_poll(struct pollfd *fds, int numFDs, bigtime_t timeout) } +ssize_t +_kern_wait_for_objects(object_wait_info* infos, int numInfos, uint32 flags, + bigtime_t timeout) +{ + return common_wait_for_objects(infos, numInfos, flags, timeout, true); +} + + // #pragma mark - User syscalls @@ -623,3 +781,36 @@ err: return result; } + +ssize_t +_user_wait_for_objects(object_wait_info* userInfos, int numInfos, uint32 flags, + bigtime_t timeout) +{ + if (numInfos < 0) + return B_BAD_VALUE; + + if (userInfos == NULL || !IS_USER_ADDRESS(userInfos)) + return B_BAD_ADDRESS; + + int bytes = sizeof(object_wait_info) * numInfos; + object_wait_info* infos = (object_wait_info*)malloc(bytes); + if (infos == NULL) + return B_NO_MEMORY; + + // copy parameters to kernel space, call the function, and copy the results + // back + ssize_t result; + if (user_memcpy(infos, userInfos, bytes) == B_OK) { + result = common_wait_for_objects(infos, numInfos, flags, timeout, + false); + + if (user_memcpy(userInfos, infos, bytes) != B_OK) + result = B_BAD_ADDRESS; + } else + result = B_BAD_ADDRESS; + + free(infos); + + return result; +} + diff --git a/src/system/libroot/os/Jamfile b/src/system/libroot/os/Jamfile index b0fdf59e06..feace3b2a1 100644 --- a/src/system/libroot/os/Jamfile +++ b/src/system/libroot/os/Jamfile @@ -23,6 +23,7 @@ MergeObject os_main.o : thread.c time.c syscalls.S + wait_for_objects.cpp ; # We need to specify the dependency on the generated syscalls file explicitly. diff --git a/src/system/libroot/os/wait_for_objects.cpp b/src/system/libroot/os/wait_for_objects.cpp new file mode 100644 index 0000000000..0f9fcb0ede --- /dev/null +++ b/src/system/libroot/os/wait_for_objects.cpp @@ -0,0 +1,23 @@ +/* + * Copyright 2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ + +#include +#include + + + +ssize_t +wait_for_objects(object_wait_info* infos, int numInfos) +{ + return _kern_wait_for_objects(infos, numInfos, 0, 0); +} + + +ssize_t +wait_for_objects_etc(object_wait_info* infos, int numInfos, uint32 flags, + bigtime_t timeout) +{ + return _kern_wait_for_objects(infos, numInfos, flags, timeout); +}