Commit 27cfd48e authored by Dan Melnic's avatar Dan Melnic Committed by Facebook GitHub Bot

Add support for io_uring based async read/recvmsg

Summary:
Add support for io_uring based async read/recvmsg

(Note: this ignores all push blocking failures!)

Reviewed By: kevin-vigor

Differential Revision: D21208891

fbshipit-source-id: 21b3b5e0f201e982ecb50a284d03ab53413ec3e3
parent e8a222d4
......@@ -220,7 +220,7 @@ int IoUringBackend::submitOne(IoCb* /*unused*/) {
}
int IoUringBackend::cancelOne(IoCb* ioCb) {
auto* rentry = static_cast<IoSqe*>(allocIoCb());
auto* rentry = static_cast<IoSqe*>(allocIoCb(EventCallback()));
if (!rentry) {
return 0;
}
......@@ -228,7 +228,7 @@ int IoUringBackend::cancelOne(IoCb* ioCb) {
auto* sqe = ::io_uring_get_sqe(&ioRing_);
CHECK(sqe);
rentry->prepPollRemove(sqe, ioCb); // prev entry
rentry->prepCancel(sqe, ioCb); // prev entry
int ret = submitBusyCheck(1, WaitForEventsMode::DONT_WAIT);
......@@ -305,11 +305,37 @@ size_t IoUringBackend::submitList(
CHECK(sqe); // this should not happen
auto* ev = entry->event_->getEvent();
const auto& cb = entry->event_->getCallback();
bool processed = false;
switch (cb.type_) {
case EventCallback::Type::TYPE_NONE:
break;
case EventCallback::Type::TYPE_READ:
if (auto* iov = cb.readCb_->allocateData()) {
processed = true;
entry->prepRead(
sqe, ev->ev_fd, &iov->data_, (ev->ev_events & EV_PERSIST) != 0);
entry->cbData_.set(iov);
}
break;
case EventCallback::Type::TYPE_RECVMSG:
if (auto* msg = cb.recvmsgCb_->allocateData()) {
processed = true;
entry->prepRecvmsg(
sqe, ev->ev_fd, &msg->data_, (ev->ev_events & EV_PERSIST) != 0);
entry->cbData_.set(msg);
}
break;
}
if (!processed) {
entry->cbData_.reset();
entry->prepPollAdd(
sqe,
ev->ev_fd,
getPollFlags(ev->ev_events),
(ev->ev_events & EV_PERSIST) != 0);
}
i++;
if (ioCbs.empty()) {
int num = submitBusyCheck(i, waitForEvents);
......
......@@ -104,16 +104,52 @@ class IoUringBackend : public PollIoBackend {
::io_uring_sqe_set_data(sqe, this);
}
FOLLY_ALWAYS_INLINE void prepPollRemove(
void prepRead(void* entry, int fd, const struct iovec* iov, bool registerFd)
override {
CHECK(entry);
struct io_uring_sqe* sqe = reinterpret_cast<struct io_uring_sqe*>(entry);
if (registerFd && !fdRecord_) {
fdRecord_ = backend_->registerFd(fd);
}
if (fdRecord_) {
::io_uring_prep_read(
sqe, fdRecord_->idx_, iov->iov_base, iov->iov_len, 0 /*offset*/);
sqe->flags |= IOSQE_FIXED_FILE;
} else {
::io_uring_prep_read(
sqe, fd, iov->iov_base, iov->iov_len, 0 /*offset*/);
}
::io_uring_sqe_set_data(sqe, this);
}
void prepRecvmsg(void* entry, int fd, struct msghdr* msg, bool registerFd)
override {
CHECK(entry);
struct io_uring_sqe* sqe = reinterpret_cast<struct io_uring_sqe*>(entry);
if (registerFd && !fdRecord_) {
fdRecord_ = backend_->registerFd(fd);
}
if (fdRecord_) {
::io_uring_prep_recvmsg(sqe, fdRecord_->idx_, msg, MSG_TRUNC);
sqe->flags |= IOSQE_FIXED_FILE;
} else {
::io_uring_prep_recvmsg(sqe, fd, msg, 0);
}
::io_uring_sqe_set_data(sqe, this);
}
FOLLY_ALWAYS_INLINE void prepCancel(
struct io_uring_sqe* sqe,
void* user_data) {
CHECK(sqe);
::io_uring_prep_poll_remove(sqe, user_data);
::io_uring_prep_cancel(sqe, user_data, 0);
::io_uring_sqe_set_data(sqe, this);
}
};
PollIoBackend::IoCb* allocNewIoCb() override {
PollIoBackend::IoCb* allocNewIoCb(const EventCallback& /*cb*/) override {
auto* ret = new IoSqe(this, false);
ret->backendCb_ = PollIoBackend::processPollIoCb;
......
......@@ -337,9 +337,9 @@ size_t PollIoBackend::processSignals() {
return ret;
}
PollIoBackend::IoCb* PollIoBackend::allocIoCb() {
PollIoBackend::IoCb* PollIoBackend::allocIoCb(const EventCallback& cb) {
// try to allocate from the pool first
if (FOLLY_LIKELY(freeHead_ != nullptr)) {
if ((cb.type_ == EventCallback::Type::TYPE_NONE) && (freeHead_ != nullptr)) {
auto* ret = freeHead_;
freeHead_ = freeHead_->next_;
numIoCbInUse_++;
......@@ -347,7 +347,7 @@ PollIoBackend::IoCb* PollIoBackend::allocIoCb() {
}
// alloc a new IoCb
auto* ret = allocNewIoCb();
auto* ret = allocNewIoCb(cb);
if (FOLLY_LIKELY(!!ret)) {
numIoCbInUse_++;
}
......@@ -357,6 +357,7 @@ PollIoBackend::IoCb* PollIoBackend::allocIoCb() {
void PollIoBackend::releaseIoCb(PollIoBackend::IoCb* aioIoCb) {
numIoCbInUse_--;
aioIoCb->cbData_.releaseData();
// unregister the file descriptor record
if (aioIoCb->fdRecord_) {
unregisterFd(aioIoCb->fdRecord_);
......@@ -388,7 +389,7 @@ void PollIoBackend::processPollIo(IoCb* ioCb, int64_t res) noexcept {
// add it to the active list
event_ref_flags(ev) |= EVLIST_ACTIVE;
ev->ev_res = getPollEvents(res, ev->ev_events);
ev->ev_res = res;
activeEvents_.push_back(*ioCb);
} else {
releaseIoCb(ioCb);
......@@ -413,12 +414,16 @@ size_t PollIoBackend::processActiveEvents() {
// prevent the callback from freeing the aioIoCb
ioCb->useCount_++;
if (!ioCb->cbData_.processCb(ev->ev_res)) {
// adjust the ev_res for the poll case
ev->ev_res = getPollEvents(ev->ev_res, ev->ev_events);
// handle spurious poll events that return 0
// this can happen during high load on process startup
if (ev->ev_res) {
(*event_ref_callback(ev))(
(int)ev->ev_fd, ev->ev_res, event_ref_arg(ev));
}
}
// get the event again
event = ioCb->event_;
ev = event ? event->getEvent() : nullptr;
......@@ -535,7 +540,7 @@ int PollIoBackend::eb_event_add(Event& event, const struct timeval* timeout) {
if ((ev->ev_events & (EV_READ | EV_WRITE)) &&
!(event_ref_flags(ev) & (EVLIST_INSERTED | EVLIST_ACTIVE))) {
auto* iocb = allocIoCb();
auto* iocb = allocIoCb(event.getCallback());
CHECK(iocb);
iocb->event_ = &event;
......
......@@ -96,6 +96,83 @@ class PollIoBackend : public EventBaseBackendBase {
virtual void
prepPollAdd(void* entry, int fd, uint32_t events, bool registerFd) = 0;
virtual void prepRead(
void* /*entry*/,
int /*fd*/,
const struct iovec* /*iov*/,
bool /*registerFd*/) {}
virtual void prepRecvmsg(
void* /*entry*/,
int /*fd*/,
struct msghdr* /*msg*/,
bool /*registerFd*/) {}
struct EventCallbackData {
EventCallback::Type type_{EventCallback::Type::TYPE_NONE};
union {
EventReadCallback::IoVec* ioVec_;
EventRecvmsgCallback::MsgHdr* msgHdr_;
};
void set(EventReadCallback::IoVec* ioVec) {
type_ = EventCallback::Type::TYPE_READ;
ioVec_ = ioVec;
}
void set(EventRecvmsgCallback::MsgHdr* msgHdr) {
type_ = EventCallback::Type::TYPE_RECVMSG;
msgHdr_ = msgHdr;
}
void reset() {
type_ = EventCallback::Type::TYPE_NONE;
}
bool processCb(int res) {
bool ret = false;
switch (type_) {
case EventCallback::Type::TYPE_READ: {
ret = true;
auto cbFunc = ioVec_->cbFunc_;
cbFunc(ioVec_, res);
break;
}
case EventCallback::Type::TYPE_RECVMSG: {
ret = true;
auto cbFunc = msgHdr_->cbFunc_;
cbFunc(msgHdr_, res);
break;
}
case EventCallback::Type::TYPE_NONE:
break;
}
type_ = EventCallback::Type::TYPE_NONE;
return ret;
}
void releaseData() {
switch (type_) {
case EventCallback::Type::TYPE_READ: {
auto freeFunc = ioVec_->freeFunc_;
freeFunc(ioVec_);
break;
}
case EventCallback::Type::TYPE_RECVMSG: {
auto freeFunc = msgHdr_->freeFunc_;
freeFunc(msgHdr_);
break;
}
case EventCallback::Type::TYPE_NONE:
break;
}
type_ = EventCallback::Type::TYPE_NONE;
}
};
EventCallbackData cbData_;
};
using IoCbList =
......@@ -231,10 +308,10 @@ class PollIoBackend : public EventBaseBackendBase {
void processPollIo(IoCb* ioCb, int64_t res) noexcept;
IoCb* FOLLY_NULLABLE allocIoCb();
IoCb* FOLLY_NULLABLE allocIoCb(const EventCallback& cb);
void releaseIoCb(IoCb* aioIoCb);
virtual IoCb* allocNewIoCb() = 0;
virtual IoCb* allocNewIoCb(const EventCallback& cb) = 0;
virtual void* allocSubmissionEntry() = 0;
virtual int getActiveEvents(WaitForEventsMode waitForEvents) = 0;
......
......@@ -19,13 +19,15 @@
#include <folly/FileUtil.h>
#include <folly/experimental/io/IoUringBackend.h>
#include <folly/init/Init.h>
#include <folly/io/async/AsyncUDPServerSocket.h>
#include <folly/io/async/AsyncUDPSocket.h>
#include <folly/io/async/EventHandler.h>
#include <folly/io/async/test/EventBaseTestLib.h>
#include <folly/portability/GTest.h>
// IoUringBackend specific tests
namespace {
class EventFD : public folly::EventHandler {
class EventFD : public folly::EventHandler, public folly::EventReadCallback {
public:
EventFD(
bool valid,
......@@ -43,29 +45,75 @@ class EventFD : public folly::EventHandler {
}
}
void useAsyncReadCallback(bool val) {
if (val) {
setEventCallback(this);
} else {
resetEventCallback();
}
}
// from folly::EventHandler
void handlerReady(uint16_t /*events*/) noexcept override {
// we do not read to leave the fd signalled
if (!persist_) {
registerHandler(folly::EventHandler::READ);
}
++num_;
if (total_ > 0) {
--total_;
if (total_ == 0) {
evb_->terminateLoopSoon();
}
if (total_ > 0) {
if (!persist_) {
registerHandler(folly::EventHandler::READ);
}
} else {
if (persist_) {
unregisterHandler();
}
}
}
uint64_t getAsyncNum() const {
return asyncNum_;
}
uint64_t getNum() const {
return num_;
}
// from folly::EventReadCallback
folly::EventReadCallback::IoVec* allocateData() override {
auto* ret = ioVecPtr_.release();
return (ret ? ret : new IoVec(this));
}
private:
struct IoVec : public folly::EventReadCallback::IoVec {
IoVec() = delete;
~IoVec() override = default;
explicit IoVec(EventFD* eventFd) {
arg_ = eventFd;
freeFunc_ = IoVec::free;
cbFunc_ = IoVec::cb;
data_.iov_base = &eventData_;
data_.iov_len = sizeof(eventData_);
}
static void free(EventReadCallback::IoVec* ioVec) {
delete ioVec;
}
static void cb(EventReadCallback::IoVec* ioVec, int res) {
reinterpret_cast<EventFD*>(ioVec->arg_)
->cb(reinterpret_cast<IoVec*>(ioVec), res);
}
uint64_t eventData_{0};
};
static int createFd(uint64_t num) {
// we want it a semaphore
int fd = ::eventfd(0, EFD_CLOEXEC | EFD_SEMAPHORE | EFD_NONBLOCK);
// and blocking for the async reads
int fd = ::eventfd(0, EFD_CLOEXEC | EFD_SEMAPHORE);
CHECK_GT(fd, 0);
CHECK_EQ(folly::writeNoInt(fd, &num, sizeof(num)), sizeof(num));
return fd;
......@@ -83,14 +131,41 @@ class EventFD : public folly::EventHandler {
registerHandler(folly::EventHandler::READ);
}
}
void cb(IoVec* ioVec, int res) {
CHECK_EQ(res, sizeof(IoVec::eventData_));
CHECK_EQ(ioVec->eventData_, 1);
// reset it
ioVec->eventData_ = 0;
// save it for future use
ioVecPtr_.reset(ioVec);
++asyncNum_;
if (total_ > 0) {
--total_;
}
if (total_ > 0) {
if (!persist_) {
registerHandler(folly::EventHandler::READ);
}
} else {
if (persist_) {
unregisterHandler();
}
}
}
uint64_t asyncNum_{0};
uint64_t num_{0};
uint64_t& total_;
int fd_{-1};
bool persist_;
folly::EventBase* evb_;
std::unique_ptr<IoVec> ioVecPtr_;
};
void testOverflow(bool overflow, bool persist) {
void testEventFD(bool overflow, bool persist, bool asyncRead) {
static constexpr size_t kBackendCapacity = 64;
static constexpr size_t kBackendMaxSubmit = 32;
// for overflow == true we use a greater than kBackendCapacity number of
......@@ -114,14 +189,20 @@ void testOverflow(bool overflow, bool persist) {
std::vector<std::unique_ptr<EventFD>> eventsVec;
eventsVec.reserve(kNumEventFds);
for (size_t i = 0; i < kNumEventFds; i++) {
eventsVec.emplace_back(
std::make_unique<EventFD>(true, kEventFdCount, total, persist, &evb));
auto ev = std::make_unique<EventFD>(
true, 2 * kEventFdCount, total, persist, &evb);
ev->useAsyncReadCallback(asyncRead);
eventsVec.emplace_back(std::move(ev));
}
evb.loopForever();
evb.loop();
for (size_t i = 0; i < kNumEventFds; i++) {
CHECK_GE(eventsVec[i]->getNum(), kEventFdCount);
CHECK_GE(
(asyncRead ? eventsVec[i]->getAsyncNum() : eventsVec[i]->getNum()),
kEventFdCount);
}
}
......@@ -152,28 +233,222 @@ void testInvalidFd(size_t numTotal, size_t numValid, size_t numInvalid) {
std::make_unique<EventFD>(valid, 1, total, false /*persist*/, &evb));
}
evb.loopForever();
evb.loop();
for (size_t i = 0; i < numTotal; i++) {
CHECK_GE(eventsVec[i]->getNum(), 1);
}
}
class EventRecvmsgCallback : public folly::EventRecvmsgCallback {
private:
struct MsgHdr : public folly::EventRecvmsgCallback::MsgHdr {
static auto constexpr kBuffSize = 1024;
MsgHdr() = delete;
~MsgHdr() override = default;
explicit MsgHdr(EventRecvmsgCallback* cb) {
arg_ = cb;
freeFunc_ = MsgHdr::free;
cbFunc_ = MsgHdr::cb;
ioBuf_ = folly::IOBuf::create(kBuffSize);
}
void reset() {
::memset(&data_, 0, sizeof(data_));
iov_.iov_base = ioBuf_->writableData();
iov_.iov_len = kBuffSize;
data_.msg_iov = &iov_;
data_.msg_iovlen = 1;
::memset(&addrStorage_, 0, sizeof(addrStorage_));
data_.msg_name = reinterpret_cast<sockaddr*>(&addrStorage_);
data_.msg_namelen = sizeof(addrStorage_);
}
static void free(folly::EventRecvmsgCallback::MsgHdr* msgHdr) {
delete msgHdr;
}
static void cb(folly::EventRecvmsgCallback::MsgHdr* msgHdr, int res) {
reinterpret_cast<EventRecvmsgCallback*>(msgHdr->arg_)
->cb(reinterpret_cast<MsgHdr*>(msgHdr), res);
}
// data
std::unique_ptr<folly::IOBuf> ioBuf_;
struct iovec iov_;
// addr
struct sockaddr_storage addrStorage_;
};
void cb(MsgHdr* msgHdr, int res) {
// check the number of bytes
CHECK_EQ(res, static_cast<int>(numBytes_));
// check the contents
std::string data;
data.assign(
reinterpret_cast<const char*>(msgHdr->ioBuf_->data()),
static_cast<size_t>(res));
CHECK_EQ(data, data_);
// check the address
folly::SocketAddress addr;
addr.setFromSockaddr(
reinterpret_cast<sockaddr*>(msgHdr->data_.msg_name),
msgHdr->data_.msg_namelen);
CHECK_EQ(addr, addr_);
// reuse the msgHdr
msgHdr_.reset(msgHdr);
++asyncNum_;
if (total_ > 0) {
--total_;
if (total_ == 0) {
evb_->terminateLoopSoon();
}
}
}
public:
EventRecvmsgCallback(
const std::string& data,
const folly::SocketAddress& addr,
size_t numBytes,
uint64_t& total,
folly::EventBase* eventBase)
: data_(data),
addr_(addr),
numBytes_(numBytes),
total_(total),
evb_(eventBase) {}
~EventRecvmsgCallback() override = default;
// from EventRecvmsgCallback
EventRecvmsgCallback::MsgHdr* allocateData() override {
auto* ret = msgHdr_.release();
if (!ret) {
ret = new MsgHdr(this);
}
ret->reset();
return ret;
}
uint64_t getAsyncNum() const {
return asyncNum_;
}
private:
const std::string& data_;
folly::SocketAddress addr_;
size_t numBytes_{0};
uint64_t& total_;
folly::EventBase* evb_;
uint64_t asyncNum_{0};
std::unique_ptr<MsgHdr> msgHdr_;
};
void testAsyncUDPRecvmsg(bool useRegisteredFds) {
static constexpr size_t kBackendCapacity = 64;
static constexpr size_t kBackendMaxSubmit = 32;
static constexpr size_t kBackendMaxGet = 32;
static constexpr size_t kNumSockets = 32;
static constexpr size_t kNumBytes = 16;
static constexpr size_t kNumPackets = 32;
auto total = kNumPackets * kNumSockets;
std::unique_ptr<folly::EventBaseBackendBase> backend;
try {
backend = std::make_unique<folly::IoUringBackend>(
kBackendCapacity, kBackendMaxSubmit, kBackendMaxGet, useRegisteredFds);
} catch (const folly::IoUringBackend::NotAvailable&) {
}
SKIP_IF(!backend) << "Backend not available";
folly::EventBase evb(std::move(backend));
// create the server sockets
std::vector<std::unique_ptr<folly::AsyncUDPServerSocket>> serverSocketVec;
serverSocketVec.reserve(kNumSockets);
std::vector<std::unique_ptr<folly::AsyncUDPSocket>> clientSocketVec;
serverSocketVec.reserve(kNumSockets);
std::vector<std::unique_ptr<EventRecvmsgCallback>> cbVec;
cbVec.reserve(kNumSockets);
std::string data(kNumBytes, 'A');
for (size_t i = 0; i < kNumSockets; i++) {
auto clientSock = std::make_unique<folly::AsyncUDPSocket>(&evb);
clientSock->bind(folly::SocketAddress("::1", 0));
auto cb = std::make_unique<EventRecvmsgCallback>(
data, clientSock->address(), kNumBytes, total, &evb);
auto serverSock = std::make_unique<folly::AsyncUDPServerSocket>(
&evb, 1500, folly::AsyncUDPServerSocket::DispatchMechanism::RoundRobin);
// set the event callback
serverSock->setEventCallback(cb.get());
// bind
serverSock->bind(folly::SocketAddress("::1", 0));
// retrieve the real address
folly::SocketAddress addr = serverSock->address();
serverSock->listen();
serverSocketVec.emplace_back(std::move(serverSock));
// connect the client
CHECK_EQ(clientSock->connect(addr), 0);
for (size_t j = 0; j < kNumPackets; j++) {
auto buf = folly::IOBuf::copyBuffer(data.c_str(), data.size());
CHECK_EQ(clientSock->write(addr, std::move(buf)), data.size());
}
clientSocketVec.emplace_back(std::move(clientSock));
cbVec.emplace_back(std::move(cb));
}
evb.loopForever();
for (size_t i = 0; i < kNumSockets; i++) {
CHECK_GE(cbVec[i]->getAsyncNum(), kNumPackets);
}
}
} // namespace
TEST(IoUringBackend, NoOverflowNoPersist) {
testOverflow(false, false);
TEST(IoUringBackend, AsyncUDPRecvmsgNoRegisterFd) {
testAsyncUDPRecvmsg(false);
}
TEST(IoUringBackend, AsyncUDPRecvmsgRegisterFd) {
testAsyncUDPRecvmsg(true);
}
TEST(IoUringBackend, EventFD_NoOverflowNoPersist) {
testEventFD(false, false, false);
}
TEST(IoUringBackend, EventFD_OverflowNoPersist) {
testEventFD(true, false, false);
}
TEST(IoUringBackend, OverflowNoPersist) {
testOverflow(true, false);
TEST(IoUringBackend, EventFD_NoOverflowPersist) {
testEventFD(false, true, false);
}
TEST(IoUringBackend, NoOverflowPersist) {
testOverflow(false, true);
TEST(IoUringBackend, EventFD_OverflowPersist) {
testEventFD(true, true, false);
}
TEST(IoUringBackend, OverflowPersist) {
testOverflow(true, true);
TEST(IoUringBackend, EventFD_Persist_AsyncRead) {
testEventFD(false, true, true);
}
// 9 valid fds followed by an invalid one
......
......@@ -117,6 +117,8 @@ class AsyncUDPServerSocket : private AsyncUDPSocket::ReadCallback,
validateSocketOptions(
options, addy.getFamily(), SocketOptionKey::ApplyPos::POST_BIND),
SocketOptionKey::ApplyPos::POST_BIND);
applyEventCallback();
}
void setReusePort(bool reusePort) {
......@@ -208,6 +210,11 @@ class AsyncUDPServerSocket : private AsyncUDPSocket::ReadCallback,
}
}
void setEventCallback(EventRecvmsgCallback* cb) {
eventCb_ = cb;
applyEventCallback();
}
private:
// AsyncUDPSocket::ReadCallback
void getReadBuffer(void** buf, size_t* len) noexcept override {
......@@ -283,6 +290,16 @@ class AsyncUDPServerSocket : private AsyncUDPSocket::ReadCallback,
}
}
void applyEventCallback() {
if (socket_) {
if (eventCb_) {
socket_->setEventCallback(eventCb_);
} else {
socket_->resetEventCallback();
}
}
}
EventBase* const evb_;
const size_t packetSize_;
......@@ -302,6 +319,8 @@ class AsyncUDPServerSocket : private AsyncUDPSocket::ReadCallback,
bool reusePort_{false};
bool reuseAddr_{false};
EventRecvmsgCallback* eventCb_{nullptr};
};
} // namespace folly
......@@ -19,11 +19,72 @@
#include <memory>
#include <folly/io/async/EventUtil.h>
#include <folly/net/NetOps.h>
#include <folly/portability/Event.h>
#include <folly/portability/IOVec.h>
namespace folly {
class EventBase;
class EventReadCallback {
public:
struct IoVec {
virtual ~IoVec() = default;
using FreeFunc = void (*)(IoVec*);
using CallbackFunc = void (*)(IoVec*, int);
void* arg_{nullptr};
struct iovec data_;
FreeFunc freeFunc_{nullptr};
CallbackFunc cbFunc_{nullptr};
};
EventReadCallback() = default;
virtual ~EventReadCallback() = default;
virtual IoVec* allocateData() = 0;
};
class EventRecvmsgCallback {
public:
struct MsgHdr {
virtual ~MsgHdr() = default;
using FreeFunc = void (*)(MsgHdr*);
using CallbackFunc = void (*)(MsgHdr*, int);
void* arg_{nullptr};
struct msghdr data_;
FreeFunc freeFunc_{nullptr};
CallbackFunc cbFunc_{nullptr};
};
EventRecvmsgCallback() = default;
virtual ~EventRecvmsgCallback() = default;
virtual MsgHdr* allocateData() = 0;
};
struct EventCallback {
enum class Type { TYPE_NONE = 0, TYPE_READ = 1, TYPE_RECVMSG = 2 };
Type type_{Type::TYPE_NONE};
union {
EventReadCallback* readCb_;
EventRecvmsgCallback* recvmsgCb_;
};
void set(EventReadCallback* cb) {
type_ = Type::TYPE_READ;
readCb_ = cb;
}
void set(EventRecvmsgCallback* cb) {
type_ = Type::TYPE_RECVMSG;
recvmsgCb_ = cb;
}
void reset() {
type_ = Type::TYPE_NONE;
}
};
class EventBaseEvent {
public:
EventBaseEvent() = default;
......@@ -75,6 +136,22 @@ class EventBaseEvent {
freeFn_ = freeFn;
}
void setCallback(EventReadCallback* cb) {
cb_.set(cb);
}
void setCallback(EventRecvmsgCallback* cb) {
cb_.set(cb);
}
void resetCallback() {
cb_.reset();
}
const EventCallback& getCallback() const {
return cb_;
}
void eb_event_set(
libevent_fd_t fd,
short events,
......@@ -110,6 +187,7 @@ class EventBaseEvent {
EventBase* evb_{nullptr};
void* userData_{nullptr};
FreeFunction freeFn_{nullptr};
EventCallback cb_;
};
class EventBaseBackendBase {
......
......@@ -179,6 +179,18 @@ class EventHandler {
bool isPending() const;
void setEventCallback(EventReadCallback* cb) {
event_.setCallback(cb);
}
void setEventCallback(EventRecvmsgCallback* cb) {
event_.setCallback(cb);
}
void resetEventCallback() {
event_.resetCallback();
}
private:
bool registerImpl(uint16_t events, bool internal);
void ensureNotRegistered(const char* fn);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment