Skip to content

Commit 236aea3

Browse files
Jamie GennisAndroid (Google) Code Review
authored andcommitted
Merge changes Ibc99cb1c,Ie1f4f6f8 into ics-mr1
* changes: BlobCache: implement cache serialization BlobCache: remove the mutex locking
2 parents 3f9ce4c + 9d9768d commit 236aea3

3 files changed

Lines changed: 389 additions & 24 deletions

File tree

include/utils/BlobCache.h

Lines changed: 87 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,21 @@
1919

2020
#include <stddef.h>
2121

22+
#include <utils/Flattenable.h>
2223
#include <utils/RefBase.h>
2324
#include <utils/SortedVector.h>
2425
#include <utils/threads.h>
2526

2627
namespace android {
2728

28-
// A BlobCache is an in-memory cache for binary key/value pairs. All the public
29-
// methods are thread-safe.
29+
// A BlobCache is an in-memory cache for binary key/value pairs. A BlobCache
30+
// does NOT provide any thread-safety guarantees.
3031
//
31-
// The cache contents can be serialized to a file and reloaded in a subsequent
32-
// execution of the program. This serialization is non-portable and should only
33-
// be loaded by the device that generated it.
34-
class BlobCache : public RefBase {
32+
// The cache contents can be serialized to an in-memory buffer or mmap'd file
33+
// and then reloaded in a subsequent execution of the program. This
34+
// serialization is non-portable and the data should only be used by the device
35+
// that generated it.
36+
class BlobCache : public RefBase, public Flattenable {
3537
public:
3638

3739
// Create an empty blob cache. The blob cache will cache key/value pairs
@@ -58,14 +60,13 @@ class BlobCache : public RefBase {
5860
void set(const void* key, size_t keySize, const void* value,
5961
size_t valueSize);
6062

61-
// The get function retrieves from the cache the binary value associated
62-
// with a given binary key. If the key is present in the cache then the
63-
// length of the binary value associated with that key is returned. If the
64-
// value argument is non-NULL and the size of the cached value is less than
65-
// valueSize bytes then the cached value is copied into the buffer pointed
66-
// to by the value argument. If the key is not present in the cache then 0
67-
// is returned and the buffer pointed to by the value argument is not
68-
// modified.
63+
// get retrieves from the cache the binary value associated with a given
64+
// binary key. If the key is present in the cache then the length of the
65+
// binary value associated with that key is returned. If the value argument
66+
// is non-NULL and the size of the cached value is less than valueSize bytes
67+
// then the cached value is copied into the buffer pointed to by the value
68+
// argument. If the key is not present in the cache then 0 is returned and
69+
// the buffer pointed to by the value argument is not modified.
6970
//
7071
// Note that when calling get multiple times with the same key, the later
7172
// calls may fail, returning 0, even if earlier calls succeeded. The return
@@ -77,6 +78,37 @@ class BlobCache : public RefBase {
7778
// 0 <= valueSize
7879
size_t get(const void* key, size_t keySize, void* value, size_t valueSize);
7980

81+
// getFlattenedSize returns the number of bytes needed to store the entire
82+
// serialized cache.
83+
virtual size_t getFlattenedSize() const;
84+
85+
// getFdCount returns the number of file descriptors that will result from
86+
// flattening the cache. This will always return 0 so as to allow the
87+
// flattened cache to be saved to disk and then later restored.
88+
virtual size_t getFdCount() const;
89+
90+
// flatten serializes the current contents of the cache into the memory
91+
// pointed to by 'buffer'. The serialized cache contents can later be
92+
// loaded into a BlobCache object using the unflatten method. The contents
93+
// of the BlobCache object will not be modified.
94+
//
95+
// Preconditions:
96+
// size >= this.getFlattenedSize()
97+
// count == 0
98+
virtual status_t flatten(void* buffer, size_t size, int fds[],
99+
size_t count) const;
100+
101+
// unflatten replaces the contents of the cache with the serialized cache
102+
// contents in the memory pointed to by 'buffer'. The previous contents of
103+
// the BlobCache will be evicted from the cache. If an error occurs while
104+
// unflattening the serialized cache contents then the BlobCache will be
105+
// left in an empty state.
106+
//
107+
// Preconditions:
108+
// count == 0
109+
virtual status_t unflatten(void const* buffer, size_t size, int fds[],
110+
size_t count);
111+
80112
private:
81113
// Copying is disallowed.
82114
BlobCache(const BlobCache&);
@@ -144,6 +176,46 @@ class BlobCache : public RefBase {
144176
sp<Blob> mValue;
145177
};
146178

179+
// A Header is the header for the entire BlobCache serialization format. No
180+
// need to make this portable, so we simply write the struct out.
181+
struct Header {
182+
// mMagicNumber is the magic number that identifies the data as
183+
// serialized BlobCache contents. It must always contain 'Blb$'.
184+
uint32_t mMagicNumber;
185+
186+
// mBlobCacheVersion is the serialization format version.
187+
uint32_t mBlobCacheVersion;
188+
189+
// mDeviceVersion is the device-specific version of the cache. This can
190+
// be used to invalidate the cache.
191+
uint32_t mDeviceVersion;
192+
193+
// mNumEntries is number of cache entries following the header in the
194+
// data.
195+
size_t mNumEntries;
196+
};
197+
198+
// An EntryHeader is the header for a serialized cache entry. No need to
199+
// make this portable, so we simply write the struct out. Each EntryHeader
200+
// is followed imediately by the key data and then the value data.
201+
//
202+
// The beginning of each serialized EntryHeader is 4-byte aligned, so the
203+
// number of bytes that a serialized cache entry will occupy is:
204+
//
205+
// ((sizeof(EntryHeader) + keySize + valueSize) + 3) & ~3
206+
//
207+
struct EntryHeader {
208+
// mKeySize is the size of the entry key in bytes.
209+
size_t mKeySize;
210+
211+
// mValueSize is the size of the entry value in bytes.
212+
size_t mValueSize;
213+
214+
// mData contains both the key and value data for the cache entry. The
215+
// key comes first followed immediately by the value.
216+
uint8_t mData[];
217+
};
218+
147219
// mMaxKeySize is the maximum key size that will be cached. Calls to
148220
// BlobCache::set with a keySize parameter larger than mMaxKeySize will
149221
// simply not add the key/value pair to the cache.
@@ -166,17 +238,12 @@ class BlobCache : public RefBase {
166238
size_t mTotalSize;
167239

168240
// mRandState is the pseudo-random number generator state. It is passed to
169-
// nrand48 to generate random numbers when needed. It must be protected by
170-
// mMutex.
241+
// nrand48 to generate random numbers when needed.
171242
unsigned short mRandState[3];
172243

173244
// mCacheEntries stores all the cache entries that are resident in memory.
174245
// Cache entries are added to it by the 'set' method.
175246
SortedVector<CacheEntry> mCacheEntries;
176-
177-
// mMutex is used to synchronize access to all member variables. It must be
178-
// locked any time the member variables are written or read.
179-
Mutex mMutex;
180247
};
181248

182249
}

libs/utils/BlobCache.cpp

Lines changed: 138 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,20 @@
2121
#include <string.h>
2222

2323
#include <utils/BlobCache.h>
24+
#include <utils/Errors.h>
2425
#include <utils/Log.h>
2526

2627
namespace android {
2728

29+
// BlobCache::Header::mMagicNumber value
30+
static const uint32_t blobCacheMagic = '_Bb$';
31+
32+
// BlobCache::Header::mBlobCacheVersion value
33+
static const uint32_t blobCacheVersion = 1;
34+
35+
// BlobCache::Header::mDeviceVersion value
36+
static const uint32_t blobCacheDeviceVersion = 1;
37+
2838
BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize):
2939
mMaxKeySize(maxKeySize),
3040
mMaxValueSize(maxValueSize),
@@ -67,12 +77,10 @@ void BlobCache::set(const void* key, size_t keySize, const void* value,
6777
return;
6878
}
6979

70-
Mutex::Autolock lock(mMutex);
7180
sp<Blob> dummyKey(new Blob(key, keySize, false));
7281
CacheEntry dummyEntry(dummyKey, NULL);
7382

7483
while (true) {
75-
7684
ssize_t index = mCacheEntries.indexOf(dummyEntry);
7785
if (index < 0) {
7886
// Create a new cache entry.
@@ -129,7 +137,6 @@ size_t BlobCache::get(const void* key, size_t keySize, void* value,
129137
keySize, mMaxKeySize);
130138
return 0;
131139
}
132-
Mutex::Autolock lock(mMutex);
133140
sp<Blob> dummyKey(new Blob(key, keySize, false));
134141
CacheEntry dummyEntry(dummyKey, NULL);
135142
ssize_t index = mCacheEntries.indexOf(dummyEntry);
@@ -152,6 +159,133 @@ size_t BlobCache::get(const void* key, size_t keySize, void* value,
152159
return valueBlobSize;
153160
}
154161

162+
static inline size_t align4(size_t size) {
163+
return (size + 3) & ~3;
164+
}
165+
166+
size_t BlobCache::getFlattenedSize() const {
167+
size_t size = sizeof(Header);
168+
for (size_t i = 0; i < mCacheEntries.size(); i++) {
169+
const CacheEntry& e(mCacheEntries[i]);
170+
sp<Blob> keyBlob = e.getKey();
171+
sp<Blob> valueBlob = e.getValue();
172+
size = align4(size);
173+
size += sizeof(EntryHeader) + keyBlob->getSize() +
174+
valueBlob->getSize();
175+
}
176+
return size;
177+
}
178+
179+
size_t BlobCache::getFdCount() const {
180+
return 0;
181+
}
182+
183+
status_t BlobCache::flatten(void* buffer, size_t size, int fds[], size_t count)
184+
const {
185+
if (count != 0) {
186+
LOGE("flatten: nonzero fd count: %d", count);
187+
return BAD_VALUE;
188+
}
189+
190+
// Write the cache header
191+
if (size < sizeof(Header)) {
192+
LOGE("flatten: not enough room for cache header");
193+
return BAD_VALUE;
194+
}
195+
Header* header = reinterpret_cast<Header*>(buffer);
196+
header->mMagicNumber = blobCacheMagic;
197+
header->mBlobCacheVersion = blobCacheVersion;
198+
header->mDeviceVersion = blobCacheDeviceVersion;
199+
header->mNumEntries = mCacheEntries.size();
200+
201+
// Write cache entries
202+
uint8_t* byteBuffer = reinterpret_cast<uint8_t*>(buffer);
203+
off_t byteOffset = align4(sizeof(Header));
204+
for (size_t i = 0; i < mCacheEntries.size(); i++) {
205+
const CacheEntry& e(mCacheEntries[i]);
206+
sp<Blob> keyBlob = e.getKey();
207+
sp<Blob> valueBlob = e.getValue();
208+
size_t keySize = keyBlob->getSize();
209+
size_t valueSize = valueBlob->getSize();
210+
211+
size_t entrySize = sizeof(EntryHeader) + keySize + valueSize;
212+
if (byteOffset + entrySize > size) {
213+
LOGE("flatten: not enough room for cache entries");
214+
return BAD_VALUE;
215+
}
216+
217+
EntryHeader* eheader = reinterpret_cast<EntryHeader*>(
218+
&byteBuffer[byteOffset]);
219+
eheader->mKeySize = keySize;
220+
eheader->mValueSize = valueSize;
221+
222+
memcpy(eheader->mData, keyBlob->getData(), keySize);
223+
memcpy(eheader->mData + keySize, valueBlob->getData(), valueSize);
224+
225+
byteOffset += align4(entrySize);
226+
}
227+
228+
return OK;
229+
}
230+
231+
status_t BlobCache::unflatten(void const* buffer, size_t size, int fds[],
232+
size_t count) {
233+
// All errors should result in the BlobCache being in an empty state.
234+
mCacheEntries.clear();
235+
236+
if (count != 0) {
237+
LOGE("unflatten: nonzero fd count: %d", count);
238+
return BAD_VALUE;
239+
}
240+
241+
// Read the cache header
242+
if (size < sizeof(Header)) {
243+
LOGE("unflatten: not enough room for cache header");
244+
return BAD_VALUE;
245+
}
246+
const Header* header = reinterpret_cast<const Header*>(buffer);
247+
if (header->mMagicNumber != blobCacheMagic) {
248+
LOGE("unflatten: bad magic number: %d", header->mMagicNumber);
249+
return BAD_VALUE;
250+
}
251+
if (header->mBlobCacheVersion != blobCacheVersion ||
252+
header->mDeviceVersion != blobCacheDeviceVersion) {
253+
// We treat version mismatches as an empty cache.
254+
return OK;
255+
}
256+
257+
// Read cache entries
258+
const uint8_t* byteBuffer = reinterpret_cast<const uint8_t*>(buffer);
259+
off_t byteOffset = align4(sizeof(Header));
260+
size_t numEntries = header->mNumEntries;
261+
for (size_t i = 0; i < numEntries; i++) {
262+
if (byteOffset + sizeof(EntryHeader) > size) {
263+
mCacheEntries.clear();
264+
LOGE("unflatten: not enough room for cache entry headers");
265+
return BAD_VALUE;
266+
}
267+
268+
const EntryHeader* eheader = reinterpret_cast<const EntryHeader*>(
269+
&byteBuffer[byteOffset]);
270+
size_t keySize = eheader->mKeySize;
271+
size_t valueSize = eheader->mValueSize;
272+
size_t entrySize = sizeof(EntryHeader) + keySize + valueSize;
273+
274+
if (byteOffset + entrySize > size) {
275+
mCacheEntries.clear();
276+
LOGE("unflatten: not enough room for cache entry headers");
277+
return BAD_VALUE;
278+
}
279+
280+
const uint8_t* data = eheader->mData;
281+
set(data, keySize, data + keySize, valueSize);
282+
283+
byteOffset += align4(entrySize);
284+
}
285+
286+
return OK;
287+
}
288+
155289
long int BlobCache::blob_random() {
156290
#ifdef _WIN32
157291
return rand();
@@ -179,7 +313,7 @@ BlobCache::Blob::Blob(const void* data, size_t size, bool copyData):
179313
mData(copyData ? malloc(size) : data),
180314
mSize(size),
181315
mOwnsData(copyData) {
182-
if (copyData) {
316+
if (data != NULL && copyData) {
183317
memcpy(const_cast<void*>(mData), data, size);
184318
}
185319
}

0 commit comments

Comments
 (0)