LCOV - code coverage report
Current view: top level - pkcs11/gkm - gkm-timer.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 98.9 % 92 91
Test Date: 2024-04-08 13:24:42 Functions: 100.0 % 6 6

            Line data    Source code
       1              : /*
       2              :  * gnome-keyring
       3              :  *
       4              :  * Copyright (C) 2009 Stefan Walter
       5              :  *
       6              :  * This program is free software; you can redistribute it and/or modify
       7              :  * it under the terms of the GNU Lesser General Public License as
       8              :  * published by the Free Software Foundation; either version 2.1 of
       9              :  * the License, or (at your option) any later version.
      10              :  *
      11              :  * This program is distributed in the hope that it will be useful, but
      12              :  * WITHOUT ANY WARRANTY; without even the implied warranty of
      13              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14              :  * Lesser General Public License for more details.
      15              :  *
      16              :  * You should have received a copy of the GNU Lesser General Public
      17              :  * License along with this program; if not, see
      18              :  * <http://www.gnu.org/licenses/>.
      19              :  */
      20              : 
      21              : #include "config.h"
      22              : 
      23              : #include "gkm-timer.h"
      24              : 
      25              : #include "egg/egg-error.h"
      26              : 
      27              : #include <glib.h>
      28              : 
      29              : struct _GkmTimer {
      30              :         gint64 when;
      31              :         GMutex *mutex;
      32              :         gpointer identifier;
      33              :         GkmTimerFunc callback;
      34              :         gpointer user_data;
      35              : };
      36              : 
      37              : static GMutex timer_mutex = { 0, };
      38              : static GQueue *timer_queue = NULL;
      39              : static GThread *timer_thread = NULL;
      40              : static GCond timer_condition;
      41              : static GCond *timer_cond = NULL;
      42              : static gboolean timer_run = FALSE;
      43              : static gint timer_refs = 0;
      44              : 
      45              : static gint
      46            6 : compare_timers (gconstpointer a, gconstpointer b, gpointer user_data)
      47              : {
      48            6 :         const GkmTimer *ta = a;
      49            6 :         const GkmTimer *tb = b;
      50            6 :         if (ta->when < tb->when)
      51            3 :                 return -1;
      52            3 :         return ta->when > tb->when;
      53              : }
      54              : 
      55              : static gpointer
      56          206 : timer_thread_func (gpointer unused)
      57              : {
      58              :         GkmTimer *timer;
      59              : 
      60          206 :         g_mutex_lock (&timer_mutex);
      61              : 
      62          421 :         while (timer_run) {
      63          215 :                 timer = g_queue_peek_head (timer_queue);
      64              : 
      65              :                 /* Nothing in the queue, wait until we have action */
      66          215 :                 if (!timer) {
      67          201 :                         g_cond_wait (timer_cond, &timer_mutex);
      68          201 :                         continue;
      69              :                 }
      70              : 
      71           14 :                 if (timer->when) {
      72           13 :                         gint64 offset = timer->when - g_get_monotonic_time ();
      73           13 :                         if (offset > 0) {
      74            6 :                                 g_cond_wait_until (timer_cond, &timer_mutex, g_get_monotonic_time () + offset);
      75            6 :                                 continue;
      76              :                         }
      77              :                 }
      78              : 
      79              :                 /* Leave our thread mutex, and enter the module */
      80            8 :                 g_mutex_unlock (&timer_mutex);
      81            8 :                 g_mutex_lock (timer->mutex);
      82              : 
      83            8 :                         if (timer->callback)
      84            7 :                                 (timer->callback) (timer, timer->user_data);
      85              : 
      86              :                 /* Leave the module, and go back into our thread mutex */
      87            8 :                 g_mutex_unlock (timer->mutex);
      88            8 :                 g_mutex_lock (&timer_mutex);
      89              : 
      90              :                 /* There's a chance that the timer may no longer be at head of queue */
      91            8 :                 g_queue_remove (timer_queue, timer);
      92            8 :                 g_slice_free (GkmTimer, timer);
      93              :         }
      94              : 
      95          206 :         g_mutex_unlock (&timer_mutex);
      96          206 :         return NULL;
      97              : }
      98              : 
      99              : void
     100          288 : gkm_timer_initialize (void)
     101              : {
     102          288 :         GError *error = NULL;
     103          288 :         g_mutex_lock (&timer_mutex);
     104              : 
     105          288 :                 g_atomic_int_inc (&timer_refs);
     106          288 :                 if (!timer_thread) {
     107          206 :                         timer_run = TRUE;
     108          206 :                         timer_thread = g_thread_new ("timer", timer_thread_func, NULL);
     109          206 :                         if (timer_thread) {
     110          206 :                                 g_assert (timer_queue == NULL);
     111          206 :                                 timer_queue = g_queue_new ();
     112              : 
     113          206 :                                 g_assert (timer_cond == NULL);
     114          206 :                                 timer_cond = &timer_condition;
     115          206 :                                 g_cond_init (timer_cond);
     116              :                         } else {
     117            0 :                                 g_warning ("could not create timer thread: %s",
     118              :                                            egg_error_message (error));
     119              :                         }
     120              :                 }
     121              : 
     122          288 :         g_mutex_unlock (&timer_mutex);
     123          288 : }
     124              : 
     125              : void
     126          288 : gkm_timer_shutdown (void)
     127              : {
     128              :         GkmTimer *timer;
     129              : 
     130          288 :         if (g_atomic_int_dec_and_test (&timer_refs)) {
     131              : 
     132          206 :                 g_mutex_lock (&timer_mutex);
     133              : 
     134          206 :                         timer_run = FALSE;
     135          206 :                         g_assert (timer_cond);
     136          206 :                         g_cond_broadcast (timer_cond);
     137              : 
     138          206 :                 g_mutex_unlock (&timer_mutex);
     139              : 
     140          206 :                 g_assert (timer_thread);
     141          206 :                 g_thread_join (timer_thread);
     142          206 :                 timer_thread = NULL;
     143              : 
     144          206 :                 g_assert (timer_queue);
     145              : 
     146              :                 /* Cleanup any outstanding timers */
     147          209 :                 while (!g_queue_is_empty (timer_queue)) {
     148            3 :                         timer = g_queue_pop_head (timer_queue);
     149            3 :                         g_slice_free (GkmTimer, timer);
     150              :                 }
     151              : 
     152          206 :                 g_queue_free (timer_queue);
     153          206 :                 timer_queue = NULL;
     154              : 
     155          206 :                 g_cond_clear (timer_cond);
     156          206 :                 timer_cond = NULL;
     157              :         }
     158          288 : }
     159              : 
     160              : /* We're the only caller of this function */
     161              : GMutex* _gkm_module_get_scary_mutex_that_you_should_not_touch (GkmModule *self);
     162              : 
     163              : GkmTimer*
     164           11 : gkm_timer_start (GkmModule *module, glong seconds, GkmTimerFunc callback, gpointer user_data)
     165              : {
     166              :         GkmTimer *timer;
     167              : 
     168           11 :         g_return_val_if_fail (callback, NULL);
     169           11 :         g_return_val_if_fail (timer_queue, NULL);
     170              : 
     171           11 :         timer = g_slice_new (GkmTimer);
     172           11 :         timer->when = g_get_monotonic_time () + seconds * G_TIME_SPAN_SECOND;
     173           11 :         timer->callback = callback;
     174           11 :         timer->user_data = user_data;
     175              : 
     176           11 :         timer->mutex = _gkm_module_get_scary_mutex_that_you_should_not_touch (module);
     177           11 :         g_return_val_if_fail (timer->mutex, NULL);
     178              : 
     179           11 :         g_mutex_lock (&timer_mutex);
     180              : 
     181           11 :                 g_assert (timer_queue);
     182           11 :                 g_queue_insert_sorted (timer_queue, timer, compare_timers, NULL);
     183           11 :                 g_assert (timer_cond);
     184           11 :                 g_cond_broadcast (timer_cond);
     185              : 
     186           11 :         g_mutex_unlock (&timer_mutex);
     187              : 
     188              :         /*
     189              :          * Note that the timer thread could not already completed this timer.
     190              :          * This is because we're in the module, and in order to complete a timer
     191              :          * the timer thread must enter the module mutex.
     192              :          */
     193              : 
     194           11 :         return timer;
     195              : }
     196              : 
     197              : void
     198            1 : gkm_timer_cancel (GkmTimer *timer)
     199              : {
     200              :         GList *link;
     201              : 
     202            1 :         g_return_if_fail (timer_queue);
     203              : 
     204            1 :         g_mutex_lock (&timer_mutex);
     205              : 
     206            1 :                 g_assert (timer_queue);
     207              : 
     208            1 :                 link = g_queue_find (timer_queue, timer);
     209            1 :                 if (link) {
     210              : 
     211              :                         /*
     212              :                          * For thread safety the timer struct must be freed
     213              :                          * from the timer thread. So to cancel, what we do
     214              :                          * is move the timer to the front of the queue,
     215              :                          * and reset the callback and when.
     216              :                          */
     217              : 
     218            1 :                         timer->when = 0;
     219            1 :                         timer->callback = NULL;
     220              : 
     221            1 :                         g_queue_delete_link (timer_queue, link);
     222            1 :                         g_queue_push_head (timer_queue, timer);
     223              : 
     224            1 :                         g_assert (timer_cond);
     225            1 :                         g_cond_broadcast (timer_cond);
     226              :                 }
     227              : 
     228            1 :         g_mutex_unlock (&timer_mutex);
     229              : }
        

Generated by: LCOV version 2.0-1