]> git.meshlink.io Git - catta/blob - cache.c
b668d3338730612f646951ac90a9c44f676764bb
[catta] / cache.c
1 #include <string.h>
2
3 #include "cache.h"
4
5 static void remove_entry(flxCache *c, flxCacheEntry *e, gboolean remove_from_hash_table) {
6     g_assert(c);
7     g_assert(e);
8
9     g_message("remvoin from cache: %p %p", c, e);
10     
11     if (remove_from_hash_table) {
12         flxCacheEntry *t;
13         t = g_hash_table_lookup(c->hash_table, e->record->key);
14         FLX_LLIST_REMOVE(flxCacheEntry, by_name, t, e);
15         if (t)
16             g_hash_table_replace(c->hash_table, t->record->key, t);
17         else
18             g_hash_table_remove(c->hash_table, e->record->key);
19     }
20         
21     flx_record_unref(e->record);
22
23     if (e->time_event)
24         flx_time_event_queue_remove(c->server->time_event_queue, e->time_event);
25     
26     g_free(e);
27 }
28
29 flxCache *flx_cache_new(flxServer *server, flxInterface *iface) {
30     flxCache *c;
31     g_assert(server);
32
33     c = g_new(flxCache, 1);
34     c->server = server;
35     c->interface = iface;
36     c->hash_table = g_hash_table_new((GHashFunc) flx_key_hash, (GEqualFunc) flx_key_equal);
37
38     return c;
39 }
40
41 gboolean remove_func(gpointer key, gpointer value, gpointer user_data) {
42     flxCacheEntry *e, *next;
43
44     for (e = value; e; e = next) {
45         next = e->by_name_next;
46         remove_entry(user_data, e, FALSE);
47     }
48     
49     return TRUE;
50 }
51
52 void flx_cache_free(flxCache *c) {
53     g_assert(c);
54
55     g_hash_table_foreach_remove(c->hash_table, remove_func, c);
56     g_hash_table_destroy(c->hash_table);
57     
58     g_free(c);
59 }
60
61 flxCacheEntry *flx_cache_lookup_key(flxCache *c, flxKey *k) {
62     g_assert(c);
63     g_assert(k);
64
65     return g_hash_table_lookup(c->hash_table, k);
66 }
67
68 flxCacheEntry *flx_cache_lookup_record(flxCache *c, flxRecord *r) {
69     flxCacheEntry *e;
70     g_assert(c);
71     g_assert(r);
72
73     for (e = flx_cache_lookup_key(c, r->key); e; e = e->by_name_next)
74         if (e->record->size == r->size && !memcmp(e->record->data, r->data, r->size))
75             return e;
76
77     return NULL;
78 }
79
80 static void next_expiry(flxCache *c, flxCacheEntry *e, guint percent);
81
82 static void elapse_func(flxTimeEvent *t, void *userdata) {
83     flxCacheEntry *e = userdata;
84     
85     g_assert(t);
86     g_assert(e);
87
88     if (e->state == FLX_CACHE_FINAL) {
89         remove_entry(e->cache, e, TRUE);
90         g_message("Removing entry from cache due to expiration");
91     } else {
92         guint percent = 0;
93     
94         switch (e->state) {
95             case FLX_CACHE_VALID:
96                 e->state = FLX_CACHE_EXPIRY1;
97                 percent = 85;
98                 break;
99                 
100             case FLX_CACHE_EXPIRY1:
101                 e->state = FLX_CACHE_EXPIRY2;
102                 percent = 90;
103                 break;
104             case FLX_CACHE_EXPIRY2:
105                 e->state = FLX_CACHE_EXPIRY3;
106                 percent = 95;
107                 break;
108                 
109             case FLX_CACHE_EXPIRY3:
110                 e->state = FLX_CACHE_FINAL;
111                 percent = 100;
112                 break;
113
114             default:
115                 ;
116         }
117
118         g_assert(percent > 0);
119
120         g_message("Requesting cache entry update at %i%%.", percent);
121
122         /* Request a cache update */
123         flx_interface_post_query(e->cache->interface, e->record->key);
124
125         /* Check again later */
126         next_expiry(e->cache, e, percent);
127     }
128 }
129
130 static void next_expiry(flxCache *c, flxCacheEntry *e, guint percent) {
131     gulong usec;
132
133     g_assert(c);
134     g_assert(e);
135     g_assert(percent > 0 && percent <= 100);
136
137     e->expiry = e->timestamp;
138
139     usec = e->record->ttl * 10000;
140
141     /* 2% jitter */
142     usec = g_random_int_range(usec*percent, usec*(percent+2));
143     
144     g_time_val_add(&e->expiry, usec);
145
146     if (e->time_event)
147         flx_time_event_queue_update(c->server->time_event_queue, e->time_event, &e->expiry);
148     else
149         e->time_event = flx_time_event_queue_add(c->server->time_event_queue, &e->expiry, elapse_func, e);
150 }
151
152 flxCacheEntry *flx_cache_update(flxCache *c, flxRecord *r, gboolean unique, const flxAddress *a) {
153     flxCacheEntry *e, *t;
154     gchar *txt;
155     
156     g_assert(c);
157     g_assert(r && r->ref >= 1);
158
159     g_message("cache update: %s", (txt = flx_record_to_string(r)));
160     g_free(txt);
161
162     if ((t = e = flx_cache_lookup_key(c, r->key))) {
163
164 /*         g_message("found prev cache entry"); */
165
166         if (unique) {
167             /* Drop all entries but the first which we replace */
168             while (e->by_name_next)
169                 remove_entry(c, e->by_name_next, TRUE);
170
171         } else {
172             /* Look for exactly the same entry */
173             for (; e; e = e->by_name_next)
174                 if (flx_record_equal(e->record, r))
175                     break;
176         }
177     }
178     
179     if (e) {
180
181 /*         g_message("found matching cache entry"); */
182
183         /* We are the first in the linked list so let's replace the hash table key with the new one */
184         if (e->by_name_prev == NULL)
185             g_hash_table_replace(c->hash_table, r->key, e);
186         
187         /* Update the record */
188         flx_record_unref(e->record);
189         e->record = flx_record_ref(r);
190
191         
192     } else {
193         /* No entry found, therefore we create a new one */
194
195 /*         g_message("couldn't find matching cache entry"); */
196         
197         e = g_new(flxCacheEntry, 1);
198         e->cache = c;
199         e->time_event = NULL;
200         e->record = flx_record_ref(r);
201         FLX_LLIST_PREPEND(flxCacheEntry, by_name, t, e);
202         g_hash_table_replace(c->hash_table, e->record->key, t);
203     } 
204
205     e->origin = *a;
206     g_get_current_time(&e->timestamp);
207     next_expiry(c, e, 80);
208     e->state = FLX_CACHE_VALID;
209
210     return e;
211 }
212
213 void flx_cache_drop_key(flxCache *c, flxKey *k) {
214     flxCacheEntry *e;
215     
216     g_assert(c);
217     g_assert(k);
218
219     while ((e = flx_cache_lookup_key(c, k)))
220         remove_entry(c, e, TRUE);
221 }
222
223 void flx_cache_drop_record(flxCache *c, flxRecord *r) {
224     flxCacheEntry *e;
225     
226     g_assert(c);
227     g_assert(r);
228
229     if ((e = flx_cache_lookup_record(c, r))) 
230         remove_entry(c, e, TRUE);
231 }
232
233 static void func(gpointer key, gpointer data, gpointer userdata) {
234     flxCacheEntry *e = data;
235     flxKey *k = key;
236     gchar *t;
237
238     t = flx_record_to_string(e->record);
239     fprintf((FILE*) userdata, "%s\n", t);
240     g_free(t);
241 }
242
243 void flx_cache_dump(flxCache *c, FILE *f) {
244     g_assert(c);
245     g_assert(f);
246
247     fprintf(f, ";;; CACHE DUMP FOLLOWS ;;;\n");
248     g_hash_table_foreach(c->hash_table, func, f);
249 }