diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2020-10-31 10:01:39 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2020-10-31 10:04:03 +0200 |
commit | 0916f224575687b444c1026d9df84e78dfaf3190 (patch) | |
tree | 58c9b82fd9d6a0385e37ba1e1d2da74dfe7f8391 | |
parent | 274985d77bf2e69dec8d7eb90bd3f752f115f6e9 (diff) | |
download | gdbm-0916f224575687b444c1026d9df84e78dfaf3190.tar.gz |
Recover from ENOMEM in cache_lookup
* src/bucket.c (cache_lookup): Try to recover from ENOMEM by freeing
the least recently used cache entry.
-rw-r--r-- | src/bucket.c | 50 |
1 files changed, 35 insertions, 15 deletions
diff --git a/src/bucket.c b/src/bucket.c index 979150a..66b6b59 100644 --- a/src/bucket.c +++ b/src/bucket.c @@ -162,14 +162,30 @@ cache_elem_free (GDBM_FILE dbf, cache_elem *elem) dbf->cache_num--; } +/* Free the least recently used cache entry. */ +static inline int +cache_lru_free (GDBM_FILE dbf) +{ + cache_elem *last = dbf->cache_lru; + if (last->ca_changed) + { + if (_gdbm_write_bucket (dbf, last)) + return -1; + } + cache_elem_free (dbf, last); + return 0; +} + static int cache_lookup (GDBM_FILE dbf, off_t adr, cache_elem *ref, cache_elem **ret_elem) { int rc; cache_node *node; cache_elem *elem; - + int retrying = 0; + dbf->cache_access_count++; + retry: rc = _gdbm_cache_tree_lookup (dbf->cache_tree, adr, &node); switch (rc) { @@ -178,7 +194,7 @@ cache_lookup (GDBM_FILE dbf, off_t adr, cache_elem *ref, cache_elem **ret_elem) elem->ca_hits++; lru_unlink_elem (dbf, elem); break; - + case node_new: elem = cache_elem_new (dbf, adr); if (!elem) @@ -189,24 +205,28 @@ cache_lookup (GDBM_FILE dbf, off_t adr, cache_elem *ref, cache_elem **ret_elem) elem->ca_node = node; node->elem = elem; - if (dbf->cache_num > dbf->cache_size) + if (dbf->cache_num > dbf->cache_size && cache_lru_free (dbf)) { - cache_elem *last = dbf->cache_lru; - if (last->ca_changed) + cache_elem_free (dbf, elem); + return node_failure; + } + break; + + case node_failure: + if (!retrying) + { + if (errno == ENOMEM) { - if (_gdbm_write_bucket (dbf, last)) - { - cache_elem_free (dbf, elem); - return node_failure; - } + /* Release the last recently used element and retry. */ + if (cache_lru_free (dbf)) + return node_failure; + retrying = 1; + goto retry; } - cache_elem_free (dbf, last); } - break; - - default: - return rc; + return node_failure; } + lru_link_elem (dbf, elem, ref); *ret_elem = elem; return rc; |