Files
tromcho.net/src/client/msgr_iothread.cpp
T

130 lines
3.0 KiB
C++

// Copyright (c) Vitaliy Filippov, 2019+
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
#include <stdexcept>
#include <sys/poll.h>
#include <unistd.h>
#include "messenger.h"
#include "msgr_iothread.h"
msgr_iothread_t::msgr_iothread_t():
ring(RINGLOOP_DEFAULT_SIZE, true),
thread(&msgr_iothread_t::run, this)
{
eventfd = ring.register_eventfd();
if (eventfd < 0)
{
throw std::runtime_error(std::string("failed to register eventfd: ") + strerror(-eventfd));
}
}
msgr_iothread_t::~msgr_iothread_t()
{
stop();
}
void msgr_iothread_t::add_sqe(io_uring_sqe & sqe)
{
mu.lock();
queue.push_back((iothread_sqe_t){ .sqe = sqe, .data = std::move(*(ring_data_t*)sqe.user_data) });
if (queue.size() == 1)
{
cond.notify_all();
}
mu.unlock();
}
void msgr_iothread_t::stop()
{
mu.lock();
if (stopped)
{
mu.unlock();
return;
}
stopped = true;
if (outer_loop_data)
{
outer_loop_data->callback = [](ring_data_t*){};
}
cond.notify_all();
close(eventfd);
mu.unlock();
thread.join();
}
void msgr_iothread_t::add_to_ringloop(ring_loop_i *outer_loop)
{
assert(!this->outer_loop || this->outer_loop == outer_loop);
io_uring_sqe *sqe = outer_loop->get_sqe();
assert(sqe != NULL);
this->outer_loop = outer_loop;
this->outer_loop_data = ((ring_data_t*)sqe->user_data);
io_uring_prep_poll_add(sqe, eventfd, POLLIN);
outer_loop_data->callback = [this](ring_data_t *data)
{
if (data->res < 0)
{
throw std::runtime_error(std::string("eventfd poll failed: ") + strerror(-data->res));
}
outer_loop_data = NULL;
if (stopped)
{
return;
}
add_to_ringloop(this->outer_loop);
ring.loop();
};
}
void msgr_iothread_t::run()
{
while (true)
{
{
std::unique_lock<std::mutex> lk(mu);
while (!stopped && !queue.size())
cond.wait(lk);
if (stopped)
return;
int i = 0;
for (; i < queue.size(); i++)
{
io_uring_sqe *sqe = ring.get_sqe();
if (!sqe)
break;
ring_data_t *data = ((ring_data_t*)sqe->user_data);
*data = std::move(queue[i].data);
*sqe = queue[i].sqe;
sqe->user_data = (uint64_t)data;
}
queue.erase(queue.begin(), queue.begin()+i);
}
// We only want to offload sendmsg/recvmsg. Callbacks will be called in main thread
ring.submit();
}
}
void osd_messenger_t::init_iothreads()
{
for (int i = 0; i < iothread_count; i++)
{
auto iot = new msgr_iothread_t();
iothreads.push_back(iot);
iot->add_to_ringloop(ringloop);
}
}
void osd_messenger_t::destroy_iothreads()
{
if (iothreads.size())
{
for (auto iot: iothreads)
{
delete iot;
}
iothreads.clear();
}
}