2003-08-15 Havoc Pennington <hp@redhat.com>
[platform/upstream/dbus.git] / dbus / dbus-object-registry.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-object-registry.c  DBusObjectRegistry (internals of DBusConnection)
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 #include "dbus-object-registry.h"
24 #include "dbus-connection-internal.h"
25 #include "dbus-internals.h"
26 #include "dbus-hash.h"
27 #include "dbus-protocol.h"
28 #include <string.h>
29
30 /**
31  * @defgroup DBusObjectRegistry Map object IDs to implementations
32  * @ingroup  DBusInternals
33  * @brief DBusObjectRegistry is used by DBusConnection to track object IDs
34  *
35  * Types and functions related to DBusObjectRegistry. These
36  * are all internal.
37  *
38  * @todo interface entries and signal connections are handled pretty
39  * much identically, with lots of duplicate code.  Once we're sure
40  * they will always be the same, we could merge this code.
41  *
42  * @{
43  */
44
45 typedef struct DBusObjectEntry DBusObjectEntry;
46 typedef struct DBusInterfaceEntry DBusInterfaceEntry;
47 typedef struct DBusSignalEntry DBusSignalEntry;
48
49 #define DBUS_MAX_OBJECTS_PER_INTERFACE 65535
50 struct DBusInterfaceEntry
51 {
52   unsigned int n_objects : 16;   /**< Number of objects with this interface */
53   unsigned int n_allocated : 16; /**< Allocated size of objects array */
54   dbus_uint16_t *objects;        /**< Index of each object with the interface */
55   char name[4];                  /**< Name of interface (actually allocated larger) */
56 };
57
58 #define DBUS_MAX_CONNECTIONS_PER_SIGNAL 65535
59 struct DBusSignalEntry
60 {
61   unsigned int n_connections : 16; /**< Number of connections to this signal */
62   unsigned int n_allocated : 16;   /**< Allocated size of objects array */
63   dbus_uint16_t *connections;      /**< Index of each object connected (can have dups for multiple
64                                     * connections)
65                                     */
66   char name[4];                    /**< Name of signal (actually allocated larger) */
67 };
68
69  /* 14 bits for object index, 32K objects */
70 #define DBUS_OBJECT_INDEX_BITS          (14)
71 #define DBUS_OBJECT_INDEX_MASK          (0x3fff)
72 #define DBUS_MAX_OBJECTS_PER_CONNECTION DBUS_OBJECT_INDEX_MASK
73 struct DBusObjectEntry
74 {
75   unsigned int id_index      : 14; /**< Index of this entry in the entries array */
76   unsigned int id_times_used : 18; /**< Count of times entry has been used; avoids recycling IDs too often */
77
78   void *object_impl;               /**< Pointer to application-supplied implementation */
79   const DBusObjectVTable *vtable;  /**< Virtual table for this object */
80   DBusInterfaceEntry **interfaces; /**< NULL-terminated list of interfaces */
81   DBusSignalEntry    **signals;    /**< Signal connections (contains dups, one each time we connect) */
82 };
83
84 struct DBusObjectRegistry
85 {
86   int refcount;
87   DBusConnection *connection;
88
89   DBusObjectEntry *entries;
90   int n_entries_allocated;
91   int n_entries_used;
92
93   DBusHashTable *interface_table;
94
95   DBusHashTable *signal_table;
96 };
97
98 static void
99 free_interface_entry (void *entry)
100 {
101   DBusInterfaceEntry *iface = entry;
102
103   if (iface == NULL) /* DBusHashTable stupidity */
104     return;
105   
106   dbus_free (iface->objects);
107   dbus_free (iface);
108 }
109
110 static void
111 free_signal_entry (void *entry)
112 {
113   DBusSignalEntry *signal = entry;
114
115   if (signal == NULL) /* DBusHashTable stupidity */
116     return;
117   
118   dbus_free (signal->connections);
119   dbus_free (signal);
120 }
121
122 DBusObjectRegistry*
123 _dbus_object_registry_new (DBusConnection *connection)
124 {
125   DBusObjectRegistry *registry;
126   DBusHashTable *interface_table;
127   DBusHashTable *signal_table;
128   
129   /* the connection passed in here isn't fully constructed,
130    * so don't do anything more than store a pointer to
131    * it
132    */
133
134   registry = NULL;
135   interface_table = NULL;
136   signal_table = NULL;
137   
138   registry = dbus_new0 (DBusObjectRegistry, 1);
139   if (registry == NULL)
140     goto oom;
141
142   interface_table = _dbus_hash_table_new (DBUS_HASH_STRING,
143                                           NULL, free_interface_entry);
144   if (interface_table == NULL)
145     goto oom;
146
147   signal_table = _dbus_hash_table_new (DBUS_HASH_STRING,
148                                           NULL, free_signal_entry);
149   if (signal_table == NULL)
150     goto oom;
151   
152   registry->refcount = 1;
153   registry->connection = connection;
154   registry->interface_table = interface_table;
155   registry->signal_table = signal_table;
156   
157   return registry;
158
159  oom:
160   if (registry)
161     dbus_free (registry);
162   if (interface_table)
163     _dbus_hash_table_unref (interface_table);
164   if (signal_table)
165     _dbus_hash_table_unref (signal_table);
166   
167   return NULL;
168 }
169
170 void
171 _dbus_object_registry_ref (DBusObjectRegistry *registry)
172 {
173   _dbus_assert (registry->refcount > 0);
174
175   registry->refcount += 1;
176 }
177
178 void
179 _dbus_object_registry_unref (DBusObjectRegistry *registry)
180 {
181   _dbus_assert (registry->refcount > 0);
182
183   registry->refcount -= 1;
184
185   if (registry->refcount == 0)
186     {
187       int i;
188       
189       _dbus_assert (registry->n_entries_used == 0);
190       _dbus_assert (_dbus_hash_table_get_n_entries (registry->interface_table) == 0);
191       _dbus_assert (_dbus_hash_table_get_n_entries (registry->signal_table) == 0);
192
193       i = 0;
194       while (i < registry->n_entries_allocated)
195         {
196           if (registry->entries[i].interfaces)
197             dbus_free (registry->entries[i].interfaces);
198           if (registry->entries[i].signals)
199             dbus_free (registry->entries[i].signals);
200           ++i;
201         }
202       
203       _dbus_hash_table_unref (registry->interface_table);
204       _dbus_hash_table_unref (registry->signal_table);
205       dbus_free (registry->entries);
206       dbus_free (registry);
207     }
208 }
209
210 #define ENTRY_TO_ID(entry)                                              \
211   (((dbus_uint32_t) (entry)->id_index) |                                \
212    (((dbus_uint32_t)(entry)->id_times_used) << DBUS_OBJECT_INDEX_BITS))
213
214 #define ID_TO_INDEX(id) \
215   (((dbus_uint32_t) (id)) & DBUS_OBJECT_INDEX_MASK)
216
217 #define ID_TO_TIMES_USED(id) \
218   (((dbus_uint32_t) (id)) >> DBUS_OBJECT_INDEX_BITS)
219
220 static DBusObjectEntry*
221 validate_id (DBusObjectRegistry *registry,
222              const DBusObjectID *object_id)
223 {
224   int idx;
225   int times_used;
226   dbus_uint32_t instance_bits;
227   
228   instance_bits = dbus_object_id_get_instance_bits (object_id);
229
230   /* Verify that connection ID bits are the same */
231 #ifdef DBUS_BUILD_TESTS
232   if (registry->connection)
233 #endif
234     {
235       DBusObjectID tmp_id;
236       
237       _dbus_connection_init_id (registry->connection,
238                                 &tmp_id);
239       dbus_object_id_set_instance_bits (&tmp_id, instance_bits);
240       
241       if (!dbus_object_id_equal (&tmp_id, object_id))
242         return NULL;
243     }
244   
245   idx = ID_TO_INDEX (instance_bits);
246   times_used = ID_TO_TIMES_USED (instance_bits);
247   
248   if (idx >= registry->n_entries_allocated)
249     return NULL;
250   if (registry->entries[idx].vtable == NULL)
251     return NULL;
252   if (registry->entries[idx].id_times_used != times_used)
253     return NULL;
254   _dbus_assert (registry->entries[idx].id_index == idx);
255   _dbus_assert (registry->n_entries_used > 0);
256
257   return &registry->entries[idx];
258 }
259
260 static void
261 id_from_entry (DBusObjectRegistry *registry,
262                DBusObjectID       *object_id,
263                DBusObjectEntry    *entry)
264 {
265 #ifdef DBUS_BUILD_TESTS
266   if (registry->connection)
267 #endif
268     _dbus_connection_init_id (registry->connection,
269                               object_id);
270 #ifdef DBUS_BUILD_TESTS
271   else
272     {
273       dbus_object_id_set_server_bits (object_id, 1);
274       dbus_object_id_set_client_bits (object_id, 2);
275     }
276 #endif
277
278   _dbus_assert (dbus_object_id_get_server_bits (object_id) != 0);
279   _dbus_assert (dbus_object_id_get_client_bits (object_id) != 0);
280   
281   dbus_object_id_set_instance_bits (object_id,
282                                     ENTRY_TO_ID (entry));
283
284   _dbus_assert (dbus_object_id_get_instance_bits (object_id) != 0);
285 }
286
287 static void
288 info_from_entry (DBusObjectRegistry *registry,
289                  DBusObjectInfo     *info,
290                  DBusObjectEntry    *entry)
291 {
292   info->connection = registry->connection;
293   info->object_impl = entry->object_impl;
294
295   id_from_entry (registry, &info->object_id, entry);
296 }
297
298 static DBusInterfaceEntry*
299 lookup_interface (DBusObjectRegistry *registry,
300                   const char         *name,
301                   dbus_bool_t         create_if_not_found)
302 {
303   DBusInterfaceEntry *entry;
304   int sz;
305   int len;
306   
307   entry = _dbus_hash_table_lookup_string (registry->interface_table,
308                                           name);
309   if (entry != NULL || !create_if_not_found)
310     return entry;
311   
312   _dbus_assert (create_if_not_found);
313
314   len = strlen (name);
315   sz = _DBUS_STRUCT_OFFSET (DBusInterfaceEntry, name) + len + 1;
316   entry = dbus_malloc (sz);
317   if (entry == NULL)
318     return NULL;
319   entry->n_objects = 0;
320   entry->n_allocated = 0;
321   entry->objects = NULL;
322   memcpy (entry->name, name, len + 1);
323
324   if (!_dbus_hash_table_insert_string (registry->interface_table,
325                                        entry->name, entry))
326     {
327       dbus_free (entry);
328       return NULL;
329     }
330   
331   return entry;
332 }
333
334 static void
335 delete_interface (DBusObjectRegistry *registry,
336                   DBusInterfaceEntry *entry)
337 {
338   _dbus_hash_table_remove_string (registry->interface_table,
339                                   entry->name);
340 }
341
342 static dbus_bool_t
343 interface_entry_add_object (DBusInterfaceEntry *entry,
344                             dbus_uint16_t       object_index)
345 {
346   if (entry->n_objects == entry->n_allocated)
347     {
348       unsigned int new_alloc;
349       dbus_uint16_t *new_objects;
350       
351       if (entry->n_allocated == 0)
352         new_alloc = 2;
353       else
354         new_alloc = entry->n_allocated * 2;
355
356       /* Right now MAX_OBJECTS_PER_INTERFACE can't possibly be reached
357        * since the max number of objects _total_ is smaller, but the
358        * code is here for future robustness.
359        */
360       
361       if (new_alloc > DBUS_MAX_OBJECTS_PER_INTERFACE)
362         new_alloc = DBUS_MAX_OBJECTS_PER_INTERFACE;
363       if (new_alloc == entry->n_allocated)
364         {
365           _dbus_warn ("Attempting to register another instance with interface %s, but max count %d reached\n",
366                       entry->name, DBUS_MAX_OBJECTS_PER_INTERFACE);
367           return FALSE;
368         }
369
370       new_objects = dbus_realloc (entry->objects, new_alloc * sizeof (dbus_uint16_t));
371       if (new_objects == NULL)
372         return FALSE;
373       entry->objects = new_objects;
374       entry->n_allocated = new_alloc;
375     }
376
377   _dbus_assert (entry->n_objects < entry->n_allocated);
378
379   entry->objects[entry->n_objects] = object_index;
380   entry->n_objects += 1;
381
382   return TRUE;
383 }
384
385 static void
386 interface_entry_remove_object (DBusInterfaceEntry *entry,
387                                dbus_uint16_t       object_index)
388 {
389   unsigned int i;
390
391   i = 0;
392   while (i < entry->n_objects)
393     {
394       if (entry->objects[i] == object_index)
395         break;
396       ++i;
397     }
398
399   if (i == entry->n_objects)
400     {
401       _dbus_assert_not_reached ("Tried to remove object from an interface that didn't list that object\n");
402       return;
403     }
404
405   memmove (&entry->objects[i],
406            &entry->objects[i+1],
407            (entry->n_objects - i - 1) * sizeof (entry->objects[0]));
408   entry->n_objects -= 1;  
409 }
410
411 static void
412 object_remove_from_interfaces (DBusObjectRegistry *registry,
413                                DBusObjectEntry    *entry)
414 {
415   if (entry->interfaces != NULL)
416     {
417       int i;
418       
419       i = 0;
420       while (entry->interfaces[i] != NULL)
421         {
422           DBusInterfaceEntry *iface = entry->interfaces[i];
423           
424           interface_entry_remove_object (iface, entry->id_index);
425           if (iface->n_objects == 0)
426             delete_interface (registry, iface);
427           ++i;
428         }
429     }
430 }
431
432 static DBusSignalEntry*
433 lookup_signal (DBusObjectRegistry *registry,
434                const char         *name,
435                dbus_bool_t         create_if_not_found)
436 {
437   DBusSignalEntry *entry;
438   int sz;
439   int len;
440   
441   entry = _dbus_hash_table_lookup_string (registry->signal_table,
442                                           name);
443   if (entry != NULL || !create_if_not_found)
444     return entry;
445   
446   _dbus_assert (create_if_not_found);
447
448   len = strlen (name);
449   sz = _DBUS_STRUCT_OFFSET (DBusSignalEntry, name) + len + 1;
450   entry = dbus_malloc (sz);
451   if (entry == NULL)
452     return NULL;
453   entry->n_connections = 0;
454   entry->n_allocated = 0;
455   entry->connections = NULL;
456   memcpy (entry->name, name, len + 1);
457
458   if (!_dbus_hash_table_insert_string (registry->signal_table,
459                                        entry->name, entry))
460     {
461       dbus_free (entry);
462       return NULL;
463     }
464   
465   return entry;
466 }
467
468 static void
469 delete_signal (DBusObjectRegistry *registry,
470                DBusSignalEntry *entry)
471 {
472   _dbus_hash_table_remove_string (registry->signal_table,
473                                   entry->name);
474 }
475
476 static dbus_bool_t
477 signal_entry_add_object (DBusSignalEntry *entry,
478                          dbus_uint16_t    object_index)
479 {
480   if (entry->n_connections == entry->n_allocated)
481     {
482       unsigned int new_alloc;
483       dbus_uint16_t *new_objects;
484       
485       if (entry->n_allocated == 0)
486         new_alloc = 2;
487       else
488         new_alloc = entry->n_allocated * 2;
489
490       /* Right now MAX_CONNECTIONS_PER_SIGNAL can't possibly be reached
491        * since the max number of objects _total_ is smaller, but the
492        * code is here for future robustness.
493        */
494       
495       if (new_alloc > DBUS_MAX_CONNECTIONS_PER_SIGNAL)
496         new_alloc = DBUS_MAX_CONNECTIONS_PER_SIGNAL;
497       if (new_alloc == entry->n_allocated)
498         {
499           _dbus_warn ("Attempting to register another instance with signal %s, but max count %d reached\n",
500                       entry->name, DBUS_MAX_CONNECTIONS_PER_SIGNAL);
501           return FALSE;
502         }
503
504       new_objects = dbus_realloc (entry->connections, new_alloc * sizeof (dbus_uint16_t));
505       if (new_objects == NULL)
506         return FALSE;
507       entry->connections = new_objects;
508       entry->n_allocated = new_alloc;
509     }
510
511   _dbus_assert (entry->n_connections < entry->n_allocated);
512
513   entry->connections[entry->n_connections] = object_index;
514   entry->n_connections += 1;
515
516   return TRUE;
517 }
518
519 static void
520 signal_entry_remove_object (DBusSignalEntry *entry,
521                             dbus_uint16_t    object_index)
522 {
523   unsigned int i;
524
525   i = 0;
526   while (i < entry->n_connections)
527     {
528       if (entry->connections[i] == object_index)
529         break;
530       ++i;
531     }
532
533   if (i == entry->n_connections)
534     {
535       _dbus_assert_not_reached ("Tried to remove object from an signal that didn't list that object\n");
536       return;
537     }
538
539   memmove (&entry->connections[i],
540            &entry->connections[i+1],
541            (entry->n_connections - i - 1) * sizeof (entry->connections[0]));
542   entry->n_connections -= 1;  
543 }
544
545 static void
546 object_remove_from_signals (DBusObjectRegistry *registry,
547                             DBusObjectEntry    *entry)
548 {
549   if (entry->signals != NULL)
550     {
551       int i;
552       
553       i = 0;
554       while (entry->signals[i] != NULL)
555         {
556           DBusSignalEntry *iface = entry->signals[i];
557           
558           signal_entry_remove_object (iface, entry->id_index);
559           if (iface->n_connections == 0)
560             delete_signal (registry, iface);
561           ++i;
562         }
563     }
564 }
565
566 /**
567  * Connect this object to the given signal, such that if a
568  * signal emission message is received with the given
569  * signal name, the message will be routed to the
570  * given object.
571  *
572  * Must be called with #DBusConnection lock held.
573  * 
574  * @param registry the object registry
575  * @param object_id object that would like to see the signal
576  * @param signal signal name
577  *
578  * @returns #FALSE if no memory
579  */
580 dbus_bool_t
581 _dbus_object_registry_connect_locked (DBusObjectRegistry *registry,
582                                       const DBusObjectID *object_id,
583                                       const char         *signal_name)
584 {
585   DBusSignalEntry **new_signals;
586   DBusSignalEntry *signal;
587   DBusObjectEntry *entry;
588   int i;
589   
590   entry = validate_id (registry, object_id);
591   if (entry == NULL)
592     {
593       _dbus_warn ("Tried to connect a nonexistent D-BUS object ID to signal \"%s\"\n",
594                   signal_name);
595       
596       return FALSE;
597     }
598
599   /* O(n) in number of connections unfortunately, but in practice I
600    * don't think it will matter.  It's marginally a space-time
601    * tradeoff (save an n_signals field) but the NULL termination is
602    * just as large as an n_signals once we have even a single
603    * connection.
604    */
605   i = 0;
606   if (entry->signals != NULL)
607     {
608       while (entry->signals[i] != NULL)
609         ++i;
610     }
611   
612   new_signals = dbus_realloc (entry->signals,
613                               (i + 2) * sizeof (DBusSignalEntry*));
614   
615   if (new_signals == NULL)
616     return FALSE;
617
618   entry->signals = new_signals;
619   
620   signal = lookup_signal (registry, signal_name, TRUE); 
621   if (signal == NULL)
622     goto oom;
623
624   if (!signal_entry_add_object (signal, entry->id_index))
625     goto oom;
626   
627   entry->signals[i] = signal;
628   ++i;
629   entry->signals[i] = NULL;
630
631   return TRUE;
632   
633  oom:
634   if (signal && signal->n_connections == 0)
635     delete_signal (registry, signal);
636   
637   return FALSE;
638 }
639
640 /**
641  * Reverses effects of _dbus_object_registry_disconnect_locked().
642  *
643  * @param registry the object registry
644  * @param object_id object that would like to see the signal
645  * @param signal signal name
646  */
647 void
648 _dbus_object_registry_disconnect_locked (DBusObjectRegistry      *registry,
649                                          const DBusObjectID      *object_id,
650                                          const char              *signal_name)
651 {
652   DBusObjectEntry *entry;
653   DBusSignalEntry *signal;
654   
655   entry = validate_id (registry, object_id);
656   if (entry == NULL)
657     {
658       _dbus_warn ("Tried to disconnect signal \"%s\" from a nonexistent D-BUS object ID\n",
659                   signal_name);
660       
661       return;
662     }
663
664   signal = lookup_signal (registry, signal_name, FALSE);
665   if (signal == NULL)
666     {
667       _dbus_warn ("Tried to disconnect signal \"%s\" but no such signal is connected\n",
668                   signal_name);
669       return;
670     }
671   
672   signal_entry_remove_object (signal, entry->id_index);
673
674   if (signal->n_connections == 0)
675     delete_signal (registry, signal);
676 }
677
678 static DBusHandlerResult
679 handle_method_call_and_unlock (DBusObjectRegistry *registry,
680                                DBusMessage        *message)
681 {
682   DBusInterfaceEntry *iface_entry;
683   DBusObjectEntry *object_entry;
684   DBusObjectInfo info;
685   const DBusObjectVTable *vtable;
686   
687   _dbus_assert (registry != NULL);
688   _dbus_assert (message != NULL);  
689
690   /* FIXME handle calls to an object ID instead of just an
691    * interface name
692    */
693   
694   /* If the message isn't to a specific object ID, we send
695    * it to the first object that supports the given interface.
696    */
697   iface_entry = lookup_interface (registry,
698                                   dbus_message_get_name (message),
699                                   FALSE);
700   
701   if (iface_entry == NULL)
702     {
703 #ifdef DBUS_BUILD_TESTS
704       if (registry->connection)
705 #endif
706         _dbus_connection_unlock (registry->connection);
707
708       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
709     }
710   
711   _dbus_assert (iface_entry->n_objects > 0);
712   _dbus_assert (iface_entry->objects != NULL);
713
714   object_entry = &registry->entries[iface_entry->objects[0]];
715
716
717   /* Once we have an object entry, pass message to the object */
718   
719   _dbus_assert (object_entry->vtable != NULL);
720
721   info_from_entry (registry, &info, object_entry);
722   vtable = object_entry->vtable;
723   
724   /* Drop lock and invoke application code */
725 #ifdef DBUS_BUILD_TESTS
726   if (registry->connection)
727 #endif
728     _dbus_connection_unlock (registry->connection);
729   
730   (* vtable->message) (&info, message);
731
732   return DBUS_HANDLER_RESULT_HANDLED;
733 }
734
735 typedef struct
736 {
737   DBusObjectID id;
738 } ObjectEmitData;
739
740 static DBusHandlerResult
741 handle_signal_and_unlock (DBusObjectRegistry *registry,
742                           DBusMessage        *message)
743 {
744   DBusSignalEntry *signal_entry;
745   int i;
746   ObjectEmitData *objects;
747   int n_objects;
748   
749   _dbus_assert (registry != NULL);
750   _dbus_assert (message != NULL);
751
752   signal_entry = lookup_signal (registry,
753                                 dbus_message_get_name (message),
754                                 FALSE);
755   
756   if (signal_entry == NULL)
757     {
758 #ifdef DBUS_BUILD_TESTS
759       if (registry->connection)
760 #endif
761         _dbus_connection_unlock (registry->connection);
762       
763       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
764     }
765   
766   _dbus_assert (signal_entry->n_connections > 0);
767   _dbus_assert (signal_entry->connections != NULL);
768
769   /* make a copy for safety vs. reentrancy */
770
771   /* FIXME (?) if you disconnect a signal during (vs. before)
772    * emission, you still receive that signal. To fix this uses more
773    * memory because we don't have a per-connection object at the
774    * moment. You would have to introduce a connection object and
775    * refcount it and have a "disconnected" flag. This is more like
776    * GObject semantics but also maybe not important at this level (the
777    * GObject/Qt wrappers can mop it up).
778    */
779   
780   n_objects = signal_entry->n_connections;
781   objects = dbus_new (ObjectEmitData, n_objects);
782
783   if (objects == NULL)
784     {
785 #ifdef DBUS_BUILD_TESTS
786       if (registry->connection)
787 #endif
788         _dbus_connection_unlock (registry->connection);
789       
790       return DBUS_HANDLER_RESULT_NEED_MEMORY;
791     }
792
793   i = 0;
794   while (i < signal_entry->n_connections)
795     {
796       DBusObjectEntry *object_entry;
797       int idx;
798       
799       idx = signal_entry->connections[i];
800
801       object_entry = &registry->entries[idx];
802
803       _dbus_assert (object_entry->vtable != NULL);
804       
805       id_from_entry (registry,
806                      &objects[i].id,
807                      object_entry);
808       
809       ++i;
810     }
811
812 #ifdef DBUS_BUILD_TESTS
813   if (registry->connection)
814 #endif
815     _dbus_connection_ref_unlocked (registry->connection);
816   _dbus_object_registry_ref (registry);
817   dbus_message_ref (message);
818   
819   i = 0;
820   while (i < n_objects)
821     {
822       DBusObjectEntry *object_entry;
823
824       /* If an object ID no longer exists, don't send the
825        * signal
826        */
827       object_entry = validate_id (registry, &objects[i].id);
828       if (object_entry != NULL)
829         {
830           const DBusObjectVTable *vtable;
831           DBusObjectInfo info;
832
833           info_from_entry (registry, &info, object_entry);
834           vtable = object_entry->vtable;
835
836           /* Drop lock and invoke application code */
837 #ifdef DBUS_BUILD_TESTS
838           if (registry->connection)
839 #endif
840             _dbus_connection_unlock (registry->connection);
841           
842           (* vtable->message) (&info, message);
843
844           /* Reacquire lock */
845 #ifdef DBUS_BUILD_TESTS
846           if (registry->connection)
847 #endif
848             _dbus_connection_lock (registry->connection);
849         }
850       ++i;
851     }
852
853   dbus_message_unref (message);
854   _dbus_object_registry_unref (registry);
855 #ifdef DBUS_BUILD_TESTS
856   if (registry->connection)
857 #endif
858     _dbus_connection_unref_unlocked (registry->connection);
859
860   dbus_free (objects);
861   
862   /* Drop lock a final time */
863 #ifdef DBUS_BUILD_TESTS
864   if (registry->connection)
865 #endif
866     _dbus_connection_unlock (registry->connection);
867
868   return DBUS_HANDLER_RESULT_HANDLED;
869 }
870
871 /**
872  * Handle a message, passing it to any objects in the registry that
873  * should receive it.
874  *
875  * @todo handle messages to an object ID, not just those to
876  * an interface name.
877  * 
878  * @param registry the object registry
879  * @param message the message to handle
880  * @returns what to do with the message next
881  */
882 DBusHandlerResult
883 _dbus_object_registry_handle_and_unlock (DBusObjectRegistry *registry,
884                                          DBusMessage        *message)
885 {
886   int type;
887   
888   _dbus_assert (registry != NULL);
889   _dbus_assert (message != NULL);
890   
891   type = dbus_message_get_type (message);
892
893   switch (type)
894     {
895     case DBUS_MESSAGE_TYPE_METHOD_CALL:
896       return handle_method_call_and_unlock (registry, message);
897     case DBUS_MESSAGE_TYPE_SIGNAL:
898       return handle_signal_and_unlock (registry, message);
899     default:
900 #ifdef DBUS_BUILD_TESTS
901       if (registry->connection)
902 #endif
903         _dbus_connection_unlock (registry->connection);
904
905       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
906     }
907 }
908
909 dbus_bool_t
910 _dbus_object_registry_add_and_unlock (DBusObjectRegistry      *registry,
911                                       const char             **interfaces,
912                                       const DBusObjectVTable  *vtable,
913                                       void                    *object_impl,
914                                       DBusObjectID            *object_id)
915 {
916   int idx;
917   int i;
918   DBusObjectInfo info;
919   
920   if (registry->n_entries_used == registry->n_entries_allocated)
921     {
922       DBusObjectEntry *new_entries;
923       int new_alloc;
924
925       if (registry->n_entries_allocated == 0)
926         new_alloc = 16;
927       else
928         {
929           if (registry->n_entries_allocated == DBUS_MAX_OBJECTS_PER_CONNECTION)
930             {
931               _dbus_warn ("Attempting to register a new D-BUS object, but maximum object count of %d reached\n",
932                           DBUS_MAX_OBJECTS_PER_CONNECTION);
933               goto out_0;
934             }
935
936           new_alloc = registry->n_entries_allocated * 2;
937           if (new_alloc > DBUS_MAX_OBJECTS_PER_CONNECTION)
938             new_alloc = DBUS_MAX_OBJECTS_PER_CONNECTION;
939         }
940
941       new_entries = dbus_realloc (registry->entries,
942                                   new_alloc * sizeof (DBusObjectEntry));
943
944       if (new_entries == NULL)
945         goto out_0;
946
947       memset (&new_entries[registry->n_entries_allocated],
948               '\0',
949               sizeof (DBusObjectEntry) * (new_alloc - registry->n_entries_allocated));
950
951       registry->entries = new_entries;
952       registry->n_entries_allocated = new_alloc;
953     }
954   _dbus_assert (registry->n_entries_used < registry->n_entries_allocated);
955
956   /* We linear search for an available entry. However, short-circuit
957    * the hopefully-common situation where we don't have a sparse
958    * array.
959    */
960   if (registry->entries[registry->n_entries_used].vtable == NULL)
961     {
962       idx = registry->n_entries_used;
963     }
964   else
965     {
966       /* If we do have a sparse array, we try to get rid of it rather
967        * than using empty slots on the end, so we won't hit this case
968        * next time.
969        */
970
971       /* If index n_entries_used is occupied, then
972        * there is at least one entry outside of
973        * the range [0, n_entries_used). Thus, there is
974        * at least one blank entry inside that range.
975        */
976       idx = 0;
977       while (idx < registry->n_entries_used)
978         {
979           if (registry->entries[idx].vtable == NULL)
980             break;
981           ++idx;
982         }
983
984       _dbus_assert (idx < registry->n_entries_used);
985     }
986   
987   registry->entries[idx].id_index = idx;
988   /* Overflow is OK here, but zero isn't as it's a null ID */
989   registry->entries[idx].id_times_used += 1;
990   if (registry->entries[idx].id_times_used == 0)
991     registry->entries[idx].id_times_used += 1;
992     
993   registry->entries[idx].vtable = vtable;
994   registry->entries[idx].object_impl = object_impl;
995
996   registry->n_entries_used += 1;
997
998   i = 0;
999   if (interfaces != NULL)
1000     {
1001       while (interfaces[i] != NULL)
1002         ++i;
1003     }
1004
1005   if (i > 0)
1006     {
1007       DBusInterfaceEntry **new_interfaces;
1008       
1009       new_interfaces = 
1010         dbus_realloc (registry->entries[idx].interfaces,
1011                       (i + 1) * sizeof (DBusInterfaceEntry*));
1012       
1013       if (new_interfaces == NULL)
1014         {
1015           /* maintain invariant that .interfaces array points to something
1016            * valid in oom handler (entering this function it pointed to
1017            * stale data but a valid malloc block)
1018            */
1019           dbus_free (registry->entries[idx].interfaces);
1020           registry->entries[idx].interfaces = NULL;
1021           goto out_1;
1022         }
1023
1024       /* NULL-init so it's NULL-terminated and the OOM
1025        * case can see how far we got
1026        */
1027       while (i >= 0)
1028         {
1029           new_interfaces[i] = NULL;
1030           --i;
1031         }
1032       
1033       registry->entries[idx].interfaces = new_interfaces;
1034     }
1035   else
1036     {
1037       dbus_free (registry->entries[idx].interfaces);
1038       registry->entries[idx].interfaces = NULL;
1039     }
1040
1041   /* Fill in interfaces */
1042   if (interfaces != NULL)
1043     {
1044       i = 0;
1045       while (interfaces[i] != NULL)
1046         {
1047           DBusInterfaceEntry *iface;
1048           
1049           iface = lookup_interface (registry, interfaces[i],
1050                                     TRUE);
1051           if (iface == NULL)
1052             goto out_1;
1053           
1054           if (!interface_entry_add_object (iface, idx))
1055             {
1056               if (iface->n_objects == 0)
1057                 delete_interface (registry, iface);
1058               goto out_1;
1059             }
1060           
1061           registry->entries[idx].interfaces[i] = iface;
1062           
1063           ++i;
1064         }
1065     }
1066   
1067   info_from_entry (registry, &info, &registry->entries[idx]);
1068   if (object_id)
1069     *object_id = info.object_id;
1070   
1071   /* Drop lock and invoke application code */
1072 #ifdef DBUS_BUILD_TESTS
1073   if (registry->connection)
1074 #endif
1075     _dbus_connection_unlock (registry->connection);
1076   
1077   (* vtable->registered) (&info);
1078
1079   return TRUE;
1080   
1081  out_1:    
1082   registry->entries[idx].vtable = NULL;
1083   registry->entries[idx].object_impl = NULL;
1084   registry->n_entries_used -= 1;
1085
1086   object_remove_from_interfaces (registry,
1087                                  &registry->entries[idx]);
1088   
1089  out_0:
1090 #ifdef DBUS_BUILD_TESTS
1091   if (registry->connection)
1092 #endif
1093     _dbus_connection_unlock (registry->connection);
1094   return FALSE;
1095 }
1096
1097 void
1098 _dbus_object_registry_remove_and_unlock (DBusObjectRegistry *registry,
1099                                          const DBusObjectID *object_id)
1100 {
1101   DBusObjectInfo info;
1102   DBusObjectEntry *entry;
1103   const DBusObjectVTable *vtable;
1104
1105   entry = validate_id (registry, object_id);
1106   if (entry == NULL)
1107     {
1108       _dbus_warn ("Tried to unregister a nonexistent D-BUS object ID\n");
1109 #ifdef DBUS_BUILD_TESTS
1110       if (registry->connection)
1111 #endif
1112         _dbus_connection_unlock (registry->connection);
1113       
1114       return;
1115     }
1116
1117   object_remove_from_signals (registry, entry);
1118   object_remove_from_interfaces (registry, entry);
1119   
1120   info_from_entry (registry, &info, entry);
1121   vtable = entry->vtable;
1122   entry->vtable = NULL;
1123   entry->object_impl = NULL;
1124   registry->n_entries_used -= 1;
1125   
1126   /* Drop lock and invoke application code */
1127 #ifdef DBUS_BUILD_TESTS
1128   if (registry->connection)
1129 #endif
1130     _dbus_connection_unlock (registry->connection);
1131
1132   (* vtable->unregistered) (&info);
1133 }
1134
1135
1136 void
1137 _dbus_object_registry_free_all_unlocked (DBusObjectRegistry *registry)
1138 {
1139   int i;
1140   
1141   i = 0;
1142   while (registry->n_entries_used > 0)
1143     {
1144       _dbus_assert (i < registry->n_entries_allocated);
1145       if (registry->entries[i].vtable != NULL)
1146         {
1147           DBusObjectInfo info;
1148           const DBusObjectVTable *vtable;
1149
1150           object_remove_from_interfaces (registry,
1151                                          &registry->entries[i]);
1152           
1153           info_from_entry (registry, &info, &registry->entries[i]);
1154           vtable = registry->entries[i].vtable;
1155           registry->entries[i].vtable = NULL;
1156           registry->entries[i].object_impl = NULL;
1157           registry->n_entries_used -= 1;
1158           _dbus_assert (registry->n_entries_used >= 0);
1159
1160           (* vtable->unregistered) (&info);
1161         }
1162
1163       ++i;
1164     }
1165
1166   _dbus_assert (registry->n_entries_used == 0);
1167 }
1168
1169 /** @} */
1170
1171 #ifdef DBUS_BUILD_TESTS
1172 #include "dbus-test.h"
1173 #include <stdio.h>
1174
1175 static void
1176 noop_message_function (DBusObjectInfo *info,
1177                        DBusMessage    *message)
1178 {
1179   /* nothing */
1180 }
1181
1182 static void
1183 add_and_remove_objects (DBusObjectRegistry *registry)
1184 {
1185 #define N_OBJECTS 73
1186   DBusObjectID ids[N_OBJECTS];
1187   const char *zero_interfaces[] = { NULL };
1188   const char *one_interface[] = { "org.freedesktop.Test.Blah", NULL };
1189   const char *three_interfaces[] = { "org.freedesktop.Test.Blah",
1190                                      "org.freedesktop.Test.Baz",
1191                                      "org.freedesktop.Test.Foo",
1192                                      NULL };
1193   int i;
1194   DBusMessage *message;
1195   
1196   i = 0;
1197   while (i < N_OBJECTS)
1198     {
1199       DBusCallbackObject *callback;
1200       const char **interfaces;
1201       
1202       callback = dbus_callback_object_new (noop_message_function, NULL, NULL);
1203       if (callback == NULL)
1204         goto out;
1205
1206       interfaces = NULL;
1207       switch (i % 3)
1208         {
1209         case 0:
1210           interfaces = zero_interfaces;
1211           break;
1212         case 1:
1213           interfaces = one_interface;
1214           break;
1215         case 2:
1216           interfaces = three_interfaces;
1217           break;
1218         }
1219       _dbus_assert (interfaces != NULL);
1220       
1221       if (!_dbus_object_registry_add_and_unlock (registry,
1222                                                  interfaces,
1223                                                  dbus_callback_object_vtable,
1224                                                  callback,
1225                                                  &ids[i]))
1226         {
1227           dbus_callback_object_unref (callback);
1228           goto out;
1229         }
1230
1231       dbus_callback_object_unref (callback);
1232       
1233       ++i;
1234     }
1235                                      
1236   i = 0;
1237   while (i < N_OBJECTS)
1238     {
1239       if (i > (N_OBJECTS - 20) || (i % 3) == 0)
1240         {
1241           _dbus_object_registry_remove_and_unlock (registry,
1242                                                    &ids[i]);
1243           dbus_object_id_set_null (&ids[i]);
1244         }
1245       
1246       ++i;
1247     }
1248                                      
1249   i = 0;
1250   while (i < N_OBJECTS)
1251     {
1252       if (dbus_object_id_is_null (&ids[i]))
1253         {
1254           DBusCallbackObject *callback;
1255           const char **interfaces;
1256       
1257           callback = dbus_callback_object_new (noop_message_function, NULL, NULL);
1258           if (callback == NULL)
1259             goto out;
1260
1261           interfaces = NULL;
1262           switch (i % 4)
1263             {
1264             case 0:
1265               interfaces = NULL;
1266               break;
1267             case 1:
1268               interfaces = zero_interfaces;
1269               break;
1270             case 2:
1271               interfaces = one_interface;
1272               break;
1273             case 3:
1274               interfaces = three_interfaces;
1275               break;
1276             }
1277           _dbus_assert (interfaces != NULL);
1278       
1279           if (!_dbus_object_registry_add_and_unlock (registry,
1280                                                      interfaces,
1281                                                      dbus_callback_object_vtable,
1282                                                      callback,
1283                                                      &ids[i]))
1284             {
1285               dbus_callback_object_unref (callback);
1286               goto out;
1287             }
1288           
1289           dbus_callback_object_unref (callback);
1290         }
1291       
1292       ++i;
1293     }
1294
1295   message = dbus_message_new_method_call ("org.freedesktop.Test.Foo", NULL);
1296   if (message != NULL)
1297     {
1298       if (_dbus_object_registry_handle_and_unlock (registry, message) !=
1299           DBUS_HANDLER_RESULT_HANDLED)
1300         _dbus_assert_not_reached ("message not handled\n");
1301       dbus_message_unref (message);
1302     }
1303
1304   message = dbus_message_new_method_call ("org.freedesktop.Test.Blah", NULL);
1305   if (message != NULL)
1306     {
1307       if (_dbus_object_registry_handle_and_unlock (registry, message) !=
1308           DBUS_HANDLER_RESULT_HANDLED)
1309         _dbus_assert_not_reached ("message not handled\n");
1310       dbus_message_unref (message);
1311     }
1312
1313   message = dbus_message_new_method_call ("org.freedesktop.Test.NotRegisteredIface", NULL);
1314   if (message != NULL)
1315     {
1316       if (_dbus_object_registry_handle_and_unlock (registry, message) !=
1317           DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
1318         _dbus_assert_not_reached ("message handled but no handler was registered\n");
1319       dbus_message_unref (message);
1320     }
1321   
1322   i = 0;
1323   while (i < (N_OBJECTS - 30))
1324     {
1325       _dbus_assert (!dbus_object_id_is_null (&ids[i]));
1326       
1327       _dbus_object_registry_remove_and_unlock (registry,
1328                                                &ids[i]);
1329       ++i;
1330     }
1331
1332  out:
1333   /* unregister the rest this way, to test this function */
1334   _dbus_object_registry_free_all_unlocked (registry);
1335 }
1336
1337 static dbus_bool_t
1338 object_registry_test_iteration (void *data)
1339 {
1340   DBusObjectRegistry *registry;
1341   
1342   registry = _dbus_object_registry_new (NULL);
1343   if (registry == NULL)
1344     return TRUE;
1345
1346   /* we do this twice since realloc behavior will differ each time,
1347    * and the IDs will get recycled leading to slightly different
1348    * codepaths
1349    */
1350   add_and_remove_objects (registry);
1351   add_and_remove_objects (registry);
1352   
1353   _dbus_object_registry_unref (registry);
1354
1355   return TRUE;
1356 }
1357
1358 /**
1359  * @ingroup DBusObjectRegistry
1360  * Unit test for DBusObjectRegistry
1361  * @returns #TRUE on success.
1362  */
1363 dbus_bool_t
1364 _dbus_object_registry_test (void)
1365 {
1366   _dbus_test_oom_handling ("object registry",
1367                            object_registry_test_iteration,
1368                            NULL);
1369   
1370   return TRUE;
1371 }
1372
1373 #endif /* DBUS_BUILD_TESTS */