1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-mainloop.c Main loop utility
4 * Copyright (C) 2003 Red Hat, Inc.
6 * Licensed under the Academic Free License version 1.2
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.
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.
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
24 #include "dbus-mainloop.h"
26 #include <dbus/dbus-list.h>
27 #include <dbus/dbus-sysdeps.h>
33 int callback_list_serial;
36 int depth; /**< number of recursive runs */
49 DBusFreeFunction free_data_func;
55 DBusWatchFunction function;
57 /* last watch handle failed due to OOM */
58 unsigned int last_iteration_oom : 1;
65 DBusTimeoutFunction function;
66 unsigned long last_tv_sec;
67 unsigned long last_tv_usec;
70 #define WATCH_CALLBACK(callback) ((WatchCallback*)callback)
71 #define TIMEOUT_CALLBACK(callback) ((TimeoutCallback*)callback)
74 watch_callback_new (DBusWatch *watch,
75 DBusWatchFunction function,
77 DBusFreeFunction free_data_func)
81 cb = dbus_new (WatchCallback, 1);
86 cb->function = function;
87 cb->last_iteration_oom = FALSE;
88 cb->callback.type = CALLBACK_WATCH;
89 cb->callback.data = data;
90 cb->callback.free_data_func = free_data_func;
95 static TimeoutCallback*
96 timeout_callback_new (DBusTimeout *timeout,
97 DBusTimeoutFunction function,
99 DBusFreeFunction free_data_func)
103 cb = dbus_new (TimeoutCallback, 1);
107 cb->timeout = timeout;
108 cb->function = function;
109 _dbus_get_current_time (&cb->last_tv_sec,
111 cb->callback.type = CALLBACK_TIMEOUT;
112 cb->callback.data = data;
113 cb->callback.free_data_func = free_data_func;
119 callback_free (Callback *cb)
121 if (cb->free_data_func)
122 (* cb->free_data_func) (cb->data);
128 add_callback (DBusLoop *loop,
131 if (!_dbus_list_append (&loop->callbacks, cb))
134 loop->callback_list_serial += 1;
139 loop->watch_count += 1;
141 case CALLBACK_TIMEOUT:
142 loop->timeout_count += 1;
150 remove_callback (DBusLoop *loop,
153 Callback *cb = link->data;
158 loop->watch_count -= 1;
160 case CALLBACK_TIMEOUT:
161 loop->timeout_count -= 1;
166 _dbus_list_remove_link (&loop->callbacks, link);
167 loop->callback_list_serial += 1;
171 _dbus_loop_new (void)
175 loop = dbus_new0 (DBusLoop, 1);
185 _dbus_loop_ref (DBusLoop *loop)
187 _dbus_assert (loop != NULL);
188 _dbus_assert (loop->refcount > 0);
194 _dbus_loop_unref (DBusLoop *loop)
196 _dbus_assert (loop != NULL);
197 _dbus_assert (loop->refcount > 0);
200 if (loop->refcount == 0)
208 _dbus_loop_add_watch (DBusLoop *loop,
210 DBusWatchFunction function,
212 DBusFreeFunction free_data_func)
216 wcb = watch_callback_new (watch, function, data, free_data_func);
220 if (!add_callback (loop, (Callback*) wcb))
222 wcb->callback.free_data_func = NULL; /* don't want to have this side effect */
223 callback_free ((Callback*) wcb);
231 _dbus_loop_remove_watch (DBusLoop *loop,
233 DBusWatchFunction function,
238 link = _dbus_list_get_first_link (&loop->callbacks);
241 DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link);
242 Callback *this = link->data;
244 if (this->type == CALLBACK_WATCH &&
245 WATCH_CALLBACK (this)->watch == watch &&
246 this->data == data &&
247 WATCH_CALLBACK (this)->function == function)
249 remove_callback (loop, link);
257 _dbus_warn ("could not find watch %p function %p data %p to remove\n",
258 watch, function, data);
262 _dbus_loop_add_timeout (DBusLoop *loop,
263 DBusTimeout *timeout,
264 DBusTimeoutFunction function,
266 DBusFreeFunction free_data_func)
268 TimeoutCallback *tcb;
270 tcb = timeout_callback_new (timeout, function, data, free_data_func);
274 if (!add_callback (loop, (Callback*) tcb))
276 tcb->callback.free_data_func = NULL; /* don't want to have this side effect */
277 callback_free ((Callback*) tcb);
285 _dbus_loop_remove_timeout (DBusLoop *loop,
286 DBusTimeout *timeout,
287 DBusTimeoutFunction function,
292 link = _dbus_list_get_first_link (&loop->callbacks);
295 DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link);
296 Callback *this = link->data;
298 if (this->type == CALLBACK_TIMEOUT &&
299 TIMEOUT_CALLBACK (this)->timeout == timeout &&
300 this->data == data &&
301 TIMEOUT_CALLBACK (this)->function == function)
303 remove_callback (loop, link);
311 _dbus_warn ("could not find timeout %p function %p data %p to remove\n",
312 timeout, function, data);
315 /* Convolutions from GLib, there really must be a better way
319 check_timeout (unsigned long tv_sec,
320 unsigned long tv_usec,
321 TimeoutCallback *tcb,
326 unsigned long expiration_tv_sec;
327 unsigned long expiration_tv_usec;
328 long interval_seconds;
329 long interval_milliseconds;
332 interval = dbus_timeout_get_interval (tcb->timeout);
334 interval_seconds = interval / 1000;
335 interval_milliseconds = interval - interval_seconds * 1000;
337 expiration_tv_sec = tcb->last_tv_sec + interval_seconds;
338 expiration_tv_usec = tcb->last_tv_usec + interval_milliseconds * 1000;
339 if (expiration_tv_usec >= 1000000)
341 expiration_tv_usec -= 1000000;
342 expiration_tv_sec += 1;
345 sec = expiration_tv_sec - tv_sec;
346 msec = (expiration_tv_usec - tv_usec) / 1000;
349 printf ("Interval is %ld seconds %ld msecs\n",
351 interval_milliseconds);
352 printf ("Now is %lu seconds %lu usecs\n",
354 printf ("Exp is %lu seconds %lu usecs\n",
355 expiration_tv_sec, expiration_tv_usec);
356 printf ("Pre-correction, remaining sec %ld msec %ld\n", sec, msec);
359 /* We do the following in a rather convoluted fashion to deal with
360 * the fact that we don't have an integral type big enough to hold
361 * the difference of two timevals in millseconds.
363 if (sec < 0 || (sec == 0 && msec < 0))
373 if (sec > interval_seconds ||
374 (sec == interval_seconds && msec > interval_milliseconds))
376 _dbus_verbose ("System clock went backward interval_seconds %ld interval_msecs %ld sec %ld msec %ld last_tv_sec %lu last_tv_usec %lu tv_sec %lu tv_usec %lu\n",
377 interval_seconds, interval_milliseconds, sec, msec, tcb->last_tv_sec,
378 tcb->last_tv_usec, tv_sec, tv_usec);
380 /* The system time has been set backwards, reset the timeout */
382 tcb->last_tv_sec = tv_sec;
383 tcb->last_tv_usec = tv_usec;
385 msec = MIN (_DBUS_INT_MAX, interval);
389 msec = MIN (_DBUS_INT_MAX, (unsigned int)msec + 1000 * (unsigned int)sec);
396 printf ("Timeout expires in %d milliseconds\n", *timeout);
402 /* Returns TRUE if we have any timeouts or ready file descriptors,
403 * which is just used in test code as a debug hack
407 _dbus_loop_iterate (DBusLoop *loop,
413 WatchCallback **watches_for_fds;
419 dbus_bool_t oom_watch_pending;
425 watches_for_fds = NULL;
426 oom_watch_pending = FALSE;
427 orig_depth = loop->depth;
430 _dbus_verbose (" iterate %d timeouts %d watches\n",
431 loop->timeout_count, loop->watch_count);
434 if (loop->callbacks == NULL)
436 _dbus_loop_quit (loop);
440 /* count enabled watches */
442 link = _dbus_list_get_first_link (&loop->callbacks);
445 DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link);
446 Callback *cb = link->data;
447 if (cb->type == CALLBACK_WATCH)
449 WatchCallback *wcb = WATCH_CALLBACK (cb);
451 if (!wcb->last_iteration_oom &&
452 dbus_watch_get_enabled (wcb->watch))
459 /* fill our array of fds and watches */
462 fds = dbus_new0 (DBusPollFD, n_fds);
465 _dbus_wait_for_memory ();
466 fds = dbus_new0 (DBusPollFD, n_fds);
469 watches_for_fds = dbus_new (WatchCallback*, n_fds);
470 while (watches_for_fds == NULL)
472 _dbus_wait_for_memory ();
473 watches_for_fds = dbus_new (WatchCallback*, n_fds);
477 link = _dbus_list_get_first_link (&loop->callbacks);
480 DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link);
481 Callback *cb = link->data;
482 if (cb->type == CALLBACK_WATCH)
485 WatchCallback *wcb = WATCH_CALLBACK (cb);
487 if (wcb->last_iteration_oom)
489 /* we skip this one this time, but reenable it next time,
490 * and have a timeout on this iteration
492 wcb->last_iteration_oom = FALSE;
493 oom_watch_pending = TRUE;
495 else if (dbus_watch_get_enabled (wcb->watch))
497 watches_for_fds[i] = wcb;
499 flags = dbus_watch_get_flags (wcb->watch);
501 fds[i].fd = dbus_watch_get_fd (wcb->watch);
502 if (flags & DBUS_WATCH_READABLE)
503 fds[i].events |= _DBUS_POLLIN;
504 if (flags & DBUS_WATCH_WRITABLE)
505 fds[i].events |= _DBUS_POLLOUT;
514 _dbus_assert (i == n_fds);
518 if (loop->timeout_count > 0)
520 unsigned long tv_sec;
521 unsigned long tv_usec;
525 _dbus_get_current_time (&tv_sec, &tv_usec);
527 link = _dbus_list_get_first_link (&loop->callbacks);
530 DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link);
531 Callback *cb = link->data;
533 if (cb->type == CALLBACK_TIMEOUT &&
534 dbus_timeout_get_enabled (TIMEOUT_CALLBACK (cb)->timeout))
536 TimeoutCallback *tcb = TIMEOUT_CALLBACK (cb);
539 check_timeout (tv_sec, tv_usec, tcb, &msecs_remaining);
542 timeout = msecs_remaining;
544 timeout = MIN (msecs_remaining, timeout);
546 _dbus_assert (timeout >= 0);
549 break; /* it's not going to get shorter... */
560 printf ("timeout is 0 as we aren't blocking\n");
564 /* if a watch is OOM, don't wait longer than the OOM
565 * wait to re-enable it
567 if (oom_watch_pending)
568 timeout = MIN (timeout, _dbus_get_oom_wait ());
570 n_ready = _dbus_poll (fds, n_fds, timeout);
572 initial_serial = loop->callback_list_serial;
574 if (loop->timeout_count > 0)
576 unsigned long tv_sec;
577 unsigned long tv_usec;
579 _dbus_get_current_time (&tv_sec, &tv_usec);
581 /* It'd be nice to avoid this O(n) thingy here */
582 link = _dbus_list_get_first_link (&loop->callbacks);
585 DBusList *next = _dbus_list_get_next_link (&loop->callbacks, link);
586 Callback *cb = link->data;
588 if (initial_serial != loop->callback_list_serial)
591 if (loop->depth != orig_depth)
594 if (cb->type == CALLBACK_TIMEOUT &&
595 dbus_timeout_get_enabled (TIMEOUT_CALLBACK (cb)->timeout))
597 TimeoutCallback *tcb = TIMEOUT_CALLBACK (cb);
600 if (check_timeout (tv_sec, tv_usec,
601 tcb, &msecs_remaining))
603 /* Save last callback time and fire this timeout */
604 tcb->last_tv_sec = tv_sec;
605 tcb->last_tv_usec = tv_usec;
608 printf (" invoking timeout\n");
611 (* tcb->function) (tcb->timeout,
625 /* FIXME I think this "restart if we change the watches"
626 * approach could result in starving watches
627 * toward the end of the list.
629 if (initial_serial != loop->callback_list_serial)
632 if (loop->depth != orig_depth)
635 if (fds[i].revents != 0)
638 unsigned int condition;
640 wcb = watches_for_fds[i];
643 if (fds[i].revents & _DBUS_POLLIN)
644 condition |= DBUS_WATCH_READABLE;
645 if (fds[i].revents & _DBUS_POLLOUT)
646 condition |= DBUS_WATCH_WRITABLE;
647 if (fds[i].revents & _DBUS_POLLHUP)
648 condition |= DBUS_WATCH_HANGUP;
649 if (fds[i].revents & _DBUS_POLLERR)
650 condition |= DBUS_WATCH_ERROR;
652 /* condition may still be 0 if we got some
653 * weird POLLFOO thing like POLLWRBAND
656 if (condition != 0 &&
657 dbus_watch_get_enabled (wcb->watch))
659 if (!(* wcb->function) (wcb->watch,
661 ((Callback*)wcb)->data))
662 wcb->last_iteration_oom = TRUE;
674 dbus_free (watches_for_fds);
680 _dbus_loop_run (DBusLoop *loop)
684 _dbus_loop_ref (loop);
686 our_exit_depth = loop->depth;
689 while (loop->depth != our_exit_depth)
690 _dbus_loop_iterate (loop, TRUE);
692 _dbus_loop_unref (loop);
696 _dbus_loop_quit (DBusLoop *loop)
698 _dbus_assert (loop->depth > 0);
704 _dbus_get_oom_wait (void)
706 #ifdef DBUS_BUILD_TESTS
707 /* make tests go fast */
715 _dbus_wait_for_memory (void)
717 _dbus_sleep_milliseconds (_dbus_get_oom_wait ());