84b9d9a3a2
In HHVM (and HPHPc before it) we've been piggybacking resources on the KindOfObject machinery. At the language level, resource is considered to be a different type than object, and there are a number of differences in behavior between objects and resources (ex. resources don't allow for dynamic properties, resources don't work with the clone operator, the "(object)" cast behaves differently for resources vs. objects, etc). Piggybacking resources on the KindOfObject machinery has some downsides. Code that deals with KindOfObject values often needs to check if the value is a resource and go down a different code path. This makes things harder to maintain and harder to keep parity with Zend. Also, these extra branches hurt performance a little, and they make it harder for the JIT to do a good job in some cases when its generating machine code that operates on objects. This diff prepares the code base for a new KindOfResource type by adding a new "Resource" smart pointer type (currently a typedef for the Object smart pointer type) and it updates the C++ code and the idl files appropriately. This diff is essentially a cosmetic change and should not impact run time behavior. In the next diff (part 2) we'll actually add a new KindOfResource type, detach ResourceData from the ObjectData inheritence hierarchy, and provide a real implementation for the Resource smart pointer type (instead of just aliasing the Object smart pointer type).
1315 linhas
42 KiB
C++
1315 linhas
42 KiB
C++
/*
|
|
+----------------------------------------------------------------------+
|
|
| HipHop for PHP |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
|
|
| Copyright (c) 1997-2010 The PHP Group |
|
|
+----------------------------------------------------------------------+
|
|
| 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 "hphp/runtime/ext/ext_apc.h"
|
|
#include "hphp/runtime/ext/ext_variable.h"
|
|
#include "hphp/runtime/ext/ext_fb.h"
|
|
#include "hphp/runtime/base/runtime_option.h"
|
|
#include "hphp/util/async_job.h"
|
|
#include "hphp/util/timer.h"
|
|
#include <dlfcn.h>
|
|
#include "hphp/runtime/base/program_functions.h"
|
|
#include "hphp/runtime/base/builtin_functions.h"
|
|
#include "hphp/runtime/base/variable_serializer.h"
|
|
#include "hphp/util/alloc.h"
|
|
#include "hphp/runtime/base/ini_setting.h"
|
|
|
|
using HPHP::Util::ScopedMem;
|
|
|
|
namespace HPHP {
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
extern void const_load();
|
|
|
|
|
|
static class apcExtension : public Extension {
|
|
public:
|
|
apcExtension() : Extension("apc") {}
|
|
virtual void moduleInit() {
|
|
IniSetting::SetGlobalDefault("apc.enabled","1");
|
|
IniSetting::SetGlobalDefault("apc.stat",
|
|
RuntimeOption::RepoAuthoritative
|
|
? "0" : "1");
|
|
IniSetting::SetGlobalDefault("apc.enable_cli", "1");
|
|
if (RuntimeOption::ApcUseFileStorage) {
|
|
s_apc_file_storage.enable(RuntimeOption::ApcFileStoragePrefix,
|
|
RuntimeOption::ApcFileStorageChunkSize,
|
|
RuntimeOption::ApcFileStorageMaxSize);
|
|
}
|
|
}
|
|
virtual void moduleShutdown() {
|
|
if (RuntimeOption::ApcUseFileStorage) {
|
|
s_apc_file_storage.cleanup();
|
|
}
|
|
}
|
|
} s_apc_extension;
|
|
|
|
bool f_apc_store(CStrRef key, CVarRef var, int64_t ttl /* = 0 */,
|
|
int64_t cache_id /* = 0 */) {
|
|
if (!RuntimeOption::EnableApc) return false;
|
|
|
|
if (cache_id < 0 || cache_id >= MAX_SHARED_STORE) {
|
|
throw_invalid_argument("cache_id: %" PRId64, cache_id);
|
|
return false;
|
|
}
|
|
|
|
return s_apc_store[cache_id].store(key, var, ttl);
|
|
}
|
|
|
|
bool f_apc_add(CStrRef key, CVarRef var, int64_t ttl /* = 0 */,
|
|
int64_t cache_id /* = 0 */) {
|
|
if (!RuntimeOption::EnableApc) return false;
|
|
|
|
if (cache_id < 0 || cache_id >= MAX_SHARED_STORE) {
|
|
throw_invalid_argument("cache_id: %" PRId64, cache_id);
|
|
return false;
|
|
}
|
|
|
|
return s_apc_store[cache_id].store(key, var, ttl, false);
|
|
}
|
|
|
|
Variant f_apc_fetch(CVarRef key, VRefParam success /* = null */,
|
|
int64_t cache_id /* = 0 */) {
|
|
if (!RuntimeOption::EnableApc) return false;
|
|
|
|
if (cache_id < 0 || cache_id >= MAX_SHARED_STORE) {
|
|
throw_invalid_argument("cache_id: %" PRId64, cache_id);
|
|
return false;
|
|
}
|
|
|
|
Variant v;
|
|
|
|
if (key.is(KindOfArray)) {
|
|
bool tmp = false;
|
|
Array keys = key.toArray();
|
|
ArrayInit init(keys.size());
|
|
for (ArrayIter iter(keys); iter; ++iter) {
|
|
Variant k = iter.second();
|
|
if (!k.isString()) {
|
|
throw_invalid_argument("apc key: (not a string)");
|
|
return false;
|
|
}
|
|
String strKey = k.toString();
|
|
if (s_apc_store[cache_id].get(strKey, v)) {
|
|
tmp = true;
|
|
init.set(strKey, v, true);
|
|
}
|
|
}
|
|
success = tmp;
|
|
return init.create();
|
|
}
|
|
|
|
if (s_apc_store[cache_id].get(key.toString(), v)) {
|
|
success = true;
|
|
} else {
|
|
success = false;
|
|
v = false;
|
|
}
|
|
return v;
|
|
}
|
|
|
|
Variant f_apc_delete(CVarRef key, int64_t cache_id /* = 0 */) {
|
|
if (!RuntimeOption::EnableApc) return false;
|
|
|
|
if (cache_id < 0 || cache_id >= MAX_SHARED_STORE) {
|
|
throw_invalid_argument("cache_id: %" PRId64, cache_id);
|
|
return false;
|
|
}
|
|
|
|
if (key.is(KindOfArray)) {
|
|
Array keys = key.toArray();
|
|
ArrayInit init(keys.size());
|
|
for (ArrayIter iter(keys); iter; ++iter) {
|
|
Variant k = iter.second();
|
|
if (!k.isString()) {
|
|
raise_warning("apc key is not a string");
|
|
init.set(k);
|
|
} else if (!s_apc_store[cache_id].erase(k.toString())) {
|
|
init.set(k);
|
|
}
|
|
}
|
|
return init.create();
|
|
}
|
|
|
|
return s_apc_store[cache_id].erase(key.toString());
|
|
}
|
|
|
|
bool f_apc_clear_cache(int64_t cache_id /* = 0 */) {
|
|
if (!RuntimeOption::EnableApc) return false;
|
|
|
|
if (cache_id < 0 || cache_id >= MAX_SHARED_STORE) {
|
|
throw_invalid_argument("cache_id: %" PRId64, cache_id);
|
|
return false;
|
|
}
|
|
return s_apc_store[cache_id].clear();
|
|
}
|
|
|
|
Variant f_apc_inc(CStrRef key, int64_t step /* = 1 */,
|
|
VRefParam success /* = null */, int64_t cache_id /* = 0 */) {
|
|
if (!RuntimeOption::EnableApc) return false;
|
|
|
|
if (cache_id < 0 || cache_id >= MAX_SHARED_STORE) {
|
|
throw_invalid_argument("cache_id: %" PRId64, cache_id);
|
|
return false;
|
|
}
|
|
bool found = false;
|
|
int64_t newValue = s_apc_store[cache_id].inc(key, step, found);
|
|
success = found;
|
|
return newValue;
|
|
}
|
|
|
|
Variant f_apc_dec(CStrRef key, int64_t step /* = 1 */,
|
|
VRefParam success /* = null */, int64_t cache_id /* = 0 */) {
|
|
if (!RuntimeOption::EnableApc) return false;
|
|
|
|
if (cache_id < 0 || cache_id >= MAX_SHARED_STORE) {
|
|
throw_invalid_argument("cache_id: %" PRId64, cache_id);
|
|
return false;
|
|
}
|
|
bool found = false;
|
|
int64_t newValue = s_apc_store[cache_id].inc(key, -step, found);
|
|
success = found;
|
|
return newValue;
|
|
}
|
|
|
|
bool f_apc_cas(CStrRef key, int64_t old_cas, int64_t new_cas,
|
|
int64_t cache_id /* = 0 */) {
|
|
if (!RuntimeOption::EnableApc) return false;
|
|
|
|
if (cache_id < 0 || cache_id >= MAX_SHARED_STORE) {
|
|
throw_invalid_argument("cache_id: %" PRId64, cache_id);
|
|
return false;
|
|
}
|
|
return s_apc_store[cache_id].cas(key, old_cas, new_cas);
|
|
}
|
|
|
|
Variant f_apc_exists(CVarRef key, int64_t cache_id /* = 0 */) {
|
|
if (!RuntimeOption::EnableApc) return false;
|
|
|
|
if (cache_id < 0 || cache_id >= MAX_SHARED_STORE) {
|
|
throw_invalid_argument("cache_id: %" PRId64, cache_id);
|
|
return false;
|
|
}
|
|
|
|
if (key.is(KindOfArray)) {
|
|
Array keys = key.toArray();
|
|
ArrayInit init(keys.size());
|
|
for (ArrayIter iter(keys); iter; ++iter) {
|
|
Variant k = iter.second();
|
|
if (!k.isString()) {
|
|
throw_invalid_argument("apc key: (not a string)");
|
|
return false;
|
|
}
|
|
String strKey = k.toString();
|
|
if (s_apc_store[cache_id].exists(strKey)) {
|
|
init.set(strKey);
|
|
}
|
|
}
|
|
return init.create();
|
|
}
|
|
|
|
return s_apc_store[cache_id].exists(key.toString());
|
|
}
|
|
|
|
const StaticString s_start_time("start_time");
|
|
|
|
Variant f_apc_cache_info(int64_t cache_id /* = 0 */, bool limited /* = false */) {
|
|
return CREATE_MAP1(s_start_time, start_time());
|
|
}
|
|
|
|
Array f_apc_sma_info(bool limited /* = false */) {
|
|
return Array::Create();
|
|
}
|
|
bool f_apc_define_constants(CStrRef key, CStrRef constants,
|
|
bool case_sensitive /* = true */,
|
|
int64_t cache_id /* = 0 */) {
|
|
throw NotSupportedException(__func__, "dynamic coding");
|
|
}
|
|
bool f_apc_load_constants(CStrRef key, bool case_sensitive /* = true */,
|
|
int64_t cache_id /* = 0 */) {
|
|
throw NotSupportedException(__func__, "dynamic coding");
|
|
}
|
|
bool f_apc_compile_file(CStrRef filename, bool atomic /* = true */,
|
|
int64_t cache_id /* = 0 */) {
|
|
throw NotSupportedException(__func__, "dynamic coding");
|
|
}
|
|
Array f_apc_filehits() {
|
|
throw NotSupportedException(__func__, "feature not supported");
|
|
}
|
|
Variant f_apc_delete_file(CVarRef keys, int64_t cache_id /* = 0 */) {
|
|
throw NotSupportedException(__func__, "feature not supported");
|
|
}
|
|
Variant f_apc_bin_dump(int64_t cache_id /* = 0 */, CVarRef filter /* = null_variant */) {
|
|
throw NotSupportedException(__func__, "feature not supported");
|
|
}
|
|
bool f_apc_bin_load(CStrRef data, int64_t flags /* = 0 */, int64_t cache_id /* = 0 */) {
|
|
throw NotSupportedException(__func__, "feature not supported");
|
|
}
|
|
Variant f_apc_bin_dumpfile(int64_t cache_id, CVarRef filter,
|
|
CStrRef filename, int64_t flags /* = 0 */,
|
|
CResRef context /* = uninit_null() */) {
|
|
throw NotSupportedException(__func__, "feature not supported");
|
|
}
|
|
bool f_apc_bin_loadfile(CStrRef filename, CResRef context /* = uninit_null() */,
|
|
int64_t flags /* = 0 */, int64_t cache_id /* = 0 */) {
|
|
throw NotSupportedException(__func__, "feature not supported");
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// loading APC from archive files
|
|
|
|
typedef void(*PFUNC_APC_LOAD)();
|
|
|
|
// Structure to hold cache meta data
|
|
// Same definition in ext_apc.cpp
|
|
struct cache_info {
|
|
char *a_name;
|
|
bool use_const;
|
|
};
|
|
|
|
static Mutex dl_mutex;
|
|
static PFUNC_APC_LOAD apc_load_func(void *handle, const char *name) {
|
|
Lock lock(dl_mutex);
|
|
dlerror(); // clear errors
|
|
PFUNC_APC_LOAD p = (PFUNC_APC_LOAD)dlsym(handle, name);
|
|
const char *error = dlerror();
|
|
if (error || p == NULL) {
|
|
throw Exception("Unable to find %s in %s: %s", name,
|
|
RuntimeOption::ApcPrimeLibrary.c_str(),
|
|
error ? error : "(unknown error)");
|
|
}
|
|
return p;
|
|
}
|
|
|
|
DECLARE_BOOST_TYPES(ApcLoadJob);
|
|
class ApcLoadJob {
|
|
public:
|
|
ApcLoadJob(void *handle, int index) : m_handle(handle), m_index(index) {}
|
|
void *m_handle; int m_index;
|
|
};
|
|
|
|
class ApcLoadWorker {
|
|
public:
|
|
void onThreadEnter() {}
|
|
void doJob(ApcLoadJobPtr job) {
|
|
char func_name[128];
|
|
snprintf(func_name, sizeof(func_name), "_apc_load_%d", job->m_index);
|
|
apc_load_func(job->m_handle, func_name)();
|
|
}
|
|
void onThreadExit() {}
|
|
};
|
|
|
|
static size_t s_const_map_size = 0;
|
|
|
|
EXTERNALLY_VISIBLE
|
|
void apc_load(int thread) {
|
|
static void *handle = NULL;
|
|
if (handle ||
|
|
RuntimeOption::ApcPrimeLibrary.empty() ||
|
|
!RuntimeOption::EnableApc) {
|
|
static uint64_t keep_entry_points_around_under_lto;
|
|
if (++keep_entry_points_around_under_lto == UINT64_MAX) {
|
|
// this had better never happen...
|
|
|
|
// Fill out a cache_info to prevent g++ from optimizing out
|
|
// the calls to const_load_impl*
|
|
cache_info info;
|
|
info.a_name = "dummy";
|
|
info.use_const = true;
|
|
|
|
const_load();
|
|
const_load_impl(&info, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
|
const_load_impl_compressed(&info,
|
|
NULL, NULL, NULL,
|
|
NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL);
|
|
apc_load_impl(&info, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
|
apc_load_impl_compressed(&info,
|
|
NULL, NULL, NULL,
|
|
NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL);
|
|
}
|
|
return;
|
|
}
|
|
|
|
Timer timer(Timer::WallTime, "loading APC data");
|
|
handle = dlopen(RuntimeOption::ApcPrimeLibrary.c_str(), RTLD_LAZY);
|
|
if (!handle) {
|
|
throw Exception("Unable to open apc prime library %s: %s",
|
|
RuntimeOption::ApcPrimeLibrary.c_str(), dlerror());
|
|
}
|
|
|
|
if (thread <= 1) {
|
|
apc_load_func(handle, "_apc_load_all")();
|
|
} else {
|
|
int count = ((int(*)())apc_load_func(handle, "_apc_load_count"))();
|
|
|
|
ApcLoadJobPtrVec jobs;
|
|
jobs.reserve(count);
|
|
for (int i = 0; i < count; i++) {
|
|
jobs.push_back(ApcLoadJobPtr(new ApcLoadJob(handle, i)));
|
|
}
|
|
JobDispatcher<ApcLoadJob, ApcLoadWorker>(jobs, thread).run();
|
|
}
|
|
|
|
s_apc_store[0].primeDone();
|
|
|
|
if (RuntimeOption::EnableConstLoad) {
|
|
#ifdef USE_JEMALLOC
|
|
size_t allocated_before = 0;
|
|
size_t allocated_after = 0;
|
|
size_t sz = sizeof(size_t);
|
|
if (mallctl) {
|
|
uint64_t epoch = 1;
|
|
mallctl("epoch", NULL, NULL, &epoch, sizeof(epoch));
|
|
mallctl("stats.allocated", &allocated_before, &sz, NULL, 0);
|
|
// Ignore the first result because it may be inaccurate due to internal
|
|
// allocation.
|
|
epoch = 1;
|
|
mallctl("epoch", NULL, NULL, &epoch, sizeof(epoch));
|
|
mallctl("stats.allocated", &allocated_before, &sz, NULL, 0);
|
|
}
|
|
#endif
|
|
apc_load_func(handle, "_hphp_const_load_all")();
|
|
#ifdef USE_JEMALLOC
|
|
if (mallctl) {
|
|
uint64_t epoch = 1;
|
|
mallctl("epoch", NULL, NULL, &epoch, sizeof(epoch));
|
|
sz = sizeof(size_t);
|
|
mallctl("stats.allocated", &allocated_after, &sz, NULL, 0);
|
|
s_const_map_size = allocated_after - allocated_before;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// We've copied all the data out, so close it out.
|
|
dlclose(handle);
|
|
}
|
|
|
|
size_t get_const_map_size() {
|
|
return s_const_map_size;
|
|
}
|
|
|
|
//define in ext_fb.cpp
|
|
extern void const_load_set(CStrRef key, CVarRef value);
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Constant and APC priming with uncompressed data
|
|
// Note (qixin): this is going to be deprecated by the compressed version.
|
|
|
|
static int count_items(const char **p, int step) {
|
|
int count = 0;
|
|
for (const char **k = p; *k; k += step) {
|
|
count++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
EXTERNALLY_VISIBLE
|
|
void const_load_impl(struct cache_info *info,
|
|
const char **int_keys, long long *int_values,
|
|
const char **char_keys, char *char_values,
|
|
const char **strings, const char **objects,
|
|
const char **thrifts, const char **others) {
|
|
if (!RuntimeOption::EnableConstLoad || !info || !info->use_const) return;
|
|
{
|
|
int count = count_items(int_keys, 2);
|
|
if (count) {
|
|
const char **k = int_keys;
|
|
long long* v = int_values;
|
|
for (int i = 0; i < count; i++, k += 2) {
|
|
String key(*k, (int)(int64_t)*(k+1), CopyString);
|
|
int64_t value = *v++;
|
|
const_load_set(key, value);
|
|
}
|
|
}
|
|
}
|
|
{
|
|
int count = count_items(char_keys, 2);
|
|
if (count) {
|
|
const char **k = char_keys;
|
|
char *v = char_values;
|
|
for (int i = 0; i < count; i++, k += 2) {
|
|
String key(*k, (int)(int64_t)*(k+1), CopyString);
|
|
Variant value;
|
|
switch (*v++) {
|
|
case 0: value = false; break;
|
|
case 1: value = true; break;
|
|
case 2: value = uninit_null(); break;
|
|
default:
|
|
throw Exception("bad apc archive, unknown char type");
|
|
}
|
|
const_load_set(key, value);
|
|
}
|
|
}
|
|
}
|
|
{
|
|
int count = count_items(strings, 4);
|
|
if (count) {
|
|
const char **p = strings;
|
|
for (int i = 0; i < count; i++, p += 4) {
|
|
String key(*p, (int)(int64_t)*(p+1), CopyString);
|
|
String value(*(p+2), (int)(int64_t)*(p+3), CopyString);
|
|
const_load_set(key, value);
|
|
}
|
|
}
|
|
}
|
|
// unserialize_from_string object is extremely slow here;
|
|
// currently turned off: no objects in haste_maps.
|
|
if (false) {
|
|
int count = count_items(objects, 4);
|
|
if (count) {
|
|
const char **p = objects;
|
|
for (int i = 0; i < count; i++, p += 4) {
|
|
String key(*p, (int)(int64_t)*(p+1), CopyString);
|
|
String value(*(p+2), (int)(int64_t)*(p+3), AttachLiteral);
|
|
const_load_set(key, unserialize_from_string(value));
|
|
}
|
|
}
|
|
}
|
|
{
|
|
int count = count_items(thrifts, 4);
|
|
if (count) {
|
|
vector<SharedStore::KeyValuePair> vars(count);
|
|
const char **p = thrifts;
|
|
for (int i = 0; i < count; i++, p += 4) {
|
|
String key(*p, (int)(int64_t)*(p+1), CopyString);
|
|
String value(*(p+2), (int)(int64_t)*(p+3), AttachLiteral);
|
|
Variant success;
|
|
Variant v = f_fb_thrift_unserialize(value, ref(success));
|
|
if (same(success, false)) {
|
|
throw Exception("bad apc archive, f_fb_thrift_unserialize failed");
|
|
}
|
|
const_load_set(key, v);
|
|
}
|
|
}
|
|
}
|
|
{//Would we use others[]?
|
|
int count = count_items(others, 4);
|
|
if (count) {
|
|
const char **p = others;
|
|
for (int i = 0; i < count; i++, p += 4) {
|
|
String key(*p, (int)(int64_t)*(p+1), CopyString);
|
|
String value(*(p+2), (int)(int64_t)*(p+3), AttachLiteral);
|
|
Variant v = unserialize_from_string(value);
|
|
if (same(v, false)) {
|
|
throw Exception("bad apc archive, unserialize_from_string failed");
|
|
}
|
|
const_load_set(key, v);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
EXTERNALLY_VISIBLE
|
|
void apc_load_impl(struct cache_info *info,
|
|
const char **int_keys, long long *int_values,
|
|
const char **char_keys, char *char_values,
|
|
const char **strings, const char **objects,
|
|
const char **thrifts, const char **others) {
|
|
if (!RuntimeOption::ForceConstLoadToAPC) {
|
|
if (RuntimeOption::EnableConstLoad && info && info->use_const) return;
|
|
}
|
|
SharedStore &s = s_apc_store[0];
|
|
{
|
|
int count = count_items(int_keys, 2);
|
|
if (count) {
|
|
vector<SharedStore::KeyValuePair> vars(count);
|
|
const char **k = int_keys;
|
|
long long*v = int_values;
|
|
for (int i = 0; i < count; i++, k += 2) {
|
|
SharedStore::KeyValuePair &item = vars[i];
|
|
item.key = *k;
|
|
item.len = (int)(int64_t)*(k+1);
|
|
s.constructPrime(*v++, item);
|
|
}
|
|
s.prime(vars);
|
|
}
|
|
}
|
|
{
|
|
int count = count_items(char_keys, 2);
|
|
if (count) {
|
|
vector<SharedStore::KeyValuePair> vars(count);
|
|
const char **k = char_keys;
|
|
char *v = char_values;
|
|
for (int i = 0; i < count; i++, k += 2) {
|
|
SharedStore::KeyValuePair &item = vars[i];
|
|
item.key = *k;
|
|
item.len = (int)(int64_t)*(k+1);
|
|
switch (*v++) {
|
|
case 0: s.constructPrime(false, item); break;
|
|
case 1: s.constructPrime(true , item); break;
|
|
case 2: s.constructPrime(uninit_null() , item); break;
|
|
default:
|
|
throw Exception("bad apc archive, unknown char type");
|
|
}
|
|
}
|
|
s.prime(vars);
|
|
}
|
|
}
|
|
{
|
|
int count = count_items(strings, 4);
|
|
if (count) {
|
|
vector<SharedStore::KeyValuePair> vars(count);
|
|
const char **p = strings;
|
|
for (int i = 0; i < count; i++, p += 4) {
|
|
SharedStore::KeyValuePair &item = vars[i];
|
|
item.key = *p;
|
|
item.len = (int)(int64_t)*(p+1);
|
|
// Strings would be copied into APC anyway.
|
|
String value(*(p+2), (int)(int64_t)*(p+3), AttachLiteral);
|
|
s.constructPrime(value, item, false);
|
|
}
|
|
s.prime(vars);
|
|
}
|
|
}
|
|
{
|
|
int count = count_items(objects, 4);
|
|
if (count) {
|
|
vector<SharedStore::KeyValuePair> vars(count);
|
|
const char **p = objects;
|
|
for (int i = 0; i < count; i++, p += 4) {
|
|
SharedStore::KeyValuePair &item = vars[i];
|
|
item.key = *p;
|
|
item.len = (int)(int64_t)*(p+1);
|
|
String value(*(p+2), (int)(int64_t)*(p+3), AttachLiteral);
|
|
s.constructPrime(value, item, true);
|
|
}
|
|
s.prime(vars);
|
|
}
|
|
}
|
|
{
|
|
int count = count_items(thrifts, 4);
|
|
if (count) {
|
|
vector<SharedStore::KeyValuePair> vars(count);
|
|
const char **p = thrifts;
|
|
for (int i = 0; i < count; i++, p += 4) {
|
|
SharedStore::KeyValuePair &item = vars[i];
|
|
item.key = *p;
|
|
item.len = (int)(int64_t)*(p+1);
|
|
String value(*(p+2), (int)(int64_t)*(p+3), AttachLiteral);
|
|
Variant success;
|
|
Variant v = f_fb_thrift_unserialize(value, ref(success));
|
|
if (same(success, false)) {
|
|
throw Exception("bad apc archive, f_fb_thrift_unserialize failed");
|
|
}
|
|
s.constructPrime(v, item);
|
|
}
|
|
s.prime(vars);
|
|
}
|
|
}
|
|
{
|
|
int count = count_items(others, 4);
|
|
if (count) {
|
|
vector<SharedStore::KeyValuePair> vars(count);
|
|
const char **p = others;
|
|
for (int i = 0; i < count; i++, p += 4) {
|
|
SharedStore::KeyValuePair &item = vars[i];
|
|
item.key = *p;
|
|
item.len = (int)(int64_t)*(p+1);
|
|
|
|
String value(*(p+2), (int)(int64_t)*(p+3), AttachLiteral);
|
|
Variant v = unserialize_from_string(value);
|
|
if (same(v, false)) {
|
|
// we can't possibly get here if it was a boolean "false" that's
|
|
// supposed to be serialized as a char
|
|
throw Exception("bad apc archive, unserialize_from_string failed");
|
|
}
|
|
s.constructPrime(v, item);
|
|
}
|
|
s.prime(vars);
|
|
}
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Constant and APC priming with compressed data
|
|
|
|
EXTERNALLY_VISIBLE
|
|
void const_load_impl_compressed
|
|
(struct cache_info *info,
|
|
int *int_lens, const char *int_keys, long long *int_values,
|
|
int *char_lens, const char *char_keys, char *char_values,
|
|
int *string_lens, const char *strings,
|
|
int *object_lens, const char *objects,
|
|
int *thrift_lens, const char *thrifts,
|
|
int *other_lens, const char *others) {
|
|
if (!RuntimeOption::EnableConstLoad || !info || !info->use_const) return;
|
|
{
|
|
int count = int_lens[0];
|
|
int len = int_lens[1];
|
|
if (count) {
|
|
char *keys = gzdecode(int_keys, len);
|
|
if (keys == NULL) throw Exception("bad compressed const archive.");
|
|
ScopedMem holder(keys);
|
|
const char *k = keys;
|
|
long long* v = int_values;
|
|
for (int i = 0; i < count; i++) {
|
|
String key(k, int_lens[i + 2], CopyString);
|
|
int64_t value = *v++;
|
|
const_load_set(key, value);
|
|
k += int_lens[i + 2] + 1;
|
|
}
|
|
assert((k - keys) == len);
|
|
}
|
|
}
|
|
{
|
|
int count = char_lens[0];
|
|
int len = char_lens[1];
|
|
if (count) {
|
|
char *keys = gzdecode(char_keys, len);
|
|
if (keys == NULL) throw Exception("bad compressed const archive.");
|
|
ScopedMem holder(keys);
|
|
const char *k = keys;
|
|
char *v = char_values;
|
|
for (int i = 0; i < count; i++) {
|
|
String key(k, char_lens[i + 2], CopyString);
|
|
Variant value;
|
|
switch (*v++) {
|
|
case 0: value = false; break;
|
|
case 1: value = true; break;
|
|
case 2: value = uninit_null(); break;
|
|
default:
|
|
throw Exception("bad const archive, unknown char type");
|
|
}
|
|
const_load_set(key, value);
|
|
k += char_lens[i + 2] + 1;
|
|
}
|
|
assert((k - keys) == len);
|
|
}
|
|
}
|
|
{
|
|
int count = string_lens[0] / 2;
|
|
int len = string_lens[1];
|
|
if (count) {
|
|
char *decoded = gzdecode(strings, len);
|
|
if (decoded == NULL) throw Exception("bad compressed const archive.");
|
|
ScopedMem holder(decoded);
|
|
const char *p = decoded;
|
|
for (int i = 0; i < count; i++) {
|
|
String key(p, string_lens[i + i + 2], CopyString);
|
|
p += string_lens[i + i + 2] + 1;
|
|
String value(p, string_lens[i + i + 3], CopyString);
|
|
const_load_set(key, value);
|
|
p += string_lens[i + i + 3] + 1;
|
|
}
|
|
assert((p - decoded) == len);
|
|
}
|
|
}
|
|
// unserialize_from_string object is extremely slow here;
|
|
// currently turned off: no objects in haste_maps.
|
|
if (false) {
|
|
int count = object_lens[0] / 2;
|
|
int len = object_lens[1];
|
|
if (count) {
|
|
char *decoded = gzdecode(objects, len);
|
|
if (decoded == NULL) throw Exception("bad compressed const archive.");
|
|
ScopedMem holder(decoded);
|
|
const char *p = decoded;
|
|
for (int i = 0; i < count; i++) {
|
|
String key(p, object_lens[i + i + 2], CopyString);
|
|
p += object_lens[i + i + 2] + 1;
|
|
String value(p, object_lens[i + i + 3], AttachLiteral);
|
|
const_load_set(key, unserialize_from_string(value));
|
|
p += object_lens[i + i + 3] + 1;
|
|
}
|
|
assert((p - decoded) == len);
|
|
}
|
|
}
|
|
{
|
|
int count = thrift_lens[0] / 2;
|
|
int len = thrift_lens[1];
|
|
if (count) {
|
|
char *decoded = gzdecode(thrifts, len);
|
|
if (decoded == NULL) throw Exception("bad compressed const archive.");
|
|
ScopedMem holder(decoded);
|
|
const char *p = decoded;
|
|
for (int i = 0; i < count; i++) {
|
|
String key(p, thrift_lens[i + i + 2], CopyString);
|
|
p += thrift_lens[i + i + 2] + 1;
|
|
String value(p, thrift_lens[i + i + 3], AttachLiteral);
|
|
Variant success;
|
|
Variant v = f_fb_thrift_unserialize(value, ref(success));
|
|
if (same(success, false)) {
|
|
throw Exception("bad apc archive, f_fb_thrift_unserialize failed");
|
|
}
|
|
const_load_set(key, v);
|
|
p += thrift_lens[i + i + 3] + 1;
|
|
}
|
|
assert((p - decoded) == len);
|
|
}
|
|
}
|
|
{//Would we use others[]?
|
|
int count = other_lens[0] / 2;
|
|
int len = other_lens[1];
|
|
if (count) {
|
|
char *decoded = gzdecode(others, len);
|
|
if (decoded == NULL) throw Exception("bad compressed const archive.");
|
|
ScopedMem holder(decoded);
|
|
const char *p = decoded;
|
|
for (int i = 0; i < count; i++) {
|
|
String key(p, other_lens[i + i + 2], CopyString);
|
|
p += other_lens[i + i + 2] + 1;
|
|
String value(p, other_lens[i + i + 3], AttachLiteral);
|
|
Variant v = unserialize_from_string(value);
|
|
if (same(v, false)) {
|
|
throw Exception("bad apc archive, unserialize_from_string failed");
|
|
}
|
|
const_load_set(key, v);
|
|
p += other_lens[i + i + 3] + 1;
|
|
}
|
|
assert((p - decoded) == len);
|
|
}
|
|
}
|
|
}
|
|
|
|
EXTERNALLY_VISIBLE
|
|
void apc_load_impl_compressed
|
|
(struct cache_info *info,
|
|
int *int_lens, const char *int_keys, long long *int_values,
|
|
int *char_lens, const char *char_keys, char *char_values,
|
|
int *string_lens, const char *strings,
|
|
int *object_lens, const char *objects,
|
|
int *thrift_lens, const char *thrifts,
|
|
int *other_lens, const char *others) {
|
|
if (!RuntimeOption::ForceConstLoadToAPC) {
|
|
if (RuntimeOption::EnableConstLoad && info && info->use_const) return;
|
|
}
|
|
SharedStore &s = s_apc_store[0];
|
|
{
|
|
int count = int_lens[0];
|
|
int len = int_lens[1];
|
|
if (count) {
|
|
vector<SharedStore::KeyValuePair> vars(count);
|
|
char *keys = gzdecode(int_keys, len);
|
|
if (keys == NULL) throw Exception("bad compressed apc archive.");
|
|
ScopedMem holder(keys);
|
|
const char *k = keys;
|
|
long long* v = int_values;
|
|
for (int i = 0; i < count; i++) {
|
|
SharedStore::KeyValuePair &item = vars[i];
|
|
item.key = k;
|
|
item.len = int_lens[i + 2];
|
|
s.constructPrime(*v++, item);
|
|
k += int_lens[i + 2] + 1; // skip \0
|
|
}
|
|
s.prime(vars);
|
|
assert((k - keys) == len);
|
|
}
|
|
}
|
|
{
|
|
int count = char_lens[0];
|
|
int len = char_lens[1];
|
|
if (count) {
|
|
vector<SharedStore::KeyValuePair> vars(count);
|
|
char *keys = gzdecode(char_keys, len);
|
|
if (keys == NULL) throw Exception("bad compressed apc archive.");
|
|
ScopedMem holder(keys);
|
|
const char *k = keys;
|
|
char *v = char_values;
|
|
for (int i = 0; i < count; i++) {
|
|
SharedStore::KeyValuePair &item = vars[i];
|
|
item.key = k;
|
|
item.len = char_lens[i + 2];
|
|
switch (*v++) {
|
|
case 0: s.constructPrime(false, item); break;
|
|
case 1: s.constructPrime(true , item); break;
|
|
case 2: s.constructPrime(uninit_null() , item); break;
|
|
default:
|
|
throw Exception("bad apc archive, unknown char type");
|
|
}
|
|
k += char_lens[i + 2] + 1; // skip \0
|
|
}
|
|
s.prime(vars);
|
|
assert((k - keys) == len);
|
|
}
|
|
}
|
|
{
|
|
int count = string_lens[0] / 2;
|
|
int len = string_lens[1];
|
|
if (count) {
|
|
vector<SharedStore::KeyValuePair> vars(count);
|
|
char *decoded = gzdecode(strings, len);
|
|
if (decoded == NULL) throw Exception("bad compressed apc archive.");
|
|
ScopedMem holder(decoded);
|
|
const char *p = decoded;
|
|
for (int i = 0; i < count; i++) {
|
|
SharedStore::KeyValuePair &item = vars[i];
|
|
item.key = p;
|
|
item.len = string_lens[i + i + 2];
|
|
p += string_lens[i + i + 2] + 1; // skip \0
|
|
// Strings would be copied into APC anyway.
|
|
String value(p, string_lens[i + i + 3], AttachLiteral);
|
|
// todo: t2539893: check if value is already a static string
|
|
s.constructPrime(value, item, false);
|
|
p += string_lens[i + i + 3] + 1; // skip \0
|
|
}
|
|
s.prime(vars);
|
|
assert((p - decoded) == len);
|
|
}
|
|
}
|
|
{
|
|
int count = object_lens[0] / 2;
|
|
int len = object_lens[1];
|
|
if (count) {
|
|
vector<SharedStore::KeyValuePair> vars(count);
|
|
char *decoded = gzdecode(objects, len);
|
|
if (decoded == NULL) throw Exception("bad compressed APC archive.");
|
|
ScopedMem holder(decoded);
|
|
const char *p = decoded;
|
|
for (int i = 0; i < count; i++) {
|
|
SharedStore::KeyValuePair &item = vars[i];
|
|
item.key = p;
|
|
item.len = object_lens[i + i + 2];
|
|
p += object_lens[i + i + 2] + 1; // skip \0
|
|
String value(p, object_lens[i + i + 3], AttachLiteral);
|
|
s.constructPrime(value, item, true);
|
|
p += object_lens[i + i + 3] + 1; // skip \0
|
|
}
|
|
s.prime(vars);
|
|
assert((p - decoded) == len);
|
|
}
|
|
}
|
|
{
|
|
int count = thrift_lens[0] / 2;
|
|
int len = thrift_lens[1];
|
|
if (count) {
|
|
vector<SharedStore::KeyValuePair> vars(count);
|
|
char *decoded = gzdecode(thrifts, len);
|
|
if (decoded == NULL) throw Exception("bad compressed apc archive.");
|
|
ScopedMem holder(decoded);
|
|
const char *p = decoded;
|
|
for (int i = 0; i < count; i++) {
|
|
SharedStore::KeyValuePair &item = vars[i];
|
|
item.key = p;
|
|
item.len = thrift_lens[i + i + 2];
|
|
p += thrift_lens[i + i + 2] + 1; // skip \0
|
|
String value(p, thrift_lens[i + i + 3], AttachLiteral);
|
|
Variant success;
|
|
Variant v = f_fb_thrift_unserialize(value, ref(success));
|
|
if (same(success, false)) {
|
|
throw Exception("bad apc archive, f_fb_thrift_unserialize failed");
|
|
}
|
|
s.constructPrime(v, item);
|
|
p += thrift_lens[i + i + 3] + 1; // skip \0
|
|
}
|
|
s.prime(vars);
|
|
assert((p - decoded) == len);
|
|
}
|
|
}
|
|
{
|
|
int count = other_lens[0] / 2;
|
|
int len = other_lens[1];
|
|
if (count) {
|
|
vector<SharedStore::KeyValuePair> vars(count);
|
|
char *decoded = gzdecode(others, len);
|
|
if (decoded == NULL) throw Exception("bad compressed apc archive.");
|
|
ScopedMem holder(decoded);
|
|
const char *p = decoded;
|
|
for (int i = 0; i < count; i++) {
|
|
SharedStore::KeyValuePair &item = vars[i];
|
|
item.key = p;
|
|
item.len = other_lens[i + i + 2];
|
|
p += other_lens[i + i + 2] + 1; // skip \0
|
|
String value(p, other_lens[i + i + 3], AttachLiteral);
|
|
Variant v = unserialize_from_string(value);
|
|
if (same(v, false)) {
|
|
// we can't possibly get here if it was a boolean "false" that's
|
|
// supposed to be serialized as a char
|
|
throw Exception("bad apc archive, unserialize_from_string failed");
|
|
}
|
|
s.constructPrime(v, item);
|
|
p += other_lens[i + i + 3] + 1; // skip \0
|
|
}
|
|
s.prime(vars);
|
|
assert((p - decoded) == len);
|
|
}
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
static double my_time() {
|
|
struct timeval a;
|
|
double t;
|
|
gettimeofday(&a, NULL);
|
|
t = a.tv_sec + (a.tv_usec/1000000.00);
|
|
return t;
|
|
}
|
|
|
|
const StaticString
|
|
s_total("total"),
|
|
s_current("current"),
|
|
s_filename("filename"),
|
|
s_name("name"),
|
|
s_done("done"),
|
|
s_temp_filename("temp_filename"),
|
|
s_cancel_upload("cancel_upload"),
|
|
s_rate("rate");
|
|
|
|
#define RFC1867_TRACKING_KEY_MAXLEN 63
|
|
#define RFC1867_NAME_MAXLEN 63
|
|
#define RFC1867_FILENAME_MAXLEN 127
|
|
|
|
int apc_rfc1867_progress(apc_rfc1867_data *rfc1867ApcData,
|
|
unsigned int event, void *event_data,
|
|
void **extra) {
|
|
switch (event) {
|
|
case MULTIPART_EVENT_START: {
|
|
multipart_event_start *data = (multipart_event_start *) event_data;
|
|
rfc1867ApcData->content_length = data->content_length;
|
|
rfc1867ApcData->tracking_key.clear();
|
|
rfc1867ApcData->name.clear();
|
|
rfc1867ApcData->cancel_upload = 0;
|
|
rfc1867ApcData->temp_filename = NULL;
|
|
rfc1867ApcData->start_time = my_time();
|
|
rfc1867ApcData->bytes_processed = 0;
|
|
rfc1867ApcData->prev_bytes_processed = 0;
|
|
rfc1867ApcData->rate = 0;
|
|
rfc1867ApcData->update_freq = RuntimeOption::Rfc1867Freq;
|
|
|
|
if (rfc1867ApcData->update_freq < 0) {
|
|
assert(false); // TODO: support percentage
|
|
// frequency is a percentage, not bytes
|
|
rfc1867ApcData->update_freq =
|
|
rfc1867ApcData->content_length * RuntimeOption::Rfc1867Freq / 100;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case MULTIPART_EVENT_FORMDATA: {
|
|
multipart_event_formdata *data = (multipart_event_formdata *)event_data;
|
|
if (data->name &&
|
|
!strncasecmp(data->name, RuntimeOption::Rfc1867Name.c_str(),
|
|
RuntimeOption::Rfc1867Name.size()) &&
|
|
data->value && data->length &&
|
|
data->length < RFC1867_TRACKING_KEY_MAXLEN -
|
|
RuntimeOption::Rfc1867Prefix.size()) {
|
|
int len = RuntimeOption::Rfc1867Prefix.size();
|
|
if (len > RFC1867_TRACKING_KEY_MAXLEN) {
|
|
len = RFC1867_TRACKING_KEY_MAXLEN;
|
|
}
|
|
rfc1867ApcData->tracking_key =
|
|
string(RuntimeOption::Rfc1867Prefix.c_str(), len);
|
|
len = strlen(*data->value);
|
|
int rem = RFC1867_TRACKING_KEY_MAXLEN -
|
|
rfc1867ApcData->tracking_key.size();
|
|
if (len > rem) len = rem;
|
|
rfc1867ApcData->tracking_key +=
|
|
string(*data->value, len);
|
|
rfc1867ApcData->bytes_processed = data->post_bytes_processed;
|
|
}
|
|
/* Facebook: Temporary fix for a bug in PHP's rfc1867 code,
|
|
fixed here for convenience:
|
|
http://cvs.php.net/viewvc.cgi/php-src/main/
|
|
rfc1867.c?r1=1.173.2.1.2.11&r2=1.173.2.1.2.12 */
|
|
(*data->newlength) = data->length;
|
|
break;
|
|
}
|
|
|
|
case MULTIPART_EVENT_FILE_START:
|
|
if (!rfc1867ApcData->tracking_key.empty()) {
|
|
multipart_event_file_start *data =
|
|
(multipart_event_file_start *)event_data;
|
|
|
|
rfc1867ApcData->bytes_processed = data->post_bytes_processed;
|
|
int len = strlen(*data->filename);
|
|
if (len > RFC1867_FILENAME_MAXLEN) len = RFC1867_FILENAME_MAXLEN;
|
|
rfc1867ApcData->filename = string(*data->filename, len);
|
|
rfc1867ApcData->temp_filename = NULL;
|
|
len = strlen(data->name);
|
|
if (len > RFC1867_NAME_MAXLEN) len = RFC1867_NAME_MAXLEN;
|
|
rfc1867ApcData->name = string(data->name, len);
|
|
ArrayInit track(6);
|
|
track.set(s_total, rfc1867ApcData->content_length);
|
|
track.set(s_current, rfc1867ApcData->bytes_processed);
|
|
track.set(s_filename, rfc1867ApcData->filename);
|
|
track.set(s_name, rfc1867ApcData->name);
|
|
track.set(s_done, 0);
|
|
track.set(s_start_time, rfc1867ApcData->start_time);
|
|
f_apc_store(rfc1867ApcData->tracking_key, track.create(), 3600);
|
|
}
|
|
break;
|
|
|
|
case MULTIPART_EVENT_FILE_DATA:
|
|
if (!rfc1867ApcData->tracking_key.empty()) {
|
|
multipart_event_file_data *data =
|
|
(multipart_event_file_data *) event_data;
|
|
rfc1867ApcData->bytes_processed = data->post_bytes_processed;
|
|
if (rfc1867ApcData->bytes_processed -
|
|
rfc1867ApcData->prev_bytes_processed >
|
|
rfc1867ApcData->update_freq) {
|
|
Variant v;
|
|
if (s_apc_store[0].get(rfc1867ApcData->tracking_key, v)) {
|
|
if (v.is(KindOfArray)) {
|
|
ArrayInit track(6);
|
|
track.set(s_total, rfc1867ApcData->content_length);
|
|
track.set(s_current, rfc1867ApcData->bytes_processed);
|
|
track.set(s_filename, rfc1867ApcData->filename);
|
|
track.set(s_name, rfc1867ApcData->name);
|
|
track.set(s_done, 0);
|
|
track.set(s_start_time, rfc1867ApcData->start_time);
|
|
f_apc_store(rfc1867ApcData->tracking_key, track.create(), 3600);
|
|
}
|
|
rfc1867ApcData->prev_bytes_processed =
|
|
rfc1867ApcData->bytes_processed;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MULTIPART_EVENT_FILE_END:
|
|
if (!rfc1867ApcData->tracking_key.empty()) {
|
|
multipart_event_file_end *data =
|
|
(multipart_event_file_end *)event_data;
|
|
rfc1867ApcData->bytes_processed = data->post_bytes_processed;
|
|
rfc1867ApcData->cancel_upload = data->cancel_upload;
|
|
rfc1867ApcData->temp_filename = data->temp_filename;
|
|
ArrayInit track(8);
|
|
track.set(s_total, rfc1867ApcData->content_length);
|
|
track.set(s_current, rfc1867ApcData->bytes_processed);
|
|
track.set(s_filename, rfc1867ApcData->filename);
|
|
track.set(s_name, rfc1867ApcData->name);
|
|
track.set(s_temp_filename, rfc1867ApcData->temp_filename, CopyString);
|
|
track.set(s_cancel_upload, rfc1867ApcData->cancel_upload);
|
|
track.set(s_done, 0);
|
|
track.set(s_start_time, rfc1867ApcData->start_time);
|
|
f_apc_store(rfc1867ApcData->tracking_key, track.create(), 3600);
|
|
}
|
|
break;
|
|
|
|
case MULTIPART_EVENT_END:
|
|
if (!rfc1867ApcData->tracking_key.empty()) {
|
|
double now = my_time();
|
|
multipart_event_end *data = (multipart_event_end *)event_data;
|
|
rfc1867ApcData->bytes_processed = data->post_bytes_processed;
|
|
if(now>rfc1867ApcData->start_time) {
|
|
rfc1867ApcData->rate =
|
|
8.0*rfc1867ApcData->bytes_processed/(now-rfc1867ApcData->start_time);
|
|
} else {
|
|
rfc1867ApcData->rate =
|
|
8.0*rfc1867ApcData->bytes_processed; /* Too quick */
|
|
ArrayInit track(8);
|
|
track.set(s_total, rfc1867ApcData->content_length);
|
|
track.set(s_current, rfc1867ApcData->bytes_processed);
|
|
track.set(s_rate, rfc1867ApcData->rate);
|
|
track.set(s_filename, rfc1867ApcData->filename);
|
|
track.set(s_name, rfc1867ApcData->name);
|
|
track.set(s_cancel_upload, rfc1867ApcData->cancel_upload);
|
|
track.set(s_done, 1);
|
|
track.set(s_start_time, rfc1867ApcData->start_time);
|
|
f_apc_store(rfc1867ApcData->tracking_key, track.create(), 3600);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// apc serialization
|
|
|
|
String apc_serialize(CVarRef value) {
|
|
VariableSerializer::Type sType =
|
|
RuntimeOption::EnableApcSerialize ?
|
|
VariableSerializer::Type::APCSerialize :
|
|
VariableSerializer::Type::Serialize;
|
|
VariableSerializer vs(sType);
|
|
return vs.serialize(value, true);
|
|
}
|
|
|
|
Variant apc_unserialize(CStrRef str) {
|
|
VariableUnserializer::Type sType =
|
|
RuntimeOption::EnableApcSerialize ?
|
|
VariableUnserializer::Type::APCSerialize :
|
|
VariableUnserializer::Type::Serialize;
|
|
return unserialize_ex(str, sType);
|
|
}
|
|
|
|
void reserialize(VariableUnserializer *uns, StringBuffer &buf) {
|
|
|
|
char type = uns->readChar();
|
|
char sep = uns->readChar();
|
|
|
|
if (type == 'N') {
|
|
buf.append(type);
|
|
buf.append(sep);
|
|
return;
|
|
}
|
|
|
|
switch (type) {
|
|
case 'r':
|
|
case 'R':
|
|
case 'b':
|
|
case 'i':
|
|
case 'd':
|
|
{
|
|
buf.append(type);
|
|
buf.append(sep);
|
|
while (uns->peek() != ';') {
|
|
char ch;
|
|
ch = uns->readChar();
|
|
buf.append(ch);
|
|
}
|
|
}
|
|
break;
|
|
case 'S':
|
|
case 'A':
|
|
{
|
|
// shouldn't happen, but keep the code here anyway.
|
|
buf.append(type);
|
|
buf.append(sep);
|
|
char pointer[8];
|
|
uns->read(pointer, 8);
|
|
buf.append(pointer, 8);
|
|
}
|
|
break;
|
|
case 's':
|
|
{
|
|
String v;
|
|
v.unserialize(uns);
|
|
assert(!v.isNull());
|
|
if (v->isStatic()) {
|
|
union {
|
|
char pointer[8];
|
|
StringData *sd;
|
|
} u;
|
|
u.sd = v.get();
|
|
buf.append("S:");
|
|
buf.append(u.pointer, 8);
|
|
buf.append(';');
|
|
} else {
|
|
buf.append("s:");
|
|
buf.append(v.size());
|
|
buf.append(":\"");
|
|
buf.append(v.data(), v.size());
|
|
buf.append("\";");
|
|
}
|
|
sep = uns->readChar();
|
|
return;
|
|
}
|
|
break;
|
|
case 'a':
|
|
{
|
|
buf.append("a:");
|
|
int64_t size = uns->readInt();
|
|
char sep2 = uns->readChar();
|
|
buf.append(size);
|
|
buf.append(sep2);
|
|
sep2 = uns->readChar();
|
|
buf.append(sep2);
|
|
for (int64_t i = 0; i < size; i++) {
|
|
reserialize(uns, buf); // key
|
|
reserialize(uns, buf); // value
|
|
}
|
|
sep2 = uns->readChar(); // '}'
|
|
buf.append(sep2);
|
|
return;
|
|
}
|
|
break;
|
|
case 'o':
|
|
case 'O':
|
|
{
|
|
buf.append(type);
|
|
buf.append(sep);
|
|
|
|
String clsName;
|
|
clsName.unserialize(uns);
|
|
buf.append(clsName.size());
|
|
buf.append(":\"");
|
|
buf.append(clsName.data(), clsName.size());
|
|
buf.append("\":");
|
|
|
|
uns->readChar();
|
|
int64_t size = uns->readInt();
|
|
char sep2 = uns->readChar();
|
|
|
|
buf.append(size);
|
|
buf.append(sep2);
|
|
sep2 = uns->readChar(); // '{'
|
|
buf.append(sep2);
|
|
for (int64_t i = 0; i < size; i++) {
|
|
reserialize(uns, buf); // property name
|
|
reserialize(uns, buf); // property value
|
|
}
|
|
sep2 = uns->readChar(); // '}'
|
|
buf.append(sep2);
|
|
return;
|
|
}
|
|
break;
|
|
case 'C':
|
|
{
|
|
buf.append(type);
|
|
buf.append(sep);
|
|
|
|
String clsName;
|
|
clsName.unserialize(uns);
|
|
buf.append(clsName.size());
|
|
buf.append(":\"");
|
|
buf.append(clsName.data(), clsName.size());
|
|
buf.append("\":");
|
|
|
|
sep = uns->readChar(); // ':'
|
|
String serialized;
|
|
serialized.unserialize(uns, '{', '}');
|
|
buf.append(serialized.size());
|
|
buf.append(":{");
|
|
buf.append(serialized.data(), serialized.size());
|
|
buf.append('}');
|
|
return;
|
|
}
|
|
break;
|
|
default:
|
|
throw Exception("Unknown type '%c'", type);
|
|
}
|
|
|
|
sep = uns->readChar(); // the last ';'
|
|
buf.append(sep);
|
|
}
|
|
|
|
String apc_reserialize(CStrRef str) {
|
|
if (str.empty() ||
|
|
!RuntimeOption::EnableApcSerialize) return str;
|
|
|
|
VariableUnserializer uns(str.data(), str.size(),
|
|
VariableUnserializer::Type::APCSerialize);
|
|
StringBuffer buf;
|
|
reserialize(&uns, buf);
|
|
|
|
return buf.detach();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// debugging support
|
|
|
|
bool apc_dump(const char *filename, bool keyOnly, int waitSeconds) {
|
|
const int CACHE_ID = 0; /* 0 is used as default for apc */
|
|
std::ofstream out(filename);
|
|
if (out.fail()) {
|
|
return false;
|
|
}
|
|
s_apc_store[CACHE_ID].dump(out, keyOnly, waitSeconds);
|
|
out.close();
|
|
return true;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
}
|