diff --git a/hphp/NEWS b/hphp/NEWS index 11a6bf101..0eaf32877 100644 --- a/hphp/NEWS +++ b/hphp/NEWS @@ -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) diff --git a/hphp/runtime/base/builtin_functions.cpp b/hphp/runtime/base/builtin_functions.cpp index fac9395d5..b5811cd84 100644 --- a/hphp/runtime/base/builtin_functions.cpp +++ b/hphp/runtime/base/builtin_functions.cpp @@ -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 @@ -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(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(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; } diff --git a/hphp/runtime/base/execution_context.cpp b/hphp/runtime/base/execution_context.cpp index 7b97d2f26..3054b453e 100644 --- a/hphp/runtime/base/execution_context.cpp +++ b/hphp/runtime/base/execution_context.cpp @@ -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)); } /////////////////////////////////////////////////////////////////////////////// diff --git a/hphp/runtime/base/execution_context.h b/hphp/runtime/base/execution_context.h index bb6c91cd8..32c0dcd34 100644 --- a/hphp/runtime/base/execution_context.h +++ b/hphp/runtime/base/execution_context.h @@ -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 diff --git a/hphp/runtime/base/ini_setting.cpp b/hphp/runtime/base/ini_setting.cpp index d7e8b6632..3f4eb1d81 100644 --- a/hphp/runtime/base/ini_setting.cpp +++ b/hphp/runtime/base/ini_setting.cpp @@ -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); diff --git a/hphp/runtime/base/program_functions.cpp b/hphp/runtime/base/program_functions.cpp index 073d4af39..e12e997cd 100644 --- a/hphp/runtime/base/program_functions.cpp +++ b/hphp/runtime/base/program_functions.cpp @@ -60,6 +60,7 @@ #include #include #include +#include #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(); diff --git a/hphp/runtime/base/thread_info.cpp b/hphp/runtime/base/thread_info.cpp index a3d78e78e..331c94c55 100644 --- a/hphp/runtime/base/thread_info.cpp +++ b/hphp/runtime/base/thread_info.cpp @@ -22,6 +22,8 @@ #include "hphp/util/lock.h" #include "hphp/util/alloc.h" +#include + 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); diff --git a/hphp/runtime/base/timeout_thread.cpp b/hphp/runtime/base/timeout_thread.cpp deleted file mode 100644 index 7b7750a79..000000000 --- a/hphp/runtime/base/timeout_thread.cpp +++ /dev/null @@ -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 - -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); -} - -/////////////////////////////////////////////////////////////////////////////// -} diff --git a/hphp/runtime/base/timeout_thread.h b/hphp/runtime/base/timeout_thread.h deleted file mode 100644 index 5037e17be..000000000 --- a/hphp/runtime/base/timeout_thread.h +++ /dev/null @@ -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 - -#include "hphp/runtime/base/types.h" -#include "hphp/util/base.h" -#include "hphp/util/process.h" -#include "hphp/util/synchronizable.h" -#include - -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 m_pendingIds; - bool m_stopped; - - event_base *m_eventBase; - - struct ClientThread { - RequestInjectionData* data; - event e; - }; - std::map m_clients; - int m_timeoutSeconds; - - // signal to wake up the thread - event m_eventPipe; - CPipe m_pipe; -}; - -/////////////////////////////////////////////////////////////////////////////// -} - -#endif // incl_HPHP_TIMEOUT_THREAD_H_ - diff --git a/hphp/runtime/base/types.h b/hphp/runtime/base/types.h index 8cc6e9d9e..fada97bee 100644 --- a/hphp/runtime/base/types.h +++ b/hphp/runtime/base/types.h @@ -23,15 +23,17 @@ #include "hphp/util/thread_local.h" #include "hphp/util/mutex.h" #include "hphp/util/case_insensitive.h" -#include #include "hphp/runtime/base/macros.h" #include "hphp/runtime/base/memory_manager.h" #include #include -#include + #include +#include #include +#include +#include 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 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(); diff --git a/hphp/runtime/ext/ext_options.cpp b/hphp/runtime/ext/ext_options.cpp index fae4b4867..90aec44bc 100644 --- a/hphp/runtime/ext/ext_options.cpp +++ b/hphp/runtime/ext/ext_options.cpp @@ -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() { diff --git a/hphp/runtime/ext/ext_server.cpp b/hphp/runtime/ext/ext_server.cpp index 90acda916..315de378b 100644 --- a/hphp/runtime/ext/ext_server.cpp +++ b/hphp/runtime/ext/ext_server.cpp @@ -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 { diff --git a/hphp/runtime/server/admin_request_handler.cpp b/hphp/runtime/server/admin_request_handler.cpp index 4f002ee2e..1432c371b 100644 --- a/hphp/runtime/server/admin_request_handler.cpp +++ b/hphp/runtime/server/admin_request_handler.cpp @@ -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. diff --git a/hphp/runtime/server/admin_request_handler.h b/hphp/runtime/server/admin_request_handler.h index 86fdcf6d0..02493b0aa 100644 --- a/hphp/runtime/server/admin_request_handler.h +++ b/hphp/runtime/server/admin_request_handler.h @@ -28,7 +28,7 @@ public: static AccessLog &GetAccessLog() { return s_accessLog; } public: - AdminRequestHandler(); + explicit AdminRequestHandler(int timeout); // implementing RequestHandler virtual void handleRequest(Transport *transport); diff --git a/hphp/runtime/server/http_request_handler.cpp b/hphp/runtime/server/http_request_handler.cpp index 40fae646a..123aa01c7 100644 --- a/hphp/runtime/server/http_request_handler.cpp +++ b/hphp/runtime/server/http_request_handler.cpp @@ -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 { diff --git a/hphp/runtime/server/http_request_handler.h b/hphp/runtime/server/http_request_handler.h index 83b83b9e3..3ab2e81aa 100644 --- a/hphp/runtime/server/http_request_handler.h +++ b/hphp/runtime/server/http_request_handler.h @@ -38,7 +38,7 @@ public: static AccessLog &GetAccessLog() { return s_accessLog; } public: - HttpRequestHandler(); + explicit HttpRequestHandler(int timeout); // implementing RequestHandler virtual void handleRequest(Transport *transport); diff --git a/hphp/runtime/server/http_server.cpp b/hphp/runtime/server/http_server.cpp index 89602a4e0..1f0e6299e 100644 --- a/hphp/runtime/server/http_server.cpp +++ b/hphp/runtime/server/http_server.cpp @@ -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( m_pageServer, additionalThreads, - RuntimeOption::ServerWarmupThrottleRequestCount); + RuntimeOption::ServerWarmupThrottleRequestCount, + RuntimeOption::RequestTimeoutSeconds); m_pageServer->setRequestHandlerFactory([handlerFactory] { return handlerFactory->createHandler(); }); } else { - m_pageServer->setRequestHandlerFactory(); + m_pageServer->setRequestHandlerFactory( + 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(); + RuntimeOption::AdminThreadCount); + m_adminServer->setRequestHandlerFactory( + 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) { diff --git a/hphp/runtime/server/libevent_server.cpp b/hphp/runtime/server/libevent_server.cpp index d5c509859..543086ca9 100644 --- a/hphp/runtime/server/libevent_server.cpp +++ b/hphp/runtime/server/libevent_server.cpp @@ -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) { diff --git a/hphp/runtime/server/libevent_server.h b/hphp/runtime/server/libevent_server.h index c58be7e8e..6fa397faa 100644 --- a/hphp/runtime/server/libevent_server.h +++ b/hphp/runtime/server/libevent_server.h @@ -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 m_timeoutThread; - private: JobQueueDispatcher m_dispatcher; AsyncFunc m_dispatcherThread; diff --git a/hphp/runtime/server/libevent_server_factory.cpp b/hphp/runtime/server/libevent_server_factory.cpp index 561ccefec..4cc002391 100644 --- a/hphp/runtime/server/libevent_server_factory.cpp +++ b/hphp/runtime/server/libevent_server_factory.cpp @@ -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 - (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 - (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(options.m_address, options.m_port, - options.m_numThreads, - options.m_timeout.count()); + options.m_numThreads); } /////////////////////////////////////////////////////////////////////////////// diff --git a/hphp/runtime/server/libevent_server_with_fd.cpp b/hphp/runtime/server/libevent_server_with_fd.cpp index 53d01311d..576aa1438 100644 --- a/hphp/runtime/server/libevent_server_with_fd.cpp +++ b/hphp/runtime/server/libevent_server_with_fd.cpp @@ -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) { } diff --git a/hphp/runtime/server/libevent_server_with_fd.h b/hphp/runtime/server/libevent_server_with_fd.h index fad957096..0919d8c2c 100644 --- a/hphp/runtime/server/libevent_server_with_fd.h +++ b/hphp/runtime/server/libevent_server_with_fd.h @@ -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; diff --git a/hphp/runtime/server/libevent_server_with_takeover.cpp b/hphp/runtime/server/libevent_server_with_takeover.cpp index 3e8420f11..0a8aaec3a 100644 --- a/hphp/runtime/server/libevent_server_with_takeover.cpp +++ b/hphp/runtime/server/libevent_server_with_takeover.cpp @@ -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) diff --git a/hphp/runtime/server/libevent_server_with_takeover.h b/hphp/runtime/server/libevent_server_with_takeover.h index 77c3670f5..ddc03897e 100644 --- a/hphp/runtime/server/libevent_server_with_takeover.h +++ b/hphp/runtime/server/libevent_server_with_takeover.h @@ -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(); diff --git a/hphp/runtime/server/pagelet_server.cpp b/hphp/runtime/server/pagelet_server.cpp index 70fa5b239..f1f3f837f 100644 --- a/hphp/runtime/server/pagelet_server.cpp +++ b/hphp/runtime/server/pagelet_server.cpp @@ -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"); diff --git a/hphp/runtime/server/rpc_request_handler.cpp b/hphp/runtime/server/rpc_request_handler.cpp index 34c442d08..8019027c4 100644 --- a/hphp/runtime/server/rpc_request_handler.cpp +++ b/hphp/runtime/server/rpc_request_handler.cpp @@ -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"); diff --git a/hphp/runtime/server/rpc_request_handler.h b/hphp/runtime/server/rpc_request_handler.h index a79252ee6..867c8ff1f 100644 --- a/hphp/runtime/server/rpc_request_handler.h +++ b/hphp/runtime/server/rpc_request_handler.h @@ -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;} diff --git a/hphp/runtime/server/satellite_server.cpp b/hphp/runtime/server/satellite_server.cpp index 9cae035b8..37697a8a5 100644 --- a/hphp/runtime/server/satellite_server.cpp +++ b/hphp/runtime/server/satellite_server.cpp @@ -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(); + info->getThreadCount()); + m_server->setRequestHandlerFactory( + 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(); + info->getThreadCount()); + m_server->setRequestHandlerFactory( + 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(); + auto handler = make_unique( + info->getTimeoutSeconds().count(), true); handler->setServerInfo(info); return handler; }); diff --git a/hphp/runtime/server/server.cpp b/hphp/runtime/server/server.cpp index 8a4b4a09b..50b0dde5d 100644 --- a/hphp/runtime/server/server.cpp +++ b/hphp/runtime/server/server.cpp @@ -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); } diff --git a/hphp/runtime/server/server.h b/hphp/runtime/server/server.h index 43e4de918..5eff9c87c 100644 --- a/hphp/runtime/server/server.h +++ b/hphp/runtime/server/server.h @@ -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 - void setRequestHandlerFactory() { - setRequestHandlerFactory([] { - return std::unique_ptr(new TRequestHandler()); + void setRequestHandlerFactory(int timeout) { + setRequestHandlerFactory([timeout] { + return std::unique_ptr(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); diff --git a/hphp/runtime/server/service_thread.cpp b/hphp/runtime/server/service_thread.cpp index e584fb887..5299d4a14 100644 --- a/hphp/runtime/server/service_thread.cpp +++ b/hphp/runtime/server/service_thread.cpp @@ -51,7 +51,7 @@ void ServiceThread::threadRun() { hdf["url"] = m_url; hdf["remote_host"] = RuntimeOption::ServerIP; - HttpRequestHandler handler; + HttpRequestHandler handler(0); handler.disablePathTranslation(); do { diff --git a/hphp/runtime/server/virtual_host.cpp b/hphp/runtime/server/virtual_host.cpp index 2fec30172..6484863db 100644 --- a/hphp/runtime/server/virtual_host.cpp +++ b/hphp/runtime/server/virtual_host.cpp @@ -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& 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) { diff --git a/hphp/runtime/server/virtual_host.h b/hphp/runtime/server/virtual_host.h index b6166693a..a7eec49ee 100644 --- a/hphp/runtime/server/virtual_host.h +++ b/hphp/runtime/server/virtual_host.h @@ -41,8 +41,7 @@ public: void init(Hdf vh); void addAllowedDirectories(const std::vector& 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;} diff --git a/hphp/runtime/server/warmup_request_handler.cpp b/hphp/runtime/server/warmup_request_handler.cpp index 768605393..309030839 100644 --- a/hphp/runtime/server/warmup_request_handler.cpp +++ b/hphp/runtime/server/warmup_request_handler.cpp @@ -32,7 +32,7 @@ void WarmupRequestHandler::handleRequest(Transport *transport) { } std::unique_ptr WarmupRequestHandlerFactory::createHandler() { - return make_unique(shared_from_this()); + return make_unique(m_timeout, shared_from_this()); } void WarmupRequestHandlerFactory::bumpReqCount() { diff --git a/hphp/runtime/server/warmup_request_handler.h b/hphp/runtime/server/warmup_request_handler.h index e52783c17..ee94d2c35 100644 --- a/hphp/runtime/server/warmup_request_handler.h +++ b/hphp/runtime/server/warmup_request_handler.h @@ -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 createHandler(); @@ -61,6 +64,7 @@ private: std::atomic m_additionalThreads; std::atomic 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; diff --git a/hphp/runtime/server/xbox_server.cpp b/hphp/runtime/server/xbox_server.cpp index 13b3bdf0b..24e7db873 100644 --- a/hphp/runtime/server/xbox_server.cpp +++ b/hphp/runtime/server/xbox_server.cpp @@ -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 diff --git a/hphp/runtime/vm/bytecode.cpp b/hphp/runtime/vm/bytecode.cpp index c264fc8b5..e70777814 100644 --- a/hphp/runtime/vm/bytecode.cpp +++ b/hphp/runtime/vm/bytecode.cpp @@ -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; } diff --git a/hphp/test/ext/test_ext_curl.cpp b/hphp/test/ext/test_ext_curl.cpp index f285a185e..fc9e3f1ef 100644 --- a/hphp/test/ext/test_ext_curl.cpp +++ b/hphp/test/ext/test_ext_curl.cpp @@ -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( - "127.0.0.1", s_server_port, 4, -1); - server->setRequestHandlerFactory(); + "127.0.0.1", s_server_port, 4); + server->setRequestHandlerFactory(0); server->start(); return server; diff --git a/hphp/test/ext/test_server.cpp b/hphp/test/ext/test_server.cpp index fba5cfc59..dd9c61b0a 100644 --- a/hphp/test/ext/test_server.cpp +++ b/hphp/test/ext/test_server.cpp @@ -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( - "127.0.0.1", port, 50, -1); - server->setRequestHandlerFactory(); + "127.0.0.1", port, 50); + server->setRequestHandlerFactory(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( - "127.0.0.1", s_server_port, 50, -1); - server->setRequestHandlerFactory(); + "127.0.0.1", s_server_port, 50); + server->setRequestHandlerFactory(0); server->start(); break; } catch (const FailedToListenException& e) {