root/lib/vfs/gc.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. vfs_stamp_compare
  2. vfs_addstamp
  3. vfs_stamp
  4. vfs_rmstamp
  5. vfs_stamp_path
  6. vfs_stamp_create
  7. vfs_expire
  8. vfs_timeouts
  9. vfs_timeout_handler
  10. vfs_release_path
  11. vfs_gc_done

   1 /*
   2    Virtual File System garbage collection code
   3 
   4    Copyright (C) 2003-2025
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Miguel de Icaza, 1995
   9    Jakub Jelinek, 1995
  10    Pavel Machek, 1998
  11    Pavel Roskin, 2003
  12 
  13    This file is part of the Midnight Commander.
  14 
  15    The Midnight Commander is free software: you can redistribute it
  16    and/or modify it under the terms of the GNU General Public License as
  17    published by the Free Software Foundation, either version 3 of the License,
  18    or (at your option) any later version.
  19 
  20    The Midnight Commander is distributed in the hope that it will be useful,
  21    but WITHOUT ANY WARRANTY; without even the implied warranty of
  22    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  23    GNU General Public License for more details.
  24 
  25    You should have received a copy of the GNU General Public License
  26    along with this program.  If not, see <https://www.gnu.org/licenses/>.
  27  */
  28 
  29 /**
  30  * \file
  31  * \brief Source: Virtual File System: garbage collection code
  32  * \author Miguel de Icaza
  33  * \author Jakub Jelinek
  34  * \author Pavel Machek
  35  * \author Pavel Roskin
  36  * \date 1995, 1998, 2003
  37  */
  38 
  39 #include <config.h>
  40 
  41 #include <stdlib.h>
  42 
  43 #include "lib/global.h"
  44 #include "lib/event.h"
  45 #include "lib/util.h"  // MC_PTR_FREE
  46 
  47 #include "vfs.h"
  48 #include "utilvfs.h"
  49 
  50 #include "gc.h"
  51 
  52 /*
  53  * The garbage collection mechanism is based on "stamps".
  54  *
  55  * A stamp is a record that says "I'm a filesystem which is no longer in
  56  * use. Free me when you get a chance."
  57  *
  58  * This file contains a set of functions used for managing this stamp. You
  59  * should use them when you write your own filesystem. Here are some rules
  60  * of thumb:
  61  *
  62  * (1) When the last open file in your filesystem gets closed, conditionally
  63  *     create a stamp. You do this with vfs_stamp_create(). (The meaning
  64  *     of "conditionally" is explained below.)
  65  *
  66  * (2) When a file in your filesystem is opened, delete the stamp. You do
  67  *     this with vfs_rmstamp().
  68  *
  69  * (3) When a path inside your filesystem is invoked, call vfs_stamp() to
  70  *     postpone the free'ing of your filesystem a bit. (This simply updates
  71  *     a timestamp variable inside the stamp.)
  72  *
  73  * Additionally, when a user navigates to a new directory in a panel (or a
  74  * programmer uses mc_chdir()), a stamp is conditionally created for the
  75  * previous directory's filesystem. This ensures that that filesystem is
  76  * free'ed. (see: _do_panel_cd() -> vfs_release_path(); mc_chdir()).
  77  *
  78  * We've spoken here of "conditionally creating" a stamp. What we mean is
  79  * that vfs_stamp_create() is to be used: this function creates a stamp
  80  * only if no directories are open (aka "active") in your filesystem. (If
  81  * there _are_ directories open, it means that the filesystem is in use, in
  82  * which case we don't want to free it.)
  83  */
  84 
  85 /*** global variables ****************************************************************************/
  86 
  87 int vfs_timeout = 60;  // VFS timeout in seconds
  88 
  89 /*** file scope macro definitions ****************************************************************/
  90 
  91 #define VFS_STAMPING(a) ((struct vfs_stamping *) (a))
  92 
  93 /*** file scope type declarations ****************************************************************/
  94 
  95 struct vfs_stamping
  96 {
  97     struct vfs_class *v;
  98     vfsid id;
  99     gint64 time;
 100 };
 101 
 102 /*** forward declarations (file scope functions) *************************************************/
 103 
 104 /*** file scope variables ************************************************************************/
 105 
 106 static GSList *stamps = NULL;
 107 
 108 /* --------------------------------------------------------------------------------------------- */
 109 /*** file scope functions ************************************************************************/
 110 /* --------------------------------------------------------------------------------------------- */
 111 
 112 static gint
 113 vfs_stamp_compare (gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help]  */
 114 {
 115     const struct vfs_stamping *vsa = (const struct vfs_stamping *) a;
 116     const struct vfs_stamping *vsb = (const struct vfs_stamping *) b;
 117 
 118     return (vsa == NULL || vsb == NULL || (vsa->v == vsb->v && vsa->id == vsb->id)) ? 0 : 1;
 119 }
 120 
 121 /* --------------------------------------------------------------------------------------------- */
 122 
 123 static void
 124 vfs_addstamp (struct vfs_class *v, vfsid id)
     /* [previous][next][first][last][top][bottom][index][help]  */
 125 {
 126     if ((v->flags & VFSF_LOCAL) == 0 && id != NULL && !vfs_stamp (v, id))
 127     {
 128         struct vfs_stamping *stamp;
 129 
 130         stamp = g_new (struct vfs_stamping, 1);
 131         stamp->v = v;
 132         stamp->id = id;
 133         stamp->time = g_get_monotonic_time ();
 134 
 135         stamps = g_slist_append (stamps, stamp);
 136     }
 137 }
 138 
 139 /* --------------------------------------------------------------------------------------------- */
 140 /*** public functions ****************************************************************************/
 141 /* --------------------------------------------------------------------------------------------- */
 142 
 143 gboolean
 144 vfs_stamp (struct vfs_class *v, vfsid id)
     /* [previous][next][first][last][top][bottom][index][help]  */
 145 {
 146     struct vfs_stamping what = {
 147         .v = v,
 148         .id = id,
 149     };
 150     GSList *stamp;
 151     gboolean ret = FALSE;
 152 
 153     stamp = g_slist_find_custom (stamps, &what, vfs_stamp_compare);
 154     if (stamp != NULL && stamp->data != NULL)
 155     {
 156         VFS_STAMPING (stamp->data)->time = g_get_monotonic_time ();
 157         ret = TRUE;
 158     }
 159 
 160     return ret;
 161 }
 162 
 163 /* --------------------------------------------------------------------------------------------- */
 164 
 165 void
 166 vfs_rmstamp (struct vfs_class *v, vfsid id)
     /* [previous][next][first][last][top][bottom][index][help]  */
 167 {
 168     struct vfs_stamping what = {
 169         .v = v,
 170         .id = id,
 171     };
 172     GSList *stamp;
 173 
 174     stamp = g_slist_find_custom (stamps, &what, vfs_stamp_compare);
 175     if (stamp != NULL)
 176     {
 177         g_free (stamp->data);
 178         stamps = g_slist_delete_link (stamps, stamp);
 179     }
 180 }
 181 
 182 /* --------------------------------------------------------------------------------------------- */
 183 
 184 void
 185 vfs_stamp_path (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 186 {
 187     vfsid id;
 188     struct vfs_class *me;
 189 
 190     me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath));
 191     id = vfs_getid (vpath);
 192     vfs_addstamp (me, id);
 193 }
 194 
 195 /* --------------------------------------------------------------------------------------------- */
 196 /**
 197  * Create a new timestamp item by VFS class and VFS id.
 198  */
 199 
 200 void
 201 vfs_stamp_create (struct vfs_class *vclass, vfsid id)
     /* [previous][next][first][last][top][bottom][index][help]  */
 202 {
 203     vfsid nvfsid;
 204 
 205     ev_vfs_stamp_create_t event_data = { vclass, id, FALSE };
 206     const vfs_path_t *vpath;
 207     struct vfs_class *me;
 208 
 209     /* There are three directories we have to take care of: current_dir,
 210        current_panel->cwd and other_panel->cwd. Although most of the time either
 211        current_dir and current_panel->cwd or current_dir and other_panel->cwd are the
 212        same, it's possible that all three are different -- Norbert */
 213 
 214     if (!mc_event_present (MCEVENT_GROUP_CORE, "vfs_timestamp"))
 215         return;
 216 
 217     vpath = vfs_get_raw_current_dir ();
 218     me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath));
 219 
 220     nvfsid = vfs_getid (vpath);
 221     vfs_rmstamp (me, nvfsid);
 222 
 223     if (!(id == NULL || (me == vclass && nvfsid == id)))
 224     {
 225         mc_event_raise (MCEVENT_GROUP_CORE, "vfs_timestamp", (gpointer) &event_data);
 226 
 227         if (!event_data.ret && vclass != NULL && vclass->nothingisopen != NULL
 228             && vclass->nothingisopen (id))
 229             vfs_addstamp (vclass, id);
 230     }
 231 }
 232 
 233 /* --------------------------------------------------------------------------------------------- */
 234 /** This is called from timeout handler with now = FALSE,
 235     or can be called with now = TRUE to force freeing all filesystems */
 236 
 237 void
 238 vfs_expire (gboolean now)
     /* [previous][next][first][last][top][bottom][index][help]  */
 239 {
 240     static gboolean locked = FALSE;
 241     gint64 curr_time, exp_time;
 242     GSList *stamp;
 243 
 244     /* Avoid recursive invocation, e.g. when one of the free functions
 245        calls message */
 246     if (locked)
 247         return;
 248     locked = TRUE;
 249 
 250     curr_time = g_get_monotonic_time ();
 251     exp_time = curr_time - vfs_timeout * G_USEC_PER_SEC;
 252 
 253     if (now)
 254     {
 255         // reverse list to free nested VFSes at first
 256         stamps = g_slist_reverse (stamps);
 257     }
 258 
 259     // NULLize stamps that point to expired VFS
 260     for (stamp = stamps; stamp != NULL; stamp = g_slist_next (stamp))
 261     {
 262         struct vfs_stamping *stamping = VFS_STAMPING (stamp->data);
 263 
 264         if (now)
 265         {
 266             // free VFS forced
 267             if (stamping->v->free != NULL)
 268                 stamping->v->free (stamping->id);
 269             MC_PTR_FREE (stamp->data);
 270         }
 271         else if (stamping->time <= exp_time)
 272         {
 273             // update timestamp of VFS that is in use, or free unused VFS
 274             if (stamping->v->nothingisopen != NULL && !stamping->v->nothingisopen (stamping->id))
 275                 stamping->time = curr_time;
 276             else
 277             {
 278                 if (stamping->v->free != NULL)
 279                     stamping->v->free (stamping->id);
 280                 MC_PTR_FREE (stamp->data);
 281             }
 282         }
 283     }
 284 
 285     // then remove NULLized stamps
 286     stamps = g_slist_remove_all (stamps, NULL);
 287 
 288     locked = FALSE;
 289 }
 290 
 291 /* --------------------------------------------------------------------------------------------- */
 292 /*
 293  * Return the number of seconds remaining to the vfs timeout.
 294  * FIXME: The code should be improved to actually return the number of
 295  * seconds until the next item times out.
 296  */
 297 
 298 int
 299 vfs_timeouts (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 300 {
 301     return stamps != NULL ? 10 : 0;
 302 }
 303 
 304 /* --------------------------------------------------------------------------------------------- */
 305 
 306 void
 307 vfs_timeout_handler (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 308 {
 309     vfs_expire (FALSE);
 310 }
 311 
 312 /* --------------------------------------------------------------------------------------------- */
 313 
 314 void
 315 vfs_release_path (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 316 {
 317     vfsid id;
 318     struct vfs_class *me;
 319 
 320     me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath));
 321     id = vfs_getid (vpath);
 322     vfs_stamp_create (me, id);
 323 }
 324 
 325 /* --------------------------------------------------------------------------------------------- */
 326 /* Free all data */
 327 
 328 void
 329 vfs_gc_done (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 330 {
 331     vfs_expire (TRUE);
 332 }
 333 
 334 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */