Your IP : 18.189.170.65
/*
* Phusion Passenger - https://www.phusionpassenger.com/
* Copyright (c) 2011-2017 Phusion Holding B.V.
*
* "Passenger", "Phusion Passenger" and "Union Station" are registered
* trademarks of Phusion Holding B.V.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <cstdlib>
#include <cerrno>
#include <cassert>
#include <boost/bind/bind.hpp>
#include <boost/make_shared.hpp>
#include <boost/scoped_ptr.hpp>
#include <oxt/thread.hpp>
#include <oxt/backtrace.hpp>
#include <oxt/system_calls.hpp>
#include <oxt/detail/context.hpp>
#include <ev.h>
#include <uv.h>
#include <BackgroundEventLoop.h>
#include <LoggingKit/LoggingKit.h>
#include <Exceptions.h>
#include <SafeLibev.h>
#ifndef HAVE_KQUEUE
#if defined(__APPLE__) || \
defined(__DragonFly__) || \
defined(__FreeBSD__) || \
defined(__FreeBSD_kernel__) || \
defined(__OpenBSD__) || \
defined(__NetBSD__)
#define HAVE_KQUEUE 1
#endif
#endif
#ifndef HAVE_EPOLL
#ifdef __linux__
#define HAVE_EPOLL 1
#endif
#endif
#ifndef HAVE_POLLSET
#ifdef __AIX
#define HAVE_POLLSET 1
#endif
#endif
#ifndef HAVE_EVENT_PORTS
#if defined(sun) || defined(__sun)
#define HAVE_EVENT_PORTS
#endif
#endif
#if defined(HAVE_KQUEUE)
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
#elif defined(HAVE_EPOLL)
#include <sys/epoll.h>
#elif defined(HAVE_POLLSET)
#include <sys/poll.h>
#include <sys/pollset.h>
#include <sys/fcntl.h>
#elif defined(HAVE_EVENT_PORTS)
#include <port.h>
#endif
namespace Passenger {
using namespace std;
using namespace boost;
using namespace oxt;
struct BackgroundEventLoopPrivate {
struct ev_async exitSignaller;
struct ev_async libuvActivitySignaller;
uv_loop_t libuv_loop;
/**
* Coordinates communication between the libuv poller thread and the
* libuv activity callback (the latter which runs on the libevent thread.
*/
uv_sem_t libuv_sem;
/**
* This timer doesn't do anything. It only exists to prevent
* uv_backend_timeout() from returning 0, which would make the
* libuv poller thread use 100% CPU.
*/
uv_timer_t libuv_timer;
oxt::thread *thr;
oxt::thread *libuvPollerThr;
uv_barrier_t startBarrier;
bool usesLibuv;
bool started;
};
static void
signalLibevExit(struct ev_loop *loop, ev_async *async, int revents) {
BackgroundEventLoop *bg = (BackgroundEventLoop *) async->data;
if (bg->priv->usesLibuv) {
ev_async_stop(bg->libev_loop, &bg->priv->libuvActivitySignaller);
}
ev_async_stop(bg->libev_loop, &bg->priv->exitSignaller);
ev_break(bg->libev_loop, EVBREAK_ALL);
if (bg->priv->usesLibuv) {
uv_timer_stop(&bg->priv->libuv_timer);
uv_run(bg->libuv_loop, UV_RUN_NOWAIT);
}
}
static void
onLibuvActivity(struct ev_loop *loop, ev_async *async, int revents) {
BackgroundEventLoop *bg = (BackgroundEventLoop *) async->data;
uv_run(bg->libuv_loop, UV_RUN_NOWAIT);
uv_sem_post(&bg->priv->libuv_sem);
}
static void
doNothing(uv_timer_t *timer) {
// Do nothing
}
static void
runBackgroundLoop(BackgroundEventLoop *bg) {
bg->safe->setCurrentThread();
if (bg->priv->usesLibuv) {
uv_timer_start(&bg->priv->libuv_timer, doNothing, 99999000, 99999000);
uv_run(bg->libuv_loop, UV_RUN_NOWAIT);
}
uv_barrier_wait(&bg->priv->startBarrier);
ev_run(bg->libev_loop, 0);
}
static void
pollLibuv(BackgroundEventLoop *bg) {
uv_barrier_wait(&bg->priv->startBarrier);
int ret;
int fd;
int timeout;
int lastErrno;
bool intrRequested = false;
oxt::thread_local_context *ctx = oxt::get_thread_local_context();
assert(ctx != NULL);
fd = uv_backend_fd(&bg->priv->libuv_loop);
while (!boost::this_thread::interruption_requested()) {
timeout = uv_backend_timeout(&bg->priv->libuv_loop);
ctx->syscall_interruption_lock.unlock();
do {
#if defined(HAVE_KQUEUE)
struct timespec ts;
struct kevent event;
ts.tv_sec = timeout / 1000;
ts.tv_nsec = (timeout % 1000) * 1000000;
ret = kevent(fd, NULL, 0, &event, 1, (timeout == -1) ? NULL : &ts);
#elif defined(HAVE_EPOLL)
struct epoll_event ev;
ret = epoll_wait(fd, &ev, 1, timeout);
#elif defined(HAVE_POLLSET)
struct pollfd event;
ret = pollset_poll(fd, &event, 1, timeout);
#elif defined(HAVE_EVENT_PORTS)
struct timespec ts;
struct port_event event;
ts.tv_sec = timeout / 1000;
ts.tv_nsec = (timeout % 1000) * 1000000;
ret = port_get(fd, &event, (timeout == -1) ? NULL : &ts);
#else
#error "This platform is not supported. Please add corresponding I/O polling code."
#endif
lastErrno = errno;
} while (ret == -1
&& lastErrno == EINTR
&& (!boost::this_thread::syscalls_interruptable()
|| !(intrRequested = boost::this_thread::interruption_requested())));
ctx->syscall_interruption_lock.lock();
if (ret == -1
&& lastErrno == EINTR
&& boost::this_thread::syscalls_interruptable()
&& intrRequested)
{
throw boost::thread_interrupted();
}
ev_async_send(bg->libev_loop, &bg->priv->libuvActivitySignaller);
uv_sem_wait(&bg->priv->libuv_sem);
}
}
BackgroundEventLoop::BackgroundEventLoop(bool scalable, bool usesLibuv)
: libev_loop(NULL),
libuv_loop(NULL),
priv(NULL)
{
struct Guard {
BackgroundEventLoop *self;
Guard(BackgroundEventLoop *_self)
: self(_self)
{ }
~Guard() {
if (self != NULL) {
if (self->libev_loop != NULL) {
ev_loop_destroy(self->libev_loop);
}
if (self->libuv_loop != NULL) {
uv_loop_close(self->libuv_loop);
}
delete self->priv;
}
}
void clear() {
self = NULL;
}
};
TRACE_POINT();
Guard guard(this);
priv = new BackgroundEventLoopPrivate();
if (scalable) {
libev_loop = ev_loop_new(EVBACKEND_KQUEUE);
if (libev_loop == NULL) {
libev_loop = ev_loop_new(EVBACKEND_EPOLL);
}
if (libev_loop == NULL) {
libev_loop = ev_loop_new(EVFLAG_AUTO);
}
} else {
libev_loop = ev_loop_new(EVBACKEND_POLL);
}
if (libev_loop == NULL) {
throw RuntimeException("Cannot create a libev event loop");
}
#ifdef USE_VENDORED_LIBEV
P_LOG_FILE_DESCRIPTOR_OPEN2(ev_backend_fd(libev_loop), "libev event loop: backend FD");
#endif
ev_async_init(&priv->exitSignaller, signalLibevExit);
#ifdef USE_VENDORED_LIBEV
P_LOG_FILE_DESCRIPTOR_OPEN2(ev_loop_get_pipe(libev_loop, 0), "libev event loop: async pipe 0");
P_LOG_FILE_DESCRIPTOR_OPEN2(ev_loop_get_pipe(libev_loop, 1), "libev event loop: async pipe 1");
#endif
priv->exitSignaller.data = this;
safe = boost::make_shared<SafeLibev>(libev_loop);
uv_barrier_init(&priv->startBarrier, usesLibuv ? 3 : 2);
if (usesLibuv) {
ev_async_init(&priv->libuvActivitySignaller, onLibuvActivity);
priv->libuvActivitySignaller.data = this;
libuv_loop = &priv->libuv_loop;
uv_loop_init(&priv->libuv_loop);
uv_timer_init(&priv->libuv_loop, &priv->libuv_timer);
uv_sem_init(&priv->libuv_sem, 0);
P_LOG_FILE_DESCRIPTOR_OPEN2(uv_backend_fd(libuv_loop), "libuv event loop: backend");
P_LOG_FILE_DESCRIPTOR_OPEN2(libuv_loop->signal_pipefd[0], "libuv event loop: signal pipe 0");
P_LOG_FILE_DESCRIPTOR_OPEN2(libuv_loop->signal_pipefd[1], "libuv event loop: signal pipe 1");
}
priv->thr = NULL;
priv->libuvPollerThr = NULL;
priv->usesLibuv = usesLibuv;
priv->started = false;
guard.clear();
}
BackgroundEventLoop::~BackgroundEventLoop() {
stop();
if (priv->usesLibuv) {
uv_close((uv_handle_t *) &priv->libuv_timer, NULL);
while (uv_loop_alive(libuv_loop)) {
uv_run(libuv_loop, UV_RUN_NOWAIT);
syscalls::usleep(10000);
}
uv_sem_destroy(&priv->libuv_sem);
P_LOG_FILE_DESCRIPTOR_CLOSE(uv_backend_fd(libuv_loop));
P_LOG_FILE_DESCRIPTOR_CLOSE(libuv_loop->signal_pipefd[0]);
P_LOG_FILE_DESCRIPTOR_CLOSE(libuv_loop->signal_pipefd[1]);
uv_loop_close(libuv_loop);
if (ev_is_active(&priv->libuvActivitySignaller)) {
ev_async_stop(libev_loop, &priv->libuvActivitySignaller);
}
}
if (ev_is_active(&priv->exitSignaller)) {
ev_async_stop(libev_loop, &priv->exitSignaller);
}
uv_barrier_destroy(&priv->startBarrier);
delete priv;
}
void
BackgroundEventLoop::start(const string &threadName, unsigned int stackSize) {
assert(priv->thr == NULL);
ev_async_start(libev_loop, &priv->exitSignaller);
if (priv->usesLibuv) {
ev_async_start(libev_loop, &priv->libuvActivitySignaller);
}
priv->thr = new oxt::thread(
boost::bind(runBackgroundLoop, this),
threadName,
stackSize
);
if (priv->usesLibuv) {
priv->libuvPollerThr = new oxt::thread(
boost::bind(pollLibuv, this),
threadName + ": libuv poller",
1024 * 512
);
}
uv_barrier_wait(&priv->startBarrier);
}
void
BackgroundEventLoop::stop() {
if (priv->thr != NULL) {
if (priv->usesLibuv) {
priv->libuvPollerThr->interrupt_and_join();
delete priv->libuvPollerThr;
priv->libuvPollerThr = NULL;
}
ev_async_send(libev_loop, &priv->exitSignaller);
priv->thr->join();
delete priv->thr;
priv->thr = NULL;
}
}
bool
BackgroundEventLoop::isStarted() const {
return priv->thr != NULL;
}
pthread_t
BackgroundEventLoop::getNativeHandle() const {
return priv->thr->native_handle();
}
} // namespace Passenger