2
* Copyright 2010 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
#ifndef PAGESPEED_KERNEL_SHAREDMEM_SHARED_MEM_CACHE_H_
20
#define PAGESPEED_KERNEL_SHAREDMEM_SHARED_MEM_CACHE_H_
25
#include "pagespeed/kernel/base/basictypes.h"
26
#include "pagespeed/kernel/base/cache_interface.h"
27
#include "pagespeed/kernel/base/scoped_ptr.h"
28
#include "pagespeed/kernel/base/string.h"
29
#include "pagespeed/kernel/base/thread_annotations.h"
30
#include "pagespeed/kernel/sharedmem/shared_mem_cache_data.h"
32
namespace net_instaweb {
34
class AbstractSharedMem;
35
class AbstractSharedMemSegment;
38
class SharedMemCacheDump;
42
// Abstract interface for a cache.
43
template<size_t kBlockSize>
44
class SharedMemCache : public CacheInterface {
46
static const int kAssociativity = 4; // Note: changing this requires changing
47
// code of ExtractPosition as well.
49
// Initializes the cache's settings, but does not actually touch the shared
50
// memory --- you must call Initialize or Attach (and handle them potentially
51
// returning false) to do so. The filename parameter will be used to identify
52
// the shared memory segment, so distinct caches should use distinct values.
54
// Precondition: hasher's raw mode must produce 13 bytes or more.
55
SharedMemCache(AbstractSharedMem* shm_runtime, const GoogleString& filename,
56
Timer* timer, const Hasher* hasher, int sectors,
57
int entries_per_sector, int blocks_per_sector,
58
MessageHandler* handler);
60
virtual ~SharedMemCache();
62
// Sets up our shared state for use of all child processes/threads. Returns
63
// whether successful. This should be called exactly once for every cache
64
// in the root process, before forking.
67
// Connects to already initialized state from a child process. It must be
68
// called once for every cache in every child process (that is, post-fork).
69
// Returns whether successful.
72
// This should be called from the root process as it is about to exit, when
73
// no further children are expected to start.
74
static void GlobalCleanup(AbstractSharedMem* shm_runtime,
75
const GoogleString& filename,
76
MessageHandler* message_handler);
78
// Computes how many entries and blocks per sector a cache with total size
79
// 'size_kb' and 'sectors' should have if there are about 'block_entry_ratio'
80
// worth of blocks of data per every entry. You probably want to underestimate
81
// this ratio somewhat, since having extra entries can reduce conflicts.
82
// Also outputs size_cap, which is the limit on object size for the resulting
84
static void ComputeDimensions(int64 size_kb,
85
int block_entry_ratio,
87
int* entries_per_sector_out,
88
int* blocks_per_sector_out,
91
// Returns the largest size of an object this cache can store.
92
size_t MaxValueSize() const {
93
return (blocks_per_sector_ * kBlockSize) / 8;
96
// Returns some statistics as plaintext.
97
// TODO(morlovich): Potentially periodically push these to the main
98
// Statistics system (or pull to it from these).
99
GoogleString DumpStats();
101
// Saves information on sector contents to *dest. You can call this multiple
102
// times with the same dest.
103
// Note: other accesses to the sector will be locked out for the duration.
104
void AddSectorToSnapshot(int sector_num, SharedMemCacheDump* dest);
106
// Restores entries stored in the dump into this cache. The dump
107
// may contain multiple sectors.
108
void RestoreSnapshot(const SharedMemCacheDump& dump);
110
// Encode/Decode SharedMemCacheDump objects.
111
static void MarshalSnapshot(const SharedMemCacheDump& dump,
113
static void DemarshalSnapshot(const GoogleString& marshaled,
114
SharedMemCacheDump* out);
116
virtual void Get(const GoogleString& key, Callback* callback);
117
virtual void Put(const GoogleString& key, SharedString* value);
118
virtual void Delete(const GoogleString& key);
119
static GoogleString FormatName();
120
virtual GoogleString Name() const { return FormatName();}
122
virtual bool IsBlocking() const { return true; }
123
virtual bool IsHealthy() const { return true; }
124
virtual void ShutDown() {
125
// TODO(morlovich): Implement
128
// Sanity check the cache data structures.
132
// Describes potential placements of a key
135
SharedMemCacheData::EntryNum keys[kAssociativity];
138
bool InitCache(bool parent);
140
void PutRawHash(const GoogleString& raw_hash, int64 last_use_timestamp_ms,
141
SharedString* value);
143
// Finish a get, with the entry matching and sector lock held.
144
// Releases lock when done.
145
void GetFromEntry(const GoogleString& key,
146
SharedMemCacheData::Sector<kBlockSize>* sector,
147
SharedMemCacheData::EntryNum entry_num, Callback* callback)
148
UNLOCK_FUNCTION(sector->mutex());
150
// Finish a put into the given entry. Lock is expected to be held at entry,
151
// will be released when done. The hash in the entry must also be already
152
// correct at time of entry.
153
void PutIntoEntry(SharedMemCacheData::Sector<kBlockSize>* sector,
154
SharedMemCacheData::EntryNum entry_num,
155
int64 last_use_timestamp_ms, SharedString* value)
156
EXCLUSIVE_LOCKS_REQUIRED(sector->mutex())
157
UNLOCK_FUNCTION(sector->mutex());
159
// Finish a delete, with the entry matching and sector lock held.
160
// Releases lock when done.
161
void DeleteEntry(SharedMemCacheData::Sector<kBlockSize>* sector,
162
SharedMemCacheData::EntryNum entry_num)
163
UNLOCK_FUNCTION(sector->mutex());
165
// Attempts to allocate at least the given number of blocks, and appends any
166
// blocks it manages to allocate to *blocks. Returns whether successful.
168
// Note that in case of failure, some blocks may still have been allocated,
169
// so the caller may have to clean them up. When successful, this method may
170
// allocate more memory than is requested.
171
bool TryAllocateBlocks(SharedMemCacheData::Sector<kBlockSize>* sector,
172
int goal, SharedMemCacheData::BlockVector* blocks)
173
EXCLUSIVE_LOCKS_REQUIRED(sector->mutex());
175
// Marks the given entry free in the directory, and unlinks it from the LRU.
176
// Note that this does not touch the entry's blocks.
177
void MarkEntryFree(SharedMemCacheData::Sector<kBlockSize>* sector,
178
SharedMemCacheData::EntryNum entry_num);
180
// Marks entry as having been recently used, and updates timestamp.
181
void TouchEntry(SharedMemCacheData::Sector<kBlockSize>* sector,
182
int64 last_use_timestamp_ms,
183
SharedMemCacheData::EntryNum entry_num);
185
// Returns true if the entry can be written (in particular meaning it's not
186
// opened by someone else)
187
bool Writeable(const SharedMemCacheData::CacheEntry* entry);
189
bool KeyMatch(SharedMemCacheData::CacheEntry* entry,
190
const GoogleString& raw_hash);
192
GoogleString ToRawHash(const GoogleString& key);
194
// Given a hash, tells what sector and what entries in it to check.
195
void ExtractPosition(const GoogleString& raw_hash, Position* out_pos);
197
// Makes sure we have exclusive write access to the entry, with no concurrent
198
// readers. Must be called with sector lock held.
199
void EnsureReadyForWriting(SharedMemCacheData::Sector<kBlockSize>* sector,
200
SharedMemCacheData::CacheEntry* entry)
201
EXCLUSIVE_LOCKS_REQUIRED(sector->mutex());
203
AbstractSharedMem* shm_runtime_;
204
const Hasher* hasher_;
206
GoogleString filename_;
208
int entries_per_sector_;
209
int blocks_per_sector_;
210
MessageHandler* handler_;
212
scoped_ptr<AbstractSharedMemSegment> segment_;
213
std::vector<SharedMemCacheData::Sector<kBlockSize>*> sectors_;
217
DISALLOW_COPY_AND_ASSIGN(SharedMemCache);
220
} // namespace net_instaweb
222
#endif // PAGESPEED_KERNEL_SHAREDMEM_SHARED_MEM_CACHE_H_