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

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