aboutsummaryrefslogtreecommitdiff
path: root/src/lib/common/timeout.c
blob: b12780b01463e5c47db284df5f46fc2dd2e07f1f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#include "timeout.h"

#include <stdbool.h>

#define LOG(kind, ...) GNUNET_log_from (kind, "dbus-timeout", __VA_ARGS__)

struct Timeout
{
  /* doubly-linked list */
  struct Timeout *next;
  struct Timeout *prev;

  DBusTimeout *timeout;

  bool scheduled;

  struct GNUNET_SCHEDULER_Task *task;

  unsigned ref_count;
};

struct Timeout *
timeout_create (
    DBusTimeout *timeout)
{
  struct Timeout *t = GNUNET_new (struct Timeout);

  t->timeout = timeout;
  t->scheduled = false;
  t->ref_count = 1;

  return t;
}

void
timeout_ref (
    struct Timeout *t)
{
  t->ref_count++;
}

void
timeout_unref (
    struct Timeout *t)
{
  if (0 == t->ref_count)
  {
    LOG (GNUNET_ERROR_TYPE_ERROR, "Tried to unref timeout with ref_count == 0\n");
    GNUNET_abort_ ();
  };

  if (0 == --t->ref_count)
  {
    GNUNET_free (t);
  }
}

static void
handle_timeout (
    void *cls,
    const struct GNUNET_SCHEDULER_TaskContext *tc)
{
  struct Timeout *t = (struct Timeout *)cls;

  t->scheduled = false;

  if (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT)
    dbus_timeout_handle (t->timeout);

  if (t->ref_count > 1 && ! (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
    timeout_schedule (t);
  timeout_unref (t);
};

void
timeout_schedule (
    struct Timeout *t)
{
  if (! dbus_timeout_get_enabled (t->timeout))
  {
    LOG (GNUNET_ERROR_TYPE_WARNING, "Tried to schedule timeout that is disabled!\n");
    return;
  };

  if (t->scheduled)
    return;

  int interval = dbus_timeout_get_interval (t->timeout);
  struct GNUNET_TIME_Relative delay;
  delay.rel_value_us = interval * 1000;
  t->task = GNUNET_SCHEDULER_add_delayed (
      delay,
      handle_timeout,
      t);
  t->scheduled = true;
  timeout_ref (t);
}

void
do_timeout_unschedule (
    void *cls,
    const struct GNUNET_SCHEDULER_TaskContext *tc)
{
  (void)tc;
  struct Timeout *t = (struct Timeout *)cls;

  if (! t->scheduled)
    return;
  
  void *ret = GNUNET_SCHEDULER_cancel (t->task);
  if ((struct Timeout *)ret != t)
    LOG (GNUNET_ERROR_TYPE_WARNING, "Weird result unscheduling task. t == %p, GNUNET_SCHEDULER_cancel returned %p\n", t, ret);

  timeout_unref (t);
}

void
timeout_unschedule (
    struct Timeout *t)
{
  GNUNET_SCHEDULER_add_now (do_timeout_unschedule, t);
}

struct TimeoutIter *
timeout_find (struct TimeoutIter *ti, DBusTimeout *timeout)
{
  while (ti && ti->t->timeout != timeout)
    ti = ti->next;
  return ti;
}