a8e3321fbd
We'd like to start using ##mixed## instead of ##var## for attribute types to be consistent with Hack. As a followup to this (once released), we would codemod all ##var## to ##mixed##.
297 linhas
8.5 KiB
C++
297 linhas
8.5 KiB
C++
/*
|
|
+----------------------------------------------------------------------+
|
|
| 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/shared_store_base.h"
|
|
#include "hphp/runtime/base/complex_types.h"
|
|
#include "hphp/runtime/base/runtime_option.h"
|
|
#include "hphp/runtime/base/type_conversions.h"
|
|
#include "hphp/runtime/base/builtin_functions.h"
|
|
#include "hphp/runtime/base/leak_detectable.h"
|
|
#include "hphp/runtime/server/server_stats.h"
|
|
#include "hphp/runtime/base/concurrent_shared_store.h"
|
|
#include "hphp/util/timer.h"
|
|
#include "hphp/util/logger.h"
|
|
#include <sys/mman.h>
|
|
|
|
#if !defined(HAVE_POSIX_FALLOCATE) && \
|
|
(_XOPEN_SOURCE >= 600 || _POSIX_C_SOURCE >= 200112L)
|
|
# define HAVE_POSIX_FALLOCATE 1
|
|
#endif
|
|
|
|
namespace HPHP {
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// SharedStore
|
|
|
|
SharedStore::SharedStore(int id) : m_id(id) {
|
|
}
|
|
|
|
SharedStore::~SharedStore() {
|
|
}
|
|
|
|
std::string SharedStore::GetSkeleton(CStrRef key) {
|
|
std::string ret;
|
|
const char *p = key.data();
|
|
ret.reserve(key.size());
|
|
bool added = false; // whether consecutive numbers are replaced by # yet
|
|
for (int i = 0; i < key.size(); i++) {
|
|
char ch = *p++;
|
|
if (ch >= '0' && ch <= '9') {
|
|
if (!added) {
|
|
ret += '#';
|
|
added = true;
|
|
}
|
|
} else {
|
|
added = false;
|
|
ret += ch;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool SharedStore::erase(CStrRef key, bool expired /* = false */) {
|
|
bool success = eraseImpl(key, expired);
|
|
|
|
if (RuntimeOption::EnableStats && RuntimeOption::EnableAPCStats) {
|
|
ServerStats::Log(success ? "apc.erased" : "apc.erase", 1);
|
|
}
|
|
return success;
|
|
}
|
|
|
|
void StoreValue::set(SharedVariant *v, int64_t ttl) {
|
|
var = v;
|
|
expiry = ttl ? time(nullptr) + ttl : 0;
|
|
}
|
|
bool StoreValue::expired() const {
|
|
return expiry && time(nullptr) >= expiry;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// SharedStores
|
|
|
|
SharedStores s_apc_store;
|
|
|
|
SharedStores::SharedStores() {
|
|
}
|
|
|
|
void SharedStores::create() {
|
|
for (int i = 0; i < MAX_SHARED_STORE; i++) {
|
|
switch (RuntimeOption::ApcTableType) {
|
|
case RuntimeOption::ApcTableTypes::ApcConcurrentTable:
|
|
m_stores[i] = new ConcurrentTableSharedStore(i);
|
|
break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
SharedStores::~SharedStores() {
|
|
clear();
|
|
}
|
|
|
|
void SharedStores::clear() {
|
|
for (int i = 0; i < MAX_SHARED_STORE; i++) {
|
|
delete m_stores[i];
|
|
m_stores[i] = nullptr;
|
|
}
|
|
}
|
|
|
|
void SharedStores::reset() {
|
|
clear();
|
|
create();
|
|
}
|
|
|
|
void SharedStores::Create() {
|
|
s_apc_store.create();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
SharedStoreFileStorage s_apc_file_storage;
|
|
|
|
void SharedStoreFileStorage::enable(const std::string& prefix,
|
|
int64_t chunkSize, int64_t maxSize) {
|
|
Lock lock(m_lock);
|
|
m_prefix = prefix;
|
|
m_chunkSize = chunkSize;
|
|
m_maxSize = maxSize;
|
|
if (m_state != StorageState::Invalid) {
|
|
return;
|
|
}
|
|
if (!addFile()) {
|
|
Logger::Error("Failed to open file for apc, fallback to in-memory mode");
|
|
return;
|
|
}
|
|
m_state = StorageState::Open;
|
|
}
|
|
|
|
char *SharedStoreFileStorage::put(const char *data, int32_t len) {
|
|
Lock lock(m_lock);
|
|
if (m_state != StorageState::Open ||
|
|
len + PaddingSize > m_chunkSize - PaddingSize) {
|
|
return nullptr;
|
|
}
|
|
if (len + PaddingSize > m_chunkRemain && !addFile()) {
|
|
m_state = StorageState::Full;
|
|
return nullptr;
|
|
}
|
|
assert(m_current);
|
|
assert(len + PaddingSize <= m_chunkRemain);
|
|
strhash_t h = hash_string_inline(data, len);
|
|
*(strhash_t*)m_current = h;
|
|
m_current += sizeof(h);
|
|
*(int32_t*)m_current = len;
|
|
m_current += sizeof(len);
|
|
// should be no overlap
|
|
memcpy(m_current, data, len);
|
|
char *addr = m_current;
|
|
addr[len] = '\0';
|
|
m_current += len + sizeof(char);
|
|
*(strhash_t*)m_current = TombHash;
|
|
m_chunkRemain -= len + PaddingSize;
|
|
return addr;
|
|
}
|
|
|
|
void SharedStoreFileStorage::seal() {
|
|
Lock lock(m_lock);
|
|
if (m_state == StorageState::Sealed) {
|
|
return;
|
|
}
|
|
assert(m_state == StorageState::Open || m_state == StorageState::Full);
|
|
m_current = nullptr;
|
|
m_chunkRemain = 0;
|
|
m_state = StorageState::Sealed;
|
|
|
|
for (int i = 0; i < (int)m_chunks.size(); i++) {
|
|
if (mprotect(m_chunks[i], m_chunkSize, PROT_READ) < 0) {
|
|
Logger::Error("Failed to mprotect chunk %d", i);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SharedStoreFileStorage::adviseOut() {
|
|
Lock lock(m_lock);
|
|
Timer timer(Timer::WallTime, "advising out apc prime");
|
|
for (int i = 0; i < (int)m_chunks.size(); i++) {
|
|
if (madvise(m_chunks[i], m_chunkSize, MADV_DONTNEED) < 0) {
|
|
Logger::Error("Failed to madvise chunk %d", i);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool SharedStoreFileStorage::hashCheck() {
|
|
Lock lock(m_lock);
|
|
for (int i = 0; i < (int)m_chunks.size(); i++) {
|
|
char *current = (char*)m_chunks[i];
|
|
char *boundary = (char*)m_chunks[i] + m_chunkSize;
|
|
while (1) {
|
|
strhash_t h = *(strhash_t*)current;
|
|
if (h == TombHash) {
|
|
break;
|
|
}
|
|
current += sizeof(h);
|
|
int32_t len = *(int32_t*)current;
|
|
current += sizeof(len);
|
|
if (len < 0 ||
|
|
len + PaddingSize >= (int64_t)boundary - (int64_t)current) {
|
|
Logger::Error("invalid len %d at chunk %d offset %" PRId64, len, i,
|
|
(int64_t)current - (int64_t)m_chunks[i]);
|
|
return false;
|
|
}
|
|
strhash_t h_data = hash_string_inline(current, len);
|
|
if (h_data != h) {
|
|
Logger::Error("invalid hash at chunk %d offset %" PRId64, i,
|
|
(int64_t)current - (int64_t)m_chunks[i]);
|
|
return false;
|
|
}
|
|
current += len;
|
|
if (*current != '\0') {
|
|
Logger::Error("missing \\0 at chunk %d offset %" PRId64, i,
|
|
(int64_t)current - (int64_t)m_chunks[i]);
|
|
return false;
|
|
}
|
|
current++;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void SharedStoreFileStorage::cleanup() {
|
|
Lock lock(m_lock);
|
|
for (unsigned int i = 0 ; i < m_fileNames.size(); i++) {
|
|
unlink(m_fileNames[i].c_str());
|
|
}
|
|
m_chunks.clear();
|
|
}
|
|
|
|
bool SharedStoreFileStorage::addFile() {
|
|
if ((int64_t)m_chunks.size() * m_chunkSize >= m_maxSize) {
|
|
m_state = StorageState::Full;
|
|
return false;
|
|
}
|
|
char name[PATH_MAX];
|
|
snprintf(name, sizeof(name), "%s.XXXXXX", m_prefix.c_str());
|
|
int fd = mkstemp(name);
|
|
if (fd < 0) {
|
|
Logger::Error("Failed to open temp file");
|
|
return false;
|
|
}
|
|
bool couldAllocate = false;
|
|
#if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE
|
|
couldAllocate = posix_fallocate(fd, 0, m_chunkSize) == 0;
|
|
#elif defined(__APPLE__)
|
|
fstore_t store = { F_ALLOCATECONTIG, F_PEOFPOSMODE, 0, m_chunkSize };
|
|
int ret = fcntl(fd, F_PREALLOCATE, &store);
|
|
if(ret == -1) {
|
|
store.fst_flags = F_ALLOCATEALL;
|
|
ret = fcntl(fd, F_PREALLOCATE, &store);
|
|
if (ret == -1) {
|
|
couldAllocate = false;
|
|
}
|
|
}
|
|
couldAllocate = ftruncate(fd, m_chunkSize) == 0;
|
|
#else
|
|
#error "No implementation for posix_fallocate on your platform."
|
|
#endif
|
|
if (!couldAllocate) {
|
|
Logger::Error("Failed to posix_fallocate of size %" PRId64, m_chunkSize);
|
|
close(fd);
|
|
return false;
|
|
}
|
|
if (RuntimeOption::ApcFileStorageKeepFileLinked) {
|
|
m_fileNames.push_back(std::string(name));
|
|
} else {
|
|
unlink(name);
|
|
}
|
|
char *addr = (char *)mmap(nullptr, m_chunkSize, PROT_READ | PROT_WRITE,
|
|
MAP_SHARED, fd, 0);
|
|
if (addr == (char *)-1) {
|
|
Logger::Error("Failed to mmap %s of size %" PRId64, name, m_chunkSize);
|
|
close(fd);
|
|
return false;
|
|
}
|
|
m_current = addr;
|
|
m_chunkRemain = m_chunkSize - PaddingSize;
|
|
m_chunks.push_back(addr);
|
|
close(fd);
|
|
return true;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
}
|