Mesa (main): util/fossilize_db: Do not lock the fossilize db permanently.

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Mon Jun 21 10:27:39 UTC 2021


Module: Mesa
Branch: main
Commit: 4f0f8133a35ec2fec8d99936cd7425e40d092169
URL:    http://cgit.freedesktop.org/mesa/mesa/commit/?id=4f0f8133a35ec2fec8d99936cd7425e40d092169

Author: Bas Nieuwenhuizen <bas at basnieuwenhuizen.nl>
Date:   Sun Jun 20 01:06:53 2021 +0200

util/fossilize_db: Do not lock the fossilize db permanently.

This avoids all locks for reads and using lock only while actually
writing.

This is enabled by doing two things:

1) Reading the index incrementally. This way we get new entries
   written by other processes and do not write duplicate entries.
2) Taking the lock only during writes, and applying the incremental
   read while holding the lock so we always append to the actual end of the file.

Fixes: eca6bb9540d ("util/fossilize_db: add basic fossilize db util to read/write shader caches")
Reviewed-by: Timothy Arceri <tarceri at itsqueeze.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/11485>

---

 src/util/fossilize_db.c | 77 +++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 62 insertions(+), 15 deletions(-)

diff --git a/src/util/fossilize_db.c b/src/util/fossilize_db.c
index e65d3a92e5d..37f1d4ccacb 100644
--- a/src/util/fossilize_db.c
+++ b/src/util/fossilize_db.c
@@ -177,25 +177,49 @@ update_foz_index(struct foz_db *foz_db, FILE *db_idx, unsigned file_idx)
    fseek(db_idx, parsed_offset, SEEK_SET);
 }
 
+/* exclusive flock with timeout. timeout is in nanoseconds */
+static int lock_file_with_timeout(FILE *f, int64_t timeout)
+{
+   int err;
+   int fd = fileno(f);
+   int64_t iterations = MAX2(DIV_ROUND_UP(timeout, 1000000), 1);
+
+   /* Since there is no blocking flock with timeout and we don't want to totally spin on getting the
+    * lock, use a nonblocking method and retry every millisecond. */
+   for (int64_t iter = 0; iter < iterations; ++iter) {
+      err = flock(fd, LOCK_EX | LOCK_NB);
+      if (err == 0 || errno != EAGAIN)
+         break;
+      usleep(1000);
+   }
+   return err;
+}
+
 static bool
 load_foz_dbs(struct foz_db *foz_db, FILE *db_idx, uint8_t file_idx,
              bool read_only)
 {
-   int err = flock(fileno(foz_db->file[file_idx]), LOCK_EX | LOCK_NB);
-   if (err == -1)
-      goto fail;
-
-   err = flock(fileno(db_idx), LOCK_EX | LOCK_NB);
-   if (err == -1)
-      goto fail;
-
    /* Scan through the archive and get the list of cache entries. */
    fseek(db_idx, 0, SEEK_END);
    size_t len = ftell(db_idx);
    rewind(db_idx);
 
-   if (!read_only)
-       fseek(foz_db->file[file_idx], 0, SEEK_END);
+   /* Try not to take the lock if len > 0, but if it is 0 we take the lock to initialize the files. */
+   if (len == 0) {
+      /* Wait for 100 ms in case of contention, after that we prioritize getting the app started. */
+      int err = lock_file_with_timeout(foz_db->file[file_idx], 100000000);
+      if (err == -1)
+         goto fail;
+
+      err = lock_file_with_timeout(db_idx, 100000000);
+      if (err == -1)
+         goto fail;
+
+      /* Compute length again so we know nobody else did it in the meantime */
+      fseek(db_idx, 0, SEEK_END);
+      len = ftell(db_idx);
+      rewind(db_idx);
+   }
 
    if (len != 0) {
       uint8_t magic[FOZ_REF_MAGIC_SIZE];
@@ -224,12 +248,17 @@ load_foz_dbs(struct foz_db *foz_db, FILE *db_idx, uint8_t file_idx,
          goto fail;
    }
 
+   flock(fileno(db_idx), LOCK_UN);
+   flock(fileno(foz_db->file[file_idx]), LOCK_UN);
+
    update_foz_index(foz_db, db_idx, file_idx);
 
    foz_db->alive = true;
    return true;
 
 fail:
+   flock(fileno(db_idx), LOCK_UN);
+   flock(fileno(foz_db->file[file_idx]), LOCK_UN);
    foz_destroy(foz_db);
    return false;
 }
@@ -342,13 +371,16 @@ foz_read_entry(struct foz_db *foz_db, const uint8_t *cache_key_160bit,
 
    struct foz_db_entry *entry =
       _mesa_hash_table_u64_search(foz_db->index_db, hash);
+   if (!entry) {
+      update_foz_index(foz_db, foz_db->db_idx, 0);
+      entry = _mesa_hash_table_u64_search(foz_db->index_db, hash);
+   }
    if (!entry) {
       simple_mtx_unlock(&foz_db->mtx);
       return NULL;
    }
 
    uint8_t file_idx = entry->file_idx;
-   off_t offset = ftell(foz_db->file[file_idx]);
    if (fseek(foz_db->file[file_idx], entry->offset, SEEK_SET) < 0)
       goto fail;
 
@@ -376,9 +408,6 @@ foz_read_entry(struct foz_db *foz_db, const uint8_t *cache_key_160bit,
          goto fail;
    }
 
-   /* Reset file offset to the end of the file ready for writing */
-   fseek(foz_db->file[file_idx], offset, SEEK_SET);
-
    simple_mtx_unlock(&foz_db->mtx);
 
    if (size)
@@ -390,7 +419,6 @@ fail:
    free(data);
 
    /* reading db entry failed. reset the file offset */
-   fseek(foz_db->file[file_idx], offset, SEEK_SET);
    simple_mtx_unlock(&foz_db->mtx);
 
    return NULL;
@@ -407,8 +435,20 @@ foz_write_entry(struct foz_db *foz_db, const uint8_t *cache_key_160bit,
    if (!foz_db->alive)
       return false;
 
+   /* Wait for 1 second. This is done outside of the mutex as I believe there is more potential
+    * for file contention than mtx contention of significant length. */
+   int err = lock_file_with_timeout(foz_db->file[0], 1000000000);
+   if (err == -1)
+      goto fail_file;
+
+   err = lock_file_with_timeout(foz_db->db_idx, 1000000000);
+   if (err == -1)
+      goto fail_file;
+
    simple_mtx_lock(&foz_db->mtx);
 
+   update_foz_index(foz_db, foz_db->db_idx, 0);
+
    struct foz_db_entry *entry =
       _mesa_hash_table_u64_search(foz_db->index_db, hash);
    if (entry) {
@@ -423,6 +463,8 @@ foz_write_entry(struct foz_db *foz_db, const uint8_t *cache_key_160bit,
    header.payload_size = blob_size;
    header.crc = util_hash_crc32(blob, blob_size);
 
+   fseek(foz_db->file[0], 0, SEEK_END);
+
    /* Write hash header to db */
    char hash_str[FOSSILIZE_BLOB_HASH_LENGTH + 1]; /* 40 digits + null */
    _mesa_sha1_format(hash_str, cache_key_160bit);
@@ -472,11 +514,16 @@ foz_write_entry(struct foz_db *foz_db, const uint8_t *cache_key_160bit,
    _mesa_hash_table_u64_insert(foz_db->index_db, hash, entry);
 
    simple_mtx_unlock(&foz_db->mtx);
+   flock(fileno(foz_db->db_idx), LOCK_UN);
+   flock(fileno(foz_db->file[0]), LOCK_UN);
 
    return true;
 
 fail:
    simple_mtx_unlock(&foz_db->mtx);
+fail_file:
+   flock(fileno(foz_db->db_idx), LOCK_UN);
+   flock(fileno(foz_db->file[0]), LOCK_UN);
    return false;
 }
 #else



More information about the mesa-commit mailing list