Files
memgraph/src/io/network/epoll.hpp
2022-03-14 15:47:41 +01:00

108 lines
4.1 KiB
C++

// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
// License, and you may not use this file except in compliance with the Business Source License.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#pragma once
#include <errno.h>
#include <fmt/format.h>
#include <malloc.h>
#include <sys/epoll.h>
#include "io/network/socket.hpp"
#include "utils/exceptions.hpp"
#include "utils/likely.hpp"
#include "utils/logging.hpp"
namespace memgraph::io::network {
/**
* Wrapper class for epoll.
* Creates an object that listens on file descriptor status changes.
* see: man 4 epoll
*/
class Epoll {
public:
using Event = struct epoll_event;
Epoll(bool set_cloexec = false) : epoll_fd_(epoll_create1(set_cloexec ? EPOLL_CLOEXEC : 0)) {
// epoll_create1 returns an error if there is a logical error in our code
// (for example invalid flags) or if there is irrecoverable error. In both
// cases it is best to terminate.
MG_ASSERT(epoll_fd_ != -1, "Error on epoll create: ({}) {}", errno, strerror(errno));
}
/**
* This function adds/modifies a file descriptor to be listened for events.
*
* @param fd file descriptor to add to epoll
* @param events epoll events mask
* @param ptr pointer to the associated event handler
* @param modify modify an existing file descriptor
*/
void Add(int fd, uint32_t events, void *ptr, bool modify = false) {
Event event;
event.events = events;
event.data.ptr = ptr;
int status = epoll_ctl(epoll_fd_, (modify ? EPOLL_CTL_MOD : EPOLL_CTL_ADD), fd, &event);
// epoll_ctl can return an error on our logical error or on irrecoverable
// error. There is a third possibility that some system limit is reached. In
// that case we could return an erorr and close connection. Chances of
// reaching system limit in normally working memgraph is extremely unlikely,
// so it is correct to terminate even in that case.
MG_ASSERT(!status, "Error on epoll {}: ({}) {}", (modify ? "modify" : "add"), errno, strerror(errno));
}
/**
* This function modifies a file descriptor that is listened for events.
*
* @param fd file descriptor to modify in epoll
* @param events epoll events mask
* @param ptr pointer to the associated event handler
*/
void Modify(int fd, uint32_t events, void *ptr) { Add(fd, events, ptr, true); }
/**
* This function deletes a file descriptor that is listened for events.
*
* @param fd file descriptor to delete from epoll
*/
void Delete(int fd) {
int status = epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, NULL);
// epoll_ctl can return an error on our logical error or on irrecoverable
// error. There is a third possibility that some system limit is reached. In
// that case we could return an erorr and close connection. Chances of
// reaching system limit in normally working memgraph is extremely unlikely,
// so it is correct to terminate even in that case.
MG_ASSERT(!status, "Error on epoll delete: ({}) {}", errno, strerror(errno));
}
/**
* This function waits for events from epoll.
* It can be called from multiple threads, but should be used with care in
* that case, see:
* https://stackoverflow.com/questions/7058737/is-epoll-thread-safe
*
* @param fd file descriptor to delete from epoll
*/
int Wait(Event *events, int max_events, int timeout) {
auto num_events = epoll_wait(epoll_fd_, events, max_events, timeout);
// If this check fails there was logical error in our code.
MG_ASSERT(num_events != -1 || errno == EINTR, "Error on epoll wait: ({}) {}", errno, strerror(errno));
// num_events can be -1 if errno was EINTR (epoll_wait interrupted by signal
// handler). We treat that as no events, so we return 0.
return num_events == -1 ? 0 : num_events;
}
private:
const int epoll_fd_;
};
} // namespace memgraph::io::network