Rename files in runtime/base, part 6
[hiphop-php.git] / hphp / runtime / base / shared-store-base.cpp
blob932874fa0cd1145f89e9cb59da7cc3395e9f4eac
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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"
27 #include <sys/mman.h>
29 #if !defined(HAVE_POSIX_FALLOCATE) && \
30 (_XOPEN_SOURCE >= 600 || _POSIX_C_SOURCE >= 200112L)
31 # define HAVE_POSIX_FALLOCATE 1
32 #endif
34 namespace HPHP {
36 ///////////////////////////////////////////////////////////////////////////////
37 // SharedStore
39 SharedStore::SharedStore(int id) : m_id(id) {
42 SharedStore::~SharedStore() {
45 std::string SharedStore::GetSkeleton(CStrRef key) {
46 std::string ret;
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++) {
51 char ch = *p++;
52 if (ch >= '0' && ch <= '9') {
53 if (!added) {
54 ret += '#';
55 added = true;
57 } else {
58 added = false;
59 ret += ch;
62 return ret;
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);
71 return success;
74 void StoreValue::set(SharedVariant *v, int64_t ttl) {
75 var = v;
76 expiry = ttl ? time(nullptr) + ttl : 0;
78 bool StoreValue::expired() const {
79 return expiry && time(nullptr) >= expiry;
82 ///////////////////////////////////////////////////////////////////////////////
83 // SharedStores
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);
95 break;
96 default:
97 assert(false);
102 SharedStores::~SharedStores() {
103 clear();
106 void SharedStores::clear() {
107 for (int i = 0; i < MAX_SHARED_STORE; i++) {
108 delete m_stores[i];
109 m_stores[i] = nullptr;
113 void SharedStores::reset() {
114 clear();
115 create();
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) {
128 Lock lock(m_lock);
129 m_prefix = prefix;
130 m_chunkSize = chunkSize;
131 m_maxSize = maxSize;
132 if (m_state != StorageState::Invalid) {
133 return;
135 if (!addFile()) {
136 Logger::Error("Failed to open file for apc, fallback to in-memory mode");
137 return;
139 m_state = StorageState::Open;
142 char *SharedStoreFileStorage::put(const char *data, int32_t len) {
143 Lock lock(m_lock);
144 if (m_state != StorageState::Open ||
145 len + PaddingSize > m_chunkSize - PaddingSize) {
146 return nullptr;
148 if (len + PaddingSize > m_chunkRemain && !addFile()) {
149 m_state = StorageState::Full;
150 return nullptr;
152 assert(m_current);
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;
162 addr[len] = '\0';
163 m_current += len + sizeof(char);
164 *(strhash_t*)m_current = TombHash;
165 m_chunkRemain -= len + PaddingSize;
166 return addr;
169 void SharedStoreFileStorage::seal() {
170 Lock lock(m_lock);
171 if (m_state == StorageState::Sealed) {
172 return;
174 assert(m_state == StorageState::Open || m_state == StorageState::Full);
175 m_current = nullptr;
176 m_chunkRemain = 0;
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() {
187 Lock lock(m_lock);
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() {
197 Lock lock(m_lock);
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;
201 while (1) {
202 strhash_t h = *(strhash_t*)current;
203 if (h == TombHash) {
204 break;
206 current += sizeof(h);
207 int32_t len = *(int32_t*)current;
208 current += sizeof(len);
209 if (len < 0 ||
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]);
213 return false;
215 strhash_t h_data = hash_string_inline(current, len);
216 if (h_data != h) {
217 Logger::Error("invalid hash at chunk %d offset %" PRId64, i,
218 (int64_t)current - (int64_t)m_chunks[i]);
219 return false;
221 current += len;
222 if (*current != '\0') {
223 Logger::Error("missing \\0 at chunk %d offset %" PRId64, i,
224 (int64_t)current - (int64_t)m_chunks[i]);
225 return false;
227 current++;
230 return true;
233 void SharedStoreFileStorage::cleanup() {
234 Lock lock(m_lock);
235 for (unsigned int i = 0 ; i < m_fileNames.size(); i++) {
236 unlink(m_fileNames[i].c_str());
238 m_chunks.clear();
241 bool SharedStoreFileStorage::addFile() {
242 if ((int64_t)m_chunks.size() * m_chunkSize >= m_maxSize) {
243 m_state = StorageState::Full;
244 return false;
246 char name[PATH_MAX];
247 snprintf(name, sizeof(name), "%s.XXXXXX", m_prefix.c_str());
248 int fd = mkstemp(name);
249 if (fd < 0) {
250 Logger::Error("Failed to open temp file");
251 return false;
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);
259 if(ret == -1) {
260 store.fst_flags = F_ALLOCATEALL;
261 ret = fcntl(fd, F_PREALLOCATE, &store);
262 if (ret == -1) {
263 couldAllocate = false;
266 couldAllocate = ftruncate(fd, m_chunkSize) == 0;
267 #else
268 #error "No implementation for posix_fallocate on your platform."
269 #endif
270 if (!couldAllocate) {
271 Logger::Error("Failed to posix_fallocate of size %" PRId64, m_chunkSize);
272 close(fd);
273 return false;
275 if (apcExtension::FileStorageKeepFileLinked) {
276 m_fileNames.push_back(std::string(name));
277 } else {
278 unlink(name);
280 char *addr = (char *)mmap(nullptr, m_chunkSize, PROT_READ | PROT_WRITE,
281 MAP_SHARED, fd, 0);
282 if (addr == (char *)-1) {
283 Logger::Error("Failed to mmap %s of size %" PRId64, name, m_chunkSize);
284 close(fd);
285 return false;
287 m_current = addr;
288 m_chunkRemain = m_chunkSize - PaddingSize;
289 m_chunks.push_back(addr);
290 close(fd);
291 return true;
294 ///////////////////////////////////////////////////////////////////////////////