2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/runtime/base/shared-store-base.h"
18 #include "hphp/runtime/base/complex_types.h"
19 #include "hphp/runtime/base/runtime-option.h"
20 #include "hphp/runtime/base/type-conversions.h"
21 #include "hphp/runtime/base/builtin_functions.h"
22 #include "hphp/runtime/server/server_stats.h"
23 #include "hphp/runtime/base/concurrent_shared_store.h"
24 #include "hphp/runtime/ext/ext_apc.h"
25 #include "hphp/util/timer.h"
26 #include "hphp/util/logger.h"
29 #if !defined(HAVE_POSIX_FALLOCATE) && \
30 (_XOPEN_SOURCE >= 600 || _POSIX_C_SOURCE >= 200112L)
31 # define HAVE_POSIX_FALLOCATE 1
36 ///////////////////////////////////////////////////////////////////////////////
39 SharedStore::SharedStore(int id
) : m_id(id
) {
42 SharedStore::~SharedStore() {
45 std::string
SharedStore::GetSkeleton(CStrRef key
) {
47 const char *p
= key
.data();
48 ret
.reserve(key
.size());
49 bool added
= false; // whether consecutive numbers are replaced by # yet
50 for (int i
= 0; i
< key
.size(); i
++) {
52 if (ch
>= '0' && ch
<= '9') {
65 bool SharedStore::erase(CStrRef key
, bool expired
/* = false */) {
66 bool success
= eraseImpl(key
, expired
);
68 if (RuntimeOption::EnableStats
&& RuntimeOption::EnableAPCStats
) {
69 ServerStats::Log(success
? "apc.erased" : "apc.erase", 1);
74 void StoreValue::set(SharedVariant
*v
, int64_t ttl
) {
76 expiry
= ttl
? time(nullptr) + ttl
: 0;
78 bool StoreValue::expired() const {
79 return expiry
&& time(nullptr) >= expiry
;
82 ///////////////////////////////////////////////////////////////////////////////
85 SharedStores s_apc_store
;
87 SharedStores::SharedStores() {
90 void SharedStores::create() {
91 for (int i
= 0; i
< MAX_SHARED_STORE
; i
++) {
92 switch (apcExtension::TableType
) {
93 case apcExtension::TableTypes::ConcurrentTable
:
94 m_stores
[i
] = new ConcurrentTableSharedStore(i
);
102 SharedStores::~SharedStores() {
106 void SharedStores::clear() {
107 for (int i
= 0; i
< MAX_SHARED_STORE
; i
++) {
109 m_stores
[i
] = nullptr;
113 void SharedStores::reset() {
118 void SharedStores::Create() {
119 s_apc_store
.create();
122 ///////////////////////////////////////////////////////////////////////////////
124 SharedStoreFileStorage s_apc_file_storage
;
126 void SharedStoreFileStorage::enable(const std::string
& prefix
,
127 int64_t chunkSize
, int64_t maxSize
) {
130 m_chunkSize
= chunkSize
;
132 if (m_state
!= StorageState::Invalid
) {
136 Logger::Error("Failed to open file for apc, fallback to in-memory mode");
139 m_state
= StorageState::Open
;
142 char *SharedStoreFileStorage::put(const char *data
, int32_t len
) {
144 if (m_state
!= StorageState::Open
||
145 len
+ PaddingSize
> m_chunkSize
- PaddingSize
) {
148 if (len
+ PaddingSize
> m_chunkRemain
&& !addFile()) {
149 m_state
= StorageState::Full
;
153 assert(len
+ PaddingSize
<= m_chunkRemain
);
154 strhash_t h
= hash_string_inline(data
, len
);
155 *(strhash_t
*)m_current
= h
;
156 m_current
+= sizeof(h
);
157 *(int32_t*)m_current
= len
;
158 m_current
+= sizeof(len
);
159 // should be no overlap
160 memcpy(m_current
, data
, len
);
161 char *addr
= m_current
;
163 m_current
+= len
+ sizeof(char);
164 *(strhash_t
*)m_current
= TombHash
;
165 m_chunkRemain
-= len
+ PaddingSize
;
169 void SharedStoreFileStorage::seal() {
171 if (m_state
== StorageState::Sealed
) {
174 assert(m_state
== StorageState::Open
|| m_state
== StorageState::Full
);
177 m_state
= StorageState::Sealed
;
179 for (int i
= 0; i
< (int)m_chunks
.size(); i
++) {
180 if (mprotect(m_chunks
[i
], m_chunkSize
, PROT_READ
) < 0) {
181 Logger::Error("Failed to mprotect chunk %d", i
);
186 void SharedStoreFileStorage::adviseOut() {
188 Timer
timer(Timer::WallTime
, "advising out apc prime");
189 for (int i
= 0; i
< (int)m_chunks
.size(); i
++) {
190 if (madvise(m_chunks
[i
], m_chunkSize
, MADV_DONTNEED
) < 0) {
191 Logger::Error("Failed to madvise chunk %d", i
);
196 bool SharedStoreFileStorage::hashCheck() {
198 for (int i
= 0; i
< (int)m_chunks
.size(); i
++) {
199 char *current
= (char*)m_chunks
[i
];
200 char *boundary
= (char*)m_chunks
[i
] + m_chunkSize
;
202 strhash_t h
= *(strhash_t
*)current
;
206 current
+= sizeof(h
);
207 int32_t len
= *(int32_t*)current
;
208 current
+= sizeof(len
);
210 len
+ PaddingSize
>= (int64_t)boundary
- (int64_t)current
) {
211 Logger::Error("invalid len %d at chunk %d offset %" PRId64
, len
, i
,
212 (int64_t)current
- (int64_t)m_chunks
[i
]);
215 strhash_t h_data
= hash_string_inline(current
, len
);
217 Logger::Error("invalid hash at chunk %d offset %" PRId64
, i
,
218 (int64_t)current
- (int64_t)m_chunks
[i
]);
222 if (*current
!= '\0') {
223 Logger::Error("missing \\0 at chunk %d offset %" PRId64
, i
,
224 (int64_t)current
- (int64_t)m_chunks
[i
]);
233 void SharedStoreFileStorage::cleanup() {
235 for (unsigned int i
= 0 ; i
< m_fileNames
.size(); i
++) {
236 unlink(m_fileNames
[i
].c_str());
241 bool SharedStoreFileStorage::addFile() {
242 if ((int64_t)m_chunks
.size() * m_chunkSize
>= m_maxSize
) {
243 m_state
= StorageState::Full
;
247 snprintf(name
, sizeof(name
), "%s.XXXXXX", m_prefix
.c_str());
248 int fd
= mkstemp(name
);
250 Logger::Error("Failed to open temp file");
253 bool couldAllocate
= false;
254 #if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE
255 couldAllocate
= posix_fallocate(fd
, 0, m_chunkSize
) == 0;
256 #elif defined(__APPLE__)
257 fstore_t store
= { F_ALLOCATECONTIG
, F_PEOFPOSMODE
, 0, m_chunkSize
};
258 int ret
= fcntl(fd
, F_PREALLOCATE
, &store
);
260 store
.fst_flags
= F_ALLOCATEALL
;
261 ret
= fcntl(fd
, F_PREALLOCATE
, &store
);
263 couldAllocate
= false;
266 couldAllocate
= ftruncate(fd
, m_chunkSize
) == 0;
268 #error "No implementation for posix_fallocate on your platform."
270 if (!couldAllocate
) {
271 Logger::Error("Failed to posix_fallocate of size %" PRId64
, m_chunkSize
);
275 if (apcExtension::FileStorageKeepFileLinked
) {
276 m_fileNames
.push_back(std::string(name
));
280 char *addr
= (char *)mmap(nullptr, m_chunkSize
, PROT_READ
| PROT_WRITE
,
282 if (addr
== (char *)-1) {
283 Logger::Error("Failed to mmap %s of size %" PRId64
, name
, m_chunkSize
);
288 m_chunkRemain
= m_chunkSize
- PaddingSize
;
289 m_chunks
.push_back(addr
);
294 ///////////////////////////////////////////////////////////////////////////////