mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 15:40:19 +00:00
Add support for socket send/receive timeouts.
Only the receive timeout is hooked up yet. You can change the timeout by calling setsockopt(..., SOL_SOCKET, SO_RCVTIMEO, ...). Use this mechanism to make /bin/ping report timeouts.
This commit is contained in:
parent
7bcd386338
commit
562663df7c
Notes:
sideshowbarker
2024-07-19 15:04:27 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/562663df7c1
12 changed files with 212 additions and 12 deletions
|
@ -157,10 +157,15 @@ ssize_t IPv4Socket::recvfrom(void* buffer, size_t buffer_length, int flags, cons
|
|||
}
|
||||
if (packet_buffer.is_null()) {
|
||||
current->set_blocked_socket(this);
|
||||
load_receive_deadline();
|
||||
block(Process::BlockedReceive);
|
||||
Scheduler::yield();
|
||||
|
||||
LOCKER(m_lock);
|
||||
if (!m_can_read) {
|
||||
// Unblocked due to timeout.
|
||||
return -EAGAIN;
|
||||
}
|
||||
ASSERT(m_can_read);
|
||||
ASSERT(!m_receive_queue.is_empty());
|
||||
packet_buffer = m_receive_queue.take_first();
|
||||
|
@ -175,10 +180,10 @@ ssize_t IPv4Socket::recvfrom(void* buffer, size_t buffer_length, int flags, cons
|
|||
|
||||
void IPv4Socket::did_receive(ByteBuffer&& packet)
|
||||
{
|
||||
#ifdef IPV4_SOCKET_DEBUG
|
||||
kprintf("IPv4Socket(%p): did_receive %d bytes\n", this, packet.size());
|
||||
#endif
|
||||
LOCKER(m_lock);
|
||||
m_receive_queue.append(move(packet));
|
||||
m_can_read = true;
|
||||
#ifdef IPV4_SOCKET_DEBUG
|
||||
kprintf("IPv4Socket(%p): did_receive %d bytes, packets in queue: %d\n", this, packet.size(), m_receive_queue.size_slow());
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -1589,13 +1589,17 @@ int Process::sys$sleep(unsigned seconds)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void kgettimeofday(timeval& tv)
|
||||
{
|
||||
tv.tv_sec = RTC::now();
|
||||
tv.tv_usec = (PIT::ticks_since_boot() % 1000) * 1000;
|
||||
}
|
||||
|
||||
int Process::sys$gettimeofday(timeval* tv)
|
||||
{
|
||||
if (!validate_write_typed(tv))
|
||||
return -EFAULT;
|
||||
auto now = RTC::now();
|
||||
tv->tv_sec = now;
|
||||
tv->tv_usec = (PIT::ticks_since_boot() % 1000) * 1000;
|
||||
kgettimeofday(*tv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2567,6 +2571,50 @@ ssize_t Process::sys$recvfrom(const Syscall::SC_recvfrom_params* params)
|
|||
return socket.recvfrom(buffer, buffer_length, flags, addr, addr_length);
|
||||
}
|
||||
|
||||
int Process::sys$getsockopt(const Syscall::SC_getsockopt_params* params)
|
||||
{
|
||||
if (!validate_read_typed(params))
|
||||
return -EFAULT;
|
||||
int sockfd = params->sockfd;
|
||||
int level = params->level;
|
||||
int option = params->option;
|
||||
auto* value = params->value;
|
||||
auto* value_size = (socklen_t*)params->value_size;
|
||||
|
||||
if (!validate_write_typed(value_size))
|
||||
return -EFAULT;
|
||||
if (!validate_write(value, *value_size))
|
||||
return -EFAULT;
|
||||
auto* descriptor = file_descriptor(sockfd);
|
||||
if (!descriptor)
|
||||
return -EBADF;
|
||||
if (!descriptor->is_socket())
|
||||
return -ENOTSOCK;
|
||||
auto& socket = *descriptor->socket();
|
||||
return socket.getsockopt(level, option, value, value_size);
|
||||
}
|
||||
|
||||
int Process::sys$setsockopt(const Syscall::SC_setsockopt_params* params)
|
||||
{
|
||||
if (!validate_read_typed(params))
|
||||
return -EFAULT;
|
||||
int sockfd = params->sockfd;
|
||||
int level = params->level;
|
||||
int option = params->option;
|
||||
auto* value = params->value;
|
||||
auto value_size = (socklen_t)params->value_size;
|
||||
|
||||
if (!validate_read(value, value_size))
|
||||
return -EFAULT;
|
||||
auto* descriptor = file_descriptor(sockfd);
|
||||
if (!descriptor)
|
||||
return -EBADF;
|
||||
if (!descriptor->is_socket())
|
||||
return -ENOTSOCK;
|
||||
auto& socket = *descriptor->socket();
|
||||
return socket.setsockopt(level, option, value, value_size);
|
||||
}
|
||||
|
||||
struct SharedBuffer {
|
||||
SharedBuffer(pid_t pid1, pid_t pid2, int size)
|
||||
: m_pid1(pid1)
|
||||
|
|
|
@ -46,6 +46,8 @@ struct DisplayInfo {
|
|||
unsigned pitch;
|
||||
};
|
||||
|
||||
void kgettimeofday(timeval&);
|
||||
|
||||
class Process : public InlineLinkedListNode<Process>, public Weakable<Process> {
|
||||
friend class InlineLinkedListNode<Process>;
|
||||
public:
|
||||
|
@ -233,6 +235,8 @@ public:
|
|||
int sys$connect(int sockfd, const sockaddr*, socklen_t);
|
||||
ssize_t sys$sendto(const Syscall::SC_sendto_params*);
|
||||
ssize_t sys$recvfrom(const Syscall::SC_recvfrom_params*);
|
||||
int sys$getsockopt(const Syscall::SC_getsockopt_params*);
|
||||
int sys$setsockopt(const Syscall::SC_setsockopt_params*);
|
||||
int sys$restore_signal_mask(dword mask);
|
||||
|
||||
int sys$create_shared_buffer(pid_t peer_pid, int, void** buffer);
|
||||
|
|
|
@ -54,8 +54,11 @@ bool Scheduler::pick_next()
|
|||
return context_switch(*s_colonel_process);
|
||||
}
|
||||
|
||||
auto now_sec = RTC::now();
|
||||
auto now_usec = (suseconds_t)((PIT::ticks_since_boot() % 1000) * 1000);
|
||||
|
||||
// Check and unblock processes whose wait conditions have been met.
|
||||
Process::for_each([] (Process& process) {
|
||||
Process::for_each([&] (Process& process) {
|
||||
if (process.state() == Process::BlockedSleep) {
|
||||
if (process.wakeup_time() <= system.uptime)
|
||||
process.unblock();
|
||||
|
@ -100,18 +103,19 @@ bool Scheduler::pick_next()
|
|||
|
||||
if (process.state() == Process::BlockedReceive) {
|
||||
ASSERT(process.m_blocked_socket);
|
||||
auto& socket = *process.m_blocked_socket;
|
||||
// FIXME: Block until the amount of data wanted is available.
|
||||
if (process.m_blocked_socket->can_read(SocketRole::None)) {
|
||||
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 || socket.can_read(SocketRole::None)) {
|
||||
process.unblock();
|
||||
process.m_blocked_socket = nullptr;
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (process.state() == Process::BlockedSelect) {
|
||||
if (process.m_select_has_timeout) {
|
||||
auto now_sec = RTC::now();
|
||||
auto now_usec = PIT::ticks_since_boot() % 1000;
|
||||
if (now_sec > process.m_select_timeout.tv_sec || (now_sec == process.m_select_timeout.tv_sec && now_usec >= process.m_select_timeout.tv_usec)) {
|
||||
process.unblock();
|
||||
return true;
|
||||
|
|
|
@ -60,3 +60,63 @@ KResult Socket::queue_connection_from(Socket& peer)
|
|||
m_pending.append(peer);
|
||||
return KSuccess;
|
||||
}
|
||||
|
||||
KResult Socket::setsockopt(int level, int option, const void* value, socklen_t value_size)
|
||||
{
|
||||
ASSERT(level == SOL_SOCKET);
|
||||
switch (option) {
|
||||
case SO_SNDTIMEO:
|
||||
if (value_size != sizeof(timeval))
|
||||
return KResult(-EINVAL);
|
||||
m_send_timeout = *(timeval*)value;
|
||||
return KSuccess;
|
||||
case SO_RCVTIMEO:
|
||||
if (value_size != sizeof(timeval))
|
||||
return KResult(-EINVAL);
|
||||
m_receive_timeout = *(timeval*)value;
|
||||
return KSuccess;
|
||||
default:
|
||||
kprintf("%s(%u): setsockopt() at SOL_SOCKET with unimplemented option %d\n", option);
|
||||
return KResult(-ENOPROTOOPT);
|
||||
}
|
||||
}
|
||||
|
||||
KResult Socket::getsockopt(int level, int option, void* value, socklen_t* value_size)
|
||||
{
|
||||
ASSERT(level == SOL_SOCKET);
|
||||
switch (option) {
|
||||
case SO_SNDTIMEO:
|
||||
if (*value_size < sizeof(timeval))
|
||||
return KResult(-EINVAL);
|
||||
*(timeval*)value = m_send_timeout;
|
||||
*value_size = sizeof(timeval);
|
||||
return KSuccess;
|
||||
case SO_RCVTIMEO:
|
||||
if (*value_size < sizeof(timeval))
|
||||
return KResult(-EINVAL);
|
||||
*(timeval*)value = m_receive_timeout;
|
||||
*value_size = sizeof(timeval);
|
||||
return KSuccess;
|
||||
default:
|
||||
kprintf("%s(%u): getsockopt() at SOL_SOCKET with unimplemented option %d\n", option);
|
||||
return KResult(-ENOPROTOOPT);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -38,13 +38,22 @@ public:
|
|||
virtual ssize_t sendto(const void*, size_t, int flags, const sockaddr*, socklen_t) = 0;
|
||||
virtual ssize_t recvfrom(void*, size_t, int flags, const sockaddr*, socklen_t) = 0;
|
||||
|
||||
KResult setsockopt(int level, int option, const void*, socklen_t);
|
||||
KResult getsockopt(int level, int option, void*, socklen_t*);
|
||||
|
||||
pid_t origin_pid() const { return m_origin_pid; }
|
||||
|
||||
timeval receive_deadline() const { return m_receive_deadline; }
|
||||
timeval send_deadline() const { return m_send_deadline; }
|
||||
|
||||
protected:
|
||||
Socket(int domain, int type, int protocol);
|
||||
|
||||
KResult queue_connection_from(Socket&);
|
||||
|
||||
void load_receive_deadline();
|
||||
void load_send_deadline();
|
||||
|
||||
private:
|
||||
Lock m_lock;
|
||||
pid_t m_origin_pid { 0 };
|
||||
|
@ -54,6 +63,12 @@ private:
|
|||
int m_backlog { 0 };
|
||||
bool m_connected { false };
|
||||
|
||||
timeval m_receive_timeout { 0, 0 };
|
||||
timeval m_send_timeout { 0, 0 };
|
||||
|
||||
timeval m_receive_deadline { 0, 0 };
|
||||
timeval m_send_deadline { 0, 0 };
|
||||
|
||||
Vector<RetainPtr<Socket>> m_pending;
|
||||
Vector<RetainPtr<Socket>> m_clients;
|
||||
};
|
||||
|
|
|
@ -231,6 +231,10 @@ static dword handle(RegisterDump& regs, dword function, dword arg1, dword arg2,
|
|||
return current->sys$sendto((const SC_sendto_params*)arg1);
|
||||
case Syscall::SC_recvfrom:
|
||||
return current->sys$recvfrom((const SC_recvfrom_params*)arg1);
|
||||
case Syscall::SC_getsockopt:
|
||||
return current->sys$getsockopt((const SC_getsockopt_params*)arg1);
|
||||
case Syscall::SC_setsockopt:
|
||||
return current->sys$setsockopt((const SC_setsockopt_params*)arg1);
|
||||
default:
|
||||
kprintf("<%u> int0x82: Unknown function %u requested {%x, %x, %x}\n", current->pid(), function, arg1, arg2, arg3);
|
||||
break;
|
||||
|
|
|
@ -90,6 +90,8 @@
|
|||
__ENUMERATE_SYSCALL(seal_shared_buffer) \
|
||||
__ENUMERATE_SYSCALL(sendto) \
|
||||
__ENUMERATE_SYSCALL(recvfrom) \
|
||||
__ENUMERATE_SYSCALL(getsockopt) \
|
||||
__ENUMERATE_SYSCALL(setsockopt) \
|
||||
|
||||
|
||||
namespace Syscall {
|
||||
|
@ -148,6 +150,22 @@ struct SC_recvfrom_params {
|
|||
size_t addr_length; // socklen_t
|
||||
};
|
||||
|
||||
struct SC_getsockopt_params {
|
||||
int sockfd;
|
||||
int level;
|
||||
int option;
|
||||
void* value;
|
||||
void* value_size; // socklen_t*
|
||||
};
|
||||
|
||||
struct SC_setsockopt_params {
|
||||
int sockfd;
|
||||
int level;
|
||||
int option;
|
||||
const void* value;
|
||||
size_t value_size; // socklen_t
|
||||
};
|
||||
|
||||
void initialize();
|
||||
int sync();
|
||||
|
||||
|
|
|
@ -325,6 +325,11 @@ struct pollfd {
|
|||
#define SOCK_NONBLOCK 04000
|
||||
#define SOCK_CLOEXEC 02000000
|
||||
|
||||
#define SOL_SOCKET 1
|
||||
|
||||
#define SO_RCVTIMEO 1
|
||||
#define SO_SNDTIMEO 2
|
||||
|
||||
#define IPPROTO_ICMP 1
|
||||
#define IPPROTO_TCP 6
|
||||
#define IPPROTO_UDP 17
|
||||
|
|
|
@ -48,4 +48,18 @@ ssize_t recvfrom(int sockfd, void* buffer, size_t buffer_length, int flags, cons
|
|||
__RETURN_WITH_ERRNO(rc, rc, -1);
|
||||
}
|
||||
|
||||
int getsockopt(int sockfd, int level, int option, void* value, socklen_t* value_size)
|
||||
{
|
||||
Syscall::SC_getsockopt_params params { sockfd, level, option, value, value_size };
|
||||
int rc = syscall(SC_getsockopt, ¶ms);
|
||||
__RETURN_WITH_ERRNO(rc, rc, -1);
|
||||
}
|
||||
|
||||
int setsockopt(int sockfd, int level, int option, const void* value, socklen_t value_size)
|
||||
{
|
||||
Syscall::SC_setsockopt_params params { sockfd, level, option, value, value_size };
|
||||
int rc = syscall(SC_setsockopt, ¶ms);
|
||||
__RETURN_WITH_ERRNO(rc, rc, -1);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -45,6 +45,11 @@ struct sockaddr_in {
|
|||
char sin_zero[8];
|
||||
};
|
||||
|
||||
#define SOL_SOCKET 1
|
||||
|
||||
#define SO_RCVTIMEO 1
|
||||
#define SO_SNDTIMEO 2
|
||||
|
||||
int socket(int domain, int type, int protocol);
|
||||
int bind(int sockfd, const sockaddr* addr, socklen_t);
|
||||
int listen(int sockfd, int backlog);
|
||||
|
@ -52,6 +57,8 @@ int accept(int sockfd, sockaddr*, socklen_t*);
|
|||
int connect(int sockfd, const sockaddr*, socklen_t);
|
||||
ssize_t sendto(int sockfd, const void*, size_t, int flags, const struct sockaddr*, socklen_t);
|
||||
ssize_t recvfrom(int sockfd, void*, size_t, int flags, const struct sockaddr*, socklen_t);
|
||||
int getsockopt(int sockfd, int level, int option, void*, socklen_t*);
|
||||
int setsockopt(int sockfd, int level, int option, const void*, socklen_t);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
|
|
|
@ -39,6 +39,13 @@ int main(int argc, char** argv)
|
|||
return 1;
|
||||
}
|
||||
|
||||
struct timeval timeout { 1, 0 };
|
||||
int rc = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
|
||||
if (rc < 0) {
|
||||
perror("setsockopt");
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char* addr_str = "192.168.5.1";
|
||||
if (argc > 1)
|
||||
addr_str = argv[1];
|
||||
|
@ -50,7 +57,7 @@ int main(int argc, char** argv)
|
|||
peer_address.sin_family = AF_INET;
|
||||
peer_address.sin_port = 0;
|
||||
|
||||
int rc = inet_pton(AF_INET, addr_str, &peer_address.sin_addr);
|
||||
rc = inet_pton(AF_INET, addr_str, &peer_address.sin_addr);
|
||||
|
||||
struct PingPacket {
|
||||
struct icmphdr header;
|
||||
|
@ -84,6 +91,10 @@ int main(int argc, char** argv)
|
|||
for (;;) {
|
||||
rc = recvfrom(fd, &pong_packet, sizeof(PingPacket), 0, (const struct sockaddr*)&peer_address, sizeof(sockaddr_in));
|
||||
if (rc < 0) {
|
||||
if (errno == EAGAIN) {
|
||||
printf("Request (seq=%u) timed out.\n", ntohs(ping_packet.header.un.echo.sequence));
|
||||
break;
|
||||
}
|
||||
perror("recvfrom");
|
||||
return 1;
|
||||
}
|
||||
|
@ -104,12 +115,17 @@ int main(int argc, char** argv)
|
|||
int ms = tv_diff.tv_sec * 1000 + tv_diff.tv_usec / 1000;
|
||||
|
||||
char addr_buf[64];
|
||||
printf("Pong from %s: id=%u, seq=%u, time=%dms\n",
|
||||
printf("Pong from %s: id=%u, seq=%u%s, time=%dms\n",
|
||||
inet_ntop(AF_INET, &peer_address.sin_addr, addr_buf, sizeof(addr_buf)),
|
||||
ntohs(pong_packet.header.un.echo.id),
|
||||
ntohs(pong_packet.header.un.echo.sequence),
|
||||
pong_packet.header.un.echo.sequence != ping_packet.header.un.echo.sequence ? "(!)" : "",
|
||||
ms
|
||||
);
|
||||
|
||||
// If this was a response to an earlier packet, we still need to wait for the current one.
|
||||
if (pong_packet.header.un.echo.sequence != ping_packet.header.un.echo.sequence)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue