1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 *
3 * Copyright (C) 2009-2015 Richard Hughes <richard@hughsie.com>
4 *
5 * Most of this code was taken from Zif, libzif/zif-state.c
6 *
7 * Licensed under the GNU Lesser General Public License Version 2.1
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or(at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24 /**
25 * SECTION:dnf-state
26 * @short_description: Progress reporting
27 * @include: libdnf.h
28 * @stability: Unstable
29 *
30 * Objects can use dnf_state_set_percentage() if the absolute percentage
31 * is known. Percentages should always go up, not down.
32 *
33 * Modules usually set the number of steps that are expected using
34 * dnf_state_set_number_steps() and then after each section is completed,
35 * the dnf_state_done() function should be called. This will automatically
36 * call dnf_state_set_percentage() with the correct values.
37 *
38 * #DnfState allows sub-modules to be "chained up" to the parent module
39 * so that as the sub-module progresses, so does the parent.
40 * The child can be reused for each section, and chains can be deep.
41 *
42 * To get a child object, you should use dnf_state_get_child() and then
43 * use the result in any sub-process. You should ensure that the child
44 * is not re-used without calling dnf_state_done().
45 *
46 * There are a few nice touches in this module, so that if a module only has
47 * one progress step, the child progress is used for updates.
48 *
49 * <example>
50 * <title>Using a #DnfState.</title>
51 * <programlisting>
52 * static void
53 * _do_something(DnfState *state)
54 * {
55 * DnfState *state_local;
56 *
57 * // setup correct number of steps
58 * dnf_state_set_number_steps(state, 2);
59 *
60 * // we can't cancel this function
61 * dnf_state_set_allow_cancel(state, FALSE);
62 *
63 * // run a sub function
64 * state_local = dnf_state_get_child(state);
65 * _do_something_else1(state_local);
66 *
67 * // this section done
68 * dnf_state_done(state);
69 *
70 * // run another sub function
71 * state_local = dnf_state_get_child(state);
72 * _do_something_else2(state_local);
73 *
74 * // this section done(all complete)
75 * dnf_state_done(state);
76 * }
77 * </programlisting>
78 * </example>
79 *
80 * See also: #DnfLock
81 */
82
83
84 #include "catch-error.hpp"
85 #include "dnf-state.h"
86 #include "dnf-utils.h"
87
88 #include "utils/bgettext/bgettext-lib.h"
89
90 typedef struct
91 {
92 gboolean allow_cancel;
93 gboolean allow_cancel_changed_state;
94 gboolean allow_cancel_child;
95 gboolean enable_profile;
96 gboolean report_progress;
97 GCancellable *cancellable;
98 gchar *action_hint;
99 gchar *id;
100 gdouble *step_profile;
101 GTimer *timer;
102 guint64 speed;
103 guint64 *speed_data;
104 guint current;
105 guint last_percentage;
106 guint *step_data;
107 guint steps;
108 gulong action_child_id;
109 gulong package_progress_child_id;
110 gulong notify_speed_child_id;
111 gulong allow_cancel_child_id;
112 gulong percentage_child_id;
113 DnfStateAction action;
114 DnfStateAction last_action;
115 DnfStateAction child_action;
116 DnfState *child;
117 DnfState *parent;
118 GPtrArray *lock_ids;
119 DnfLock *lock;
120 } DnfStatePrivate;
121
122 enum {
123 SIGNAL_PERCENTAGE_CHANGED,
124 SIGNAL_SUBPERCENTAGE_CHANGED,
125 SIGNAL_ALLOW_CANCEL_CHANGED,
126 SIGNAL_ACTION_CHANGED,
127 SIGNAL_PACKAGE_PROGRESS_CHANGED,
128 SIGNAL_LAST
129 };
130
131 enum {
132 PROP_0,
133 PROP_SPEED,
134 PROP_LAST
135 };
136
137 static guint signals [SIGNAL_LAST] = { 0 };
138
G_DEFINE_TYPE_WITH_PRIVATE(DnfState,dnf_state,G_TYPE_OBJECT)139 G_DEFINE_TYPE_WITH_PRIVATE(DnfState, dnf_state, G_TYPE_OBJECT)
140 #define GET_PRIVATE(o) (static_cast<DnfStatePrivate *>(dnf_state_get_instance_private (o)))
141
142 #define DNF_STATE_SPEED_SMOOTHING_ITEMS 5
143
144 /**
145 * dnf_state_finalize:
146 **/
147 static void
148 dnf_state_finalize(GObject *object)
149 {
150 DnfState *state = DNF_STATE(object);
151 DnfStatePrivate *priv = GET_PRIVATE(state);
152
153 /* no more locks */
154 dnf_state_release_locks(state);
155
156 dnf_state_reset(state);
157 g_free(priv->id);
158 g_free(priv->action_hint);
159 g_free(priv->step_data);
160 g_free(priv->step_profile);
161 if (priv->cancellable != NULL)
162 g_object_unref(priv->cancellable);
163 g_timer_destroy(priv->timer);
164 g_free(priv->speed_data);
165 g_ptr_array_unref(priv->lock_ids);
166 g_object_unref(priv->lock);
167
168 G_OBJECT_CLASS(dnf_state_parent_class)->finalize(object);
169 }
170
171 /**
172 * dnf_state_init:
173 **/
174 static void
dnf_state_init(DnfState * state)175 dnf_state_init(DnfState *state)
176 {
177 DnfStatePrivate *priv = GET_PRIVATE(state);
178 priv->allow_cancel = TRUE;
179 priv->allow_cancel_child = TRUE;
180 priv->action = DNF_STATE_ACTION_UNKNOWN;
181 priv->last_action = DNF_STATE_ACTION_UNKNOWN;
182 priv->timer = g_timer_new();
183 priv->lock_ids = g_ptr_array_new();
184 priv->report_progress = TRUE;
185 priv->lock = dnf_lock_new();
186 priv->speed_data = g_new0(guint64, DNF_STATE_SPEED_SMOOTHING_ITEMS);
187 }
188
189 /**
190 * dnf_state_get_property:
191 **/
192 static void
dnf_state_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)193 dnf_state_get_property(GObject *object,
194 guint prop_id,
195 GValue *value,
196 GParamSpec *pspec)
197 {
198 DnfState *state = DNF_STATE(object);
199 DnfStatePrivate *priv = GET_PRIVATE(state);
200
201 switch(prop_id) {
202 case PROP_SPEED:
203 g_value_set_uint64(value, priv->speed);
204 break;
205 default:
206 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
207 break;
208 }
209 }
210
211 /**
212 * dnf_state_set_property:
213 **/
214 static void
dnf_state_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)215 dnf_state_set_property(GObject *object,
216 guint prop_id,
217 const GValue *value,
218 GParamSpec *pspec)
219 {
220 DnfState *state = DNF_STATE(object);
221 DnfStatePrivate *priv = GET_PRIVATE(state);
222
223 switch(prop_id) {
224 case PROP_SPEED:
225 priv->speed = g_value_get_uint64(value);
226 break;
227 default:
228 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
229 break;
230 }
231 }
232
233 /**
234 * dnf_state_class_init:
235 **/
236 static void
dnf_state_class_init(DnfStateClass * klass)237 dnf_state_class_init(DnfStateClass *klass)
238 {
239 GParamSpec *pspec;
240 GObjectClass *object_class = G_OBJECT_CLASS(klass);
241 object_class->finalize = dnf_state_finalize;
242 object_class->get_property = dnf_state_get_property;
243 object_class->set_property = dnf_state_set_property;
244
245 /**
246 * DnfState:speed:
247 **/
248 pspec = g_param_spec_uint64("speed", NULL, NULL,
249 0, G_MAXUINT64, 0,
250 G_PARAM_READABLE);
251 g_object_class_install_property(object_class, PROP_SPEED, pspec);
252
253 signals [SIGNAL_PERCENTAGE_CHANGED] =
254 g_signal_new("percentage-changed",
255 G_TYPE_FROM_CLASS(object_class), G_SIGNAL_RUN_LAST,
256 G_STRUCT_OFFSET(DnfStateClass, percentage_changed),
257 NULL, NULL, g_cclosure_marshal_VOID__UINT,
258 G_TYPE_NONE, 1, G_TYPE_UINT);
259
260 signals [SIGNAL_ALLOW_CANCEL_CHANGED] =
261 g_signal_new("allow-cancel-changed",
262 G_TYPE_FROM_CLASS(object_class), G_SIGNAL_RUN_LAST,
263 G_STRUCT_OFFSET(DnfStateClass, allow_cancel_changed),
264 NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN,
265 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
266
267 signals [SIGNAL_ACTION_CHANGED] =
268 g_signal_new("action-changed",
269 G_TYPE_FROM_CLASS(object_class), G_SIGNAL_RUN_LAST,
270 G_STRUCT_OFFSET(DnfStateClass, action_changed),
271 NULL, NULL, g_cclosure_marshal_generic,
272 G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
273
274 signals [SIGNAL_PACKAGE_PROGRESS_CHANGED] =
275 g_signal_new("package-progress-changed",
276 G_TYPE_FROM_CLASS(object_class), G_SIGNAL_RUN_LAST,
277 G_STRUCT_OFFSET(DnfStateClass, package_progress_changed),
278 NULL, NULL, g_cclosure_marshal_generic,
279 G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT);
280 }
281
282 /**
283 * dnf_state_set_report_progress:
284 * @state: A #DnfState
285 * @report_progress: if we care about percentage status
286 *
287 * This disables progress tracking for DnfState. This is generally a bad
288 * thing to do, except when you know you cannot guess the number of steps
289 * in the state.
290 *
291 * Using this function also reduced the amount of time spent getting a
292 * child state using dnf_state_get_child() as a refcounted version of
293 * the parent is returned instead. *
294 * Since: 0.1.0
295 **/
296 void
dnf_state_set_report_progress(DnfState * state,gboolean report_progress)297 dnf_state_set_report_progress(DnfState *state, gboolean report_progress)
298 {
299 DnfStatePrivate *priv = GET_PRIVATE(state);
300 priv->report_progress = report_progress;
301 }
302
303 /**
304 * dnf_state_set_enable_profile:
305 * @state: A #DnfState
306 * @enable_profile: if profiling should be enabled
307 *
308 * This enables profiling of DnfState. This may be useful in development,
309 * but be warned; enabling profiling makes #DnfState very slow.
310 *
311 * Since: 0.1.0
312 **/
313 void
dnf_state_set_enable_profile(DnfState * state,gboolean enable_profile)314 dnf_state_set_enable_profile(DnfState *state, gboolean enable_profile)
315 {
316 DnfStatePrivate *priv = GET_PRIVATE(state);
317 priv->enable_profile = enable_profile;
318 }
319
320 /**
321 * dnf_state_take_lock:
322 * @state: A #DnfState
323 * @lock_type: A #DnfLockType, e.g. %DNF_LOCK_TYPE_RPMDB
324 * @lock_mode: A #DnfLockMode, e.g. %DNF_LOCK_MODE_PROCESS
325 * @error: A #GError
326 *
327 * Takes a lock of a specified type.
328 * The lock is automatically free'd when the DnfState has been completed.
329 *
330 * You can call dnf_state_take_lock() multiple times with different or
331 * even the same @lock_type value.
332 *
333 * Returns: %FALSE if the lock is fatal, %TRUE otherwise
334 *
335 * Since: 0.1.0
336 **/
337 gboolean
dnf_state_take_lock(DnfState * state,DnfLockType lock_type,DnfLockMode lock_mode,GError ** error)338 dnf_state_take_lock(DnfState *state,
339 DnfLockType lock_type,
340 DnfLockMode lock_mode,
341 GError **error) try
342 {
343 DnfStatePrivate *priv = GET_PRIVATE(state);
344 guint lock_id = 0;
345
346 /* no custom handler */
347 lock_id = dnf_lock_take(priv->lock,
348 lock_type,
349 lock_mode,
350 error);
351 if (lock_id == 0)
352 return FALSE;
353
354 /* add the lock to an array so we can release on completion */
355 g_debug("adding lock %i", lock_id);
356 g_ptr_array_add(priv->lock_ids,
357 GUINT_TO_POINTER(lock_id));
358 return TRUE;
359 } CATCH_TO_GERROR(FALSE)
360
361 /**
362 * dnf_state_discrete_to_percent:
363 **/
364 static gfloat
365 dnf_state_discrete_to_percent(guint discrete, guint steps)
366 {
367 /* check we are in range */
368 if (discrete > steps)
369 return 100;
370 if (steps == 0) {
371 g_warning("steps is 0!");
372 return 0;
373 }
374 return((gfloat) ((gdouble) discrete *(100.0 /(gdouble)(steps))));
375 }
376
377 /**
378 * dnf_state_print_parent_chain:
379 **/
380 static void
dnf_state_print_parent_chain(DnfState * state,guint level)381 dnf_state_print_parent_chain(DnfState *state, guint level)
382 {
383 DnfStatePrivate *priv = GET_PRIVATE(state);
384 if (priv->parent != NULL)
385 dnf_state_print_parent_chain(priv->parent, level + 1);
386 g_print("%i) %s(%i/%i)\n",
387 level, priv->id, priv->current, priv->steps);
388 }
389
390 /**
391 * dnf_state_get_cancellable:
392 * @state: A #DnfState
393 *
394 * Gets the #GCancellable for this operation
395 *
396 * Returns:(transfer none): The #GCancellable or %NULL
397 *
398 * Since: 0.1.0
399 **/
400 GCancellable *
dnf_state_get_cancellable(DnfState * state)401 dnf_state_get_cancellable(DnfState *state)
402 {
403 DnfStatePrivate *priv = GET_PRIVATE(state);
404 return priv->cancellable;
405 }
406
407 /**
408 * dnf_state_set_cancellable:
409 * @state: a #DnfState instance.
410 * @cancellable: The #GCancellable which is used to cancel tasks, or %NULL
411 *
412 * Sets the #GCancellable object to use.
413 *
414 * Since: 0.1.0
415 **/
416 void
dnf_state_set_cancellable(DnfState * state,GCancellable * cancellable)417 dnf_state_set_cancellable(DnfState *state, GCancellable *cancellable)
418 {
419 DnfStatePrivate *priv = GET_PRIVATE(state);
420 g_return_if_fail(priv->cancellable == NULL);
421 if (priv->cancellable != NULL)
422 g_clear_object(&priv->cancellable);
423 if (cancellable != NULL)
424 priv->cancellable = static_cast<GCancellable *>(g_object_ref(cancellable));
425 }
426
427 /**
428 * dnf_state_get_allow_cancel:
429 * @state: A #DnfState
430 *
431 * Gets if the sub-task(or one of it's sub-sub-tasks) is cancellable
432 *
433 * Returns: %TRUE if the translation has a chance of being cancelled
434 *
435 * Since: 0.1.0
436 **/
437 gboolean
dnf_state_get_allow_cancel(DnfState * state)438 dnf_state_get_allow_cancel(DnfState *state)
439 {
440 DnfStatePrivate *priv = GET_PRIVATE(state);
441 return priv->allow_cancel && priv->allow_cancel_child;
442 }
443
444 /**
445 * dnf_state_set_allow_cancel:
446 * @state: A #DnfState
447 * @allow_cancel: If this sub-task can be cancelled
448 *
449 * Set is this sub task can be cancelled safely.
450 *
451 * Since: 0.1.0
452 **/
453 void
dnf_state_set_allow_cancel(DnfState * state,gboolean allow_cancel)454 dnf_state_set_allow_cancel(DnfState *state, gboolean allow_cancel)
455 {
456 DnfStatePrivate *priv = GET_PRIVATE(state);
457
458 priv->allow_cancel_changed_state = TRUE;
459
460 /* quick optimisation that saves lots of signals */
461 if (priv->allow_cancel == allow_cancel)
462 return;
463 priv->allow_cancel = allow_cancel;
464
465 /* just emit if both this and child is okay */
466 g_signal_emit(state, signals [SIGNAL_ALLOW_CANCEL_CHANGED], 0,
467 priv->allow_cancel && priv->allow_cancel_child);
468 }
469
470 /**
471 * dnf_state_get_speed:
472 * @state: a #DnfState instance.
473 *
474 * Gets the transaction speed in bytes per second.
475 *
476 * Returns: speed, or 0 for unknown.
477 *
478 * Since: 0.1.0
479 **/
480 guint64
dnf_state_get_speed(DnfState * state)481 dnf_state_get_speed(DnfState *state)
482 {
483 DnfStatePrivate *priv = GET_PRIVATE(state);
484 return priv->speed;
485 }
486
487 /**
488 * dnf_state_set_speed-private:
489 **/
490 static void
dnf_state_set_speed_internal(DnfState * state,guint64 speed)491 dnf_state_set_speed_internal(DnfState *state, guint64 speed)
492 {
493 DnfStatePrivate *priv = GET_PRIVATE(state);
494 if (priv->speed == speed)
495 return;
496 priv->speed = speed;
497 g_object_notify(G_OBJECT(state), "speed");
498 }
499
500 /**
501 * dnf_state_set_speed:
502 * @state: a #DnfState instance.
503 * @speed: The transaction speed.
504 *
505 * Sets the download or install transaction speed in bytes per second.
506 *
507 * Since: 0.1.0
508 **/
509 void
dnf_state_set_speed(DnfState * state,guint64 speed)510 dnf_state_set_speed(DnfState *state, guint64 speed)
511 {
512 DnfStatePrivate *priv = GET_PRIVATE(state);
513 guint i;
514 guint64 sum = 0;
515 guint sum_cnt = 0;
516
517 /* move the data down one entry */
518 for (i=DNF_STATE_SPEED_SMOOTHING_ITEMS-1; i > 0; i--)
519 priv->speed_data[i] = priv->speed_data[i-1];
520 priv->speed_data[0] = speed;
521
522 /* get the average */
523 for (i = 0; i < DNF_STATE_SPEED_SMOOTHING_ITEMS; i++) {
524 if (priv->speed_data[i] > 0) {
525 sum += priv->speed_data[i];
526 sum_cnt++;
527 }
528 }
529 if (sum_cnt > 0)
530 sum /= sum_cnt;
531
532 dnf_state_set_speed_internal(state, sum);
533 }
534
535 /**
536 * dnf_state_release_locks:
537 * @state: a #DnfState instance.
538 *
539 * Releases all locks used in the state.
540 *
541 * Returns: %TRUE for success, %FALSE otherwise
542 *
543 * Since: 0.1.0
544 **/
545 gboolean
dnf_state_release_locks(DnfState * state)546 dnf_state_release_locks(DnfState *state)
547 {
548 DnfStatePrivate *priv = GET_PRIVATE(state);
549 guint i;
550 guint lock_id;
551
552 /* release children first */
553 if (priv->child != NULL)
554 dnf_state_release_locks(priv->child);
555
556 /* release each one */
557 for (i = 0; i < priv->lock_ids->len; i++) {
558 lock_id = GPOINTER_TO_UINT(g_ptr_array_index(priv->lock_ids, i));
559 g_debug("releasing lock %i", lock_id);
560 if (!dnf_lock_release(priv->lock, lock_id, NULL))
561 return FALSE;
562 }
563 g_ptr_array_set_size(priv->lock_ids, 0);
564 return TRUE;
565 }
566
567 /**
568 * dnf_state_set_percentage:
569 * @state: a #DnfState instance.
570 * @percentage: Percentage value between 0% and 100%
571 *
572 * Set a percentage manually.
573 * NOTE: this must be above what was previously set, or it will be rejected.
574 *
575 * Returns: %TRUE if the signal was propagated, %FALSE otherwise
576 *
577 * Since: 0.1.0
578 **/
579 gboolean
dnf_state_set_percentage(DnfState * state,guint percentage)580 dnf_state_set_percentage(DnfState *state, guint percentage)
581 {
582 DnfStatePrivate *priv = GET_PRIVATE(state);
583
584 /* do we care */
585 if (!priv->report_progress)
586 return TRUE;
587
588 /* is it the same */
589 if (percentage == priv->last_percentage)
590 return FALSE;
591
592 /* is it invalid */
593 if (percentage > 100) {
594 dnf_state_print_parent_chain(state, 0);
595 g_warning("percentage %i%% is invalid on %p!",
596 percentage, state);
597 return FALSE;
598 }
599
600 /* is it less */
601 if (percentage < priv->last_percentage) {
602 if (priv->enable_profile) {
603 dnf_state_print_parent_chain(state, 0);
604 g_warning("percentage should not go down from %i to %i on %p!",
605 priv->last_percentage, percentage, state);
606 }
607 return FALSE;
608 }
609
610 /* we're done, so we're not preventing cancellation anymore */
611 if (percentage == 100 && !priv->allow_cancel) {
612 g_debug("done, so allow cancel 1 for %p", state);
613 dnf_state_set_allow_cancel(state, TRUE);
614 }
615
616 /* automatically cancel any action */
617 if (percentage == 100 && priv->action != DNF_STATE_ACTION_UNKNOWN)
618 dnf_state_action_stop(state);
619
620 /* speed no longer valid */
621 if (percentage == 100)
622 dnf_state_set_speed_internal(state, 0);
623
624 /* release locks? */
625 if (percentage == 100) {
626 if (!dnf_state_release_locks(state))
627 return FALSE;
628 }
629
630 /* save */
631 priv->last_percentage = percentage;
632
633 /* emit */
634 g_signal_emit(state, signals [SIGNAL_PERCENTAGE_CHANGED], 0, percentage);
635
636 /* success */
637 return TRUE;
638 }
639
640 /**
641 * dnf_state_get_percentage:
642 * @state: a #DnfState instance.
643 *
644 * Get the percentage state.
645 *
646 * Returns: The percentage value, or %G_MAXUINT for error
647 *
648 * Since: 0.1.0
649 **/
650 guint
dnf_state_get_percentage(DnfState * state)651 dnf_state_get_percentage(DnfState *state)
652 {
653 DnfStatePrivate *priv = GET_PRIVATE(state);
654 return priv->last_percentage;
655 }
656
657 /**
658 * dnf_state_action_start:
659 * @state: a #DnfState instance.
660 * @action: An action, e.g. %DNF_STATE_ACTION_DECOMPRESSING
661 * @action_hint: A hint on what the action is doing, e.g. "/var/cache/yum/i386/15/koji/primary.sqlite"
662 *
663 * Sets the action which is being performed. This is emitted up the chain
664 * to any parent %DnfState objects, using the action-changed signal.
665 *
666 * If a %DnfState reaches 100% then it is automatically stopped with a
667 * call to dnf_state_action_stop().
668 *
669 * It is allowed to call dnf_state_action_start() more than once for a
670 * given %DnfState instance.
671 *
672 * Returns: %TRUE if the signal was propagated, %FALSE otherwise
673 *
674 * Since: 0.1.0
675 **/
676 gboolean
dnf_state_action_start(DnfState * state,DnfStateAction action,const gchar * action_hint)677 dnf_state_action_start(DnfState *state, DnfStateAction action, const gchar *action_hint)
678 {
679 DnfStatePrivate *priv = GET_PRIVATE(state);
680
681 /* ignore this */
682 if (action == DNF_STATE_ACTION_UNKNOWN) {
683 g_warning("cannot set action DNF_STATE_ACTION_UNKNOWN");
684 return FALSE;
685 }
686
687 /* is different? */
688 if (priv->action == action &&
689 g_strcmp0(action_hint, priv->action_hint) == 0)
690 return FALSE;
691
692 /* remember for stop */
693 priv->last_action = priv->action;
694
695 /* save hint */
696 g_free(priv->action_hint);
697 priv->action_hint = g_strdup(action_hint);
698
699 /* save */
700 priv->action = action;
701
702 /* just emit */
703 g_signal_emit(state, signals [SIGNAL_ACTION_CHANGED], 0, action, action_hint);
704 return TRUE;
705 }
706
707 /**
708 * dnf_state_set_package_progress:
709 * @state: a #DnfState instance.
710 * @dnf_package_get_id: A package_id
711 * @action: A #DnfStateAction
712 * @percentage: A percentage
713 *
714 * Sets any package progress.
715 *
716 * Since: 0.1.0
717 **/
718 void
dnf_state_set_package_progress(DnfState * state,const gchar * dnf_package_get_id,DnfStateAction action,guint percentage)719 dnf_state_set_package_progress(DnfState *state,
720 const gchar *dnf_package_get_id,
721 DnfStateAction action,
722 guint percentage)
723 {
724 g_return_if_fail(dnf_package_get_id != NULL);
725 g_return_if_fail(action != DNF_STATE_ACTION_UNKNOWN);
726 g_return_if_fail(percentage <= 100);
727
728 /* just emit */
729 g_signal_emit(state, signals [SIGNAL_PACKAGE_PROGRESS_CHANGED], 0,
730 dnf_package_get_id, action, percentage);
731 }
732
733 /**
734 * dnf_state_action_stop:
735 * @state: A #DnfState
736 *
737 * Returns the DnfState to it's previous value.
738 * It is not expected you will ever need to use this function.
739 *
740 * Returns: %TRUE if the signal was propagated, %FALSE otherwise
741 *
742 * Since: 0.1.0
743 **/
744 gboolean
dnf_state_action_stop(DnfState * state)745 dnf_state_action_stop(DnfState *state)
746 {
747 DnfStatePrivate *priv = GET_PRIVATE(state);
748
749 /* nothing ever set */
750 if (priv->action == DNF_STATE_ACTION_UNKNOWN) {
751 g_debug("cannot unset action DNF_STATE_ACTION_UNKNOWN");
752 return FALSE;
753 }
754
755 /* pop and reset */
756 priv->action = priv->last_action;
757 priv->last_action = DNF_STATE_ACTION_UNKNOWN;
758 if (priv->action_hint != NULL) {
759 g_free(priv->action_hint);
760 priv->action_hint = NULL;
761 }
762
763 /* just emit */
764 g_signal_emit(state, signals [SIGNAL_ACTION_CHANGED], 0, priv->action, NULL);
765 return TRUE;
766 }
767
768 /**
769 * dnf_state_get_action_hint:
770 * @state: a #DnfState instance.
771 *
772 * Gets the action hint, which may be useful to the users.
773 *
774 * Returns: An a ction hint, e.g. "/var/cache/yum/i386/15/koji/primary.sqlite"
775 *
776 * Since: 0.1.0
777 **/
778 const gchar *
dnf_state_get_action_hint(DnfState * state)779 dnf_state_get_action_hint(DnfState *state)
780 {
781 DnfStatePrivate *priv = GET_PRIVATE(state);
782 return priv->action_hint;
783 }
784
785 /**
786 * dnf_state_get_action:
787 * @state: a #DnfState instance.
788 *
789 * Gets the last set action value.
790 *
791 * Returns: An action, e.g. %DNF_STATE_ACTION_DECOMPRESSING
792 *
793 * Since: 0.1.0
794 **/
795 DnfStateAction
dnf_state_get_action(DnfState * state)796 dnf_state_get_action(DnfState *state)
797 {
798 DnfStatePrivate *priv = GET_PRIVATE(state);
799 return priv->action;
800 }
801
802 /**
803 * dnf_state_child_percentage_changed_cb:
804 **/
805 static void
dnf_state_child_percentage_changed_cb(DnfState * child,guint percentage,DnfState * state)806 dnf_state_child_percentage_changed_cb(DnfState *child, guint percentage, DnfState *state)
807 {
808 DnfStatePrivate *priv = GET_PRIVATE(state);
809 gfloat offset;
810 gfloat range;
811 gfloat extra;
812 guint parent_percentage;
813
814 /* propagate up the stack if DnfState has only one step */
815 if (priv->steps == 1) {
816 dnf_state_set_percentage(state, percentage);
817 return;
818 }
819
820 /* did we call done on a state that did not have a size set? */
821 if (priv->steps == 0)
822 return;
823
824 /* already at >= 100% */
825 if (priv->current >= priv->steps) {
826 g_warning("already at %i/%i steps on %p", priv->current, priv->steps, state);
827 return;
828 }
829
830 /* we have to deal with non-linear steps */
831 if (priv->step_data != NULL) {
832 /* we don't store zero */
833 if (priv->current == 0) {
834 parent_percentage = percentage * priv->step_data[priv->current] / 100;
835 } else {
836 /* bilinearly interpolate for speed */
837 parent_percentage =(((100 - percentage) * priv->step_data[priv->current-1]) +
838 (percentage * priv->step_data[priv->current])) / 100;
839 }
840 goto out;
841 }
842
843 /* get the offset */
844 offset = dnf_state_discrete_to_percent(priv->current, priv->steps);
845
846 /* get the range between the parent step and the next parent step */
847 range = dnf_state_discrete_to_percent(priv->current+1, priv->steps) - offset;
848 if (range < 0.01) {
849 g_warning("range=%f(from %i to %i), should be impossible", range, priv->current+1, priv->steps);
850 return;
851 }
852
853 /* restore the pre-child action */
854 if (percentage == 100)
855 priv->last_action = priv->child_action;
856
857 /* get the extra contributed by the child */
858 extra =((gfloat) percentage / 100.0f) * range;
859
860 /* emit from the parent */
861 parent_percentage =(guint)(offset + extra);
862 out:
863 dnf_state_set_percentage(state, parent_percentage);
864 }
865
866 /**
867 * dnf_state_child_allow_cancel_changed_cb:
868 **/
869 static void
dnf_state_child_allow_cancel_changed_cb(DnfState * child,gboolean allow_cancel,DnfState * state)870 dnf_state_child_allow_cancel_changed_cb(DnfState *child, gboolean allow_cancel, DnfState *state)
871 {
872 DnfStatePrivate *priv = GET_PRIVATE(state);
873
874 /* save */
875 priv->allow_cancel_child = allow_cancel;
876
877 /* just emit if both this and child is okay */
878 g_signal_emit(state, signals [SIGNAL_ALLOW_CANCEL_CHANGED], 0,
879 priv->allow_cancel && priv->allow_cancel_child);
880 }
881
882 /**
883 * dnf_state_child_action_changed_cb:
884 **/
885 static void
dnf_state_child_action_changed_cb(DnfState * child,DnfStateAction action,const gchar * action_hint,DnfState * state)886 dnf_state_child_action_changed_cb(DnfState *child,
887 DnfStateAction action,
888 const gchar *action_hint,
889 DnfState *state)
890 {
891 DnfStatePrivate *priv = GET_PRIVATE(state);
892 /* save */
893 priv->action = action;
894
895 /* just emit */
896 g_signal_emit(state, signals [SIGNAL_ACTION_CHANGED], 0, action, action_hint);
897 }
898
899 /**
900 * dnf_state_child_package_progress_changed_cb:
901 **/
902 static void
dnf_state_child_package_progress_changed_cb(DnfState * child,const gchar * dnf_package_get_id,DnfStateAction action,guint progress,DnfState * state)903 dnf_state_child_package_progress_changed_cb(DnfState *child,
904 const gchar *dnf_package_get_id,
905 DnfStateAction action,
906 guint progress,
907 DnfState *state)
908 {
909 /* just emit */
910 g_signal_emit(state, signals [SIGNAL_PACKAGE_PROGRESS_CHANGED], 0,
911 dnf_package_get_id, action, progress);
912 }
913
914 /**
915 * dnf_state_reset:
916 * @state: a #DnfState instance.
917 *
918 * Resets the #DnfState object to unset
919 *
920 * Returns: %TRUE for success, %FALSE otherwise
921 *
922 * Since: 0.1.0
923 **/
924 gboolean
dnf_state_reset(DnfState * state)925 dnf_state_reset(DnfState *state)
926 {
927 DnfStatePrivate *priv = GET_PRIVATE(state);
928
929 g_return_val_if_fail(DNF_IS_STATE(state), FALSE);
930
931 /* do we care */
932 if (!priv->report_progress)
933 return TRUE;
934
935 /* reset values */
936 priv->steps = 0;
937 priv->current = 0;
938 priv->last_percentage = 0;
939
940 /* only use the timer if profiling; it's expensive */
941 if (priv->enable_profile)
942 g_timer_start(priv->timer);
943
944 /* disconnect client */
945 if (priv->percentage_child_id != 0) {
946 g_signal_handler_disconnect(priv->child, priv->percentage_child_id);
947 priv->percentage_child_id = 0;
948 }
949 if (priv->allow_cancel_child_id != 0) {
950 g_signal_handler_disconnect(priv->child, priv->allow_cancel_child_id);
951 priv->allow_cancel_child_id = 0;
952 }
953 if (priv->action_child_id != 0) {
954 g_signal_handler_disconnect(priv->child, priv->action_child_id);
955 priv->action_child_id = 0;
956 }
957 if (priv->package_progress_child_id != 0) {
958 g_signal_handler_disconnect(priv->child, priv->package_progress_child_id);
959 priv->package_progress_child_id = 0;
960 }
961 if (priv->notify_speed_child_id != 0) {
962 g_signal_handler_disconnect(priv->child, priv->notify_speed_child_id);
963 priv->notify_speed_child_id = 0;
964 }
965
966 /* unref child */
967 if (priv->child != NULL) {
968 g_object_unref(priv->child);
969 priv->child = NULL;
970 }
971
972 /* no more locks */
973 dnf_state_release_locks(state);
974
975 /* no more step data */
976 g_free(priv->step_data);
977 g_free(priv->step_profile);
978 priv->step_data = NULL;
979 priv->step_profile = NULL;
980 return TRUE;
981 }
982
983 /**
984 * dnf_state_child_notify_speed_cb:
985 **/
986 static void
dnf_state_child_notify_speed_cb(DnfState * child,GParamSpec * pspec,DnfState * state)987 dnf_state_child_notify_speed_cb(DnfState *child,
988 GParamSpec *pspec,
989 DnfState *state)
990 {
991 dnf_state_set_speed_internal(state,
992 dnf_state_get_speed(child));
993 }
994
995 /**
996 * dnf_state_get_child:
997 * @state: a #DnfState instance.
998 *
999 * Monitor a child state and proxy back up to the parent state.
1000 * You should not g_object_unref() this object, it is owned by the parent.
1001 *
1002 * Returns:(transfer none): A new %DnfState or %NULL for failure
1003 *
1004 * Since: 0.1.0
1005 **/
1006 DnfState *
dnf_state_get_child(DnfState * state)1007 dnf_state_get_child(DnfState *state)
1008 {
1009 DnfState *child = NULL;
1010 DnfStatePrivate *child_priv;
1011 DnfStatePrivate *priv = GET_PRIVATE(state);
1012
1013 g_return_val_if_fail(DNF_IS_STATE(state), NULL);
1014
1015 /* do we care */
1016 if (!priv->report_progress)
1017 return state;
1018
1019 /* already set child */
1020 if (priv->child != NULL) {
1021 g_signal_handler_disconnect(priv->child,
1022 priv->percentage_child_id);
1023 g_signal_handler_disconnect(priv->child,
1024 priv->allow_cancel_child_id);
1025 g_signal_handler_disconnect(priv->child,
1026 priv->action_child_id);
1027 g_signal_handler_disconnect(priv->child,
1028 priv->package_progress_child_id);
1029 g_signal_handler_disconnect(priv->child,
1030 priv->notify_speed_child_id);
1031 g_object_unref(priv->child);
1032 }
1033
1034 /* connect up signals */
1035 child = dnf_state_new();
1036 child_priv = GET_PRIVATE(child);
1037 child_priv->parent = state; /* do not ref! */
1038 priv->child = child;
1039 priv->percentage_child_id =
1040 g_signal_connect(child, "percentage-changed",
1041 G_CALLBACK(dnf_state_child_percentage_changed_cb),
1042 state);
1043 priv->allow_cancel_child_id =
1044 g_signal_connect(child, "allow-cancel-changed",
1045 G_CALLBACK(dnf_state_child_allow_cancel_changed_cb),
1046 state);
1047 priv->action_child_id =
1048 g_signal_connect(child, "action-changed",
1049 G_CALLBACK(dnf_state_child_action_changed_cb),
1050 state);
1051 priv->package_progress_child_id =
1052 g_signal_connect(child, "package-progress-changed",
1053 G_CALLBACK(dnf_state_child_package_progress_changed_cb),
1054 state);
1055 priv->notify_speed_child_id =
1056 g_signal_connect(child, "notify::speed",
1057 G_CALLBACK(dnf_state_child_notify_speed_cb),
1058 state);
1059
1060 /* reset child */
1061 child_priv->current = 0;
1062 child_priv->last_percentage = 0;
1063
1064 /* save so we can recover after child has done */
1065 child_priv->action = priv->action;
1066 priv->child_action = priv->action;
1067
1068 /* set cancellable, creating if required */
1069 if (priv->cancellable == NULL)
1070 priv->cancellable = g_cancellable_new();
1071 dnf_state_set_cancellable(child, priv->cancellable);
1072
1073 /* set the profile state */
1074 dnf_state_set_enable_profile(child, priv->enable_profile);
1075 return child;
1076 }
1077
1078 /**
1079 * dnf_state_set_number_steps_real:
1080 * @state: a #DnfState instance.
1081 * @steps: The number of sub-tasks in this transaction, can be 0
1082 * @strloc: Code position identifier.
1083 *
1084 * Sets the number of sub-tasks, i.e. how many times the dnf_state_done()
1085 * function will be called in the loop.
1086 *
1087 * The function will immediately return with TRUE when the number of steps is 0
1088 * or if dnf_state_set_report_progress(FALSE) was previously called.
1089 *
1090 * Returns: %TRUE for success, %FALSE otherwise
1091 *
1092 * Since: 0.1.0
1093 **/
1094 gboolean
dnf_state_set_number_steps_real(DnfState * state,guint steps,const gchar * strloc)1095 dnf_state_set_number_steps_real(DnfState *state, guint steps, const gchar *strloc)
1096 {
1097 DnfStatePrivate *priv = GET_PRIVATE(state);
1098
1099 g_return_val_if_fail(state != NULL, FALSE);
1100
1101 /* nothing to do for 0 steps */
1102 if (steps == 0)
1103 return TRUE;
1104
1105 /* do we care */
1106 if (!priv->report_progress)
1107 return TRUE;
1108
1109 /* did we call done on a state that did not have a size set? */
1110 if (priv->steps != 0) {
1111 g_warning("steps already set to %i, can't set %i! [%s]",
1112 priv->steps, steps, strloc);
1113 dnf_state_print_parent_chain(state, 0);
1114 return FALSE;
1115 }
1116
1117 /* set id */
1118 g_free(priv->id);
1119 priv->id = g_strdup_printf("%s", strloc);
1120
1121 /* only use the timer if profiling; it's expensive */
1122 if (priv->enable_profile)
1123 g_timer_start(priv->timer);
1124
1125 /* set steps */
1126 priv->steps = steps;
1127
1128 /* success */
1129 return TRUE;
1130 }
1131
1132 /**
1133 * dnf_state_set_steps_real:
1134 * @state: a #DnfState instance.
1135 * @error: A #GError, or %NULL
1136 * @strloc: the code location
1137 * @value: A step weighting variable argument array
1138 * @...: -1 terminated step values
1139 *
1140 * This sets the step weighting, which you will want to do if one action
1141 * will take a bigger chunk of time than another.
1142 *
1143 * All the values must add up to 100, and the list must end with -1.
1144 * Do not use this function directly, instead use the dnf_state_set_steps() macro.
1145 *
1146 * Returns: %TRUE for success, %FALSE otherwise
1147 *
1148 * Since: 0.1.0
1149 **/
1150 gboolean
dnf_state_set_steps_real(DnfState * state,GError ** error,const gchar * strloc,gint value,...)1151 dnf_state_set_steps_real(DnfState *state, GError **error, const gchar *strloc, gint value, ...) try
1152 {
1153 DnfStatePrivate *priv = GET_PRIVATE(state);
1154 va_list args;
1155 guint i;
1156 gint value_temp;
1157 guint total;
1158
1159 g_return_val_if_fail(state != NULL, FALSE);
1160 g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
1161
1162 /* do we care */
1163 if (!priv->report_progress)
1164 return TRUE;
1165
1166 /* we must set at least one thing */
1167 total = value;
1168
1169 /* process the valist */
1170 va_start(args, value);
1171 for (i = 0;; i++) {
1172 value_temp = va_arg(args, gint);
1173 if (value_temp == -1)
1174 break;
1175 total +=(guint) value_temp;
1176 }
1177 va_end(args);
1178
1179 /* does not sum to 100% */
1180 if (total != 100) {
1181 g_set_error(error,
1182 DNF_ERROR,
1183 DNF_ERROR_INTERNAL_ERROR,
1184 _("percentage not 100: %i"),
1185 total);
1186 return FALSE;
1187 }
1188
1189 /* set step number */
1190 if (!dnf_state_set_number_steps_real(state, i+1, strloc)) {
1191 g_set_error(error,
1192 DNF_ERROR,
1193 DNF_ERROR_INTERNAL_ERROR,
1194 _("failed to set number steps: %i"),
1195 i+1);
1196 return FALSE;
1197 }
1198
1199 /* save this data */
1200 total = value;
1201 priv->step_data = g_new0(guint, i+2);
1202 priv->step_profile = g_new0(gdouble, i+2);
1203 priv->step_data[0] = total;
1204 va_start(args, value);
1205 for (i = 0;; i++) {
1206 value_temp = va_arg(args, gint);
1207 if (value_temp == -1)
1208 break;
1209
1210 /* we pre-add the data to make access simpler */
1211 total +=(guint) value_temp;
1212 priv->step_data[i+1] = total;
1213 }
1214 va_end(args);
1215
1216 /* success */
1217 return TRUE;
1218 } CATCH_TO_GERROR(FALSE)
1219
1220 /**
1221 * dnf_state_show_profile:
1222 **/
1223 static void
1224 dnf_state_show_profile(DnfState *state)
1225 {
1226 DnfStatePrivate *priv = GET_PRIVATE(state);
1227 gdouble division;
1228 gdouble total_time = 0.0f;
1229 GString *result;
1230 guint i;
1231 guint uncumalitive = 0;
1232
1233 /* get the total time */
1234 for (i = 0; i < priv->steps; i++)
1235 total_time += priv->step_profile[i];
1236 if (total_time < 0.01)
1237 return;
1238
1239 /* get the total time so we can work out the divisor */
1240 result = g_string_new("Raw timing data was { ");
1241 for (i = 0; i < priv->steps; i++) {
1242 g_string_append_printf(result, "%.3f, ",
1243 priv->step_profile[i]);
1244 }
1245 if (priv->steps > 0)
1246 g_string_set_size(result, result->len - 2);
1247 g_string_append(result, " }\n");
1248
1249 /* what we set */
1250 g_string_append(result, "steps were set as [ ");
1251 for (i = 0; i < priv->steps; i++) {
1252 g_string_append_printf(result, "%i, ",
1253 priv->step_data[i] - uncumalitive);
1254 uncumalitive = priv->step_data[i];
1255 }
1256
1257 /* what we _should_ have set */
1258 g_string_append_printf(result, "-1 ] but should have been: [ ");
1259 division = total_time / 100.0f;
1260 for (i = 0; i < priv->steps; i++) {
1261 g_string_append_printf(result, "%.0f, ",
1262 priv->step_profile[i] / division);
1263 }
1264 g_string_append(result, "-1 ]");
1265 g_printerr("\n\n%s at %s\n\n", result->str, priv->id);
1266 g_string_free(result, TRUE);
1267 }
1268
1269 /**
1270 * dnf_state_check:
1271 * @state: a #DnfState instance.
1272 * @error: A #GError or %NULL
1273 *
1274 * Do any checks to see if the task has been cancelled.
1275 *
1276 * Returns: %TRUE for success, %FALSE otherwise
1277 *
1278 * Since: 0.1.0
1279 **/
1280 gboolean
dnf_state_check(DnfState * state,GError ** error)1281 dnf_state_check(DnfState *state, GError **error) try
1282 {
1283 DnfStatePrivate *priv = GET_PRIVATE(state);
1284
1285 g_return_val_if_fail(state != NULL, FALSE);
1286 g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
1287
1288 /* are we cancelled */
1289 if (g_cancellable_is_cancelled(priv->cancellable)) {
1290 g_set_error_literal(error,
1291 DNF_ERROR,
1292 DNF_ERROR_CANCELLED,
1293 _("cancelled by user action"));
1294 return FALSE;
1295 }
1296 return TRUE;
1297 } CATCH_TO_GERROR(FALSE)
1298
1299 /**
1300 * dnf_state_done_real:
1301 * @state: a #DnfState instance.
1302 * @error: A #GError or %NULL
1303 * @strloc: Code position identifier.
1304 *
1305 * Called when the current sub-task has finished.
1306 *
1307 * Returns: %TRUE for success, %FALSE otherwise
1308 *
1309 * Since: 0.1.0
1310 **/
1311 gboolean
1312 dnf_state_done_real(DnfState *state, GError **error, const gchar *strloc) try
1313 {
1314 DnfStatePrivate *priv = GET_PRIVATE(state);
1315 gdouble elapsed;
1316 gfloat percentage;
1317
1318 g_return_val_if_fail(state != NULL, FALSE);
1319 g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
1320
1321 /* check */
1322 if (!dnf_state_check(state, error))
1323 return FALSE;
1324
1325 /* do we care */
1326 if (!priv->report_progress)
1327 return TRUE;
1328
1329 /* did we call done on a state that did not have a size set? */
1330 if (priv->steps == 0) {
1331 g_set_error(error, DNF_ERROR, DNF_ERROR_INTERNAL_ERROR,
1332 _("done on a state %1$p that did not have a size set! [%2$s]"),
1333 state, strloc);
1334 dnf_state_print_parent_chain(state, 0);
1335 return FALSE;
1336 }
1337
1338 /* check the interval was too big in allow_cancel false mode */
1339 if (priv->enable_profile) {
1340 elapsed = g_timer_elapsed(priv->timer, NULL);
1341 if (!priv->allow_cancel_changed_state && priv->current > 0) {
1342 if (elapsed > 0.1f) {
1343 g_warning("%.1fms between dnf_state_done() and no dnf_state_set_allow_cancel()", elapsed * 1000);
1344 dnf_state_print_parent_chain(state, 0);
1345 }
1346 }
1347
1348 /* save the duration in the array */
1349 if (priv->step_profile != NULL)
1350 priv->step_profile[priv->current] = elapsed;
1351 g_timer_start(priv->timer);
1352 }
1353
1354 /* is already at 100%? */
1355 if (priv->current >= priv->steps) {
1356 g_set_error(error, DNF_ERROR, DNF_ERROR_INTERNAL_ERROR,
1357 _("already at 100%% state [%s]"), strloc);
1358 dnf_state_print_parent_chain(state, 0);
1359 return FALSE;
1360 }
1361
1362 /* is child not at 100%? */
1363 if (priv->child != NULL) {
1364 DnfStatePrivate *child_priv = GET_PRIVATE(priv->child);
1365 if (child_priv->current != child_priv->steps) {
1366 g_print("child is at %i/%i steps and parent done [%s]\n",
1367 child_priv->current, child_priv->steps, strloc);
1368 dnf_state_print_parent_chain(priv->child, 0);
1369 /* do not abort, as we want to clean this up */
1370 }
1371 }
1372
1373 /* we just checked for cancel, so it's not true to say we're blocking */
1374 dnf_state_set_allow_cancel(state, TRUE);
1375
1376 /* another */
1377 priv->current++;
1378
1379 /* find new percentage */
1380 if (priv->step_data == NULL) {
1381 percentage = dnf_state_discrete_to_percent(priv->current,
1382 priv->steps);
1383 } else {
1384 /* this is cumalative, for speedy access */
1385 percentage = priv->step_data[priv->current - 1];
1386 }
1387 dnf_state_set_percentage(state,(guint) percentage);
1388
1389 /* show any profiling stats */
1390 if (priv->enable_profile &&
1391 priv->current == priv->steps &&
1392 priv->step_profile != NULL) {
1393 dnf_state_show_profile(state);
1394 }
1395
1396 /* reset child if it exists */
1397 if (priv->child != NULL)
1398 dnf_state_reset(priv->child);
1399 return TRUE;
CATCH_TO_GERROR(FALSE)1400 } CATCH_TO_GERROR(FALSE)
1401
1402 /**
1403 * dnf_state_finished_real:
1404 * @state: A #DnfState
1405 * @error: A #GError or %NULL
1406 * @strloc: Code position identifier.
1407 *
1408 * Called when the current sub-task has finished.
1409 *
1410 * Returns: %TRUE for success, %FALSE otherwise
1411 *
1412 * Since: 0.1.0
1413 **/
1414 gboolean
1415 dnf_state_finished_real(DnfState *state, GError **error, const gchar *strloc) try
1416 {
1417 DnfStatePrivate *priv = GET_PRIVATE(state);
1418
1419 g_return_val_if_fail(state != NULL, FALSE);
1420 g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
1421
1422 /* check */
1423 if (!dnf_state_check(state, error))
1424 return FALSE;
1425
1426 /* is already at 100%? */
1427 if (priv->current == priv->steps)
1428 return TRUE;
1429
1430 /* all done */
1431 priv->current = priv->steps;
1432
1433 /* set new percentage */
1434 dnf_state_set_percentage(state, 100);
1435 return TRUE;
1436 } CATCH_TO_GERROR(FALSE)
1437
1438 /**
1439 * dnf_state_new:
1440 *
1441 * Creates a new #DnfState.
1442 *
1443 * Returns:(transfer full): a #DnfState
1444 *
1445 * Since: 0.1.0
1446 **/
1447 DnfState *
1448 dnf_state_new(void)
1449 {
1450 return DNF_STATE(g_object_new(DNF_TYPE_STATE, NULL));
1451 }
1452