1
/*
2
 * AT-SPI - Assistive Technology Service Provider Interface
3
 * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
4
 *
5
 * Copyright 2009, 2010 Codethink Ltd.
6
 *
7
 * This library is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Lesser General Public
9
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
11
 *
12
 * This library is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with this library; if not, write to the
19
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20
 * Boston, MA 02110-1301, USA.
21
 */
22

            
23
#include <stdio.h>
24
#include <stdlib.h>
25
#include <string.h>
26

            
27
#include "accessible-leasing.h"
28
#include "event.h"
29

            
30
#ifdef SPI_ATK_DEBUG
31
#include "accessible-cache.h"
32
#endif
33

            
34
/*---------------------------------------------------------------------------*/
35

            
36
SpiLeasing *spi_global_leasing;
37

            
38
typedef struct _ExpiryElement
39
{
40
  gint64 expiry_s;
41
  GObject *object;
42
} ExpiryElement;
43

            
44
static void spi_leasing_dispose (GObject *object);
45

            
46
static void spi_leasing_finalize (GObject *object);
47

            
48
static void add_expiry_timeout (SpiLeasing *leasing);
49

            
50
/*---------------------------------------------------------------------------*/
51

            
52
161
G_DEFINE_TYPE (SpiLeasing, spi_leasing, G_TYPE_OBJECT)
53

            
54
static void
55
161
spi_leasing_class_init (SpiLeasingClass *klass)
56
{
57
161
  GObjectClass *object_class = (GObjectClass *) klass;
58

            
59
161
  spi_leasing_parent_class = g_type_class_ref (G_TYPE_OBJECT);
60

            
61
161
  object_class->finalize = spi_leasing_finalize;
62
161
  object_class->dispose = spi_leasing_dispose;
63
161
}
64

            
65
static void
66
161
spi_leasing_init (SpiLeasing *leasing)
67
{
68
161
  leasing->expiry_queue = g_queue_new ();
69
161
  leasing->expiry_func_id = 0;
70
161
}
71

            
72
static void
73
161
spi_leasing_finalize (GObject *object)
74
{
75
161
  SpiLeasing *leasing = SPI_LEASING (object);
76

            
77
161
  if (leasing->expiry_func_id)
78
161
    g_source_remove (leasing->expiry_func_id);
79
161
  g_queue_free (leasing->expiry_queue);
80
161
  G_OBJECT_CLASS (spi_leasing_parent_class)->finalize (object);
81
161
}
82

            
83
static void
84
161
spi_leasing_dispose (GObject *object)
85
{
86
161
  SpiLeasing *leasing = SPI_LEASING (object);
87

            
88
  ExpiryElement *head;
89
332
  while ((head = g_queue_pop_head (leasing->expiry_queue)))
90
    {
91
171
      g_object_unref (head->object);
92
171
      g_slice_free (ExpiryElement, head);
93
    }
94
161
  G_OBJECT_CLASS (spi_leasing_parent_class)->dispose (object);
95
161
}
96

            
97
/*---------------------------------------------------------------------------*/
98

            
99
/*
100
  End the lease on all objects whose expiry time has passed.
101

            
102
  Check when the next event is and set the next expiry func.
103
*/
104
static gboolean
105
expiry_func (gpointer data)
106
{
107
  SpiLeasing *leasing = SPI_LEASING (data);
108

            
109
  ExpiryElement *head, *current;
110
  gint64 secs = g_get_monotonic_time () / 1000000;
111

            
112
  head = g_queue_peek_head (leasing->expiry_queue);
113
  while (head != NULL && head->expiry_s <= secs)
114
    {
115
      current = g_queue_pop_head (leasing->expiry_queue);
116

            
117
#ifdef SPI_ATK_DEBUG
118
      g_debug ("REVOKE - ");
119
      spi_cache_print_info (current->object);
120
#endif
121

            
122
      g_object_unref (current->object);
123
      g_slice_free (ExpiryElement, current);
124

            
125
      head = g_queue_peek_head (leasing->expiry_queue);
126
    }
127

            
128
  leasing->expiry_func_id = 0;
129
  add_expiry_timeout (leasing);
130

            
131
  return FALSE;
132
}
133

            
134
/*---------------------------------------------------------------------------*/
135

            
136
/*
137
  Checks if an expiry timeout is already scheduled, if so returns.
138
  This is becasue events will always be added to the end of the queue.
139
  Events are always later in time to previoulsy added events.
140

            
141
  Otherwise calculate the next wake time using the top of the queue
142
  and add the next expiry function.
143

            
144
  This function is called when a lease is added or at the end of the
145
  expiry function to add the next expiry timeout.
146
*/
147
static void
148
171
add_expiry_timeout (SpiLeasing *leasing)
149
{
150
  ExpiryElement *elem;
151
171
  gint64 secs = g_get_monotonic_time () / 1000000;
152
  guint next_expiry;
153

            
154
171
  if (leasing->expiry_func_id != 0)
155
10
    return;
156

            
157
161
  elem = (ExpiryElement *) g_queue_peek_head (leasing->expiry_queue);
158
161
  if (elem == NULL)
159
    return;
160

            
161
161
  next_expiry = elem->expiry_s - secs;
162
161
  leasing->expiry_func_id = spi_timeout_add_seconds (next_expiry,
163
                                                     expiry_func, leasing);
164
}
165

            
166
/*---------------------------------------------------------------------------*/
167

            
168
/*
169
  The lease time is expected to be in seconds, the rounding is going to be to
170
  intervals of 1 second.
171

            
172
  The lease time is going to be rounded up, as the lease time should be
173
  considered a MINIMUM that the object will be leased for.
174
*/
175
#define LEASE_TIME_S 15
176
#define EXPIRY_TIME_S (LEASE_TIME_S + 1)
177

            
178
GObject *
179
171
spi_leasing_take (SpiLeasing *leasing, GObject *object)
180
{
181
  /*
182
     Get the current time.
183
     Quantize the time.
184
     Add the release event to the queue.
185
     Check the next expiry.
186
   */
187

            
188
171
  gint64 secs = g_get_monotonic_time () / 1000000;
189
  guint expiry_s;
190

            
191
  ExpiryElement *elem;
192

            
193
171
  expiry_s = secs + EXPIRY_TIME_S;
194

            
195
171
  elem = g_slice_new (ExpiryElement);
196
171
  elem->expiry_s = expiry_s;
197
171
  elem->object = g_object_ref (object);
198

            
199
171
  g_queue_push_tail (leasing->expiry_queue, elem);
200

            
201
171
  add_expiry_timeout (leasing);
202

            
203
#ifdef SPI_ATK_DEBUG
204
  g_debug ("LEASE - ");
205
  spi_cache_print_info (object);
206
#endif
207

            
208
171
  return object;
209
}
210

            
211
/*END------------------------------------------------------------------------*/