-
Notifications
You must be signed in to change notification settings - Fork 15
/
mmap_cache.h
240 lines (220 loc) · 7.55 KB
/
mmap_cache.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
/*
* AUTHOR
*
* Rob Mueller <[email protected]>
*
* COPYRIGHT AND LICENSE
*
* Copyright (C) 2003 by FastMail IP Partners
*
* This library is free software; you can redistribute it and/or modify
* it under the same terms as Perl itself.
*
* mmap_cache
*
* Uses an mmap'ed file to act as a shared memory interprocess cache
*
* The C interface is quite explicit in it's use, in that you have to
* call individual functions to hash a key, lock a page, and find a
* value. This allows a simpler higher level interface to be written
*
* #include <mmap_cache.h>
*
* mmap_cache * cache = mmc_new();
* cache->param = val;
* mmc_init(cache);
*
* // Read a key
*
* // Hash get to find page and slot
* mmc_hash(cache, (void *)key_ptr, (int)key_len, &hash_page, &hash_slot);
* // Lock page
* mmc_lock(cache, hash_page);
* // Get pointer to value data
* mmc_read(cache, hash_slot, (void *)key_ptr, (int)key_len, (void **)&val_ptr, (int *)val_len, &expire_time, &flags);
* // Unlock page
* mmc_unlock(cache);
*
* // Write a key
*
* // Hash get to find page and slot
* mmc_hash(cache, (void *)key_ptr, (int)key_len, &hash_page, &hash_slot);
* // Lock page
* mmc_lock(cache, hash_page);
* // Get pointer to value data
* mmc_write(cache, hash_slot, (void *)key_ptr, (int)key_len, (void *)val_ptr, (int)val_len, expire_time, flags);
* // Unlock page
* mmc_unlock(cache);
*
* DESCRIPTION
*
* This class implements a shared memory cache through an mmap'ed file. It
* uses locking to ensure multiple processes can safely access the cache
* at the same time. It uses a basic LRU algorithm to keep the most used
* entries in the cache.
*
* It tries to be quite efficient through a number of means:
*
* It uses multiple pages within a file, and uses Fcntl to only lock
* a page at a time to reduce contention when multiple processes access
* the cache.
*
* It uses a dual level hashing system (hash to find page, then hash
* within each page to find a slot) to make most I<read> calls O(1) and
* fast
*
* On each I<write>, if there are slots and page space available, only
* the slot has to be updated and the data written at the end of the used
* data space. If either runs out, a re-organisation of the page is
* performed to create new slots/space which is done in an efficient way
*
* The locking is explicitly done in the C interface, so you can create
* a 'read_many' or 'write_many' function that reduces the number of
* locks required
*
*
* IMPLEMENTATION
*
* Each file is made up of a number of 'pages'. The number of
* pages and size of each page is specified when the class is
* constructed. These values are stored in the cache class
* and must be the same for each class connecting to the cache
* file.
*
* NumPages - Number of 'pages' in the cache
* PageSize - Size of each 'page' in the cache
*
* The layout of each page is:
*
* - Magic (4 bytes) - 0x92f7e3b1 magic page start marker
*
* - NumSlots (4 bytes) - Number of hash slots in this page
*
* - FreeSlots (4 bytes) - Number of free slots left in this page.
* This includes all slots with a last access time of 0
* (empty and don't search past) or 1 (empty, but keep searching
* because deleted slot)
*
* - OldSlots (4 bytes) - Of all the free slots, how many were in use
* and are now deleted. This is slots with a last access time of 1
*
* - FreeData (4 bytes) - Offset to free data area to place next item
*
* - FreeBytes (4 bytes) - Bytes left in free data area
*
* - N Reads (4 bytes) - Number of reads performed on this page
*
* - N Read Hits (4 bytes) - Number of reads on this page that have hit
* something in the cache
*
* - Slots (4 bytes * NumSlots) - Hash slots
*
* - Data (to end of page) - Key/value data
*
* Each slot is made of:
*
* - Offset (4 bytes) - offset from start of page to actual data. This
* is 0 if slot is empty, 1 if was used but now empty. This is needed
* so deletes don't require a complete rehash with the linear
* searching method we use
*
* Each data item is made of:
*
* - LastAccess (4 bytes) - Unix time data was last accessed
*
* - ExpireTime (4 bytes) - Unix time data should expire. This is 0 if it
* should never expire
*
* - HashValue (4 bytes) - Value key was hashed to, so we don't have to
* rehash on a re-organisation of the hash table
*
* - Flags (4 bytes) - Various flags
*
* - KeyLen (4 bytes) - Length of key
*
* - ValueLen (4 bytes) - Length of value
*
* - Key (KeyLen bytes) - Key data
*
* - Value (ValueLen bytes) - Value data
*
* Each set/get/delete operation involves:
*
* - Find the page for the key
* - Lock the page
* - Read the page header
* - Find the hash slot for the key
*
* For get's:
*
* - Use linear probing to find correct key, or empty slot
*
* For set's:
*
* - Use linear probing to find empty slot
* - If not enough free slots, do an 'expunge' run
* - Store key/value at FreeData offset, update, and store in slot
* - If not enough space at FreeData offset, do an 'expunge' run
* then store data
*
* For delete's:
*
* - Use linear probing to find correct key, or empty slot
* - Set slot to empty (data cleaned up in expunge run)
*
* An expunge run consists of:
*
* - Scan slots to find used key/value parts. Remove older items
* - If ratio used/free slots too high, increase slot count
* - Compact key/value data into one memory block
* - Restore and update offsets in slots
*
*/
#include <stdint.h>
/* Main cache structure passed as a pointer to each function */
typedef struct mmap_cache mmap_cache;
/* Iterator structure for iterating over items in cache */
typedef struct mmap_cache_it mmap_cache_it;
/* Unsigned 32 bit integer */
typedef uint32_t MU32;
/* Unsigned 64 bit integer */
typedef uint64_t MU64;
/* Magic value for no p_cur */
#define NOPAGE (~(MU32)0)
/* Allow overriding "time" for tests */
void mmc_set_time_override(MU32);
/* Initialisation/closing/error functions */
mmap_cache * mmc_new();
int mmc_init(mmap_cache *);
int mmc_set_param(mmap_cache *, char *, char *);
int mmc_get_param(mmap_cache *, char *);
int mmc_close(mmap_cache *);
char * mmc_error(mmap_cache *);
/* Functions for find/locking a page */
int mmc_hash(mmap_cache *, void *, int, MU32 *, MU32 *);
int mmc_lock(mmap_cache *, MU32);
int mmc_unlock(mmap_cache *);
int mmc_is_locked(mmap_cache *);
/* Functions for getting/setting/deleting values in current page */
int mmc_read(mmap_cache *, MU32, void *, int, void **, int *, MU32 *, MU32 *);
int mmc_write(mmap_cache *, MU32, void *, int, void *, int, MU32, MU32);
int mmc_delete(mmap_cache *, MU32, void *, int, MU32 *);
/* Functions of expunging values in current page */
int mmc_calc_expunge(mmap_cache *, int, int, MU32 *, MU32 ***);
int mmc_do_expunge(mmap_cache *, int, MU32, MU32 **);
/* Functions for iterating over items in a cache */
mmap_cache_it * mmc_iterate_new(mmap_cache *);
MU32 * mmc_iterate_next(mmap_cache_it *);
void mmc_iterate_close(mmap_cache_it *);
/* Retrieve details of a cache page/entry */
void mmc_get_details(mmap_cache *, MU32 *, void **, int *, void **, int *, MU32 *, MU32 *, MU32 *);
void mmc_get_page_details(mmap_cache * cache, MU32 * nreads, MU32 * nreadhits);
void mmc_reset_page_details(mmap_cache * cache);
/* Internal functions */
int _mmc_set_error(mmap_cache *, int, char *, ...);
void _mmc_init_page(mmap_cache *, MU32);
MU32 * _mmc_find_slot(mmap_cache * , MU32 , void *, int, int );
void _mmc_delete_slot(mmap_cache * , MU32 *);
int _mmc_check_expunge(mmap_cache * , int);
int _mmc_test_page(mmap_cache *);
int _mmc_dump_page(mmap_cache *);