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-2021
   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 "conditionaly" 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 /*** file scope variables ************************************************************************/
 104 
 105 static GSList *stamps = NULL;
 106 
 107 /* --------------------------------------------------------------------------------------------- */
 108 /*** file scope functions ************************************************************************/
 109 /* --------------------------------------------------------------------------------------------- */
 110 
 111 static gint
 112 vfs_stamp_compare (gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help]  */
 113 {
 114     const struct vfs_stamping *vsa = (const struct vfs_stamping *) a;
 115     const struct vfs_stamping *vsb = (const struct vfs_stamping *) b;
 116 
 117     return (vsa == NULL || vsb == NULL || (vsa->v == vsb->v && vsa->id == vsb->id)) ? 0 : 1;
 118 }
 119 
 120 /* --------------------------------------------------------------------------------------------- */
 121 
 122 static void
 123 vfs_addstamp (struct vfs_class *v, vfsid id)
     /* [previous][next][first][last][top][bottom][index][help]  */
 124 {
 125     if ((v->flags & VFSF_LOCAL) == 0 && id != NULL && !vfs_stamp (v, id))
 126     {
 127         struct vfs_stamping *stamp;
 128 
 129         stamp = g_new (struct vfs_stamping, 1);
 130         stamp->v = v;
 131         stamp->id = id;
 132         stamp->time = g_get_real_time ();
 133 
 134         stamps = g_slist_append (stamps, stamp);
 135     }
 136 }
 137 
 138 /* --------------------------------------------------------------------------------------------- */
 139 /*** public functions ****************************************************************************/
 140 /* --------------------------------------------------------------------------------------------- */
 141 
 142 gboolean
 143 vfs_stamp (struct vfs_class *v, vfsid id)
     /* [previous][next][first][last][top][bottom][index][help]  */
 144 {
 145     struct vfs_stamping what = {
 146         .v = v,
 147         .id = id
 148     };
 149     GSList *stamp;
 150     gboolean ret = FALSE;
 151 
 152     stamp = g_slist_find_custom (stamps, &what, vfs_stamp_compare);
 153     if (stamp != NULL && stamp->data != NULL)
 154     {
 155         VFS_STAMPING (stamp->data)->time = g_get_real_time ();
 156         ret = TRUE;
 157     }
 158 
 159     return ret;
 160 }
 161 
 162 /* --------------------------------------------------------------------------------------------- */
 163 
 164 void
 165 vfs_rmstamp (struct vfs_class *v, vfsid id)
     /* [previous][next][first][last][top][bottom][index][help]  */
 166 {
 167     struct vfs_stamping what = {
 168         .v = v,
 169         .id = id
 170     };
 171     GSList *stamp;
 172 
 173     stamp = g_slist_find_custom (stamps, &what, vfs_stamp_compare);
 174     if (stamp != NULL)
 175     {
 176         g_free (stamp->data);
 177         stamps = g_slist_delete_link (stamps, stamp);
 178     }
 179 }
 180 
 181 /* --------------------------------------------------------------------------------------------- */
 182 
 183 void
 184 vfs_stamp_path (const vfs_path_t * vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 185 {
 186     vfsid id;
 187     const vfs_path_element_t *path_element;
 188 
 189     path_element = vfs_path_get_by_index (vpath, -1);
 190 
 191     id = vfs_getid (vpath);
 192     vfs_addstamp (path_element->class, 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     const vfs_path_element_t *path_element;
 208 
 209     /* There are three directories we have to take care of: current_dir,
 210        current_panel->cwd and other_panel->cwd. Athough 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     path_element = vfs_path_get_by_index (vpath, -1);
 219 
 220     nvfsid = vfs_getid (vpath);
 221     vfs_rmstamp (path_element->class, nvfsid);
 222 
 223     if (!(id == NULL || (path_element->class == 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_real_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     const vfs_path_element_t *path_element;
 318 
 319     path_element = vfs_path_get_by_index (vpath, -1);
 320     vfs_stamp_create (path_element->class, vfs_getid (vpath));
 321 }
 322 
 323 /* --------------------------------------------------------------------------------------------- */
 324 /* Free all data */
 325 
 326 void
 327 vfs_gc_done (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 328 {
 329     vfs_expire (TRUE);
 330 }
 331 
 332 /* --------------------------------------------------------------------------------------------- */

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