5 static void remove_entry(flxCache *c, flxCacheEntry *e, gboolean remove_from_hash_table) {
9 g_message("removing from cache: %p %p", c, e);
11 if (remove_from_hash_table) {
13 t = g_hash_table_lookup(c->hash_table, e->record->key);
14 FLX_LLIST_REMOVE(flxCacheEntry, by_name, t, e);
16 g_hash_table_replace(c->hash_table, t->record->key, t);
18 g_hash_table_remove(c->hash_table, e->record->key);
22 flx_time_event_queue_remove(c->server->time_event_queue, e->time_event);
24 flx_subscription_notify(c->server, c->interface, e->record, FLX_SUBSCRIPTION_REMOVE);
26 flx_record_unref(e->record);
31 flxCache *flx_cache_new(flxServer *server, flxInterface *iface) {
35 c = g_new(flxCache, 1);
38 c->hash_table = g_hash_table_new((GHashFunc) flx_key_hash, (GEqualFunc) flx_key_equal);
43 gboolean remove_func(gpointer key, gpointer value, gpointer user_data) {
44 flxCacheEntry *e, *next;
46 for (e = value; e; e = next) {
47 next = e->by_name_next;
48 remove_entry(user_data, e, FALSE);
54 void flx_cache_free(flxCache *c) {
57 g_hash_table_foreach_remove(c->hash_table, remove_func, c);
58 g_hash_table_destroy(c->hash_table);
63 flxCacheEntry *flx_cache_lookup_key(flxCache *c, flxKey *k) {
67 return g_hash_table_lookup(c->hash_table, k);
70 flxCacheEntry *flx_cache_lookup_record(flxCache *c, flxRecord *r) {
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))
82 static void next_expiry(flxCache *c, flxCacheEntry *e, guint percent);
84 static void elapse_func(flxTimeEvent *t, void *userdata) {
85 flxCacheEntry *e = userdata;
90 if (e->state == FLX_CACHE_FINAL) {
91 remove_entry(e->cache, e, TRUE);
92 g_message("Removing entry from cache due to expiration");
98 e->state = FLX_CACHE_EXPIRY1;
102 case FLX_CACHE_EXPIRY1:
103 e->state = FLX_CACHE_EXPIRY2;
106 case FLX_CACHE_EXPIRY2:
107 e->state = FLX_CACHE_EXPIRY3;
111 case FLX_CACHE_EXPIRY3:
112 e->state = FLX_CACHE_FINAL;
120 g_assert(percent > 0);
122 g_message("Requesting cache entry update at %i%%.", percent);
124 /* Request a cache update */
125 flx_interface_post_query(e->cache->interface, e->record->key);
127 /* Check again later */
128 next_expiry(e->cache, e, percent);
132 static void update_time_event(flxCache *c, flxCacheEntry *e) {
137 flx_time_event_queue_update(c->server->time_event_queue, e->time_event, &e->expiry);
139 e->time_event = flx_time_event_queue_add(c->server->time_event_queue, &e->expiry, elapse_func, e);
142 static void next_expiry(flxCache *c, flxCacheEntry *e, guint percent) {
147 g_assert(percent > 0 && percent <= 100);
149 e->expiry = e->timestamp;
151 usec = e->record->ttl * 10000;
154 usec = g_random_int_range(usec*percent, usec*(percent+2));
156 g_time_val_add(&e->expiry, usec);
157 update_time_event(c, e);
160 void flx_cache_update(flxCache *c, flxRecord *r, gboolean unique, const flxAddress *a) {
161 flxCacheEntry *e, *t;
165 g_assert(r && r->ref >= 1);
167 g_message("cache update: %s", (txt = flx_record_to_string(r)));
172 /* This is a goodbye request */
174 if ((e = flx_cache_lookup_record(c, r))) {
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);
185 /* This is an update request */
187 if ((t = e = flx_cache_lookup_key(c, r->key))) {
191 /* For unique records, remove all entries but one */
192 while (e->by_name_next)
193 remove_entry(c, e->by_name_next, TRUE);
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))
206 /* g_message("found matching cache entry"); */
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);
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);
216 /* Update the record */
217 flx_record_unref(e->record);
218 e->record = flx_record_ref(r);
221 /* No entry found, therefore we create a new one */
223 /* g_message("couldn't find matching cache entry"); */
225 e = g_new(flxCacheEntry, 1);
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);
232 /* Notify subscribers */
233 flx_subscription_notify(c->server, c->interface, e->record, FLX_SUBSCRIPTION_NEW);
237 g_get_current_time(&e->timestamp);
238 next_expiry(c, e, 80);
239 e->state = FLX_CACHE_VALID;
243 void flx_cache_drop_key(flxCache *c, flxKey *k) {
249 while ((e = flx_cache_lookup_key(c, k)))
250 remove_entry(c, e, TRUE);
253 void flx_cache_drop_record(flxCache *c, flxRecord *r) {
259 if ((e = flx_cache_lookup_record(c, r)))
260 remove_entry(c, e, TRUE);
263 static void func(gpointer key, gpointer data, gpointer userdata) {
264 flxCacheEntry *e = data;
268 t = flx_record_to_string(e->record);
269 fprintf((FILE*) userdata, "%s\n", t);
273 void flx_cache_dump(flxCache *c, FILE *f) {
277 fprintf(f, ";;; CACHE DUMP FOLLOWS ;;;\n");
278 g_hash_table_foreach(c->hash_table, func, f);