Kernel: read()/write() should respect timeouts when used on a sockets

Move timeout management to the ReadBlocker and WriteBlocker classes.
Also get rid of the specialized ReceiveBlocker since it no longer does
anything that ReadBlocker can't do.
This commit is contained in:
Andreas Kling 2020-01-26 17:54:23 +01:00
parent d1721c761e
commit 137a45dff2
7 changed files with 48 additions and 57 deletions

View file

@ -264,8 +264,7 @@ ssize_t IPv4Socket::recvfrom(FileDescription& description, void* buffer, size_t
return -EAGAIN;
}
load_receive_deadline();
auto res = current->block<Thread::ReceiveBlocker>(description);
auto res = current->block<Thread::ReadBlocker>(description);
LOCKER(lock());
if (!m_can_read) {
@ -312,8 +311,7 @@ ssize_t IPv4Socket::recvfrom(FileDescription& description, void* buffer, size_t
return 0;
}
load_receive_deadline();
auto res = current->block<Thread::ReceiveBlocker>(description);
auto res = current->block<Thread::ReadBlocker>(description);
LOCKER(lock());
if (!m_can_read) {

View file

@ -296,7 +296,7 @@ ssize_t LocalSocket::recvfrom(FileDescription& description, void* buffer, size_t
return -EAGAIN;
}
} else if (!can_read(description)) {
auto result = current->block<Thread::ReceiveBlocker>(description);
auto result = current->block<Thread::ReadBlocker>(description);
if (result != Thread::BlockResult::WokeNormally)
return -EINTR;
}

View file

@ -147,24 +147,6 @@ KResult Socket::getsockopt(FileDescription&, int level, int option, void* value,
}
}
void Socket::load_receive_deadline()
{
kgettimeofday(m_receive_deadline);
m_receive_deadline.tv_sec += m_receive_timeout.tv_sec;
m_receive_deadline.tv_usec += m_receive_timeout.tv_usec;
m_receive_deadline.tv_sec += (m_send_timeout.tv_usec / 1000000) * 1;
m_receive_deadline.tv_usec %= 1000000;
}
void Socket::load_send_deadline()
{
kgettimeofday(m_send_deadline);
m_send_deadline.tv_sec += m_send_timeout.tv_sec;
m_send_deadline.tv_usec += m_send_timeout.tv_usec;
m_send_deadline.tv_sec += (m_send_timeout.tv_usec / 1000000) * 1;
m_send_deadline.tv_usec %= 1000000;
}
ssize_t Socket::read(FileDescription& description, u8* buffer, ssize_t size)
{
return recvfrom(description, buffer, size, 0, nullptr, 0);

View file

@ -112,9 +112,6 @@ public:
uid_t acceptor_uid() const { return m_acceptor.uid; }
gid_t acceptor_gid() const { return m_acceptor.gid; }
timeval receive_deadline() const { return m_receive_deadline; }
timeval send_deadline() const { return m_send_deadline; }
Lock& lock() { return m_lock; }
// ^File
@ -122,14 +119,18 @@ public:
virtual ssize_t write(FileDescription&, const u8*, ssize_t) override final;
virtual String absolute_path(const FileDescription&) const override = 0;
bool has_receive_timeout() const { return m_receive_timeout.tv_sec || m_receive_timeout.tv_usec; }
const timeval& receive_timeout() const { return m_receive_timeout; }
bool has_send_timeout() const { return m_send_timeout.tv_sec || m_send_timeout.tv_usec; }
const timeval& send_timeout() const { return m_send_timeout; }
protected:
Socket(int domain, int type, int protocol);
KResult queue_connection_from(NonnullRefPtr<Socket>);
void load_receive_deadline();
void load_send_deadline();
int backlog() const { return m_backlog; }
void set_backlog(int backlog) { m_backlog = backlog; }
@ -156,9 +157,6 @@ private:
timeval m_receive_timeout { 0, 0 };
timeval m_send_timeout { 0, 0 };
timeval m_receive_deadline { 0, 0 };
timeval m_send_deadline { 0, 0 };
NonnullRefPtrVector<Socket> m_pending;
};

View file

@ -1658,6 +1658,8 @@ ssize_t Process::sys$read(int fd, u8* buffer, ssize_t size)
if (!description->can_read()) {
if (current->block<Thread::ReadBlocker>(*description) != Thread::BlockResult::WokeNormally)
return -EINTR;
if (!description->can_read())
return -EAGAIN;
}
}
return description->read(buffer, size);

View file

@ -118,21 +118,6 @@ bool Thread::AcceptBlocker::should_unblock(Thread&, time_t, long)
return socket.can_accept();
}
Thread::ReceiveBlocker::ReceiveBlocker(const FileDescription& description)
: FileDescriptionBlocker(description)
{
}
bool Thread::ReceiveBlocker::should_unblock(Thread&, time_t now_sec, long now_usec)
{
auto& socket = *blocked_description().socket();
// FIXME: Block until the amount of data wanted is available.
bool timed_out = now_sec > socket.receive_deadline().tv_sec || (now_sec == socket.receive_deadline().tv_sec && now_usec >= socket.receive_deadline().tv_usec);
if (timed_out || blocked_description().can_read())
return true;
return false;
}
Thread::ConnectBlocker::ConnectBlocker(const FileDescription& description)
: FileDescriptionBlocker(description)
{
@ -147,21 +132,50 @@ bool Thread::ConnectBlocker::should_unblock(Thread&, time_t, long)
Thread::WriteBlocker::WriteBlocker(const FileDescription& description)
: FileDescriptionBlocker(description)
{
if (description.is_socket()) {
auto& socket = *description.socket();
if (socket.has_send_timeout()) {
timeval deadline = kgettimeofday();
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;
}
}
}
bool Thread::WriteBlocker::should_unblock(Thread&, time_t, long)
bool Thread::WriteBlocker::should_unblock(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 blocked_description().can_write();
}
Thread::ReadBlocker::ReadBlocker(const FileDescription& description)
: FileDescriptionBlocker(description)
{
if (description.is_socket()) {
auto& socket = *description.socket();
if (socket.has_receive_timeout()) {
timeval deadline = kgettimeofday();
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;
}
}
}
bool Thread::ReadBlocker::should_unblock(Thread&, time_t, long)
bool Thread::ReadBlocker::should_unblock(Thread&, time_t now_sec, long now_usec)
{
// FIXME: Block until the amount of data wanted is available.
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 blocked_description().can_read();
}

View file

@ -167,13 +167,6 @@ public:
virtual const char* state_string() const override { return "Accepting"; }
};
class ReceiveBlocker final : public FileDescriptionBlocker {
public:
explicit ReceiveBlocker(const FileDescription&);
virtual bool should_unblock(Thread&, time_t, long) override;
virtual const char* state_string() const override { return "Receiving"; }
};
class ConnectBlocker final : public FileDescriptionBlocker {
public:
explicit ConnectBlocker(const FileDescription&);
@ -186,6 +179,8 @@ public:
explicit WriteBlocker(const FileDescription&);
virtual bool should_unblock(Thread&, time_t, long) override;
virtual const char* state_string() const override { return "Writing"; }
private:
Optional<timeval> m_deadline;
};
class ReadBlocker final : public FileDescriptionBlocker {
@ -193,6 +188,8 @@ public:
explicit ReadBlocker(const FileDescription&);
virtual bool should_unblock(Thread&, time_t, long) override;
virtual const char* state_string() const override { return "Reading"; }
private:
Optional<timeval> m_deadline;
};
class ConditionBlocker final : public Blocker {