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
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
38 #define INITIAL_ENTRIES_CAPACITY 32
39 #define ENTRIES_CAPACITY_STEP 32
41 #define STRING_SIMPLE_HASH_BODY(in_var, var, a, M) \
42 for ((var) = 0; *(in_var) != '\0'; ++(in_var)) \
43 (var) = ((a)*(var) + *(in_var)) % (M)
45 #define STRING_SIMPLE_MP2_HASH_BODY(in_var, var, a, M) \
46 for ((var) = 0; *(in_var) != 0; ++(in_var)) \
47 (var) = ((a)*(var) + *(in_var)) & (M - 1)
49 static int cache_elemsize_common_continue_func(struct cache_common_entry_ *,
50 struct cache_policy_item_ *);
51 static int cache_lifetime_common_continue_func(struct cache_common_entry_ *,
52 struct cache_policy_item_ *);
53 static void clear_cache_entry(struct cache_entry_ *);
54 static void destroy_cache_entry(struct cache_entry_ *);
55 static void destroy_cache_mp_read_session(struct cache_mp_read_session_ *);
56 static void destroy_cache_mp_write_session(struct cache_mp_write_session_ *);
57 static int entries_bsearch_cmp_func(const void *, const void *);
58 static int entries_qsort_cmp_func(const void *, const void *);
59 static struct cache_entry_ ** find_cache_entry_p(struct cache_ *,
61 static void flush_cache_entry(struct cache_entry_ *);
62 static void flush_cache_policy(struct cache_common_entry_ *,
63 struct cache_policy_ *, struct cache_policy_ *,
64 int (*)(struct cache_common_entry_ *,
65 struct cache_policy_item_ *));
66 static int ht_items_cmp_func(const void *, const void *);
67 static int ht_items_fixed_size_left_cmp_func(const void *, const void *);
68 static hashtable_index_t ht_item_hash_func(const void *, size_t);
71 * Hashing and comparing routines, that are used with the hash tables
74 ht_items_cmp_func(const void *p1, const void *p2)
76 struct cache_ht_item_data_ *hp1, *hp2;
80 hp1 = (struct cache_ht_item_data_ *)p1;
81 hp2 = (struct cache_ht_item_data_ *)p2;
83 assert(hp1->key != NULL);
84 assert(hp2->key != NULL);
86 if (hp1->key_size != hp2->key_size) {
87 min_size = (hp1->key_size < hp2->key_size) ? hp1->key_size :
89 result = memcmp(hp1->key, hp2->key, min_size);
92 return ((hp1->key_size < hp2->key_size) ? -1 : 1);
96 return (memcmp(hp1->key, hp2->key, hp1->key_size));
100 ht_items_fixed_size_left_cmp_func(const void *p1, const void *p2)
102 struct cache_ht_item_data_ *hp1, *hp2;
106 hp1 = (struct cache_ht_item_data_ *)p1;
107 hp2 = (struct cache_ht_item_data_ *)p2;
109 assert(hp1->key != NULL);
110 assert(hp2->key != NULL);
112 if (hp1->key_size != hp2->key_size) {
113 min_size = (hp1->key_size < hp2->key_size) ? hp1->key_size :
115 result = memcmp(hp1->key, hp2->key, min_size);
118 if (min_size == hp1->key_size)
121 return ((hp1->key_size < hp2->key_size) ? -1 : 1);
125 return (memcmp(hp1->key, hp2->key, hp1->key_size));
128 static hashtable_index_t
129 ht_item_hash_func(const void *p, size_t cache_entries_size)
131 struct cache_ht_item_data_ *hp;
134 hashtable_index_t retval;
136 hp = (struct cache_ht_item_data_ *)p;
137 assert(hp->key != NULL);
140 for (i = 0; i < hp->key_size; ++i)
141 retval = (127 * retval + (unsigned char)hp->key[i]) %
147 HASHTABLE_GENERATE(cache_ht_, cache_ht_item_, struct cache_ht_item_data_, data,
148 ht_item_hash_func, ht_items_cmp_func);
151 * Routines to sort and search the entries by name
154 entries_bsearch_cmp_func(const void *key, const void *ent)
160 return (strcmp((char const *)key,
161 (*(struct cache_entry_ const **)ent)->name));
165 entries_qsort_cmp_func(const void *e1, const void *e2)
171 return (strcmp((*(struct cache_entry_ const **)e1)->name,
172 (*(struct cache_entry_ const **)e2)->name));
175 static struct cache_entry_ **
176 find_cache_entry_p(struct cache_ *the_cache, const char *entry_name)
179 return ((struct cache_entry_ **)(bsearch(entry_name, the_cache->entries,
180 the_cache->entries_size, sizeof(struct cache_entry_ *),
181 entries_bsearch_cmp_func)));
185 destroy_cache_mp_write_session(struct cache_mp_write_session_ *ws)
188 struct cache_mp_data_item_ *data_item;
190 TRACE_IN(destroy_cache_mp_write_session);
192 while (!TAILQ_EMPTY(&ws->items)) {
193 data_item = TAILQ_FIRST(&ws->items);
194 TAILQ_REMOVE(&ws->items, data_item, entries);
195 free(data_item->value);
200 TRACE_OUT(destroy_cache_mp_write_session);
204 destroy_cache_mp_read_session(struct cache_mp_read_session_ *rs)
207 TRACE_IN(destroy_cache_mp_read_session);
210 TRACE_OUT(destroy_cache_mp_read_session);
214 destroy_cache_entry(struct cache_entry_ *entry)
216 struct cache_common_entry_ *common_entry;
217 struct cache_mp_entry_ *mp_entry;
218 struct cache_mp_read_session_ *rs;
219 struct cache_mp_write_session_ *ws;
220 struct cache_ht_item_ *ht_item;
221 struct cache_ht_item_data_ *ht_item_data;
223 TRACE_IN(destroy_cache_entry);
224 assert(entry != NULL);
226 if (entry->params->entry_type == CET_COMMON) {
227 common_entry = (struct cache_common_entry_ *)entry;
229 HASHTABLE_FOREACH(&(common_entry->items), ht_item) {
230 HASHTABLE_ENTRY_FOREACH(ht_item, data, ht_item_data)
232 free(ht_item_data->key);
233 free(ht_item_data->value);
235 HASHTABLE_ENTRY_CLEAR(ht_item, data);
238 HASHTABLE_DESTROY(&(common_entry->items), data);
240 /* FIFO policy is always first */
241 destroy_cache_fifo_policy(common_entry->policies[0]);
242 switch (common_entry->common_params.policy) {
244 destroy_cache_lru_policy(common_entry->policies[1]);
247 destroy_cache_lfu_policy(common_entry->policies[1]);
252 free(common_entry->policies);
254 mp_entry = (struct cache_mp_entry_ *)entry;
256 while (!TAILQ_EMPTY(&mp_entry->ws_head)) {
257 ws = TAILQ_FIRST(&mp_entry->ws_head);
258 TAILQ_REMOVE(&mp_entry->ws_head, ws, entries);
259 destroy_cache_mp_write_session(ws);
262 while (!TAILQ_EMPTY(&mp_entry->rs_head)) {
263 rs = TAILQ_FIRST(&mp_entry->rs_head);
264 TAILQ_REMOVE(&mp_entry->rs_head, rs, entries);
265 destroy_cache_mp_read_session(rs);
268 if (mp_entry->completed_write_session != NULL)
269 destroy_cache_mp_write_session(
270 mp_entry->completed_write_session);
272 if (mp_entry->pending_write_session != NULL)
273 destroy_cache_mp_write_session(
274 mp_entry->pending_write_session);
279 TRACE_OUT(destroy_cache_entry);
283 clear_cache_entry(struct cache_entry_ *entry)
285 struct cache_mp_entry_ *mp_entry;
286 struct cache_common_entry_ *common_entry;
287 struct cache_ht_item_ *ht_item;
288 struct cache_ht_item_data_ *ht_item_data;
289 struct cache_policy_ *policy;
290 struct cache_policy_item_ *item, *next_item;
294 if (entry->params->entry_type == CET_COMMON) {
295 common_entry = (struct cache_common_entry_ *)entry;
298 HASHTABLE_FOREACH(&(common_entry->items), ht_item) {
299 HASHTABLE_ENTRY_FOREACH(ht_item, data, ht_item_data)
301 free(ht_item_data->key);
302 free(ht_item_data->value);
304 entry_size += HASHTABLE_ENTRY_SIZE(ht_item, data);
305 HASHTABLE_ENTRY_CLEAR(ht_item, data);
308 common_entry->items_size -= entry_size;
309 for (i = 0; i < common_entry->policies_size; ++i) {
310 policy = common_entry->policies[i];
313 item = policy->get_first_item_func(policy);
314 while (item != NULL) {
315 next_item = policy->get_next_item_func(policy,
317 policy->remove_item_func(policy, item);
318 policy->destroy_item_func(item);
323 mp_entry = (struct cache_mp_entry_ *)entry;
325 if (mp_entry->rs_size == 0) {
326 if (mp_entry->completed_write_session != NULL) {
327 destroy_cache_mp_write_session(
328 mp_entry->completed_write_session);
329 mp_entry->completed_write_session = NULL;
332 memset(&mp_entry->creation_time, 0,
333 sizeof(struct timeval));
334 memset(&mp_entry->last_request_time, 0,
335 sizeof(struct timeval));
341 * When passed to the flush_cache_policy, ensures that all old elements are
345 cache_lifetime_common_continue_func(struct cache_common_entry_ *entry,
346 struct cache_policy_item_ *item)
349 return ((item->last_request_time.tv_sec - item->creation_time.tv_sec >
350 entry->common_params.max_lifetime.tv_sec) ? 1: 0);
354 * When passed to the flush_cache_policy, ensures that all elements, that
355 * exceed the size limit, are deleted.
358 cache_elemsize_common_continue_func(struct cache_common_entry_ *entry,
359 struct cache_policy_item_ *item)
362 return ((entry->items_size > entry->common_params.satisf_elemsize) ? 1
367 * Removes the elements from the cache entry, while the continue_func returns 1.
370 flush_cache_policy(struct cache_common_entry_ *entry,
371 struct cache_policy_ *policy,
372 struct cache_policy_ *connected_policy,
373 int (*continue_func)(struct cache_common_entry_ *,
374 struct cache_policy_item_ *))
376 struct cache_policy_item_ *item, *next_item, *connected_item;
377 struct cache_ht_item_ *ht_item;
378 struct cache_ht_item_data_ *ht_item_data, ht_key;
379 hashtable_index_t hash;
381 assert(policy != NULL);
384 item = policy->get_first_item_func(policy);
385 while ((item != NULL) && (continue_func(entry, item) == 1)) {
386 next_item = policy->get_next_item_func(policy, item);
388 connected_item = item->connected_item;
389 policy->remove_item_func(policy, item);
391 memset(&ht_key, 0, sizeof(struct cache_ht_item_data_));
392 ht_key.key = item->key;
393 ht_key.key_size = item->key_size;
395 hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &entry->items,
398 assert(hash < HASHTABLE_ENTRIES_COUNT(&entry->items));
400 ht_item = HASHTABLE_GET_ENTRY(&(entry->items), hash);
401 ht_item_data = HASHTABLE_ENTRY_FIND(cache_ht_, ht_item,
403 assert(ht_item_data != NULL);
404 free(ht_item_data->key);
405 free(ht_item_data->value);
406 HASHTABLE_ENTRY_REMOVE(cache_ht_, ht_item, ht_item_data);
409 policy->destroy_item_func(item);
411 if (connected_item != NULL) {
412 connected_policy->remove_item_func(connected_policy,
414 connected_policy->destroy_item_func(connected_item);
422 flush_cache_entry(struct cache_entry_ *entry)
424 struct cache_mp_entry_ *mp_entry;
425 struct cache_common_entry_ *common_entry;
426 struct cache_policy_ *policy, *connected_policy;
428 connected_policy = NULL;
429 if (entry->params->entry_type == CET_COMMON) {
430 common_entry = (struct cache_common_entry_ *)entry;
431 if ((common_entry->common_params.max_lifetime.tv_sec != 0) ||
432 (common_entry->common_params.max_lifetime.tv_usec != 0)) {
434 policy = common_entry->policies[0];
435 if (common_entry->policies_size > 1)
436 connected_policy = common_entry->policies[1];
438 flush_cache_policy(common_entry, policy,
440 cache_lifetime_common_continue_func);
444 if ((common_entry->common_params.max_elemsize != 0) &&
445 common_entry->items_size >
446 common_entry->common_params.max_elemsize) {
448 if (common_entry->policies_size > 1) {
449 policy = common_entry->policies[1];
450 connected_policy = common_entry->policies[0];
452 policy = common_entry->policies[0];
453 connected_policy = NULL;
456 flush_cache_policy(common_entry, policy,
458 cache_elemsize_common_continue_func);
461 mp_entry = (struct cache_mp_entry_ *)entry;
463 if ((mp_entry->mp_params.max_lifetime.tv_sec != 0)
464 || (mp_entry->mp_params.max_lifetime.tv_usec != 0)) {
466 if (mp_entry->last_request_time.tv_sec -
467 mp_entry->last_request_time.tv_sec >
468 mp_entry->mp_params.max_lifetime.tv_sec)
469 clear_cache_entry(entry);
475 init_cache(struct cache_params const *params)
477 struct cache_ *retval;
479 TRACE_IN(init_cache);
480 assert(params != NULL);
482 retval = (struct cache_ *)malloc(sizeof(struct cache_));
483 assert(retval != NULL);
484 memset(retval, 0, sizeof(struct cache_));
486 assert(params != NULL);
487 memcpy(&retval->params, params, sizeof(struct cache_params));
489 retval->entries = (struct cache_entry_ **)malloc(
490 sizeof(struct cache_entry_ *) * INITIAL_ENTRIES_CAPACITY);
491 assert(retval->entries != NULL);
492 memset(retval->entries, 0, sizeof(sizeof(struct cache_entry_ *)
493 * INITIAL_ENTRIES_CAPACITY));
495 retval->entries_capacity = INITIAL_ENTRIES_CAPACITY;
496 retval->entries_size = 0;
498 TRACE_OUT(init_cache);
503 destroy_cache(struct cache_ *the_cache)
506 TRACE_IN(destroy_cache);
507 assert(the_cache != NULL);
509 if (the_cache->entries != NULL) {
511 for (i = 0; i < the_cache->entries_size; ++i)
512 destroy_cache_entry(the_cache->entries[i]);
514 free(the_cache->entries);
518 TRACE_OUT(destroy_cache);
522 register_cache_entry(struct cache_ *the_cache,
523 struct cache_entry_params const *params)
526 size_t entry_name_size;
527 struct cache_common_entry_ *new_common_entry;
528 struct cache_mp_entry_ *new_mp_entry;
530 TRACE_IN(register_cache_entry);
531 assert(the_cache != NULL);
533 if (find_cache_entry(the_cache, params->entry_name) != NULL) {
534 TRACE_OUT(register_cache_entry);
538 if (the_cache->entries_size == the_cache->entries_capacity) {
539 struct cache_entry_ **new_entries;
542 new_capacity = the_cache->entries_capacity +
543 ENTRIES_CAPACITY_STEP;
544 new_entries = (struct cache_entry_ **)malloc(
545 sizeof(struct cache_entry_ *) * new_capacity);
546 assert(new_entries != NULL);
548 memset(new_entries, 0, sizeof(struct cache_entry_ *) *
550 memcpy(new_entries, the_cache->entries,
551 sizeof(struct cache_entry_ *)
552 * the_cache->entries_size);
554 free(the_cache->entries);
555 the_cache->entries = new_entries;
558 entry_name_size = strlen(params->entry_name);
559 switch (params->entry_type)
562 new_common_entry = (struct cache_common_entry_ *)malloc(
563 sizeof(struct cache_common_entry_));
564 assert(new_common_entry != NULL);
565 memset(new_common_entry, 0, sizeof(struct cache_common_entry_));
567 memcpy(&new_common_entry->common_params, params,
568 sizeof(struct common_cache_entry_params));
569 new_common_entry->params =
570 (struct cache_entry_params *)&new_common_entry->common_params;
572 new_common_entry->common_params.entry_name = (char *)malloc(
574 assert(new_common_entry->common_params.entry_name != NULL);
575 memset(new_common_entry->common_params.entry_name, 0,
576 entry_name_size + 1);
577 strncpy(new_common_entry->common_params.entry_name,
578 params->entry_name, entry_name_size);
579 new_common_entry->name =
580 new_common_entry->common_params.entry_name;
582 HASHTABLE_INIT(&(new_common_entry->items),
583 struct cache_ht_item_data_, data,
584 new_common_entry->common_params.cache_entries_size);
586 if (new_common_entry->common_params.policy == CPT_FIFO)
591 new_common_entry->policies = (struct cache_policy_ **)malloc(
592 sizeof(struct cache_policy_ *) * policies_size);
593 assert(new_common_entry->policies != NULL);
594 memset(new_common_entry->policies, 0,
595 sizeof(struct cache_policy_ *) * policies_size);
597 new_common_entry->policies_size = policies_size;
598 new_common_entry->policies[0] = init_cache_fifo_policy();
600 if (policies_size > 1) {
601 switch (new_common_entry->common_params.policy) {
603 new_common_entry->policies[1] =
604 init_cache_lru_policy();
607 new_common_entry->policies[1] =
608 init_cache_lfu_policy();
615 new_common_entry->get_time_func =
616 the_cache->params.get_time_func;
617 the_cache->entries[the_cache->entries_size++] =
618 (struct cache_entry_ *)new_common_entry;
621 new_mp_entry = (struct cache_mp_entry_ *)malloc(
622 sizeof(struct cache_mp_entry_));
623 assert(new_mp_entry != NULL);
624 memset(new_mp_entry, 0, sizeof(struct cache_mp_entry_));
626 memcpy(&new_mp_entry->mp_params, params,
627 sizeof(struct mp_cache_entry_params));
628 new_mp_entry->params =
629 (struct cache_entry_params *)&new_mp_entry->mp_params;
631 new_mp_entry->mp_params.entry_name = (char *)malloc(
633 assert(new_mp_entry->mp_params.entry_name != NULL);
634 memset(new_mp_entry->mp_params.entry_name, 0,
635 entry_name_size + 1);
636 strncpy(new_mp_entry->mp_params.entry_name, params->entry_name,
638 new_mp_entry->name = new_mp_entry->mp_params.entry_name;
640 TAILQ_INIT(&new_mp_entry->ws_head);
641 TAILQ_INIT(&new_mp_entry->rs_head);
643 new_mp_entry->get_time_func = the_cache->params.get_time_func;
644 the_cache->entries[the_cache->entries_size++] =
645 (struct cache_entry_ *)new_mp_entry;
650 qsort(the_cache->entries, the_cache->entries_size,
651 sizeof(struct cache_entry_ *), entries_qsort_cmp_func);
653 TRACE_OUT(register_cache_entry);
658 unregister_cache_entry(struct cache_ *the_cache, const char *entry_name)
660 struct cache_entry_ **del_ent;
662 TRACE_IN(unregister_cache_entry);
663 assert(the_cache != NULL);
665 del_ent = find_cache_entry_p(the_cache, entry_name);
666 if (del_ent != NULL) {
667 destroy_cache_entry(*del_ent);
668 --the_cache->entries_size;
670 memmove(del_ent, del_ent + 1,
671 (&(the_cache->entries[--the_cache->entries_size]) -
672 del_ent) * sizeof(struct cache_entry_ *));
674 TRACE_OUT(unregister_cache_entry);
677 TRACE_OUT(unregister_cache_entry);
682 struct cache_entry_ *
683 find_cache_entry(struct cache_ *the_cache, const char *entry_name)
685 struct cache_entry_ **result;
687 TRACE_IN(find_cache_entry);
688 result = find_cache_entry_p(the_cache, entry_name);
690 if (result == NULL) {
691 TRACE_OUT(find_cache_entry);
694 TRACE_OUT(find_cache_entry);
700 * Tries to read the element with the specified key from the cache. If the
701 * value_size is too small, it will be filled with the proper number, and
702 * the user will need to call cache_read again with the value buffer, that
704 * Function returns 0 on success, -1 on error, and -2 if the value_size is too
708 cache_read(struct cache_entry_ *entry, const char *key, size_t key_size,
709 char *value, size_t *value_size)
711 struct cache_common_entry_ *common_entry;
712 struct cache_ht_item_data_ item_data, *find_res;
713 struct cache_ht_item_ *item;
714 hashtable_index_t hash;
715 struct cache_policy_item_ *connected_item;
717 TRACE_IN(cache_read);
718 assert(entry != NULL);
720 assert(value_size != NULL);
721 assert(entry->params->entry_type == CET_COMMON);
723 common_entry = (struct cache_common_entry_ *)entry;
725 memset(&item_data, 0, sizeof(struct cache_ht_item_data_));
726 /* can't avoid the cast here */
727 item_data.key = (char *)key;
728 item_data.key_size = key_size;
730 hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &common_entry->items,
733 assert(hash < HASHTABLE_ENTRIES_COUNT(&common_entry->items));
735 item = HASHTABLE_GET_ENTRY(&(common_entry->items), hash);
736 find_res = HASHTABLE_ENTRY_FIND(cache_ht_, item, &item_data);
737 if (find_res == NULL) {
738 TRACE_OUT(cache_read);
742 if ((common_entry->common_params.max_lifetime.tv_sec != 0) ||
743 (common_entry->common_params.max_lifetime.tv_usec != 0)) {
745 if (find_res->fifo_policy_item->last_request_time.tv_sec -
746 find_res->fifo_policy_item->creation_time.tv_sec >
747 common_entry->common_params.max_lifetime.tv_sec) {
750 free(find_res->value);
753 find_res->fifo_policy_item->connected_item;
754 if (connected_item != NULL) {
755 common_entry->policies[1]->remove_item_func(
756 common_entry->policies[1],
758 common_entry->policies[1]->destroy_item_func(
762 common_entry->policies[0]->remove_item_func(
763 common_entry->policies[0],
764 find_res->fifo_policy_item);
765 common_entry->policies[0]->destroy_item_func(
766 find_res->fifo_policy_item);
768 HASHTABLE_ENTRY_REMOVE(cache_ht_, item, find_res);
769 --common_entry->items_size;
773 if ((*value_size < find_res->value_size) || (value == NULL)) {
774 *value_size = find_res->value_size;
775 TRACE_OUT(cache_read);
779 *value_size = find_res->value_size;
780 memcpy(value, find_res->value, find_res->value_size);
782 ++find_res->fifo_policy_item->request_count;
783 common_entry->get_time_func(
784 &find_res->fifo_policy_item->last_request_time);
785 common_entry->policies[0]->update_item_func(common_entry->policies[0],
786 find_res->fifo_policy_item);
788 if (find_res->fifo_policy_item->connected_item != NULL) {
789 connected_item = find_res->fifo_policy_item->connected_item;
790 memcpy(&connected_item->last_request_time,
791 &find_res->fifo_policy_item->last_request_time,
792 sizeof(struct timeval));
793 connected_item->request_count =
794 find_res->fifo_policy_item->request_count;
796 common_entry->policies[1]->update_item_func(
797 common_entry->policies[1], connected_item);
800 TRACE_OUT(cache_read);
805 * Writes the value with the specified key into the cache entry.
806 * Functions returns 0 on success, and -1 on error.
809 cache_write(struct cache_entry_ *entry, const char *key, size_t key_size,
810 char const *value, size_t value_size)
812 struct cache_common_entry_ *common_entry;
813 struct cache_ht_item_data_ item_data, *find_res;
814 struct cache_ht_item_ *item;
815 hashtable_index_t hash;
817 struct cache_policy_ *policy, *connected_policy;
818 struct cache_policy_item_ *policy_item;
819 struct cache_policy_item_ *connected_policy_item;
821 TRACE_IN(cache_write);
822 assert(entry != NULL);
824 assert(value != NULL);
825 assert(entry->params->entry_type == CET_COMMON);
827 common_entry = (struct cache_common_entry_ *)entry;
829 memset(&item_data, 0, sizeof(struct cache_ht_item_data_));
830 /* can't avoid the cast here */
831 item_data.key = (char *)key;
832 item_data.key_size = key_size;
834 hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &common_entry->items,
837 assert(hash < HASHTABLE_ENTRIES_COUNT(&common_entry->items));
839 item = HASHTABLE_GET_ENTRY(&(common_entry->items), hash);
840 find_res = HASHTABLE_ENTRY_FIND(cache_ht_, item, &item_data);
841 if (find_res != NULL) {
842 TRACE_OUT(cache_write);
846 item_data.key = (char *)malloc(key_size);
847 memcpy(item_data.key, key, key_size);
849 item_data.value = (char *)malloc(value_size);
850 assert(item_data.value != NULL);
852 memcpy(item_data.value, value, value_size);
853 item_data.value_size = value_size;
855 policy_item = common_entry->policies[0]->create_item_func();
856 policy_item->key = item_data.key;
857 policy_item->key_size = item_data.key_size;
858 common_entry->get_time_func(&policy_item->creation_time);
860 if (common_entry->policies_size > 1) {
861 connected_policy_item =
862 common_entry->policies[1]->create_item_func();
863 memcpy(&connected_policy_item->creation_time,
864 &policy_item->creation_time,
865 sizeof(struct timeval));
866 connected_policy_item->key = policy_item->key;
867 connected_policy_item->key_size = policy_item->key_size;
869 connected_policy_item->connected_item = policy_item;
870 policy_item->connected_item = connected_policy_item;
873 item_data.fifo_policy_item = policy_item;
875 common_entry->policies[0]->add_item_func(common_entry->policies[0],
877 if (common_entry->policies_size > 1)
878 common_entry->policies[1]->add_item_func(
879 common_entry->policies[1], connected_policy_item);
881 HASHTABLE_ENTRY_STORE(cache_ht_, item, &item_data);
882 ++common_entry->items_size;
884 if ((common_entry->common_params.max_elemsize != 0) &&
885 (common_entry->items_size >
886 common_entry->common_params.max_elemsize)) {
887 if (common_entry->policies_size > 1) {
888 policy = common_entry->policies[1];
889 connected_policy = common_entry->policies[0];
891 policy = common_entry->policies[0];
892 connected_policy = NULL;
895 flush_cache_policy(common_entry, policy, connected_policy,
896 cache_elemsize_common_continue_func);
899 TRACE_OUT(cache_write);
904 * Initializes the write session for the specified multipart entry. This
905 * session then should be filled with data either committed or abandoned by
906 * using close_cache_mp_write_session or abandon_cache_mp_write_session
908 * Returns NULL on errors (when there are too many opened write sessions for
911 struct cache_mp_write_session_ *
912 open_cache_mp_write_session(struct cache_entry_ *entry)
914 struct cache_mp_entry_ *mp_entry;
915 struct cache_mp_write_session_ *retval;
917 TRACE_IN(open_cache_mp_write_session);
918 assert(entry != NULL);
919 assert(entry->params->entry_type == CET_MULTIPART);
920 mp_entry = (struct cache_mp_entry_ *)entry;
922 if ((mp_entry->mp_params.max_sessions > 0) &&
923 (mp_entry->ws_size == mp_entry->mp_params.max_sessions)) {
924 TRACE_OUT(open_cache_mp_write_session);
928 retval = (struct cache_mp_write_session_ *)malloc(
929 sizeof(struct cache_mp_write_session_));
930 assert(retval != NULL);
931 memset(retval, 0, sizeof(struct cache_mp_write_session_));
933 TAILQ_INIT(&retval->items);
934 retval->parent_entry = mp_entry;
936 TAILQ_INSERT_HEAD(&mp_entry->ws_head, retval, entries);
939 TRACE_OUT(open_cache_mp_write_session);
944 * Writes data to the specified session. Return 0 on success and -1 on errors
945 * (when write session size limit is exceeded).
948 cache_mp_write(struct cache_mp_write_session_ *ws, char *data,
951 struct cache_mp_data_item_ *new_item;
953 TRACE_IN(cache_mp_write);
955 assert(ws->parent_entry != NULL);
956 assert(ws->parent_entry->params->entry_type == CET_MULTIPART);
958 if ((ws->parent_entry->mp_params.max_elemsize > 0) &&
959 (ws->parent_entry->mp_params.max_elemsize == ws->items_size)) {
960 TRACE_OUT(cache_mp_write);
964 new_item = (struct cache_mp_data_item_ *)malloc(
965 sizeof(struct cache_mp_data_item_));
966 assert(new_item != NULL);
967 memset(new_item, 0, sizeof(struct cache_mp_data_item_));
969 new_item->value = (char *)malloc(data_size);
970 assert(new_item->value != NULL);
971 memcpy(new_item->value, data, data_size);
972 new_item->value_size = data_size;
974 TAILQ_INSERT_TAIL(&ws->items, new_item, entries);
977 TRACE_OUT(cache_mp_write);
982 * Abandons the write session and frees all the connected resources.
985 abandon_cache_mp_write_session(struct cache_mp_write_session_ *ws)
988 TRACE_IN(abandon_cache_mp_write_session);
990 assert(ws->parent_entry != NULL);
991 assert(ws->parent_entry->params->entry_type == CET_MULTIPART);
993 TAILQ_REMOVE(&ws->parent_entry->ws_head, ws, entries);
994 --ws->parent_entry->ws_size;
996 destroy_cache_mp_write_session(ws);
997 TRACE_OUT(abandon_cache_mp_write_session);
1001 * Commits the session to the entry, for which it was created.
1004 close_cache_mp_write_session(struct cache_mp_write_session_ *ws)
1007 TRACE_IN(close_cache_mp_write_session);
1009 assert(ws->parent_entry != NULL);
1010 assert(ws->parent_entry->params->entry_type == CET_MULTIPART);
1012 TAILQ_REMOVE(&ws->parent_entry->ws_head, ws, entries);
1013 --ws->parent_entry->ws_size;
1015 if (ws->parent_entry->completed_write_session == NULL) {
1017 * If there is no completed session yet, this will be the one
1019 ws->parent_entry->get_time_func(
1020 &ws->parent_entry->creation_time);
1021 ws->parent_entry->completed_write_session = ws;
1024 * If there is a completed session, then we'll save our session
1025 * as a pending session. If there is already a pending session,
1026 * it would be destroyed.
1028 if (ws->parent_entry->pending_write_session != NULL)
1029 destroy_cache_mp_write_session(
1030 ws->parent_entry->pending_write_session);
1032 ws->parent_entry->pending_write_session = ws;
1034 TRACE_OUT(close_cache_mp_write_session);
1038 * Opens read session for the specified entry. Returns NULL on errors (when
1039 * there are no data in the entry, or the data are obsolete).
1041 struct cache_mp_read_session_ *
1042 open_cache_mp_read_session(struct cache_entry_ *entry)
1044 struct cache_mp_entry_ *mp_entry;
1045 struct cache_mp_read_session_ *retval;
1047 TRACE_IN(open_cache_mp_read_session);
1048 assert(entry != NULL);
1049 assert(entry->params->entry_type == CET_MULTIPART);
1050 mp_entry = (struct cache_mp_entry_ *)entry;
1052 if (mp_entry->completed_write_session == NULL) {
1053 TRACE_OUT(open_cache_mp_read_session);
1057 if ((mp_entry->mp_params.max_lifetime.tv_sec != 0)
1058 || (mp_entry->mp_params.max_lifetime.tv_usec != 0)) {
1059 if (mp_entry->last_request_time.tv_sec -
1060 mp_entry->last_request_time.tv_sec >
1061 mp_entry->mp_params.max_lifetime.tv_sec) {
1062 flush_cache_entry(entry);
1063 TRACE_OUT(open_cache_mp_read_session);
1068 retval = (struct cache_mp_read_session_ *)malloc(
1069 sizeof(struct cache_mp_read_session_));
1070 assert(retval != NULL);
1071 memset(retval, 0, sizeof(struct cache_mp_read_session_));
1073 retval->parent_entry = mp_entry;
1074 retval->current_item = TAILQ_FIRST(
1075 &mp_entry->completed_write_session->items);
1077 TAILQ_INSERT_HEAD(&mp_entry->rs_head, retval, entries);
1078 ++mp_entry->rs_size;
1080 mp_entry->get_time_func(&mp_entry->last_request_time);
1081 TRACE_OUT(open_cache_mp_read_session);
1086 * Reads the data from the read session - step by step.
1087 * Returns 0 on success, -1 on error (when there are no more data), and -2 if
1088 * the data_size is too small. In the last case, data_size would be filled
1092 cache_mp_read(struct cache_mp_read_session_ *rs, char *data, size_t *data_size)
1095 TRACE_IN(cache_mp_read);
1098 if (rs->current_item == NULL) {
1099 TRACE_OUT(cache_mp_read);
1103 if (rs->current_item->value_size > *data_size) {
1104 *data_size = rs->current_item->value_size;
1106 TRACE_OUT(cache_mp_read);
1110 TRACE_OUT(cache_mp_read);
1114 *data_size = rs->current_item->value_size;
1115 memcpy(data, rs->current_item->value, rs->current_item->value_size);
1116 rs->current_item = TAILQ_NEXT(rs->current_item, entries);
1118 TRACE_OUT(cache_mp_read);
1123 * Closes the read session. If there are no more read sessions and there is
1124 * a pending write session, it will be committed and old
1125 * completed_write_session will be destroyed.
1128 close_cache_mp_read_session(struct cache_mp_read_session_ *rs)
1131 TRACE_IN(close_cache_mp_read_session);
1133 assert(rs->parent_entry != NULL);
1135 TAILQ_REMOVE(&rs->parent_entry->rs_head, rs, entries);
1136 --rs->parent_entry->rs_size;
1138 if ((rs->parent_entry->rs_size == 0) &&
1139 (rs->parent_entry->pending_write_session != NULL)) {
1140 destroy_cache_mp_write_session(
1141 rs->parent_entry->completed_write_session);
1142 rs->parent_entry->completed_write_session =
1143 rs->parent_entry->pending_write_session;
1144 rs->parent_entry->pending_write_session = NULL;
1147 destroy_cache_mp_read_session(rs);
1148 TRACE_OUT(close_cache_mp_read_session);
1152 transform_cache_entry(struct cache_entry_ *entry,
1153 enum cache_transformation_t transformation)
1156 TRACE_IN(transform_cache_entry);
1157 switch (transformation) {
1159 clear_cache_entry(entry);
1160 TRACE_OUT(transform_cache_entry);
1163 flush_cache_entry(entry);
1164 TRACE_OUT(transform_cache_entry);
1167 TRACE_OUT(transform_cache_entry);
1173 transform_cache_entry_part(struct cache_entry_ *entry,
1174 enum cache_transformation_t transformation, const char *key_part,
1175 size_t key_part_size, enum part_position_t part_position)
1177 struct cache_common_entry_ *common_entry;
1178 struct cache_ht_item_ *ht_item;
1179 struct cache_ht_item_data_ *ht_item_data, ht_key;
1181 struct cache_policy_item_ *item, *connected_item;
1183 TRACE_IN(transform_cache_entry_part);
1184 if (entry->params->entry_type != CET_COMMON) {
1185 TRACE_OUT(transform_cache_entry_part);
1189 if (transformation != CTT_CLEAR) {
1190 TRACE_OUT(transform_cache_entry_part);
1194 memset(&ht_key, 0, sizeof(struct cache_ht_item_data_));
1195 ht_key.key = (char *)key_part; /* can't avoid casting here */
1196 ht_key.key_size = key_part_size;
1198 common_entry = (struct cache_common_entry_ *)entry;
1199 HASHTABLE_FOREACH(&(common_entry->items), ht_item) {
1201 ht_item_data = HASHTABLE_ENTRY_FIND_SPECIAL(cache_ht_,
1203 ht_items_fixed_size_left_cmp_func);
1205 if (ht_item_data != NULL) {
1206 item = ht_item_data->fifo_policy_item;
1207 connected_item = item->connected_item;
1209 common_entry->policies[0]->remove_item_func(
1210 common_entry->policies[0],
1213 free(ht_item_data->key);
1214 free(ht_item_data->value);
1215 HASHTABLE_ENTRY_REMOVE(cache_ht_, ht_item,
1217 --common_entry->items_size;
1219 common_entry->policies[0]->destroy_item_func(
1221 if (common_entry->policies_size == 2) {
1222 common_entry->policies[1]->remove_item_func(
1223 common_entry->policies[1],
1225 common_entry->policies[1]->destroy_item_func(
1229 } while (ht_item_data != NULL);
1232 TRACE_OUT(transform_cache_entry_part);