2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 | Copyright (c) 1997-2010 The PHP Group |
7 +----------------------------------------------------------------------+
8 | This source file is subject to version 3.01 of the PHP license, |
9 | that is bundled with this package in the file LICENSE, and is |
10 | available through the world-wide-web at the following url: |
11 | http://www.php.net/license/3_01.txt |
12 | If you did not receive a copy of the PHP license and are unable to |
13 | obtain it through the world-wide-web, please send a note to |
14 | license@php.net so we can mail you a copy immediately. |
15 +----------------------------------------------------------------------+
17 #include "hphp/runtime/ext/apc/ext_apc.h"
28 #include <type_traits>
30 #include <folly/portability/SysTime.h>
32 #include "hphp/util/alloc.h"
33 #include "hphp/util/async-job.h"
34 #include "hphp/util/boot-stats.h"
35 #include "hphp/util/hdf.h"
36 #include "hphp/util/logger.h"
38 #include "hphp/runtime/ext/apc/snapshot-builder.h"
39 #include "hphp/runtime/ext/fb/ext_fb.h"
40 #include "hphp/runtime/base/array-init.h"
41 #include "hphp/runtime/base/comparisons.h"
42 #include "hphp/runtime/base/execution-context.h"
43 #include "hphp/runtime/base/runtime-option.h"
44 #include "hphp/runtime/base/program-functions.h"
45 #include "hphp/runtime/base/builtin-functions.h"
46 #include "hphp/runtime/base/variable-serializer.h"
47 #include "hphp/runtime/base/concurrent-shared-store.h"
48 #include "hphp/runtime/base/ini-setting.h"
49 #include "hphp/runtime/base/config.h"
50 #include "hphp/runtime/base/apc-file-storage.h"
51 #include "hphp/runtime/server/cli-server.h"
53 using HPHP::ScopedMem
;
56 ///////////////////////////////////////////////////////////////////////////////
61 sizeof(ConcurrentTableSharedStore
),
62 alignof(ConcurrentTableSharedStore
)
63 >::type s_apc_storage
;
65 using UserAPCCache
= folly::AtomicHashMap
<uid_t
, ConcurrentTableSharedStore
*>;
70 >::type s_user_apc_storage
;
72 UserAPCCache
& apc_store_local() {
73 void* vpUserStore
= &s_user_apc_storage
;
74 return *static_cast<UserAPCCache
*>(vpUserStore
);
77 ConcurrentTableSharedStore
& apc_store_local(uid_t uid
) {
78 auto& cache
= apc_store_local();
79 auto iter
= cache
.find(uid
);
80 if (iter
!= cache
.end()) return *(iter
->second
);
81 auto table
= new ConcurrentTableSharedStore
;
82 auto res
= cache
.insert(uid
, table
);
83 if (!res
.second
) delete table
;
84 return *res
.first
->second
;
87 ConcurrentTableSharedStore
& apc_store() {
88 if (UNLIKELY(!RuntimeOption::RepoAuthoritative
&&
89 RuntimeOption::EvalUnixServerQuarantineApc
)) {
90 if (auto uc
= get_cli_ucred()) {
91 return apc_store_local(uc
->uid
);
94 void* vpStore
= &s_apc_storage
;
95 return *static_cast<ConcurrentTableSharedStore
*>(vpStore
);
100 //////////////////////////////////////////////////////////////////////
102 void initialize_apc() {
104 // Note: we never destruct APC, currently.
105 void* vpStore
= &s_apc_storage
;
106 new (vpStore
) ConcurrentTableSharedStore
;
108 if (UNLIKELY(!RuntimeOption::RepoAuthoritative
&&
109 RuntimeOption::EvalUnixServerQuarantineApc
)) {
110 new (&s_user_apc_storage
) UserAPCCache(10);
114 //////////////////////////////////////////////////////////////////////
119 extern void const_load();
121 typedef ConcurrentTableSharedStore::KeyValuePair KeyValuePair
;
122 typedef ConcurrentTableSharedStore::DumpMode DumpMode
;
124 static void* keep_alive
;
126 void apcExtension::moduleLoad(const IniSetting::Map
& ini
, Hdf config
) {
127 if (!keep_alive
&& ini
.isString()) {
128 // this is a hack to preserve some dynamic entry points
129 switch (ini
.toString().size()) {
130 case 0: keep_alive
= (void*)const_load
; break;
131 case 2: keep_alive
= (void*)const_load_impl_compressed
; break;
132 case 4: keep_alive
= (void*)apc_load_impl_compressed
; break;
136 Config::Bind(Enable
, ini
, config
, "Server.APC.EnableApc", true);
137 Config::Bind(EnableConstLoad
, ini
, config
, "Server.APC.EnableConstLoad",
139 Config::Bind(ForceConstLoadToAPC
, ini
, config
,
140 "Server.APC.ForceConstLoadToAPC", true);
141 Config::Bind(PrimeLibrary
, ini
, config
, "Server.APC.PrimeLibrary");
142 Config::Bind(LoadThread
, ini
, config
, "Server.APC.LoadThread", 15);
143 Config::Bind(CompletionKeys
, ini
, config
, "Server.APC.CompletionKeys");
144 std::string tblType
= Config::GetString(ini
, config
, "Server.APC.TableType",
146 if (strcasecmp(tblType
.c_str(), "concurrent") == 0) {
147 TableType
= TableTypes::ConcurrentTable
;
149 throw std::runtime_error("invalid apc table type");
151 Config::Bind(EnableApcSerialize
, ini
, config
, "Server.APC.EnableApcSerialize",
153 Config::Bind(ExpireOnSets
, ini
, config
, "Server.APC.ExpireOnSets");
154 Config::Bind(PurgeFrequency
, ini
, config
, "Server.APC.PurgeFrequency", 4096);
155 Config::Bind(PurgeRate
, ini
, config
, "Server.APC.PurgeRate", -1);
157 Config::Bind(AllowObj
, ini
, config
, "Server.APC.AllowObject");
158 Config::Bind(TTLLimit
, ini
, config
, "Server.APC.TTLLimit", -1);
159 // Any TTL > TTLMaxFinite will be made infinite. NB: Applied *after* TTLLimit.
160 Config::Bind(TTLMaxFinite
, ini
, config
, "Server.APC.TTLMaxFinite",
161 std::numeric_limits
<int64_t>::max());
162 Config::Bind(HotPrefix
, ini
, config
, "Server.APC.HotPrefix");
163 Config::Bind(HotSize
, ini
, config
, "Server.APC.HotSize", 30000);
164 Config::Bind(HotLoadFactor
, ini
, config
, "Server.APC.HotLoadFactor", 0.5);
165 Config::Bind(HotKeyAllocLow
, ini
, config
, "Server.APC.HotKeyAllocLow", false);
166 Config::Bind(HotMapAllocLow
, ini
, config
, "Server.APC.HotMapAllocLow", false);
168 // Loads .so PrimeLibrary, writes snapshot output to this file, then exits.
169 Config::Bind(PrimeLibraryUpgradeDest
, ini
, config
,
170 "Server.APC.PrimeLibraryUpgradeDest");
173 Config::Bind(UseFileStorage
, ini
, config
, "Server.APC.FileStorage.Enable");
174 FileStorageChunkSize
= Config::GetInt64(ini
, config
,
175 "Server.APC.FileStorage.ChunkSize",
177 Config::Bind(FileStoragePrefix
, ini
, config
, "Server.APC.FileStorage.Prefix",
179 Config::Bind(FileStorageFlagKey
, ini
, config
,
180 "Server.APC.FileStorage.FlagKey", "_madvise_out");
181 Config::Bind(FileStorageAdviseOutPeriod
, ini
, config
,
182 "Server.APC.FileStorage.AdviseOutPeriod", 1800);
183 Config::Bind(FileStorageKeepFileLinked
, ini
, config
,
184 "Server.APC.FileStorage.KeepFileLinked");
186 Config::Bind(KeyMaturityThreshold
, ini
, config
,
187 "Server.APC.KeyMaturityThreshold", 20);
188 Config::Bind(MaximumCapacity
, ini
, config
, "Server.APC.MaximumCapacity", 0);
189 Config::Bind(KeyFrequencyUpdatePeriod
, ini
, config
,
190 "Server.APC.KeyFrequencyUpdatePeriod", 1000);
192 Config::Bind(NoTTLPrefix
, ini
, config
, "Server.APC.NoTTLPrefix");
195 Config::Bind(UseUncounted
, ini
, config
, "Server.APC.MemModelTreadmill", true);
197 Config::Bind(UseUncounted
, ini
, config
, "Server.APC.MemModelTreadmill",
198 RuntimeOption::ServerExecutionMode());
200 Config::Bind(ShareUncounted
, ini
, config
, "Server.APC.ShareUncounted", true);
201 if (!UseUncounted
&& ShareUncounted
) ShareUncounted
= false;
203 IniSetting::Bind(this, IniSetting::PHP_INI_SYSTEM
, "apc.enabled", &Enable
);
204 IniSetting::Bind(this, IniSetting::PHP_INI_SYSTEM
, "apc.stat",
205 RuntimeOption::RepoAuthoritative
? "0" : "1", &Stat
);
206 IniSetting::Bind(this, IniSetting::PHP_INI_SYSTEM
, "apc.enable_cli",
210 void apcExtension::moduleInit() {
213 Logger::Error("Server.APC.MemModelTreadmill=false ignored in lowptr build");
217 if (UseFileStorage
) {
218 // We use 32 bits to represent offset into a chunk, so don't make it too
220 constexpr int64_t MaxChunkSize
= 1LL << 31;
221 if (FileStorageChunkSize
> MaxChunkSize
) {
222 Logger::Warning("Server.APC.FileStorage.ChunkSize too large, "
223 "resetting to %" PRId64
, MaxChunkSize
);
224 FileStorageChunkSize
= MaxChunkSize
;
226 s_apc_file_storage
.enable(FileStoragePrefix
, FileStorageChunkSize
);
229 HHVM_RC_INT(APC_ITER_TYPE
, 0x1);
230 HHVM_RC_INT(APC_ITER_KEY
, 0x2);
231 HHVM_RC_INT(APC_ITER_FILENAME
, 0x4);
232 HHVM_RC_INT(APC_ITER_DEVICE
, 0x8);
233 HHVM_RC_INT(APC_ITER_INODE
, 0x10);
234 HHVM_RC_INT(APC_ITER_VALUE
, 0x20);
235 HHVM_RC_INT(APC_ITER_MD5
, 0x40);
236 HHVM_RC_INT(APC_ITER_NUM_HITS
, 0x80);
237 HHVM_RC_INT(APC_ITER_MTIME
, 0x100);
238 HHVM_RC_INT(APC_ITER_CTIME
, 0x200);
239 HHVM_RC_INT(APC_ITER_DTIME
, 0x400);
240 HHVM_RC_INT(APC_ITER_ATIME
, 0x800);
241 HHVM_RC_INT(APC_ITER_REFCOUNT
, 0x1000);
242 HHVM_RC_INT(APC_ITER_MEM_SIZE
, 0x2000);
243 HHVM_RC_INT(APC_ITER_TTL
, 0x4000);
244 HHVM_RC_INT(APC_ITER_NONE
, 0x0);
245 HHVM_RC_INT(APC_ITER_ALL
, 0xFFFFFFFFFF);
246 HHVM_RC_INT(APC_LIST_ACTIVE
, 1);
247 HHVM_RC_INT(APC_LIST_DELETED
, 2);
251 HHVM_FE(apc_store_as_primed_do_not_use
);
254 HHVM_FE(apc_clear_cache
);
259 HHVM_FE(apc_cache_info
);
260 HHVM_FE(apc_sma_info
);
264 void apcExtension::moduleShutdown() {
265 if (UseFileStorage
) {
266 s_apc_file_storage
.cleanup();
271 bool apcExtension::Enable
= true;
272 bool apcExtension::EnableConstLoad
= false;
273 bool apcExtension::ForceConstLoadToAPC
= true;
274 std::string
apcExtension::PrimeLibrary
;
275 int apcExtension::LoadThread
= 15;
276 std::set
<std::string
> apcExtension::CompletionKeys
;
277 apcExtension::TableTypes
apcExtension::TableType
=
278 TableTypes::ConcurrentTable
;
279 bool apcExtension::EnableApcSerialize
= true;
280 int64_t apcExtension::KeyMaturityThreshold
= 20;
281 int64_t apcExtension::MaximumCapacity
= 0;
282 int apcExtension::KeyFrequencyUpdatePeriod
= 1000;
283 bool apcExtension::ExpireOnSets
= false;
284 int apcExtension::PurgeFrequency
= 4096;
285 int apcExtension::PurgeRate
= -1;
286 bool apcExtension::AllowObj
= false;
287 int apcExtension::TTLLimit
= -1;
288 int64_t apcExtension::TTLMaxFinite
= std::numeric_limits
<int64_t>::max();
289 int apcExtension::HotSize
= 30000;
290 double apcExtension::HotLoadFactor
= 0.5;
291 std::vector
<std::string
> apcExtension::HotPrefix
;
292 bool apcExtension::HotKeyAllocLow
= false;
293 bool apcExtension::HotMapAllocLow
= false;
294 std::string
apcExtension::PrimeLibraryUpgradeDest
;
295 bool apcExtension::UseFileStorage
= false;
296 int64_t apcExtension::FileStorageChunkSize
= int64_t(1LL << 29);
297 std::string
apcExtension::FileStoragePrefix
= "/tmp/apc_store";
298 int apcExtension::FileStorageAdviseOutPeriod
= 1800;
299 std::string
apcExtension::FileStorageFlagKey
= "_madvise_out";
300 bool apcExtension::FileStorageKeepFileLinked
= false;
301 std::vector
<std::string
> apcExtension::NoTTLPrefix
;
303 bool apcExtension::UseUncounted
= true;
305 bool apcExtension::UseUncounted
= false;
307 bool apcExtension::ShareUncounted
= true;
308 bool apcExtension::Stat
= true;
309 // Different from zend default but matches what we've been returning for years
310 bool apcExtension::EnableCLI
= true;
312 static apcExtension s_apc_extension
;
314 Variant
HHVM_FUNCTION(apc_store
,
315 const Variant
& key_or_array
,
316 const Variant
& var
/* = null */,
317 int64_t ttl
/* = 0 */) {
318 if (!apcExtension::Enable
) return Variant(false);
320 if (key_or_array
.isArray()) {
321 Array valuesArr
= key_or_array
.toArray();
323 for (ArrayIter
iter(valuesArr
); iter
; ++iter
) {
324 Variant key
= iter
.first();
325 if (!key
.isString()) {
326 throw_invalid_argument("apc key: (not a string)");
327 return Variant(false);
329 Variant v
= iter
.second();
330 apc_store().set(key
.toString(), v
, ttl
);
333 return Variant(staticEmptyArray());
336 if (!key_or_array
.isString()) {
337 throw_invalid_argument("apc key: (not a string)");
338 return Variant(false);
340 String strKey
= key_or_array
.toString();
341 apc_store().set(strKey
, var
, ttl
);
342 return Variant(true);
346 * Stores the key in a similar fashion as "priming" would do (no TTL limit).
347 * Using this function is equivalent to adding your key to apc_prime.so.
349 bool HHVM_FUNCTION(apc_store_as_primed_do_not_use
,
351 const Variant
& var
) {
352 if (!apcExtension::Enable
) return false;
353 apc_store().setWithoutTTL(key
, var
);
357 Variant
HHVM_FUNCTION(apc_add
,
358 const Variant
& key_or_array
,
359 const Variant
& var
/* = null */,
360 int64_t ttl
/* = 0 */) {
361 if (!apcExtension::Enable
) return false;
363 if (key_or_array
.isArray()) {
364 Array valuesArr
= key_or_array
.toArray();
366 // errors stores all keys corresponding to entries that could not be cached
367 ArrayInit
errors(valuesArr
.size(), ArrayInit::Map
{});
369 for (ArrayIter
iter(valuesArr
); iter
; ++iter
) {
370 Variant key
= iter
.first();
371 if (!key
.isString()) {
372 throw_invalid_argument("apc key: (not a string)");
375 Variant v
= iter
.second();
376 if (!apc_store().add(key
.toString(), v
, ttl
)) {
380 return errors
.toVariant();
383 if (!key_or_array
.isString()) {
384 throw_invalid_argument("apc key: (not a string)");
387 String strKey
= key_or_array
.toString();
388 return apc_store().add(strKey
, var
, ttl
);
391 TypedValue
HHVM_FUNCTION(apc_fetch
, const Variant
& key
, VRefParam success
) {
392 if (!apcExtension::Enable
) return make_tv
<KindOfBoolean
>(false);
398 Array keys
= key
.toArray();
399 ArrayInit
init(keys
.size(), ArrayInit::Map
{});
400 for (ArrayIter
iter(keys
); iter
; ++iter
) {
401 Variant k
= iter
.second();
403 throw_invalid_argument("apc key: (not a string)");
404 return make_tv
<KindOfBoolean
>(false);
406 String strKey
= k
.toString();
407 if (apc_store().get(strKey
, v
)) {
412 success
.assignIfRef(tmp
);
413 return tvReturn(init
.toVariant());
416 if (apc_store().get(key
.toString(), v
)) {
417 success
.assignIfRef(true);
419 success
.assignIfRef(false);
422 return tvReturn(std::move(v
));
425 Variant
HHVM_FUNCTION(apc_delete
,
426 const Variant
& key
) {
427 if (!apcExtension::Enable
) return false;
430 Array keys
= key
.toArray();
431 PackedArrayInit
init(keys
.size());
432 for (ArrayIter
iter(keys
); iter
; ++iter
) {
433 Variant k
= iter
.second();
435 raise_warning("apc key is not a string");
437 } else if (!apc_store().eraseKey(k
.toCStrRef())) {
441 return init
.toVariant();
442 } else if(key
.is(KindOfObject
)) {
443 if (!key
.getObjectData()->getVMClass()->
444 classof(SystemLib::s_APCIteratorClass
)) {
446 "apc_delete(): apc_delete object argument must be instance"
452 SystemLib::s_APCIteratorClass
->lookupMethod(s_delete
.get());
453 return Variant::attach(
454 g_context
->invokeFuncFew(method
, key
.getObjectData())
458 return apc_store().eraseKey(key
.toString());
461 bool HHVM_FUNCTION(apc_clear_cache
, const String
& /*cache_type*/ /* = "" */) {
462 if (!apcExtension::Enable
) return false;
463 return apc_store().clear();
466 Variant
HHVM_FUNCTION(apc_inc
,
468 int64_t step
/* = 1 */,
469 VRefParam success
/* = null */) {
470 if (!apcExtension::Enable
) return false;
473 int64_t newValue
= apc_store().inc(key
, step
, found
);
474 success
.assignIfRef(found
);
475 if (!found
) return false;
479 Variant
HHVM_FUNCTION(apc_dec
,
481 int64_t step
/* = 1 */,
482 VRefParam success
/* = null */) {
483 if (!apcExtension::Enable
) return false;
486 int64_t newValue
= apc_store().inc(key
, -step
, found
);
487 success
.assignIfRef(found
);
488 if (!found
) return false;
492 bool HHVM_FUNCTION(apc_cas
,
496 if (!apcExtension::Enable
) return false;
497 return apc_store().cas(key
, old_cas
, new_cas
);
500 Variant
HHVM_FUNCTION(apc_exists
,
501 const Variant
& key
) {
502 if (!apcExtension::Enable
) return false;
505 Array keys
= key
.toArray();
506 PackedArrayInit
init(keys
.size());
507 for (ArrayIter
iter(keys
); iter
; ++iter
) {
508 Variant k
= iter
.second();
510 throw_invalid_argument("apc key: (not a string)");
513 String strKey
= k
.toString();
514 if (apc_store().exists(strKey
)) {
518 return init
.toVariant();
521 return apc_store().exists(key
.toString());
525 const StaticString
s_user("user");
526 const StaticString
s_start_time("start_time");
527 const StaticString
s_ttl("ttl");
528 const StaticString
s_cache_list("cache_list");
529 const StaticString
s_info("info");
530 const StaticString
s_in_memory("in_memory");
531 const StaticString
s_mem_size("mem_size");
532 const StaticString
s_type("type");
533 const StaticString
s_c_time("creation_time");
534 const StaticString
s_mtime("mtime");
536 // This is a guess to the size of the info array. It is significantly
537 // bigger than what we need but hard to control all the info that we
538 // may want to add here.
539 // Try to keep it such that we do not have to resize the array
540 const uint32_t kCacheInfoSize
= 40;
541 // Number of elements in the entry array
542 const int32_t kEntryInfoSize
= 7;
544 Variant
HHVM_FUNCTION(apc_cache_info
,
545 const String
& cache_type
,
546 bool limited
/* = false */) {
547 ArrayInit
info(kCacheInfoSize
, ArrayInit::Map
{});
548 info
.add(s_start_time
, start_time());
549 if (cache_type
.size() != 0 && !cache_type
.same(s_user
)) {
550 return info
.toArray();
553 info
.add(s_ttl
, apcExtension::TTLLimit
);
555 std::map
<const StringData
*, int64_t> stats
;
556 APCStats::getAPCStats().collectStats(stats
);
557 for (auto it
= stats
.begin(); it
!= stats
.end(); it
++) {
558 info
.add(Variant(it
->first
, Variant::PersistentStrInit
{}), it
->second
);
561 auto const entries
= apc_store().getEntriesInfo();
562 PackedArrayInit
ents(entries
.size());
563 for (auto& entry
: entries
) {
564 ArrayInit
ent(kEntryInfoSize
, ArrayInit::Map
{});
566 Variant::attach(StringData::Make(entry
.key
.c_str())));
567 ent
.add(s_in_memory
, entry
.inMem
);
568 ent
.add(s_ttl
, entry
.ttl
);
569 ent
.add(s_mem_size
, entry
.size
);
570 ent
.add(s_type
, static_cast<int64_t>(entry
.type
));
571 ent
.add(s_c_time
, entry
.c_time
);
572 ent
.add(s_mtime
, entry
.mtime
);
573 ents
.append(ent
.toArray());
575 info
.add(s_cache_list
, ents
.toArray(), false);
577 return info
.toArray();
580 Array
HHVM_FUNCTION(apc_sma_info
, bool /*limited*/ /* = false */) {
581 return empty_array();
584 ///////////////////////////////////////////////////////////////////////////////
585 // loading APC from archive files
587 typedef void(*PFUNC_APC_LOAD
)();
589 // Structure to hold cache meta data
590 // Same definition in ext_apc.cpp
596 static Mutex dl_mutex
;
597 static PFUNC_APC_LOAD
apc_load_func(void *handle
, const char *name
) {
599 throw Exception("apc_load_func is not currently supported under MSVC!");
601 PFUNC_APC_LOAD p
= (PFUNC_APC_LOAD
)dlsym(handle
, name
);
603 throw Exception("Unable to find %s in %s", name
,
604 apcExtension::PrimeLibrary
.c_str());
611 ApcLoadJob(void *handle
, int index
) : m_handle(handle
), m_index(index
) {}
612 void *m_handle
; int m_index
;
615 struct ApcLoadWorker
{
616 void onThreadEnter() {
617 g_context
.getCheck();
619 void doJob(std::shared_ptr
<ApcLoadJob
> job
) {
621 MemoryManager::SuppressOOM
so(*tl_heap
);
622 snprintf(func_name
, sizeof(func_name
), "_apc_load_%d", job
->m_index
);
623 apc_load_func(job
->m_handle
, func_name
)();
625 void onThreadExit() {
626 hphp_memory_cleanup();
630 static size_t s_const_map_size
= 0;
632 static SnapshotBuilder s_snapshotBuilder
;
634 void apc_load(int thread
) {
636 static void *handle
= nullptr;
638 apcExtension::PrimeLibrary
.empty() ||
639 !apcExtension::Enable
) {
642 BootStats::Block
timer("loading APC data");
643 if (apc_store().primeFromSnapshot(apcExtension::PrimeLibrary
.c_str())) {
646 Logger::Info("Fall back to shared object format");
647 handle
= dlopen(apcExtension::PrimeLibrary
.c_str(), RTLD_LAZY
);
649 throw Exception("Unable to open apc prime library %s: %s",
650 apcExtension::PrimeLibrary
.c_str(), dlerror());
653 auto upgradeDest
= apcExtension::PrimeLibraryUpgradeDest
;
654 if (!upgradeDest
.empty()) {
655 thread
= 1; // SnapshotBuilder is not (yet) thread-safe.
656 // TODO(9755792): Ensure APCFileStorage is enabled.
660 apc_load_func(handle
, "_apc_load_all")();
662 int count
= ((int(*)())apc_load_func(handle
, "_apc_load_count"))();
664 std::vector
<std::shared_ptr
<ApcLoadJob
>> jobs
;
666 for (int i
= 0; i
< count
; i
++) {
667 jobs
.push_back(std::make_shared
<ApcLoadJob
>(handle
, i
));
669 JobDispatcher
<ApcLoadJob
, ApcLoadWorker
>(std::move(jobs
), thread
).run();
672 apc_store().primeDone();
673 if (!upgradeDest
.empty()) {
674 BootStats::Block
block("SnapshotBuilder::writeToFile");
675 s_snapshotBuilder
.writeToFile(upgradeDest
);
678 // We've copied all the data out, so close it out.
683 void apc_advise_out() {
684 apc_store().adviseOut();
687 size_t get_const_map_size() {
688 return s_const_map_size
;
691 ///////////////////////////////////////////////////////////////////////////////
692 // Constant and APC priming (always with compressed data).
695 void const_load_impl_compressed(
696 struct cache_info
* /*info*/, int* /*int_lens*/, const char* /*int_keys*/,
697 long long* /*int_values*/, int* /*char_lens*/, const char* /*char_keys*/,
698 char* /*char_values*/, int* /*string_lens*/, const char* /*strings*/,
699 int* /*object_lens*/, const char* /*objects*/, int* /*thrift_lens*/,
700 const char* /*thrifts*/, int* /*other_lens*/, const char* /*others*/) {
701 // TODO(8117903): Unused; remove after updating www side.
705 void apc_load_impl_compressed
706 (struct cache_info
*info
,
707 int *int_lens
, const char *int_keys
, long long *int_values
,
708 int *char_lens
, const char *char_keys
, char *char_values
,
709 int *string_lens
, const char *strings
,
710 int *object_lens
, const char *objects
,
711 int *thrift_lens
, const char *thrifts
,
712 int *other_lens
, const char *others
) {
713 bool readOnly
= apcExtension::EnableConstLoad
&& info
&& info
->use_const
;
714 if (readOnly
&& info
->a_name
) Logger::FInfo("const archive {}", info
->a_name
);
715 auto& s
= apc_store();
716 SnapshotBuilder
* snap
= apcExtension::PrimeLibraryUpgradeDest
.empty() ?
717 nullptr : &s_snapshotBuilder
;
719 int count
= int_lens
[0];
720 int len
= int_lens
[1];
722 std::vector
<KeyValuePair
> vars(count
);
723 char *keys
= gzdecode(int_keys
, len
);
724 if (keys
== nullptr) throw Exception("bad compressed apc archive.");
725 ScopedMem
holder(keys
);
726 const char *k
= keys
;
727 long long* v
= int_values
;
728 for (int i
= 0; i
< count
; i
++) {
729 auto& item
= vars
[i
];
731 item
.readOnly
= readOnly
;
732 s
.constructPrime(*v
++, item
);
733 if (UNLIKELY(snap
!= nullptr)) snap
->addInt(v
[-1], item
);
734 k
+= int_lens
[i
+ 2] + 1; // skip \0
736 s
.prime(std::move(vars
));
737 assertx((k
- keys
) == len
);
741 int count
= char_lens
[0];
742 int len
= char_lens
[1];
744 std::vector
<KeyValuePair
> vars(count
);
745 char *keys
= gzdecode(char_keys
, len
);
746 if (keys
== nullptr) throw Exception("bad compressed apc archive.");
747 ScopedMem
holder(keys
);
748 const char *k
= keys
;
749 char *v
= char_values
;
750 for (int i
= 0; i
< count
; i
++) {
751 auto& item
= vars
[i
];
753 item
.readOnly
= readOnly
;
756 s
.constructPrime(false, item
);
757 if (UNLIKELY(snap
!= nullptr)) snap
->addFalse(item
);
760 s
.constructPrime(true, item
);
761 if (UNLIKELY(snap
!= nullptr)) snap
->addTrue(item
);
764 s
.constructPrime(uninit_null(), item
);
765 if (UNLIKELY(snap
!= nullptr)) snap
->addNull(item
);
768 throw Exception("bad apc archive, unknown char type");
770 k
+= char_lens
[i
+ 2] + 1; // skip \0
772 s
.prime(std::move(vars
));
773 assertx((k
- keys
) == len
);
777 int count
= string_lens
[0] / 2;
778 int len
= string_lens
[1];
780 std::vector
<KeyValuePair
> vars(count
);
781 char *decoded
= gzdecode(strings
, len
);
782 if (decoded
== nullptr) throw Exception("bad compressed apc archive.");
783 ScopedMem
holder(decoded
);
784 const char *p
= decoded
;
785 for (int i
= 0; i
< count
; i
++) {
786 auto& item
= vars
[i
];
788 item
.readOnly
= readOnly
;
789 p
+= string_lens
[i
+ i
+ 2] + 1; // skip \0
790 // Strings would be copied into APC anyway.
791 String
value(p
, string_lens
[i
+ i
+ 3], CopyString
);
792 // todo: t2539893: check if value is already a static string
793 s
.constructPrime(value
, item
, false);
794 if (UNLIKELY(snap
!= nullptr)) snap
->addString(value
, item
);
795 p
+= string_lens
[i
+ i
+ 3] + 1; // skip \0
797 s
.prime(std::move(vars
));
798 assertx((p
- decoded
) == len
);
802 int count
= object_lens
[0] / 2;
803 int len
= object_lens
[1];
805 std::vector
<KeyValuePair
> vars(count
);
806 char *decoded
= gzdecode(objects
, len
);
807 if (decoded
== nullptr) throw Exception("bad compressed APC archive.");
808 ScopedMem
holder(decoded
);
809 const char *p
= decoded
;
810 for (int i
= 0; i
< count
; i
++) {
811 auto& item
= vars
[i
];
813 item
.readOnly
= readOnly
;
814 p
+= object_lens
[i
+ i
+ 2] + 1; // skip \0
815 String
value(p
, object_lens
[i
+ i
+ 3], CopyString
);
816 s
.constructPrime(value
, item
, true);
817 if (UNLIKELY(snap
!= nullptr)) snap
->addObject(value
, item
);
818 p
+= object_lens
[i
+ i
+ 3] + 1; // skip \0
820 s
.prime(std::move(vars
));
821 assertx((p
- decoded
) == len
);
825 int count
= thrift_lens
[0] / 2;
826 int len
= thrift_lens
[1];
828 std::vector
<KeyValuePair
> vars(count
);
829 char *decoded
= gzdecode(thrifts
, len
);
830 if (decoded
== nullptr) throw Exception("bad compressed apc archive.");
831 ScopedMem
holder(decoded
);
832 const char *p
= decoded
;
833 for (int i
= 0; i
< count
; i
++) {
834 auto& item
= vars
[i
];
836 item
.readOnly
= readOnly
;
837 p
+= thrift_lens
[i
+ i
+ 2] + 1; // skip \0
838 String
value(p
, thrift_lens
[i
+ i
+ 3], CopyString
);
840 Variant v
= HHVM_FN(fb_unserialize
)(value
, ref(success
));
841 if (same(success
, false)) {
842 throw Exception("bad apc archive, fb_unserialize failed");
844 s
.constructPrime(v
, item
);
845 if (UNLIKELY(snap
!= nullptr)) snap
->addThrift(value
, item
);
846 p
+= thrift_lens
[i
+ i
+ 3] + 1; // skip \0
848 s
.prime(std::move(vars
));
849 assertx((p
- decoded
) == len
);
853 int count
= other_lens
[0] / 2;
854 int len
= other_lens
[1];
856 std::vector
<KeyValuePair
> vars(count
);
857 char *decoded
= gzdecode(others
, len
);
858 if (decoded
== nullptr) throw Exception("bad compressed apc archive.");
859 ScopedMem
holder(decoded
);
860 const char *p
= decoded
;
861 for (int i
= 0; i
< count
; i
++) {
862 auto& item
= vars
[i
];
864 item
.readOnly
= readOnly
;
865 p
+= other_lens
[i
+ i
+ 2] + 1; // skip \0
866 String
value(p
, other_lens
[i
+ i
+ 3], CopyString
);
868 unserialize_from_string(value
, VariableUnserializer::Type::Internal
);
869 if (same(v
, false)) {
870 // we can't possibly get here if it was a boolean "false" that's
871 // supposed to be serialized as a char
872 throw Exception("bad apc archive, unserialize_from_string failed");
874 s
.constructPrime(v
, item
);
875 if (UNLIKELY(snap
!= nullptr)) snap
->addOther(value
, item
);
876 p
+= other_lens
[i
+ i
+ 3] + 1; // skip \0
878 s
.prime(std::move(vars
));
879 assertx((p
- decoded
) == len
);
884 ///////////////////////////////////////////////////////////////////////////////
886 static double my_time() {
889 gettimeofday(&a
, nullptr);
890 t
= a
.tv_sec
+ (a
.tv_usec
/1000000.00);
896 s_current("current"),
897 s_filename("filename"),
900 s_temp_filename("temp_filename"),
901 s_cancel_upload("cancel_upload"),
904 #define RFC1867_TRACKING_KEY_MAXLEN 63
905 #define RFC1867_NAME_MAXLEN 63
906 #define RFC1867_FILENAME_MAXLEN 127
908 int apc_rfc1867_progress(apc_rfc1867_data
* rfc1867ApcData
, unsigned int event
,
909 void* event_data
, void** /*extra*/) {
911 case MULTIPART_EVENT_START
: {
912 multipart_event_start
*data
= (multipart_event_start
*) event_data
;
913 rfc1867ApcData
->content_length
= data
->content_length
;
914 rfc1867ApcData
->tracking_key
.clear();
915 rfc1867ApcData
->name
.clear();
916 rfc1867ApcData
->cancel_upload
= 0;
917 rfc1867ApcData
->temp_filename
= "";
918 rfc1867ApcData
->start_time
= my_time();
919 rfc1867ApcData
->bytes_processed
= 0;
920 rfc1867ApcData
->prev_bytes_processed
= 0;
921 rfc1867ApcData
->rate
= 0;
922 rfc1867ApcData
->update_freq
= RuntimeOption::Rfc1867Freq
;
924 if (rfc1867ApcData
->update_freq
< 0) {
925 assertx(false); // TODO: support percentage
926 // frequency is a percentage, not bytes
927 rfc1867ApcData
->update_freq
=
928 rfc1867ApcData
->content_length
* RuntimeOption::Rfc1867Freq
/ 100;
933 case MULTIPART_EVENT_FORMDATA
: {
934 multipart_event_formdata
*data
= (multipart_event_formdata
*)event_data
;
936 !strncasecmp(data
->name
, RuntimeOption::Rfc1867Name
.c_str(),
937 RuntimeOption::Rfc1867Name
.size()) &&
938 data
->value
&& data
->length
&&
939 data
->length
< RFC1867_TRACKING_KEY_MAXLEN
-
940 RuntimeOption::Rfc1867Prefix
.size()) {
941 int len
= RuntimeOption::Rfc1867Prefix
.size();
942 if (len
> RFC1867_TRACKING_KEY_MAXLEN
) {
943 len
= RFC1867_TRACKING_KEY_MAXLEN
;
945 rfc1867ApcData
->tracking_key
=
946 std::string(RuntimeOption::Rfc1867Prefix
.c_str(), len
);
947 len
= strlen(*data
->value
);
948 int rem
= RFC1867_TRACKING_KEY_MAXLEN
-
949 rfc1867ApcData
->tracking_key
.size();
950 if (len
> rem
) len
= rem
;
951 rfc1867ApcData
->tracking_key
+=
952 std::string(*data
->value
, len
);
953 rfc1867ApcData
->bytes_processed
= data
->post_bytes_processed
;
955 /* Facebook: Temporary fix for a bug in PHP's rfc1867 code,
956 fixed here for convenience:
957 http://cvs.php.net/viewvc.cgi/php-src/main/
958 rfc1867.c?r1=1.173.2.1.2.11&r2=1.173.2.1.2.12 */
959 (*data
->newlength
) = data
->length
;
963 case MULTIPART_EVENT_FILE_START
:
964 if (!rfc1867ApcData
->tracking_key
.empty()) {
965 multipart_event_file_start
*data
=
966 (multipart_event_file_start
*)event_data
;
968 rfc1867ApcData
->bytes_processed
= data
->post_bytes_processed
;
969 int len
= strlen(*data
->filename
);
970 if (len
> RFC1867_FILENAME_MAXLEN
) len
= RFC1867_FILENAME_MAXLEN
;
971 rfc1867ApcData
->filename
= std::string(*data
->filename
, len
);
972 rfc1867ApcData
->temp_filename
= "";
973 len
= strlen(data
->name
);
974 if (len
> RFC1867_NAME_MAXLEN
) len
= RFC1867_NAME_MAXLEN
;
975 rfc1867ApcData
->name
= std::string(data
->name
, len
);
976 ArrayInit
track(6, ArrayInit::Map
{});
977 track
.set(s_total
, rfc1867ApcData
->content_length
);
978 track
.set(s_current
, rfc1867ApcData
->bytes_processed
);
979 track
.set(s_filename
, rfc1867ApcData
->filename
);
980 track
.set(s_name
, rfc1867ApcData
->name
);
981 track
.set(s_done
, 0);
982 track
.set(s_start_time
, rfc1867ApcData
->start_time
);
983 HHVM_FN(apc_store
)(rfc1867ApcData
->tracking_key
, track
.toVariant(), 3600);
987 case MULTIPART_EVENT_FILE_DATA
:
988 if (!rfc1867ApcData
->tracking_key
.empty()) {
989 multipart_event_file_data
*data
=
990 (multipart_event_file_data
*) event_data
;
991 rfc1867ApcData
->bytes_processed
= data
->post_bytes_processed
;
992 if (rfc1867ApcData
->bytes_processed
-
993 rfc1867ApcData
->prev_bytes_processed
>
994 rfc1867ApcData
->update_freq
) {
996 if (apc_store().get(rfc1867ApcData
->tracking_key
, v
)) {
998 ArrayInit
track(6, ArrayInit::Map
{});
999 track
.set(s_total
, rfc1867ApcData
->content_length
);
1000 track
.set(s_current
, rfc1867ApcData
->bytes_processed
);
1001 track
.set(s_filename
, rfc1867ApcData
->filename
);
1002 track
.set(s_name
, rfc1867ApcData
->name
);
1003 track
.set(s_done
, 0);
1004 track
.set(s_start_time
, rfc1867ApcData
->start_time
);
1005 HHVM_FN(apc_store
)(rfc1867ApcData
->tracking_key
, track
.toVariant(),
1008 rfc1867ApcData
->prev_bytes_processed
=
1009 rfc1867ApcData
->bytes_processed
;
1015 case MULTIPART_EVENT_FILE_END
:
1016 if (!rfc1867ApcData
->tracking_key
.empty()) {
1017 multipart_event_file_end
*data
=
1018 (multipart_event_file_end
*)event_data
;
1019 rfc1867ApcData
->bytes_processed
= data
->post_bytes_processed
;
1020 rfc1867ApcData
->cancel_upload
= data
->cancel_upload
;
1021 rfc1867ApcData
->temp_filename
= data
->temp_filename
;
1022 ArrayInit
track(8, ArrayInit::Map
{});
1023 track
.set(s_total
, rfc1867ApcData
->content_length
);
1024 track
.set(s_current
, rfc1867ApcData
->bytes_processed
);
1025 track
.set(s_filename
, rfc1867ApcData
->filename
);
1026 track
.set(s_name
, rfc1867ApcData
->name
);
1027 track
.set(s_temp_filename
, rfc1867ApcData
->temp_filename
);
1028 track
.set(s_cancel_upload
, rfc1867ApcData
->cancel_upload
);
1029 track
.set(s_done
, 0);
1030 track
.set(s_start_time
, rfc1867ApcData
->start_time
);
1031 HHVM_FN(apc_store
)(rfc1867ApcData
->tracking_key
, track
.toVariant(), 3600);
1035 case MULTIPART_EVENT_END
:
1036 if (!rfc1867ApcData
->tracking_key
.empty()) {
1037 double now
= my_time();
1038 multipart_event_end
*data
= (multipart_event_end
*)event_data
;
1039 rfc1867ApcData
->bytes_processed
= data
->post_bytes_processed
;
1040 if (now
>rfc1867ApcData
->start_time
) {
1041 rfc1867ApcData
->rate
=
1042 8.0*rfc1867ApcData
->bytes_processed
/(now
-rfc1867ApcData
->start_time
);
1044 rfc1867ApcData
->rate
=
1045 8.0*rfc1867ApcData
->bytes_processed
; /* Too quick */
1046 ArrayInit
track(8, ArrayInit::Map
{});
1047 track
.set(s_total
, rfc1867ApcData
->content_length
);
1048 track
.set(s_current
, rfc1867ApcData
->bytes_processed
);
1049 track
.set(s_rate
, rfc1867ApcData
->rate
);
1050 track
.set(s_filename
, rfc1867ApcData
->filename
);
1051 track
.set(s_name
, rfc1867ApcData
->name
);
1052 track
.set(s_cancel_upload
, rfc1867ApcData
->cancel_upload
);
1053 track
.set(s_done
, 1);
1054 track
.set(s_start_time
, rfc1867ApcData
->start_time
);
1055 HHVM_FN(apc_store
)(rfc1867ApcData
->tracking_key
, track
.toVariant(),
1064 ///////////////////////////////////////////////////////////////////////////////
1065 // apc serialization
1067 String
apc_serialize(const Variant
& value
) {
1068 VariableSerializer::Type sType
=
1069 apcExtension::EnableApcSerialize
?
1070 VariableSerializer::Type::APCSerialize
:
1071 VariableSerializer::Type::Internal
;
1072 VariableSerializer
vs(sType
);
1073 return vs
.serialize(value
, true);
1076 Variant
apc_unserialize(const char* data
, int len
) {
1077 VariableUnserializer::Type sType
=
1078 apcExtension::EnableApcSerialize
?
1079 VariableUnserializer::Type::APCSerialize
:
1080 VariableUnserializer::Type::Internal
;
1081 return unserialize_ex(data
, len
, sType
);
1084 String
apc_reserialize(const String
& str
) {
1086 !apcExtension::EnableApcSerialize
) return str
;
1088 VariableUnserializer
uns(str
.data(), str
.size(),
1089 VariableUnserializer::Type::APCSerialize
);
1091 uns
.reserialize(buf
);
1093 return buf
.detach();
1096 ///////////////////////////////////////////////////////////////////////////////
1097 // debugging support
1099 bool apc_dump(const char *filename
, bool keyOnly
, bool metaDump
) {
1101 std::ofstream
out(filename
);
1103 // only one of these should ever be specified
1104 if (keyOnly
&& metaDump
) {
1113 mode
= DumpMode::KeyOnly
;
1114 } else if (metaDump
) {
1115 mode
= DumpMode::KeyAndMeta
;
1117 mode
= DumpMode::KeyAndValue
;
1120 apc_store().dump(out
, mode
);
1125 bool apc_get_random_entries(std::ostream
&out
, uint32_t count
) {
1126 apc_store().dumpRandomKeys(out
, count
);
1130 ///////////////////////////////////////////////////////////////////////////////