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-2020
   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 #include "lib/timer.h"
  48 
  49 #include "vfs.h"
  50 #include "utilvfs.h"
  51 
  52 #include "gc.h"
  53 
  54 /*
  55  * The garbage collection mechanism is based on "stamps".
  56  *
  57  * A stamp is a record that says "I'm a filesystem which is no longer in
  58  * use. Free me when you get a chance."
  59  *
  60  * This file contains a set of functions used for managing this stamp. You
  61  * should use them when you write your own filesystem. Here are some rules
  62  * of thumb:
  63  *
  64  * (1) When the last open file in your filesystem gets closed, conditionally
  65  *     create a stamp. You do this with vfs_stamp_create(). (The meaning
  66  *     of "conditionaly" is explained below.)
  67  *
  68  * (2) When a file in your filesystem is opened, delete the stamp. You do
  69  *     this with vfs_rmstamp().
  70  *
  71  * (3) When a path inside your filesystem is invoked, call vfs_stamp() to
  72  *     postpone the free'ing of your filesystem a bit. (This simply updates
  73  *     a timestamp variable inside the stamp.)
  74  *
  75  * Additionally, when a user navigates to a new directory in a panel (or a
  76  * programmer uses mc_chdir()), a stamp is conditionally created for the
  77  * previous directory's filesystem. This ensures that that filesystem is
  78  * free'ed. (see: _do_panel_cd() -> vfs_release_path(); mc_chdir()).
  79  *
  80  * We've spoken here of "conditionally creating" a stamp. What we mean is
  81  * that vfs_stamp_create() is to be used: this function creates a stamp
  82  * only if no directories are open (aka "active") in your filesystem. (If
  83  * there _are_ directories open, it means that the filesystem is in use, in
  84  * which case we don't want to free it.)
  85  */
  86 
  87 /*** global variables ****************************************************************************/
  88 
  89 int vfs_timeout = 60;           /* VFS timeout in seconds */
  90 
  91 /*** file scope macro definitions ****************************************************************/
  92 
  93 #define VFS_STAMPING(a) ((struct vfs_stamping *)(a))
  94 
  95 /*** file scope type declarations ****************************************************************/
  96 
  97 struct vfs_stamping
  98 {
  99     struct vfs_class *v;
 100     vfsid id;
 101     guint64 time;
 102 };
 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 = mc_timer_elapsed (mc_global.timer);
 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 = mc_timer_elapsed (mc_global.timer);
 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     const vfs_path_element_t *path_element;
 189 
 190     path_element = vfs_path_get_by_index (vpath, -1);
 191 
 192     id = vfs_getid (vpath);
 193     vfs_addstamp (path_element->class, 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     const vfs_path_element_t *path_element;
 209 
 210     /* There are three directories we have to take care of: current_dir,
 211        current_panel->cwd and other_panel->cwd. Athough 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     path_element = vfs_path_get_by_index (vpath, -1);
 220 
 221     nvfsid = vfs_getid (vpath);
 222     vfs_rmstamp (path_element->class, nvfsid);
 223 
 224     if (!(id == NULL || (path_element->class == 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     guint64 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 = mc_timer_elapsed (mc_global.timer);
 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     const vfs_path_element_t *path_element;
 319 
 320     path_element = vfs_path_get_by_index (vpath, -1);
 321     vfs_stamp_create (path_element->class, vfs_getid (vpath));
 322 }
 323 
 324 /* --------------------------------------------------------------------------------------------- */
 325 /* Free all data */
 326 
 327 void
 328 vfs_gc_done (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 329 {
 330     vfs_expire (TRUE);
 331 }
 332 
 333 /* --------------------------------------------------------------------------------------------- */

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