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