d09fd3e421
Per @mwilliams' suggestion, this is the first stage in a staggered approach to replacing int64 with int64_t. More precisely I inserted "typedef ::int64_t int64;" in util/base.h and dealt with the consequences.
157 linhas
4.7 KiB
C++
157 linhas
4.7 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 <stdlib.h>
|
|
#include <pthread.h>
|
|
#include <stdio.h>
|
|
|
|
#include <list>
|
|
|
|
#include "util/trace.h"
|
|
#include "util/base.h"
|
|
#include "util/rank.h"
|
|
#include "runtime/base/macros.h"
|
|
#include "runtime/vm/class.h"
|
|
#include "treadmill.h"
|
|
#include "runtime/vm/translator/translator-x64.h"
|
|
|
|
namespace HPHP { namespace VM { namespace Treadmill {
|
|
|
|
TRACE_SET_MOD(treadmill);
|
|
|
|
namespace {
|
|
static pthread_mutex_t s_genLock = PTHREAD_MUTEX_INITIALIZER;
|
|
static GenCount s_gen = 1;
|
|
static const GenCount kIdleGenCount = 0; // not processing any requests.
|
|
static GenCount* s_inflightRequests;
|
|
static int s_maxThreadID;
|
|
|
|
struct GenCountGuard {
|
|
GenCountGuard() {
|
|
checkRank(RankTreadmill);
|
|
pthread_mutex_lock(&s_genLock);
|
|
pushRank(RankTreadmill);
|
|
if (!s_inflightRequests) {
|
|
s_maxThreadID = 2;
|
|
s_inflightRequests = (GenCount*)calloc(sizeof(GenCount), s_maxThreadID);
|
|
static_assert(kIdleGenCount == 0, "kIdleGenCount should be zero");
|
|
}
|
|
}
|
|
~GenCountGuard() {
|
|
popRank(RankTreadmill);
|
|
pthread_mutex_unlock(&s_genLock);
|
|
}
|
|
};
|
|
}
|
|
|
|
static GenCount* idToCount(int threadID) {
|
|
if (threadID >= s_maxThreadID) {
|
|
int newSize = threadID + 1;
|
|
s_inflightRequests = (GenCount*)realloc(s_inflightRequests,
|
|
sizeof(GenCount) * newSize);
|
|
for (int i = s_maxThreadID; i < newSize; i++) {
|
|
s_inflightRequests[i] = kIdleGenCount;
|
|
}
|
|
s_maxThreadID = newSize;
|
|
}
|
|
return s_inflightRequests + threadID;
|
|
}
|
|
|
|
typedef std::list<WorkItem*> PendingTriggers;
|
|
static PendingTriggers s_tq;
|
|
|
|
// Inherently racy. We get a lower bound on the generation; presumably
|
|
// clients are aware of this, and are creating the trigger for an object
|
|
// that was reachable strictly in the past.
|
|
WorkItem::WorkItem() : m_gen(s_gen) {
|
|
}
|
|
|
|
void WorkItem::enqueue(WorkItem* gt) {
|
|
GenCountGuard g;
|
|
gt->m_gen = s_gen++;
|
|
s_tq.push_back(gt);
|
|
}
|
|
|
|
void startRequest(int threadId) {
|
|
GenCountGuard g;
|
|
assert(*idToCount(threadId) == kIdleGenCount);
|
|
TRACE(1, "tid %d start @gen %d\n", threadId, int(s_gen));
|
|
*idToCount(threadId) = s_gen;
|
|
}
|
|
|
|
void finishRequest(int threadId) {
|
|
TRACE(1, "tid %d finish\n", threadId);
|
|
std::vector<WorkItem*> toFire;
|
|
{
|
|
GenCountGuard g;
|
|
assert(*idToCount(threadId) != kIdleGenCount);
|
|
*idToCount(threadId) = kIdleGenCount;
|
|
|
|
// After finishing a request, check to see if we've allowed any triggers
|
|
// to fire.
|
|
PendingTriggers::iterator it = s_tq.begin();
|
|
PendingTriggers::iterator end = s_tq.end();
|
|
if (it != end) {
|
|
GenCount gen = (*it)->m_gen;
|
|
GenCount limit = s_gen + 1;
|
|
for (int i = 0; i < s_maxThreadID; ++i) {
|
|
if (s_inflightRequests[i] != kIdleGenCount &&
|
|
s_inflightRequests[i] < limit) {
|
|
limit = s_inflightRequests[i];
|
|
if (limit <= gen) break;
|
|
}
|
|
}
|
|
do {
|
|
TRACE(2, "considering delendum %d\n", int((*it)->m_gen));
|
|
if ((*it)->m_gen >= limit) {
|
|
TRACE(2, "not unreachable! %d\n", int((*it)->m_gen));
|
|
break;
|
|
}
|
|
toFire.push_back(*it);
|
|
it = s_tq.erase(it);
|
|
} while (it != end);
|
|
}
|
|
}
|
|
for (unsigned i = 0; i < toFire.size(); ++i) {
|
|
(*toFire[i])();
|
|
delete toFire[i];
|
|
}
|
|
}
|
|
|
|
FreeMemoryTrigger::FreeMemoryTrigger(void* ptr) : m_ptr(ptr) {
|
|
TRACE(3, "FreeMemoryTrigger @ %p, m_f %p\n", this, m_ptr);
|
|
}
|
|
|
|
void FreeMemoryTrigger::operator()() {
|
|
TRACE(3, "FreeMemoryTrigger: Firing @ %p , m_f %p\n", this, m_ptr);
|
|
free(m_ptr);
|
|
}
|
|
|
|
FreeClassTrigger::FreeClassTrigger(Class* cls) : m_cls(cls) {
|
|
TRACE(3, "FreeClassTrigger @ %p, cls %p\n", this, m_cls);
|
|
}
|
|
|
|
void FreeClassTrigger::operator()() {
|
|
TRACE(3, "FreeClassTrigger: Firing @ %p , cls %p\n", this, m_cls);
|
|
m_cls->atomicRelease();
|
|
}
|
|
|
|
void deferredFree(void* p) {
|
|
WorkItem::enqueue(new FreeMemoryTrigger(p));
|
|
}
|
|
|
|
}}}
|