2003-01-05 Havoc Pennington <hp@pobox.com>
[platform/upstream/dbus.git] / bus / loop.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* loop.c  Main loop for daemon
3  *
4  * Copyright (C) 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 "loop.h"
25 #include <dbus/dbus-list.h>
26 #include <dbus/dbus-sysdeps.h>
27
28 static DBusList *watches = NULL;
29 static int watch_list_serial = 0;
30 static dbus_bool_t exited = FALSE;
31
32 typedef struct
33 {
34   DBusWatch *watch;
35   BusWatchFunction function;
36   void *data;
37   DBusFreeFunction free_data_func;
38 } WatchCallback;
39
40 dbus_bool_t
41 bus_loop_add_watch (DBusWatch        *watch,
42                     BusWatchFunction  function,
43                     void             *data,
44                     DBusFreeFunction  free_data_func)
45 {
46   WatchCallback *cb;
47
48   cb = dbus_new (WatchCallback, 1);
49   if (cb == NULL)
50     return FALSE;
51
52   cb->watch = watch;
53   cb->function = function;
54   cb->data = data;
55   cb->free_data_func = free_data_func;
56
57   if (!_dbus_list_append (&watches, cb))
58     {
59       dbus_free (cb);
60       return FALSE;
61     }
62
63   watch_list_serial += 1;
64   
65   return TRUE;
66 }
67
68 void
69 bus_loop_remove_watch (DBusWatch        *watch,
70                        BusWatchFunction  function,
71                        void             *data)
72 {
73   DBusList *link;
74   
75   link = _dbus_list_get_first_link (&watches);
76   while (link != NULL)
77     {
78       DBusList *next = _dbus_list_get_next_link (&watches, link);
79       WatchCallback *cb = link->data;
80
81       if (cb->watch == watch &&
82           cb->function == function &&
83           cb->data == data)
84         {
85           _dbus_list_remove_link (&watches, link);
86
87           watch_list_serial += 1;
88           
89           if (cb->free_data_func)
90             (* cb->free_data_func) (cb->data);
91           dbus_free (cb);
92           
93           return;
94         }
95       
96       link = next;
97     }
98
99   _dbus_warn ("could not find watch %p function %p data %p to remove\n",
100               watch, function, data);
101 }
102
103 static void
104 wait_for_memory (void)
105 {
106   _dbus_sleep_milliseconds (500);
107 }
108
109 void
110 bus_loop_run (void)
111 {
112   while (!exited)
113     {
114       DBusPollFD *fds;
115       int n_fds;
116       WatchCallback **watches_for_fds;
117       int i;
118       DBusList *link;
119       int n_ready;
120       int initial_serial;
121       
122       fds = NULL;
123       watches_for_fds = NULL;
124       
125       n_fds = _dbus_list_get_length (&watches);
126
127       if (n_fds == 0)
128         {
129           bus_loop_quit ();
130           goto next_iteration;
131         }
132       
133       fds = dbus_new0 (DBusPollFD, n_fds);
134       while (fds == NULL)
135         {
136           wait_for_memory ();
137           fds = dbus_new0 (DBusPollFD, n_fds);
138         }
139
140       watches_for_fds = dbus_new (WatchCallback*, n_fds);
141       while (watches_for_fds == NULL)
142         {
143           wait_for_memory ();
144           watches_for_fds = dbus_new (WatchCallback*, n_fds);
145         }
146       
147       i = 0;
148       link = _dbus_list_get_first_link (&watches);
149       while (link != NULL)
150         {
151           DBusList *next = _dbus_list_get_next_link (&watches, link);
152           WatchCallback *cb = link->data;
153           int flags;
154           
155           watches_for_fds[i] = cb;
156
157           flags = dbus_watch_get_flags (cb->watch);
158           
159           fds[i].fd = dbus_watch_get_fd (cb->watch);
160           if (flags & DBUS_WATCH_READABLE)
161             fds[i].events |= _DBUS_POLLIN;
162           if (flags & DBUS_WATCH_WRITABLE)
163             fds[i].events |= _DBUS_POLLOUT;
164           
165           link = next;
166           ++i;
167         }
168
169       n_ready = _dbus_poll (fds, n_fds, -1);
170
171       if (n_ready > 0)
172         {
173           initial_serial = watch_list_serial;
174           i = 0;
175           while (i < n_fds)
176             {
177               /* FIXME I think this "restart if we change the watches"
178                * approach could result in starving watches
179                * toward the end of the list.
180                */
181               if (initial_serial != watch_list_serial)
182                 goto next_iteration;
183
184               if (exited)
185                 goto next_iteration;
186
187               if (fds[i].revents != 0)
188                 {
189                   WatchCallback *cb;
190                   unsigned int condition;
191                   
192                   cb = watches_for_fds[i];
193                   
194                   condition = 0;
195                   if (fds[i].revents & _DBUS_POLLIN)
196                     condition |= DBUS_WATCH_READABLE;
197                   if (fds[i].revents & _DBUS_POLLOUT)
198                     condition |= DBUS_WATCH_WRITABLE;
199                   if (fds[i].revents & _DBUS_POLLHUP)
200                     condition |= DBUS_WATCH_HANGUP;
201                   if (fds[i].revents & _DBUS_POLLERR)
202                     condition |= DBUS_WATCH_ERROR;
203
204                   /* condition may still be 0 if we got some
205                    * weird POLLFOO thing like POLLWRBAND
206                    */
207                   
208                   if (condition != 0)
209                     (* cb->function) (cb->watch,
210                                       condition,
211                                       cb->data);
212                 }
213               
214               ++i;
215             }
216         }
217       
218     next_iteration:
219       dbus_free (fds);
220       dbus_free (watches_for_fds);
221     }
222 }
223
224 void
225 bus_loop_quit (void)
226 {
227   exited = TRUE;
228 }