Przeglądaj źródła

Kernel: Consolidate timeout logic

Allow passing in an optional timeout to Thread::block and move
the timeout check out of Thread::Blocker. This way all Blockers
implicitly support timeouts and don't need to implement it
themselves. Do however allow them to override timeouts (e.g.
for sockets).
Tom 5 lat temu
rodzic
commit
f4a5c9b6c2

+ 3 - 3
Kernel/FileSystem/Plan9FileSystem.cpp

@@ -423,7 +423,7 @@ KResult Plan9FS::post_message(Message& message)
 
 
     while (size > 0) {
     while (size > 0) {
         if (!description.can_write()) {
         if (!description.can_write()) {
-            if (Thread::current()->block<Thread::WriteBlocker>(description).was_interrupted())
+            if (Thread::current()->block<Thread::WriteBlocker>(nullptr, description).was_interrupted())
                 return KResult(-EINTR);
                 return KResult(-EINTR);
         }
         }
         ssize_t nwritten = description.write(data, size);
         ssize_t nwritten = description.write(data, size);
@@ -441,7 +441,7 @@ KResult Plan9FS::do_read(u8* data, size_t size)
     auto& description = file_description();
     auto& description = file_description();
     while (size > 0) {
     while (size > 0) {
         if (!description.can_read()) {
         if (!description.can_read()) {
-            if (Thread::current()->block<Thread::ReadBlocker>(description).was_interrupted())
+            if (Thread::current()->block<Thread::ReadBlocker>(nullptr, description).was_interrupted())
                 return KResult(-EINTR);
                 return KResult(-EINTR);
         }
         }
         ssize_t nread = description.read(data, size);
         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:
     // Block until either:
     // * Someone else reads the message we're waiting for, and hands it to us;
     // * Someone else reads the message we're waiting for, and hands it to us;
     // * Or we become the one to read and dispatch messages.
     // * Or we become the one to read and dispatch messages.
-    if (Thread::current()->block<Plan9FS::Blocker>(completion).was_interrupted()) {
+    if (Thread::current()->block<Plan9FS::Blocker>(nullptr, completion).was_interrupted()) {
         LOCKER(m_lock);
         LOCKER(m_lock);
         m_completions.remove(tag);
         m_completions.remove(tag);
         return KResult(-EINTR);
         return KResult(-EINTR);

+ 2 - 2
Kernel/Net/IPv4Socket.cpp

@@ -246,7 +246,7 @@ ssize_t IPv4Socket::receive_byte_buffered(FileDescription& description, void* bu
             return -EAGAIN;
             return -EAGAIN;
 
 
         locker.unlock();
         locker.unlock();
-        auto res = Thread::current()->block<Thread::ReadBlocker>(description);
+        auto res = Thread::current()->block<Thread::ReadBlocker>(nullptr, description);
         locker.lock();
         locker.lock();
 
 
         if (!m_can_read) {
         if (!m_can_read) {
@@ -296,7 +296,7 @@ ssize_t IPv4Socket::receive_packet_buffered(FileDescription& description, void*
         }
         }
 
 
         locker.unlock();
         locker.unlock();
-        auto res = Thread::current()->block<Thread::ReadBlocker>(description);
+        auto res = Thread::current()->block<Thread::ReadBlocker>(nullptr, description);
         locker.lock();
         locker.lock();
 
 
         if (!m_can_read) {
         if (!m_can_read) {

+ 2 - 2
Kernel/Net/LocalSocket.cpp

@@ -176,7 +176,7 @@ KResult LocalSocket::connect(FileDescription& description, const sockaddr* addre
         return KSuccess;
         return KSuccess;
     }
     }
 
 
-    if (Thread::current()->block<Thread::ConnectBlocker>(description).was_interrupted()) {
+    if (Thread::current()->block<Thread::ConnectBlocker>(nullptr, description).was_interrupted()) {
         m_connect_side_role = Role::None;
         m_connect_side_role = Role::None;
         return KResult(-EINTR);
         return KResult(-EINTR);
     }
     }
@@ -300,7 +300,7 @@ ssize_t LocalSocket::recvfrom(FileDescription& description, void* buffer, size_t
             return -EAGAIN;
             return -EAGAIN;
         }
         }
     } else if (!can_read(description, 0)) {
     } else if (!can_read(description, 0)) {
-        if (Thread::current()->block<Thread::ReadBlocker>(description).was_interrupted())
+        if (Thread::current()->block<Thread::ReadBlocker>(nullptr, description).was_interrupted())
             return -EINTR;
             return -EINTR;
     }
     }
     if (!has_attached_peer(description) && buffer_for_me.is_empty())
     if (!has_attached_peer(description) && buffer_for_me.is_empty())

+ 1 - 1
Kernel/Net/TCPSocket.cpp

@@ -372,7 +372,7 @@ KResult TCPSocket::protocol_connect(FileDescription& description, ShouldBlock sh
     m_direction = Direction::Outgoing;
     m_direction = Direction::Outgoing;
 
 
     if (should_block == ShouldBlock::Yes) {
     if (should_block == ShouldBlock::Yes) {
-        if (Thread::current()->block<Thread::ConnectBlocker>(description).was_interrupted())
+        if (Thread::current()->block<Thread::ConnectBlocker>(nullptr, description).was_interrupted())
             return KResult(-EINTR);
             return KResult(-EINTR);
         ASSERT(setup_state() == SetupState::Completed);
         ASSERT(setup_state() == SetupState::Completed);
         if (has_error()) {
         if (has_error()) {

+ 30 - 44
Kernel/Scheduler.cpp

@@ -27,6 +27,7 @@
 #include <AK/QuickSort.h>
 #include <AK/QuickSort.h>
 #include <AK/ScopeGuard.h>
 #include <AK/ScopeGuard.h>
 #include <AK/TemporaryChange.h>
 #include <AK/TemporaryChange.h>
+#include <AK/Time.h>
 #include <Kernel/FileSystem/FileDescription.h>
 #include <Kernel/FileSystem/FileDescription.h>
 #include <Kernel/Net/Socket.h>
 #include <Kernel/Net/Socket.h>
 #include <Kernel/Process.h>
 #include <Kernel/Process.h>
@@ -131,26 +132,21 @@ bool Thread::ConnectBlocker::should_unblock(Thread&)
 Thread::WriteBlocker::WriteBlocker(const FileDescription& description)
 Thread::WriteBlocker::WriteBlocker(const FileDescription& description)
     : FileDescriptionBlocker(description)
     : FileDescriptionBlocker(description)
 {
 {
+}
+
+timespec* Thread::WriteBlocker::override_timeout(timespec* timeout)
+{
+    auto& description =  blocked_description();
     if (description.is_socket()) {
     if (description.is_socket()) {
         auto& socket = *description.socket();
         auto& socket = *description.socket();
         if (socket.has_send_timeout()) {
         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&)
 bool Thread::WriteBlocker::should_unblock(Thread&)
@@ -161,26 +157,21 @@ bool Thread::WriteBlocker::should_unblock(Thread&)
 Thread::ReadBlocker::ReadBlocker(const FileDescription& description)
 Thread::ReadBlocker::ReadBlocker(const FileDescription& description)
     : FileDescriptionBlocker(description)
     : FileDescriptionBlocker(description)
 {
 {
+}
+
+timespec* Thread::ReadBlocker::override_timeout(timespec* timeout)
+{
+    auto& description = blocked_description();
     if (description.is_socket()) {
     if (description.is_socket()) {
         auto& socket = *description.socket();
         auto& socket = *description.socket();
         if (socket.has_receive_timeout()) {
         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&)
 bool Thread::ReadBlocker::should_unblock(Thread&)
@@ -210,24 +201,13 @@ bool Thread::SleepBlocker::should_unblock(Thread&)
     return m_wakeup_time <= g_uptime;
     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_write_fds(write_fds)
     , m_select_exceptional_fds(except_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)
 bool Thread::SelectBlocker::should_unblock(Thread& thread)
 {
 {
     auto& process = thread.process();
     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 */
         /* don't know, don't care */
         return;
         return;
     case Thread::Blocked:
     case Thread::Blocked:
+    {
         ASSERT(m_blocker != nullptr);
         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();
             unblock();
         return;
         return;
+    }
     case Thread::Skip1SchedulerPass:
     case Thread::Skip1SchedulerPass:
         set_state(Thread::Skip0SchedulerPasses);
         set_state(Thread::Skip0SchedulerPasses);
         return;
         return;

+ 1 - 1
Kernel/Syscall.cpp

@@ -179,7 +179,7 @@ void syscall_handler(TrapFrame* trap)
     current_thread->die_if_needed();
     current_thread->die_if_needed();
 
 
     if (current_thread->has_unmasked_pending_signals())
     if (current_thread->has_unmasked_pending_signals())
-        (void)current_thread->block<Thread::SemiPermanentBlocker>(Thread::SemiPermanentBlocker::Reason::Signal);
+        (void)current_thread->block<Thread::SemiPermanentBlocker>(nullptr, Thread::SemiPermanentBlocker::Reason::Signal);
 }
 }
 
 
 }
 }

+ 1 - 1
Kernel/Syscalls/kill.cpp

@@ -113,7 +113,7 @@ KResult Process::do_killself(int signal)
     auto current_thread = Thread::current();
     auto current_thread = Thread::current();
     if (!current_thread->should_ignore_signal(signal)) {
     if (!current_thread->should_ignore_signal(signal)) {
         current_thread->send_signal(signal, this);
         current_thread->send_signal(signal, this);
-        (void)current_thread->block<Thread::SemiPermanentBlocker>(Thread::SemiPermanentBlocker::Reason::Signal);
+        (void)current_thread->block<Thread::SemiPermanentBlocker>(nullptr, Thread::SemiPermanentBlocker::Reason::Signal);
     }
     }
 
 
     return KSuccess;
     return KSuccess;

+ 1 - 1
Kernel/Syscalls/read.cpp

@@ -50,7 +50,7 @@ ssize_t Process::sys$read(int fd, Userspace<u8*> buffer, ssize_t size)
         return -EISDIR;
         return -EISDIR;
     if (description->is_blocking()) {
     if (description->is_blocking()) {
         if (!description->can_read()) {
         if (!description->can_read()) {
-            if (Thread::current()->block<Thread::ReadBlocker>(*description).was_interrupted())
+            if (Thread::current()->block<Thread::ReadBlocker>(nullptr, *description).was_interrupted())
                 return -EINTR;
                 return -EINTR;
             if (!description->can_read())
             if (!description->can_read())
                 return -EAGAIN;
                 return -EAGAIN;

+ 2 - 2
Kernel/Syscalls/select.cpp

@@ -105,7 +105,7 @@ int Process::sys$select(const Syscall::SC_select_params* params)
 #endif
 #endif
 
 
     if (!timeout || select_has_timeout) {
     if (!timeout || select_has_timeout) {
-        if (current_thread->block<Thread::SelectBlocker>(computed_timeout, select_has_timeout, rfds, wfds, efds).was_interrupted())
+        if (current_thread->block<Thread::SelectBlocker>(select_has_timeout ? &computed_timeout : nullptr, rfds, wfds, efds).was_interrupted())
             return -EINTR;
             return -EINTR;
         // While we blocked, the process lock was dropped. This gave other threads
         // 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
         // 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
 #endif
 
 
     if (!timeout || has_timeout) {
     if (!timeout || has_timeout) {
-        if (current_thread->block<Thread::SelectBlocker>(actual_timeout, has_timeout, rfds, wfds, Thread::SelectBlocker::FDVector()).was_interrupted())
+        if (current_thread->block<Thread::SelectBlocker>(has_timeout ? &actual_timeout : nullptr, rfds, wfds, Thread::SelectBlocker::FDVector()).was_interrupted())
             return -EINTR;
             return -EINTR;
     }
     }
 
 

+ 1 - 1
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 (!socket.can_accept()) {
         if (accepting_socket_description->is_blocking()) {
         if (accepting_socket_description->is_blocking()) {
-            if (Thread::current()->block<Thread::AcceptBlocker>(*accepting_socket_description).was_interrupted())
+            if (Thread::current()->block<Thread::AcceptBlocker>(nullptr, *accepting_socket_description).was_interrupted())
                 return -EINTR;
                 return -EINTR;
         } else {
         } else {
             return -EAGAIN;
             return -EAGAIN;

+ 1 - 1
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.
     // NOTE: pthread_join() cannot be interrupted by signals. Only by death.
     for (;;) {
     for (;;) {
-        auto result = current_thread->block<Thread::JoinBlocker>(*thread, joinee_exit_value);
+        auto result = current_thread->block<Thread::JoinBlocker>(nullptr, *thread, joinee_exit_value);
         if (result == Thread::BlockResult::InterruptedByDeath) {
         if (result == Thread::BlockResult::InterruptedByDeath) {
             // NOTE: This cleans things up so that Thread::finalize() won't
             // NOTE: This cleans things up so that Thread::finalize() won't
             //       get confused about a missing joiner when finalizing the joinee.
             //       get confused about a missing joiner when finalizing the joinee.

+ 1 - 1
Kernel/Syscalls/waitid.cpp

@@ -48,7 +48,7 @@ KResultOr<siginfo_t> Process::do_waitid(idtype_t idtype, int id, int options)
         return KResult(-EINVAL);
         return KResult(-EINVAL);
     }
     }
 
 
-    if (Thread::current()->block<Thread::WaitBlocker>(options, waitee_pid).was_interrupted())
+    if (Thread::current()->block<Thread::WaitBlocker>(nullptr, options, waitee_pid).was_interrupted())
         return KResult(-EINTR);
         return KResult(-EINTR);
 
 
     ScopedSpinLock lock(g_processes_lock);
     ScopedSpinLock lock(g_processes_lock);

+ 1 - 1
Kernel/Syscalls/write.cpp

@@ -100,7 +100,7 @@ ssize_t Process::do_write(FileDescription& description, const u8* data, int data
 #ifdef IO_DEBUG
 #ifdef IO_DEBUG
             dbg() << "block write on " << description.absolute_path();
             dbg() << "block write on " << description.absolute_path();
 #endif
 #endif
-            if (Thread::current()->block<Thread::WriteBlocker>(description).was_interrupted()) {
+            if (Thread::current()->block<Thread::WriteBlocker>(nullptr, description).was_interrupted()) {
                 if (nwritten == 0)
                 if (nwritten == 0)
                     return -EINTR;
                     return -EINTR;
             }
             }

+ 2 - 2
Kernel/Thread.cpp

@@ -209,7 +209,7 @@ u64 Thread::sleep(u64 ticks)
 {
 {
     ASSERT(state() == Thread::Running);
     ASSERT(state() == Thread::Running);
     u64 wakeup_time = g_uptime + ticks;
     u64 wakeup_time = g_uptime + ticks;
-    auto ret = Thread::current()->block<Thread::SleepBlocker>(wakeup_time);
+    auto ret = Thread::current()->block<Thread::SleepBlocker>(nullptr, wakeup_time);
     if (wakeup_time > g_uptime) {
     if (wakeup_time > g_uptime) {
         ASSERT(ret.was_interrupted());
         ASSERT(ret.was_interrupted());
     }
     }
@@ -219,7 +219,7 @@ u64 Thread::sleep(u64 ticks)
 u64 Thread::sleep_until(u64 wakeup_time)
 u64 Thread::sleep_until(u64 wakeup_time)
 {
 {
     ASSERT(state() == Thread::Running);
     ASSERT(state() == Thread::Running);
-    auto ret = Thread::current()->block<Thread::SleepBlocker>(wakeup_time);
+    auto ret = Thread::current()->block<Thread::SleepBlocker>(nullptr, wakeup_time);
     if (wakeup_time > g_uptime)
     if (wakeup_time > g_uptime)
         ASSERT(ret.was_interrupted());
         ASSERT(ret.was_interrupted());
     return wakeup_time;
     return wakeup_time;

+ 11 - 14
Kernel/Thread.h

@@ -126,13 +126,10 @@ public:
     class Blocker {
     class Blocker {
     public:
     public:
         virtual ~Blocker() { }
         virtual ~Blocker() { }
-        virtual bool should_unblock(Thread& thread, time_t, long)
-        {
-            return should_unblock(thread);
-        }
         virtual bool should_unblock(Thread&) = 0;
         virtual bool should_unblock(Thread&) = 0;
         virtual const char* state_string() const = 0;
         virtual const char* state_string() const = 0;
         virtual bool is_reason_signal() const { return false; }
         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; }
         void set_interrupted_by_death() { m_was_interrupted_by_death = true; }
         bool was_interrupted_by_death() const { return m_was_interrupted_by_death; }
         bool was_interrupted_by_death() const { return m_was_interrupted_by_death; }
         void set_interrupted_by_signal() { m_was_interrupted_while_blocked = true; }
         void set_interrupted_by_signal() { m_was_interrupted_while_blocked = true; }
@@ -184,23 +181,23 @@ public:
     class WriteBlocker final : public FileDescriptionBlocker {
     class WriteBlocker final : public FileDescriptionBlocker {
     public:
     public:
         explicit WriteBlocker(const FileDescription&);
         explicit WriteBlocker(const FileDescription&);
-        virtual bool should_unblock(Thread&, time_t, long) override;
         virtual bool should_unblock(Thread&) override;
         virtual bool should_unblock(Thread&) override;
         virtual const char* state_string() const override { return "Writing"; }
         virtual const char* state_string() const override { return "Writing"; }
+        virtual timespec* override_timeout(timespec*) override;
 
 
     private:
     private:
-        Optional<timeval> m_deadline;
+        timespec m_deadline;
     };
     };
 
 
     class ReadBlocker final : public FileDescriptionBlocker {
     class ReadBlocker final : public FileDescriptionBlocker {
     public:
     public:
         explicit ReadBlocker(const FileDescription&);
         explicit ReadBlocker(const FileDescription&);
-        virtual bool should_unblock(Thread&, time_t, long) override;
         virtual bool should_unblock(Thread&) override;
         virtual bool should_unblock(Thread&) override;
         virtual const char* state_string() const override { return "Reading"; }
         virtual const char* state_string() const override { return "Reading"; }
+        virtual timespec* override_timeout(timespec*) override;
 
 
     private:
     private:
-        Optional<timeval> m_deadline;
+        timespec m_deadline;
     };
     };
 
 
     class ConditionBlocker final : public Blocker {
     class ConditionBlocker final : public Blocker {
@@ -227,14 +224,11 @@ public:
     class SelectBlocker final : public Blocker {
     class SelectBlocker final : public Blocker {
     public:
     public:
         typedef Vector<int, FD_SETSIZE> FDVector;
         typedef Vector<int, FD_SETSIZE> 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 bool should_unblock(Thread&) override;
         virtual const char* state_string() const override { return "Selecting"; }
         virtual const char* state_string() const override { return "Selecting"; }
 
 
     private:
     private:
-        timespec m_select_timeout;
-        bool m_select_has_timeout { false };
         const FDVector& m_select_read_fds;
         const FDVector& m_select_read_fds;
         const FDVector& m_select_write_fds;
         const FDVector& m_select_write_fds;
         const FDVector& m_select_exceptional_fds;
         const FDVector& m_select_exceptional_fds;
@@ -345,7 +339,7 @@ public:
     };
     };
 
 
     template<typename T, class... Args>
     template<typename T, class... Args>
-    [[nodiscard]] BlockResult block(Args&&... args)
+    [[nodiscard]] BlockResult block(timespec* timeout, Args&&... args)
     {
     {
         T t(forward<Args>(args)...);
         T t(forward<Args>(args)...);
 
 
@@ -361,6 +355,7 @@ public:
             }
             }
 
 
             m_blocker = &t;
             m_blocker = &t;
+            m_blocker_timeout = t.override_timeout(timeout);
             set_state(Thread::Blocked);
             set_state(Thread::Blocked);
         }
         }
 
 
@@ -373,6 +368,7 @@ public:
 
 
         // Remove ourselves...
         // Remove ourselves...
         m_blocker = nullptr;
         m_blocker = nullptr;
+        m_blocker_timeout = nullptr;
 
 
         if (t.was_interrupted_by_signal())
         if (t.was_interrupted_by_signal())
             return BlockResult::InterruptedBySignal;
             return BlockResult::InterruptedBySignal;
@@ -385,7 +381,7 @@ public:
 
 
     [[nodiscard]] BlockResult block_until(const char* state_string, Function<bool()>&& condition)
     [[nodiscard]] BlockResult block_until(const char* state_string, Function<bool()>&& condition)
     {
     {
-        return block<ConditionBlocker>(state_string, move(condition));
+        return block<ConditionBlocker>(nullptr, state_string, move(condition));
     }
     }
 
 
     BlockResult wait_on(WaitQueue& queue, const char* reason, timeval* timeout = nullptr, Atomic<bool>* lock = nullptr, Thread* beneficiary = nullptr);
     BlockResult wait_on(WaitQueue& queue, const char* reason, timeval* timeout = nullptr, Atomic<bool>* lock = nullptr, Thread* beneficiary = nullptr);
@@ -552,6 +548,7 @@ private:
     size_t m_thread_specific_region_size { 0 };
     size_t m_thread_specific_region_size { 0 };
     SignalActionData m_signal_action_data[32];
     SignalActionData m_signal_action_data[32];
     Blocker* m_blocker { nullptr };
     Blocker* m_blocker { nullptr };
+    timespec* m_blocker_timeout { nullptr };
     const char* m_wait_reason { nullptr };
     const char* m_wait_reason { nullptr };
 
 
     bool m_is_active { false };
     bool m_is_active { false };