D-Bus  1.4.10
dbus-pending-call.c
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /* dbus-pending-call.c Object representing a call in progress.
3  *
4  * Copyright (C) 2002, 2003 Red Hat Inc.
5  *
6  * Licensed under the Academic Free License version 2.1
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  *
22  */
23 
24 #include <config.h>
25 #include "dbus-internals.h"
26 #include "dbus-connection-internal.h"
27 #include "dbus-pending-call-internal.h"
28 #include "dbus-pending-call.h"
29 #include "dbus-list.h"
30 #include "dbus-threads.h"
31 #include "dbus-test.h"
32 
52 #define CONNECTION_LOCK(connection) _dbus_connection_lock(connection)
53 
56 #define CONNECTION_UNLOCK(connection) _dbus_connection_unlock(connection)
57 
62 {
77  unsigned int completed : 1;
78  unsigned int timeout_added : 1;
79 };
80 
81 static dbus_int32_t notify_user_data_slot = -1;
82 
93  int timeout_milliseconds,
94  DBusTimeoutHandler timeout_handler)
95 {
96  DBusPendingCall *pending;
97  DBusTimeout *timeout;
98 
99  _dbus_assert (timeout_milliseconds >= 0 || timeout_milliseconds == -1);
100 
101  if (timeout_milliseconds == -1)
102  timeout_milliseconds = _DBUS_DEFAULT_TIMEOUT_VALUE;
103 
104  if (!dbus_pending_call_allocate_data_slot (&notify_user_data_slot))
105  return NULL;
106 
107  pending = dbus_new0 (DBusPendingCall, 1);
108 
109  if (pending == NULL)
110  {
111  dbus_pending_call_free_data_slot (&notify_user_data_slot);
112  return NULL;
113  }
114 
115  if (timeout_milliseconds != _DBUS_INT_MAX)
116  {
117  timeout = _dbus_timeout_new (timeout_milliseconds,
118  timeout_handler,
119  pending, NULL);
120 
121  if (timeout == NULL)
122  {
123  dbus_pending_call_free_data_slot (&notify_user_data_slot);
124  dbus_free (pending);
125  return NULL;
126  }
127 
128  pending->timeout = timeout;
129  }
130  else
131  {
132  pending->timeout = NULL;
133  }
134 
135  pending->refcount.value = 1;
136  pending->connection = connection;
138 
140 
141  return pending;
142 }
143 
152 void
154  DBusMessage *message)
155 {
156  if (message == NULL)
157  {
158  message = pending->timeout_link->data;
159  _dbus_list_clear (&pending->timeout_link);
160  }
161  else
162  dbus_message_ref (message);
163 
164  _dbus_verbose (" handing message %p (%s) to pending call serial %u\n",
165  message,
167  "method return" :
169  "error" : "other type",
170  pending->reply_serial);
171 
172  _dbus_assert (pending->reply == NULL);
174  pending->reply = message;
175 }
176 
184 void
186 {
187  _dbus_assert (!pending->completed);
188 
189  pending->completed = TRUE;
190 
191  if (pending->function)
192  {
193  void *user_data;
194  user_data = dbus_pending_call_get_data (pending,
195  notify_user_data_slot);
196 
197  (* pending->function) (pending, user_data);
198  }
199 }
200 
208 void
210  DBusConnection *connection)
211 {
212  _dbus_assert (connection == pending->connection);
213 
214  if (pending->timeout_link)
215  {
217  pending->timeout_link);
218  pending->timeout_link = NULL;
219  }
220 }
221 
230 {
231  _dbus_assert (pending != NULL);
232 
233  return pending->timeout_added;
234 }
235 
236 
243 void
245  dbus_bool_t is_added)
246 {
247  _dbus_assert (pending != NULL);
248 
249  pending->timeout_added = is_added;
250 }
251 
252 
259 DBusTimeout *
261 {
262  _dbus_assert (pending != NULL);
263 
264  return pending->timeout;
265 }
266 
275 {
276  _dbus_assert (pending != NULL);
277 
278  return pending->reply_serial;
279 }
280 
287 void
289  dbus_uint32_t serial)
290 {
291  _dbus_assert (pending != NULL);
292  _dbus_assert (pending->reply_serial == 0);
293 
294  pending->reply_serial = serial;
295 }
296 
305 {
306  _dbus_assert (pending != NULL);
307 
308  CONNECTION_LOCK (pending->connection);
309  return pending->connection;
310 }
311 
320 {
321  _dbus_assert (pending != NULL);
322 
323  return pending->connection;
324 }
325 
336  DBusMessage *message,
337  dbus_uint32_t serial)
338 {
339  DBusList *reply_link;
340  DBusMessage *reply;
341 
343  "Did not receive a reply. Possible causes include: "
344  "the remote application did not send a reply, "
345  "the message bus security policy blocked the reply, "
346  "the reply timeout expired, or "
347  "the network connection was broken.");
348  if (reply == NULL)
349  return FALSE;
350 
351  reply_link = _dbus_list_alloc_link (reply);
352  if (reply_link == NULL)
353  {
354  dbus_message_unref (reply);
355  return FALSE;
356  }
357 
358  pending->timeout_link = reply_link;
359 
361 
362  return TRUE;
363 }
364 
374 {
375  pending->refcount.value += 1;
376 
377  return pending;
378 }
379 
380 
381 static void
382 _dbus_pending_call_last_unref (DBusPendingCall *pending)
383 {
384  DBusConnection *connection;
385 
386  /* If we get here, we should be already detached
387  * from the connection, or never attached.
388  */
389  _dbus_assert (!pending->timeout_added);
390 
391  connection = pending->connection;
392 
393  /* this assumes we aren't holding connection lock... */
395 
396  if (pending->timeout != NULL)
397  _dbus_timeout_unref (pending->timeout);
398 
399  if (pending->timeout_link)
400  {
403  pending->timeout_link = NULL;
404  }
405 
406  if (pending->reply)
407  {
408  dbus_message_unref (pending->reply);
409  pending->reply = NULL;
410  }
411 
412  dbus_free (pending);
413 
414  dbus_pending_call_free_data_slot (&notify_user_data_slot);
415 
416  /* connection lock should not be held. */
417  /* Free the connection last to avoid a weird state while
418  * calling out to application code where the pending exists
419  * but not the connection.
420  */
421  dbus_connection_unref (connection);
422 }
423 
431 void
433 {
434  dbus_bool_t last_unref;
435 
436  _dbus_assert (pending->refcount.value > 0);
437 
438  pending->refcount.value -= 1;
439  last_unref = pending->refcount.value == 0;
440 
441  CONNECTION_UNLOCK (pending->connection);
442  if (last_unref)
443  _dbus_pending_call_last_unref (pending);
444 }
445 
455 {
456  return pending->completed;
457 }
458 
459 static DBusDataSlotAllocator slot_allocator;
460 _DBUS_DEFINE_GLOBAL_LOCK (pending_call_slots);
461 
477  dbus_int32_t slot,
478  void *data,
479  DBusFreeFunction free_data_func)
480 {
481  DBusFreeFunction old_free_func;
482  void *old_data;
483  dbus_bool_t retval;
484 
485  retval = _dbus_data_slot_list_set (&slot_allocator,
486  &pending->slot_list,
487  slot, data, free_data_func,
488  &old_free_func, &old_data);
489 
490  /* Drop locks to call out to app code */
491  CONNECTION_UNLOCK (pending->connection);
492 
493  if (retval)
494  {
495  if (old_free_func)
496  (* old_free_func) (old_data);
497  }
498 
499  CONNECTION_LOCK (pending->connection);
500 
501  return retval;
502 }
503 
532 {
533  _dbus_return_val_if_fail (pending != NULL, NULL);
534 
535  /* The connection lock is better than the global
536  * lock in the atomic increment fallback
537  */
538 #ifdef DBUS_HAVE_ATOMIC_INT
539  _dbus_atomic_inc (&pending->refcount);
540 #else
541  CONNECTION_LOCK (pending->connection);
542  _dbus_assert (pending->refcount.value > 0);
543 
544  pending->refcount.value += 1;
545  CONNECTION_UNLOCK (pending->connection);
546 #endif
547 
548  return pending;
549 }
550 
557 void
559 {
560  dbus_bool_t last_unref;
561 
562  _dbus_return_if_fail (pending != NULL);
563 
564  /* More efficient to use the connection lock instead of atomic
565  * int fallback if we lack atomic int decrement
566  */
567 #ifdef DBUS_HAVE_ATOMIC_INT
568  last_unref = (_dbus_atomic_dec (&pending->refcount) == 1);
569 #else
570  CONNECTION_LOCK (pending->connection);
571  _dbus_assert (pending->refcount.value > 0);
572  pending->refcount.value -= 1;
573  last_unref = pending->refcount.value == 0;
574  CONNECTION_UNLOCK (pending->connection);
575 #endif
576 
577  if (last_unref)
578  _dbus_pending_call_last_unref(pending);
579 }
580 
594  void *user_data,
595  DBusFreeFunction free_user_data)
596 {
597  _dbus_return_val_if_fail (pending != NULL, FALSE);
598 
599  CONNECTION_LOCK (pending->connection);
600 
601  /* could invoke application code! */
602  if (!_dbus_pending_call_set_data_unlocked (pending, notify_user_data_slot,
603  user_data, free_user_data))
604  return FALSE;
605 
606  pending->function = function;
607 
608  CONNECTION_UNLOCK (pending->connection);
609 
610  return TRUE;
611 }
612 
628 void
630 {
631  _dbus_return_if_fail (pending != NULL);
632 
634  pending);
635 }
636 
646 {
647  dbus_bool_t completed;
648 
649  _dbus_return_val_if_fail (pending != NULL, FALSE);
650 
651  CONNECTION_LOCK (pending->connection);
652  completed = pending->completed;
653  CONNECTION_UNLOCK (pending->connection);
654 
655  return completed;
656 }
657 
669 {
670  DBusMessage *message;
671 
672  _dbus_return_val_if_fail (pending != NULL, NULL);
673  _dbus_return_val_if_fail (pending->completed, NULL);
674  _dbus_return_val_if_fail (pending->reply != NULL, NULL);
675 
676  CONNECTION_LOCK (pending->connection);
677 
678  message = pending->reply;
679  pending->reply = NULL;
680 
681  CONNECTION_UNLOCK (pending->connection);
682 
683  return message;
684 }
685 
701 void
703 {
704  _dbus_return_if_fail (pending != NULL);
705 
707 }
708 
725 {
726  _dbus_return_val_if_fail (slot_p != NULL, FALSE);
727 
728  return _dbus_data_slot_allocator_alloc (&slot_allocator,
729  &_DBUS_LOCK_NAME (pending_call_slots),
730  slot_p);
731 }
732 
744 void
746 {
747  _dbus_return_if_fail (slot_p != NULL);
748  _dbus_return_if_fail (*slot_p >= 0);
749 
750  _dbus_data_slot_allocator_free (&slot_allocator, slot_p);
751 }
752 
768  dbus_int32_t slot,
769  void *data,
770  DBusFreeFunction free_data_func)
771 {
772  dbus_bool_t retval;
773 
774  _dbus_return_val_if_fail (pending != NULL, FALSE);
775  _dbus_return_val_if_fail (slot >= 0, FALSE);
776 
777 
778  CONNECTION_LOCK (pending->connection);
779  retval = _dbus_pending_call_set_data_unlocked (pending, slot, data, free_data_func);
780  CONNECTION_UNLOCK (pending->connection);
781  return retval;
782 }
783 
792 void*
794  dbus_int32_t slot)
795 {
796  void *res;
797 
798  _dbus_return_val_if_fail (pending != NULL, NULL);
799 
800  CONNECTION_LOCK (pending->connection);
801  res = _dbus_data_slot_list_get (&slot_allocator,
802  &pending->slot_list,
803  slot);
804  CONNECTION_UNLOCK (pending->connection);
805 
806  return res;
807 }
808 
811 #ifdef DBUS_BUILD_TESTS
812 
820 _dbus_pending_call_test (const char *test_data_dir)
821 {
822 
823  return TRUE;
824 }
825 #endif /* DBUS_BUILD_TESTS */