CRC32 hash function for strings
[hiphop-php.git] / hphp / runtime / base / apc-file-storage.cpp
blobc093acf0569fd871f54105a1fc54d269a0ac163a
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 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 +----------------------------------------------------------------------+
16 #include "hphp/runtime/base/apc-file-storage.h"
18 #include <sys/mman.h>
20 #include "hphp/util/alloc.h"
21 #include "hphp/util/timer.h"
22 #include "hphp/util/logger.h"
24 #include "hphp/runtime/base/complex-types.h"
25 #include "hphp/runtime/base/runtime-option.h"
26 #include "hphp/runtime/base/type-conversions.h"
27 #include "hphp/runtime/base/builtin-functions.h"
28 #include "hphp/runtime/server/server-stats.h"
29 #include "hphp/runtime/base/apc-stats.h"
30 #include "hphp/runtime/ext/apc/ext_apc.h"
32 #if !defined(HAVE_POSIX_FALLOCATE) && \
33 (_XOPEN_SOURCE >= 600 || _POSIX_C_SOURCE >= 200112L || defined(__CYGWIN__))
34 # define HAVE_POSIX_FALLOCATE 1
35 #endif
37 namespace HPHP {
39 //////////////////////////////////////////////////////////////////////
41 APCFileStorage s_apc_file_storage;
43 void APCFileStorage::enable(const std::string& prefix,
44 int64_t chunkSize, int64_t maxSize) {
45 Lock lock(m_lock);
46 m_prefix = prefix;
47 m_chunkSize = chunkSize;
48 m_maxSize = maxSize;
49 if (m_state != StorageState::Invalid) {
50 return;
52 if (!addFile()) {
53 Logger::Error("Failed to open file for apc, fallback to in-memory mode");
54 return;
56 m_state = StorageState::Open;
59 char *APCFileStorage::put(const char *data, int32_t len) {
60 Lock lock(m_lock);
61 if (m_state != StorageState::Open ||
62 len + PaddingSize > m_chunkSize - PaddingSize) {
63 return nullptr;
65 if (len + PaddingSize > m_chunkRemain && !addFile()) {
66 m_state = StorageState::Full;
67 return nullptr;
69 assert(m_current);
70 assert(len + PaddingSize <= m_chunkRemain);
71 strhash_t h = hash_string_unsafe(data, len);
72 *(strhash_t*)m_current = h;
73 m_current += sizeof(h);
74 *(int32_t*)m_current = len;
75 m_current += sizeof(len);
76 // should be no overlap
77 memcpy(m_current, data, len);
78 char *addr = m_current;
79 addr[len] = '\0';
80 m_current += len + sizeof(char);
81 *(strhash_t*)m_current = TombHash;
82 m_chunkRemain -= len + PaddingSize;
83 return addr;
86 void APCFileStorage::seal() {
87 Lock lock(m_lock);
88 if (m_state == StorageState::Sealed) {
89 return;
91 assert(m_state == StorageState::Open || m_state == StorageState::Full);
92 m_current = nullptr;
93 m_chunkRemain = 0;
94 m_state = StorageState::Sealed;
96 for (int i = 0; i < (int)m_chunks.size(); i++) {
97 if (mprotect(m_chunks[i], m_chunkSize, PROT_READ) < 0) {
98 Logger::Error("Failed to mprotect chunk %d", i);
103 void APCFileStorage::adviseOut() {
104 Lock lock(m_lock);
105 Timer timer(Timer::WallTime, "advising out apc prime");
106 for (int i = 0; i < (int)m_chunks.size(); i++) {
107 if (madvise(m_chunks[i], m_chunkSize, MADV_DONTNEED) < 0) {
108 Logger::Error("Failed to madvise chunk %d", i);
113 bool APCFileStorage::hashCheck() {
114 Lock lock(m_lock);
115 for (int i = 0; i < (int)m_chunks.size(); i++) {
116 char *current = (char*)m_chunks[i];
117 char *boundary = (char*)m_chunks[i] + m_chunkSize;
118 while (1) {
119 strhash_t h = *(strhash_t*)current;
120 if (h == TombHash) {
121 break;
123 current += sizeof(h);
124 int32_t len = *(int32_t*)current;
125 current += sizeof(len);
126 if (len < 0 ||
127 len + PaddingSize >= (int64_t)boundary - (int64_t)current) {
128 Logger::Error("invalid len %d at chunk %d offset %" PRId64, len, i,
129 (int64_t)current - (int64_t)m_chunks[i]);
130 return false;
132 strhash_t h_data = hash_string(current, len);
133 if (h_data != h) {
134 Logger::Error("invalid hash at chunk %d offset %" PRId64, i,
135 (int64_t)current - (int64_t)m_chunks[i]);
136 return false;
138 current += len;
139 if (*current != '\0') {
140 Logger::Error("missing \\0 at chunk %d offset %" PRId64, i,
141 (int64_t)current - (int64_t)m_chunks[i]);
142 return false;
144 current++;
147 return true;
150 void APCFileStorage::cleanup() {
151 Lock lock(m_lock);
152 for (unsigned int i = 0 ; i < m_fileNames.size(); i++) {
153 unlink(m_fileNames[i].c_str());
155 m_chunks.clear();
158 bool APCFileStorage::addFile() {
159 if ((int64_t)m_chunks.size() * m_chunkSize >= m_maxSize) {
160 m_state = StorageState::Full;
161 return false;
163 char name[PATH_MAX];
164 snprintf(name, sizeof(name), "%s.XXXXXX", m_prefix.c_str());
165 int fd = mkstemp(name);
166 if (fd < 0) {
167 Logger::Error("Failed to open temp file");
168 return false;
170 bool couldAllocate = false;
171 #if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE
172 couldAllocate = posix_fallocate(fd, 0, m_chunkSize) == 0;
173 #elif defined(__APPLE__)
174 fstore_t store = { F_ALLOCATECONTIG, F_PEOFPOSMODE, 0, m_chunkSize };
175 int ret = fcntl(fd, F_PREALLOCATE, &store);
176 if (ret == -1) {
177 store.fst_flags = F_ALLOCATEALL;
178 ret = fcntl(fd, F_PREALLOCATE, &store);
179 if (ret == -1) {
180 couldAllocate = false;
183 couldAllocate = ftruncate(fd, m_chunkSize) == 0;
184 #else
185 #error "No implementation for posix_fallocate on your platform."
186 #endif
187 if (!couldAllocate) {
188 Logger::Error("Failed to posix_fallocate of size %" PRId64, m_chunkSize);
189 close(fd);
190 return false;
192 if (apcExtension::FileStorageKeepFileLinked) {
193 m_fileNames.push_back(std::string(name));
194 } else {
195 unlink(name);
197 char *addr = (char *)mmap(nullptr, m_chunkSize, PROT_READ | PROT_WRITE,
198 MAP_SHARED, fd, 0);
199 if (addr == (char *)-1) {
200 Logger::Error("Failed to mmap %s of size %" PRId64, name, m_chunkSize);
201 close(fd);
202 return false;
204 numa_interleave(addr, m_chunkSize);
205 m_current = addr;
206 m_chunkRemain = m_chunkSize - PaddingSize;
207 m_chunks.push_back(addr);
208 close(fd);
209 return true;
212 ///////////////////////////////////////////////////////////////////////////////