1 /*
   2     libparted - a library for manipulating disk partitions
   3     Copyright (C) 2001, 2007 Free Software Foundation, Inc.
   4 
   5     This program is free software; you can redistribute it and/or modify
   6     it under the terms of the GNU General Public License as published by
   7     the Free Software Foundation; either version 3 of the License, or
   8     (at your option) any later version.
   9 
  10     This program is distributed in the hope that it will be useful,
  11     but WITHOUT ANY WARRANTY; without even the implied warranty of
  12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13     GNU General Public License for more details.
  14 
  15     You should have received a copy of the GNU General Public License
  16     along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17 */
  18 
  19 /** \file timer.c */
  20 
  21 /**
  22  * \addtogroup PedTimer
  23  *
  24  * \brief A PedTimer keeps track of the progress of a single (possibly 
  25  * compound) operation.  
  26  *
  27  * The user of libparted constructs a PedTimer, and passes it to libparted 
  28  * functions that are likely to be expensive operations 
  29  * (like ped_file_system_resize).  Use of timers is optional... you may
  30  * pass NULL instead.
  31  * 
  32  * When you create a PedTimer, you must specify a timer handler function.
  33  * This will be called when there's an update on how work is progressing.
  34  * 
  35  * Timers may be nested.  When a timer is constructed, you can choose
  36  * to assign it a parent, along with an estimate of what proportion of
  37  * the total (parent's) time will be used in the nested operation.  In
  38  * this case, the nested timer's handler is internal to libparted,
  39  * and simply updates the parent's progress, and calls its handler.
  40  * 
  41  * @{
  42  */
  43 
  44 
  45 #include <config.h>
  46 #include <parted/parted.h>
  47 #include <parted/debug.h>
  48 
  49 #define PED_TIMER_START_DELAY   2
  50 
  51 typedef struct {
  52         PedTimer*       parent;
  53         float           nest_frac;
  54         float           start_frac;
  55 } NestedContext;
  56 
  57 
  58 /**
  59  * \brief Creates a timer.  
  60  *
  61  * Context will be passed in the \p context
  62  *         argument to the \p handler, when it is invoked.
  63  *
  64  * \return a new PedTimer
  65  */
  66 PedTimer*
  67 ped_timer_new (PedTimerHandler* handler, void* context)
  68 {
  69         PedTimer*       timer;
  70 
  71         PED_ASSERT (handler != NULL, return NULL);
  72 
  73         timer = (PedTimer*) ped_malloc (sizeof (PedTimer));
  74         if (!timer)
  75                 return NULL;
  76 
  77         timer->handler = handler;
  78         timer->context = context;
  79         ped_timer_reset (timer);
  80         return timer;
  81 }
  82 
  83 
  84 /**
  85  * \brief Destroys a \p timer.
  86  */
  87 void
  88 ped_timer_destroy (PedTimer* timer)
  89 {
  90         if (!timer)
  91                 return;
  92 
  93         ped_free (timer);
  94 }
  95 
  96 /* This function is used by ped_timer_new_nested() as the timer->handler
  97  * function.
  98  */
  99 static void
 100 _nest_handler (PedTimer* timer, void* context)
 101 {
 102         NestedContext*  ncontext = (NestedContext*) context;
 103 
 104         ped_timer_update (
 105                 ncontext->parent,
 106                 ncontext->start_frac + ncontext->nest_frac * timer->frac);
 107 }
 108 
 109 
 110 /** 
 111  * \brief Creates a new nested timer. 
 112  *
 113  * This function creates a "nested" timer that describes the progress
 114  * of a subtask. \p parent is the parent timer, and \p nested_frac is 
 115  * the estimated proportion (between 0 and 1) of the time that will be 
 116  * spent doing the nested timer's operation. The timer should only be 
 117  * constructed immediately prior to starting the nested operation.  
 118  * (It will be inaccurate, otherwise).
 119  * Updates to the progress of the subtask are propagated
 120  * back through to the parent task's timer.
 121  *
 122  * \return nested timer
 123  */
 124 PedTimer*
 125 ped_timer_new_nested (PedTimer* parent, float nest_frac)
 126 {
 127         NestedContext*  context;
 128 
 129         if (!parent)
 130                 return NULL;
 131 
 132         PED_ASSERT (nest_frac >= 0.0, return NULL);
 133         PED_ASSERT (nest_frac <= 1.0, return NULL);
 134 
 135         context = (NestedContext*) ped_malloc (sizeof (NestedContext));
 136         if (!context)
 137                 return NULL;
 138         context->parent = parent;
 139         context->nest_frac = nest_frac;
 140         context->start_frac = parent->frac;
 141 
 142         return ped_timer_new (_nest_handler, context);
 143 }
 144 
 145 /**
 146  * \brief Destroys a nested \p timer.
 147  */
 148 void
 149 ped_timer_destroy_nested (PedTimer* timer)
 150 {
 151         if (!timer)
 152                 return;
 153 
 154         ped_free (timer->context);
 155         ped_timer_destroy (timer);
 156 }
 157 
 158 /**
 159  * \internal 
 160  *
 161  * \brief This function calls the update handler, making sure that it has
 162  *      the latest time. 
 163  *
 164  * First it updates \p timer->now and recomputes \p timer->predicted_end, 
 165  * and then calls the handler.
 166  */
 167 void
 168 ped_timer_touch (PedTimer* timer)
 169 {
 170         if (!timer)
 171                return;
 172 
 173         timer->now = time (NULL);
 174         if (timer->now > timer->predicted_end)
 175                 timer->predicted_end = timer->now;
 176 
 177         timer->handler (timer, timer->context);
 178 }
 179 
 180 /** 
 181  * \internal
 182  * 
 183  * \brief This function sets the \p timer into a "start of task" position.
 184  * 
 185  * It resets the \p timer, by setting \p timer->start and \p timer->now
 186  * to the current time.
 187  */
 188 void
 189 ped_timer_reset (PedTimer* timer)
 190 {
 191         if (!timer)
 192                return;
 193 
 194         timer->start = timer->now = timer->predicted_end = time (NULL);
 195         timer->state_name = NULL;
 196         timer->frac = 0;
 197 
 198         ped_timer_touch (timer);
 199 }
 200 
 201 /** 
 202  * \internal
 203  * 
 204  * \brief This function tells a \p timer what fraction \p frac of the task
 205  * has been completed.
 206  * 
 207  * Sets the new \p timer->frac, and calls ped_timer_touch().
 208  */
 209 void
 210 ped_timer_update (PedTimer* timer, float frac)
 211 {
 212         if (!timer)
 213                return;
 214 
 215         timer->now = time (NULL);
 216         timer->frac = frac;
 217 
 218         if (frac)
 219                 timer->predicted_end
 220                         = timer->start
 221                           + (long) ((timer->now - timer->start) / frac);
 222 
 223         ped_timer_touch (timer);
 224 }
 225 
 226 /**
 227  * \internal 
 228  * 
 229  * \brief This function changes the description of the current task that the
 230  *      \p timer describes.
 231  *      
 232  * Sets a new name - \p state_name - for the current "phase" of the operation,
 233  * and calls ped_timer_touch().
 234  */
 235 void
 236 ped_timer_set_state_name (PedTimer* timer, const char* state_name)
 237 {
 238         if (!timer)
 239                return;
 240 
 241         timer->state_name = state_name;
 242         ped_timer_touch (timer);
 243 }
 244 
 245 /** @} */