Get rid of TimeoutThread(s)
Use timer events instead. A side effect is that we now support timeout in client mode. We also get much more accurate timeouts. Previously starting a server with a timeout of 4s, and GETing an endpoint that just looped, would timeout somewhere between 3.5(!) and 8 seconds. Now it times out between 4.0001 and 4.02s. This will also make fixing pagelet timeouts much easier.
Esse commit está contido em:
@@ -2,6 +2,7 @@ Future (next release):
|
||||
- Implement assert with string arguments
|
||||
- Implement RecursiveArrayIterator
|
||||
- Implement php_strip_whitespace()
|
||||
- Support for timeouts in cli mode
|
||||
|
||||
"Tamale" 7/22/2013
|
||||
- Optimize vector-shaped Arrays (arrays with keys in range 0..size-1)
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include "hphp/runtime/vm/unit.h"
|
||||
#include "hphp/runtime/vm/event_hook.h"
|
||||
#include "hphp/system/systemlib.h"
|
||||
#include "folly/Format.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
@@ -631,22 +632,19 @@ Exception* generate_request_timeout_exception() {
|
||||
Exception* ret = nullptr;
|
||||
ThreadInfo *info = ThreadInfo::s_threadInfo.getNoCheck();
|
||||
RequestInjectionData &data = info->m_reqInjectionData;
|
||||
if (data.timeoutSeconds > 0) {
|
||||
// This extra checking is needed, because there may be a race condition
|
||||
// a TimeoutThread sets flag "true" right after an old request finishes and
|
||||
// right before a new requets resets "started". In this case, we flag
|
||||
// "timedout" back to "false".
|
||||
if (time(0) - data.started >= data.timeoutSeconds) {
|
||||
std::string exceptionMsg = "entire web request took longer than ";
|
||||
exceptionMsg += boost::lexical_cast<std::string>(data.timeoutSeconds);
|
||||
exceptionMsg += " seconds and timed out";
|
||||
ArrayHolder exceptionStack;
|
||||
if (RuntimeOption::InjectedStackTrace) {
|
||||
exceptionStack = g_vmContext->debugBacktrace(false, true, true).get();
|
||||
}
|
||||
ret = new FatalErrorException(exceptionMsg, exceptionStack.get());
|
||||
}
|
||||
|
||||
bool cli = RuntimeOption::ClientExecutionMode();
|
||||
std::string exceptionMsg = cli ?
|
||||
"Maximum execution time of " :
|
||||
"entire web request took longer than ";
|
||||
exceptionMsg += folly::to<std::string>(data.getTimeout());
|
||||
exceptionMsg += cli ? " seconds exceeded" : " seconds and timed out";
|
||||
ArrayHolder exceptionStack;
|
||||
if (RuntimeOption::InjectedStackTrace) {
|
||||
exceptionStack = g_vmContext->debugBacktrace(false, true, true).get();
|
||||
}
|
||||
ret = new FatalErrorException(exceptionMsg, exceptionStack.get());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,6 @@ const StaticString BaseExecutionContext::s_amp("&");
|
||||
BaseExecutionContext::BaseExecutionContext() :
|
||||
m_fp(nullptr), m_pc(nullptr),
|
||||
m_transport(nullptr),
|
||||
m_maxTime(RuntimeOption::RequestTimeoutSeconds),
|
||||
m_cwd(Process::CurrentWorkingDirectory),
|
||||
m_out(nullptr), m_implicitFlush(false), m_protectedLevel(0),
|
||||
m_stdout(nullptr), m_stdoutData(nullptr),
|
||||
@@ -494,6 +493,8 @@ void BaseExecutionContext::onRequestShutdown() {
|
||||
}
|
||||
|
||||
void BaseExecutionContext::executeFunctions(CArrRef funcs) {
|
||||
ThreadInfo::s_threadInfo->m_reqInjectionData.resetTimer();
|
||||
|
||||
for (ArrayIter iter(funcs); iter; ++iter) {
|
||||
Array callback = iter.second().toArray();
|
||||
vm_call_user_func(callback[s_name], callback[s_args].toArray());
|
||||
@@ -770,7 +771,8 @@ void BaseExecutionContext::debuggerInfo(InfoVec &info) {
|
||||
} else {
|
||||
Add(info, "Max Memory", FormatSize(m_maxMemory));
|
||||
}
|
||||
Add(info, "Max Time", FormatTime(m_maxTime * 1000));
|
||||
Add(info, "Max Time", FormatTime(ThreadInfo::s_threadInfo.getNoCheck()->
|
||||
m_reqInjectionData.getTimeout() * 1000));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -228,8 +228,6 @@ public:
|
||||
void setContentType(CStrRef mimetype, CStrRef charset);
|
||||
int64_t getRequestMemoryMaxBytes() const { return m_maxMemory; }
|
||||
void setRequestMemoryMaxBytes(int64_t max);
|
||||
int64_t getRequestTimeLimit() const { return m_maxTime; }
|
||||
void setRequestTimeLimit(int64_t limit) { m_maxTime = limit;}
|
||||
String getCwd() const { return m_cwd;}
|
||||
void setCwd(CStrRef cwd) { m_cwd = cwd;}
|
||||
|
||||
@@ -356,7 +354,6 @@ private:
|
||||
// system settings
|
||||
Transport *m_transport;
|
||||
int64_t m_maxMemory;
|
||||
int64_t m_maxTime;
|
||||
String m_cwd;
|
||||
|
||||
// output buffering
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include "hphp/runtime/base/builtin_functions.h"
|
||||
#include "hphp/runtime/base/hphp_system.h"
|
||||
#include "hphp/runtime/base/runtime_option.h"
|
||||
#include "hphp/runtime/base/timeout_thread.h"
|
||||
#include "hphp/runtime/ext/extension.h"
|
||||
#include "hphp/util/lock.h"
|
||||
|
||||
@@ -241,7 +240,9 @@ bool IniSetting::Get(CStrRef name, String &value) {
|
||||
return true;
|
||||
}
|
||||
if (name == s_max_execution_time || name == s_maximum_execution_time) {
|
||||
value = String((int64_t)g_context->getRequestTimeLimit());
|
||||
int64_t timeout = ThreadInfo::s_threadInfo.getNoCheck()->
|
||||
m_reqInjectionData.getTimeout();
|
||||
value = String(timeout);
|
||||
return true;
|
||||
}
|
||||
if (name == s_hphp_build_id) {
|
||||
@@ -321,9 +322,8 @@ bool IniSetting::Set(CStrRef name, CStrRef value) {
|
||||
}
|
||||
} else if (name == "max_execution_time" || name == "maximum_execution_time"){
|
||||
int64_t limit = value.toInt64();
|
||||
TimeoutThread::DeferTimeout(limit);
|
||||
// Just for ini_get
|
||||
g_context->setRequestTimeLimit(limit);
|
||||
ThreadInfo::s_threadInfo.getNoCheck()->
|
||||
m_reqInjectionData.setTimeout(limit);
|
||||
return true;
|
||||
} else if (name == "arg_separator.output") {
|
||||
g_context->setArgSeparatorOutput(value);
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
#include <boost/program_options/parsers.hpp>
|
||||
#include <libgen.h>
|
||||
#include <oniguruma.h>
|
||||
#include <signal.h>
|
||||
#include "libxml/parser.h"
|
||||
|
||||
#include "hphp/runtime/base/file_repository.h"
|
||||
@@ -663,7 +664,7 @@ static int start_server(const std::string &username) {
|
||||
// If we have any warmup requests, replay them before listening for
|
||||
// real connections
|
||||
for (auto& file : RuntimeOption::ServerWarmupRequests) {
|
||||
HttpRequestHandler handler;
|
||||
HttpRequestHandler handler(0);
|
||||
ReplayTransport rt;
|
||||
timespec start;
|
||||
Timer::GetMonotonicTime(start);
|
||||
@@ -1212,7 +1213,7 @@ static int execute_program_impl(int argc, char** argv) {
|
||||
RuntimeOption::RecordInput = false;
|
||||
set_execution_mode("server");
|
||||
HttpServer server; // so we initialize runtime properly
|
||||
HttpRequestHandler handler;
|
||||
HttpRequestHandler handler(0);
|
||||
for (int i = 0; i < po.count; i++) {
|
||||
for (unsigned int j = 0; j < po.args.size(); j++) {
|
||||
ReplayTransport rt;
|
||||
@@ -1290,6 +1291,13 @@ extern "C" void hphp_fatal_error(const char *s) {
|
||||
throw_fatal(s);
|
||||
}
|
||||
|
||||
static void on_timeout(int sig, siginfo_t* info, void* context) {
|
||||
if (sig == SIGVTALRM && info && info->si_code == SI_TIMER) {
|
||||
auto data = (RequestInjectionData*)info->si_value.sival_ptr;
|
||||
data->onTimeout();
|
||||
}
|
||||
}
|
||||
|
||||
void hphp_process_init() {
|
||||
pthread_attr_t attr;
|
||||
#ifndef __APPLE__
|
||||
@@ -1300,6 +1308,11 @@ void hphp_process_init() {
|
||||
Util::init_stack_limits(&attr);
|
||||
pthread_attr_destroy(&attr);
|
||||
|
||||
struct sigaction action = {};
|
||||
action.sa_sigaction = on_timeout;
|
||||
action.sa_flags = SA_SIGINFO | SA_NODEFER;
|
||||
sigaction(SIGVTALRM, &action, nullptr);
|
||||
|
||||
init_thread_locals();
|
||||
ClassInfo::Load();
|
||||
Process::InitProcessStatics();
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
#include "hphp/util/lock.h"
|
||||
#include "hphp/util/alloc.h"
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
using std::map;
|
||||
|
||||
namespace HPHP {
|
||||
@@ -116,15 +118,97 @@ void ThreadInfo::setPendingException(Exception* e) {
|
||||
}
|
||||
|
||||
void ThreadInfo::onSessionExit() {
|
||||
m_reqInjectionData.setTimeout(0);
|
||||
m_reqInjectionData.reset();
|
||||
Transl::TargetCache::requestExit();
|
||||
}
|
||||
|
||||
RequestInjectionData::~RequestInjectionData() {
|
||||
if (m_hasTimer) {
|
||||
timer_delete(m_timer_id);
|
||||
}
|
||||
}
|
||||
|
||||
void RequestInjectionData::onSessionInit() {
|
||||
Transl::TargetCache::requestInit();
|
||||
cflagsPtr = Transl::TargetCache::conditionFlagsPtr();
|
||||
reset();
|
||||
started = time(0);
|
||||
}
|
||||
|
||||
void RequestInjectionData::onTimeout() {
|
||||
setTimedOutFlag();
|
||||
if (surprisePage) {
|
||||
mprotect(surprisePage, sizeof(void*), PROT_NONE);
|
||||
}
|
||||
m_timerActive.store(false, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void RequestInjectionData::setSurprisePage(void* page) {
|
||||
if (page != surprisePage) {
|
||||
if (!page) {
|
||||
if (m_timerActive.load(std::memory_order_relaxed)) {
|
||||
setTimeout(0);
|
||||
}
|
||||
}
|
||||
assert(!m_timerActive.load(std::memory_order_relaxed));
|
||||
surprisePage = page;
|
||||
}
|
||||
}
|
||||
|
||||
void RequestInjectionData::setTimeout(int seconds) {
|
||||
m_timeoutSeconds = seconds > 0 ? seconds : 0;
|
||||
if (!m_hasTimer) {
|
||||
if (!m_timeoutSeconds) {
|
||||
// we don't have a timer, and we don't have a timeout
|
||||
return;
|
||||
}
|
||||
sigevent sev;
|
||||
memset(&sev, 0, sizeof(sev));
|
||||
sev.sigev_notify = SIGEV_SIGNAL;
|
||||
sev.sigev_signo = SIGVTALRM;
|
||||
sev.sigev_value.sival_ptr = this;
|
||||
if (timer_create(CLOCK_REALTIME, &sev, &m_timer_id)) {
|
||||
raise_error("Failed to set timeout: %s", strerror(errno));
|
||||
}
|
||||
m_hasTimer = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* There is a potential race here. Callers want to assume that
|
||||
* if they cancel the timeout (seconds = 0), they *wont* get
|
||||
* a signal after they call this (although they may get a signal
|
||||
* during the call).
|
||||
* So we need to clear the timeout, wait (if necessary) for a
|
||||
* pending signal to be handled, and then set the new timeout
|
||||
*/
|
||||
itimerspec ts = {};
|
||||
itimerspec old;
|
||||
timer_settime(m_timer_id, 0, &ts, &old);
|
||||
if (!old.it_value.tv_sec && !old.it_value.tv_nsec) {
|
||||
// the timer has gone off...
|
||||
if (m_timerActive.load(std::memory_order_acquire)) {
|
||||
// but m_timerActive is still set, so we haven't processed
|
||||
// the signal yet.
|
||||
// spin until its done.
|
||||
while (m_timerActive.load(std::memory_order_relaxed)) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_timeoutSeconds) {
|
||||
m_timerActive.store(true, std::memory_order_relaxed);
|
||||
ts.it_value.tv_sec = m_timeoutSeconds;
|
||||
timer_settime(m_timer_id, 0, &ts, nullptr);
|
||||
} else {
|
||||
m_timerActive.store(false, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
void RequestInjectionData::resetTimer(int seconds /* = -1 */) {
|
||||
auto data = &ThreadInfo::s_threadInfo->m_reqInjectionData;
|
||||
if (seconds <= 0) seconds = data->getTimeout();
|
||||
data->setTimeout(seconds);
|
||||
data->clearTimedOutFlag();
|
||||
|
||||
}
|
||||
|
||||
void RequestInjectionData::reset() {
|
||||
@@ -154,6 +238,11 @@ void RequestInjectionData::setTimedOutFlag() {
|
||||
RequestInjectionData::TimedOutFlag);
|
||||
}
|
||||
|
||||
void RequestInjectionData::clearTimedOutFlag() {
|
||||
__sync_fetch_and_and(getConditionFlags(),
|
||||
~RequestInjectionData::TimedOutFlag);
|
||||
}
|
||||
|
||||
void RequestInjectionData::setSignaledFlag() {
|
||||
__sync_fetch_and_or(getConditionFlags(),
|
||||
RequestInjectionData::SignaledFlag);
|
||||
|
||||
@@ -1,214 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| HipHop for PHP |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
|
||||
| Copyright (c) 1998-2010 Zend Technologies Ltd. (http://www.zend.com) |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 2.00 of the Zend license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| http://www.zend.com/license/2_00.txt. |
|
||||
| If you did not receive a copy of the Zend license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@zend.com so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "hphp/runtime/base/timeout_thread.h"
|
||||
#include "hphp/runtime/base/runtime_option.h"
|
||||
#include "hphp/util/lock.h"
|
||||
#include "hphp/util/logger.h"
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
namespace HPHP {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// statics
|
||||
|
||||
// class defined in runtime/base/types.h
|
||||
static void on_timer(int fd, short events, void *context) {
|
||||
((TimeoutThread*)context)->onTimer(fd);
|
||||
}
|
||||
|
||||
static void on_thread_stop(int fd, short events, void *context) {
|
||||
event_base_loopbreak((struct event_base *)context);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void TimeoutThread::DeferTimeout(int seconds) {
|
||||
RequestInjectionData &data = ThreadInfo::s_threadInfo->m_reqInjectionData;
|
||||
if (seconds > 0) {
|
||||
// cheating by resetting started to desired timestamp
|
||||
data.started = time(0) + (seconds - data.timeoutSeconds);
|
||||
} else {
|
||||
data.started = 0;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TimeoutThread::TimeoutThread(int timeoutSeconds)
|
||||
: m_nextId(0), m_stopped(false), m_timeoutSeconds(timeoutSeconds) {
|
||||
m_eventBase = event_base_new();
|
||||
|
||||
// We need to open the pipe here because worker threads can start
|
||||
// before the timeout thread starts
|
||||
m_pipe.open();
|
||||
}
|
||||
|
||||
TimeoutThread::~TimeoutThread() {
|
||||
event_base_free(m_eventBase);
|
||||
}
|
||||
|
||||
void TimeoutThread::registerRequestThread(RequestInjectionData* data) {
|
||||
assert(data);
|
||||
data->timeoutSeconds = m_timeoutSeconds;
|
||||
|
||||
{
|
||||
Lock l(this);
|
||||
int id = m_nextId++;
|
||||
assert(!mapContains(m_clients, id));
|
||||
m_clients[id].data = data;
|
||||
m_pendingIds.push(id);
|
||||
}
|
||||
notifyPipe();
|
||||
}
|
||||
|
||||
void TimeoutThread::removeRequestThread(RequestInjectionData* data) {
|
||||
assert(data);
|
||||
{
|
||||
Lock l(this);
|
||||
for (auto& pair : m_clients) {
|
||||
if (pair.second.data == data) {
|
||||
m_pendingIds.push(pair.first);
|
||||
pair.second.data = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
notifyPipe();
|
||||
}
|
||||
|
||||
void TimeoutThread::checkForNewWorkers() {
|
||||
// If m_timeoutSeconds is not a positive number, then workers threads
|
||||
// are allowed to run forever, so don't bother creating timers for the
|
||||
// workers
|
||||
if (m_timeoutSeconds <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Lock lock(this);
|
||||
for (; !m_pendingIds.empty(); m_pendingIds.pop()) {
|
||||
int id = m_pendingIds.front();
|
||||
assert(mapContains(m_clients, id));
|
||||
ClientThread& ct = m_clients[id];
|
||||
|
||||
if (ct.data != nullptr) {
|
||||
// This is a new thread and we have to register its timeout event
|
||||
|
||||
struct timeval timeout;
|
||||
timeout.tv_usec = 0;
|
||||
// +2 to make sure when it times out, this equation always holds:
|
||||
// time(0) - RequestInjection::s_reqInjectionData->started >=
|
||||
// m_timeoutSeconds
|
||||
timeout.tv_sec = m_timeoutSeconds + 2;
|
||||
|
||||
event_set(&ct.e, id, 0, on_timer, this);
|
||||
event_base_set(m_eventBase, &ct.e);
|
||||
event_add(&ct.e, &timeout);
|
||||
} else {
|
||||
// This was a deleted thread and we have to remove its timeout event
|
||||
event_del(&ct.e);
|
||||
m_clients.erase(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TimeoutThread::drainPipe() {
|
||||
struct pollfd fdArray[1];
|
||||
fdArray[0].fd = m_pipe.getOut();
|
||||
fdArray[0].events = POLLIN;
|
||||
while (poll(fdArray, 1, 0) > 0) {
|
||||
char buf[256];
|
||||
read(m_pipe.getOut(), buf, 256);
|
||||
}
|
||||
}
|
||||
|
||||
void TimeoutThread::notifyPipe() {
|
||||
if (write(m_pipe.getIn(), "", 1) < 0) {
|
||||
Logger::Warning("Error notifying the timeout thread that an event has "
|
||||
"happened");
|
||||
}
|
||||
}
|
||||
|
||||
void TimeoutThread::run() {
|
||||
event_set(&m_eventPipe, m_pipe.getOut(), EV_READ|EV_PERSIST,
|
||||
on_thread_stop, m_eventBase);
|
||||
event_base_set(m_eventBase, &m_eventPipe);
|
||||
event_add(&m_eventPipe, nullptr);
|
||||
|
||||
while (!m_stopped) {
|
||||
checkForNewWorkers();
|
||||
event_base_loop(m_eventBase, EVLOOP_ONCE);
|
||||
drainPipe();
|
||||
}
|
||||
|
||||
for (auto& pair : m_clients) {
|
||||
event_del(&pair.second.e);
|
||||
}
|
||||
event_del(&m_eventPipe);
|
||||
}
|
||||
|
||||
void TimeoutThread::stop() {
|
||||
m_stopped = true;
|
||||
notifyPipe();
|
||||
}
|
||||
|
||||
void TimeoutThread::onTimer(int index) {
|
||||
Lock l(this);
|
||||
assert(mapContains(m_clients, index));
|
||||
ClientThread& ct = m_clients[index];
|
||||
if (ct.data == nullptr) {
|
||||
// The thread has been deleted but we haven't processed it
|
||||
// yet. This is ok: just do nothing.
|
||||
return;
|
||||
}
|
||||
|
||||
event *e = &ct.e;
|
||||
event_del(e);
|
||||
|
||||
RequestInjectionData *data = ct.data;
|
||||
assert(data);
|
||||
struct timeval timeout;
|
||||
timeout.tv_usec = 0;
|
||||
if (data->started > 0) {
|
||||
time_t now = time(0);
|
||||
int delta = now - data->started;
|
||||
if (delta >= m_timeoutSeconds) {
|
||||
timeout.tv_sec = m_timeoutSeconds + 2;
|
||||
Lock l(data->surpriseLock);
|
||||
data->setTimedOutFlag();
|
||||
if (data->surprisePage) {
|
||||
mprotect(data->surprisePage, sizeof(void*), PROT_NONE);
|
||||
}
|
||||
} else {
|
||||
// Negative delta means start time was adjusted forward to give more time
|
||||
if (delta < 0) delta = 0;
|
||||
|
||||
// otherwise, a new request started after we started the timer
|
||||
timeout.tv_sec = m_timeoutSeconds - delta + 2;
|
||||
}
|
||||
} else {
|
||||
// Another cycle of m_timeoutSeconds
|
||||
timeout.tv_sec = m_timeoutSeconds;
|
||||
}
|
||||
|
||||
event_set(e, index, 0, on_timer, this);
|
||||
event_base_set(m_eventBase, e);
|
||||
event_add(e, &timeout);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| HipHop for PHP |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
|
||||
| Copyright (c) 1998-2010 Zend Technologies Ltd. (http://www.zend.com) |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 2.00 of the Zend license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| http://www.zend.com/license/2_00.txt. |
|
||||
| If you did not receive a copy of the Zend license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@zend.com so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef incl_HPHP_TIMEOUT_THREAD_H_
|
||||
#define incl_HPHP_TIMEOUT_THREAD_H_
|
||||
|
||||
#include <queue>
|
||||
|
||||
#include "hphp/runtime/base/types.h"
|
||||
#include "hphp/util/base.h"
|
||||
#include "hphp/util/process.h"
|
||||
#include "hphp/util/synchronizable.h"
|
||||
#include <event.h>
|
||||
|
||||
namespace HPHP {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class TimeoutThread : public Synchronizable {
|
||||
public:
|
||||
static void DeferTimeout(int seconds);
|
||||
|
||||
public:
|
||||
TimeoutThread(int timeoutSeconds);
|
||||
~TimeoutThread();
|
||||
|
||||
void registerRequestThread(RequestInjectionData* data);
|
||||
void removeRequestThread(RequestInjectionData* data);
|
||||
void run();
|
||||
void stop();
|
||||
|
||||
void onTimer(int index);
|
||||
|
||||
private:
|
||||
void checkForNewWorkers();
|
||||
void drainPipe();
|
||||
void notifyPipe();
|
||||
|
||||
int m_nextId;
|
||||
// m_pendingIds contains ids of threads that were added or removed
|
||||
// and need to be processed by the worker thread
|
||||
std::queue<int> m_pendingIds;
|
||||
bool m_stopped;
|
||||
|
||||
event_base *m_eventBase;
|
||||
|
||||
struct ClientThread {
|
||||
RequestInjectionData* data;
|
||||
event e;
|
||||
};
|
||||
std::map<int, ClientThread> m_clients;
|
||||
int m_timeoutSeconds;
|
||||
|
||||
// signal to wake up the thread
|
||||
event m_eventPipe;
|
||||
CPipe m_pipe;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
}
|
||||
|
||||
#endif // incl_HPHP_TIMEOUT_THREAD_H_
|
||||
|
||||
@@ -23,15 +23,17 @@
|
||||
#include "hphp/util/thread_local.h"
|
||||
#include "hphp/util/mutex.h"
|
||||
#include "hphp/util/case_insensitive.h"
|
||||
#include <vector>
|
||||
#include "hphp/runtime/base/macros.h"
|
||||
#include "hphp/runtime/base/memory_manager.h"
|
||||
|
||||
#include <boost/static_assert.hpp>
|
||||
#include <boost/intrusive_ptr.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <atomic>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace HPHP {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@@ -207,12 +209,15 @@ public:
|
||||
static const ssize_t LastFlag = DebuggerSignalFlag;
|
||||
|
||||
RequestInjectionData()
|
||||
: cflagsPtr(nullptr), surprisePage(nullptr), started(0), timeoutSeconds(-1),
|
||||
: cflagsPtr(nullptr), surprisePage(nullptr),
|
||||
m_timeoutSeconds(-1), m_hasTimer(false), m_timerActive(false),
|
||||
m_debugger(false), m_dummySandbox(false),
|
||||
m_debuggerIntr(false), m_coverage(false),
|
||||
m_jit(false) {
|
||||
}
|
||||
|
||||
~RequestInjectionData();
|
||||
|
||||
inline volatile ssize_t* getConditionFlags() {
|
||||
assert(cflagsPtr);
|
||||
return cflagsPtr;
|
||||
@@ -222,17 +227,25 @@ public:
|
||||
// somewhere in the thread's targetcache
|
||||
void *surprisePage; // beginning address of page to
|
||||
// protect for error conditions
|
||||
Mutex surpriseLock; // mutex controlling access to surprisePage
|
||||
|
||||
time_t started; // when a request was started
|
||||
int timeoutSeconds; // how many seconds to timeout
|
||||
private:
|
||||
timer_t m_timer_id; // id of our timer
|
||||
int m_timeoutSeconds; // how many seconds to timeout
|
||||
bool m_hasTimer; // Whether we've created our timer yet
|
||||
std::atomic<bool> m_timerActive;
|
||||
// Set true when we activate a timer,
|
||||
// cleared when the signal handler runs
|
||||
bool m_debugger; // whether there is a DebuggerProxy attached to me
|
||||
bool m_dummySandbox; // indicating it is from a dummy sandbox thread
|
||||
bool m_debuggerIntr; // indicating we should force interrupt for debugger
|
||||
bool m_coverage; // is coverage being collected
|
||||
bool m_jit; // is the jit enabled
|
||||
public:
|
||||
int getTimeout() const { return m_timeoutSeconds; }
|
||||
void setTimeout(int seconds);
|
||||
void resetTimer(int seconds = -1);
|
||||
void setSurprisePage(void* page);
|
||||
void onTimeout();
|
||||
bool getJit() const { return m_jit; }
|
||||
bool getDebugger() const { return m_debugger; }
|
||||
void setDebugger(bool d) {
|
||||
@@ -262,6 +275,7 @@ public:
|
||||
|
||||
void setMemExceededFlag();
|
||||
void setTimedOutFlag();
|
||||
void clearTimedOutFlag();
|
||||
void setSignaledFlag();
|
||||
void setEventHookFlag();
|
||||
void clearEventHookFlag();
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#include "hphp/runtime/base/ini_setting.h"
|
||||
#include "hphp/runtime/base/memory_manager.h"
|
||||
#include "hphp/runtime/base/request_local.h"
|
||||
#include "hphp/runtime/base/timeout_thread.h"
|
||||
#include "hphp/runtime/base/runtime_error.h"
|
||||
#include "hphp/runtime/base/zend_functions.h"
|
||||
#include "hphp/runtime/base/zend_string.h"
|
||||
@@ -847,13 +846,9 @@ bool f_set_magic_quotes_runtime(bool new_setting) {
|
||||
}
|
||||
|
||||
void f_set_time_limit(int seconds) {
|
||||
TimeoutThread::DeferTimeout(seconds);
|
||||
// Just for ini_get
|
||||
g_context->setRequestTimeLimit(seconds);
|
||||
if (RuntimeOption::ClientExecutionMode() &&
|
||||
seconds != 0) {
|
||||
raise_warning("set_time_limit is not supported in client mode");
|
||||
}
|
||||
ThreadInfo *info = ThreadInfo::s_threadInfo.getNoCheck();
|
||||
RequestInjectionData &data = info->m_reqInjectionData;
|
||||
data.setTimeout(seconds);
|
||||
}
|
||||
|
||||
String f_sys_get_temp_dir() {
|
||||
|
||||
@@ -210,7 +210,7 @@ Variant f_xbox_process_call_message(CStrRef msg) {
|
||||
}
|
||||
|
||||
int64_t f_xbox_get_thread_timeout() {
|
||||
XboxServerInfoPtr server_info = XboxServer::GetServerInfo();
|
||||
auto server_info = XboxServer::GetServerInfo();
|
||||
if (server_info) {
|
||||
return server_info->getMaxDuration();
|
||||
}
|
||||
@@ -222,7 +222,7 @@ void f_xbox_set_thread_timeout(int timeout) {
|
||||
raise_warning("Cannot set timeout/duration to a negative number.");
|
||||
return;
|
||||
}
|
||||
XboxServerInfoPtr server_info = XboxServer::GetServerInfo();
|
||||
auto server_info = XboxServer::GetServerInfo();
|
||||
if (server_info) {
|
||||
server_info->setMaxDuration(timeout);
|
||||
} else {
|
||||
|
||||
@@ -62,7 +62,8 @@ IMPLEMENT_THREAD_LOCAL(AccessLog::ThreadData,
|
||||
AccessLog AdminRequestHandler::s_accessLog(
|
||||
&(AdminRequestHandler::getAccessLogThreadData));
|
||||
|
||||
AdminRequestHandler::AdminRequestHandler() {
|
||||
AdminRequestHandler::AdminRequestHandler(int timeout) :
|
||||
RequestHandler(timeout) {
|
||||
}
|
||||
|
||||
// Helper machinery for jemalloc-stats-print command.
|
||||
|
||||
@@ -28,7 +28,7 @@ public:
|
||||
static AccessLog &GetAccessLog() { return s_accessLog; }
|
||||
|
||||
public:
|
||||
AdminRequestHandler();
|
||||
explicit AdminRequestHandler(int timeout);
|
||||
// implementing RequestHandler
|
||||
virtual void handleRequest(Transport *transport);
|
||||
|
||||
|
||||
@@ -43,9 +43,9 @@ IMPLEMENT_THREAD_LOCAL(AccessLog::ThreadData,
|
||||
AccessLog HttpRequestHandler::s_accessLog(
|
||||
&(HttpRequestHandler::getAccessLogThreadData));
|
||||
|
||||
HttpRequestHandler::HttpRequestHandler()
|
||||
: m_pathTranslation(true)
|
||||
,m_requestTimedOutOnQueue(ServiceData::createTimeseries(
|
||||
HttpRequestHandler::HttpRequestHandler(int timeout)
|
||||
: RequestHandler(timeout), m_pathTranslation(true)
|
||||
, m_requestTimedOutOnQueue(ServiceData::createTimeseries(
|
||||
"requests_timed_out_on_queue",
|
||||
{ServiceData::StatsType::COUNT})) { }
|
||||
|
||||
@@ -135,9 +135,8 @@ void HttpRequestHandler::handleRequest(Transport *transport) {
|
||||
|
||||
// don't serve the request if it's been sitting in queue for longer than our
|
||||
// allowed request timeout.
|
||||
int requestTimeoutSeconds = (vhost->getRequestTimeoutSeconds() > 0 ?
|
||||
vhost->getRequestTimeoutSeconds() :
|
||||
RuntimeOption::RequestTimeoutSeconds);
|
||||
int requestTimeoutSeconds =
|
||||
vhost->getRequestTimeoutSeconds(RuntimeOption::RequestTimeoutSeconds);
|
||||
if (requestTimeoutSeconds > 0) {
|
||||
timespec now;
|
||||
Timer::GetMonotonicTime(now);
|
||||
@@ -269,7 +268,8 @@ void HttpRequestHandler::handleRequest(Transport *transport) {
|
||||
|
||||
// main body
|
||||
hphp_session_init();
|
||||
vhost->setRequestTimeoutSeconds();
|
||||
ThreadInfo::s_threadInfo->m_reqInjectionData.
|
||||
setTimeout(requestTimeoutSeconds);
|
||||
|
||||
bool ret = false;
|
||||
try {
|
||||
|
||||
@@ -38,7 +38,7 @@ public:
|
||||
static AccessLog &GetAccessLog() { return s_accessLog; }
|
||||
|
||||
public:
|
||||
HttpRequestHandler();
|
||||
explicit HttpRequestHandler(int timeout);
|
||||
|
||||
// implementing RequestHandler
|
||||
virtual void handleRequest(Transport *transport);
|
||||
|
||||
@@ -59,7 +59,6 @@ HttpServer::HttpServer(void *sslCTX /* = NULL */)
|
||||
|
||||
int startingThreadCount = RuntimeOption::ServerThreadCount;
|
||||
uint32_t additionalThreads = 0;
|
||||
RequestHandlerFactory handlerFactory;
|
||||
if (RuntimeOption::ServerWarmupThrottleRequestCount > 0 &&
|
||||
RuntimeOption::ServerThreadCount > kNumProcessors) {
|
||||
startingThreadCount = kNumProcessors;
|
||||
@@ -70,8 +69,7 @@ HttpServer::HttpServer(void *sslCTX /* = NULL */)
|
||||
(RuntimeOption::ServerType);
|
||||
ServerOptions options
|
||||
(RuntimeOption::ServerIP, RuntimeOption::ServerPort,
|
||||
startingThreadCount,
|
||||
std::chrono::seconds(RuntimeOption::RequestTimeoutSeconds));
|
||||
startingThreadCount);
|
||||
options.m_serverFD = RuntimeOption::ServerPortFd;
|
||||
options.m_sslFD = RuntimeOption::SSLPortFd;
|
||||
options.m_takeoverFilename = RuntimeOption::TakeoverFilename;
|
||||
@@ -81,12 +79,14 @@ HttpServer::HttpServer(void *sslCTX /* = NULL */)
|
||||
if (additionalThreads) {
|
||||
auto handlerFactory = boost::make_shared<WarmupRequestHandlerFactory>(
|
||||
m_pageServer, additionalThreads,
|
||||
RuntimeOption::ServerWarmupThrottleRequestCount);
|
||||
RuntimeOption::ServerWarmupThrottleRequestCount,
|
||||
RuntimeOption::RequestTimeoutSeconds);
|
||||
m_pageServer->setRequestHandlerFactory([handlerFactory] {
|
||||
return handlerFactory->createHandler();
|
||||
});
|
||||
} else {
|
||||
m_pageServer->setRequestHandlerFactory<HttpRequestHandler>();
|
||||
m_pageServer->setRequestHandlerFactory<HttpRequestHandler>(
|
||||
RuntimeOption::RequestTimeoutSeconds);
|
||||
}
|
||||
|
||||
if (RuntimeOption::EnableSSL && m_sslCTX) {
|
||||
@@ -97,9 +97,9 @@ HttpServer::HttpServer(void *sslCTX /* = NULL */)
|
||||
m_adminServer = ServerFactoryRegistry::createServer
|
||||
(RuntimeOption::ServerType,
|
||||
RuntimeOption::ServerIP, RuntimeOption::AdminServerPort,
|
||||
RuntimeOption::AdminThreadCount,
|
||||
std::chrono::seconds(RuntimeOption::RequestTimeoutSeconds));
|
||||
m_adminServer->setRequestHandlerFactory<AdminRequestHandler>();
|
||||
RuntimeOption::AdminThreadCount);
|
||||
m_adminServer->setRequestHandlerFactory<AdminRequestHandler>(
|
||||
RuntimeOption::RequestTimeoutSeconds);
|
||||
|
||||
for (unsigned int i = 0; i < RuntimeOption::SatelliteServerInfos.size();
|
||||
i++) {
|
||||
@@ -139,7 +139,7 @@ HttpServer::HttpServer(void *sslCTX /* = NULL */)
|
||||
|
||||
ReplayTransport rt;
|
||||
rt.replayInput(hdf);
|
||||
HttpRequestHandler handler;
|
||||
HttpRequestHandler handler(0);
|
||||
handler.handleRequest(&rt);
|
||||
int code = rt.getResponseCode();
|
||||
if (code == 200) {
|
||||
|
||||
@@ -132,7 +132,6 @@ void LibEventWorker::doJob(LibEventJobPtr job) {
|
||||
void LibEventWorker::onThreadEnter() {
|
||||
assert(m_opaque);
|
||||
LibEventServer *server = (LibEventServer*)m_opaque;
|
||||
server->onThreadEnter();
|
||||
if (RuntimeOption::EnableDebugger) {
|
||||
Eval::Debugger::RegisterThread();
|
||||
}
|
||||
@@ -141,8 +140,6 @@ void LibEventWorker::onThreadEnter() {
|
||||
|
||||
void LibEventWorker::onThreadExit() {
|
||||
assert(m_opaque);
|
||||
LibEventServer *server = (LibEventServer*)m_opaque;
|
||||
server->onThreadExit();
|
||||
m_handler.reset();
|
||||
}
|
||||
|
||||
@@ -150,12 +147,10 @@ void LibEventWorker::onThreadExit() {
|
||||
// constructor and destructor
|
||||
|
||||
LibEventServer::LibEventServer(const std::string &address, int port,
|
||||
int thread, int timeoutSeconds)
|
||||
int thread)
|
||||
: Server(address, port, thread),
|
||||
m_accept_sock(-1),
|
||||
m_accept_sock_ssl(-1),
|
||||
m_timeoutThreadData(timeoutSeconds),
|
||||
m_timeoutThread(&m_timeoutThreadData, &TimeoutThread::run),
|
||||
m_dispatcher(thread, RuntimeOption::ServerThreadRoundRobin,
|
||||
RuntimeOption::ServerThreadDropCacheTimeoutSeconds,
|
||||
RuntimeOption::ServerThreadDropStack,
|
||||
@@ -225,14 +220,10 @@ void LibEventServer::start() {
|
||||
setStatus(RunStatus::RUNNING);
|
||||
m_dispatcher.start();
|
||||
m_dispatcherThread.start();
|
||||
m_timeoutThread.start();
|
||||
}
|
||||
|
||||
void LibEventServer::waitForEnd() {
|
||||
m_dispatcherThread.waitForEnd();
|
||||
|
||||
m_timeoutThreadData.stop();
|
||||
m_timeoutThread.waitForEnd();
|
||||
}
|
||||
|
||||
void LibEventServer::dispatchWithTimeout(int timeoutSeconds) {
|
||||
@@ -269,7 +260,7 @@ void LibEventServer::dispatch() {
|
||||
}
|
||||
m_responseQueue.close();
|
||||
|
||||
// flusing all remaining events
|
||||
// flushing all remaining events
|
||||
if (RuntimeOption::ServerGracefulShutdownWait) {
|
||||
dispatchWithTimeout(RuntimeOption::ServerGracefulShutdownWait);
|
||||
}
|
||||
@@ -325,10 +316,6 @@ void LibEventServer::stop() {
|
||||
}
|
||||
m_dispatcherThread.waitForEnd();
|
||||
|
||||
// wait for the timeout thread to stop
|
||||
m_timeoutThreadData.stop();
|
||||
m_timeoutThread.waitForEnd();
|
||||
|
||||
evhttp_free(m_server);
|
||||
m_server = nullptr;
|
||||
}
|
||||
@@ -370,16 +357,6 @@ int LibEventServer::getAcceptSocketSSL() {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// request/response handling
|
||||
|
||||
void LibEventServer::onThreadEnter() {
|
||||
m_timeoutThreadData.registerRequestThread
|
||||
(&ThreadInfo::s_threadInfo->m_reqInjectionData);
|
||||
}
|
||||
|
||||
void LibEventServer::onThreadExit() {
|
||||
m_timeoutThreadData.removeRequestThread
|
||||
(&ThreadInfo::s_threadInfo->m_reqInjectionData);
|
||||
}
|
||||
|
||||
void LibEventServer::onRequest(struct evhttp_request *request) {
|
||||
if (RuntimeOption::EnableKeepAlive &&
|
||||
RuntimeOption::ConnectionTimeoutSeconds > 0) {
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
|
||||
#include "hphp/runtime/server/server.h"
|
||||
#include "hphp/runtime/server/libevent_transport.h"
|
||||
#include "hphp/runtime/base/timeout_thread.h"
|
||||
#include "hphp/runtime/server/job_queue_vm_stack.h"
|
||||
#include "hphp/util/job_queue.h"
|
||||
#include "hphp/util/process.h"
|
||||
@@ -127,8 +126,7 @@ public:
|
||||
/**
|
||||
* Constructor and destructor.
|
||||
*/
|
||||
LibEventServer(const std::string &address, int port, int thread,
|
||||
int timeoutSeconds);
|
||||
LibEventServer(const std::string &address, int port, int thread);
|
||||
~LibEventServer();
|
||||
|
||||
// implementing Server
|
||||
@@ -146,9 +144,6 @@ public:
|
||||
}
|
||||
int getLibEventConnectionCount();
|
||||
|
||||
void onThreadEnter();
|
||||
void onThreadExit();
|
||||
|
||||
/**
|
||||
* Request handler called by evhttp library.
|
||||
*/
|
||||
@@ -188,9 +183,6 @@ protected:
|
||||
event m_eventStop;
|
||||
CPipe m_pipeStop;
|
||||
|
||||
TimeoutThread m_timeoutThreadData;
|
||||
AsyncFunc<TimeoutThread> m_timeoutThread;
|
||||
|
||||
private:
|
||||
JobQueueDispatcher<LibEventJobPtr, LibEventWorker> m_dispatcher;
|
||||
AsyncFunc<LibEventServer> m_dispatcherThread;
|
||||
|
||||
@@ -33,8 +33,7 @@ public:
|
||||
ServerPtr LibEventServerFactory::createServer(const ServerOptions& options) {
|
||||
if (options.m_serverFD != -1 || options.m_sslFD != -1) {
|
||||
auto const server = boost::make_shared<LibEventServerWithFd>
|
||||
(options.m_address, options.m_port, options.m_numThreads,
|
||||
options.m_timeout.count());
|
||||
(options.m_address, options.m_port, options.m_numThreads);
|
||||
server->setServerSocketFd(options.m_serverFD);
|
||||
server->setSSLSocketFd(options.m_sslFD);
|
||||
return server;
|
||||
@@ -42,15 +41,13 @@ ServerPtr LibEventServerFactory::createServer(const ServerOptions& options) {
|
||||
|
||||
if (!options.m_takeoverFilename.empty()) {
|
||||
auto const server = boost::make_shared<LibEventServerWithTakeover>
|
||||
(options.m_address, options.m_port, options.m_numThreads,
|
||||
options.m_timeout.count());
|
||||
(options.m_address, options.m_port, options.m_numThreads);
|
||||
server->setTransferFilename(options.m_takeoverFilename);
|
||||
return server;
|
||||
}
|
||||
|
||||
return boost::make_shared<LibEventServer>(options.m_address, options.m_port,
|
||||
options.m_numThreads,
|
||||
options.m_timeout.count());
|
||||
options.m_numThreads);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
namespace HPHP {
|
||||
|
||||
LibEventServerWithFd::LibEventServerWithFd
|
||||
(const std::string &address, int port, int thread, int timeoutSeconds)
|
||||
: LibEventServer(address, port, thread, timeoutSeconds)
|
||||
(const std::string &address, int port, int thread)
|
||||
: LibEventServer(address, port, thread)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -27,8 +27,7 @@ namespace HPHP {
|
||||
*/
|
||||
class LibEventServerWithFd : public LibEventServer {
|
||||
public:
|
||||
LibEventServerWithFd(const std::string &address, int port, int thread,
|
||||
int timeoutSeconds);
|
||||
LibEventServerWithFd(const std::string &address, int port, int thread);
|
||||
|
||||
void setServerSocketFd(int sock_fd) {
|
||||
m_accept_sock = sock_fd;
|
||||
|
||||
@@ -83,8 +83,8 @@ static int fd_transfer_request_handler(
|
||||
}
|
||||
|
||||
LibEventServerWithTakeover::LibEventServerWithTakeover
|
||||
(const std::string &address, int port, int thread, int timeoutSeconds)
|
||||
: LibEventServer(address, port, thread, timeoutSeconds),
|
||||
(const std::string &address, int port, int thread)
|
||||
: LibEventServer(address, port, thread),
|
||||
m_delete_handle(nullptr),
|
||||
m_took_over(false),
|
||||
m_takeover_state(TakeoverState::NotStarted)
|
||||
|
||||
@@ -28,8 +28,7 @@ namespace HPHP {
|
||||
*/
|
||||
class LibEventServerWithTakeover : public LibEventServer {
|
||||
public:
|
||||
LibEventServerWithTakeover(const std::string &address, int port, int thread,
|
||||
int timeoutSeconds);
|
||||
LibEventServerWithTakeover(const std::string &address, int port, int thread);
|
||||
|
||||
virtual void stop();
|
||||
|
||||
|
||||
@@ -239,7 +239,7 @@ struct PageletWorker
|
||||
virtual void doJob(PageletTransport *job) {
|
||||
try {
|
||||
job->onRequestStart(job->getStartTimer());
|
||||
HttpRequestHandler().handleRequest(job);
|
||||
HttpRequestHandler(0).handleRequest(job);
|
||||
job->decRefCount();
|
||||
} catch (...) {
|
||||
Logger::Error("HttpRequestHandler leaked exceptions");
|
||||
|
||||
@@ -32,8 +32,9 @@ using std::set;
|
||||
namespace HPHP {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
RPCRequestHandler::RPCRequestHandler(bool info /* = true */)
|
||||
: m_requestsSinceReset(0),
|
||||
RPCRequestHandler::RPCRequestHandler(int timeout, bool info)
|
||||
: RequestHandler(timeout),
|
||||
m_requestsSinceReset(0),
|
||||
m_reset(false),
|
||||
m_logResets(info),
|
||||
m_returnEncodeType(ReturnEncodeType::Json) {
|
||||
@@ -145,7 +146,8 @@ void RPCRequestHandler::handleRequest(Transport *transport) {
|
||||
HttpRequestHandler::GetAccessLog().log(transport, vhost);
|
||||
return;
|
||||
}
|
||||
vhost->setRequestTimeoutSeconds();
|
||||
ThreadInfo::s_threadInfo->m_reqInjectionData.
|
||||
setTimeout(vhost->getRequestTimeoutSeconds(getDefaultTimeout()));
|
||||
|
||||
// resolve source root
|
||||
string host = transport->getHeader("Host");
|
||||
@@ -184,9 +186,6 @@ const StaticString
|
||||
bool RPCRequestHandler::executePHPFunction(Transport *transport,
|
||||
SourceRootInfo &sourceRootInfo,
|
||||
ReturnEncodeType returnEncodeType) {
|
||||
// reset timeout counter
|
||||
ThreadInfo::s_threadInfo->m_reqInjectionData.started = time(0);
|
||||
|
||||
string rpcFunc = transport->getCommand();
|
||||
{
|
||||
ServerStatsHelper ssh("input");
|
||||
|
||||
@@ -35,7 +35,7 @@ public:
|
||||
Serialize = 2,
|
||||
};
|
||||
|
||||
explicit RPCRequestHandler(bool info = true);
|
||||
RPCRequestHandler(int timeout, bool info);
|
||||
virtual ~RPCRequestHandler();
|
||||
|
||||
void setServerInfo(SatelliteServerInfoPtr info) { m_serverInfo = info;}
|
||||
|
||||
@@ -92,8 +92,9 @@ public:
|
||||
: m_allowedURLs(info->getURLs()) {
|
||||
m_server = ServerFactoryRegistry::createServer
|
||||
(RuntimeOption::ServerType, RuntimeOption::ServerIP, info->getPort(),
|
||||
info->getThreadCount(), info->getTimeoutSeconds());
|
||||
m_server->setRequestHandlerFactory<HttpRequestHandler>();
|
||||
info->getThreadCount());
|
||||
m_server->setRequestHandlerFactory<HttpRequestHandler>(
|
||||
info->getTimeoutSeconds().count());
|
||||
m_server->setUrlChecker(std::bind(&InternalPageServer::checkURL, this,
|
||||
std::placeholders::_1));
|
||||
}
|
||||
@@ -131,8 +132,9 @@ public:
|
||||
explicit DanglingPageServer(SatelliteServerInfoPtr info) {
|
||||
m_server = ServerFactoryRegistry::createServer
|
||||
(RuntimeOption::ServerType, RuntimeOption::ServerIP, info->getPort(),
|
||||
info->getThreadCount(), info->getTimeoutSeconds());
|
||||
m_server->setRequestHandlerFactory<HttpRequestHandler>();
|
||||
info->getThreadCount());
|
||||
m_server->setRequestHandlerFactory<HttpRequestHandler>(
|
||||
info->getTimeoutSeconds().count());
|
||||
}
|
||||
|
||||
virtual void start() {
|
||||
@@ -154,9 +156,10 @@ public:
|
||||
explicit RPCServer(SatelliteServerInfoPtr info) {
|
||||
m_server = ServerFactoryRegistry::createServer
|
||||
(RuntimeOption::ServerType, RuntimeOption::ServerIP, info->getPort(),
|
||||
info->getThreadCount(), info->getTimeoutSeconds());
|
||||
info->getThreadCount());
|
||||
m_server->setRequestHandlerFactory([info] {
|
||||
auto handler = make_unique<RPCRequestHandler>();
|
||||
auto handler = make_unique<RPCRequestHandler>(
|
||||
info->getTimeoutSeconds().count(), true);
|
||||
handler->setServerInfo(info);
|
||||
return handler;
|
||||
});
|
||||
|
||||
@@ -58,9 +58,8 @@ Server::Server(const std::string &address, int port, int threadCount)
|
||||
|
||||
ServerPtr ServerFactory::createServer(const std::string &address,
|
||||
uint16_t port,
|
||||
int numThreads,
|
||||
std::chrono::seconds timeout) {
|
||||
ServerOptions options(address, port, numThreads, timeout);
|
||||
int numThreads) {
|
||||
ServerOptions options(address, port, numThreads);
|
||||
return createServer(options);
|
||||
}
|
||||
|
||||
@@ -76,10 +75,9 @@ ServerFactoryRegistry *ServerFactoryRegistry::getInstance() {
|
||||
ServerPtr ServerFactoryRegistry::createServer(const std::string &type,
|
||||
const std::string &address,
|
||||
uint16_t port,
|
||||
int numThreads,
|
||||
std::chrono::seconds timeout) {
|
||||
int numThreads) {
|
||||
auto factory = getInstance()->getFactory(type);
|
||||
ServerOptions options(address, port, numThreads, timeout);
|
||||
ServerOptions options(address, port, numThreads);
|
||||
return factory->createServer(options);
|
||||
}
|
||||
|
||||
|
||||
@@ -79,12 +79,16 @@ DECLARE_BOOST_TYPES(ServerFactory);
|
||||
*/
|
||||
class RequestHandler {
|
||||
public:
|
||||
explicit RequestHandler(int timeout) : m_timeout(timeout) {}
|
||||
virtual ~RequestHandler() {}
|
||||
|
||||
/**
|
||||
* Sub-class handles a request by implementing this function.
|
||||
*/
|
||||
virtual void handleRequest(Transport *transport) = 0;
|
||||
int getDefaultTimeout() const { return m_timeout; }
|
||||
private:
|
||||
int m_timeout;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -142,9 +146,9 @@ public:
|
||||
* GenericRequestHandlerFactory for the specified handler type.
|
||||
*/
|
||||
template<class TRequestHandler>
|
||||
void setRequestHandlerFactory() {
|
||||
setRequestHandlerFactory([] {
|
||||
return std::unique_ptr<RequestHandler>(new TRequestHandler());
|
||||
void setRequestHandlerFactory(int timeout) {
|
||||
setRequestHandlerFactory([timeout] {
|
||||
return std::unique_ptr<RequestHandler>(new TRequestHandler(timeout));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -256,12 +260,10 @@ class ServerOptions {
|
||||
public:
|
||||
ServerOptions(const std::string &address,
|
||||
uint16_t port,
|
||||
int numThreads,
|
||||
std::chrono::seconds timeout)
|
||||
int numThreads)
|
||||
: m_address(address),
|
||||
m_port(port),
|
||||
m_numThreads(numThreads),
|
||||
m_timeout(timeout),
|
||||
m_serverFD(-1),
|
||||
m_sslFD(-1),
|
||||
m_takeoverFilename() {
|
||||
@@ -270,7 +272,6 @@ public:
|
||||
std::string m_address;
|
||||
uint16_t m_port;
|
||||
int m_numThreads;
|
||||
std::chrono::seconds m_timeout;
|
||||
int m_serverFD;
|
||||
int m_sslFD;
|
||||
std::string m_takeoverFilename;
|
||||
@@ -287,8 +288,7 @@ public:
|
||||
|
||||
ServerPtr createServer(const std::string &address,
|
||||
uint16_t port,
|
||||
int numThreads,
|
||||
std::chrono::seconds timeout);
|
||||
int numThreads);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -306,8 +306,7 @@ public:
|
||||
static ServerPtr createServer(const std::string &type,
|
||||
const std::string &address,
|
||||
uint16_t port,
|
||||
int numThreads,
|
||||
std::chrono::seconds timeout);
|
||||
int numThreads);
|
||||
|
||||
void registerFactory(const std::string &name,
|
||||
const ServerFactoryPtr &factory);
|
||||
|
||||
@@ -51,7 +51,7 @@ void ServiceThread::threadRun() {
|
||||
hdf["url"] = m_url;
|
||||
hdf["remote_host"] = RuntimeOption::ServerIP;
|
||||
|
||||
HttpRequestHandler handler;
|
||||
HttpRequestHandler handler(0);
|
||||
handler.disablePathTranslation();
|
||||
|
||||
do {
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#include "hphp/runtime/base/preg.h"
|
||||
#include "hphp/runtime/base/runtime_option.h"
|
||||
#include "hphp/runtime/base/comparisons.h"
|
||||
#include "hphp/runtime/base/timeout_thread.h"
|
||||
#include "hphp/runtime/base/string_util.h"
|
||||
#include "hphp/util/util.h"
|
||||
|
||||
@@ -130,14 +129,9 @@ void VirtualHost::addAllowedDirectories(const std::vector<std::string>& dirs) {
|
||||
}
|
||||
}
|
||||
|
||||
void VirtualHost::setRequestTimeoutSeconds() const {
|
||||
if (m_runtimeOption.requestTimeoutSeconds != -1) {
|
||||
TimeoutThread::DeferTimeout(m_runtimeOption.requestTimeoutSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
int VirtualHost::getRequestTimeoutSeconds() const {
|
||||
return m_runtimeOption.requestTimeoutSeconds;
|
||||
int VirtualHost::getRequestTimeoutSeconds(int defaultTimeout) const {
|
||||
return m_runtimeOption.requestTimeoutSeconds < 0 ?
|
||||
defaultTimeout : m_runtimeOption.requestTimeoutSeconds;
|
||||
}
|
||||
|
||||
VirtualHost::VirtualHost() : m_disabled(false) {
|
||||
|
||||
@@ -41,8 +41,7 @@ public:
|
||||
|
||||
void init(Hdf vh);
|
||||
void addAllowedDirectories(const std::vector<std::string>& dirs);
|
||||
void setRequestTimeoutSeconds() const;
|
||||
int getRequestTimeoutSeconds() const;
|
||||
int getRequestTimeoutSeconds(int defaultTimeout) const;
|
||||
|
||||
const std::string &getName() const { return m_name;}
|
||||
const std::string &getPathTranslation() const { return m_pathTranslation;}
|
||||
|
||||
@@ -32,7 +32,7 @@ void WarmupRequestHandler::handleRequest(Transport *transport) {
|
||||
}
|
||||
|
||||
std::unique_ptr<RequestHandler> WarmupRequestHandlerFactory::createHandler() {
|
||||
return make_unique<WarmupRequestHandler>(shared_from_this());
|
||||
return make_unique<WarmupRequestHandler>(m_timeout, shared_from_this());
|
||||
}
|
||||
|
||||
void WarmupRequestHandlerFactory::bumpReqCount() {
|
||||
|
||||
@@ -32,8 +32,9 @@ DECLARE_BOOST_TYPES(WarmupRequestHandlerFactory);
|
||||
*/
|
||||
class WarmupRequestHandler : public RequestHandler {
|
||||
public:
|
||||
explicit WarmupRequestHandler(const WarmupRequestHandlerFactoryPtr& factory)
|
||||
: m_factory(factory) {}
|
||||
explicit WarmupRequestHandler(int timeout,
|
||||
const WarmupRequestHandlerFactoryPtr& factory)
|
||||
: RequestHandler(timeout), m_factory(factory), m_reqHandler(timeout) {}
|
||||
|
||||
virtual void handleRequest(Transport *transport);
|
||||
|
||||
@@ -47,10 +48,12 @@ class WarmupRequestHandlerFactory :
|
||||
public:
|
||||
WarmupRequestHandlerFactory(ServerPtr server,
|
||||
uint32_t additionalThreads,
|
||||
uint32_t reqCount)
|
||||
uint32_t reqCount,
|
||||
int timeout)
|
||||
: m_additionalThreads(additionalThreads),
|
||||
m_reqNumber(0),
|
||||
m_warmupReqThreshold(reqCount),
|
||||
m_timeout(timeout),
|
||||
m_server(server) {}
|
||||
|
||||
std::unique_ptr<RequestHandler> createHandler();
|
||||
@@ -61,6 +64,7 @@ private:
|
||||
std::atomic<uint32_t> m_additionalThreads;
|
||||
std::atomic<uint32_t> m_reqNumber;
|
||||
uint32_t const m_warmupReqThreshold;
|
||||
int m_timeout;
|
||||
// The server has a shared pointer to us, so use a weak pointer to the
|
||||
// server to avoid a circular reference.
|
||||
ServerWeakPtr m_server;
|
||||
|
||||
@@ -143,19 +143,22 @@ private:
|
||||
string m_reqInitDoc;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static IMPLEMENT_THREAD_LOCAL(XboxServerInfoPtr, s_xbox_server_info);
|
||||
static IMPLEMENT_THREAD_LOCAL(string, s_xbox_prev_req_init_doc);
|
||||
|
||||
class XboxRequestHandler: public RPCRequestHandler {
|
||||
public:
|
||||
XboxRequestHandler() : RPCRequestHandler(Info) {}
|
||||
XboxRequestHandler() : RPCRequestHandler(
|
||||
(*s_xbox_server_info)->getTimeoutSeconds().count(), Info) {}
|
||||
static bool Info;
|
||||
};
|
||||
|
||||
bool XboxRequestHandler::Info = false;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static IMPLEMENT_THREAD_LOCAL(XboxServerInfoPtr, s_xbox_server_info);
|
||||
static IMPLEMENT_THREAD_LOCAL(XboxRequestHandler, s_xbox_request_handler);
|
||||
static IMPLEMENT_THREAD_LOCAL(string, s_xbox_prev_req_init_doc);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct XboxWorker
|
||||
|
||||
@@ -600,10 +600,7 @@ void
|
||||
Stack::requestInit() {
|
||||
m_elms = t_se->elms();
|
||||
if (Transl::trustSigSegv) {
|
||||
RequestInjectionData& data = ThreadInfo::s_threadInfo->m_reqInjectionData;
|
||||
Lock l(data.surpriseLock);
|
||||
assert(data.surprisePage == nullptr);
|
||||
data.surprisePage = m_elms;
|
||||
ThreadInfo::s_threadInfo->m_reqInjectionData.setSurprisePage(m_elms);
|
||||
}
|
||||
// Burn one element of the stack, to satisfy the constraint that
|
||||
// valid m_top values always have the same high-order (>
|
||||
@@ -626,11 +623,8 @@ void
|
||||
Stack::requestExit() {
|
||||
if (m_elms != nullptr) {
|
||||
if (Transl::trustSigSegv) {
|
||||
RequestInjectionData& data = ThreadInfo::s_threadInfo->m_reqInjectionData;
|
||||
Lock l(data.surpriseLock);
|
||||
assert(data.surprisePage == m_elms);
|
||||
ThreadInfo::s_threadInfo->m_reqInjectionData.setSurprisePage(nullptr);
|
||||
unprotect();
|
||||
data.surprisePage = nullptr;
|
||||
}
|
||||
m_elms = nullptr;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
class TestCurlRequestHandler : public RequestHandler {
|
||||
public:
|
||||
explicit TestCurlRequestHandler(int timeout) : RequestHandler(timeout) {}
|
||||
// implementing RequestHandler
|
||||
virtual void handleRequest(Transport *transport) {
|
||||
transport->addHeader("ECHOED", transport->getHeader("ECHO").c_str());
|
||||
@@ -56,8 +57,8 @@ static ServerPtr runServer() {
|
||||
for (s_server_port = PORT_MIN; s_server_port <= PORT_MAX; s_server_port++) {
|
||||
try {
|
||||
ServerPtr server = boost::make_shared<LibEventServer>(
|
||||
"127.0.0.1", s_server_port, 4, -1);
|
||||
server->setRequestHandlerFactory<TestCurlRequestHandler>();
|
||||
"127.0.0.1", s_server_port, 4);
|
||||
server->setRequestHandlerFactory<TestCurlRequestHandler>(0);
|
||||
server->start();
|
||||
return server;
|
||||
|
||||
|
||||
@@ -181,6 +181,7 @@ void TestServer::StopServer() {
|
||||
|
||||
class TestServerRequestHandler : public RequestHandler {
|
||||
public:
|
||||
explicit TestServerRequestHandler(int timeout) : RequestHandler(timeout) {}
|
||||
// implementing RequestHandler
|
||||
virtual void handleRequest(Transport *transport) {
|
||||
// do nothing
|
||||
@@ -191,8 +192,8 @@ static int find_server_port(int port_min, int port_max) {
|
||||
for (int port = port_min; ; port++) {
|
||||
try {
|
||||
ServerPtr server = boost::make_shared<LibEventServer>(
|
||||
"127.0.0.1", port, 50, -1);
|
||||
server->setRequestHandlerFactory<TestServerRequestHandler>();
|
||||
"127.0.0.1", port, 50);
|
||||
server->setRequestHandlerFactory<TestServerRequestHandler>(30);
|
||||
server->start();
|
||||
server->stop();
|
||||
server->waitForEnd();
|
||||
@@ -420,7 +421,7 @@ public:
|
||||
}
|
||||
|
||||
void process() {
|
||||
HttpRequestHandler handler;
|
||||
HttpRequestHandler handler(0);
|
||||
for (unsigned int i = 0; i < 100; i++) {
|
||||
handler.handleRequest(this);
|
||||
}
|
||||
@@ -531,6 +532,7 @@ bool TestServer::TestInheritFdServer() {
|
||||
|
||||
class EchoHandler : public RequestHandler {
|
||||
public:
|
||||
explicit EchoHandler(int timeout) : RequestHandler(timeout) {}
|
||||
// implementing RequestHandler
|
||||
virtual void handleRequest(Transport *transport) {
|
||||
HeaderMap headers;
|
||||
@@ -569,8 +571,8 @@ bool TestServer::TestHttpClient() {
|
||||
for (s_server_port = PORT_MIN; s_server_port <= PORT_MAX; s_server_port++) {
|
||||
try {
|
||||
server = boost::make_shared<LibEventServer>(
|
||||
"127.0.0.1", s_server_port, 50, -1);
|
||||
server->setRequestHandlerFactory<EchoHandler>();
|
||||
"127.0.0.1", s_server_port, 50);
|
||||
server->setRequestHandlerFactory<EchoHandler>(0);
|
||||
server->start();
|
||||
break;
|
||||
} catch (const FailedToListenException& e) {
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário