607 lines
18 KiB
C++
607 lines
18 KiB
C++
|
/*
|
|||
|
* Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved.
|
|||
|
*
|
|||
|
* This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit).
|
|||
|
*
|
|||
|
* Use of this source code is governed by MIT license that can be found in the
|
|||
|
* LICENSE file in the root of the source tree. All contributing project authors
|
|||
|
* may be found in the AUTHORS file in the root of the source tree.
|
|||
|
*/
|
|||
|
|
|||
|
#include "SelectWrap.h"
|
|||
|
#include "EventPoller.h"
|
|||
|
#include "Util/util.h"
|
|||
|
#include "Util/uv_errno.h"
|
|||
|
#include "Util/TimeTicker.h"
|
|||
|
#include "Util/NoticeCenter.h"
|
|||
|
#include "Network/sockutil.h"
|
|||
|
|
|||
|
#if defined(HAS_EPOLL)
|
|||
|
#include <sys/epoll.h>
|
|||
|
|
|||
|
#if !defined(EPOLLEXCLUSIVE)
|
|||
|
#define EPOLLEXCLUSIVE 0
|
|||
|
#endif
|
|||
|
|
|||
|
#define EPOLL_SIZE 1024
|
|||
|
|
|||
|
//防止epoll惊群
|
|||
|
#ifndef EPOLLEXCLUSIVE
|
|||
|
#define EPOLLEXCLUSIVE 0
|
|||
|
#endif
|
|||
|
|
|||
|
#define toEpoll(event) (((event) & Event_Read) ? EPOLLIN : 0) \
|
|||
|
| (((event) & Event_Write) ? EPOLLOUT : 0) \
|
|||
|
| (((event) & Event_Error) ? (EPOLLHUP | EPOLLERR) : 0) \
|
|||
|
| (((event) & Event_LT) ? 0 : EPOLLET)
|
|||
|
|
|||
|
#define toPoller(epoll_event) (((epoll_event) & (EPOLLIN | EPOLLRDNORM | EPOLLHUP)) ? Event_Read : 0) \
|
|||
|
| (((epoll_event) & (EPOLLOUT | EPOLLWRNORM)) ? Event_Write : 0) \
|
|||
|
| (((epoll_event) & EPOLLHUP) ? Event_Error : 0) \
|
|||
|
| (((epoll_event) & EPOLLERR) ? Event_Error : 0)
|
|||
|
#define create_event() epoll_create(EPOLL_SIZE)
|
|||
|
#endif //HAS_EPOLL
|
|||
|
|
|||
|
#if defined(HAS_KQUEUE)
|
|||
|
#include <sys/event.h>
|
|||
|
#define KEVENT_SIZE 1024
|
|||
|
#define create_event() kqueue()
|
|||
|
#endif // HAS_KQUEUE
|
|||
|
|
|||
|
using namespace std;
|
|||
|
|
|||
|
namespace toolkit {
|
|||
|
|
|||
|
EventPoller &EventPoller::Instance() {
|
|||
|
return *(EventPollerPool::Instance().getFirstPoller());
|
|||
|
}
|
|||
|
|
|||
|
void EventPoller::addEventPipe() {
|
|||
|
SockUtil::setNoBlocked(_pipe.readFD());
|
|||
|
SockUtil::setNoBlocked(_pipe.writeFD());
|
|||
|
|
|||
|
// 添加内部管道事件
|
|||
|
if (addEvent(_pipe.readFD(), EventPoller::Event_Read, [this](int event) { onPipeEvent(); }) == -1) {
|
|||
|
throw std::runtime_error("Add pipe fd to poller failed");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
EventPoller::EventPoller(std::string name) {
|
|||
|
#if defined(HAS_EPOLL) || defined(HAS_KQUEUE)
|
|||
|
_event_fd = create_event();
|
|||
|
if (_event_fd == -1) {
|
|||
|
throw runtime_error(StrPrinter << "Create event fd failed: " << get_uv_errmsg());
|
|||
|
}
|
|||
|
SockUtil::setCloExec(_event_fd);
|
|||
|
#endif //HAS_EPOLL
|
|||
|
|
|||
|
_name = std::move(name);
|
|||
|
_logger = Logger::Instance().shared_from_this();
|
|||
|
addEventPipe();
|
|||
|
}
|
|||
|
|
|||
|
void EventPoller::shutdown() {
|
|||
|
async_l([]() {
|
|||
|
throw ExitException();
|
|||
|
}, false, true);
|
|||
|
|
|||
|
if (_loop_thread) {
|
|||
|
//防止作为子进程时崩溃
|
|||
|
try { _loop_thread->join(); } catch (...) { _loop_thread->detach(); }
|
|||
|
delete _loop_thread;
|
|||
|
_loop_thread = nullptr;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
EventPoller::~EventPoller() {
|
|||
|
shutdown();
|
|||
|
|
|||
|
#if defined(HAS_EPOLL) || defined(HAS_KQUEUE)
|
|||
|
if (_event_fd != -1) {
|
|||
|
close(_event_fd);
|
|||
|
_event_fd = -1;
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
//退出前清理管道中的数据
|
|||
|
onPipeEvent(true);
|
|||
|
InfoL << getThreadName();
|
|||
|
}
|
|||
|
|
|||
|
int EventPoller::addEvent(int fd, int event, PollEventCB cb) {
|
|||
|
TimeTicker();
|
|||
|
if (!cb) {
|
|||
|
WarnL << "PollEventCB is empty";
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
if (isCurrentThread()) {
|
|||
|
#if defined(HAS_EPOLL)
|
|||
|
struct epoll_event ev = {0};
|
|||
|
ev.events = toEpoll(event) ;
|
|||
|
ev.data.fd = fd;
|
|||
|
int ret = epoll_ctl(_event_fd, EPOLL_CTL_ADD, fd, &ev);
|
|||
|
if (ret != -1) {
|
|||
|
_event_map.emplace(fd, std::make_shared<PollEventCB>(std::move(cb)));
|
|||
|
}
|
|||
|
return ret;
|
|||
|
#elif defined(HAS_KQUEUE)
|
|||
|
struct kevent kev[2];
|
|||
|
int index = 0;
|
|||
|
if (event & Event_Read) {
|
|||
|
EV_SET(&kev[index++], fd, EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, nullptr);
|
|||
|
}
|
|||
|
if (event & Event_Write) {
|
|||
|
EV_SET(&kev[index++], fd, EVFILT_WRITE, EV_ADD | EV_CLEAR, 0, 0, nullptr);
|
|||
|
}
|
|||
|
int ret = kevent(_event_fd, kev, index, nullptr, 0, nullptr);
|
|||
|
if (ret != -1) {
|
|||
|
_event_map.emplace(fd, std::make_shared<PollEventCB>(std::move(cb)));
|
|||
|
}
|
|||
|
return ret;
|
|||
|
#else
|
|||
|
#ifndef _WIN32
|
|||
|
// win32平台,socket套接字不等于文件描述符,所以可能不适用这个限制
|
|||
|
if (fd >= FD_SETSIZE) {
|
|||
|
WarnL << "select() can not watch fd bigger than " << FD_SETSIZE;
|
|||
|
return -1;
|
|||
|
}
|
|||
|
#endif
|
|||
|
auto record = std::make_shared<Poll_Record>();
|
|||
|
record->fd = fd;
|
|||
|
record->event = event;
|
|||
|
record->call_back = std::move(cb);
|
|||
|
_event_map.emplace(fd, record);
|
|||
|
return 0;
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
async([this, fd, event, cb]() mutable {
|
|||
|
addEvent(fd, event, std::move(cb));
|
|||
|
});
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
int EventPoller::delEvent(int fd, PollCompleteCB cb) {
|
|||
|
TimeTicker();
|
|||
|
if (!cb) {
|
|||
|
cb = [](bool success) {};
|
|||
|
}
|
|||
|
|
|||
|
if (isCurrentThread()) {
|
|||
|
#if defined(HAS_EPOLL)
|
|||
|
int ret = -1;
|
|||
|
if (_event_map.erase(fd)) {
|
|||
|
_event_cache_expired.emplace(fd);
|
|||
|
ret = epoll_ctl(_event_fd, EPOLL_CTL_DEL, fd, nullptr);
|
|||
|
}
|
|||
|
cb(ret != -1);
|
|||
|
return ret;
|
|||
|
#elif defined(HAS_KQUEUE)
|
|||
|
int ret = -1;
|
|||
|
if (_event_map.erase(fd)) {
|
|||
|
_event_cache_expired.emplace(fd);
|
|||
|
struct kevent kev[2];
|
|||
|
int index = 0;
|
|||
|
EV_SET(&kev[index++], fd, EVFILT_READ, EV_DELETE, 0, 0, nullptr);
|
|||
|
EV_SET(&kev[index++], fd, EVFILT_WRITE, EV_DELETE, 0, 0, nullptr);
|
|||
|
ret = kevent(_event_fd, kev, index, nullptr, 0, nullptr);
|
|||
|
}
|
|||
|
cb(ret != -1);
|
|||
|
return ret;
|
|||
|
#else
|
|||
|
int ret = -1;
|
|||
|
if (_event_map.erase(fd)) {
|
|||
|
_event_cache_expired.emplace(fd);
|
|||
|
ret = 0;
|
|||
|
}
|
|||
|
cb(ret != -1);
|
|||
|
return ret;
|
|||
|
#endif //HAS_EPOLL
|
|||
|
}
|
|||
|
|
|||
|
//跨线程操作
|
|||
|
async([this, fd, cb]() mutable {
|
|||
|
delEvent(fd, std::move(cb));
|
|||
|
});
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
int EventPoller::modifyEvent(int fd, int event, PollCompleteCB cb) {
|
|||
|
TimeTicker();
|
|||
|
if (!cb) {
|
|||
|
cb = [](bool success) {};
|
|||
|
}
|
|||
|
if (isCurrentThread()) {
|
|||
|
#if defined(HAS_EPOLL)
|
|||
|
struct epoll_event ev = { 0 };
|
|||
|
ev.events = toEpoll(event);
|
|||
|
ev.data.fd = fd;
|
|||
|
auto ret = epoll_ctl(_event_fd, EPOLL_CTL_MOD, fd, &ev);
|
|||
|
cb(ret != -1);
|
|||
|
return ret;
|
|||
|
#elif defined(HAS_KQUEUE)
|
|||
|
struct kevent kev[2];
|
|||
|
int index = 0;
|
|||
|
EV_SET(&kev[index++], fd, EVFILT_READ, event & Event_Read ? EV_ADD | EV_CLEAR : EV_DELETE, 0, 0, nullptr);
|
|||
|
EV_SET(&kev[index++], fd, EVFILT_WRITE, event & Event_Write ? EV_ADD | EV_CLEAR : EV_DELETE, 0, 0, nullptr);
|
|||
|
int ret = kevent(_event_fd, kev, index, nullptr, 0, nullptr);
|
|||
|
cb(ret != -1);
|
|||
|
return ret;
|
|||
|
#else
|
|||
|
auto it = _event_map.find(fd);
|
|||
|
if (it != _event_map.end()) {
|
|||
|
it->second->event = event;
|
|||
|
}
|
|||
|
cb(it != _event_map.end());
|
|||
|
return it != _event_map.end() ? 0 : -1;
|
|||
|
#endif // HAS_EPOLL
|
|||
|
}
|
|||
|
async([this, fd, event, cb]() mutable {
|
|||
|
modifyEvent(fd, event, std::move(cb));
|
|||
|
});
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
Task::Ptr EventPoller::async(TaskIn task, bool may_sync) {
|
|||
|
return async_l(std::move(task), may_sync, false);
|
|||
|
}
|
|||
|
|
|||
|
Task::Ptr EventPoller::async_first(TaskIn task, bool may_sync) {
|
|||
|
return async_l(std::move(task), may_sync, true);
|
|||
|
}
|
|||
|
|
|||
|
Task::Ptr EventPoller::async_l(TaskIn task, bool may_sync, bool first) {
|
|||
|
TimeTicker();
|
|||
|
if (may_sync && isCurrentThread()) {
|
|||
|
task();
|
|||
|
return nullptr;
|
|||
|
}
|
|||
|
|
|||
|
auto ret = std::make_shared<Task>(std::move(task));
|
|||
|
{
|
|||
|
lock_guard<mutex> lck(_mtx_task);
|
|||
|
if (first) {
|
|||
|
_list_task.emplace_front(ret);
|
|||
|
} else {
|
|||
|
_list_task.emplace_back(ret);
|
|||
|
}
|
|||
|
}
|
|||
|
//写数据到管道,唤醒主线程
|
|||
|
_pipe.write("", 1);
|
|||
|
return ret;
|
|||
|
}
|
|||
|
|
|||
|
bool EventPoller::isCurrentThread() {
|
|||
|
return !_loop_thread || _loop_thread->get_id() == this_thread::get_id();
|
|||
|
}
|
|||
|
|
|||
|
inline void EventPoller::onPipeEvent(bool flush) {
|
|||
|
char buf[1024];
|
|||
|
int err = 0;
|
|||
|
if (!flush) {
|
|||
|
for (;;) {
|
|||
|
if ((err = _pipe.read(buf, sizeof(buf))) > 0) {
|
|||
|
// 读到管道数据,继续读,直到读空为止
|
|||
|
continue;
|
|||
|
}
|
|||
|
if (err == 0 || get_uv_error(true) != UV_EAGAIN) {
|
|||
|
// 收到eof或非EAGAIN(无更多数据)错误,说明管道无效了,重新打开管道
|
|||
|
ErrorL << "Invalid pipe fd of event poller, reopen it";
|
|||
|
delEvent(_pipe.readFD());
|
|||
|
_pipe.reOpen();
|
|||
|
addEventPipe();
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
decltype(_list_task) _list_swap;
|
|||
|
{
|
|||
|
lock_guard<mutex> lck(_mtx_task);
|
|||
|
_list_swap.swap(_list_task);
|
|||
|
}
|
|||
|
|
|||
|
_list_swap.for_each([&](const Task::Ptr &task) {
|
|||
|
try {
|
|||
|
(*task)();
|
|||
|
} catch (ExitException &) {
|
|||
|
_exit_flag = true;
|
|||
|
} catch (std::exception &ex) {
|
|||
|
ErrorL << "Exception occurred when do async task: " << ex.what();
|
|||
|
}
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
SocketRecvBuffer::Ptr EventPoller::getSharedBuffer(bool is_udp) {
|
|||
|
#if !defined(__linux) && !defined(__linux__)
|
|||
|
// 非Linux平台下,tcp和udp共享recvfrom方案,使用同一个buffer
|
|||
|
is_udp = 0;
|
|||
|
#endif
|
|||
|
auto ret = _shared_buffer[is_udp].lock();
|
|||
|
if (!ret) {
|
|||
|
ret = SocketRecvBuffer::create(is_udp);
|
|||
|
_shared_buffer[is_udp] = ret;
|
|||
|
}
|
|||
|
return ret;
|
|||
|
}
|
|||
|
|
|||
|
thread::id EventPoller::getThreadId() const {
|
|||
|
return _loop_thread ? _loop_thread->get_id() : thread::id();
|
|||
|
}
|
|||
|
|
|||
|
const std::string& EventPoller::getThreadName() const {
|
|||
|
return _name;
|
|||
|
}
|
|||
|
|
|||
|
static thread_local std::weak_ptr<EventPoller> s_current_poller;
|
|||
|
|
|||
|
// static
|
|||
|
EventPoller::Ptr EventPoller::getCurrentPoller() {
|
|||
|
return s_current_poller.lock();
|
|||
|
}
|
|||
|
|
|||
|
void EventPoller::runLoop(bool blocked, bool ref_self) {
|
|||
|
if (blocked) {
|
|||
|
if (ref_self) {
|
|||
|
s_current_poller = shared_from_this();
|
|||
|
}
|
|||
|
_sem_run_started.post();
|
|||
|
_exit_flag = false;
|
|||
|
uint64_t minDelay;
|
|||
|
#if defined(HAS_EPOLL)
|
|||
|
struct epoll_event events[EPOLL_SIZE];
|
|||
|
while (!_exit_flag) {
|
|||
|
minDelay = getMinDelay();
|
|||
|
startSleep();//用于统计当前线程负载情况
|
|||
|
int ret = epoll_wait(_event_fd, events, EPOLL_SIZE, minDelay ? minDelay : -1);
|
|||
|
sleepWakeUp();//用于统计当前线程负载情况
|
|||
|
if (ret <= 0) {
|
|||
|
//超时或被打断
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
_event_cache_expired.clear();
|
|||
|
|
|||
|
for (int i = 0; i < ret; ++i) {
|
|||
|
struct epoll_event &ev = events[i];
|
|||
|
int fd = ev.data.fd;
|
|||
|
if (_event_cache_expired.count(fd)) {
|
|||
|
//event cache refresh
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
auto it = _event_map.find(fd);
|
|||
|
if (it == _event_map.end()) {
|
|||
|
epoll_ctl(_event_fd, EPOLL_CTL_DEL, fd, nullptr);
|
|||
|
continue;
|
|||
|
}
|
|||
|
auto cb = it->second;
|
|||
|
try {
|
|||
|
(*cb)(toPoller(ev.events));
|
|||
|
} catch (std::exception &ex) {
|
|||
|
ErrorL << "Exception occurred when do event task: " << ex.what();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
#elif defined(HAS_KQUEUE)
|
|||
|
struct kevent kevents[KEVENT_SIZE];
|
|||
|
while (!_exit_flag) {
|
|||
|
minDelay = getMinDelay();
|
|||
|
struct timespec timeout = { (long)minDelay / 1000, (long)minDelay % 1000 * 1000000 };
|
|||
|
|
|||
|
startSleep();
|
|||
|
int ret = kevent(_event_fd, nullptr, 0, kevents, KEVENT_SIZE, minDelay ? &timeout : nullptr);
|
|||
|
sleepWakeUp();
|
|||
|
if (ret <= 0) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
_event_cache_expired.clear();
|
|||
|
|
|||
|
for (int i = 0; i < ret; ++i) {
|
|||
|
auto &kev = kevents[i];
|
|||
|
auto fd = kev.ident;
|
|||
|
if (_event_cache_expired.count(fd)) {
|
|||
|
// event cache refresh
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
auto it = _event_map.find(fd);
|
|||
|
if (it == _event_map.end()) {
|
|||
|
EV_SET(&kev, fd, kev.filter, EV_DELETE, 0, 0, nullptr);
|
|||
|
kevent(_event_fd, &kev, 1, nullptr, 0, nullptr);
|
|||
|
continue;
|
|||
|
}
|
|||
|
auto cb = it->second;
|
|||
|
int event = 0;
|
|||
|
switch (kev.filter) {
|
|||
|
case EVFILT_READ: event = Event_Read; break;
|
|||
|
case EVFILT_WRITE: event = Event_Write; break;
|
|||
|
default: WarnL << "unknown kevent filter: " << kev.filter; break;
|
|||
|
}
|
|||
|
|
|||
|
try {
|
|||
|
(*cb)(event);
|
|||
|
} catch (std::exception &ex) {
|
|||
|
ErrorL << "Exception occurred when do event task: " << ex.what();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
#else
|
|||
|
int ret, max_fd;
|
|||
|
FdSet set_read, set_write, set_err;
|
|||
|
List<Poll_Record::Ptr> callback_list;
|
|||
|
struct timeval tv;
|
|||
|
while (!_exit_flag) {
|
|||
|
//定时器事件中可能操作_event_map
|
|||
|
minDelay = getMinDelay();
|
|||
|
tv.tv_sec = (decltype(tv.tv_sec)) (minDelay / 1000);
|
|||
|
tv.tv_usec = 1000 * (minDelay % 1000);
|
|||
|
|
|||
|
set_read.fdZero();
|
|||
|
set_write.fdZero();
|
|||
|
set_err.fdZero();
|
|||
|
max_fd = 0;
|
|||
|
for (auto &pr : _event_map) {
|
|||
|
if (pr.first > max_fd) {
|
|||
|
max_fd = pr.first;
|
|||
|
}
|
|||
|
if (pr.second->event & Event_Read) {
|
|||
|
set_read.fdSet(pr.first);//监听管道可读事件
|
|||
|
}
|
|||
|
if (pr.second->event & Event_Write) {
|
|||
|
set_write.fdSet(pr.first);//监听管道可写事件
|
|||
|
}
|
|||
|
if (pr.second->event & Event_Error) {
|
|||
|
set_err.fdSet(pr.first);//监听管道错误事件
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
startSleep();//用于统计当前线程负载情况
|
|||
|
ret = zl_select(max_fd + 1, &set_read, &set_write, &set_err, minDelay ? &tv : nullptr);
|
|||
|
sleepWakeUp();//用于统计当前线程负载情况
|
|||
|
|
|||
|
if (ret <= 0) {
|
|||
|
//超时或被打断
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
_event_cache_expired.clear();
|
|||
|
|
|||
|
//收集select事件类型
|
|||
|
for (auto &pr : _event_map) {
|
|||
|
int event = 0;
|
|||
|
if (set_read.isSet(pr.first)) {
|
|||
|
event |= Event_Read;
|
|||
|
}
|
|||
|
if (set_write.isSet(pr.first)) {
|
|||
|
event |= Event_Write;
|
|||
|
}
|
|||
|
if (set_err.isSet(pr.first)) {
|
|||
|
event |= Event_Error;
|
|||
|
}
|
|||
|
if (event != 0) {
|
|||
|
pr.second->attach = event;
|
|||
|
callback_list.emplace_back(pr.second);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
callback_list.for_each([&](Poll_Record::Ptr &record) {
|
|||
|
if (_event_cache_expired.count(record->fd)) {
|
|||
|
//event cache refresh
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
try {
|
|||
|
record->call_back(record->attach);
|
|||
|
} catch (std::exception &ex) {
|
|||
|
ErrorL << "Exception occurred when do event task: " << ex.what();
|
|||
|
}
|
|||
|
});
|
|||
|
callback_list.clear();
|
|||
|
}
|
|||
|
#endif //HAS_EPOLL
|
|||
|
} else {
|
|||
|
_loop_thread = new thread(&EventPoller::runLoop, this, true, ref_self);
|
|||
|
_sem_run_started.wait();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
uint64_t EventPoller::flushDelayTask(uint64_t now_time) {
|
|||
|
decltype(_delay_task_map) task_copy;
|
|||
|
task_copy.swap(_delay_task_map);
|
|||
|
|
|||
|
for (auto it = task_copy.begin(); it != task_copy.end() && it->first <= now_time; it = task_copy.erase(it)) {
|
|||
|
//已到期的任务
|
|||
|
try {
|
|||
|
auto next_delay = (*(it->second))();
|
|||
|
if (next_delay) {
|
|||
|
//可重复任务,更新时间截止线
|
|||
|
_delay_task_map.emplace(next_delay + now_time, std::move(it->second));
|
|||
|
}
|
|||
|
} catch (std::exception &ex) {
|
|||
|
ErrorL << "Exception occurred when do delay task: " << ex.what();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
task_copy.insert(_delay_task_map.begin(), _delay_task_map.end());
|
|||
|
task_copy.swap(_delay_task_map);
|
|||
|
|
|||
|
auto it = _delay_task_map.begin();
|
|||
|
if (it == _delay_task_map.end()) {
|
|||
|
//没有剩余的定时器了
|
|||
|
return 0;
|
|||
|
}
|
|||
|
//最近一个定时器的执行延时
|
|||
|
return it->first - now_time;
|
|||
|
}
|
|||
|
|
|||
|
uint64_t EventPoller::getMinDelay() {
|
|||
|
auto it = _delay_task_map.begin();
|
|||
|
if (it == _delay_task_map.end()) {
|
|||
|
//没有剩余的定时器了
|
|||
|
return 0;
|
|||
|
}
|
|||
|
auto now = getCurrentMillisecond();
|
|||
|
if (it->first > now) {
|
|||
|
//所有任务尚未到期
|
|||
|
return it->first - now;
|
|||
|
}
|
|||
|
//执行已到期的任务并刷新休眠延时
|
|||
|
return flushDelayTask(now);
|
|||
|
}
|
|||
|
|
|||
|
EventPoller::DelayTask::Ptr EventPoller::doDelayTask(uint64_t delay_ms, function<uint64_t()> task) {
|
|||
|
DelayTask::Ptr ret = std::make_shared<DelayTask>(std::move(task));
|
|||
|
auto time_line = getCurrentMillisecond() + delay_ms;
|
|||
|
async_first([time_line, ret, this]() {
|
|||
|
//异步执行的目的是刷新select或epoll的休眠时间
|
|||
|
_delay_task_map.emplace(time_line, ret);
|
|||
|
});
|
|||
|
return ret;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
///////////////////////////////////////////////
|
|||
|
|
|||
|
static size_t s_pool_size = 0;
|
|||
|
static bool s_enable_cpu_affinity = true;
|
|||
|
|
|||
|
INSTANCE_IMP(EventPollerPool)
|
|||
|
|
|||
|
EventPoller::Ptr EventPollerPool::getFirstPoller() {
|
|||
|
return static_pointer_cast<EventPoller>(_threads.front());
|
|||
|
}
|
|||
|
|
|||
|
EventPoller::Ptr EventPollerPool::getPoller(bool prefer_current_thread) {
|
|||
|
auto poller = EventPoller::getCurrentPoller();
|
|||
|
if (prefer_current_thread && _prefer_current_thread && poller) {
|
|||
|
return poller;
|
|||
|
}
|
|||
|
return static_pointer_cast<EventPoller>(getExecutor());
|
|||
|
}
|
|||
|
|
|||
|
void EventPollerPool::preferCurrentThread(bool flag) {
|
|||
|
_prefer_current_thread = flag;
|
|||
|
}
|
|||
|
|
|||
|
const std::string EventPollerPool::kOnStarted = "kBroadcastEventPollerPoolStarted";
|
|||
|
|
|||
|
EventPollerPool::EventPollerPool() {
|
|||
|
auto size = addPoller("event poller", s_pool_size, ThreadPool::PRIORITY_HIGHEST, true, s_enable_cpu_affinity);
|
|||
|
NOTICE_EMIT(EventPollerPoolOnStartedArgs, kOnStarted, *this, size);
|
|||
|
InfoL << "EventPoller created size: " << size;
|
|||
|
}
|
|||
|
|
|||
|
void EventPollerPool::setPoolSize(size_t size) {
|
|||
|
s_pool_size = size;
|
|||
|
}
|
|||
|
|
|||
|
void EventPollerPool::enableCpuAffinity(bool enable) {
|
|||
|
s_enable_cpu_affinity = enable;
|
|||
|
}
|
|||
|
|
|||
|
} // namespace toolkit
|
|||
|
|