Deleted Added
full compact
cachelib.c (194093) cachelib.c (194095)
1/*-
2 * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 */
27
28#include <sys/cdefs.h>
1/*-
2 * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD: head/usr.sbin/nscd/cachelib.c 194093 2009-06-13 00:43:56Z des $");
29__FBSDID("$FreeBSD: head/usr.sbin/nscd/cachelib.c 194095 2009-06-13 00:54:52Z des $");
30
31#include <sys/time.h>
32
33#include <assert.h>
34#include <stdlib.h>
35#include <string.h>
36
37#include "cachelib.h"
38#include "debug.h"
39
40#define INITIAL_ENTRIES_CAPACITY 32
41#define ENTRIES_CAPACITY_STEP 32
42
43#define STRING_SIMPLE_HASH_BODY(in_var, var, a, M) \
44 for ((var) = 0; *(in_var) != '\0'; ++(in_var)) \
45 (var) = ((a)*(var) + *(in_var)) % (M)
46
47#define STRING_SIMPLE_MP2_HASH_BODY(in_var, var, a, M) \
48 for ((var) = 0; *(in_var) != 0; ++(in_var)) \
49 (var) = ((a)*(var) + *(in_var)) & (M - 1)
50
51static int cache_elemsize_common_continue_func(struct cache_common_entry_ *,
52 struct cache_policy_item_ *);
53static int cache_lifetime_common_continue_func(struct cache_common_entry_ *,
54 struct cache_policy_item_ *);
55static void clear_cache_entry(struct cache_entry_ *);
56static void destroy_cache_entry(struct cache_entry_ *);
57static void destroy_cache_mp_read_session(struct cache_mp_read_session_ *);
58static void destroy_cache_mp_write_session(struct cache_mp_write_session_ *);
59static int entries_bsearch_cmp_func(const void *, const void *);
60static int entries_qsort_cmp_func(const void *, const void *);
61static struct cache_entry_ ** find_cache_entry_p(struct cache_ *,
62 const char *);
63static void flush_cache_entry(struct cache_entry_ *);
64static void flush_cache_policy(struct cache_common_entry_ *,
65 struct cache_policy_ *, struct cache_policy_ *,
66 int (*)(struct cache_common_entry_ *,
67 struct cache_policy_item_ *));
68static int ht_items_cmp_func(const void *, const void *);
69static int ht_items_fixed_size_left_cmp_func(const void *, const void *);
70static hashtable_index_t ht_item_hash_func(const void *, size_t);
71
72/*
73 * Hashing and comparing routines, that are used with the hash tables
74 */
75static int
76ht_items_cmp_func(const void *p1, const void *p2)
77{
78 struct cache_ht_item_data_ *hp1, *hp2;
79 size_t min_size;
80 int result;
81
82 hp1 = (struct cache_ht_item_data_ *)p1;
83 hp2 = (struct cache_ht_item_data_ *)p2;
84
85 assert(hp1->key != NULL);
86 assert(hp2->key != NULL);
87
88 if (hp1->key_size != hp2->key_size) {
89 min_size = (hp1->key_size < hp2->key_size) ? hp1->key_size :
90 hp2->key_size;
91 result = memcmp(hp1->key, hp2->key, min_size);
92
93 if (result == 0)
94 return ((hp1->key_size < hp2->key_size) ? -1 : 1);
95 else
96 return (result);
97 } else
98 return (memcmp(hp1->key, hp2->key, hp1->key_size));
99}
100
101static int
102ht_items_fixed_size_left_cmp_func(const void *p1, const void *p2)
103{
104 struct cache_ht_item_data_ *hp1, *hp2;
105 size_t min_size;
106 int result;
107
108 hp1 = (struct cache_ht_item_data_ *)p1;
109 hp2 = (struct cache_ht_item_data_ *)p2;
110
111 assert(hp1->key != NULL);
112 assert(hp2->key != NULL);
113
114 if (hp1->key_size != hp2->key_size) {
115 min_size = (hp1->key_size < hp2->key_size) ? hp1->key_size :
116 hp2->key_size;
117 result = memcmp(hp1->key, hp2->key, min_size);
118
119 if (result == 0)
120 if (min_size == hp1->key_size)
121 return (0);
122 else
123 return ((hp1->key_size < hp2->key_size) ? -1 : 1);
124 else
125 return (result);
126 } else
127 return (memcmp(hp1->key, hp2->key, hp1->key_size));
128}
129
130static hashtable_index_t
131ht_item_hash_func(const void *p, size_t cache_entries_size)
132{
133 struct cache_ht_item_data_ *hp;
134 size_t i;
135
136 hashtable_index_t retval;
137
138 hp = (struct cache_ht_item_data_ *)p;
139 assert(hp->key != NULL);
140
141 retval = 0;
142 for (i = 0; i < hp->key_size; ++i)
143 retval = (127 * retval + (unsigned char)hp->key[i]) %
144 cache_entries_size;
145
146 return retval;
147}
148
30
31#include <sys/time.h>
32
33#include <assert.h>
34#include <stdlib.h>
35#include <string.h>
36
37#include "cachelib.h"
38#include "debug.h"
39
40#define INITIAL_ENTRIES_CAPACITY 32
41#define ENTRIES_CAPACITY_STEP 32
42
43#define STRING_SIMPLE_HASH_BODY(in_var, var, a, M) \
44 for ((var) = 0; *(in_var) != '\0'; ++(in_var)) \
45 (var) = ((a)*(var) + *(in_var)) % (M)
46
47#define STRING_SIMPLE_MP2_HASH_BODY(in_var, var, a, M) \
48 for ((var) = 0; *(in_var) != 0; ++(in_var)) \
49 (var) = ((a)*(var) + *(in_var)) & (M - 1)
50
51static int cache_elemsize_common_continue_func(struct cache_common_entry_ *,
52 struct cache_policy_item_ *);
53static int cache_lifetime_common_continue_func(struct cache_common_entry_ *,
54 struct cache_policy_item_ *);
55static void clear_cache_entry(struct cache_entry_ *);
56static void destroy_cache_entry(struct cache_entry_ *);
57static void destroy_cache_mp_read_session(struct cache_mp_read_session_ *);
58static void destroy_cache_mp_write_session(struct cache_mp_write_session_ *);
59static int entries_bsearch_cmp_func(const void *, const void *);
60static int entries_qsort_cmp_func(const void *, const void *);
61static struct cache_entry_ ** find_cache_entry_p(struct cache_ *,
62 const char *);
63static void flush_cache_entry(struct cache_entry_ *);
64static void flush_cache_policy(struct cache_common_entry_ *,
65 struct cache_policy_ *, struct cache_policy_ *,
66 int (*)(struct cache_common_entry_ *,
67 struct cache_policy_item_ *));
68static int ht_items_cmp_func(const void *, const void *);
69static int ht_items_fixed_size_left_cmp_func(const void *, const void *);
70static hashtable_index_t ht_item_hash_func(const void *, size_t);
71
72/*
73 * Hashing and comparing routines, that are used with the hash tables
74 */
75static int
76ht_items_cmp_func(const void *p1, const void *p2)
77{
78 struct cache_ht_item_data_ *hp1, *hp2;
79 size_t min_size;
80 int result;
81
82 hp1 = (struct cache_ht_item_data_ *)p1;
83 hp2 = (struct cache_ht_item_data_ *)p2;
84
85 assert(hp1->key != NULL);
86 assert(hp2->key != NULL);
87
88 if (hp1->key_size != hp2->key_size) {
89 min_size = (hp1->key_size < hp2->key_size) ? hp1->key_size :
90 hp2->key_size;
91 result = memcmp(hp1->key, hp2->key, min_size);
92
93 if (result == 0)
94 return ((hp1->key_size < hp2->key_size) ? -1 : 1);
95 else
96 return (result);
97 } else
98 return (memcmp(hp1->key, hp2->key, hp1->key_size));
99}
100
101static int
102ht_items_fixed_size_left_cmp_func(const void *p1, const void *p2)
103{
104 struct cache_ht_item_data_ *hp1, *hp2;
105 size_t min_size;
106 int result;
107
108 hp1 = (struct cache_ht_item_data_ *)p1;
109 hp2 = (struct cache_ht_item_data_ *)p2;
110
111 assert(hp1->key != NULL);
112 assert(hp2->key != NULL);
113
114 if (hp1->key_size != hp2->key_size) {
115 min_size = (hp1->key_size < hp2->key_size) ? hp1->key_size :
116 hp2->key_size;
117 result = memcmp(hp1->key, hp2->key, min_size);
118
119 if (result == 0)
120 if (min_size == hp1->key_size)
121 return (0);
122 else
123 return ((hp1->key_size < hp2->key_size) ? -1 : 1);
124 else
125 return (result);
126 } else
127 return (memcmp(hp1->key, hp2->key, hp1->key_size));
128}
129
130static hashtable_index_t
131ht_item_hash_func(const void *p, size_t cache_entries_size)
132{
133 struct cache_ht_item_data_ *hp;
134 size_t i;
135
136 hashtable_index_t retval;
137
138 hp = (struct cache_ht_item_data_ *)p;
139 assert(hp->key != NULL);
140
141 retval = 0;
142 for (i = 0; i < hp->key_size; ++i)
143 retval = (127 * retval + (unsigned char)hp->key[i]) %
144 cache_entries_size;
145
146 return retval;
147}
148
149HASHTABLE_PROTOTYPE(cache_ht_, cache_ht_item_, struct cache_ht_item_data_);
149HASHTABLE_GENERATE(cache_ht_, cache_ht_item_, struct cache_ht_item_data_, data,
150 ht_item_hash_func, ht_items_cmp_func);
151
152/*
153 * Routines to sort and search the entries by name
154 */
155static int
156entries_bsearch_cmp_func(const void *key, const void *ent)
157{
158
159 assert(key != NULL);
160 assert(ent != NULL);
161
162 return (strcmp((char const *)key,
163 (*(struct cache_entry_ const **)ent)->name));
164}
165
166static int
167entries_qsort_cmp_func(const void *e1, const void *e2)
168{
169
170 assert(e1 != NULL);
171 assert(e2 != NULL);
172
173 return (strcmp((*(struct cache_entry_ const **)e1)->name,
174 (*(struct cache_entry_ const **)e2)->name));
175}
176
177static struct cache_entry_ **
178find_cache_entry_p(struct cache_ *the_cache, const char *entry_name)
179{
180
181 return ((struct cache_entry_ **)(bsearch(entry_name, the_cache->entries,
182 the_cache->entries_size, sizeof(struct cache_entry_ *),
183 entries_bsearch_cmp_func)));
184}
185
186static void
187destroy_cache_mp_write_session(struct cache_mp_write_session_ *ws)
188{
189
190 struct cache_mp_data_item_ *data_item;
191
192 TRACE_IN(destroy_cache_mp_write_session);
193 assert(ws != NULL);
194 while (!TAILQ_EMPTY(&ws->items)) {
195 data_item = TAILQ_FIRST(&ws->items);
196 TAILQ_REMOVE(&ws->items, data_item, entries);
197 free(data_item->value);
198 free(data_item);
199 }
200
201 free(ws);
202 TRACE_OUT(destroy_cache_mp_write_session);
203}
204
205static void
206destroy_cache_mp_read_session(struct cache_mp_read_session_ *rs)
207{
208
209 TRACE_IN(destroy_cache_mp_read_session);
210 assert(rs != NULL);
211 free(rs);
212 TRACE_OUT(destroy_cache_mp_read_session);
213}
214
215static void
216destroy_cache_entry(struct cache_entry_ *entry)
217{
218 struct cache_common_entry_ *common_entry;
219 struct cache_mp_entry_ *mp_entry;
220 struct cache_mp_read_session_ *rs;
221 struct cache_mp_write_session_ *ws;
222 struct cache_ht_item_ *ht_item;
223 struct cache_ht_item_data_ *ht_item_data;
224
225 TRACE_IN(destroy_cache_entry);
226 assert(entry != NULL);
227
228 if (entry->params->entry_type == CET_COMMON) {
229 common_entry = (struct cache_common_entry_ *)entry;
230
231 HASHTABLE_FOREACH(&(common_entry->items), ht_item) {
232 HASHTABLE_ENTRY_FOREACH(ht_item, data, ht_item_data)
233 {
234 free(ht_item_data->key);
235 free(ht_item_data->value);
236 }
237 HASHTABLE_ENTRY_CLEAR(ht_item, data);
238 }
239
240 HASHTABLE_DESTROY(&(common_entry->items), data);
241
242 /* FIFO policy is always first */
243 destroy_cache_fifo_policy(common_entry->policies[0]);
244 switch (common_entry->common_params.policy) {
245 case CPT_LRU:
246 destroy_cache_lru_policy(common_entry->policies[1]);
247 break;
248 case CPT_LFU:
249 destroy_cache_lfu_policy(common_entry->policies[1]);
250 break;
251 default:
252 break;
253 }
254 free(common_entry->policies);
255 } else {
256 mp_entry = (struct cache_mp_entry_ *)entry;
257
258 while (!TAILQ_EMPTY(&mp_entry->ws_head)) {
259 ws = TAILQ_FIRST(&mp_entry->ws_head);
260 TAILQ_REMOVE(&mp_entry->ws_head, ws, entries);
261 destroy_cache_mp_write_session(ws);
262 }
263
264 while (!TAILQ_EMPTY(&mp_entry->rs_head)) {
265 rs = TAILQ_FIRST(&mp_entry->rs_head);
266 TAILQ_REMOVE(&mp_entry->rs_head, rs, entries);
267 destroy_cache_mp_read_session(rs);
268 }
269
270 if (mp_entry->completed_write_session != NULL)
271 destroy_cache_mp_write_session(
272 mp_entry->completed_write_session);
273
274 if (mp_entry->pending_write_session != NULL)
275 destroy_cache_mp_write_session(
276 mp_entry->pending_write_session);
277 }
278
279 free(entry->name);
280 free(entry);
281 TRACE_OUT(destroy_cache_entry);
282}
283
284static void
285clear_cache_entry(struct cache_entry_ *entry)
286{
287 struct cache_mp_entry_ *mp_entry;
288 struct cache_common_entry_ *common_entry;
289 struct cache_ht_item_ *ht_item;
290 struct cache_ht_item_data_ *ht_item_data;
291 struct cache_policy_ *policy;
292 struct cache_policy_item_ *item, *next_item;
293 size_t entry_size;
150HASHTABLE_GENERATE(cache_ht_, cache_ht_item_, struct cache_ht_item_data_, data,
151 ht_item_hash_func, ht_items_cmp_func);
152
153/*
154 * Routines to sort and search the entries by name
155 */
156static int
157entries_bsearch_cmp_func(const void *key, const void *ent)
158{
159
160 assert(key != NULL);
161 assert(ent != NULL);
162
163 return (strcmp((char const *)key,
164 (*(struct cache_entry_ const **)ent)->name));
165}
166
167static int
168entries_qsort_cmp_func(const void *e1, const void *e2)
169{
170
171 assert(e1 != NULL);
172 assert(e2 != NULL);
173
174 return (strcmp((*(struct cache_entry_ const **)e1)->name,
175 (*(struct cache_entry_ const **)e2)->name));
176}
177
178static struct cache_entry_ **
179find_cache_entry_p(struct cache_ *the_cache, const char *entry_name)
180{
181
182 return ((struct cache_entry_ **)(bsearch(entry_name, the_cache->entries,
183 the_cache->entries_size, sizeof(struct cache_entry_ *),
184 entries_bsearch_cmp_func)));
185}
186
187static void
188destroy_cache_mp_write_session(struct cache_mp_write_session_ *ws)
189{
190
191 struct cache_mp_data_item_ *data_item;
192
193 TRACE_IN(destroy_cache_mp_write_session);
194 assert(ws != NULL);
195 while (!TAILQ_EMPTY(&ws->items)) {
196 data_item = TAILQ_FIRST(&ws->items);
197 TAILQ_REMOVE(&ws->items, data_item, entries);
198 free(data_item->value);
199 free(data_item);
200 }
201
202 free(ws);
203 TRACE_OUT(destroy_cache_mp_write_session);
204}
205
206static void
207destroy_cache_mp_read_session(struct cache_mp_read_session_ *rs)
208{
209
210 TRACE_IN(destroy_cache_mp_read_session);
211 assert(rs != NULL);
212 free(rs);
213 TRACE_OUT(destroy_cache_mp_read_session);
214}
215
216static void
217destroy_cache_entry(struct cache_entry_ *entry)
218{
219 struct cache_common_entry_ *common_entry;
220 struct cache_mp_entry_ *mp_entry;
221 struct cache_mp_read_session_ *rs;
222 struct cache_mp_write_session_ *ws;
223 struct cache_ht_item_ *ht_item;
224 struct cache_ht_item_data_ *ht_item_data;
225
226 TRACE_IN(destroy_cache_entry);
227 assert(entry != NULL);
228
229 if (entry->params->entry_type == CET_COMMON) {
230 common_entry = (struct cache_common_entry_ *)entry;
231
232 HASHTABLE_FOREACH(&(common_entry->items), ht_item) {
233 HASHTABLE_ENTRY_FOREACH(ht_item, data, ht_item_data)
234 {
235 free(ht_item_data->key);
236 free(ht_item_data->value);
237 }
238 HASHTABLE_ENTRY_CLEAR(ht_item, data);
239 }
240
241 HASHTABLE_DESTROY(&(common_entry->items), data);
242
243 /* FIFO policy is always first */
244 destroy_cache_fifo_policy(common_entry->policies[0]);
245 switch (common_entry->common_params.policy) {
246 case CPT_LRU:
247 destroy_cache_lru_policy(common_entry->policies[1]);
248 break;
249 case CPT_LFU:
250 destroy_cache_lfu_policy(common_entry->policies[1]);
251 break;
252 default:
253 break;
254 }
255 free(common_entry->policies);
256 } else {
257 mp_entry = (struct cache_mp_entry_ *)entry;
258
259 while (!TAILQ_EMPTY(&mp_entry->ws_head)) {
260 ws = TAILQ_FIRST(&mp_entry->ws_head);
261 TAILQ_REMOVE(&mp_entry->ws_head, ws, entries);
262 destroy_cache_mp_write_session(ws);
263 }
264
265 while (!TAILQ_EMPTY(&mp_entry->rs_head)) {
266 rs = TAILQ_FIRST(&mp_entry->rs_head);
267 TAILQ_REMOVE(&mp_entry->rs_head, rs, entries);
268 destroy_cache_mp_read_session(rs);
269 }
270
271 if (mp_entry->completed_write_session != NULL)
272 destroy_cache_mp_write_session(
273 mp_entry->completed_write_session);
274
275 if (mp_entry->pending_write_session != NULL)
276 destroy_cache_mp_write_session(
277 mp_entry->pending_write_session);
278 }
279
280 free(entry->name);
281 free(entry);
282 TRACE_OUT(destroy_cache_entry);
283}
284
285static void
286clear_cache_entry(struct cache_entry_ *entry)
287{
288 struct cache_mp_entry_ *mp_entry;
289 struct cache_common_entry_ *common_entry;
290 struct cache_ht_item_ *ht_item;
291 struct cache_ht_item_data_ *ht_item_data;
292 struct cache_policy_ *policy;
293 struct cache_policy_item_ *item, *next_item;
294 size_t entry_size;
294 int i;
295 unsigned int i;
295
296 if (entry->params->entry_type == CET_COMMON) {
297 common_entry = (struct cache_common_entry_ *)entry;
298
299 entry_size = 0;
300 HASHTABLE_FOREACH(&(common_entry->items), ht_item) {
301 HASHTABLE_ENTRY_FOREACH(ht_item, data, ht_item_data)
302 {
303 free(ht_item_data->key);
304 free(ht_item_data->value);
305 }
306 entry_size += HASHTABLE_ENTRY_SIZE(ht_item, data);
307 HASHTABLE_ENTRY_CLEAR(ht_item, data);
308 }
309
310 common_entry->items_size -= entry_size;
311 for (i = 0; i < common_entry->policies_size; ++i) {
312 policy = common_entry->policies[i];
313
314 next_item = NULL;
315 item = policy->get_first_item_func(policy);
316 while (item != NULL) {
317 next_item = policy->get_next_item_func(policy,
318 item);
319 policy->remove_item_func(policy, item);
320 policy->destroy_item_func(item);
321 item = next_item;
322 }
323 }
324 } else {
325 mp_entry = (struct cache_mp_entry_ *)entry;
326
327 if (mp_entry->rs_size == 0) {
328 if (mp_entry->completed_write_session != NULL) {
329 destroy_cache_mp_write_session(
330 mp_entry->completed_write_session);
331 mp_entry->completed_write_session = NULL;
332 }
333
334 memset(&mp_entry->creation_time, 0,
335 sizeof(struct timeval));
336 memset(&mp_entry->last_request_time, 0,
337 sizeof(struct timeval));
338 }
339 }
340}
341
342/*
343 * When passed to the flush_cache_policy, ensures that all old elements are
344 * deleted.
345 */
346static int
347cache_lifetime_common_continue_func(struct cache_common_entry_ *entry,
348 struct cache_policy_item_ *item)
349{
350
351 return ((item->last_request_time.tv_sec - item->creation_time.tv_sec >
352 entry->common_params.max_lifetime.tv_sec) ? 1: 0);
353}
354
355/*
356 * When passed to the flush_cache_policy, ensures that all elements, that
357 * exceed the size limit, are deleted.
358 */
359static int
360cache_elemsize_common_continue_func(struct cache_common_entry_ *entry,
361 struct cache_policy_item_ *item)
362{
363
364 return ((entry->items_size > entry->common_params.satisf_elemsize) ? 1
365 : 0);
366}
367
368/*
369 * Removes the elements from the cache entry, while the continue_func returns 1.
370 */
371static void
372flush_cache_policy(struct cache_common_entry_ *entry,
373 struct cache_policy_ *policy,
374 struct cache_policy_ *connected_policy,
375 int (*continue_func)(struct cache_common_entry_ *,
376 struct cache_policy_item_ *))
377{
378 struct cache_policy_item_ *item, *next_item, *connected_item;
379 struct cache_ht_item_ *ht_item;
380 struct cache_ht_item_data_ *ht_item_data, ht_key;
381 hashtable_index_t hash;
382
383 assert(policy != NULL);
384
385 next_item = NULL;
386 item = policy->get_first_item_func(policy);
387 while ((item != NULL) && (continue_func(entry, item) == 1)) {
388 next_item = policy->get_next_item_func(policy, item);
389
390 connected_item = item->connected_item;
391 policy->remove_item_func(policy, item);
392
393 memset(&ht_key, 0, sizeof(struct cache_ht_item_data_));
394 ht_key.key = item->key;
395 ht_key.key_size = item->key_size;
396
397 hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &entry->items,
398 &ht_key);
296
297 if (entry->params->entry_type == CET_COMMON) {
298 common_entry = (struct cache_common_entry_ *)entry;
299
300 entry_size = 0;
301 HASHTABLE_FOREACH(&(common_entry->items), ht_item) {
302 HASHTABLE_ENTRY_FOREACH(ht_item, data, ht_item_data)
303 {
304 free(ht_item_data->key);
305 free(ht_item_data->value);
306 }
307 entry_size += HASHTABLE_ENTRY_SIZE(ht_item, data);
308 HASHTABLE_ENTRY_CLEAR(ht_item, data);
309 }
310
311 common_entry->items_size -= entry_size;
312 for (i = 0; i < common_entry->policies_size; ++i) {
313 policy = common_entry->policies[i];
314
315 next_item = NULL;
316 item = policy->get_first_item_func(policy);
317 while (item != NULL) {
318 next_item = policy->get_next_item_func(policy,
319 item);
320 policy->remove_item_func(policy, item);
321 policy->destroy_item_func(item);
322 item = next_item;
323 }
324 }
325 } else {
326 mp_entry = (struct cache_mp_entry_ *)entry;
327
328 if (mp_entry->rs_size == 0) {
329 if (mp_entry->completed_write_session != NULL) {
330 destroy_cache_mp_write_session(
331 mp_entry->completed_write_session);
332 mp_entry->completed_write_session = NULL;
333 }
334
335 memset(&mp_entry->creation_time, 0,
336 sizeof(struct timeval));
337 memset(&mp_entry->last_request_time, 0,
338 sizeof(struct timeval));
339 }
340 }
341}
342
343/*
344 * When passed to the flush_cache_policy, ensures that all old elements are
345 * deleted.
346 */
347static int
348cache_lifetime_common_continue_func(struct cache_common_entry_ *entry,
349 struct cache_policy_item_ *item)
350{
351
352 return ((item->last_request_time.tv_sec - item->creation_time.tv_sec >
353 entry->common_params.max_lifetime.tv_sec) ? 1: 0);
354}
355
356/*
357 * When passed to the flush_cache_policy, ensures that all elements, that
358 * exceed the size limit, are deleted.
359 */
360static int
361cache_elemsize_common_continue_func(struct cache_common_entry_ *entry,
362 struct cache_policy_item_ *item)
363{
364
365 return ((entry->items_size > entry->common_params.satisf_elemsize) ? 1
366 : 0);
367}
368
369/*
370 * Removes the elements from the cache entry, while the continue_func returns 1.
371 */
372static void
373flush_cache_policy(struct cache_common_entry_ *entry,
374 struct cache_policy_ *policy,
375 struct cache_policy_ *connected_policy,
376 int (*continue_func)(struct cache_common_entry_ *,
377 struct cache_policy_item_ *))
378{
379 struct cache_policy_item_ *item, *next_item, *connected_item;
380 struct cache_ht_item_ *ht_item;
381 struct cache_ht_item_data_ *ht_item_data, ht_key;
382 hashtable_index_t hash;
383
384 assert(policy != NULL);
385
386 next_item = NULL;
387 item = policy->get_first_item_func(policy);
388 while ((item != NULL) && (continue_func(entry, item) == 1)) {
389 next_item = policy->get_next_item_func(policy, item);
390
391 connected_item = item->connected_item;
392 policy->remove_item_func(policy, item);
393
394 memset(&ht_key, 0, sizeof(struct cache_ht_item_data_));
395 ht_key.key = item->key;
396 ht_key.key_size = item->key_size;
397
398 hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &entry->items,
399 &ht_key);
399 assert(hash >= 0);
400 assert(hash < HASHTABLE_ENTRIES_COUNT(&entry->items));
401
402 ht_item = HASHTABLE_GET_ENTRY(&(entry->items), hash);
403 ht_item_data = HASHTABLE_ENTRY_FIND(cache_ht_, ht_item,
404 &ht_key);
405 assert(ht_item_data != NULL);
406 free(ht_item_data->key);
407 free(ht_item_data->value);
408 HASHTABLE_ENTRY_REMOVE(cache_ht_, ht_item, ht_item_data);
409 --entry->items_size;
410
411 policy->destroy_item_func(item);
412
413 if (connected_item != NULL) {
414 connected_policy->remove_item_func(connected_policy,
415 connected_item);
416 connected_policy->destroy_item_func(connected_item);
417 }
418
419 item = next_item;
420 }
421}
422
423static void
424flush_cache_entry(struct cache_entry_ *entry)
425{
426 struct cache_mp_entry_ *mp_entry;
427 struct cache_common_entry_ *common_entry;
428 struct cache_policy_ *policy, *connected_policy;
429
430 connected_policy = NULL;
431 if (entry->params->entry_type == CET_COMMON) {
432 common_entry = (struct cache_common_entry_ *)entry;
433 if ((common_entry->common_params.max_lifetime.tv_sec != 0) ||
434 (common_entry->common_params.max_lifetime.tv_usec != 0)) {
435
436 policy = common_entry->policies[0];
437 if (common_entry->policies_size > 1)
438 connected_policy = common_entry->policies[1];
439
440 flush_cache_policy(common_entry, policy,
441 connected_policy,
442 cache_lifetime_common_continue_func);
443 }
444
445
446 if ((common_entry->common_params.max_elemsize != 0) &&
447 common_entry->items_size >
448 common_entry->common_params.max_elemsize) {
449
450 if (common_entry->policies_size > 1) {
451 policy = common_entry->policies[1];
452 connected_policy = common_entry->policies[0];
453 } else {
454 policy = common_entry->policies[0];
455 connected_policy = NULL;
456 }
457
458 flush_cache_policy(common_entry, policy,
459 connected_policy,
460 cache_elemsize_common_continue_func);
461 }
462 } else {
463 mp_entry = (struct cache_mp_entry_ *)entry;
464
465 if ((mp_entry->mp_params.max_lifetime.tv_sec != 0)
466 || (mp_entry->mp_params.max_lifetime.tv_usec != 0)) {
467
468 if (mp_entry->last_request_time.tv_sec -
469 mp_entry->last_request_time.tv_sec >
470 mp_entry->mp_params.max_lifetime.tv_sec)
471 clear_cache_entry(entry);
472 }
473 }
474}
475
476struct cache_ *
477init_cache(struct cache_params const *params)
478{
479 struct cache_ *retval;
480
481 TRACE_IN(init_cache);
482 assert(params != NULL);
483
484 retval = (struct cache_ *)calloc(1, sizeof(struct cache_));
485 assert(retval != NULL);
486
487 assert(params != NULL);
488 memcpy(&retval->params, params, sizeof(struct cache_params));
489
490 retval->entries = (struct cache_entry_ **)calloc(1,
491 sizeof(struct cache_entry_ *) * INITIAL_ENTRIES_CAPACITY);
492 assert(retval->entries != NULL);
493
494 retval->entries_capacity = INITIAL_ENTRIES_CAPACITY;
495 retval->entries_size = 0;
496
497 TRACE_OUT(init_cache);
498 return (retval);
499}
500
501void
502destroy_cache(struct cache_ *the_cache)
503{
504
505 TRACE_IN(destroy_cache);
506 assert(the_cache != NULL);
507
508 if (the_cache->entries != NULL) {
509 size_t i;
510 for (i = 0; i < the_cache->entries_size; ++i)
511 destroy_cache_entry(the_cache->entries[i]);
512
513 free(the_cache->entries);
514 }
515
516 free(the_cache);
517 TRACE_OUT(destroy_cache);
518}
519
520int
521register_cache_entry(struct cache_ *the_cache,
522 struct cache_entry_params const *params)
523{
524 int policies_size;
525 size_t entry_name_size;
526 struct cache_common_entry_ *new_common_entry;
527 struct cache_mp_entry_ *new_mp_entry;
528
529 TRACE_IN(register_cache_entry);
530 assert(the_cache != NULL);
531
532 if (find_cache_entry(the_cache, params->entry_name) != NULL) {
533 TRACE_OUT(register_cache_entry);
534 return (-1);
535 }
536
537 if (the_cache->entries_size == the_cache->entries_capacity) {
538 struct cache_entry_ **new_entries;
539 size_t new_capacity;
540
541 new_capacity = the_cache->entries_capacity +
542 ENTRIES_CAPACITY_STEP;
543 new_entries = (struct cache_entry_ **)calloc(1,
544 sizeof(struct cache_entry_ *) * new_capacity);
545 assert(new_entries != NULL);
546
547 memcpy(new_entries, the_cache->entries,
548 sizeof(struct cache_entry_ *)
549 * the_cache->entries_size);
550
551 free(the_cache->entries);
552 the_cache->entries = new_entries;
553 }
554
555 entry_name_size = strlen(params->entry_name) + 1;
556 switch (params->entry_type)
557 {
558 case CET_COMMON:
559 new_common_entry = (struct cache_common_entry_ *)calloc(1,
560 sizeof(struct cache_common_entry_));
561 assert(new_common_entry != NULL);
562
563 memcpy(&new_common_entry->common_params, params,
564 sizeof(struct common_cache_entry_params));
565 new_common_entry->params =
566 (struct cache_entry_params *)&new_common_entry->common_params;
567
568 new_common_entry->common_params.entry_name = (char *)calloc(1,
569 entry_name_size);
570 assert(new_common_entry->common_params.entry_name != NULL);
571 strlcpy(new_common_entry->common_params.entry_name,
572 params->entry_name, entry_name_size);
573 new_common_entry->name =
574 new_common_entry->common_params.entry_name;
575
576 HASHTABLE_INIT(&(new_common_entry->items),
577 struct cache_ht_item_data_, data,
578 new_common_entry->common_params.cache_entries_size);
579
580 if (new_common_entry->common_params.policy == CPT_FIFO)
581 policies_size = 1;
582 else
583 policies_size = 2;
584
585 new_common_entry->policies = (struct cache_policy_ **)calloc(1,
586 sizeof(struct cache_policy_ *) * policies_size);
587 assert(new_common_entry->policies != NULL);
588
589 new_common_entry->policies_size = policies_size;
590 new_common_entry->policies[0] = init_cache_fifo_policy();
591
592 if (policies_size > 1) {
593 switch (new_common_entry->common_params.policy) {
594 case CPT_LRU:
595 new_common_entry->policies[1] =
596 init_cache_lru_policy();
597 break;
598 case CPT_LFU:
599 new_common_entry->policies[1] =
600 init_cache_lfu_policy();
601 break;
602 default:
603 break;
604 }
605 }
606
607 new_common_entry->get_time_func =
608 the_cache->params.get_time_func;
609 the_cache->entries[the_cache->entries_size++] =
610 (struct cache_entry_ *)new_common_entry;
611 break;
612 case CET_MULTIPART:
613 new_mp_entry = (struct cache_mp_entry_ *)calloc(1,
614 sizeof(struct cache_mp_entry_));
615 assert(new_mp_entry != NULL);
616
617 memcpy(&new_mp_entry->mp_params, params,
618 sizeof(struct mp_cache_entry_params));
619 new_mp_entry->params =
620 (struct cache_entry_params *)&new_mp_entry->mp_params;
621
622 new_mp_entry->mp_params.entry_name = (char *)calloc(1,
623 entry_name_size);
624 assert(new_mp_entry->mp_params.entry_name != NULL);
625 strlcpy(new_mp_entry->mp_params.entry_name, params->entry_name,
626 entry_name_size);
627 new_mp_entry->name = new_mp_entry->mp_params.entry_name;
628
629 TAILQ_INIT(&new_mp_entry->ws_head);
630 TAILQ_INIT(&new_mp_entry->rs_head);
631
632 new_mp_entry->get_time_func = the_cache->params.get_time_func;
633 the_cache->entries[the_cache->entries_size++] =
634 (struct cache_entry_ *)new_mp_entry;
635 break;
636 }
637
638
639 qsort(the_cache->entries, the_cache->entries_size,
640 sizeof(struct cache_entry_ *), entries_qsort_cmp_func);
641
642 TRACE_OUT(register_cache_entry);
643 return (0);
644}
645
646int
647unregister_cache_entry(struct cache_ *the_cache, const char *entry_name)
648{
649 struct cache_entry_ **del_ent;
650
651 TRACE_IN(unregister_cache_entry);
652 assert(the_cache != NULL);
653
654 del_ent = find_cache_entry_p(the_cache, entry_name);
655 if (del_ent != NULL) {
656 destroy_cache_entry(*del_ent);
657 --the_cache->entries_size;
658
659 memmove(del_ent, del_ent + 1,
660 (&(the_cache->entries[--the_cache->entries_size]) -
661 del_ent) * sizeof(struct cache_entry_ *));
662
663 TRACE_OUT(unregister_cache_entry);
664 return (0);
665 } else {
666 TRACE_OUT(unregister_cache_entry);
667 return (-1);
668 }
669}
670
671struct cache_entry_ *
672find_cache_entry(struct cache_ *the_cache, const char *entry_name)
673{
674 struct cache_entry_ **result;
675
676 TRACE_IN(find_cache_entry);
677 result = find_cache_entry_p(the_cache, entry_name);
678
679 if (result == NULL) {
680 TRACE_OUT(find_cache_entry);
681 return (NULL);
682 } else {
683 TRACE_OUT(find_cache_entry);
684 return (*result);
685 }
686}
687
688/*
689 * Tries to read the element with the specified key from the cache. If the
690 * value_size is too small, it will be filled with the proper number, and
691 * the user will need to call cache_read again with the value buffer, that
692 * is large enough.
693 * Function returns 0 on success, -1 on error, and -2 if the value_size is too
694 * small.
695 */
696int
697cache_read(struct cache_entry_ *entry, const char *key, size_t key_size,
698 char *value, size_t *value_size)
699{
700 struct cache_common_entry_ *common_entry;
701 struct cache_ht_item_data_ item_data, *find_res;
702 struct cache_ht_item_ *item;
703 hashtable_index_t hash;
704 struct cache_policy_item_ *connected_item;
705
706 TRACE_IN(cache_read);
707 assert(entry != NULL);
708 assert(key != NULL);
709 assert(value_size != NULL);
710 assert(entry->params->entry_type == CET_COMMON);
711
712 common_entry = (struct cache_common_entry_ *)entry;
713
714 memset(&item_data, 0, sizeof(struct cache_ht_item_data_));
715 /* can't avoid the cast here */
716 item_data.key = (char *)key;
717 item_data.key_size = key_size;
718
719 hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &common_entry->items,
720 &item_data);
400 assert(hash < HASHTABLE_ENTRIES_COUNT(&entry->items));
401
402 ht_item = HASHTABLE_GET_ENTRY(&(entry->items), hash);
403 ht_item_data = HASHTABLE_ENTRY_FIND(cache_ht_, ht_item,
404 &ht_key);
405 assert(ht_item_data != NULL);
406 free(ht_item_data->key);
407 free(ht_item_data->value);
408 HASHTABLE_ENTRY_REMOVE(cache_ht_, ht_item, ht_item_data);
409 --entry->items_size;
410
411 policy->destroy_item_func(item);
412
413 if (connected_item != NULL) {
414 connected_policy->remove_item_func(connected_policy,
415 connected_item);
416 connected_policy->destroy_item_func(connected_item);
417 }
418
419 item = next_item;
420 }
421}
422
423static void
424flush_cache_entry(struct cache_entry_ *entry)
425{
426 struct cache_mp_entry_ *mp_entry;
427 struct cache_common_entry_ *common_entry;
428 struct cache_policy_ *policy, *connected_policy;
429
430 connected_policy = NULL;
431 if (entry->params->entry_type == CET_COMMON) {
432 common_entry = (struct cache_common_entry_ *)entry;
433 if ((common_entry->common_params.max_lifetime.tv_sec != 0) ||
434 (common_entry->common_params.max_lifetime.tv_usec != 0)) {
435
436 policy = common_entry->policies[0];
437 if (common_entry->policies_size > 1)
438 connected_policy = common_entry->policies[1];
439
440 flush_cache_policy(common_entry, policy,
441 connected_policy,
442 cache_lifetime_common_continue_func);
443 }
444
445
446 if ((common_entry->common_params.max_elemsize != 0) &&
447 common_entry->items_size >
448 common_entry->common_params.max_elemsize) {
449
450 if (common_entry->policies_size > 1) {
451 policy = common_entry->policies[1];
452 connected_policy = common_entry->policies[0];
453 } else {
454 policy = common_entry->policies[0];
455 connected_policy = NULL;
456 }
457
458 flush_cache_policy(common_entry, policy,
459 connected_policy,
460 cache_elemsize_common_continue_func);
461 }
462 } else {
463 mp_entry = (struct cache_mp_entry_ *)entry;
464
465 if ((mp_entry->mp_params.max_lifetime.tv_sec != 0)
466 || (mp_entry->mp_params.max_lifetime.tv_usec != 0)) {
467
468 if (mp_entry->last_request_time.tv_sec -
469 mp_entry->last_request_time.tv_sec >
470 mp_entry->mp_params.max_lifetime.tv_sec)
471 clear_cache_entry(entry);
472 }
473 }
474}
475
476struct cache_ *
477init_cache(struct cache_params const *params)
478{
479 struct cache_ *retval;
480
481 TRACE_IN(init_cache);
482 assert(params != NULL);
483
484 retval = (struct cache_ *)calloc(1, sizeof(struct cache_));
485 assert(retval != NULL);
486
487 assert(params != NULL);
488 memcpy(&retval->params, params, sizeof(struct cache_params));
489
490 retval->entries = (struct cache_entry_ **)calloc(1,
491 sizeof(struct cache_entry_ *) * INITIAL_ENTRIES_CAPACITY);
492 assert(retval->entries != NULL);
493
494 retval->entries_capacity = INITIAL_ENTRIES_CAPACITY;
495 retval->entries_size = 0;
496
497 TRACE_OUT(init_cache);
498 return (retval);
499}
500
501void
502destroy_cache(struct cache_ *the_cache)
503{
504
505 TRACE_IN(destroy_cache);
506 assert(the_cache != NULL);
507
508 if (the_cache->entries != NULL) {
509 size_t i;
510 for (i = 0; i < the_cache->entries_size; ++i)
511 destroy_cache_entry(the_cache->entries[i]);
512
513 free(the_cache->entries);
514 }
515
516 free(the_cache);
517 TRACE_OUT(destroy_cache);
518}
519
520int
521register_cache_entry(struct cache_ *the_cache,
522 struct cache_entry_params const *params)
523{
524 int policies_size;
525 size_t entry_name_size;
526 struct cache_common_entry_ *new_common_entry;
527 struct cache_mp_entry_ *new_mp_entry;
528
529 TRACE_IN(register_cache_entry);
530 assert(the_cache != NULL);
531
532 if (find_cache_entry(the_cache, params->entry_name) != NULL) {
533 TRACE_OUT(register_cache_entry);
534 return (-1);
535 }
536
537 if (the_cache->entries_size == the_cache->entries_capacity) {
538 struct cache_entry_ **new_entries;
539 size_t new_capacity;
540
541 new_capacity = the_cache->entries_capacity +
542 ENTRIES_CAPACITY_STEP;
543 new_entries = (struct cache_entry_ **)calloc(1,
544 sizeof(struct cache_entry_ *) * new_capacity);
545 assert(new_entries != NULL);
546
547 memcpy(new_entries, the_cache->entries,
548 sizeof(struct cache_entry_ *)
549 * the_cache->entries_size);
550
551 free(the_cache->entries);
552 the_cache->entries = new_entries;
553 }
554
555 entry_name_size = strlen(params->entry_name) + 1;
556 switch (params->entry_type)
557 {
558 case CET_COMMON:
559 new_common_entry = (struct cache_common_entry_ *)calloc(1,
560 sizeof(struct cache_common_entry_));
561 assert(new_common_entry != NULL);
562
563 memcpy(&new_common_entry->common_params, params,
564 sizeof(struct common_cache_entry_params));
565 new_common_entry->params =
566 (struct cache_entry_params *)&new_common_entry->common_params;
567
568 new_common_entry->common_params.entry_name = (char *)calloc(1,
569 entry_name_size);
570 assert(new_common_entry->common_params.entry_name != NULL);
571 strlcpy(new_common_entry->common_params.entry_name,
572 params->entry_name, entry_name_size);
573 new_common_entry->name =
574 new_common_entry->common_params.entry_name;
575
576 HASHTABLE_INIT(&(new_common_entry->items),
577 struct cache_ht_item_data_, data,
578 new_common_entry->common_params.cache_entries_size);
579
580 if (new_common_entry->common_params.policy == CPT_FIFO)
581 policies_size = 1;
582 else
583 policies_size = 2;
584
585 new_common_entry->policies = (struct cache_policy_ **)calloc(1,
586 sizeof(struct cache_policy_ *) * policies_size);
587 assert(new_common_entry->policies != NULL);
588
589 new_common_entry->policies_size = policies_size;
590 new_common_entry->policies[0] = init_cache_fifo_policy();
591
592 if (policies_size > 1) {
593 switch (new_common_entry->common_params.policy) {
594 case CPT_LRU:
595 new_common_entry->policies[1] =
596 init_cache_lru_policy();
597 break;
598 case CPT_LFU:
599 new_common_entry->policies[1] =
600 init_cache_lfu_policy();
601 break;
602 default:
603 break;
604 }
605 }
606
607 new_common_entry->get_time_func =
608 the_cache->params.get_time_func;
609 the_cache->entries[the_cache->entries_size++] =
610 (struct cache_entry_ *)new_common_entry;
611 break;
612 case CET_MULTIPART:
613 new_mp_entry = (struct cache_mp_entry_ *)calloc(1,
614 sizeof(struct cache_mp_entry_));
615 assert(new_mp_entry != NULL);
616
617 memcpy(&new_mp_entry->mp_params, params,
618 sizeof(struct mp_cache_entry_params));
619 new_mp_entry->params =
620 (struct cache_entry_params *)&new_mp_entry->mp_params;
621
622 new_mp_entry->mp_params.entry_name = (char *)calloc(1,
623 entry_name_size);
624 assert(new_mp_entry->mp_params.entry_name != NULL);
625 strlcpy(new_mp_entry->mp_params.entry_name, params->entry_name,
626 entry_name_size);
627 new_mp_entry->name = new_mp_entry->mp_params.entry_name;
628
629 TAILQ_INIT(&new_mp_entry->ws_head);
630 TAILQ_INIT(&new_mp_entry->rs_head);
631
632 new_mp_entry->get_time_func = the_cache->params.get_time_func;
633 the_cache->entries[the_cache->entries_size++] =
634 (struct cache_entry_ *)new_mp_entry;
635 break;
636 }
637
638
639 qsort(the_cache->entries, the_cache->entries_size,
640 sizeof(struct cache_entry_ *), entries_qsort_cmp_func);
641
642 TRACE_OUT(register_cache_entry);
643 return (0);
644}
645
646int
647unregister_cache_entry(struct cache_ *the_cache, const char *entry_name)
648{
649 struct cache_entry_ **del_ent;
650
651 TRACE_IN(unregister_cache_entry);
652 assert(the_cache != NULL);
653
654 del_ent = find_cache_entry_p(the_cache, entry_name);
655 if (del_ent != NULL) {
656 destroy_cache_entry(*del_ent);
657 --the_cache->entries_size;
658
659 memmove(del_ent, del_ent + 1,
660 (&(the_cache->entries[--the_cache->entries_size]) -
661 del_ent) * sizeof(struct cache_entry_ *));
662
663 TRACE_OUT(unregister_cache_entry);
664 return (0);
665 } else {
666 TRACE_OUT(unregister_cache_entry);
667 return (-1);
668 }
669}
670
671struct cache_entry_ *
672find_cache_entry(struct cache_ *the_cache, const char *entry_name)
673{
674 struct cache_entry_ **result;
675
676 TRACE_IN(find_cache_entry);
677 result = find_cache_entry_p(the_cache, entry_name);
678
679 if (result == NULL) {
680 TRACE_OUT(find_cache_entry);
681 return (NULL);
682 } else {
683 TRACE_OUT(find_cache_entry);
684 return (*result);
685 }
686}
687
688/*
689 * Tries to read the element with the specified key from the cache. If the
690 * value_size is too small, it will be filled with the proper number, and
691 * the user will need to call cache_read again with the value buffer, that
692 * is large enough.
693 * Function returns 0 on success, -1 on error, and -2 if the value_size is too
694 * small.
695 */
696int
697cache_read(struct cache_entry_ *entry, const char *key, size_t key_size,
698 char *value, size_t *value_size)
699{
700 struct cache_common_entry_ *common_entry;
701 struct cache_ht_item_data_ item_data, *find_res;
702 struct cache_ht_item_ *item;
703 hashtable_index_t hash;
704 struct cache_policy_item_ *connected_item;
705
706 TRACE_IN(cache_read);
707 assert(entry != NULL);
708 assert(key != NULL);
709 assert(value_size != NULL);
710 assert(entry->params->entry_type == CET_COMMON);
711
712 common_entry = (struct cache_common_entry_ *)entry;
713
714 memset(&item_data, 0, sizeof(struct cache_ht_item_data_));
715 /* can't avoid the cast here */
716 item_data.key = (char *)key;
717 item_data.key_size = key_size;
718
719 hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &common_entry->items,
720 &item_data);
721 assert(hash >= 0);
722 assert(hash < HASHTABLE_ENTRIES_COUNT(&common_entry->items));
723
724 item = HASHTABLE_GET_ENTRY(&(common_entry->items), hash);
725 find_res = HASHTABLE_ENTRY_FIND(cache_ht_, item, &item_data);
726 if (find_res == NULL) {
727 TRACE_OUT(cache_read);
728 return (-1);
729 }
730
731 if ((common_entry->common_params.max_lifetime.tv_sec != 0) ||
732 (common_entry->common_params.max_lifetime.tv_usec != 0)) {
733
734 if (find_res->fifo_policy_item->last_request_time.tv_sec -
735 find_res->fifo_policy_item->creation_time.tv_sec >
736 common_entry->common_params.max_lifetime.tv_sec) {
737
738 free(find_res->key);
739 free(find_res->value);
740
741 connected_item =
742 find_res->fifo_policy_item->connected_item;
743 if (connected_item != NULL) {
744 common_entry->policies[1]->remove_item_func(
745 common_entry->policies[1],
746 connected_item);
747 common_entry->policies[1]->destroy_item_func(
748 connected_item);
749 }
750
751 common_entry->policies[0]->remove_item_func(
752 common_entry->policies[0],
753 find_res->fifo_policy_item);
754 common_entry->policies[0]->destroy_item_func(
755 find_res->fifo_policy_item);
756
757 HASHTABLE_ENTRY_REMOVE(cache_ht_, item, find_res);
758 --common_entry->items_size;
759 }
760 }
761
762 if ((*value_size < find_res->value_size) || (value == NULL)) {
763 *value_size = find_res->value_size;
764 TRACE_OUT(cache_read);
765 return (-2);
766 }
767
768 *value_size = find_res->value_size;
769 memcpy(value, find_res->value, find_res->value_size);
770
771 ++find_res->fifo_policy_item->request_count;
772 common_entry->get_time_func(
773 &find_res->fifo_policy_item->last_request_time);
774 common_entry->policies[0]->update_item_func(common_entry->policies[0],
775 find_res->fifo_policy_item);
776
777 if (find_res->fifo_policy_item->connected_item != NULL) {
778 connected_item = find_res->fifo_policy_item->connected_item;
779 memcpy(&connected_item->last_request_time,
780 &find_res->fifo_policy_item->last_request_time,
781 sizeof(struct timeval));
782 connected_item->request_count =
783 find_res->fifo_policy_item->request_count;
784
785 common_entry->policies[1]->update_item_func(
786 common_entry->policies[1], connected_item);
787 }
788
789 TRACE_OUT(cache_read);
790 return (0);
791}
792
793/*
794 * Writes the value with the specified key into the cache entry.
795 * Functions returns 0 on success, and -1 on error.
796 */
797int
798cache_write(struct cache_entry_ *entry, const char *key, size_t key_size,
799 char const *value, size_t value_size)
800{
801 struct cache_common_entry_ *common_entry;
802 struct cache_ht_item_data_ item_data, *find_res;
803 struct cache_ht_item_ *item;
804 hashtable_index_t hash;
805
806 struct cache_policy_ *policy, *connected_policy;
807 struct cache_policy_item_ *policy_item;
808 struct cache_policy_item_ *connected_policy_item;
809
810 TRACE_IN(cache_write);
811 assert(entry != NULL);
812 assert(key != NULL);
813 assert(value != NULL);
814 assert(entry->params->entry_type == CET_COMMON);
815
816 common_entry = (struct cache_common_entry_ *)entry;
817
818 memset(&item_data, 0, sizeof(struct cache_ht_item_data_));
819 /* can't avoid the cast here */
820 item_data.key = (char *)key;
821 item_data.key_size = key_size;
822
823 hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &common_entry->items,
824 &item_data);
721 assert(hash < HASHTABLE_ENTRIES_COUNT(&common_entry->items));
722
723 item = HASHTABLE_GET_ENTRY(&(common_entry->items), hash);
724 find_res = HASHTABLE_ENTRY_FIND(cache_ht_, item, &item_data);
725 if (find_res == NULL) {
726 TRACE_OUT(cache_read);
727 return (-1);
728 }
729
730 if ((common_entry->common_params.max_lifetime.tv_sec != 0) ||
731 (common_entry->common_params.max_lifetime.tv_usec != 0)) {
732
733 if (find_res->fifo_policy_item->last_request_time.tv_sec -
734 find_res->fifo_policy_item->creation_time.tv_sec >
735 common_entry->common_params.max_lifetime.tv_sec) {
736
737 free(find_res->key);
738 free(find_res->value);
739
740 connected_item =
741 find_res->fifo_policy_item->connected_item;
742 if (connected_item != NULL) {
743 common_entry->policies[1]->remove_item_func(
744 common_entry->policies[1],
745 connected_item);
746 common_entry->policies[1]->destroy_item_func(
747 connected_item);
748 }
749
750 common_entry->policies[0]->remove_item_func(
751 common_entry->policies[0],
752 find_res->fifo_policy_item);
753 common_entry->policies[0]->destroy_item_func(
754 find_res->fifo_policy_item);
755
756 HASHTABLE_ENTRY_REMOVE(cache_ht_, item, find_res);
757 --common_entry->items_size;
758 }
759 }
760
761 if ((*value_size < find_res->value_size) || (value == NULL)) {
762 *value_size = find_res->value_size;
763 TRACE_OUT(cache_read);
764 return (-2);
765 }
766
767 *value_size = find_res->value_size;
768 memcpy(value, find_res->value, find_res->value_size);
769
770 ++find_res->fifo_policy_item->request_count;
771 common_entry->get_time_func(
772 &find_res->fifo_policy_item->last_request_time);
773 common_entry->policies[0]->update_item_func(common_entry->policies[0],
774 find_res->fifo_policy_item);
775
776 if (find_res->fifo_policy_item->connected_item != NULL) {
777 connected_item = find_res->fifo_policy_item->connected_item;
778 memcpy(&connected_item->last_request_time,
779 &find_res->fifo_policy_item->last_request_time,
780 sizeof(struct timeval));
781 connected_item->request_count =
782 find_res->fifo_policy_item->request_count;
783
784 common_entry->policies[1]->update_item_func(
785 common_entry->policies[1], connected_item);
786 }
787
788 TRACE_OUT(cache_read);
789 return (0);
790}
791
792/*
793 * Writes the value with the specified key into the cache entry.
794 * Functions returns 0 on success, and -1 on error.
795 */
796int
797cache_write(struct cache_entry_ *entry, const char *key, size_t key_size,
798 char const *value, size_t value_size)
799{
800 struct cache_common_entry_ *common_entry;
801 struct cache_ht_item_data_ item_data, *find_res;
802 struct cache_ht_item_ *item;
803 hashtable_index_t hash;
804
805 struct cache_policy_ *policy, *connected_policy;
806 struct cache_policy_item_ *policy_item;
807 struct cache_policy_item_ *connected_policy_item;
808
809 TRACE_IN(cache_write);
810 assert(entry != NULL);
811 assert(key != NULL);
812 assert(value != NULL);
813 assert(entry->params->entry_type == CET_COMMON);
814
815 common_entry = (struct cache_common_entry_ *)entry;
816
817 memset(&item_data, 0, sizeof(struct cache_ht_item_data_));
818 /* can't avoid the cast here */
819 item_data.key = (char *)key;
820 item_data.key_size = key_size;
821
822 hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &common_entry->items,
823 &item_data);
825 assert(hash >= 0);
826 assert(hash < HASHTABLE_ENTRIES_COUNT(&common_entry->items));
827
828 item = HASHTABLE_GET_ENTRY(&(common_entry->items), hash);
829 find_res = HASHTABLE_ENTRY_FIND(cache_ht_, item, &item_data);
830 if (find_res != NULL) {
831 TRACE_OUT(cache_write);
832 return (-1);
833 }
834
835 item_data.key = (char *)malloc(key_size);
836 memcpy(item_data.key, key, key_size);
837
838 item_data.value = (char *)malloc(value_size);
839 assert(item_data.value != NULL);
840
841 memcpy(item_data.value, value, value_size);
842 item_data.value_size = value_size;
843
844 policy_item = common_entry->policies[0]->create_item_func();
845 policy_item->key = item_data.key;
846 policy_item->key_size = item_data.key_size;
847 common_entry->get_time_func(&policy_item->creation_time);
848
849 if (common_entry->policies_size > 1) {
850 connected_policy_item =
851 common_entry->policies[1]->create_item_func();
852 memcpy(&connected_policy_item->creation_time,
853 &policy_item->creation_time,
854 sizeof(struct timeval));
855 connected_policy_item->key = policy_item->key;
856 connected_policy_item->key_size = policy_item->key_size;
857
858 connected_policy_item->connected_item = policy_item;
859 policy_item->connected_item = connected_policy_item;
860 }
861
862 item_data.fifo_policy_item = policy_item;
863
864 common_entry->policies[0]->add_item_func(common_entry->policies[0],
865 policy_item);
866 if (common_entry->policies_size > 1)
867 common_entry->policies[1]->add_item_func(
868 common_entry->policies[1], connected_policy_item);
869
870 HASHTABLE_ENTRY_STORE(cache_ht_, item, &item_data);
871 ++common_entry->items_size;
872
873 if ((common_entry->common_params.max_elemsize != 0) &&
874 (common_entry->items_size >
875 common_entry->common_params.max_elemsize)) {
876 if (common_entry->policies_size > 1) {
877 policy = common_entry->policies[1];
878 connected_policy = common_entry->policies[0];
879 } else {
880 policy = common_entry->policies[0];
881 connected_policy = NULL;
882 }
883
884 flush_cache_policy(common_entry, policy, connected_policy,
885 cache_elemsize_common_continue_func);
886 }
887
888 TRACE_OUT(cache_write);
889 return (0);
890}
891
892/*
893 * Initializes the write session for the specified multipart entry. This
894 * session then should be filled with data either committed or abandoned by
895 * using close_cache_mp_write_session or abandon_cache_mp_write_session
896 * respectively.
897 * Returns NULL on errors (when there are too many opened write sessions for
898 * the entry).
899 */
900struct cache_mp_write_session_ *
901open_cache_mp_write_session(struct cache_entry_ *entry)
902{
903 struct cache_mp_entry_ *mp_entry;
904 struct cache_mp_write_session_ *retval;
905
906 TRACE_IN(open_cache_mp_write_session);
907 assert(entry != NULL);
908 assert(entry->params->entry_type == CET_MULTIPART);
909 mp_entry = (struct cache_mp_entry_ *)entry;
910
911 if ((mp_entry->mp_params.max_sessions > 0) &&
912 (mp_entry->ws_size == mp_entry->mp_params.max_sessions)) {
913 TRACE_OUT(open_cache_mp_write_session);
914 return (NULL);
915 }
916
917 retval = (struct cache_mp_write_session_ *)calloc(1,
918 sizeof(struct cache_mp_write_session_));
919 assert(retval != NULL);
920
921 TAILQ_INIT(&retval->items);
922 retval->parent_entry = mp_entry;
923
924 TAILQ_INSERT_HEAD(&mp_entry->ws_head, retval, entries);
925 ++mp_entry->ws_size;
926
927 TRACE_OUT(open_cache_mp_write_session);
928 return (retval);
929}
930
931/*
932 * Writes data to the specified session. Return 0 on success and -1 on errors
933 * (when write session size limit is exceeded).
934 */
935int
936cache_mp_write(struct cache_mp_write_session_ *ws, char *data,
937 size_t data_size)
938{
939 struct cache_mp_data_item_ *new_item;
940
941 TRACE_IN(cache_mp_write);
942 assert(ws != NULL);
943 assert(ws->parent_entry != NULL);
944 assert(ws->parent_entry->params->entry_type == CET_MULTIPART);
945
946 if ((ws->parent_entry->mp_params.max_elemsize > 0) &&
947 (ws->parent_entry->mp_params.max_elemsize == ws->items_size)) {
948 TRACE_OUT(cache_mp_write);
949 return (-1);
950 }
951
952 new_item = (struct cache_mp_data_item_ *)calloc(1,
953 sizeof(struct cache_mp_data_item_));
954 assert(new_item != NULL);
955
956 new_item->value = (char *)malloc(data_size);
957 assert(new_item->value != NULL);
958 memcpy(new_item->value, data, data_size);
959 new_item->value_size = data_size;
960
961 TAILQ_INSERT_TAIL(&ws->items, new_item, entries);
962 ++ws->items_size;
963
964 TRACE_OUT(cache_mp_write);
965 return (0);
966}
967
968/*
969 * Abandons the write session and frees all the connected resources.
970 */
971void
972abandon_cache_mp_write_session(struct cache_mp_write_session_ *ws)
973{
974
975 TRACE_IN(abandon_cache_mp_write_session);
976 assert(ws != NULL);
977 assert(ws->parent_entry != NULL);
978 assert(ws->parent_entry->params->entry_type == CET_MULTIPART);
979
980 TAILQ_REMOVE(&ws->parent_entry->ws_head, ws, entries);
981 --ws->parent_entry->ws_size;
982
983 destroy_cache_mp_write_session(ws);
984 TRACE_OUT(abandon_cache_mp_write_session);
985}
986
987/*
988 * Commits the session to the entry, for which it was created.
989 */
990void
991close_cache_mp_write_session(struct cache_mp_write_session_ *ws)
992{
993
994 TRACE_IN(close_cache_mp_write_session);
995 assert(ws != NULL);
996 assert(ws->parent_entry != NULL);
997 assert(ws->parent_entry->params->entry_type == CET_MULTIPART);
998
999 TAILQ_REMOVE(&ws->parent_entry->ws_head, ws, entries);
1000 --ws->parent_entry->ws_size;
1001
1002 if (ws->parent_entry->completed_write_session == NULL) {
1003 /*
1004 * If there is no completed session yet, this will be the one
1005 */
1006 ws->parent_entry->get_time_func(
1007 &ws->parent_entry->creation_time);
1008 ws->parent_entry->completed_write_session = ws;
1009 } else {
1010 /*
1011 * If there is a completed session, then we'll save our session
1012 * as a pending session. If there is already a pending session,
1013 * it would be destroyed.
1014 */
1015 if (ws->parent_entry->pending_write_session != NULL)
1016 destroy_cache_mp_write_session(
1017 ws->parent_entry->pending_write_session);
1018
1019 ws->parent_entry->pending_write_session = ws;
1020 }
1021 TRACE_OUT(close_cache_mp_write_session);
1022}
1023
1024/*
1025 * Opens read session for the specified entry. Returns NULL on errors (when
1026 * there are no data in the entry, or the data are obsolete).
1027 */
1028struct cache_mp_read_session_ *
1029open_cache_mp_read_session(struct cache_entry_ *entry)
1030{
1031 struct cache_mp_entry_ *mp_entry;
1032 struct cache_mp_read_session_ *retval;
1033
1034 TRACE_IN(open_cache_mp_read_session);
1035 assert(entry != NULL);
1036 assert(entry->params->entry_type == CET_MULTIPART);
1037 mp_entry = (struct cache_mp_entry_ *)entry;
1038
1039 if (mp_entry->completed_write_session == NULL) {
1040 TRACE_OUT(open_cache_mp_read_session);
1041 return (NULL);
1042 }
1043
1044 if ((mp_entry->mp_params.max_lifetime.tv_sec != 0)
1045 || (mp_entry->mp_params.max_lifetime.tv_usec != 0)) {
1046 if (mp_entry->last_request_time.tv_sec -
1047 mp_entry->last_request_time.tv_sec >
1048 mp_entry->mp_params.max_lifetime.tv_sec) {
1049 flush_cache_entry(entry);
1050 TRACE_OUT(open_cache_mp_read_session);
1051 return (NULL);
1052 }
1053 }
1054
1055 retval = (struct cache_mp_read_session_ *)calloc(1,
1056 sizeof(struct cache_mp_read_session_));
1057 assert(retval != NULL);
1058
1059 retval->parent_entry = mp_entry;
1060 retval->current_item = TAILQ_FIRST(
1061 &mp_entry->completed_write_session->items);
1062
1063 TAILQ_INSERT_HEAD(&mp_entry->rs_head, retval, entries);
1064 ++mp_entry->rs_size;
1065
1066 mp_entry->get_time_func(&mp_entry->last_request_time);
1067 TRACE_OUT(open_cache_mp_read_session);
1068 return (retval);
1069}
1070
1071/*
1072 * Reads the data from the read session - step by step.
1073 * Returns 0 on success, -1 on error (when there are no more data), and -2 if
1074 * the data_size is too small. In the last case, data_size would be filled
1075 * the proper value.
1076 */
1077int
1078cache_mp_read(struct cache_mp_read_session_ *rs, char *data, size_t *data_size)
1079{
1080
1081 TRACE_IN(cache_mp_read);
1082 assert(rs != NULL);
1083
1084 if (rs->current_item == NULL) {
1085 TRACE_OUT(cache_mp_read);
1086 return (-1);
1087 }
1088
1089 if (rs->current_item->value_size > *data_size) {
1090 *data_size = rs->current_item->value_size;
1091 if (data == NULL) {
1092 TRACE_OUT(cache_mp_read);
1093 return (0);
1094 }
1095
1096 TRACE_OUT(cache_mp_read);
1097 return (-2);
1098 }
1099
1100 *data_size = rs->current_item->value_size;
1101 memcpy(data, rs->current_item->value, rs->current_item->value_size);
1102 rs->current_item = TAILQ_NEXT(rs->current_item, entries);
1103
1104 TRACE_OUT(cache_mp_read);
1105 return (0);
1106}
1107
1108/*
1109 * Closes the read session. If there are no more read sessions and there is
1110 * a pending write session, it will be committed and old
1111 * completed_write_session will be destroyed.
1112 */
1113void
1114close_cache_mp_read_session(struct cache_mp_read_session_ *rs)
1115{
1116
1117 TRACE_IN(close_cache_mp_read_session);
1118 assert(rs != NULL);
1119 assert(rs->parent_entry != NULL);
1120
1121 TAILQ_REMOVE(&rs->parent_entry->rs_head, rs, entries);
1122 --rs->parent_entry->rs_size;
1123
1124 if ((rs->parent_entry->rs_size == 0) &&
1125 (rs->parent_entry->pending_write_session != NULL)) {
1126 destroy_cache_mp_write_session(
1127 rs->parent_entry->completed_write_session);
1128 rs->parent_entry->completed_write_session =
1129 rs->parent_entry->pending_write_session;
1130 rs->parent_entry->pending_write_session = NULL;
1131 }
1132
1133 destroy_cache_mp_read_session(rs);
1134 TRACE_OUT(close_cache_mp_read_session);
1135}
1136
1137int
1138transform_cache_entry(struct cache_entry_ *entry,
1139 enum cache_transformation_t transformation)
1140{
1141
1142 TRACE_IN(transform_cache_entry);
1143 switch (transformation) {
1144 case CTT_CLEAR:
1145 clear_cache_entry(entry);
1146 TRACE_OUT(transform_cache_entry);
1147 return (0);
1148 case CTT_FLUSH:
1149 flush_cache_entry(entry);
1150 TRACE_OUT(transform_cache_entry);
1151 return (0);
1152 default:
1153 TRACE_OUT(transform_cache_entry);
1154 return (-1);
1155 }
1156}
1157
1158int
1159transform_cache_entry_part(struct cache_entry_ *entry,
1160 enum cache_transformation_t transformation, const char *key_part,
1161 size_t key_part_size, enum part_position_t part_position)
1162{
1163 struct cache_common_entry_ *common_entry;
1164 struct cache_ht_item_ *ht_item;
1165 struct cache_ht_item_data_ *ht_item_data, ht_key;
1166
1167 struct cache_policy_item_ *item, *connected_item;
1168
1169 TRACE_IN(transform_cache_entry_part);
1170 if (entry->params->entry_type != CET_COMMON) {
1171 TRACE_OUT(transform_cache_entry_part);
1172 return (-1);
1173 }
1174
1175 if (transformation != CTT_CLEAR) {
1176 TRACE_OUT(transform_cache_entry_part);
1177 return (-1);
1178 }
1179
1180 memset(&ht_key, 0, sizeof(struct cache_ht_item_data_));
1181 ht_key.key = (char *)key_part; /* can't avoid casting here */
1182 ht_key.key_size = key_part_size;
1183
1184 common_entry = (struct cache_common_entry_ *)entry;
1185 HASHTABLE_FOREACH(&(common_entry->items), ht_item) {
1186 do {
1187 ht_item_data = HASHTABLE_ENTRY_FIND_SPECIAL(cache_ht_,
1188 ht_item, &ht_key,
1189 ht_items_fixed_size_left_cmp_func);
1190
1191 if (ht_item_data != NULL) {
1192 item = ht_item_data->fifo_policy_item;
1193 connected_item = item->connected_item;
1194
1195 common_entry->policies[0]->remove_item_func(
1196 common_entry->policies[0],
1197 item);
1198
1199 free(ht_item_data->key);
1200 free(ht_item_data->value);
1201 HASHTABLE_ENTRY_REMOVE(cache_ht_, ht_item,
1202 ht_item_data);
1203 --common_entry->items_size;
1204
1205 common_entry->policies[0]->destroy_item_func(
1206 item);
1207 if (common_entry->policies_size == 2) {
1208 common_entry->policies[1]->remove_item_func(
1209 common_entry->policies[1],
1210 connected_item);
1211 common_entry->policies[1]->destroy_item_func(
1212 connected_item);
1213 }
1214 }
1215 } while (ht_item_data != NULL);
1216 }
1217
1218 TRACE_OUT(transform_cache_entry_part);
1219 return (0);
1220}
824 assert(hash < HASHTABLE_ENTRIES_COUNT(&common_entry->items));
825
826 item = HASHTABLE_GET_ENTRY(&(common_entry->items), hash);
827 find_res = HASHTABLE_ENTRY_FIND(cache_ht_, item, &item_data);
828 if (find_res != NULL) {
829 TRACE_OUT(cache_write);
830 return (-1);
831 }
832
833 item_data.key = (char *)malloc(key_size);
834 memcpy(item_data.key, key, key_size);
835
836 item_data.value = (char *)malloc(value_size);
837 assert(item_data.value != NULL);
838
839 memcpy(item_data.value, value, value_size);
840 item_data.value_size = value_size;
841
842 policy_item = common_entry->policies[0]->create_item_func();
843 policy_item->key = item_data.key;
844 policy_item->key_size = item_data.key_size;
845 common_entry->get_time_func(&policy_item->creation_time);
846
847 if (common_entry->policies_size > 1) {
848 connected_policy_item =
849 common_entry->policies[1]->create_item_func();
850 memcpy(&connected_policy_item->creation_time,
851 &policy_item->creation_time,
852 sizeof(struct timeval));
853 connected_policy_item->key = policy_item->key;
854 connected_policy_item->key_size = policy_item->key_size;
855
856 connected_policy_item->connected_item = policy_item;
857 policy_item->connected_item = connected_policy_item;
858 }
859
860 item_data.fifo_policy_item = policy_item;
861
862 common_entry->policies[0]->add_item_func(common_entry->policies[0],
863 policy_item);
864 if (common_entry->policies_size > 1)
865 common_entry->policies[1]->add_item_func(
866 common_entry->policies[1], connected_policy_item);
867
868 HASHTABLE_ENTRY_STORE(cache_ht_, item, &item_data);
869 ++common_entry->items_size;
870
871 if ((common_entry->common_params.max_elemsize != 0) &&
872 (common_entry->items_size >
873 common_entry->common_params.max_elemsize)) {
874 if (common_entry->policies_size > 1) {
875 policy = common_entry->policies[1];
876 connected_policy = common_entry->policies[0];
877 } else {
878 policy = common_entry->policies[0];
879 connected_policy = NULL;
880 }
881
882 flush_cache_policy(common_entry, policy, connected_policy,
883 cache_elemsize_common_continue_func);
884 }
885
886 TRACE_OUT(cache_write);
887 return (0);
888}
889
890/*
891 * Initializes the write session for the specified multipart entry. This
892 * session then should be filled with data either committed or abandoned by
893 * using close_cache_mp_write_session or abandon_cache_mp_write_session
894 * respectively.
895 * Returns NULL on errors (when there are too many opened write sessions for
896 * the entry).
897 */
898struct cache_mp_write_session_ *
899open_cache_mp_write_session(struct cache_entry_ *entry)
900{
901 struct cache_mp_entry_ *mp_entry;
902 struct cache_mp_write_session_ *retval;
903
904 TRACE_IN(open_cache_mp_write_session);
905 assert(entry != NULL);
906 assert(entry->params->entry_type == CET_MULTIPART);
907 mp_entry = (struct cache_mp_entry_ *)entry;
908
909 if ((mp_entry->mp_params.max_sessions > 0) &&
910 (mp_entry->ws_size == mp_entry->mp_params.max_sessions)) {
911 TRACE_OUT(open_cache_mp_write_session);
912 return (NULL);
913 }
914
915 retval = (struct cache_mp_write_session_ *)calloc(1,
916 sizeof(struct cache_mp_write_session_));
917 assert(retval != NULL);
918
919 TAILQ_INIT(&retval->items);
920 retval->parent_entry = mp_entry;
921
922 TAILQ_INSERT_HEAD(&mp_entry->ws_head, retval, entries);
923 ++mp_entry->ws_size;
924
925 TRACE_OUT(open_cache_mp_write_session);
926 return (retval);
927}
928
929/*
930 * Writes data to the specified session. Return 0 on success and -1 on errors
931 * (when write session size limit is exceeded).
932 */
933int
934cache_mp_write(struct cache_mp_write_session_ *ws, char *data,
935 size_t data_size)
936{
937 struct cache_mp_data_item_ *new_item;
938
939 TRACE_IN(cache_mp_write);
940 assert(ws != NULL);
941 assert(ws->parent_entry != NULL);
942 assert(ws->parent_entry->params->entry_type == CET_MULTIPART);
943
944 if ((ws->parent_entry->mp_params.max_elemsize > 0) &&
945 (ws->parent_entry->mp_params.max_elemsize == ws->items_size)) {
946 TRACE_OUT(cache_mp_write);
947 return (-1);
948 }
949
950 new_item = (struct cache_mp_data_item_ *)calloc(1,
951 sizeof(struct cache_mp_data_item_));
952 assert(new_item != NULL);
953
954 new_item->value = (char *)malloc(data_size);
955 assert(new_item->value != NULL);
956 memcpy(new_item->value, data, data_size);
957 new_item->value_size = data_size;
958
959 TAILQ_INSERT_TAIL(&ws->items, new_item, entries);
960 ++ws->items_size;
961
962 TRACE_OUT(cache_mp_write);
963 return (0);
964}
965
966/*
967 * Abandons the write session and frees all the connected resources.
968 */
969void
970abandon_cache_mp_write_session(struct cache_mp_write_session_ *ws)
971{
972
973 TRACE_IN(abandon_cache_mp_write_session);
974 assert(ws != NULL);
975 assert(ws->parent_entry != NULL);
976 assert(ws->parent_entry->params->entry_type == CET_MULTIPART);
977
978 TAILQ_REMOVE(&ws->parent_entry->ws_head, ws, entries);
979 --ws->parent_entry->ws_size;
980
981 destroy_cache_mp_write_session(ws);
982 TRACE_OUT(abandon_cache_mp_write_session);
983}
984
985/*
986 * Commits the session to the entry, for which it was created.
987 */
988void
989close_cache_mp_write_session(struct cache_mp_write_session_ *ws)
990{
991
992 TRACE_IN(close_cache_mp_write_session);
993 assert(ws != NULL);
994 assert(ws->parent_entry != NULL);
995 assert(ws->parent_entry->params->entry_type == CET_MULTIPART);
996
997 TAILQ_REMOVE(&ws->parent_entry->ws_head, ws, entries);
998 --ws->parent_entry->ws_size;
999
1000 if (ws->parent_entry->completed_write_session == NULL) {
1001 /*
1002 * If there is no completed session yet, this will be the one
1003 */
1004 ws->parent_entry->get_time_func(
1005 &ws->parent_entry->creation_time);
1006 ws->parent_entry->completed_write_session = ws;
1007 } else {
1008 /*
1009 * If there is a completed session, then we'll save our session
1010 * as a pending session. If there is already a pending session,
1011 * it would be destroyed.
1012 */
1013 if (ws->parent_entry->pending_write_session != NULL)
1014 destroy_cache_mp_write_session(
1015 ws->parent_entry->pending_write_session);
1016
1017 ws->parent_entry->pending_write_session = ws;
1018 }
1019 TRACE_OUT(close_cache_mp_write_session);
1020}
1021
1022/*
1023 * Opens read session for the specified entry. Returns NULL on errors (when
1024 * there are no data in the entry, or the data are obsolete).
1025 */
1026struct cache_mp_read_session_ *
1027open_cache_mp_read_session(struct cache_entry_ *entry)
1028{
1029 struct cache_mp_entry_ *mp_entry;
1030 struct cache_mp_read_session_ *retval;
1031
1032 TRACE_IN(open_cache_mp_read_session);
1033 assert(entry != NULL);
1034 assert(entry->params->entry_type == CET_MULTIPART);
1035 mp_entry = (struct cache_mp_entry_ *)entry;
1036
1037 if (mp_entry->completed_write_session == NULL) {
1038 TRACE_OUT(open_cache_mp_read_session);
1039 return (NULL);
1040 }
1041
1042 if ((mp_entry->mp_params.max_lifetime.tv_sec != 0)
1043 || (mp_entry->mp_params.max_lifetime.tv_usec != 0)) {
1044 if (mp_entry->last_request_time.tv_sec -
1045 mp_entry->last_request_time.tv_sec >
1046 mp_entry->mp_params.max_lifetime.tv_sec) {
1047 flush_cache_entry(entry);
1048 TRACE_OUT(open_cache_mp_read_session);
1049 return (NULL);
1050 }
1051 }
1052
1053 retval = (struct cache_mp_read_session_ *)calloc(1,
1054 sizeof(struct cache_mp_read_session_));
1055 assert(retval != NULL);
1056
1057 retval->parent_entry = mp_entry;
1058 retval->current_item = TAILQ_FIRST(
1059 &mp_entry->completed_write_session->items);
1060
1061 TAILQ_INSERT_HEAD(&mp_entry->rs_head, retval, entries);
1062 ++mp_entry->rs_size;
1063
1064 mp_entry->get_time_func(&mp_entry->last_request_time);
1065 TRACE_OUT(open_cache_mp_read_session);
1066 return (retval);
1067}
1068
1069/*
1070 * Reads the data from the read session - step by step.
1071 * Returns 0 on success, -1 on error (when there are no more data), and -2 if
1072 * the data_size is too small. In the last case, data_size would be filled
1073 * the proper value.
1074 */
1075int
1076cache_mp_read(struct cache_mp_read_session_ *rs, char *data, size_t *data_size)
1077{
1078
1079 TRACE_IN(cache_mp_read);
1080 assert(rs != NULL);
1081
1082 if (rs->current_item == NULL) {
1083 TRACE_OUT(cache_mp_read);
1084 return (-1);
1085 }
1086
1087 if (rs->current_item->value_size > *data_size) {
1088 *data_size = rs->current_item->value_size;
1089 if (data == NULL) {
1090 TRACE_OUT(cache_mp_read);
1091 return (0);
1092 }
1093
1094 TRACE_OUT(cache_mp_read);
1095 return (-2);
1096 }
1097
1098 *data_size = rs->current_item->value_size;
1099 memcpy(data, rs->current_item->value, rs->current_item->value_size);
1100 rs->current_item = TAILQ_NEXT(rs->current_item, entries);
1101
1102 TRACE_OUT(cache_mp_read);
1103 return (0);
1104}
1105
1106/*
1107 * Closes the read session. If there are no more read sessions and there is
1108 * a pending write session, it will be committed and old
1109 * completed_write_session will be destroyed.
1110 */
1111void
1112close_cache_mp_read_session(struct cache_mp_read_session_ *rs)
1113{
1114
1115 TRACE_IN(close_cache_mp_read_session);
1116 assert(rs != NULL);
1117 assert(rs->parent_entry != NULL);
1118
1119 TAILQ_REMOVE(&rs->parent_entry->rs_head, rs, entries);
1120 --rs->parent_entry->rs_size;
1121
1122 if ((rs->parent_entry->rs_size == 0) &&
1123 (rs->parent_entry->pending_write_session != NULL)) {
1124 destroy_cache_mp_write_session(
1125 rs->parent_entry->completed_write_session);
1126 rs->parent_entry->completed_write_session =
1127 rs->parent_entry->pending_write_session;
1128 rs->parent_entry->pending_write_session = NULL;
1129 }
1130
1131 destroy_cache_mp_read_session(rs);
1132 TRACE_OUT(close_cache_mp_read_session);
1133}
1134
1135int
1136transform_cache_entry(struct cache_entry_ *entry,
1137 enum cache_transformation_t transformation)
1138{
1139
1140 TRACE_IN(transform_cache_entry);
1141 switch (transformation) {
1142 case CTT_CLEAR:
1143 clear_cache_entry(entry);
1144 TRACE_OUT(transform_cache_entry);
1145 return (0);
1146 case CTT_FLUSH:
1147 flush_cache_entry(entry);
1148 TRACE_OUT(transform_cache_entry);
1149 return (0);
1150 default:
1151 TRACE_OUT(transform_cache_entry);
1152 return (-1);
1153 }
1154}
1155
1156int
1157transform_cache_entry_part(struct cache_entry_ *entry,
1158 enum cache_transformation_t transformation, const char *key_part,
1159 size_t key_part_size, enum part_position_t part_position)
1160{
1161 struct cache_common_entry_ *common_entry;
1162 struct cache_ht_item_ *ht_item;
1163 struct cache_ht_item_data_ *ht_item_data, ht_key;
1164
1165 struct cache_policy_item_ *item, *connected_item;
1166
1167 TRACE_IN(transform_cache_entry_part);
1168 if (entry->params->entry_type != CET_COMMON) {
1169 TRACE_OUT(transform_cache_entry_part);
1170 return (-1);
1171 }
1172
1173 if (transformation != CTT_CLEAR) {
1174 TRACE_OUT(transform_cache_entry_part);
1175 return (-1);
1176 }
1177
1178 memset(&ht_key, 0, sizeof(struct cache_ht_item_data_));
1179 ht_key.key = (char *)key_part; /* can't avoid casting here */
1180 ht_key.key_size = key_part_size;
1181
1182 common_entry = (struct cache_common_entry_ *)entry;
1183 HASHTABLE_FOREACH(&(common_entry->items), ht_item) {
1184 do {
1185 ht_item_data = HASHTABLE_ENTRY_FIND_SPECIAL(cache_ht_,
1186 ht_item, &ht_key,
1187 ht_items_fixed_size_left_cmp_func);
1188
1189 if (ht_item_data != NULL) {
1190 item = ht_item_data->fifo_policy_item;
1191 connected_item = item->connected_item;
1192
1193 common_entry->policies[0]->remove_item_func(
1194 common_entry->policies[0],
1195 item);
1196
1197 free(ht_item_data->key);
1198 free(ht_item_data->value);
1199 HASHTABLE_ENTRY_REMOVE(cache_ht_, ht_item,
1200 ht_item_data);
1201 --common_entry->items_size;
1202
1203 common_entry->policies[0]->destroy_item_func(
1204 item);
1205 if (common_entry->policies_size == 2) {
1206 common_entry->policies[1]->remove_item_func(
1207 common_entry->policies[1],
1208 connected_item);
1209 common_entry->policies[1]->destroy_item_func(
1210 connected_item);
1211 }
1212 }
1213 } while (ht_item_data != NULL);
1214 }
1215
1216 TRACE_OUT(transform_cache_entry_part);
1217 return (0);
1218}