2 * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
26 * $FreeBSD: src/usr.sbin/nscd/cachelib.c,v 1.4 2008/10/23 00:31:15 delphij Exp $
36 #define INITIAL_ENTRIES_CAPACITY 32
37 #define ENTRIES_CAPACITY_STEP 32
39 #define STRING_SIMPLE_HASH_BODY(in_var, var, a, M) \
40 for ((var) = 0; *(in_var) != '\0'; ++(in_var)) \
41 (var) = ((a)*(var) + *(in_var)) % (M)
43 #define STRING_SIMPLE_MP2_HASH_BODY(in_var, var, a, M) \
44 for ((var) = 0; *(in_var) != 0; ++(in_var)) \
45 (var) = ((a)*(var) + *(in_var)) & (M - 1)
47 static int cache_elemsize_common_continue_func(struct cache_common_entry_
*,
48 struct cache_policy_item_
*);
49 static int cache_lifetime_common_continue_func(struct cache_common_entry_
*,
50 struct cache_policy_item_
*);
51 static void clear_cache_entry(struct cache_entry_
*);
52 static void destroy_cache_entry(struct cache_entry_
*);
53 static void destroy_cache_mp_read_session(struct cache_mp_read_session_
*);
54 static void destroy_cache_mp_write_session(struct cache_mp_write_session_
*);
55 static int entries_bsearch_cmp_func(const void *, const void *);
56 static int entries_qsort_cmp_func(const void *, const void *);
57 static struct cache_entry_
** find_cache_entry_p(struct cache_
*,
59 static void flush_cache_entry(struct cache_entry_
*);
60 static void flush_cache_policy(struct cache_common_entry_
*,
61 struct cache_policy_
*, struct cache_policy_
*,
62 int (*)(struct cache_common_entry_
*,
63 struct cache_policy_item_
*));
64 static int ht_items_cmp_func(const void *, const void *);
65 static int ht_items_fixed_size_left_cmp_func(const void *, const void *);
66 static hashtable_index_t
ht_item_hash_func(const void *, size_t);
69 * Hashing and comparing routines, that are used with the hash tables
72 ht_items_cmp_func(const void *p1
, const void *p2
)
74 struct cache_ht_item_data_
*hp1
, *hp2
;
78 hp1
= (struct cache_ht_item_data_
*)p1
;
79 hp2
= (struct cache_ht_item_data_
*)p2
;
81 assert(hp1
->key
!= NULL
);
82 assert(hp2
->key
!= NULL
);
84 if (hp1
->key_size
!= hp2
->key_size
) {
85 min_size
= (hp1
->key_size
< hp2
->key_size
) ? hp1
->key_size
:
87 result
= memcmp(hp1
->key
, hp2
->key
, min_size
);
90 return ((hp1
->key_size
< hp2
->key_size
) ? -1 : 1);
94 return (memcmp(hp1
->key
, hp2
->key
, hp1
->key_size
));
98 ht_items_fixed_size_left_cmp_func(const void *p1
, const void *p2
)
100 struct cache_ht_item_data_
*hp1
, *hp2
;
104 hp1
= (struct cache_ht_item_data_
*)p1
;
105 hp2
= (struct cache_ht_item_data_
*)p2
;
107 assert(hp1
->key
!= NULL
);
108 assert(hp2
->key
!= NULL
);
110 if (hp1
->key_size
!= hp2
->key_size
) {
111 min_size
= (hp1
->key_size
< hp2
->key_size
) ? hp1
->key_size
:
113 result
= memcmp(hp1
->key
, hp2
->key
, min_size
);
116 if (min_size
== hp1
->key_size
)
119 return ((hp1
->key_size
< hp2
->key_size
) ? -1 : 1);
123 return (memcmp(hp1
->key
, hp2
->key
, hp1
->key_size
));
126 static hashtable_index_t
127 ht_item_hash_func(const void *p
, size_t cache_entries_size
)
129 struct cache_ht_item_data_
*hp
;
132 hashtable_index_t retval
;
134 hp
= (struct cache_ht_item_data_
*)p
;
135 assert(hp
->key
!= NULL
);
138 for (i
= 0; i
< hp
->key_size
; ++i
)
139 retval
= (127 * retval
+ (unsigned char)hp
->key
[i
]) %
145 HASHTABLE_GENERATE(cache_ht_
, cache_ht_item_
, struct cache_ht_item_data_
, data
,
146 ht_item_hash_func
, ht_items_cmp_func
);
149 * Routines to sort and search the entries by name
152 entries_bsearch_cmp_func(const void *key
, const void *ent
)
158 return (strcmp((char const *)key
,
159 (*(struct cache_entry_
const **)ent
)->name
));
163 entries_qsort_cmp_func(const void *e1
, const void *e2
)
169 return (strcmp((*(struct cache_entry_
const **)e1
)->name
,
170 (*(struct cache_entry_
const **)e2
)->name
));
173 static struct cache_entry_
**
174 find_cache_entry_p(struct cache_
*the_cache
, const char *entry_name
)
177 return ((struct cache_entry_
**)(bsearch(entry_name
, the_cache
->entries
,
178 the_cache
->entries_size
, sizeof(struct cache_entry_
*),
179 entries_bsearch_cmp_func
)));
183 destroy_cache_mp_write_session(struct cache_mp_write_session_
*ws
)
186 struct cache_mp_data_item_
*data_item
;
188 TRACE_IN(destroy_cache_mp_write_session
);
190 while (!TAILQ_EMPTY(&ws
->items
)) {
191 data_item
= TAILQ_FIRST(&ws
->items
);
192 TAILQ_REMOVE(&ws
->items
, data_item
, entries
);
193 free(data_item
->value
);
198 TRACE_OUT(destroy_cache_mp_write_session
);
202 destroy_cache_mp_read_session(struct cache_mp_read_session_
*rs
)
205 TRACE_IN(destroy_cache_mp_read_session
);
208 TRACE_OUT(destroy_cache_mp_read_session
);
212 destroy_cache_entry(struct cache_entry_
*entry
)
214 struct cache_common_entry_
*common_entry
;
215 struct cache_mp_entry_
*mp_entry
;
216 struct cache_mp_read_session_
*rs
;
217 struct cache_mp_write_session_
*ws
;
218 struct cache_ht_item_
*ht_item
;
219 struct cache_ht_item_data_
*ht_item_data
;
221 TRACE_IN(destroy_cache_entry
);
222 assert(entry
!= NULL
);
224 if (entry
->params
->entry_type
== CET_COMMON
) {
225 common_entry
= (struct cache_common_entry_
*)entry
;
227 HASHTABLE_FOREACH(&(common_entry
->items
), ht_item
) {
228 HASHTABLE_ENTRY_FOREACH(ht_item
, data
, ht_item_data
)
230 free(ht_item_data
->key
);
231 free(ht_item_data
->value
);
233 HASHTABLE_ENTRY_CLEAR(ht_item
, data
);
236 HASHTABLE_DESTROY(&(common_entry
->items
), data
);
238 /* FIFO policy is always first */
239 destroy_cache_fifo_policy(common_entry
->policies
[0]);
240 switch (common_entry
->common_params
.policy
) {
242 destroy_cache_lru_policy(common_entry
->policies
[1]);
245 destroy_cache_lfu_policy(common_entry
->policies
[1]);
250 free(common_entry
->policies
);
252 mp_entry
= (struct cache_mp_entry_
*)entry
;
254 while (!TAILQ_EMPTY(&mp_entry
->ws_head
)) {
255 ws
= TAILQ_FIRST(&mp_entry
->ws_head
);
256 TAILQ_REMOVE(&mp_entry
->ws_head
, ws
, entries
);
257 destroy_cache_mp_write_session(ws
);
260 while (!TAILQ_EMPTY(&mp_entry
->rs_head
)) {
261 rs
= TAILQ_FIRST(&mp_entry
->rs_head
);
262 TAILQ_REMOVE(&mp_entry
->rs_head
, rs
, entries
);
263 destroy_cache_mp_read_session(rs
);
266 if (mp_entry
->completed_write_session
!= NULL
)
267 destroy_cache_mp_write_session(
268 mp_entry
->completed_write_session
);
270 if (mp_entry
->pending_write_session
!= NULL
)
271 destroy_cache_mp_write_session(
272 mp_entry
->pending_write_session
);
277 TRACE_OUT(destroy_cache_entry
);
281 clear_cache_entry(struct cache_entry_
*entry
)
283 struct cache_mp_entry_
*mp_entry
;
284 struct cache_common_entry_
*common_entry
;
285 struct cache_ht_item_
*ht_item
;
286 struct cache_ht_item_data_
*ht_item_data
;
287 struct cache_policy_
*policy
;
288 struct cache_policy_item_
*item
, *next_item
;
292 if (entry
->params
->entry_type
== CET_COMMON
) {
293 common_entry
= (struct cache_common_entry_
*)entry
;
296 HASHTABLE_FOREACH(&(common_entry
->items
), ht_item
) {
297 HASHTABLE_ENTRY_FOREACH(ht_item
, data
, ht_item_data
)
299 free(ht_item_data
->key
);
300 free(ht_item_data
->value
);
302 entry_size
+= HASHTABLE_ENTRY_SIZE(ht_item
, data
);
303 HASHTABLE_ENTRY_CLEAR(ht_item
, data
);
306 common_entry
->items_size
-= entry_size
;
307 for (i
= 0; i
< common_entry
->policies_size
; ++i
) {
308 policy
= common_entry
->policies
[i
];
311 item
= policy
->get_first_item_func(policy
);
312 while (item
!= NULL
) {
313 next_item
= policy
->get_next_item_func(policy
,
315 policy
->remove_item_func(policy
, item
);
316 policy
->destroy_item_func(item
);
321 mp_entry
= (struct cache_mp_entry_
*)entry
;
323 if (mp_entry
->rs_size
== 0) {
324 if (mp_entry
->completed_write_session
!= NULL
) {
325 destroy_cache_mp_write_session(
326 mp_entry
->completed_write_session
);
327 mp_entry
->completed_write_session
= NULL
;
330 memset(&mp_entry
->creation_time
, 0,
331 sizeof(struct timeval
));
332 memset(&mp_entry
->last_request_time
, 0,
333 sizeof(struct timeval
));
339 * When passed to the flush_cache_policy, ensures that all old elements are
343 cache_lifetime_common_continue_func(struct cache_common_entry_
*entry
,
344 struct cache_policy_item_
*item
)
347 return ((item
->last_request_time
.tv_sec
- item
->creation_time
.tv_sec
>
348 entry
->common_params
.max_lifetime
.tv_sec
) ? 1: 0);
352 * When passed to the flush_cache_policy, ensures that all elements, that
353 * exceed the size limit, are deleted.
356 cache_elemsize_common_continue_func(struct cache_common_entry_
*entry
,
357 struct cache_policy_item_
*item
)
360 return ((entry
->items_size
> entry
->common_params
.satisf_elemsize
) ? 1
365 * Removes the elements from the cache entry, while the continue_func returns 1.
368 flush_cache_policy(struct cache_common_entry_
*entry
,
369 struct cache_policy_
*policy
,
370 struct cache_policy_
*connected_policy
,
371 int (*continue_func
)(struct cache_common_entry_
*,
372 struct cache_policy_item_
*))
374 struct cache_policy_item_
*item
, *next_item
, *connected_item
;
375 struct cache_ht_item_
*ht_item
;
376 struct cache_ht_item_data_
*ht_item_data
, ht_key
;
377 hashtable_index_t hash
;
379 assert(policy
!= NULL
);
382 item
= policy
->get_first_item_func(policy
);
383 while ((item
!= NULL
) && (continue_func(entry
, item
) == 1)) {
384 next_item
= policy
->get_next_item_func(policy
, item
);
386 connected_item
= item
->connected_item
;
387 policy
->remove_item_func(policy
, item
);
389 memset(&ht_key
, 0, sizeof(struct cache_ht_item_data_
));
390 ht_key
.key
= item
->key
;
391 ht_key
.key_size
= item
->key_size
;
393 hash
= HASHTABLE_CALCULATE_HASH(cache_ht_
, &entry
->items
,
396 assert(hash
< HASHTABLE_ENTRIES_COUNT(&entry
->items
));
398 ht_item
= HASHTABLE_GET_ENTRY(&(entry
->items
), hash
);
399 ht_item_data
= HASHTABLE_ENTRY_FIND(cache_ht_
, ht_item
,
401 assert(ht_item_data
!= NULL
);
402 free(ht_item_data
->key
);
403 free(ht_item_data
->value
);
404 HASHTABLE_ENTRY_REMOVE(cache_ht_
, ht_item
, ht_item_data
);
407 policy
->destroy_item_func(item
);
409 if (connected_item
!= NULL
) {
410 connected_policy
->remove_item_func(connected_policy
,
412 connected_policy
->destroy_item_func(connected_item
);
420 flush_cache_entry(struct cache_entry_
*entry
)
422 struct cache_mp_entry_
*mp_entry
;
423 struct cache_common_entry_
*common_entry
;
424 struct cache_policy_
*policy
, *connected_policy
;
426 connected_policy
= NULL
;
427 if (entry
->params
->entry_type
== CET_COMMON
) {
428 common_entry
= (struct cache_common_entry_
*)entry
;
429 if ((common_entry
->common_params
.max_lifetime
.tv_sec
!= 0) ||
430 (common_entry
->common_params
.max_lifetime
.tv_usec
!= 0)) {
432 policy
= common_entry
->policies
[0];
433 if (common_entry
->policies_size
> 1)
434 connected_policy
= common_entry
->policies
[1];
436 flush_cache_policy(common_entry
, policy
,
438 cache_lifetime_common_continue_func
);
442 if ((common_entry
->common_params
.max_elemsize
!= 0) &&
443 common_entry
->items_size
>
444 common_entry
->common_params
.max_elemsize
) {
446 if (common_entry
->policies_size
> 1) {
447 policy
= common_entry
->policies
[1];
448 connected_policy
= common_entry
->policies
[0];
450 policy
= common_entry
->policies
[0];
451 connected_policy
= NULL
;
454 flush_cache_policy(common_entry
, policy
,
456 cache_elemsize_common_continue_func
);
459 mp_entry
= (struct cache_mp_entry_
*)entry
;
461 if ((mp_entry
->mp_params
.max_lifetime
.tv_sec
!= 0)
462 || (mp_entry
->mp_params
.max_lifetime
.tv_usec
!= 0)) {
464 if (mp_entry
->last_request_time
.tv_sec
-
465 mp_entry
->last_request_time
.tv_sec
>
466 mp_entry
->mp_params
.max_lifetime
.tv_sec
)
467 clear_cache_entry(entry
);
473 init_cache(struct cache_params
const *params
)
475 struct cache_
*retval
;
477 TRACE_IN(init_cache
);
478 assert(params
!= NULL
);
480 retval
= (struct cache_
*)calloc(1, sizeof(struct cache_
));
481 assert(retval
!= NULL
);
483 assert(params
!= NULL
);
484 memcpy(&retval
->params
, params
, sizeof(struct cache_params
));
486 retval
->entries
= (struct cache_entry_
**)calloc(1,
487 sizeof(struct cache_entry_
*) * INITIAL_ENTRIES_CAPACITY
);
488 assert(retval
->entries
!= NULL
);
490 retval
->entries_capacity
= INITIAL_ENTRIES_CAPACITY
;
491 retval
->entries_size
= 0;
493 TRACE_OUT(init_cache
);
498 destroy_cache(struct cache_
*the_cache
)
501 TRACE_IN(destroy_cache
);
502 assert(the_cache
!= NULL
);
504 if (the_cache
->entries
!= NULL
) {
506 for (i
= 0; i
< the_cache
->entries_size
; ++i
)
507 destroy_cache_entry(the_cache
->entries
[i
]);
509 free(the_cache
->entries
);
513 TRACE_OUT(destroy_cache
);
517 register_cache_entry(struct cache_
*the_cache
,
518 struct cache_entry_params
const *params
)
521 size_t entry_name_size
;
522 struct cache_common_entry_
*new_common_entry
;
523 struct cache_mp_entry_
*new_mp_entry
;
525 TRACE_IN(register_cache_entry
);
526 assert(the_cache
!= NULL
);
528 if (find_cache_entry(the_cache
, params
->entry_name
) != NULL
) {
529 TRACE_OUT(register_cache_entry
);
533 if (the_cache
->entries_size
== the_cache
->entries_capacity
) {
534 struct cache_entry_
**new_entries
;
537 new_capacity
= the_cache
->entries_capacity
+
538 ENTRIES_CAPACITY_STEP
;
539 new_entries
= (struct cache_entry_
**)calloc(1,
540 sizeof(struct cache_entry_
*) * new_capacity
);
541 assert(new_entries
!= NULL
);
543 memcpy(new_entries
, the_cache
->entries
,
544 sizeof(struct cache_entry_
*)
545 * the_cache
->entries_size
);
547 free(the_cache
->entries
);
548 the_cache
->entries
= new_entries
;
551 entry_name_size
= strlen(params
->entry_name
) + 1;
552 switch (params
->entry_type
)
555 new_common_entry
= (struct cache_common_entry_
*)calloc(1,
556 sizeof(struct cache_common_entry_
));
557 assert(new_common_entry
!= NULL
);
559 memcpy(&new_common_entry
->common_params
, params
,
560 sizeof(struct common_cache_entry_params
));
561 new_common_entry
->params
=
562 (struct cache_entry_params
*)&new_common_entry
->common_params
;
564 new_common_entry
->common_params
.entry_name
= (char *)calloc(1,
566 assert(new_common_entry
->common_params
.entry_name
!= NULL
);
567 strlcpy(new_common_entry
->common_params
.entry_name
,
568 params
->entry_name
, entry_name_size
);
569 new_common_entry
->name
=
570 new_common_entry
->common_params
.entry_name
;
572 HASHTABLE_INIT(&(new_common_entry
->items
),
573 struct cache_ht_item_data_
, data
,
574 new_common_entry
->common_params
.cache_entries_size
);
576 if (new_common_entry
->common_params
.policy
== CPT_FIFO
)
581 new_common_entry
->policies
= (struct cache_policy_
**)calloc(1,
582 sizeof(struct cache_policy_
*) * policies_size
);
583 assert(new_common_entry
->policies
!= NULL
);
585 new_common_entry
->policies_size
= policies_size
;
586 new_common_entry
->policies
[0] = init_cache_fifo_policy();
588 if (policies_size
> 1) {
589 switch (new_common_entry
->common_params
.policy
) {
591 new_common_entry
->policies
[1] =
592 init_cache_lru_policy();
595 new_common_entry
->policies
[1] =
596 init_cache_lfu_policy();
603 new_common_entry
->get_time_func
=
604 the_cache
->params
.get_time_func
;
605 the_cache
->entries
[the_cache
->entries_size
++] =
606 (struct cache_entry_
*)new_common_entry
;
609 new_mp_entry
= (struct cache_mp_entry_
*)calloc(1,
610 sizeof(struct cache_mp_entry_
));
611 assert(new_mp_entry
!= NULL
);
613 memcpy(&new_mp_entry
->mp_params
, params
,
614 sizeof(struct mp_cache_entry_params
));
615 new_mp_entry
->params
=
616 (struct cache_entry_params
*)&new_mp_entry
->mp_params
;
618 new_mp_entry
->mp_params
.entry_name
= (char *)calloc(1,
620 assert(new_mp_entry
->mp_params
.entry_name
!= NULL
);
621 strlcpy(new_mp_entry
->mp_params
.entry_name
, params
->entry_name
,
623 new_mp_entry
->name
= new_mp_entry
->mp_params
.entry_name
;
625 TAILQ_INIT(&new_mp_entry
->ws_head
);
626 TAILQ_INIT(&new_mp_entry
->rs_head
);
628 new_mp_entry
->get_time_func
= the_cache
->params
.get_time_func
;
629 the_cache
->entries
[the_cache
->entries_size
++] =
630 (struct cache_entry_
*)new_mp_entry
;
635 qsort(the_cache
->entries
, the_cache
->entries_size
,
636 sizeof(struct cache_entry_
*), entries_qsort_cmp_func
);
638 TRACE_OUT(register_cache_entry
);
643 unregister_cache_entry(struct cache_
*the_cache
, const char *entry_name
)
645 struct cache_entry_
**del_ent
;
647 TRACE_IN(unregister_cache_entry
);
648 assert(the_cache
!= NULL
);
650 del_ent
= find_cache_entry_p(the_cache
, entry_name
);
651 if (del_ent
!= NULL
) {
652 destroy_cache_entry(*del_ent
);
653 --the_cache
->entries_size
;
655 memmove(del_ent
, del_ent
+ 1,
656 (&(the_cache
->entries
[--the_cache
->entries_size
]) -
657 del_ent
) * sizeof(struct cache_entry_
*));
659 TRACE_OUT(unregister_cache_entry
);
662 TRACE_OUT(unregister_cache_entry
);
667 struct cache_entry_
*
668 find_cache_entry(struct cache_
*the_cache
, const char *entry_name
)
670 struct cache_entry_
**result
;
672 TRACE_IN(find_cache_entry
);
673 result
= find_cache_entry_p(the_cache
, entry_name
);
675 if (result
== NULL
) {
676 TRACE_OUT(find_cache_entry
);
679 TRACE_OUT(find_cache_entry
);
685 * Tries to read the element with the specified key from the cache. If the
686 * value_size is too small, it will be filled with the proper number, and
687 * the user will need to call cache_read again with the value buffer, that
689 * Function returns 0 on success, -1 on error, and -2 if the value_size is too
693 cache_read(struct cache_entry_
*entry
, const char *key
, size_t key_size
,
694 char *value
, size_t *value_size
)
696 struct cache_common_entry_
*common_entry
;
697 struct cache_ht_item_data_ item_data
, *find_res
;
698 struct cache_ht_item_
*item
;
699 hashtable_index_t hash
;
700 struct cache_policy_item_
*connected_item
;
702 TRACE_IN(cache_read
);
703 assert(entry
!= NULL
);
705 assert(value_size
!= NULL
);
706 assert(entry
->params
->entry_type
== CET_COMMON
);
708 common_entry
= (struct cache_common_entry_
*)entry
;
710 memset(&item_data
, 0, sizeof(struct cache_ht_item_data_
));
711 /* can't avoid the cast here */
712 item_data
.key
= (char *)key
;
713 item_data
.key_size
= key_size
;
715 hash
= HASHTABLE_CALCULATE_HASH(cache_ht_
, &common_entry
->items
,
718 assert(hash
< HASHTABLE_ENTRIES_COUNT(&common_entry
->items
));
720 item
= HASHTABLE_GET_ENTRY(&(common_entry
->items
), hash
);
721 find_res
= HASHTABLE_ENTRY_FIND(cache_ht_
, item
, &item_data
);
722 if (find_res
== NULL
) {
723 TRACE_OUT(cache_read
);
727 if ((common_entry
->common_params
.max_lifetime
.tv_sec
!= 0) ||
728 (common_entry
->common_params
.max_lifetime
.tv_usec
!= 0)) {
730 if (find_res
->fifo_policy_item
->last_request_time
.tv_sec
-
731 find_res
->fifo_policy_item
->creation_time
.tv_sec
>
732 common_entry
->common_params
.max_lifetime
.tv_sec
) {
735 free(find_res
->value
);
738 find_res
->fifo_policy_item
->connected_item
;
739 if (connected_item
!= NULL
) {
740 common_entry
->policies
[1]->remove_item_func(
741 common_entry
->policies
[1],
743 common_entry
->policies
[1]->destroy_item_func(
747 common_entry
->policies
[0]->remove_item_func(
748 common_entry
->policies
[0],
749 find_res
->fifo_policy_item
);
750 common_entry
->policies
[0]->destroy_item_func(
751 find_res
->fifo_policy_item
);
753 HASHTABLE_ENTRY_REMOVE(cache_ht_
, item
, find_res
);
754 --common_entry
->items_size
;
758 if ((*value_size
< find_res
->value_size
) || (value
== NULL
)) {
759 *value_size
= find_res
->value_size
;
760 TRACE_OUT(cache_read
);
764 *value_size
= find_res
->value_size
;
765 memcpy(value
, find_res
->value
, find_res
->value_size
);
767 ++find_res
->fifo_policy_item
->request_count
;
768 common_entry
->get_time_func(
769 &find_res
->fifo_policy_item
->last_request_time
);
770 common_entry
->policies
[0]->update_item_func(common_entry
->policies
[0],
771 find_res
->fifo_policy_item
);
773 if (find_res
->fifo_policy_item
->connected_item
!= NULL
) {
774 connected_item
= find_res
->fifo_policy_item
->connected_item
;
775 memcpy(&connected_item
->last_request_time
,
776 &find_res
->fifo_policy_item
->last_request_time
,
777 sizeof(struct timeval
));
778 connected_item
->request_count
=
779 find_res
->fifo_policy_item
->request_count
;
781 common_entry
->policies
[1]->update_item_func(
782 common_entry
->policies
[1], connected_item
);
785 TRACE_OUT(cache_read
);
790 * Writes the value with the specified key into the cache entry.
791 * Functions returns 0 on success, and -1 on error.
794 cache_write(struct cache_entry_
*entry
, const char *key
, size_t key_size
,
795 char const *value
, size_t value_size
)
797 struct cache_common_entry_
*common_entry
;
798 struct cache_ht_item_data_ item_data
, *find_res
;
799 struct cache_ht_item_
*item
;
800 hashtable_index_t hash
;
802 struct cache_policy_
*policy
, *connected_policy
;
803 struct cache_policy_item_
*policy_item
;
804 struct cache_policy_item_
*connected_policy_item
;
806 TRACE_IN(cache_write
);
807 assert(entry
!= NULL
);
809 assert(value
!= NULL
);
810 assert(entry
->params
->entry_type
== CET_COMMON
);
812 common_entry
= (struct cache_common_entry_
*)entry
;
814 memset(&item_data
, 0, sizeof(struct cache_ht_item_data_
));
815 /* can't avoid the cast here */
816 item_data
.key
= (char *)key
;
817 item_data
.key_size
= key_size
;
819 hash
= HASHTABLE_CALCULATE_HASH(cache_ht_
, &common_entry
->items
,
822 assert(hash
< HASHTABLE_ENTRIES_COUNT(&common_entry
->items
));
824 item
= HASHTABLE_GET_ENTRY(&(common_entry
->items
), hash
);
825 find_res
= HASHTABLE_ENTRY_FIND(cache_ht_
, item
, &item_data
);
826 if (find_res
!= NULL
) {
827 TRACE_OUT(cache_write
);
831 item_data
.key
= (char *)malloc(key_size
);
832 memcpy(item_data
.key
, key
, key_size
);
834 item_data
.value
= (char *)malloc(value_size
);
835 assert(item_data
.value
!= NULL
);
837 memcpy(item_data
.value
, value
, value_size
);
838 item_data
.value_size
= value_size
;
840 policy_item
= common_entry
->policies
[0]->create_item_func();
841 policy_item
->key
= item_data
.key
;
842 policy_item
->key_size
= item_data
.key_size
;
843 common_entry
->get_time_func(&policy_item
->creation_time
);
845 if (common_entry
->policies_size
> 1) {
846 connected_policy_item
=
847 common_entry
->policies
[1]->create_item_func();
848 memcpy(&connected_policy_item
->creation_time
,
849 &policy_item
->creation_time
,
850 sizeof(struct timeval
));
851 connected_policy_item
->key
= policy_item
->key
;
852 connected_policy_item
->key_size
= policy_item
->key_size
;
854 connected_policy_item
->connected_item
= policy_item
;
855 policy_item
->connected_item
= connected_policy_item
;
858 item_data
.fifo_policy_item
= policy_item
;
860 common_entry
->policies
[0]->add_item_func(common_entry
->policies
[0],
862 if (common_entry
->policies_size
> 1)
863 common_entry
->policies
[1]->add_item_func(
864 common_entry
->policies
[1], connected_policy_item
);
866 HASHTABLE_ENTRY_STORE(cache_ht_
, item
, &item_data
);
867 ++common_entry
->items_size
;
869 if ((common_entry
->common_params
.max_elemsize
!= 0) &&
870 (common_entry
->items_size
>
871 common_entry
->common_params
.max_elemsize
)) {
872 if (common_entry
->policies_size
> 1) {
873 policy
= common_entry
->policies
[1];
874 connected_policy
= common_entry
->policies
[0];
876 policy
= common_entry
->policies
[0];
877 connected_policy
= NULL
;
880 flush_cache_policy(common_entry
, policy
, connected_policy
,
881 cache_elemsize_common_continue_func
);
884 TRACE_OUT(cache_write
);
889 * Initializes the write session for the specified multipart entry. This
890 * session then should be filled with data either committed or abandoned by
891 * using close_cache_mp_write_session or abandon_cache_mp_write_session
893 * Returns NULL on errors (when there are too many opened write sessions for
896 struct cache_mp_write_session_
*
897 open_cache_mp_write_session(struct cache_entry_
*entry
)
899 struct cache_mp_entry_
*mp_entry
;
900 struct cache_mp_write_session_
*retval
;
902 TRACE_IN(open_cache_mp_write_session
);
903 assert(entry
!= NULL
);
904 assert(entry
->params
->entry_type
== CET_MULTIPART
);
905 mp_entry
= (struct cache_mp_entry_
*)entry
;
907 if ((mp_entry
->mp_params
.max_sessions
> 0) &&
908 (mp_entry
->ws_size
== mp_entry
->mp_params
.max_sessions
)) {
909 TRACE_OUT(open_cache_mp_write_session
);
913 retval
= (struct cache_mp_write_session_
*)calloc(1,
914 sizeof(struct cache_mp_write_session_
));
915 assert(retval
!= NULL
);
917 TAILQ_INIT(&retval
->items
);
918 retval
->parent_entry
= mp_entry
;
920 TAILQ_INSERT_HEAD(&mp_entry
->ws_head
, retval
, entries
);
923 TRACE_OUT(open_cache_mp_write_session
);
928 * Writes data to the specified session. Return 0 on success and -1 on errors
929 * (when write session size limit is exceeded).
932 cache_mp_write(struct cache_mp_write_session_
*ws
, char *data
,
935 struct cache_mp_data_item_
*new_item
;
937 TRACE_IN(cache_mp_write
);
939 assert(ws
->parent_entry
!= NULL
);
940 assert(ws
->parent_entry
->params
->entry_type
== CET_MULTIPART
);
942 if ((ws
->parent_entry
->mp_params
.max_elemsize
> 0) &&
943 (ws
->parent_entry
->mp_params
.max_elemsize
== ws
->items_size
)) {
944 TRACE_OUT(cache_mp_write
);
948 new_item
= (struct cache_mp_data_item_
*)calloc(1,
949 sizeof(struct cache_mp_data_item_
));
950 assert(new_item
!= NULL
);
952 new_item
->value
= (char *)malloc(data_size
);
953 assert(new_item
->value
!= NULL
);
954 memcpy(new_item
->value
, data
, data_size
);
955 new_item
->value_size
= data_size
;
957 TAILQ_INSERT_TAIL(&ws
->items
, new_item
, entries
);
960 TRACE_OUT(cache_mp_write
);
965 * Abandons the write session and frees all the connected resources.
968 abandon_cache_mp_write_session(struct cache_mp_write_session_
*ws
)
971 TRACE_IN(abandon_cache_mp_write_session
);
973 assert(ws
->parent_entry
!= NULL
);
974 assert(ws
->parent_entry
->params
->entry_type
== CET_MULTIPART
);
976 TAILQ_REMOVE(&ws
->parent_entry
->ws_head
, ws
, entries
);
977 --ws
->parent_entry
->ws_size
;
979 destroy_cache_mp_write_session(ws
);
980 TRACE_OUT(abandon_cache_mp_write_session
);
984 * Commits the session to the entry, for which it was created.
987 close_cache_mp_write_session(struct cache_mp_write_session_
*ws
)
990 TRACE_IN(close_cache_mp_write_session
);
992 assert(ws
->parent_entry
!= NULL
);
993 assert(ws
->parent_entry
->params
->entry_type
== CET_MULTIPART
);
995 TAILQ_REMOVE(&ws
->parent_entry
->ws_head
, ws
, entries
);
996 --ws
->parent_entry
->ws_size
;
998 if (ws
->parent_entry
->completed_write_session
== NULL
) {
1000 * If there is no completed session yet, this will be the one
1002 ws
->parent_entry
->get_time_func(
1003 &ws
->parent_entry
->creation_time
);
1004 ws
->parent_entry
->completed_write_session
= ws
;
1007 * If there is a completed session, then we'll save our session
1008 * as a pending session. If there is already a pending session,
1009 * it would be destroyed.
1011 if (ws
->parent_entry
->pending_write_session
!= NULL
)
1012 destroy_cache_mp_write_session(
1013 ws
->parent_entry
->pending_write_session
);
1015 ws
->parent_entry
->pending_write_session
= ws
;
1017 TRACE_OUT(close_cache_mp_write_session
);
1021 * Opens read session for the specified entry. Returns NULL on errors (when
1022 * there are no data in the entry, or the data are obsolete).
1024 struct cache_mp_read_session_
*
1025 open_cache_mp_read_session(struct cache_entry_
*entry
)
1027 struct cache_mp_entry_
*mp_entry
;
1028 struct cache_mp_read_session_
*retval
;
1030 TRACE_IN(open_cache_mp_read_session
);
1031 assert(entry
!= NULL
);
1032 assert(entry
->params
->entry_type
== CET_MULTIPART
);
1033 mp_entry
= (struct cache_mp_entry_
*)entry
;
1035 if (mp_entry
->completed_write_session
== NULL
) {
1036 TRACE_OUT(open_cache_mp_read_session
);
1040 if ((mp_entry
->mp_params
.max_lifetime
.tv_sec
!= 0)
1041 || (mp_entry
->mp_params
.max_lifetime
.tv_usec
!= 0)) {
1042 if (mp_entry
->last_request_time
.tv_sec
-
1043 mp_entry
->last_request_time
.tv_sec
>
1044 mp_entry
->mp_params
.max_lifetime
.tv_sec
) {
1045 flush_cache_entry(entry
);
1046 TRACE_OUT(open_cache_mp_read_session
);
1051 retval
= (struct cache_mp_read_session_
*)calloc(1,
1052 sizeof(struct cache_mp_read_session_
));
1053 assert(retval
!= NULL
);
1055 retval
->parent_entry
= mp_entry
;
1056 retval
->current_item
= TAILQ_FIRST(
1057 &mp_entry
->completed_write_session
->items
);
1059 TAILQ_INSERT_HEAD(&mp_entry
->rs_head
, retval
, entries
);
1060 ++mp_entry
->rs_size
;
1062 mp_entry
->get_time_func(&mp_entry
->last_request_time
);
1063 TRACE_OUT(open_cache_mp_read_session
);
1068 * Reads the data from the read session - step by step.
1069 * Returns 0 on success, -1 on error (when there are no more data), and -2 if
1070 * the data_size is too small. In the last case, data_size would be filled
1074 cache_mp_read(struct cache_mp_read_session_
*rs
, char *data
, size_t *data_size
)
1077 TRACE_IN(cache_mp_read
);
1080 if (rs
->current_item
== NULL
) {
1081 TRACE_OUT(cache_mp_read
);
1085 if (rs
->current_item
->value_size
> *data_size
) {
1086 *data_size
= rs
->current_item
->value_size
;
1088 TRACE_OUT(cache_mp_read
);
1092 TRACE_OUT(cache_mp_read
);
1096 *data_size
= rs
->current_item
->value_size
;
1097 memcpy(data
, rs
->current_item
->value
, rs
->current_item
->value_size
);
1098 rs
->current_item
= TAILQ_NEXT(rs
->current_item
, entries
);
1100 TRACE_OUT(cache_mp_read
);
1105 * Closes the read session. If there are no more read sessions and there is
1106 * a pending write session, it will be committed and old
1107 * completed_write_session will be destroyed.
1110 close_cache_mp_read_session(struct cache_mp_read_session_
*rs
)
1113 TRACE_IN(close_cache_mp_read_session
);
1115 assert(rs
->parent_entry
!= NULL
);
1117 TAILQ_REMOVE(&rs
->parent_entry
->rs_head
, rs
, entries
);
1118 --rs
->parent_entry
->rs_size
;
1120 if ((rs
->parent_entry
->rs_size
== 0) &&
1121 (rs
->parent_entry
->pending_write_session
!= NULL
)) {
1122 destroy_cache_mp_write_session(
1123 rs
->parent_entry
->completed_write_session
);
1124 rs
->parent_entry
->completed_write_session
=
1125 rs
->parent_entry
->pending_write_session
;
1126 rs
->parent_entry
->pending_write_session
= NULL
;
1129 destroy_cache_mp_read_session(rs
);
1130 TRACE_OUT(close_cache_mp_read_session
);
1134 transform_cache_entry(struct cache_entry_
*entry
,
1135 enum cache_transformation_t transformation
)
1138 TRACE_IN(transform_cache_entry
);
1139 switch (transformation
) {
1141 clear_cache_entry(entry
);
1142 TRACE_OUT(transform_cache_entry
);
1145 flush_cache_entry(entry
);
1146 TRACE_OUT(transform_cache_entry
);
1149 TRACE_OUT(transform_cache_entry
);
1155 transform_cache_entry_part(struct cache_entry_
*entry
,
1156 enum cache_transformation_t transformation
, const char *key_part
,
1157 size_t key_part_size
, enum part_position_t part_position
)
1159 struct cache_common_entry_
*common_entry
;
1160 struct cache_ht_item_
*ht_item
;
1161 struct cache_ht_item_data_
*ht_item_data
, ht_key
;
1163 struct cache_policy_item_
*item
, *connected_item
;
1165 TRACE_IN(transform_cache_entry_part
);
1166 if (entry
->params
->entry_type
!= CET_COMMON
) {
1167 TRACE_OUT(transform_cache_entry_part
);
1171 if (transformation
!= CTT_CLEAR
) {
1172 TRACE_OUT(transform_cache_entry_part
);
1176 memset(&ht_key
, 0, sizeof(struct cache_ht_item_data_
));
1177 ht_key
.key
= (char *)key_part
; /* can't avoid casting here */
1178 ht_key
.key_size
= key_part_size
;
1180 common_entry
= (struct cache_common_entry_
*)entry
;
1181 HASHTABLE_FOREACH(&(common_entry
->items
), ht_item
) {
1183 ht_item_data
= HASHTABLE_ENTRY_FIND_SPECIAL(cache_ht_
,
1185 ht_items_fixed_size_left_cmp_func
);
1187 if (ht_item_data
!= NULL
) {
1188 item
= ht_item_data
->fifo_policy_item
;
1189 connected_item
= item
->connected_item
;
1191 common_entry
->policies
[0]->remove_item_func(
1192 common_entry
->policies
[0],
1195 free(ht_item_data
->key
);
1196 free(ht_item_data
->value
);
1197 HASHTABLE_ENTRY_REMOVE(cache_ht_
, ht_item
,
1199 --common_entry
->items_size
;
1201 common_entry
->policies
[0]->destroy_item_func(
1203 if (common_entry
->policies_size
== 2) {
1204 common_entry
->policies
[1]->remove_item_func(
1205 common_entry
->policies
[1],
1207 common_entry
->policies
[1]->destroy_item_func(
1211 } while (ht_item_data
!= NULL
);
1214 TRACE_OUT(transform_cache_entry_part
);