2
* Copyright 2011 Google Inc.
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
* you may not use this file except in compliance with the License.
6
* You may obtain a copy of the License at
8
* http://www.apache.org/licenses/LICENSE-2.0
10
* Unless required by applicable law or agreed to in writing, software
11
* distributed under the License is distributed on an "AS IS" BASIS,
12
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
* See the License for the specific language governing permissions and
14
* limitations under the License.
17
// Author: morlovich@google.com (Maksim Orlovich)
19
// Data structure operation helpers for SharedMemCache. See the top of
20
// shared_mem_cache.cc for data format descriptions.
22
#ifndef PAGESPEED_KERNEL_SHAREDMEM_SHARED_MEM_CACHE_DATA_H_
23
#define PAGESPEED_KERNEL_SHAREDMEM_SHARED_MEM_CACHE_DATA_H_
25
#include <cstddef> // for size_t
28
#include "base/logging.h"
29
#include "pagespeed/kernel/base/basictypes.h"
30
#include "pagespeed/kernel/base/scoped_ptr.h"
31
#include "pagespeed/kernel/base/string.h"
32
#include "pagespeed/kernel/base/thread_annotations.h"
34
namespace net_instaweb {
37
class AbstractSharedMem;
38
class AbstractSharedMemSegment;
41
namespace SharedMemCacheData {
43
typedef int32 EntryNum;
44
typedef int32 BlockNum;
45
typedef std::vector<BlockNum> BlockVector;
47
const BlockNum kInvalidBlock = -1;
48
const EntryNum kInvalidEntry = -1;
49
const size_t kHashSize = 16;
54
// FS operation stats --- updated by SharedMemCache. We do it
55
// this way rather than using the normal Statistics interface
56
// to avoid having to worry about extra synchronization inside
57
// critical sections --- since we already hold sector locks
58
// when doing this stuff, it's easy to update per-sector data.
59
// TODO(morlovich): Consider periodically pushing these to
62
int64 num_put_update; // update of the same key
63
int64 num_put_replace; // replacement of different key
64
int64 num_put_concurrent_create;
65
int64 num_put_concurrent_full_set;
66
int64 num_put_spins; // # of times writers had to sleep behind readers
67
int64 num_get; // # of calls to get
70
// Current state stats --- updated by SharedMemCacheData
74
// Adds number to this object's. No concurrency control is done.
75
void Add(const SectorStats& other);
77
// Text dump of the statistics. No concurrency control is done.
78
GoogleString Dump(size_t total_entries, size_t total_blocks) const;
82
BlockNum free_list_front;
83
EntryNum lru_list_front;
84
EntryNum lru_list_rear;
93
char hash_bytes[kHashSize];
94
int64 last_use_timestamp_ms;
97
// For LRU list, prev/next are kInvalidEntry to denote 'none', which
98
// can apply both at endpoints and for entries not in the LRU at all,
103
BlockNum first_block;
105
// When this is true, someone is trying to overwrite this entry.
108
// Number of readers currently accessing the data.
109
uint32 open_count : 31;
111
uint32 padding; // ensures we're 8-aligned.
114
// Helper for operating on a given sector's data structures; helping
115
// access them, lay them out in memory, and initialize them. It does not
116
// implement the actual cache operations, however. In particular, its
117
// methods affect only a single data structure at the time and do not
118
// do anything to preserve cross-structure invariants.
119
template<size_t kBlockSize>
122
// Creates a wrapper to help operate on cache sectors in a given region of
123
// memory with given geometry. The sector should have had as much memory
124
// allocated for it as returned by a call to RequiredSize with the same
127
// Note that this doesn't do any imperative initialization; you must
128
// call Initialize() in the parent process, and Attach() in child processes,
129
// and check their results as well. Also, segment is assumed to be owned
130
// separately, with lifetime longer than ours.
131
Sector(AbstractSharedMemSegment* segment, size_t sector_offset,
132
size_t cache_entries, size_t data_blocks);
135
// This should be called from child processes to initialize client
136
// state for the cache already formatted by a call to Initialize() in
139
// Returns if successful (which it should be if the parent process
140
// successfully create the memory and Initialize()'d it).
141
bool Attach(MessageHandler* handler);
143
// This should be called from the initial/parent process before the children
144
// start. It initializes the data structures in this sector, including
145
// mutexes. Returns true on success.
146
bool Initialize(MessageHandler* handler);
148
// Computes how much memory a sector will need for given number of entries.
149
// Also makes sure it's padded to proper alignment.
150
static size_t RequiredSize(AbstractSharedMem* shmem_runtime,
151
size_t cache_entries, size_t data_blocks);
155
// The sector lock should be held while doing any metadata accesses.
156
AbstractMutex* mutex() const LOCK_RETURNED(mutex_) { return mutex_.get(); }
158
// Block successor list ops.
159
// ------------------------------------------------------------
160
BlockNum GetBlockSuccessor(BlockNum block) EXCLUSIVE_LOCKS_REQUIRED(mutex()) {
162
DCHECK_LT(block, static_cast<BlockNum>(data_blocks_));
163
return block_successors_[block];
166
void SetBlockSuccessor(BlockNum block, BlockNum next)
167
EXCLUSIVE_LOCKS_REQUIRED(mutex()) {
169
DCHECK_LT(block, static_cast<BlockNum>(data_blocks_));
171
DCHECK_GE(next, kInvalidBlock);
172
DCHECK_LT(next, static_cast<BlockNum>(data_blocks_));
174
block_successors_[block] = next;
177
// Links blocks in the vector in order, with later blocks being
178
// marked as successors of later ones.
179
void LinkBlockSuccessors(const BlockVector& blocks)
180
EXCLUSIVE_LOCKS_REQUIRED(mutex()) {
181
for (size_t pos = 0; pos < blocks.size(); ++pos) {
182
if (pos == (blocks.size() - 1)) {
183
SetBlockSuccessor(blocks[pos], kInvalidBlock);
185
SetBlockSuccessor(blocks[pos], blocks[pos + 1]);
191
// ------------------------------------------------------------
193
// Allocates as close to the goal blocks from freelist as it can, and
194
// appends their numbers to blocks. Returns how much it allocated.
195
// Does not adjust block successor lists.
197
// Note that this doesn't attempt to free blocks that are in use by some
199
int AllocBlocksFromFreeList(int goal, BlockVector* blocks)
200
EXCLUSIVE_LOCKS_REQUIRED(mutex());
202
// Puts all the passed in blocks onto this sector's freelist.
203
// Does not read successors for passed in blocks, but does set them
204
// for freelist membership.
205
void ReturnBlocksToFreeList(const BlockVector& blocks)
206
EXCLUSIVE_LOCKS_REQUIRED(mutex());
208
// Cache directory ops.
209
// ------------------------------------------------------------
211
// Returns the given # entry.
212
CacheEntry* EntryAt(EntryNum slot) {
213
return reinterpret_cast<CacheEntry*>(directory_base_) + slot;
216
// Inserts the given entry into the LRU, at front.
217
// Precondition: must not be in LRU.
218
void InsertEntryIntoLRU(EntryNum entry_num);
220
// Removes from the LRU. Safe to call if not in the LRU already.
221
void UnlinkEntryFromLRU(EntryNum entry_num);
223
EntryNum OldestEntryNum() {
224
return sector_header_->lru_list_rear;
228
// ------------------------------------------------------------
230
char* BlockBytes(BlockNum block_num) {
231
return blocks_base_ + kBlockSize * block_num;
234
// Ops for lists of blocks corresponding to each directory entry,
235
// and related size computations
236
// ------------------------------------------------------------
238
// Number of blocks of data needed for size blocks.
239
static size_t DataBlocksForSize(size_t size) {
240
return NeededPieces(size, kBlockSize);
243
// The # of bytes stored in block 'b' out of 'total' blocks for file of size
245
// Precondition: 'total' is appropriate for 'total_bytes'.
246
static size_t BytesInPortion(size_t total_bytes, size_t b, size_t total);
248
// Appends the list of blocks used by the entry to 'blocks'.
249
// Returns the number of items appended.
250
int BlockListForEntry(CacheEntry* entry, BlockVector* out_blocks)
251
EXCLUSIVE_LOCKS_REQUIRED(mutex());
254
// ------------------------------------------------------------
256
SectorStats* sector_stats() { return §or_header_->stats; }
258
// Prints out all statistics in the header (some of which are maintained
259
// by the higher-level)
260
void DumpStats(MessageHandler* handler);
263
// Helper for doing sizing/memory layout computations.
266
// How many piece_size pieces suffice to fit total
267
static size_t NeededPieces(size_t total, size_t piece_size) {
268
return (total + piece_size - 1) / piece_size;
271
// Configured geometry
272
size_t cache_entries_;
275
// Pointers to where various things are, and our sizes
276
AbstractSharedMemSegment* segment_;
277
scoped_ptr<AbstractMutex> mutex_;
278
SectorHeader* sector_header_;
279
BlockNum* block_successors_ PT_GUARDED_BY(mutex());
280
char* directory_base_;
282
size_t sector_offset_; // offset of the sector within the SHM segment
284
DISALLOW_COPY_AND_ASSIGN(Sector);
287
} // namespace SharedMemCacheData
289
} // namespace net_instaweb
291
#endif // PAGESPEED_KERNEL_SHAREDMEM_SHARED_MEM_CACHE_DATA_H_