Add "lock" functionality to keep FileDatas in memory
authorOmari Stephens <xsdg@xsdg.org>
Mon, 12 Nov 2012 06:03:02 +0000 (06:03 +0000)
committerOmari Stephens <xsdg@xsdg.org>
Tue, 13 Nov 2012 08:24:44 +0000 (08:24 +0000)
Adds two primitives to keep a FileData in memory even after its refcount
drops to zero.  Intended to be used in situations when an entire group of
FileDatas _should_ stay in memory as an optimization, even if the code
would continue to function properly even if the FileData were freed.

src/filedata.c
src/filedata.h
src/typedefs.h

index e0d70f0..bc9685c 100644 (file)
@@ -549,6 +549,57 @@ static void file_data_free(FileData *fd)
        g_free(fd);
 }
 
+/**
+ * \brief Checks if the FileData is referenced
+ *
+ * Checks the refcount and whether the FileData is locked.
+ */
+static gboolean file_data_check_has_ref(FileData *fd)
+{
+       return fd->ref > 0 || fd->locked;
+}
+
+/**
+ * \brief Consider freeing a FileData.
+ *
+ * This function will free a FileData and its children provided that neither its parent nor it has
+ * a positive refcount, and provided that neither is locked.
+ */
+static void file_data_consider_free(FileData *fd)
+{
+       GList *work;
+       FileData *parent = fd->parent ? fd->parent : fd;
+
+       g_assert(fd->magick == FD_MAGICK);
+       if (file_data_check_has_ref(fd)) return;
+       if (file_data_check_has_ref(parent)) return;
+
+       work = parent->sidecar_files;
+       while (work)
+               {
+               FileData *sfd = work->data;
+               if (file_data_check_has_ref(sfd)) return;
+               work = work->next;
+               }
+
+       /* Neither the parent nor the siblings are referenced, so we can free everything */
+       DEBUG_2("file_data_consider_free: deleting '%s', parent '%s'",
+               fd->path, fd->parent ? parent->path : "-");
+
+       work = parent->sidecar_files;
+       while (work)
+               {
+               FileData *sfd = work->data;
+               file_data_free(sfd);
+               work = work->next;
+               }
+
+       g_list_free(parent->sidecar_files);
+       parent->sidecar_files = NULL;
+
+       file_data_free(parent);
+}
+
 #ifdef DEBUG_FILEDATA
 void file_data_unref_debug(const gchar *file, gint line, FileData *fd)
 #else
@@ -566,46 +617,55 @@ void file_data_unref(FileData *fd)
 
        fd->ref--;
 #ifdef DEBUG_FILEDATA
-       DEBUG_2("file_data_unref fd=%p (%d): '%s' @ %s:%d", fd, fd->ref, fd->path, file, line);
+       DEBUG_2("file_data_unref fd=%p (%d:%d): '%s' @ %s:%d", fd, fd->ref, fd->locked, fd->path,
+               file, line);
 #else
-       DEBUG_2("file_data_unref fd=%p (%d): '%s'", fd, fd->ref, fd->path);
+       DEBUG_2("file_data_unref fd=%p (%d:%d): '%s'", fd, fd->ref, fd->locked, fd->path);
 #endif
-       if (fd->ref == 0)
-               {
-               GList *work;
-               FileData *parent = fd->parent ? fd->parent : fd;
 
-               if (parent->ref > 0) return;
+       // Free FileData if it's no longer ref'd
+       file_data_consider_free(fd);
+}
 
-               work = parent->sidecar_files;
-               while (work)
-                       {
-                       FileData *sfd = work->data;
-                       if (sfd->ref > 0) return;
-                       work = work->next;
-                       }
+/**
+ * \brief Lock the FileData in memory.
+ *
+ * This allows the caller to prevent a FileData from being freed, even
+ * after its refcount is zero.
+ * <p />
+ * This differs from file_data_ref in that the behavior is reentrant -- after N calls to
+ * file_data_lock, a single call to file_data_unlock will unlock the FileData.
+ */
+void file_data_lock(FileData *fd)
+{
+       if (fd == NULL) return;
+       if (fd->magick != FD_MAGICK) DEBUG_0("fd magick mismatch fd=%p", fd);
 
-               /* none of parent/children is referenced, we can free everything */
+       g_assert(fd->magick == FD_MAGICK);
+       fd->locked = TRUE;
 
-               DEBUG_2("file_data_unref: deleting '%s', parent '%s'", fd->path, fd->parent ? parent->path : "-");
+       DEBUG_2("file_data_ref fd=%p (%d): '%s'", fd, fd->ref, fd->path);
+}
 
-               work = parent->sidecar_files;
-               while (work)
-                       {
-                       FileData *sfd = work->data;
-                       file_data_free(sfd);
-                       work = work->next;
-                       }
+/**
+ * \brief Reset the maintain-FileData-in-memory lock
+ *
+ * This again allows the FileData to be freed when its refcount drops to zero.  Automatically frees
+ * the FileData if its refcount is already zero (which will happen if the lock is the only thing
+ * keeping it from being freed.
+ */
+void file_data_unlock(FileData *fd)
+{
+       if (fd == NULL) return;
+       if (fd->magick != FD_MAGICK) DEBUG_0("fd magick mismatch fd=%p", fd);
 
-               g_list_free(parent->sidecar_files);
-               parent->sidecar_files = NULL;
+       g_assert(fd->magick == FD_MAGICK);
+       fd->locked = FALSE;
 
-               file_data_free(parent);
-               }
+       // Free FileData if it's no longer ref'd
+       file_data_consider_free(fd);
 }
 
-
-
 /*
  *-----------------------------------------------------------------------------
  * sidecar file info struct
index 558b592..c7e9e89 100644 (file)
@@ -43,6 +43,9 @@ FileData *file_data_ref(FileData *fd);
 void file_data_unref(FileData *fd);
 #endif
 
+void file_data_lock(FileData *fd);
+void file_data_unlock(FileData *fd);
+
 gboolean file_data_check_changed_files(FileData *fd);
 
 void file_data_increment_version(FileData *fd);
index 482c05a..52068c9 100644 (file)
@@ -514,6 +514,7 @@ struct _FileData {
 
        HistMap *histmap;
 
+       gboolean locked;
        gint ref;
        gint version; /* increased when any field in this structure is changed */
        gboolean disable_grouping;