mirror of
https://github.com/RGBCube/serenity
synced 2025-05-24 03:55:06 +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
7 changed files with 48 additions and 57 deletions
|
@ -264,8 +264,7 @@ ssize_t IPv4Socket::recvfrom(FileDescription& description, void* buffer, size_t
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
load_receive_deadline();
|
auto res = current->block<Thread::ReadBlocker>(description);
|
||||||
auto res = current->block<Thread::ReceiveBlocker>(description);
|
|
||||||
|
|
||||||
LOCKER(lock());
|
LOCKER(lock());
|
||||||
if (!m_can_read) {
|
if (!m_can_read) {
|
||||||
|
@ -312,8 +311,7 @@ ssize_t IPv4Socket::recvfrom(FileDescription& description, void* buffer, size_t
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
load_receive_deadline();
|
auto res = current->block<Thread::ReadBlocker>(description);
|
||||||
auto res = current->block<Thread::ReceiveBlocker>(description);
|
|
||||||
|
|
||||||
LOCKER(lock());
|
LOCKER(lock());
|
||||||
if (!m_can_read) {
|
if (!m_can_read) {
|
||||||
|
|
|
@ -296,7 +296,7 @@ ssize_t LocalSocket::recvfrom(FileDescription& description, void* buffer, size_t
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
}
|
}
|
||||||
} else if (!can_read(description)) {
|
} else if (!can_read(description)) {
|
||||||
auto result = current->block<Thread::ReceiveBlocker>(description);
|
auto result = current->block<Thread::ReadBlocker>(description);
|
||||||
if (result != Thread::BlockResult::WokeNormally)
|
if (result != Thread::BlockResult::WokeNormally)
|
||||||
return -EINTR;
|
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)
|
ssize_t Socket::read(FileDescription& description, u8* buffer, ssize_t size)
|
||||||
{
|
{
|
||||||
return recvfrom(description, buffer, size, 0, nullptr, 0);
|
return recvfrom(description, buffer, size, 0, nullptr, 0);
|
||||||
|
|
|
@ -112,9 +112,6 @@ public:
|
||||||
uid_t acceptor_uid() const { return m_acceptor.uid; }
|
uid_t acceptor_uid() const { return m_acceptor.uid; }
|
||||||
gid_t acceptor_gid() const { return m_acceptor.gid; }
|
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; }
|
Lock& lock() { return m_lock; }
|
||||||
|
|
||||||
// ^File
|
// ^File
|
||||||
|
@ -122,14 +119,18 @@ public:
|
||||||
virtual ssize_t write(FileDescription&, const u8*, ssize_t) override final;
|
virtual ssize_t write(FileDescription&, const u8*, ssize_t) override final;
|
||||||
virtual String absolute_path(const FileDescription&) const override = 0;
|
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:
|
protected:
|
||||||
Socket(int domain, int type, int protocol);
|
Socket(int domain, int type, int protocol);
|
||||||
|
|
||||||
KResult queue_connection_from(NonnullRefPtr<Socket>);
|
KResult queue_connection_from(NonnullRefPtr<Socket>);
|
||||||
|
|
||||||
void load_receive_deadline();
|
|
||||||
void load_send_deadline();
|
|
||||||
|
|
||||||
int backlog() const { return m_backlog; }
|
int backlog() const { return m_backlog; }
|
||||||
void set_backlog(int backlog) { m_backlog = backlog; }
|
void set_backlog(int backlog) { m_backlog = backlog; }
|
||||||
|
|
||||||
|
@ -156,9 +157,6 @@ private:
|
||||||
timeval m_receive_timeout { 0, 0 };
|
timeval m_receive_timeout { 0, 0 };
|
||||||
timeval m_send_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;
|
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 (!description->can_read()) {
|
||||||
if (current->block<Thread::ReadBlocker>(*description) != Thread::BlockResult::WokeNormally)
|
if (current->block<Thread::ReadBlocker>(*description) != Thread::BlockResult::WokeNormally)
|
||||||
return -EINTR;
|
return -EINTR;
|
||||||
|
if (!description->can_read())
|
||||||
|
return -EAGAIN;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return description->read(buffer, size);
|
return description->read(buffer, size);
|
||||||
|
|
|
@ -118,21 +118,6 @@ bool Thread::AcceptBlocker::should_unblock(Thread&, time_t, long)
|
||||||
return socket.can_accept();
|
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)
|
Thread::ConnectBlocker::ConnectBlocker(const FileDescription& description)
|
||||||
: FileDescriptionBlocker(description)
|
: FileDescriptionBlocker(description)
|
||||||
{
|
{
|
||||||
|
@ -147,21 +132,50 @@ bool Thread::ConnectBlocker::should_unblock(Thread&, time_t, long)
|
||||||
Thread::WriteBlocker::WriteBlocker(const FileDescription& description)
|
Thread::WriteBlocker::WriteBlocker(const FileDescription& description)
|
||||||
: FileDescriptionBlocker(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();
|
return blocked_description().can_write();
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread::ReadBlocker::ReadBlocker(const FileDescription& description)
|
Thread::ReadBlocker::ReadBlocker(const FileDescription& description)
|
||||||
: FileDescriptionBlocker(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();
|
return blocked_description().can_read();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -167,13 +167,6 @@ public:
|
||||||
virtual const char* state_string() const override { return "Accepting"; }
|
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 {
|
class ConnectBlocker final : public FileDescriptionBlocker {
|
||||||
public:
|
public:
|
||||||
explicit ConnectBlocker(const FileDescription&);
|
explicit ConnectBlocker(const FileDescription&);
|
||||||
|
@ -186,6 +179,8 @@ public:
|
||||||
explicit WriteBlocker(const FileDescription&);
|
explicit WriteBlocker(const FileDescription&);
|
||||||
virtual bool should_unblock(Thread&, time_t, long) override;
|
virtual bool should_unblock(Thread&, time_t, long) override;
|
||||||
virtual const char* state_string() const override { return "Writing"; }
|
virtual const char* state_string() const override { return "Writing"; }
|
||||||
|
private:
|
||||||
|
Optional<timeval> m_deadline;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ReadBlocker final : public FileDescriptionBlocker {
|
class ReadBlocker final : public FileDescriptionBlocker {
|
||||||
|
@ -193,6 +188,8 @@ public:
|
||||||
explicit ReadBlocker(const FileDescription&);
|
explicit ReadBlocker(const FileDescription&);
|
||||||
virtual bool should_unblock(Thread&, time_t, long) override;
|
virtual bool should_unblock(Thread&, time_t, long) override;
|
||||||
virtual const char* state_string() const override { return "Reading"; }
|
virtual const char* state_string() const override { return "Reading"; }
|
||||||
|
private:
|
||||||
|
Optional<timeval> m_deadline;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ConditionBlocker final : public Blocker {
|
class ConditionBlocker final : public Blocker {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue