a5b9af9291cc5b0cb81d754d8cea64372295e0c5
[platform/upstream/dbus.git] / dbus / dbus-watch.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-watch.c DBusWatch implementation
3  *
4  * Copyright (C) 2002, 2003  Red Hat Inc.
5  *
6  * Licensed under the Academic Free License version 1.2
7  * 
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  * 
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23
24 #include "dbus-internals.h"
25 #include "dbus-watch.h"
26 #include "dbus-list.h"
27
28 /**
29  * @defgroup DBusWatchInternals DBusWatch implementation details
30  * @ingroup  DBusInternals
31  * @brief implementation details for DBusWatch
32  * 
33  * @{
34  */
35
36 struct DBusWatch
37 {
38   int refcount;                        /**< Reference count */
39   int fd;                              /**< File descriptor. */
40   unsigned int flags;                  /**< Conditions to watch. */
41   void *data;                          /**< Application data. */
42   DBusFreeFunction free_data_function; /**< Free the application data. */
43   unsigned int enabled : 1;            /**< Whether it's enabled. */
44 };
45
46 /**
47  * Creates a new DBusWatch. Normally used by a DBusTransport
48  * implementation.
49  * @param fd the file descriptor to be watched.
50  * @param flags the conditions to watch for on the descriptor.
51  * @param enabled the initial enabled state
52  * @returns the new DBusWatch object.
53  */
54 DBusWatch*
55 _dbus_watch_new (int          fd,
56                  unsigned int flags,
57                  dbus_bool_t  enabled)
58 {
59   DBusWatch *watch;
60
61 #define VALID_WATCH_FLAGS (DBUS_WATCH_WRITABLE | DBUS_WATCH_READABLE)
62   
63   _dbus_assert ((flags & VALID_WATCH_FLAGS) == flags);
64   
65   watch = dbus_new0 (DBusWatch, 1);
66   if (watch == NULL)
67     return NULL;
68   
69   watch->refcount = 1;
70   watch->fd = fd;
71   watch->flags = flags;
72   watch->enabled = enabled;
73
74   return watch;
75 }
76
77 /**
78  * Increments the reference count of a DBusWatch object.
79  *
80  * @param watch the watch object.
81  */
82 void
83 _dbus_watch_ref (DBusWatch *watch)
84 {
85   watch->refcount += 1;
86 }
87
88 /**
89  * Decrements the reference count of a DBusWatch object
90  * and finalizes the object if the count reaches zero.
91  *
92  * @param watch the watch object.
93  */
94 void
95 _dbus_watch_unref (DBusWatch *watch)
96 {
97   _dbus_assert (watch != NULL);
98   _dbus_assert (watch->refcount > 0);
99
100   watch->refcount -= 1;
101   if (watch->refcount == 0)
102     {
103       dbus_watch_set_data (watch, NULL, NULL); /* call free_data_function */
104       dbus_free (watch);
105     }
106 }
107
108 /**
109  * Clears the file descriptor from a now-invalid watch object so that
110  * no one tries to use it.  This is because a watch may stay alive due
111  * to reference counts after the file descriptor is closed.
112  * Invalidation makes it easier to catch bugs. It also
113  * keeps people from doing dorky things like assuming file descriptors
114  * are unique (never recycled).
115  *
116  * @param watch the watch object.
117  */
118 void
119 _dbus_watch_invalidate (DBusWatch *watch)
120 {
121   watch->fd = -1;
122   watch->flags = 0;
123 }
124
125 /**
126  * Sanitizes the given condition so that it only contains
127  * flags that the DBusWatch requested. e.g. if the
128  * watch is a DBUS_WATCH_READABLE watch then
129  * DBUS_WATCH_WRITABLE will be stripped from the condition.
130  *
131  * @param watch the watch object.
132  * @param condition address of the condition to sanitize.
133  */
134 void
135 _dbus_watch_sanitize_condition (DBusWatch    *watch,
136                                 unsigned int *condition)
137 {
138   if (!(watch->flags & DBUS_WATCH_READABLE))
139     *condition &= ~DBUS_WATCH_READABLE;
140   if (!(watch->flags & DBUS_WATCH_WRITABLE))
141     *condition &= ~DBUS_WATCH_WRITABLE;
142 }
143
144
145 /**
146  * @typedef DBusWatchList
147  *
148  * Opaque data type representing a list of watches
149  * and a set of DBusAddWatchFunction/DBusRemoveWatchFunction.
150  * Automatically handles removing/re-adding watches
151  * when the DBusAddWatchFunction is updated or changed.
152  * Holds a reference count to each watch.
153  *
154  * Used in the implementation of both DBusServer and
155  * DBusClient.
156  *
157  */
158
159 /**
160  * DBusWatchList implementation details. All fields
161  * are private.
162  *
163  */
164 struct DBusWatchList
165 {
166   DBusList *watches;           /**< Watch objects. */
167
168   DBusAddWatchFunction add_watch_function;    /**< Callback for adding a watch. */
169   DBusRemoveWatchFunction remove_watch_function; /**< Callback for removing a watch. */
170   DBusWatchToggledFunction watch_toggled_function; /**< Callback on toggling enablement */
171   void *watch_data;                           /**< Data for watch callbacks */
172   DBusFreeFunction watch_free_data_function;  /**< Free function for watch callback data */
173 };
174
175 /**
176  * Creates a new watch list. Returns #NULL if insufficient
177  * memory exists.
178  *
179  * @returns the new watch list, or #NULL on failure.
180  */
181 DBusWatchList*
182 _dbus_watch_list_new (void)
183 {
184   DBusWatchList *watch_list;
185
186   watch_list = dbus_new0 (DBusWatchList, 1);
187   if (watch_list == NULL)
188     return NULL;
189
190   return watch_list;
191 }
192
193 /**
194  * Frees a DBusWatchList.
195  *
196  * @param watch_list the watch list.
197  */
198 void
199 _dbus_watch_list_free (DBusWatchList *watch_list)
200 {
201   /* free watch_data and removes watches as a side effect */
202   _dbus_watch_list_set_functions (watch_list,
203                                   NULL, NULL, NULL, NULL, NULL);
204   _dbus_list_foreach (&watch_list->watches,
205                       (DBusForeachFunction) _dbus_watch_unref,
206                       NULL);
207   _dbus_list_clear (&watch_list->watches);
208
209   dbus_free (watch_list);
210 }
211
212 /**
213  * Sets the watch functions. This function is the "backend"
214  * for dbus_connection_set_watch_functions() and
215  * dbus_server_set_watch_functions().
216  *
217  * @param watch_list the watch list.
218  * @param add_function the add watch function.
219  * @param remove_function the remove watch function.
220  * @param toggled_function function on toggling enabled flag, or #NULL
221  * @param data the data for those functions.
222  * @param free_data_function the function to free the data.
223  * @returns #FALSE if not enough memory
224  *
225  */
226 dbus_bool_t
227 _dbus_watch_list_set_functions (DBusWatchList           *watch_list,
228                                 DBusAddWatchFunction     add_function,
229                                 DBusRemoveWatchFunction  remove_function,
230                                 DBusWatchToggledFunction toggled_function,
231                                 void                    *data,
232                                 DBusFreeFunction         free_data_function)
233 {
234   /* Add watches with the new watch function, failing on OOM */
235   if (add_function != NULL)
236     {
237       DBusList *link;
238       
239       link = _dbus_list_get_first_link (&watch_list->watches);
240       while (link != NULL)
241         {
242           DBusList *next = _dbus_list_get_next_link (&watch_list->watches,
243                                                      link);
244       
245           if (!(* add_function) (link->data, data))
246             {
247               /* remove it all again and return FALSE */
248               DBusList *link2;
249               
250               link2 = _dbus_list_get_first_link (&watch_list->watches);
251               while (link2 != link)
252                 {
253                   DBusList *next = _dbus_list_get_next_link (&watch_list->watches,
254                                                              link2);
255
256                   (* remove_function) (link2->data, data);
257                   
258                   link2 = next;
259                 }
260
261               return FALSE;
262             }
263       
264           link = next;
265         }
266     }
267   
268   /* Remove all current watches from previous watch handlers */
269
270   if (watch_list->remove_watch_function != NULL)
271     {
272       _dbus_list_foreach (&watch_list->watches,
273                           (DBusForeachFunction) watch_list->remove_watch_function,
274                           watch_list->watch_data);
275     }
276
277   if (watch_list->watch_free_data_function != NULL)
278     (* watch_list->watch_free_data_function) (watch_list->watch_data);
279   
280   watch_list->add_watch_function = add_function;
281   watch_list->remove_watch_function = remove_function;
282   watch_list->watch_toggled_function = toggled_function;
283   watch_list->watch_data = data;
284   watch_list->watch_free_data_function = free_data_function;
285
286   return TRUE;
287 }
288
289 /**
290  * Adds a new watch to the watch list, invoking the
291  * application DBusAddWatchFunction if appropriate.
292  *
293  * @param watch_list the watch list.
294  * @param watch the watch to add.
295  * @returns #TRUE on success, #FALSE if no memory.
296  */
297 dbus_bool_t
298 _dbus_watch_list_add_watch (DBusWatchList *watch_list,
299                             DBusWatch     *watch)
300 {
301   if (!_dbus_list_append (&watch_list->watches, watch))
302     return FALSE;
303   
304   _dbus_watch_ref (watch);
305
306   if (watch_list->add_watch_function != NULL)
307     {
308       if (!(* watch_list->add_watch_function) (watch,
309                                                watch_list->watch_data))
310         {
311           _dbus_list_remove_last (&watch_list->watches, watch);
312           _dbus_watch_unref (watch);
313           return FALSE;
314         }
315     }
316   
317   return TRUE;
318 }
319
320 /**
321  * Removes a watch from the watch list, invoking the
322  * application's DBusRemoveWatchFunction if appropriate.
323  *
324  * @param watch_list the watch list.
325  * @param watch the watch to remove.
326  */
327 void
328 _dbus_watch_list_remove_watch  (DBusWatchList *watch_list,
329                                 DBusWatch     *watch)
330 {
331   if (!_dbus_list_remove (&watch_list->watches, watch))
332     _dbus_assert_not_reached ("Nonexistent watch was removed");
333
334   if (watch_list->remove_watch_function != NULL)
335     (* watch_list->remove_watch_function) (watch,
336                                            watch_list->watch_data);
337   
338   _dbus_watch_unref (watch);
339 }
340
341 /**
342  * Sets a watch to the given enabled state, invoking the
343  * application's DBusWatchToggledFunction if appropriate.
344  *
345  * @param watch_list the watch list.
346  * @param watch the watch to toggle.
347  * @param enabled #TRUE to enable
348  */
349 void
350 _dbus_watch_list_toggle_watch (DBusWatchList           *watch_list,
351                                DBusWatch               *watch,
352                                dbus_bool_t              enabled)
353 {
354   enabled = !!enabled;
355   
356   if (enabled == watch->enabled)
357     return;
358
359   watch->enabled = enabled;
360   
361   if (watch_list->watch_toggled_function != NULL)
362     (* watch_list->watch_toggled_function) (watch,
363                                             watch_list->watch_data);
364 }
365
366 /** @} */
367
368 /**
369  * @defgroup DBusWatch DBusWatch
370  * @ingroup  DBus
371  * @brief Object representing a file descriptor to be watched.
372  *
373  * Types and functions related to DBusWatch. A watch represents
374  * a file descriptor that the main loop needs to monitor,
375  * as in Qt's QSocketNotifier or GLib's g_io_add_watch().
376  * 
377  * @{
378  */
379
380 /**
381  * @typedef DBusWatch
382  *
383  * Opaque object representing a file descriptor
384  * to be watched for changes in readability,
385  * writability, or hangup.
386  */
387
388 /**
389  * Gets the file descriptor that should be watched.
390  *
391  * @param watch the DBusWatch object.
392  * @returns the file descriptor to watch.
393  */
394 int
395 dbus_watch_get_fd (DBusWatch *watch)
396 {
397   return watch->fd;
398 }
399
400 /**
401  * Gets flags from DBusWatchFlags indicating
402  * what conditions should be monitored on the
403  * file descriptor.
404  * 
405  * The flags returned will only contain DBUS_WATCH_READABLE
406  * and DBUS_WATCH_WRITABLE, never DBUS_WATCH_HANGUP or
407  * DBUS_WATCH_ERROR; all watches implicitly include a watch
408  * for hangups, errors, and other exceptional conditions.
409  *
410  * @param watch the DBusWatch object.
411  * @returns the conditions to watch.
412  */
413 unsigned int
414 dbus_watch_get_flags (DBusWatch *watch)
415 {
416   _dbus_assert ((watch->flags & VALID_WATCH_FLAGS) == watch->flags);
417
418   return watch->flags;
419 }
420
421 /**
422  * Gets data previously set with dbus_watch_set_data()
423  * or #NULL if none.
424  *
425  * @param watch the DBusWatch object.
426  * @returns previously-set data.
427  */
428 void*
429 dbus_watch_get_data (DBusWatch *watch)
430 {
431   return watch->data;
432 }
433
434 /**
435  * Sets data which can be retrieved with dbus_watch_get_data().
436  * Intended for use by the DBusAddWatchFunction and
437  * DBusRemoveWatchFunction to store their own data.  For example with
438  * Qt you might store the QSocketNotifier for this watch and with GLib
439  * you might store a GSource.
440  *
441  * @param watch the DBusWatch object.
442  * @param data the data.
443  * @param free_data_function function to be called to free the data.
444  */
445 void
446 dbus_watch_set_data (DBusWatch        *watch,
447                      void             *data,
448                      DBusFreeFunction  free_data_function)
449 {
450   if (watch->free_data_function != NULL)
451     (* watch->free_data_function) (watch->data);
452   
453   watch->data = data;
454   watch->free_data_function = free_data_function;
455 }
456
457 /**
458  * Returns whether a watch is enabled or not. If not
459  * enabled, it should not be polled by the main loop.
460  *
461  * @param watch the DBusWatch object
462  * @returns #TRUE if the watch is enabled
463  */
464 dbus_bool_t
465 dbus_watch_get_enabled (DBusWatch *watch)
466 {
467   return watch->enabled;
468 }
469
470 /** @} */