From 710b009f5c61e7d1643a2a474e5020c760bf6462 Mon Sep 17 00:00:00 2001 From: Omari Stephens Date: Mon, 12 Nov 2012 06:03:02 +0000 Subject: [PATCH] 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. --- src/filedata.c | 118 +++++++++++++++++++++++++++++++++++++------------ src/filedata.h | 3 ++ src/typedefs.h | 1 + 3 files changed, 93 insertions(+), 29 deletions(-) diff --git a/src/filedata.c b/src/filedata.c index e0d70f00..bc9685c0 100644 --- a/src/filedata.c +++ b/src/filedata.c @@ -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. + *

+ * 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 diff --git a/src/filedata.h b/src/filedata.h index 558b5926..c7e9e894 100644 --- a/src/filedata.h +++ b/src/filedata.h @@ -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); diff --git a/src/typedefs.h b/src/typedefs.h index 482c05a4..52068c9b 100644 --- a/src/typedefs.h +++ b/src/typedefs.h @@ -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; -- 2.20.1