summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2020-10-31 10:01:39 +0200
committerSergey Poznyakoff <gray@gnu.org>2020-10-31 10:04:03 +0200
commit0916f224575687b444c1026d9df84e78dfaf3190 (patch)
tree58c9b82fd9d6a0385e37ba1e1d2da74dfe7f8391
parent274985d77bf2e69dec8d7eb90bd3f752f115f6e9 (diff)
downloadgdbm-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.c50
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;