changeset 2236:b7e253153aa4

Add "lock" functionality to keep FileDatas in memory 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.
author Omari Stephens <xsdg@xsdg.org>
date Mon, 12 Nov 2012 06:03:02 +0000
parents 4617e1d83e9b
children db10022a79f3
files src/filedata.c src/filedata.h src/typedefs.h
diffstat 3 files changed, 99 insertions(+), 35 deletions(-) [+]
line wrap: on
line diff
--- a/src/filedata.c	Sun Oct 28 12:07:41 2012 +0100
+++ b/src/filedata.c	Mon Nov 12 06:03:02 2012 +0000
@@ -549,6 +549,57 @@
 	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,45 +617,54 @@
 
 	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;
-
-		work = parent->sidecar_files;
-		while (work)
-			{
-			FileData *sfd = work->data;
-			if (sfd->ref > 0) return;
-			work = work->next;
-			}
-
-		/* none of parent/children is referenced, we can free everything */
-
-		DEBUG_2("file_data_unref: 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);
-		}
+
+	// Free FileData if it's no longer ref'd
+	file_data_consider_free(fd);
 }
 
-
+/**
+ * \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);
+
+	g_assert(fd->magick == FD_MAGICK);
+	fd->locked = TRUE;
+
+	DEBUG_2("file_data_ref fd=%p (%d): '%s'", fd, fd->ref, fd->path);
+}
+
+/**
+ * \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_assert(fd->magick == FD_MAGICK);
+	fd->locked = FALSE;
+
+	// Free FileData if it's no longer ref'd
+	file_data_consider_free(fd);
+}
 
 /*
  *-----------------------------------------------------------------------------
--- a/src/filedata.h	Sun Oct 28 12:07:41 2012 +0100
+++ b/src/filedata.h	Mon Nov 12 06:03:02 2012 +0000
@@ -43,6 +43,9 @@
 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);
--- a/src/typedefs.h	Sun Oct 28 12:07:41 2012 +0100
+++ b/src/typedefs.h	Mon Nov 12 06:03:02 2012 +0000
@@ -514,6 +514,7 @@
 
 	HistMap *histmap;
 
+	gboolean locked;
 	gint ref;
 	gint version; /* increased when any field in this structure is changed */
 	gboolean disable_grouping;