diff --git a/Kernel/FileSystem/Plan9FileSystem.cpp b/Kernel/FileSystem/Plan9FileSystem.cpp index 0cad8056b7..3c14ad36c1 100644 --- a/Kernel/FileSystem/Plan9FileSystem.cpp +++ b/Kernel/FileSystem/Plan9FileSystem.cpp @@ -423,7 +423,7 @@ KResult Plan9FS::post_message(Message& message) while (size > 0) { if (!description.can_write()) { - if (Thread::current()->block(description).was_interrupted()) + if (Thread::current()->block(nullptr, description).was_interrupted()) return KResult(-EINTR); } ssize_t nwritten = description.write(data, size); @@ -441,7 +441,7 @@ KResult Plan9FS::do_read(u8* data, size_t size) auto& description = file_description(); while (size > 0) { if (!description.can_read()) { - if (Thread::current()->block(description).was_interrupted()) + if (Thread::current()->block(nullptr, description).was_interrupted()) return KResult(-EINTR); } ssize_t nread = description.read(data, size); @@ -524,7 +524,7 @@ KResult Plan9FS::wait_for_specific_message(u16 tag, Message& out_message) // Block until either: // * Someone else reads the message we're waiting for, and hands it to us; // * Or we become the one to read and dispatch messages. - if (Thread::current()->block(completion).was_interrupted()) { + if (Thread::current()->block(nullptr, completion).was_interrupted()) { LOCKER(m_lock); m_completions.remove(tag); return KResult(-EINTR); diff --git a/Kernel/Net/IPv4Socket.cpp b/Kernel/Net/IPv4Socket.cpp index 80c4d44c03..a8094f7b79 100644 --- a/Kernel/Net/IPv4Socket.cpp +++ b/Kernel/Net/IPv4Socket.cpp @@ -246,7 +246,7 @@ ssize_t IPv4Socket::receive_byte_buffered(FileDescription& description, void* bu return -EAGAIN; locker.unlock(); - auto res = Thread::current()->block(description); + auto res = Thread::current()->block(nullptr, description); locker.lock(); if (!m_can_read) { @@ -296,7 +296,7 @@ ssize_t IPv4Socket::receive_packet_buffered(FileDescription& description, void* } locker.unlock(); - auto res = Thread::current()->block(description); + auto res = Thread::current()->block(nullptr, description); locker.lock(); if (!m_can_read) { diff --git a/Kernel/Net/LocalSocket.cpp b/Kernel/Net/LocalSocket.cpp index 63008e2ab0..1dd6a00b5b 100644 --- a/Kernel/Net/LocalSocket.cpp +++ b/Kernel/Net/LocalSocket.cpp @@ -176,7 +176,7 @@ KResult LocalSocket::connect(FileDescription& description, const sockaddr* addre return KSuccess; } - if (Thread::current()->block(description).was_interrupted()) { + if (Thread::current()->block(nullptr, description).was_interrupted()) { m_connect_side_role = Role::None; return KResult(-EINTR); } @@ -300,7 +300,7 @@ ssize_t LocalSocket::recvfrom(FileDescription& description, void* buffer, size_t return -EAGAIN; } } else if (!can_read(description, 0)) { - if (Thread::current()->block(description).was_interrupted()) + if (Thread::current()->block(nullptr, description).was_interrupted()) return -EINTR; } if (!has_attached_peer(description) && buffer_for_me.is_empty()) diff --git a/Kernel/Net/TCPSocket.cpp b/Kernel/Net/TCPSocket.cpp index 82710eddca..287e27c1e8 100644 --- a/Kernel/Net/TCPSocket.cpp +++ b/Kernel/Net/TCPSocket.cpp @@ -372,7 +372,7 @@ KResult TCPSocket::protocol_connect(FileDescription& description, ShouldBlock sh m_direction = Direction::Outgoing; if (should_block == ShouldBlock::Yes) { - if (Thread::current()->block(description).was_interrupted()) + if (Thread::current()->block(nullptr, description).was_interrupted()) return KResult(-EINTR); ASSERT(setup_state() == SetupState::Completed); if (has_error()) { diff --git a/Kernel/Scheduler.cpp b/Kernel/Scheduler.cpp index 81b601a5af..049002836e 100644 --- a/Kernel/Scheduler.cpp +++ b/Kernel/Scheduler.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -131,26 +132,21 @@ bool Thread::ConnectBlocker::should_unblock(Thread&) Thread::WriteBlocker::WriteBlocker(const FileDescription& description) : FileDescriptionBlocker(description) { +} + +timespec* Thread::WriteBlocker::override_timeout(timespec* timeout) +{ + auto& description = blocked_description(); if (description.is_socket()) { auto& socket = *description.socket(); if (socket.has_send_timeout()) { - timeval deadline = Scheduler::time_since_boot(); - deadline.tv_sec += socket.send_timeout().tv_sec; - deadline.tv_usec += socket.send_timeout().tv_usec; - deadline.tv_sec += (socket.send_timeout().tv_usec / 1000000) * 1; - deadline.tv_usec %= 1000000; - m_deadline = deadline; + timeval_to_timespec(Scheduler::time_since_boot(), m_deadline); + timespec_add_timeval(m_deadline, socket.send_timeout(), m_deadline); + if (!timeout || m_deadline < *timeout) + return &m_deadline; } } -} - -bool Thread::WriteBlocker::should_unblock(Thread& thread, time_t now_sec, long now_usec) -{ - if (m_deadline.has_value()) { - bool timed_out = now_sec > m_deadline.value().tv_sec || (now_sec == m_deadline.value().tv_sec && now_usec >= m_deadline.value().tv_usec); - return timed_out || blocked_description().can_write(); - } - return should_unblock(thread); + return timeout; } bool Thread::WriteBlocker::should_unblock(Thread&) @@ -161,26 +157,21 @@ bool Thread::WriteBlocker::should_unblock(Thread&) Thread::ReadBlocker::ReadBlocker(const FileDescription& description) : FileDescriptionBlocker(description) { +} + +timespec* Thread::ReadBlocker::override_timeout(timespec* timeout) +{ + auto& description = blocked_description(); if (description.is_socket()) { auto& socket = *description.socket(); if (socket.has_receive_timeout()) { - timeval deadline = Scheduler::time_since_boot(); - deadline.tv_sec += socket.receive_timeout().tv_sec; - deadline.tv_usec += socket.receive_timeout().tv_usec; - deadline.tv_sec += (socket.receive_timeout().tv_usec / 1000000) * 1; - deadline.tv_usec %= 1000000; - m_deadline = deadline; + timeval_to_timespec(Scheduler::time_since_boot(), m_deadline); + timespec_add_timeval(m_deadline, socket.receive_timeout(), m_deadline); + if (!timeout || m_deadline < *timeout) + return &m_deadline; } } -} - -bool Thread::ReadBlocker::should_unblock(Thread& thread, time_t now_sec, long now_usec) -{ - if (m_deadline.has_value()) { - bool timed_out = now_sec > m_deadline.value().tv_sec || (now_sec == m_deadline.value().tv_sec && now_usec >= m_deadline.value().tv_usec); - return timed_out || blocked_description().can_read(); - } - return should_unblock(thread); + return timeout; } bool Thread::ReadBlocker::should_unblock(Thread&) @@ -210,24 +201,13 @@ bool Thread::SleepBlocker::should_unblock(Thread&) return m_wakeup_time <= g_uptime; } -Thread::SelectBlocker::SelectBlocker(const timespec& ts, bool select_has_timeout, const FDVector& read_fds, const FDVector& write_fds, const FDVector& except_fds) - : m_select_timeout(ts) - , m_select_has_timeout(select_has_timeout) - , m_select_read_fds(read_fds) +Thread::SelectBlocker::SelectBlocker(const FDVector& read_fds, const FDVector& write_fds, const FDVector& except_fds) + : m_select_read_fds(read_fds) , m_select_write_fds(write_fds) , m_select_exceptional_fds(except_fds) { } -bool Thread::SelectBlocker::should_unblock(Thread& thread, time_t now_sec, long now_usec) -{ - if (m_select_has_timeout) { - if (now_sec > m_select_timeout.tv_sec || (now_sec == m_select_timeout.tv_sec && now_usec * 1000 >= m_select_timeout.tv_nsec)) - return true; - } - return should_unblock(thread); -} - bool Thread::SelectBlocker::should_unblock(Thread& thread) { auto& process = thread.process(); @@ -317,10 +297,16 @@ void Thread::consider_unblock(time_t now_sec, long now_usec) /* don't know, don't care */ return; case Thread::Blocked: + { ASSERT(m_blocker != nullptr); - if (m_blocker->should_unblock(*this, now_sec, now_usec)) + timespec now; + now.tv_sec = now_sec, + now.tv_nsec = now_usec * 1000ull; + bool timed_out = m_blocker_timeout && now >= *m_blocker_timeout; + if (timed_out || m_blocker->should_unblock(*this)) unblock(); return; + } case Thread::Skip1SchedulerPass: set_state(Thread::Skip0SchedulerPasses); return; diff --git a/Kernel/Syscall.cpp b/Kernel/Syscall.cpp index 92f1ff5008..b656e9905d 100644 --- a/Kernel/Syscall.cpp +++ b/Kernel/Syscall.cpp @@ -179,7 +179,7 @@ void syscall_handler(TrapFrame* trap) current_thread->die_if_needed(); if (current_thread->has_unmasked_pending_signals()) - (void)current_thread->block(Thread::SemiPermanentBlocker::Reason::Signal); + (void)current_thread->block(nullptr, Thread::SemiPermanentBlocker::Reason::Signal); } } diff --git a/Kernel/Syscalls/kill.cpp b/Kernel/Syscalls/kill.cpp index 8874f5c7e8..0b05c28e56 100644 --- a/Kernel/Syscalls/kill.cpp +++ b/Kernel/Syscalls/kill.cpp @@ -113,7 +113,7 @@ KResult Process::do_killself(int signal) auto current_thread = Thread::current(); if (!current_thread->should_ignore_signal(signal)) { current_thread->send_signal(signal, this); - (void)current_thread->block(Thread::SemiPermanentBlocker::Reason::Signal); + (void)current_thread->block(nullptr, Thread::SemiPermanentBlocker::Reason::Signal); } return KSuccess; diff --git a/Kernel/Syscalls/read.cpp b/Kernel/Syscalls/read.cpp index 9832b09b76..82b60f8fba 100644 --- a/Kernel/Syscalls/read.cpp +++ b/Kernel/Syscalls/read.cpp @@ -50,7 +50,7 @@ ssize_t Process::sys$read(int fd, Userspace buffer, ssize_t size) return -EISDIR; if (description->is_blocking()) { if (!description->can_read()) { - if (Thread::current()->block(*description).was_interrupted()) + if (Thread::current()->block(nullptr, *description).was_interrupted()) return -EINTR; if (!description->can_read()) return -EAGAIN; diff --git a/Kernel/Syscalls/select.cpp b/Kernel/Syscalls/select.cpp index f3b68616a4..1308af4d7b 100644 --- a/Kernel/Syscalls/select.cpp +++ b/Kernel/Syscalls/select.cpp @@ -105,7 +105,7 @@ int Process::sys$select(const Syscall::SC_select_params* params) #endif if (!timeout || select_has_timeout) { - if (current_thread->block(computed_timeout, select_has_timeout, rfds, wfds, efds).was_interrupted()) + if (current_thread->block(select_has_timeout ? &computed_timeout : nullptr, rfds, wfds, efds).was_interrupted()) return -EINTR; // While we blocked, the process lock was dropped. This gave other threads // the opportunity to mess with the memory. For example, it could free the @@ -191,7 +191,7 @@ int Process::sys$poll(const Syscall::SC_poll_params* params) #endif if (!timeout || has_timeout) { - if (current_thread->block(actual_timeout, has_timeout, rfds, wfds, Thread::SelectBlocker::FDVector()).was_interrupted()) + if (current_thread->block(has_timeout ? &actual_timeout : nullptr, rfds, wfds, Thread::SelectBlocker::FDVector()).was_interrupted()) return -EINTR; } diff --git a/Kernel/Syscalls/socket.cpp b/Kernel/Syscalls/socket.cpp index 53f90d0794..dcc23c0c95 100644 --- a/Kernel/Syscalls/socket.cpp +++ b/Kernel/Syscalls/socket.cpp @@ -118,7 +118,7 @@ int Process::sys$accept(int accepting_socket_fd, sockaddr* user_address, socklen if (!socket.can_accept()) { if (accepting_socket_description->is_blocking()) { - if (Thread::current()->block(*accepting_socket_description).was_interrupted()) + if (Thread::current()->block(nullptr, *accepting_socket_description).was_interrupted()) return -EINTR; } else { return -EAGAIN; diff --git a/Kernel/Syscalls/thread.cpp b/Kernel/Syscalls/thread.cpp index 4917939915..a458072f97 100644 --- a/Kernel/Syscalls/thread.cpp +++ b/Kernel/Syscalls/thread.cpp @@ -146,7 +146,7 @@ int Process::sys$join_thread(int tid, void** exit_value) // NOTE: pthread_join() cannot be interrupted by signals. Only by death. for (;;) { - auto result = current_thread->block(*thread, joinee_exit_value); + auto result = current_thread->block(nullptr, *thread, joinee_exit_value); if (result == Thread::BlockResult::InterruptedByDeath) { // NOTE: This cleans things up so that Thread::finalize() won't // get confused about a missing joiner when finalizing the joinee. diff --git a/Kernel/Syscalls/waitid.cpp b/Kernel/Syscalls/waitid.cpp index 093a549ba4..dd55a75b32 100644 --- a/Kernel/Syscalls/waitid.cpp +++ b/Kernel/Syscalls/waitid.cpp @@ -48,7 +48,7 @@ KResultOr Process::do_waitid(idtype_t idtype, int id, int options) return KResult(-EINVAL); } - if (Thread::current()->block(options, waitee_pid).was_interrupted()) + if (Thread::current()->block(nullptr, options, waitee_pid).was_interrupted()) return KResult(-EINTR); ScopedSpinLock lock(g_processes_lock); diff --git a/Kernel/Syscalls/write.cpp b/Kernel/Syscalls/write.cpp index 87e6a98f42..438d9e37a1 100644 --- a/Kernel/Syscalls/write.cpp +++ b/Kernel/Syscalls/write.cpp @@ -100,7 +100,7 @@ ssize_t Process::do_write(FileDescription& description, const u8* data, int data #ifdef IO_DEBUG dbg() << "block write on " << description.absolute_path(); #endif - if (Thread::current()->block(description).was_interrupted()) { + if (Thread::current()->block(nullptr, description).was_interrupted()) { if (nwritten == 0) return -EINTR; } diff --git a/Kernel/Thread.cpp b/Kernel/Thread.cpp index 3736b6fc74..989ce711f4 100644 --- a/Kernel/Thread.cpp +++ b/Kernel/Thread.cpp @@ -209,7 +209,7 @@ u64 Thread::sleep(u64 ticks) { ASSERT(state() == Thread::Running); u64 wakeup_time = g_uptime + ticks; - auto ret = Thread::current()->block(wakeup_time); + auto ret = Thread::current()->block(nullptr, wakeup_time); if (wakeup_time > g_uptime) { ASSERT(ret.was_interrupted()); } @@ -219,7 +219,7 @@ u64 Thread::sleep(u64 ticks) u64 Thread::sleep_until(u64 wakeup_time) { ASSERT(state() == Thread::Running); - auto ret = Thread::current()->block(wakeup_time); + auto ret = Thread::current()->block(nullptr, wakeup_time); if (wakeup_time > g_uptime) ASSERT(ret.was_interrupted()); return wakeup_time; diff --git a/Kernel/Thread.h b/Kernel/Thread.h index dfb6d540d7..9ecc94e88a 100644 --- a/Kernel/Thread.h +++ b/Kernel/Thread.h @@ -126,13 +126,10 @@ public: class Blocker { public: virtual ~Blocker() { } - virtual bool should_unblock(Thread& thread, time_t, long) - { - return should_unblock(thread); - } virtual bool should_unblock(Thread&) = 0; virtual const char* state_string() const = 0; virtual bool is_reason_signal() const { return false; } + virtual timespec* override_timeout(timespec* timeout) { return timeout; } void set_interrupted_by_death() { m_was_interrupted_by_death = true; } bool was_interrupted_by_death() const { return m_was_interrupted_by_death; } void set_interrupted_by_signal() { m_was_interrupted_while_blocked = true; } @@ -184,23 +181,23 @@ public: class WriteBlocker final : public FileDescriptionBlocker { public: explicit WriteBlocker(const FileDescription&); - virtual bool should_unblock(Thread&, time_t, long) override; virtual bool should_unblock(Thread&) override; virtual const char* state_string() const override { return "Writing"; } + virtual timespec* override_timeout(timespec*) override; private: - Optional m_deadline; + timespec m_deadline; }; class ReadBlocker final : public FileDescriptionBlocker { public: explicit ReadBlocker(const FileDescription&); - virtual bool should_unblock(Thread&, time_t, long) override; virtual bool should_unblock(Thread&) override; virtual const char* state_string() const override { return "Reading"; } + virtual timespec* override_timeout(timespec*) override; private: - Optional m_deadline; + timespec m_deadline; }; class ConditionBlocker final : public Blocker { @@ -227,14 +224,11 @@ public: class SelectBlocker final : public Blocker { public: typedef Vector FDVector; - SelectBlocker(const timespec& ts, bool select_has_timeout, const FDVector& read_fds, const FDVector& write_fds, const FDVector& except_fds); - virtual bool should_unblock(Thread&, time_t, long) override; + SelectBlocker(const FDVector& read_fds, const FDVector& write_fds, const FDVector& except_fds); virtual bool should_unblock(Thread&) override; virtual const char* state_string() const override { return "Selecting"; } private: - timespec m_select_timeout; - bool m_select_has_timeout { false }; const FDVector& m_select_read_fds; const FDVector& m_select_write_fds; const FDVector& m_select_exceptional_fds; @@ -345,7 +339,7 @@ public: }; template - [[nodiscard]] BlockResult block(Args&&... args) + [[nodiscard]] BlockResult block(timespec* timeout, Args&&... args) { T t(forward(args)...); @@ -361,6 +355,7 @@ public: } m_blocker = &t; + m_blocker_timeout = t.override_timeout(timeout); set_state(Thread::Blocked); } @@ -373,6 +368,7 @@ public: // Remove ourselves... m_blocker = nullptr; + m_blocker_timeout = nullptr; if (t.was_interrupted_by_signal()) return BlockResult::InterruptedBySignal; @@ -385,7 +381,7 @@ public: [[nodiscard]] BlockResult block_until(const char* state_string, Function&& condition) { - return block(state_string, move(condition)); + return block(nullptr, state_string, move(condition)); } BlockResult wait_on(WaitQueue& queue, const char* reason, timeval* timeout = nullptr, Atomic* lock = nullptr, Thread* beneficiary = nullptr); @@ -552,6 +548,7 @@ private: size_t m_thread_specific_region_size { 0 }; SignalActionData m_signal_action_data[32]; Blocker* m_blocker { nullptr }; + timespec* m_blocker_timeout { nullptr }; const char* m_wait_reason { nullptr }; bool m_is_active { false };