* dbus/dbus-threads.c: Add static DBusList *uninitialized_mutex_list and
[platform/upstream/dbus.git] / dbus / dbus-pending-call.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23
24 #include "dbus-internals.h"
25 #include "dbus-connection-internal.h"
26 #include "dbus-pending-call-internal.h"
27 #include "dbus-pending-call.h"
28 #include "dbus-list.h"
29 #include "dbus-threads.h"
30 #include "dbus-test.h"
31
32 /**
33  * @defgroup DBusPendingCallInternals DBusPendingCall implementation details
34  * @ingroup DBusInternals
35  * @brief DBusPendingCall private implementation details.
36  *
37  * The guts of DBusPendingCall and its methods.
38  *
39  * @{
40  */
41
42 /**
43  * @brief Internals of DBusPendingCall
44  *
45  * Opaque object representing a reply message that we're waiting for.
46  */
47 #define CONNECTION_LOCK(connection)   _dbus_connection_lock(connection)
48 #define CONNECTION_UNLOCK(connection) _dbus_connection_unlock(connection)
49
50 struct DBusPendingCall
51 {
52   DBusAtomic refcount;                            /**< reference count */
53
54   DBusDataSlotList slot_list;                     /**< Data stored by allocated integer ID */
55   
56   DBusPendingCallNotifyFunction function;         /**< Notifier when reply arrives. */
57
58   DBusConnection *connection;                     /**< Connections we're associated with */
59   DBusMessage *reply;                             /**< Reply (after we've received it) */
60   DBusTimeout *timeout;                           /**< Timeout */
61
62   DBusList *timeout_link;                         /**< Preallocated timeout response */
63   
64   dbus_uint32_t reply_serial;                     /**< Expected serial of reply */
65
66   unsigned int completed : 1;                     /**< TRUE if completed */
67   unsigned int timeout_added : 1;                 /**< Have added the timeout */
68 };
69
70 static dbus_int32_t notify_user_data_slot = -1;
71
72 /**
73  * Creates a new pending reply object.
74  *
75  * @param connection connection where reply will arrive
76  * @param timeout_milliseconds length of timeout, -1 for default
77  * @param timeout_handler timeout handler, takes pending call as data
78  * @returns a new #DBusPendingCall or #NULL if no memory.
79  */
80 DBusPendingCall*
81 _dbus_pending_call_new_unlocked (DBusConnection    *connection,
82                                  int                timeout_milliseconds,
83                                  DBusTimeoutHandler timeout_handler)
84 {
85   DBusPendingCall *pending;
86   DBusTimeout *timeout;
87
88   _dbus_assert (timeout_milliseconds >= 0 || timeout_milliseconds == -1);
89  
90   if (timeout_milliseconds == -1)
91     timeout_milliseconds = _DBUS_DEFAULT_TIMEOUT_VALUE;
92
93   /* it would probably seem logical to pass in _DBUS_INT_MAX for
94    * infinite timeout, but then math in
95    * _dbus_connection_block_for_reply would get all overflow-prone, so
96    * smack that down.
97    */
98   if (timeout_milliseconds > _DBUS_ONE_HOUR_IN_MILLISECONDS * 6)
99     timeout_milliseconds = _DBUS_ONE_HOUR_IN_MILLISECONDS * 6;
100   
101   if (!dbus_pending_call_allocate_data_slot (&notify_user_data_slot))
102     return NULL;
103   
104   pending = dbus_new0 (DBusPendingCall, 1);
105   
106   if (pending == NULL)
107     {
108       dbus_pending_call_free_data_slot (&notify_user_data_slot);
109       return NULL;
110     }
111
112   timeout = _dbus_timeout_new (timeout_milliseconds,
113                                timeout_handler,
114                                pending, NULL);  
115
116   if (timeout == NULL)
117     {
118       dbus_pending_call_free_data_slot (&notify_user_data_slot);
119       dbus_free (pending);
120       return NULL;
121     }
122   
123   pending->refcount.value = 1;
124   pending->connection = connection;
125   _dbus_connection_ref_unlocked (pending->connection);
126
127   pending->timeout = timeout;
128
129
130   _dbus_data_slot_list_init (&pending->slot_list);
131   
132   return pending;
133 }
134
135 /**
136  * Sets the reply of a pending call with the given message,
137  * or if the message is #NULL, by timing out the pending call.
138  * 
139  * @param pending the pending call
140  * @param message the message to complete the call with, or #NULL
141  *  to time out the call
142  */
143 void
144 _dbus_pending_call_set_reply_unlocked (DBusPendingCall *pending,
145                                        DBusMessage     *message)
146 {
147   if (message == NULL)
148     {
149       message = pending->timeout_link->data;
150       _dbus_list_clear (&pending->timeout_link);
151     }
152   else
153     dbus_message_ref (message);
154
155   _dbus_verbose ("  handing message %p (%s) to pending call serial %u\n",
156                  message,
157                  dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_RETURN ?
158                  "method return" :
159                  dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR ?
160                  "error" : "other type",
161                  pending->reply_serial);
162   
163   _dbus_assert (pending->reply == NULL);
164   _dbus_assert (pending->reply_serial == dbus_message_get_reply_serial (message));
165   pending->reply = message;
166 }
167
168 /**
169  * Calls notifier function for the pending call
170  * and sets the call to completed.
171  *
172  * @param pending the pending call
173  * 
174  */
175 void
176 _dbus_pending_call_complete (DBusPendingCall *pending)
177 {
178   _dbus_assert (!pending->completed);
179   
180   pending->completed = TRUE;
181
182   if (pending->function)
183     {
184       void *user_data;
185       user_data = dbus_pending_call_get_data (pending,
186                                               notify_user_data_slot);
187       
188       (* pending->function) (pending, user_data);
189     }
190 }
191
192 void
193 _dbus_pending_call_queue_timeout_error_unlocked (DBusPendingCall *pending, 
194                                                  DBusConnection  *connection)
195 {
196   _dbus_assert (connection == pending->connection);
197   
198   if (pending->timeout_link)
199     {
200       _dbus_connection_queue_synthesized_message_link (connection,
201                                                        pending->timeout_link);
202       pending->timeout_link = NULL;
203     }
204 }
205
206 /**
207  * Checks to see if a timeout has been added
208  *
209  * @param pending the pending_call
210  * @returns #TRUE if there is a timeout or #FALSE if not
211  */
212 dbus_bool_t 
213 _dbus_pending_call_is_timeout_added_unlocked (DBusPendingCall  *pending)
214 {
215   _dbus_assert (pending != NULL);
216
217   return pending->timeout_added;
218 }
219
220
221 /**
222  * Sets wether the timeout has been added
223  *
224  * @param pending the pending_call
225  * @param is_added whether or not a timeout is added
226  */
227 void
228 _dbus_pending_call_set_timeout_added_unlocked (DBusPendingCall  *pending,
229                                                dbus_bool_t       is_added)
230 {
231   _dbus_assert (pending != NULL);
232
233   pending->timeout_added = is_added;
234 }
235
236
237 /**
238  * Retrives the timeout
239  *
240  * @param pending the pending_call
241  * @returns a timeout object 
242  */
243 DBusTimeout *
244 _dbus_pending_call_get_timeout_unlocked (DBusPendingCall  *pending)
245 {
246   _dbus_assert (pending != NULL);
247
248   return pending->timeout;
249 }
250
251 /**
252  * Gets the reply's serial number
253  *
254  * @param pending the pending_call
255  * @returns a serial number for the reply or 0 
256  */
257 dbus_uint32_t 
258 _dbus_pending_call_get_reply_serial_unlocked (DBusPendingCall  *pending)
259 {
260   _dbus_assert (pending != NULL);
261
262   return pending->reply_serial;
263 }
264
265 /**
266  * Sets the reply's serial number
267  *
268  * @param pending the pending_call
269  * @param serial the serial number 
270  */
271 void
272 _dbus_pending_call_set_reply_serial_unlocked  (DBusPendingCall *pending,
273                                                dbus_uint32_t serial)
274 {
275   _dbus_assert (pending != NULL);
276   _dbus_assert (pending->reply_serial == 0);
277
278   pending->reply_serial = serial;
279 }
280
281 /**
282  * Gets the connection associated with this pending call.
283  *
284  * @param pending the pending_call
285  * @returns the connection associated with the pending call
286  */
287 DBusConnection *
288 _dbus_pending_call_get_connection_and_lock (DBusPendingCall *pending)
289 {
290   _dbus_assert (pending != NULL);
291  
292   CONNECTION_LOCK (pending->connection);
293   return pending->connection;
294 }
295
296 /**
297  * Gets the connection associated with this pending call.
298  *
299  * @param pending the pending_call
300  * @returns the connection associated with the pending call
301  */
302 DBusConnection *
303 _dbus_pending_call_get_connection_unlocked (DBusPendingCall *pending)
304 {
305   _dbus_assert (pending != NULL);
306  
307   return pending->connection;
308 }
309
310 /**
311  * Sets the reply message associated with the pending call to a timeout error
312  *
313  * @param pending the pending_call
314  * @param message the message we are sending the error reply to 
315  * @param serial serial number for the reply
316  * @return #FALSE on OOM
317  */
318 dbus_bool_t
319 _dbus_pending_call_set_timeout_error_unlocked (DBusPendingCall *pending,
320                                                DBusMessage     *message,
321                                                dbus_uint32_t    serial)
322
323   DBusList *reply_link;
324   DBusMessage *reply;
325
326   reply = dbus_message_new_error (message, DBUS_ERROR_NO_REPLY,
327                                   "Did not receive a reply. Possible causes include: "
328                                   "the remote application did not send a reply, "
329                                   "the message bus security policy blocked the reply, "
330                                   "the reply timeout expired, or "
331                                   "the network connection was broken.");
332   if (reply == NULL)
333     return FALSE;
334
335   reply_link = _dbus_list_alloc_link (reply);
336   if (reply_link == NULL)
337     {
338       dbus_message_unref (reply);
339       return FALSE;
340     }
341
342   pending->timeout_link = reply_link;
343
344   _dbus_pending_call_set_reply_serial_unlocked (pending, serial);
345   
346   return TRUE;
347 }
348
349 /** @} */
350
351 /**
352  * @defgroup DBusPendingCall DBusPendingCall
353  * @ingroup  DBus
354  * @brief Pending reply to a method call message
355  *
356  * A DBusPendingCall is an object representing an
357  * expected reply. A #DBusPendingCall can be created
358  * when you send a message that should have a reply.
359  *
360  * @{
361  */
362
363 /**
364  * @typedef DBusPendingCall
365  *
366  * Opaque data type representing a message pending.
367  */
368
369 /**
370  * Increments the reference count on a pending call,
371  * while the lock on its connection is already held.
372  *
373  * @param pending the pending call object
374  * @returns the pending call object
375  */
376 DBusPendingCall *
377 _dbus_pending_call_ref_unlocked (DBusPendingCall *pending)
378 {
379   pending->refcount.value += 1;
380   
381   return pending;
382 }
383
384 /**
385  * Increments the reference count on a pending call.
386  *
387  * @param pending the pending call object
388  * @returns the pending call object
389  */
390 DBusPendingCall *
391 dbus_pending_call_ref (DBusPendingCall *pending)
392 {
393   _dbus_return_val_if_fail (pending != NULL, NULL);
394
395   /* The connection lock is better than the global
396    * lock in the atomic increment fallback
397    */
398 #ifdef DBUS_HAVE_ATOMIC_INT
399   _dbus_atomic_inc (&pending->refcount);
400 #else
401   CONNECTION_LOCK (pending->connection);
402   _dbus_assert (pending->refcount.value > 0);
403
404   pending->refcount.value += 1;
405   CONNECTION_UNLOCK (pending->connection);
406 #endif
407   
408   return pending;
409 }
410
411 static void
412 _dbus_pending_call_last_unref (DBusPendingCall *pending)
413 {
414   DBusConnection *connection;
415   
416   /* If we get here, we should be already detached
417    * from the connection, or never attached.
418    */
419   _dbus_assert (!pending->timeout_added);  
420
421   connection = pending->connection;
422
423   /* this assumes we aren't holding connection lock... */
424   _dbus_data_slot_list_free (&pending->slot_list);
425
426   if (pending->timeout != NULL)
427     _dbus_timeout_unref (pending->timeout);
428       
429   if (pending->timeout_link)
430     {
431       dbus_message_unref ((DBusMessage *)pending->timeout_link->data);
432       _dbus_list_free_link (pending->timeout_link);
433       pending->timeout_link = NULL;
434     }
435
436   if (pending->reply)
437     {
438       dbus_message_unref (pending->reply);
439       pending->reply = NULL;
440     }
441       
442   dbus_free (pending);
443
444   dbus_pending_call_free_data_slot (&notify_user_data_slot);
445
446   /* connection lock should not be held. */
447   /* Free the connection last to avoid a weird state while
448    * calling out to application code where the pending exists
449    * but not the connection.
450    */
451   dbus_connection_unref (connection);
452 }
453
454 /**
455  * Decrements the reference count on a pending call,
456  * freeing it if the count reaches 0. Assumes
457  * connection lock is already held.
458  *
459  * @param pending the pending call object
460  */
461 void
462 _dbus_pending_call_unref_and_unlock (DBusPendingCall *pending)
463 {
464   dbus_bool_t last_unref;
465   
466   _dbus_assert (pending->refcount.value > 0);
467
468   pending->refcount.value -= 1;
469   last_unref = pending->refcount.value == 0;
470
471   CONNECTION_UNLOCK (pending->connection);
472   if (last_unref)
473     _dbus_pending_call_last_unref (pending);
474 }
475
476 /**
477  * Decrements the reference count on a pending call,
478  * freeing it if the count reaches 0.
479  *
480  * @param pending the pending call object
481  */
482 void
483 dbus_pending_call_unref (DBusPendingCall *pending)
484 {
485   dbus_bool_t last_unref;
486
487   _dbus_return_if_fail (pending != NULL);
488
489   /* More efficient to use the connection lock instead of atomic
490    * int fallback if we lack atomic int decrement
491    */
492 #ifdef DBUS_HAVE_ATOMIC_INT
493   last_unref = (_dbus_atomic_dec (&pending->refcount) == 1);
494 #else
495   CONNECTION_LOCK (pending->connection);
496   _dbus_assert (pending->refcount.value > 0);
497   pending->refcount.value -= 1;
498   last_unref = pending->refcount.value == 0;
499   CONNECTION_UNLOCK (pending->connection);
500 #endif
501   
502   if (last_unref)
503     _dbus_pending_call_last_unref(pending);
504 }
505
506 /**
507  * Sets a notification function to be called when the reply is
508  * received or the pending call times out.
509  *
510  * @param pending the pending call
511  * @param function notifier function
512  * @param user_data data to pass to notifier function
513  * @param free_user_data function to free the user data
514  * @returns #FALSE if not enough memory
515  */
516 dbus_bool_t
517 dbus_pending_call_set_notify (DBusPendingCall              *pending,
518                               DBusPendingCallNotifyFunction function,
519                               void                         *user_data,
520                               DBusFreeFunction              free_user_data)
521 {
522   _dbus_return_val_if_fail (pending != NULL, FALSE);
523
524   CONNECTION_LOCK (pending->connection);
525   
526   /* could invoke application code! */
527   if (!_dbus_pending_call_set_data_unlocked (pending, notify_user_data_slot,
528                                              user_data, free_user_data))
529     return FALSE;
530   
531   pending->function = function;
532
533   CONNECTION_UNLOCK (pending->connection);
534   
535   return TRUE;
536 }
537
538 /**
539  * Cancels the pending call, such that any reply or error received
540  * will just be ignored.  Drops the dbus library's internal reference
541  * to the #DBusPendingCall so will free the call if nobody else is
542  * holding a reference. However you usually get a reference
543  * from dbus_connection_send() so probably your app owns a ref also.
544  * 
545  * @param pending the pending call
546  */
547 void
548 dbus_pending_call_cancel (DBusPendingCall *pending)
549 {
550   _dbus_connection_remove_pending_call (pending->connection,
551                                         pending);
552 }
553
554 /**
555  * Checks whether the pending call has received a reply
556  * yet, or not. Assumes connection lock is held.
557  *
558  * @param pending the pending call
559  * @returns #TRUE if a reply has been received
560  */
561 dbus_bool_t
562 _dbus_pending_call_get_completed_unlocked (DBusPendingCall    *pending)
563 {
564   return pending->completed;
565 }
566
567 /**
568  * Checks whether the pending call has received a reply
569  * yet, or not.
570  *
571  * @param pending the pending call
572  * @returns #TRUE if a reply has been received
573  */
574 dbus_bool_t
575 dbus_pending_call_get_completed (DBusPendingCall *pending)
576 {
577   dbus_bool_t completed;
578   
579   CONNECTION_LOCK (pending->connection);
580   completed = pending->completed;
581   CONNECTION_UNLOCK (pending->connection);
582
583   return completed;
584 }
585
586 /**
587  * Gets the reply, or returns #NULL if none has been received
588  * yet. Ownership of the reply message passes to the caller. This
589  * function can only be called once per pending call, since the reply
590  * message is tranferred to the caller.
591  * 
592  * @param pending the pending call
593  * @returns the reply message or #NULL.
594  */
595 DBusMessage*
596 dbus_pending_call_steal_reply (DBusPendingCall *pending)
597 {
598   DBusMessage *message;
599   
600   _dbus_return_val_if_fail (pending->completed, NULL);
601   _dbus_return_val_if_fail (pending->reply != NULL, NULL);
602
603   CONNECTION_LOCK (pending->connection);
604   
605   message = pending->reply;
606   pending->reply = NULL;
607
608   CONNECTION_UNLOCK (pending->connection);
609   
610   return message;
611 }
612
613 /**
614  * Block until the pending call is completed.  The blocking is as with
615  * dbus_connection_send_with_reply_and_block(); it does not enter the
616  * main loop or process other messages, it simply waits for the reply
617  * in question.
618  *
619  * If the pending call is already completed, this function returns
620  * immediately.
621  *
622  * @todo when you start blocking, the timeout is reset, but it should
623  * really only use time remaining since the pending call was created.
624  *
625  * @param pending the pending call
626  */
627 void
628 dbus_pending_call_block (DBusPendingCall *pending)
629 {
630   _dbus_connection_block_pending_call (pending);
631 }
632
633 static DBusDataSlotAllocator slot_allocator;
634 _DBUS_DEFINE_GLOBAL_LOCK (pending_call_slots);
635
636 /**
637  * Allocates an integer ID to be used for storing application-specific
638  * data on any DBusPendingCall. The allocated ID may then be used
639  * with dbus_pending_call_set_data() and dbus_pending_call_get_data().
640  * The passed-in slot must be initialized to -1, and is filled in
641  * with the slot ID. If the passed-in slot is not -1, it's assumed
642  * to be already allocated, and its refcount is incremented.
643  * 
644  * The allocated slot is global, i.e. all DBusPendingCall objects will
645  * have a slot with the given integer ID reserved.
646  *
647  * @param slot_p address of a global variable storing the slot
648  * @returns #FALSE on failure (no memory)
649  */
650 dbus_bool_t
651 dbus_pending_call_allocate_data_slot (dbus_int32_t *slot_p)
652 {
653   return _dbus_data_slot_allocator_alloc (&slot_allocator,
654                                           &_DBUS_LOCK_NAME (pending_call_slots),
655                                           slot_p);
656 }
657
658 /**
659  * Deallocates a global ID for #DBusPendingCall data slots.
660  * dbus_pending_call_get_data() and dbus_pending_call_set_data() may
661  * no longer be used with this slot.  Existing data stored on existing
662  * DBusPendingCall objects will be freed when the #DBusPendingCall is
663  * finalized, but may not be retrieved (and may only be replaced if
664  * someone else reallocates the slot).  When the refcount on the
665  * passed-in slot reaches 0, it is set to -1.
666  *
667  * @param slot_p address storing the slot to deallocate
668  */
669 void
670 dbus_pending_call_free_data_slot (dbus_int32_t *slot_p)
671 {
672   _dbus_return_if_fail (*slot_p >= 0);
673
674   _dbus_data_slot_allocator_free (&slot_allocator, slot_p);
675 }
676
677 /**
678  * Stores a pointer on a #DBusPendingCall, along
679  * with an optional function to be used for freeing
680  * the data when the data is set again, or when
681  * the pending call is finalized. The slot number
682  * must have been allocated with dbus_pending_call_allocate_data_slot().
683  *
684  * @param pending the pending_call
685  * @param slot the slot number
686  * @param data the data to store
687  * @param free_data_func finalizer function for the data
688  * @returns #TRUE if there was enough memory to store the data
689  */
690 dbus_bool_t
691 _dbus_pending_call_set_data_unlocked (DBusPendingCall  *pending,
692                                      dbus_int32_t      slot,
693                                      void             *data,
694                                      DBusFreeFunction  free_data_func)
695 {
696   DBusFreeFunction old_free_func;
697   void *old_data;
698   dbus_bool_t retval;
699
700   retval = _dbus_data_slot_list_set (&slot_allocator,
701                                      &pending->slot_list,
702                                      slot, data, free_data_func,
703                                      &old_free_func, &old_data);
704
705   /* Drop locks to call out to app code */
706   CONNECTION_UNLOCK (pending->connection);
707   
708   if (retval)
709     {
710       if (old_free_func)
711         (* old_free_func) (old_data);
712     }
713
714   CONNECTION_LOCK (pending->connection);
715   
716   return retval;
717 }
718
719 /**
720  * Stores a pointer on a #DBusPendingCall, along
721  * with an optional function to be used for freeing
722  * the data when the data is set again, or when
723  * the pending call is finalized. The slot number
724  * must have been allocated with dbus_pending_call_allocate_data_slot().
725  *
726  * @param pending the pending_call
727  * @param slot the slot number
728  * @param data the data to store
729  * @param free_data_func finalizer function for the data
730  * @returns #TRUE if there was enough memory to store the data
731  */
732 dbus_bool_t
733 dbus_pending_call_set_data (DBusPendingCall  *pending,
734                             dbus_int32_t      slot,
735                             void             *data,
736                             DBusFreeFunction  free_data_func)
737 {
738   dbus_bool_t retval;
739   
740   _dbus_return_val_if_fail (pending != NULL, FALSE);
741   _dbus_return_val_if_fail (slot >= 0, FALSE);
742
743   
744   CONNECTION_LOCK (pending->connection);
745   retval = _dbus_pending_call_set_data_unlocked (pending, slot, data, free_data_func);
746   CONNECTION_UNLOCK (pending->connection);
747   return retval;
748 }
749
750 /**
751  * Retrieves data previously set with dbus_pending_call_set_data().
752  * The slot must still be allocated (must not have been freed).
753  *
754  * @param pending the pending_call
755  * @param slot the slot to get data from
756  * @returns the data, or #NULL if not found
757  */
758 void*
759 dbus_pending_call_get_data (DBusPendingCall   *pending,
760                             dbus_int32_t       slot)
761 {
762   void *res;
763
764   _dbus_return_val_if_fail (pending != NULL, NULL);
765
766   CONNECTION_LOCK (pending->connection);
767   res = _dbus_data_slot_list_get (&slot_allocator,
768                                   &pending->slot_list,
769                                   slot);
770   CONNECTION_UNLOCK (pending->connection);
771
772   return res;
773 }
774
775 /** @} */
776
777 #ifdef DBUS_BUILD_TESTS
778
779 /**
780  * @ingroup DBusPendingCallInternals
781  * Unit test for DBusPendingCall.
782  *
783  * @returns #TRUE on success.
784  */
785 dbus_bool_t
786 _dbus_pending_call_test (const char *test_data_dir)
787 {  
788
789   return TRUE;
790 }
791 #endif /* DBUS_BUILD_TESTS */