/** * Callback for adns_resolve(), invoked when the resolution is complete. */ static void uhc_host_resolved(const host_addr_t *addrs, size_t n, void *uu_udata) { (void) uu_udata; g_assert(addrs); /* * If resolution failed, try again if possible. */ if (0 == n) { if (GNET_PROPERTY(bootstrap_debug)) g_warning("could not resolve UDP host cache \"%s\"", uhc_ctx.host); uhc_try_next(); return; } if (n > 1) { size_t i; host_addr_t *hav; /* Current UHC was moved to tail by uhc_get_next() */ struct uhc *uhc = hash_list_tail(uhc_list); /* * UHC resolved to multiple endpoints. Could be roundrobbin or * IPv4 and IPv6 addresss. Adding them as seperate entries: if the * IPv6 is unreachable we have an opportunity to skip it. * -- JA 24/7/2011 * * Shuffle the address array before appending them to the UHC list. * --RAM, 2015-10-01 */ hav = HCOPY_ARRAY(addrs, n); SHUFFLE_ARRAY_N(hav, n); for (i = 0; i < n; i++) { const char *host = host_addr_port_to_string(hav[i], uhc_ctx.port); g_debug("BOOT UDP host cache \"%s\" resolved to %s (#%zu)", uhc_ctx.host, host, i + 1); uhc_list_append(host); } hash_list_remove(uhc_list, uhc); /* Replaced by IP address list */ uhc_free(&uhc); /* * We're going to continue and process the first address (in our * shuffled array). Make sure it is put at the end of the list * and marked as being used, mimicing what uhc_get_next() would do. * --RAM, 2015-10-01 */ { struct uhc key; key.host = host_addr_port_to_string(hav[0], uhc_ctx.port); uhc = hash_list_lookup(uhc_list, &key); g_assert(uhc != NULL); /* We added the entry above! */ uhc->stamp = tm_time(); uhc->used++; hash_list_moveto_tail(uhc_list, uhc); } uhc_ctx.addr = hav[0]; /* Struct copy */ HFREE_NULL(hav); } else { uhc_ctx.addr = addrs[0]; } if (GNET_PROPERTY(bootstrap_debug)) g_debug("BOOT UDP host cache \"%s\" resolved to %s", uhc_ctx.host, host_addr_to_string(uhc_ctx.addr)); /* * Now send the ping. */ uhc_send_ping(); }
/** * Get a new index in the cache, and update LRU data structures. * * @param db the database * @param num page number in the DB for which we want a cache index * * * @return -1 on error, or the allocated cache index. */ static int getidx(DBM *db, long num) { struct lru_cache *cache = db->cache; long n; /* Cache index */ /* * If we invalidated pages, reuse their indices. * If we have not used all the pages yet, get the next one. * Otherwise, use the least-recently requested page. */ if (slist_length(cache->available)) { void *v = slist_shift(cache->available); n = pointer_to_int(v); g_assert(n >= 0 && n < cache->pages); g_assert(!cache->dirty[n]); g_assert(-1 == cache->numpag[n]); hash_list_prepend(cache->used, int_to_pointer(n)); } else if (cache->next < cache->pages) { n = cache->next++; cache->dirty[n] = FALSE; hash_list_prepend(cache->used, int_to_pointer(n)); } else { void *last = hash_list_tail(cache->used); long oldnum; gboolean had_ioerr = booleanize(db->flags & DBM_IOERR_W); hash_list_moveto_head(cache->used, last); n = pointer_to_int(last); /* * This page is no longer cached as its cache index is being reused * Flush it to disk if dirty before discarding it. */ g_assert(n >= 0 && n < cache->pages); oldnum = cache->numpag[n]; if (cache->dirty[n] && !writebuf(db, oldnum, n)) { hash_list_iter_t *iter; void *item; gboolean found = FALSE; /* * Cannot flush dirty page now, probably because we ran out of * disk space. Look through the cache whether we can reuse a * non-dirty page instead, which would let us keep the dirty * page a little longer in the cache, in the hope it can then * be properly flushed later. */ iter = hash_list_iterator_tail(cache->used); while (NULL != (item = hash_list_iter_previous(iter))) { long i = pointer_to_int(item); g_assert(i >= 0 && i < cache->pages); if (!cache->dirty[i]) { found = TRUE; /* OK, reuse cache slot #i then */ n = i; oldnum = cache->numpag[i]; break; } } hash_list_iter_release(&iter); if (found) { g_assert(item != NULL); hash_list_moveto_head(cache->used, item); /* * Clear error condition if we had none prior to the flush * attempt, since we can do without it for now. */ if (!had_ioerr) db->flags &= ~DBM_IOERR_W; g_warning("sdbm: \"%s\": " "reusing cache slot used by clean page #%ld instead", sdbm_name(db), oldnum); } else { g_warning("sdbm: \"%s\": cannot discard dirty page #%ld", sdbm_name(db), oldnum); return -1; } } g_hash_table_remove(cache->pagnum, ulong_to_pointer(oldnum)); cache->dirty[n] = FALSE; } /* * Record the association between the cache index and the page number. */ g_assert(n >= 0 && n < cache->pages); cache->numpag[n] = num; g_hash_table_insert(cache->pagnum, ulong_to_pointer(num), int_to_pointer(n)); return n; }