mirror of
https://github.com/SerenityOS/serenity
synced 2024-09-06 17:06:31 +00:00
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:
parent
d1721c761e
commit
137a45dff2
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue