2003-08-15 Havoc Pennington <hp@redhat.com>
[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 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-connection-internal.h"
26 #include "dbus-pending-call.h"
27 #include "dbus-list.h"
28 #include "dbus-threads.h"
29 #include "dbus-test.h"
30
31 /**
32  * @defgroup DBusPendingCallInternals DBusPendingCall implementation details
33  * @ingroup DBusInternals
34  * @brief DBusPendingCall private implementation details.
35  *
36  * The guts of DBusPendingCall and its methods.
37  *
38  * @{
39  */
40
41 /**
42  * Creates a new pending reply object.
43  *
44  * @param connection connection where reply will arrive
45  * @param timeout_milliseconds length of timeout, -1 for default
46  * @param timeout_handler timeout handler, takes pending call as data
47  * @returns a new #DBusPendingCall or #NULL if no memory.
48  */
49 DBusPendingCall*
50 _dbus_pending_call_new (DBusConnection    *connection,
51                         int                timeout_milliseconds,
52                         DBusTimeoutHandler timeout_handler)
53 {
54   DBusPendingCall *pending;
55   DBusTimeout *timeout;
56
57   _dbus_return_val_if_fail (timeout_milliseconds >= 0 || timeout_milliseconds == -1, FALSE);
58   
59   if (timeout_milliseconds == -1)
60     timeout_milliseconds = _DBUS_DEFAULT_TIMEOUT_VALUE;
61   
62   pending = dbus_new (DBusPendingCall, 1);
63   
64   if (pending == NULL)
65     return NULL;
66
67   timeout = _dbus_timeout_new (timeout_milliseconds,
68                                timeout_handler,
69                                pending, NULL);  
70
71   if (timeout == NULL)
72     {
73       dbus_free (pending);
74       return NULL;
75     }
76   
77   pending->refcount.value = 1;
78   pending->connection = connection;
79   pending->timeout = timeout;
80   
81   return pending;
82 }
83
84 /**
85  * Calls notifier function for the pending call
86  * and sets the call to completed.
87  *
88  * @param pending the pending call
89  * 
90  */
91 void
92 _dbus_pending_call_notify (DBusPendingCall *pending)
93 {
94   pending->completed = TRUE;
95
96   if (pending->function)
97     (* pending->function) (pending, pending->user_data);
98 }
99
100 /** @} */
101
102 /**
103  * @defgroup DBusPendingCall DBusPendingCall
104  * @ingroup  DBus
105  * @brief Pending reply to a method call message
106  *
107  * A DBusPendingCall is an object representing an
108  * expected reply. A #DBusPendingCall can be created
109  * when you send a message that should have a reply.
110  *
111  * @{
112  */
113
114 /**
115  * @typedef DBusPendingCall
116  *
117  * Opaque data type representing a message pending.
118  */
119
120 /**
121  * Increments the reference count on a pending call.
122  *
123  * @param pending the pending call object
124  */
125 void
126 dbus_pending_call_ref (DBusPendingCall *pending)
127 {
128   _dbus_return_if_fail (pending != NULL);
129
130   _dbus_atomic_inc (&pending->refcount);
131 }
132
133 /**
134  * Decrements the reference count on a pending call,
135  * freeing it if the count reaches 0.
136  *
137  * @param pending the pending call object
138  */
139 void
140 dbus_pending_call_unref (DBusPendingCall *pending)
141 {
142   dbus_bool_t last_unref;
143
144   _dbus_return_if_fail (pending != NULL);
145
146   last_unref = (_dbus_atomic_dec (&pending->refcount) == 1);
147
148   if (last_unref)
149     {
150       /* If we get here, we should be already detached
151        * from the connection, or never attached.
152        */
153       _dbus_assert (pending->connection == NULL);
154       _dbus_assert (!pending->timeout_added);  
155
156       /* this assumes we aren't holding connection lock... */
157       if (pending->free_user_data)
158         (* pending->free_user_data) (pending->user_data);
159
160       if (pending->timeout != NULL)
161         _dbus_timeout_unref (pending->timeout);
162       
163       if (pending->timeout_link)
164         {
165           dbus_message_unref ((DBusMessage *)pending->timeout_link->data);
166           _dbus_list_free_link (pending->timeout_link);
167           pending->timeout_link = NULL;
168         }
169
170       if (pending->reply)
171         {
172           dbus_message_unref (pending->reply);
173           pending->reply = NULL;
174         }
175       
176       dbus_free (pending);
177     }
178 }
179
180 /**
181  * Sets a notification function to be called when the reply is
182  * received or the pending call times out.
183  *
184  * @param pending the pending call
185  * @param function notifier function
186  * @param user_data data to pass to notifier function
187  * @param free_user_data function to free the user data
188  * 
189  */
190 void
191 dbus_pending_call_set_notify (DBusPendingCall              *pending,
192                               DBusPendingCallNotifyFunction function,
193                               void                         *user_data,
194                               DBusFreeFunction              free_user_data)
195 {
196   DBusFreeFunction old_free_func;
197   void *old_user_data;
198
199   _dbus_return_if_fail (pending != NULL);
200
201   old_free_func = pending->free_user_data;
202   old_user_data = pending->user_data;
203
204   pending->user_data = user_data;
205   pending->free_user_data = free_user_data;
206   pending->function = function;
207
208   if (old_free_func)
209     (* old_free_func) (old_user_data);
210 }
211
212 /**
213  * Cancels the pending call, such that any reply
214  * or error received will just be ignored.
215  * Drops at least one reference to the #DBusPendingCall
216  * so will free the call if nobody else is holding
217  * a reference.
218  * 
219  * @param pending the pending call
220  */
221 void
222 dbus_pending_call_cancel (DBusPendingCall *pending)
223 {
224   if (pending->connection)
225     _dbus_connection_remove_pending_call (pending->connection,
226                                           pending);
227 }
228
229 /**
230  * Checks whether the pending call has received a reply
231  * yet, or not.
232  *
233  * @param pending the pending call
234  * @returns #TRUE if a reply has been received
235  */
236 dbus_bool_t
237 dbus_pending_call_get_completed (DBusPendingCall *pending)
238 {
239   return pending->completed;
240 }
241
242 /**
243  * Gets the reply, or returns #NULL if none has been received yet. The
244  * reference count is not incremented on the returned message, so you
245  * have to keep a reference count on the pending call (or add one
246  * to the message).
247  *
248  * @param pending the pending call
249  * @returns the reply message or #NULL.
250  */
251 DBusMessage*
252 dbus_pending_call_get_reply (DBusPendingCall *pending)
253 {
254   return pending->reply;
255 }
256
257 /**
258  * Block until the pending call is completed.  The blocking is as with
259  * dbus_connection_send_with_reply_and_block(); it does not enter the
260  * main loop or process other messages, it simply waits for the reply
261  * in question.
262  *
263  * @todo when you start blocking, the timeout is reset, but it should
264  * really only use time remaining since the pending call was created.
265  *
266  * @param pending the pending call
267  */
268 void
269 dbus_pending_call_block (DBusPendingCall *pending)
270 {
271   DBusMessage *message;
272   
273   message = _dbus_connection_block_for_reply (pending->connection,
274                                               pending->reply_serial,
275                                               dbus_timeout_get_interval (pending->timeout));
276
277   _dbus_connection_lock (pending->connection);
278   _dbus_pending_call_complete_and_unlock (pending, message);
279   dbus_message_unref (message);
280 }
281
282 /** @} */
283
284 #ifdef DBUS_BUILD_TESTS
285
286 /**
287  * @ingroup DBusPendingCallInternals
288  * Unit test for DBusPendingCall.
289  *
290  * @returns #TRUE on success.
291  */
292 dbus_bool_t
293 _dbus_pending_call_test (const char *test_data_dir)
294 {  
295
296   return TRUE;
297 }
298 #endif /* DBUS_BUILD_TESTS */