Arquivos
hhvm/hphp/runtime/base/thread_info.cpp
T
mikemag 602b1bd64f Ensure C++ exceptions eventually propagate out of destructors.
This diff addresses what we called "step 1" in the task: simply ensure that any C++ exceptions that escape a destructor get rethrown and can continue to propagate naturally. The exception is remembered on the thread, and rethrown when we check for surprises later. If multiple destructors let C++ exceptions escape the last one to escape will be the one rethrown at the next surprise check.

This also ensures that C++ exceptions prevent more PHP code from running, by omitting calls to __destruct methods as we unwind the stack.

Finally, this also enables surprise checks for OnFunctionExit unless we're unwinding, in which case surprises remain unchecked so they can propagate later.

This is different than Zend's behavior, where destructors do run as fatals unwind.
2013-04-17 08:54:58 -07:00

188 linhas
6.0 KiB
C++

/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP 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.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include <runtime/base/types.h>
#include <runtime/base/hphp_system.h>
#include <runtime/base/code_coverage.h>
#include <runtime/base/memory/smart_allocator.h>
#include <runtime/vm/translator/targetcache.h>
#include <util/lock.h>
#include <util/alloc.h>
using std::map;
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
static Mutex s_thread_info_mutex;
static std::set<ThreadInfo*> s_thread_infos;
__thread char* ThreadInfo::t_stackbase = 0;
IMPLEMENT_THREAD_LOCAL_NO_CHECK_HOT(ThreadInfo, ThreadInfo::s_threadInfo);
ThreadInfo::ThreadInfo()
: m_stacklimit(0), m_executing(Idling) {
assert(!t_stackbase);
t_stackbase = static_cast<char*>(stack_top_ptr());
map<int, ObjectAllocatorBaseGetter> &wrappers =
ObjectAllocatorCollector::getWrappers();
m_allocators.resize(wrappers.rbegin()->first + 1);
for (map<int, ObjectAllocatorBaseGetter>::iterator it = wrappers.begin();
it != wrappers.end(); it++) {
m_allocators[it->first] = it->second();
assert(it->second() != nullptr);
}
m_mm = MemoryManager::TheMemoryManager();
m_profiler = nullptr;
m_pendingException = nullptr;
m_coverage = new CodeCoverage();
VM::Transl::TargetCache::threadInit();
onSessionInit();
Lock lock(s_thread_info_mutex);
s_thread_infos.insert(this);
}
ThreadInfo::~ThreadInfo() {
t_stackbase = 0;
Lock lock(s_thread_info_mutex);
s_thread_infos.erase(this);
delete m_coverage;
VM::Transl::TargetCache::threadExit();
}
bool ThreadInfo::valid(ThreadInfo* info) {
Lock lock(s_thread_info_mutex);
return s_thread_infos.find(info) != s_thread_infos.end();
}
void ThreadInfo::GetExecutionSamples(std::map<Executing, int> &counts) {
Lock lock(s_thread_info_mutex);
for (std::set<ThreadInfo*>::const_iterator iter = s_thread_infos.begin();
iter != s_thread_infos.end(); ++iter) {
++counts[(*iter)->m_executing];
}
}
void ThreadInfo::onSessionInit() {
m_top = nullptr;
m_reqInjectionData.onSessionInit();
// Take the address of the cached per-thread stackLimit, and use this to allow
// some slack for (a) stack usage above the caller of reset() and (b) stack
// usage after the position gets checked.
// If we're not in a threaded environment, then Util::s_stackSize will be
// zero. Use getrlimit to figure out what the size of the stack is to
// calculate an approximation of where the bottom of the stack should be.
if (Util::s_stackSize == 0) {
struct rlimit rl;
getrlimit(RLIMIT_STACK, &rl);
m_stacklimit = t_stackbase - (rl.rlim_cur - StackSlack);
} else {
m_stacklimit = (char *)Util::s_stackLimit + StackSlack;
assert(uintptr_t(m_stacklimit) < (Util::s_stackLimit + Util::s_stackSize));
}
}
void ThreadInfo::clearPendingException() {
m_reqInjectionData.clearPendingExceptionFlag();
if (m_pendingException != nullptr) delete m_pendingException;
m_pendingException = nullptr;
}
void ThreadInfo::setPendingException(Exception* e) {
m_reqInjectionData.setPendingExceptionFlag();
if (m_pendingException != nullptr) delete m_pendingException;
m_pendingException = e;
}
void ThreadInfo::onSessionExit() {
m_reqInjectionData.reset();
VM::Transl::TargetCache::requestExit();
}
void RequestInjectionData::onSessionInit() {
VM::Transl::TargetCache::requestInit();
cflagsPtr = VM::Transl::TargetCache::conditionFlagsPtr();
reset();
started = time(0);
}
void RequestInjectionData::reset() {
__sync_fetch_and_and(getConditionFlags(), 0);
coverage = RuntimeOption::RecordCodeCoverage;
debugger = false;
debuggerIntr = false;
while (!interrupts.empty()) interrupts.pop();
}
void RequestInjectionData::setMemExceededFlag() {
__sync_fetch_and_or(getConditionFlags(),
RequestInjectionData::MemExceededFlag);
}
void RequestInjectionData::setTimedOutFlag() {
__sync_fetch_and_or(getConditionFlags(),
RequestInjectionData::TimedOutFlag);
}
void RequestInjectionData::setSignaledFlag() {
__sync_fetch_and_or(getConditionFlags(),
RequestInjectionData::SignaledFlag);
}
void RequestInjectionData::setEventHookFlag() {
__sync_fetch_and_or(getConditionFlags(),
RequestInjectionData::EventHookFlag);
}
void RequestInjectionData::clearEventHookFlag() {
__sync_fetch_and_and(getConditionFlags(),
~RequestInjectionData::EventHookFlag);
}
void RequestInjectionData::setPendingExceptionFlag() {
__sync_fetch_and_or(getConditionFlags(),
RequestInjectionData::PendingExceptionFlag);
}
void RequestInjectionData::clearPendingExceptionFlag() {
__sync_fetch_and_and(getConditionFlags(),
~RequestInjectionData::PendingExceptionFlag);
}
ssize_t RequestInjectionData::fetchAndClearFlags() {
ssize_t flags;
for (;;) {
flags = atomic_acquire_load(getConditionFlags());
const ssize_t newFlags =
flags & RequestInjectionData::EventHookFlag;
if (__sync_bool_compare_and_swap(getConditionFlags(), flags, newFlags)) {
break;
}
}
return flags;
}
///////////////////////////////////////////////////////////////////////////////
}