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_queue_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
70
161
  leasing->expiry_func_id = 0;
71
161
}
72

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

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

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

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

            
100
/*---------------------------------------------------------------------------*/
101

            
102
/*
103
  End the lease on all objects whose expiry time has passed.
104

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

            
112
  ExpiryElement *head, *current;
113
  gint64 secs = g_get_monotonic_time () / 1000000;
114

            
115
  head = g_queue_peek_head (leasing->expiry_queue);
116
  while (head != NULL && head->expiry_s <= secs)
117
    {
118
      current = g_queue_pop_head (leasing->expiry_queue);
119

            
120
#ifdef SPI_ATK_DEBUG
121
      g_debug ("REVOKE - ");
122
      spi_cache_print_info (current->object);
123
#endif
124

            
125
      g_hash_table_remove (leasing->expiry_queue_hash, current->object);
126
      g_object_unref (current->object);
127
      g_slice_free (ExpiryElement, current);
128

            
129
      head = g_queue_peek_head (leasing->expiry_queue);
130
    }
131

            
132
  leasing->expiry_func_id = 0;
133
  add_expiry_timeout (leasing);
134

            
135
  return FALSE;
136
}
137

            
138
/*---------------------------------------------------------------------------*/
139

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

            
145
  Otherwise calculate the next wake time using the top of the queue
146
  and add the next expiry function.
147

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

            
158
171
  if (leasing->expiry_func_id != 0)
159
10
    return;
160

            
161
161
  elem = (ExpiryElement *) g_queue_peek_head (leasing->expiry_queue);
162
161
  if (elem == NULL)
163
    return;
164

            
165
161
  next_expiry = elem->expiry_s - secs;
166
161
  leasing->expiry_func_id = spi_timeout_add_seconds (next_expiry,
167
                                                     expiry_func, leasing);
168
}
169

            
170
/*---------------------------------------------------------------------------*/
171

            
172
/*
173
  The lease time is expected to be in seconds, the rounding is going to be to
174
  intervals of 1 second.
175

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

            
182
GObject *
183
171
spi_leasing_take (SpiLeasing *leasing, GObject *object)
184
{
185
  GList *old_entry;
186
  ExpiryElement *elem;
187

            
188
  /*
189
     Get the current time.
190
     Quantize the time.
191
     Remove any earlier lease for the given object.
192
     Add the release event to the queue.
193
     Check the next expiry.
194
   */
195

            
196
171
  gint64 secs = g_get_monotonic_time () / 1000000;
197
  guint expiry_s;
198

            
199
171
  expiry_s = secs + EXPIRY_TIME_S;
200

            
201
171
  old_entry = g_hash_table_lookup (leasing->expiry_queue_hash, object);
202
171
  if (old_entry)
203
    {
204
      elem = old_entry->data;
205
      g_queue_delete_link (leasing->expiry_queue, old_entry);
206
      g_hash_table_remove (leasing->expiry_queue_hash, object);
207
    }
208
  else
209
    {
210
171
      elem = g_slice_new (ExpiryElement);
211
171
      elem->object = g_object_ref (object);
212
    }
213

            
214
171
  elem->expiry_s = expiry_s;
215

            
216
171
  g_queue_push_tail (leasing->expiry_queue, elem);
217
171
  g_hash_table_insert (leasing->expiry_queue_hash, object, leasing->expiry_queue->tail);
218

            
219
171
  add_expiry_timeout (leasing);
220

            
221
#ifdef SPI_ATK_DEBUG
222
  g_debug ("LEASE - ");
223
  spi_cache_print_info (object);
224
#endif
225

            
226
171
  return object;
227
}
228

            
229
/*END------------------------------------------------------------------------*/