1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-object-registry.c DBusObjectRegistry (internals of DBusConnection)
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
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"
31 * @defgroup DBusObjectRegistry Map object IDs to implementations
32 * @ingroup DBusInternals
33 * @brief DBusObjectRegistry is used by DBusConnection to track object IDs
35 * Types and functions related to DBusObjectRegistry. These
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.
45 typedef struct DBusObjectEntry DBusObjectEntry;
46 typedef struct DBusInterfaceEntry DBusInterfaceEntry;
47 typedef struct DBusSignalEntry DBusSignalEntry;
49 #define DBUS_MAX_OBJECTS_PER_INTERFACE 65535
50 struct DBusInterfaceEntry
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) */
58 #define DBUS_MAX_CONNECTIONS_PER_SIGNAL 65535
59 struct DBusSignalEntry
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
66 char name[4]; /**< Interface of signal, nul, then name of signal (actually allocated larger) */
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
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 */
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) */
84 struct DBusObjectRegistry
87 DBusConnection *connection;
89 DBusObjectEntry *entries;
90 int n_entries_allocated;
93 DBusHashTable *interface_table;
95 DBusHashTable *signal_table;
99 free_interface_entry (void *entry)
101 DBusInterfaceEntry *iface = entry;
103 if (iface == NULL) /* DBusHashTable stupidity */
106 dbus_free (iface->objects);
111 free_signal_entry (void *entry)
113 DBusSignalEntry *signal = entry;
115 if (signal == NULL) /* DBusHashTable stupidity */
118 dbus_free (signal->connections);
123 _dbus_object_registry_new (DBusConnection *connection)
125 DBusObjectRegistry *registry;
126 DBusHashTable *interface_table;
127 DBusHashTable *signal_table;
129 /* the connection passed in here isn't fully constructed,
130 * so don't do anything more than store a pointer to
135 interface_table = NULL;
138 registry = dbus_new0 (DBusObjectRegistry, 1);
139 if (registry == NULL)
142 interface_table = _dbus_hash_table_new (DBUS_HASH_STRING,
143 NULL, free_interface_entry);
144 if (interface_table == NULL)
147 signal_table = _dbus_hash_table_new (DBUS_HASH_TWO_STRINGS,
148 NULL, free_signal_entry);
149 if (signal_table == NULL)
152 registry->refcount = 1;
153 registry->connection = connection;
154 registry->interface_table = interface_table;
155 registry->signal_table = signal_table;
161 dbus_free (registry);
163 _dbus_hash_table_unref (interface_table);
165 _dbus_hash_table_unref (signal_table);
171 _dbus_object_registry_ref (DBusObjectRegistry *registry)
173 _dbus_assert (registry->refcount > 0);
175 registry->refcount += 1;
179 _dbus_object_registry_unref (DBusObjectRegistry *registry)
181 _dbus_assert (registry->refcount > 0);
183 registry->refcount -= 1;
185 if (registry->refcount == 0)
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);
194 while (i < registry->n_entries_allocated)
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);
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);
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))
214 #define ID_TO_INDEX(id) \
215 (((dbus_uint32_t) (id)) & DBUS_OBJECT_INDEX_MASK)
217 #define ID_TO_TIMES_USED(id) \
218 (((dbus_uint32_t) (id)) >> DBUS_OBJECT_INDEX_BITS)
220 static DBusObjectEntry*
221 validate_id (DBusObjectRegistry *registry,
222 const DBusObjectID *object_id)
226 dbus_uint32_t instance_bits;
228 instance_bits = dbus_object_id_get_instance_bits (object_id);
230 /* Verify that connection ID bits are the same */
231 #ifdef DBUS_BUILD_TESTS
232 if (registry->connection)
237 _dbus_connection_init_id (registry->connection,
239 dbus_object_id_set_instance_bits (&tmp_id, instance_bits);
241 if (!dbus_object_id_equal (&tmp_id, object_id))
245 idx = ID_TO_INDEX (instance_bits);
246 times_used = ID_TO_TIMES_USED (instance_bits);
248 if (idx >= registry->n_entries_allocated)
250 if (registry->entries[idx].vtable == NULL)
252 if (registry->entries[idx].id_times_used != times_used)
254 _dbus_assert (registry->entries[idx].id_index == idx);
255 _dbus_assert (registry->n_entries_used > 0);
257 return ®istry->entries[idx];
261 id_from_entry (DBusObjectRegistry *registry,
262 DBusObjectID *object_id,
263 DBusObjectEntry *entry)
265 #ifdef DBUS_BUILD_TESTS
266 if (registry->connection)
268 _dbus_connection_init_id (registry->connection,
270 #ifdef DBUS_BUILD_TESTS
273 dbus_object_id_set_server_bits (object_id, 1);
274 dbus_object_id_set_client_bits (object_id, 2);
278 _dbus_assert (dbus_object_id_get_server_bits (object_id) != 0);
279 _dbus_assert (dbus_object_id_get_client_bits (object_id) != 0);
281 dbus_object_id_set_instance_bits (object_id,
282 ENTRY_TO_ID (entry));
284 _dbus_assert (dbus_object_id_get_instance_bits (object_id) != 0);
288 info_from_entry (DBusObjectRegistry *registry,
289 DBusObjectInfo *info,
290 DBusObjectEntry *entry)
292 info->connection = registry->connection;
293 info->object_impl = entry->object_impl;
295 id_from_entry (registry, &info->object_id, entry);
298 static DBusInterfaceEntry*
299 lookup_interface (DBusObjectRegistry *registry,
301 dbus_bool_t create_if_not_found)
303 DBusInterfaceEntry *entry;
307 entry = _dbus_hash_table_lookup_string (registry->interface_table,
309 if (entry != NULL || !create_if_not_found)
312 _dbus_assert (create_if_not_found);
315 sz = _DBUS_STRUCT_OFFSET (DBusInterfaceEntry, name) + len + 1;
316 entry = dbus_malloc (sz);
319 entry->n_objects = 0;
320 entry->n_allocated = 0;
321 entry->objects = NULL;
322 memcpy (entry->name, name, len + 1);
324 if (!_dbus_hash_table_insert_string (registry->interface_table,
335 delete_interface (DBusObjectRegistry *registry,
336 DBusInterfaceEntry *entry)
338 _dbus_hash_table_remove_string (registry->interface_table,
343 interface_entry_add_object (DBusInterfaceEntry *entry,
344 dbus_uint16_t object_index)
346 if (entry->n_objects == entry->n_allocated)
348 unsigned int new_alloc;
349 dbus_uint16_t *new_objects;
351 if (entry->n_allocated == 0)
354 new_alloc = entry->n_allocated * 2;
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.
361 if (new_alloc > DBUS_MAX_OBJECTS_PER_INTERFACE)
362 new_alloc = DBUS_MAX_OBJECTS_PER_INTERFACE;
363 if (new_alloc == entry->n_allocated)
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);
370 new_objects = dbus_realloc (entry->objects, new_alloc * sizeof (dbus_uint16_t));
371 if (new_objects == NULL)
373 entry->objects = new_objects;
374 entry->n_allocated = new_alloc;
377 _dbus_assert (entry->n_objects < entry->n_allocated);
379 entry->objects[entry->n_objects] = object_index;
380 entry->n_objects += 1;
386 interface_entry_remove_object (DBusInterfaceEntry *entry,
387 dbus_uint16_t object_index)
392 while (i < entry->n_objects)
394 if (entry->objects[i] == object_index)
399 if (i == entry->n_objects)
401 _dbus_assert_not_reached ("Tried to remove object from an interface that didn't list that object\n");
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;
412 object_remove_from_interfaces (DBusObjectRegistry *registry,
413 DBusObjectEntry *entry)
415 if (entry->interfaces != NULL)
420 while (entry->interfaces[i] != NULL)
422 DBusInterfaceEntry *iface = entry->interfaces[i];
424 interface_entry_remove_object (iface, entry->id_index);
425 if (iface->n_objects == 0)
426 delete_interface (registry, iface);
432 static DBusSignalEntry*
433 lookup_signal (DBusObjectRegistry *registry,
434 const char *signal_interface,
435 const char *signal_name,
436 dbus_bool_t create_if_not_found)
438 DBusSignalEntry *entry;
440 size_t len_interface, len_name;
441 char buf[2 * DBUS_MAXIMUM_NAME_LENGTH + 2];
443 /* This is all a little scary and maybe we shouldn't jump
444 * through these hoops just to save some bytes.
447 len_interface = strlen (signal_interface);
448 len_name = strlen (signal_name);
450 _dbus_assert (len_interface + len_name + 2 <= sizeof (buf));
452 memcpy (buf, signal_interface, len_interface + 1);
453 memcpy (buf + len_interface + 1, signal_name, len_name + 1);
455 entry = _dbus_hash_table_lookup_two_strings (registry->signal_table,
457 if (entry != NULL || !create_if_not_found)
460 _dbus_assert (create_if_not_found);
462 sz = _DBUS_STRUCT_OFFSET (DBusSignalEntry, name) + len_interface + len_name + 2;
463 entry = dbus_malloc (sz);
466 entry->n_connections = 0;
467 entry->n_allocated = 0;
468 entry->connections = NULL;
469 memcpy (entry->name, buf, len_interface + len_name + 2);
471 if (!_dbus_hash_table_insert_two_strings (registry->signal_table,
482 delete_signal (DBusObjectRegistry *registry,
483 DBusSignalEntry *entry)
485 _dbus_hash_table_remove_two_strings (registry->signal_table,
490 signal_entry_add_object (DBusSignalEntry *entry,
491 dbus_uint16_t object_index)
493 if (entry->n_connections == entry->n_allocated)
495 unsigned int new_alloc;
496 dbus_uint16_t *new_objects;
498 if (entry->n_allocated == 0)
501 new_alloc = entry->n_allocated * 2;
503 /* Right now MAX_CONNECTIONS_PER_SIGNAL can't possibly be reached
504 * since the max number of objects _total_ is smaller, but the
505 * code is here for future robustness.
508 if (new_alloc > DBUS_MAX_CONNECTIONS_PER_SIGNAL)
509 new_alloc = DBUS_MAX_CONNECTIONS_PER_SIGNAL;
510 if (new_alloc == entry->n_allocated)
512 _dbus_warn ("Attempting to register another instance with signal %s, but max count %d reached\n",
513 entry->name, DBUS_MAX_CONNECTIONS_PER_SIGNAL);
517 new_objects = dbus_realloc (entry->connections, new_alloc * sizeof (dbus_uint16_t));
518 if (new_objects == NULL)
520 entry->connections = new_objects;
521 entry->n_allocated = new_alloc;
524 _dbus_assert (entry->n_connections < entry->n_allocated);
526 entry->connections[entry->n_connections] = object_index;
527 entry->n_connections += 1;
533 signal_entry_remove_object (DBusSignalEntry *entry,
534 dbus_uint16_t object_index)
539 while (i < entry->n_connections)
541 if (entry->connections[i] == object_index)
546 if (i == entry->n_connections)
548 _dbus_assert_not_reached ("Tried to remove object from an signal that didn't list that object\n");
552 memmove (&entry->connections[i],
553 &entry->connections[i+1],
554 (entry->n_connections - i - 1) * sizeof (entry->connections[0]));
555 entry->n_connections -= 1;
559 object_remove_from_signals (DBusObjectRegistry *registry,
560 DBusObjectEntry *entry)
562 if (entry->signals != NULL)
567 while (entry->signals[i] != NULL)
569 DBusSignalEntry *signal = entry->signals[i];
571 signal_entry_remove_object (signal, entry->id_index);
572 if (signal->n_connections == 0)
573 delete_signal (registry, signal);
580 * Connect this object to the given signal, such that if a
581 * signal emission message is received with the given
582 * signal name, the message will be routed to the
585 * Must be called with #DBusConnection lock held.
587 * @param registry the object registry
588 * @param object_id object that would like to see the signal
589 * @param signal_interface signal interface name
590 * @param signal_name signal member name
592 * @returns #FALSE if no memory
595 _dbus_object_registry_connect_locked (DBusObjectRegistry *registry,
596 const DBusObjectID *object_id,
597 const char *signal_interface,
598 const char *signal_name)
600 DBusSignalEntry **new_signals;
601 DBusSignalEntry *signal;
602 DBusObjectEntry *entry;
605 _dbus_assert (signal_interface != NULL);
606 _dbus_assert (signal_name != NULL);
608 entry = validate_id (registry, object_id);
611 _dbus_warn ("Tried to connect a nonexistent D-BUS object ID to signal \"%s\"\n",
617 /* O(n) in number of connections unfortunately, but in practice I
618 * don't think it will matter. It's marginally a space-time
619 * tradeoff (save an n_signals field) but the NULL termination is
620 * just as large as an n_signals once we have even a single
624 if (entry->signals != NULL)
626 while (entry->signals[i] != NULL)
630 new_signals = dbus_realloc (entry->signals,
631 (i + 2) * sizeof (DBusSignalEntry*));
633 if (new_signals == NULL)
636 entry->signals = new_signals;
638 signal = lookup_signal (registry, signal_interface, signal_name, TRUE);
642 if (!signal_entry_add_object (signal, entry->id_index))
645 entry->signals[i] = signal;
647 entry->signals[i] = NULL;
652 if (signal && signal->n_connections == 0)
653 delete_signal (registry, signal);
659 * Reverses effects of _dbus_object_registry_disconnect_locked().
661 * @param registry the object registry
662 * @param object_id object that would like to see the signal
663 * @param signal_interface signal interface
664 * @param signal_name signal name
667 _dbus_object_registry_disconnect_locked (DBusObjectRegistry *registry,
668 const DBusObjectID *object_id,
669 const char *signal_interface,
670 const char *signal_name)
672 DBusObjectEntry *entry;
673 DBusSignalEntry *signal;
675 _dbus_assert (signal_interface != NULL);
676 _dbus_assert (signal_name != NULL);
678 entry = validate_id (registry, object_id);
681 _dbus_warn ("Tried to disconnect signal \"%s\"::\"%s\" from a nonexistent D-BUS object ID\n",
682 signal_interface, signal_name);
687 signal = lookup_signal (registry, signal_interface, signal_name, FALSE);
690 _dbus_warn ("Tried to disconnect signal \"%s\"::\"%s\" but no such signal is connected\n",
691 signal_interface, signal_name);
695 signal_entry_remove_object (signal, entry->id_index);
697 if (signal->n_connections == 0)
698 delete_signal (registry, signal);
701 static DBusHandlerResult
702 handle_method_call_and_unlock (DBusObjectRegistry *registry,
703 DBusMessage *message)
705 DBusInterfaceEntry *iface_entry;
706 DBusObjectEntry *object_entry;
708 const DBusObjectVTable *vtable;
710 _dbus_assert (registry != NULL);
711 _dbus_assert (message != NULL);
713 /* FIXME handle calls to an object ID instead of just an
717 /* If the message isn't to a specific object ID, we send
718 * it to the first object that supports the given interface.
720 iface_entry = lookup_interface (registry,
721 dbus_message_get_interface (message),
724 if (iface_entry == NULL)
726 #ifdef DBUS_BUILD_TESTS
727 if (registry->connection)
729 _dbus_connection_unlock (registry->connection);
731 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
734 _dbus_assert (iface_entry->n_objects > 0);
735 _dbus_assert (iface_entry->objects != NULL);
737 object_entry = ®istry->entries[iface_entry->objects[0]];
740 /* Once we have an object entry, pass message to the object */
742 _dbus_assert (object_entry->vtable != NULL);
744 info_from_entry (registry, &info, object_entry);
745 vtable = object_entry->vtable;
747 /* Drop lock and invoke application code */
748 #ifdef DBUS_BUILD_TESTS
749 if (registry->connection)
751 _dbus_connection_unlock (registry->connection);
753 (* vtable->message) (&info, message);
755 return DBUS_HANDLER_RESULT_HANDLED;
763 static DBusHandlerResult
764 handle_signal_and_unlock (DBusObjectRegistry *registry,
765 DBusMessage *message)
767 DBusSignalEntry *signal_entry;
769 ObjectEmitData *objects;
772 _dbus_assert (registry != NULL);
773 _dbus_assert (message != NULL);
775 signal_entry = lookup_signal (registry,
776 dbus_message_get_interface (message),
777 dbus_message_get_member (message),
780 if (signal_entry == NULL)
782 #ifdef DBUS_BUILD_TESTS
783 if (registry->connection)
785 _dbus_connection_unlock (registry->connection);
787 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
790 _dbus_assert (signal_entry->n_connections > 0);
791 _dbus_assert (signal_entry->connections != NULL);
793 /* make a copy for safety vs. reentrancy */
795 /* FIXME (?) if you disconnect a signal during (vs. before)
796 * emission, you still receive that signal. To fix this uses more
797 * memory because we don't have a per-connection object at the
798 * moment. You would have to introduce a connection object and
799 * refcount it and have a "disconnected" flag. This is more like
800 * GObject semantics but also maybe not important at this level (the
801 * GObject/Qt wrappers can mop it up).
804 n_objects = signal_entry->n_connections;
805 objects = dbus_new (ObjectEmitData, n_objects);
809 #ifdef DBUS_BUILD_TESTS
810 if (registry->connection)
812 _dbus_connection_unlock (registry->connection);
814 return DBUS_HANDLER_RESULT_NEED_MEMORY;
818 while (i < signal_entry->n_connections)
820 DBusObjectEntry *object_entry;
823 idx = signal_entry->connections[i];
825 object_entry = ®istry->entries[idx];
827 _dbus_assert (object_entry->vtable != NULL);
829 id_from_entry (registry,
836 #ifdef DBUS_BUILD_TESTS
837 if (registry->connection)
839 _dbus_connection_ref_unlocked (registry->connection);
840 _dbus_object_registry_ref (registry);
841 dbus_message_ref (message);
844 while (i < n_objects)
846 DBusObjectEntry *object_entry;
848 /* If an object ID no longer exists, don't send the
851 object_entry = validate_id (registry, &objects[i].id);
852 if (object_entry != NULL)
854 const DBusObjectVTable *vtable;
857 info_from_entry (registry, &info, object_entry);
858 vtable = object_entry->vtable;
860 /* Drop lock and invoke application code */
861 #ifdef DBUS_BUILD_TESTS
862 if (registry->connection)
864 _dbus_connection_unlock (registry->connection);
866 (* vtable->message) (&info, message);
869 #ifdef DBUS_BUILD_TESTS
870 if (registry->connection)
872 _dbus_connection_lock (registry->connection);
877 dbus_message_unref (message);
878 _dbus_object_registry_unref (registry);
879 #ifdef DBUS_BUILD_TESTS
880 if (registry->connection)
882 _dbus_connection_unref_unlocked (registry->connection);
886 /* Drop lock a final time */
887 #ifdef DBUS_BUILD_TESTS
888 if (registry->connection)
890 _dbus_connection_unlock (registry->connection);
892 return DBUS_HANDLER_RESULT_HANDLED;
896 * Handle a message, passing it to any objects in the registry that
899 * @todo handle messages to an object ID, not just those to
902 * @param registry the object registry
903 * @param message the message to handle
904 * @returns what to do with the message next
907 _dbus_object_registry_handle_and_unlock (DBusObjectRegistry *registry,
908 DBusMessage *message)
912 _dbus_assert (registry != NULL);
913 _dbus_assert (message != NULL);
915 type = dbus_message_get_type (message);
919 case DBUS_MESSAGE_TYPE_METHOD_CALL:
920 return handle_method_call_and_unlock (registry, message);
921 case DBUS_MESSAGE_TYPE_SIGNAL:
922 return handle_signal_and_unlock (registry, message);
924 #ifdef DBUS_BUILD_TESTS
925 if (registry->connection)
927 _dbus_connection_unlock (registry->connection);
929 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
934 _dbus_object_registry_add_and_unlock (DBusObjectRegistry *registry,
935 const char **interfaces,
936 const DBusObjectVTable *vtable,
938 DBusObjectID *object_id)
944 if (registry->n_entries_used == registry->n_entries_allocated)
946 DBusObjectEntry *new_entries;
949 if (registry->n_entries_allocated == 0)
953 if (registry->n_entries_allocated == DBUS_MAX_OBJECTS_PER_CONNECTION)
955 _dbus_warn ("Attempting to register a new D-BUS object, but maximum object count of %d reached\n",
956 DBUS_MAX_OBJECTS_PER_CONNECTION);
960 new_alloc = registry->n_entries_allocated * 2;
961 if (new_alloc > DBUS_MAX_OBJECTS_PER_CONNECTION)
962 new_alloc = DBUS_MAX_OBJECTS_PER_CONNECTION;
965 new_entries = dbus_realloc (registry->entries,
966 new_alloc * sizeof (DBusObjectEntry));
968 if (new_entries == NULL)
971 memset (&new_entries[registry->n_entries_allocated],
973 sizeof (DBusObjectEntry) * (new_alloc - registry->n_entries_allocated));
975 registry->entries = new_entries;
976 registry->n_entries_allocated = new_alloc;
978 _dbus_assert (registry->n_entries_used < registry->n_entries_allocated);
980 /* We linear search for an available entry. However, short-circuit
981 * the hopefully-common situation where we don't have a sparse
984 if (registry->entries[registry->n_entries_used].vtable == NULL)
986 idx = registry->n_entries_used;
990 /* If we do have a sparse array, we try to get rid of it rather
991 * than using empty slots on the end, so we won't hit this case
995 /* If index n_entries_used is occupied, then
996 * there is at least one entry outside of
997 * the range [0, n_entries_used). Thus, there is
998 * at least one blank entry inside that range.
1001 while (idx < registry->n_entries_used)
1003 if (registry->entries[idx].vtable == NULL)
1008 _dbus_assert (idx < registry->n_entries_used);
1011 registry->entries[idx].id_index = idx;
1012 /* Overflow is OK here, but zero isn't as it's a null ID */
1013 registry->entries[idx].id_times_used += 1;
1014 if (registry->entries[idx].id_times_used == 0)
1015 registry->entries[idx].id_times_used += 1;
1017 registry->entries[idx].vtable = vtable;
1018 registry->entries[idx].object_impl = object_impl;
1020 registry->n_entries_used += 1;
1023 if (interfaces != NULL)
1025 while (interfaces[i] != NULL)
1031 DBusInterfaceEntry **new_interfaces;
1034 dbus_realloc (registry->entries[idx].interfaces,
1035 (i + 1) * sizeof (DBusInterfaceEntry*));
1037 if (new_interfaces == NULL)
1039 /* maintain invariant that .interfaces array points to something
1040 * valid in oom handler (entering this function it pointed to
1041 * stale data but a valid malloc block)
1043 dbus_free (registry->entries[idx].interfaces);
1044 registry->entries[idx].interfaces = NULL;
1048 /* NULL-init so it's NULL-terminated and the OOM
1049 * case can see how far we got
1053 new_interfaces[i] = NULL;
1057 registry->entries[idx].interfaces = new_interfaces;
1061 dbus_free (registry->entries[idx].interfaces);
1062 registry->entries[idx].interfaces = NULL;
1065 /* Fill in interfaces */
1066 if (interfaces != NULL)
1069 while (interfaces[i] != NULL)
1071 DBusInterfaceEntry *iface;
1073 iface = lookup_interface (registry, interfaces[i],
1078 if (!interface_entry_add_object (iface, idx))
1080 if (iface->n_objects == 0)
1081 delete_interface (registry, iface);
1085 registry->entries[idx].interfaces[i] = iface;
1091 info_from_entry (registry, &info, ®istry->entries[idx]);
1093 *object_id = info.object_id;
1095 /* Drop lock and invoke application code */
1096 #ifdef DBUS_BUILD_TESTS
1097 if (registry->connection)
1099 _dbus_connection_unlock (registry->connection);
1101 (* vtable->registered) (&info);
1106 registry->entries[idx].vtable = NULL;
1107 registry->entries[idx].object_impl = NULL;
1108 registry->n_entries_used -= 1;
1110 object_remove_from_interfaces (registry,
1111 ®istry->entries[idx]);
1114 #ifdef DBUS_BUILD_TESTS
1115 if (registry->connection)
1117 _dbus_connection_unlock (registry->connection);
1122 _dbus_object_registry_remove_and_unlock (DBusObjectRegistry *registry,
1123 const DBusObjectID *object_id)
1125 DBusObjectInfo info;
1126 DBusObjectEntry *entry;
1127 const DBusObjectVTable *vtable;
1129 entry = validate_id (registry, object_id);
1132 _dbus_warn ("Tried to unregister a nonexistent D-BUS object ID\n");
1133 #ifdef DBUS_BUILD_TESTS
1134 if (registry->connection)
1136 _dbus_connection_unlock (registry->connection);
1141 object_remove_from_signals (registry, entry);
1142 object_remove_from_interfaces (registry, entry);
1144 info_from_entry (registry, &info, entry);
1145 vtable = entry->vtable;
1146 entry->vtable = NULL;
1147 entry->object_impl = NULL;
1148 registry->n_entries_used -= 1;
1150 /* Drop lock and invoke application code */
1151 #ifdef DBUS_BUILD_TESTS
1152 if (registry->connection)
1154 _dbus_connection_unlock (registry->connection);
1156 (* vtable->unregistered) (&info);
1161 _dbus_object_registry_free_all_unlocked (DBusObjectRegistry *registry)
1166 while (registry->n_entries_used > 0)
1168 _dbus_assert (i < registry->n_entries_allocated);
1169 if (registry->entries[i].vtable != NULL)
1171 DBusObjectInfo info;
1172 const DBusObjectVTable *vtable;
1174 object_remove_from_interfaces (registry,
1175 ®istry->entries[i]);
1177 info_from_entry (registry, &info, ®istry->entries[i]);
1178 vtable = registry->entries[i].vtable;
1179 registry->entries[i].vtable = NULL;
1180 registry->entries[i].object_impl = NULL;
1181 registry->n_entries_used -= 1;
1182 _dbus_assert (registry->n_entries_used >= 0);
1184 (* vtable->unregistered) (&info);
1190 _dbus_assert (registry->n_entries_used == 0);
1195 #ifdef DBUS_BUILD_TESTS
1196 #include "dbus-test.h"
1200 noop_message_function (DBusObjectInfo *info,
1201 DBusMessage *message)
1207 add_and_remove_objects (DBusObjectRegistry *registry)
1209 #define N_OBJECTS 73
1210 DBusObjectID ids[N_OBJECTS];
1211 const char *zero_interfaces[] = { NULL };
1212 const char *one_interface[] = { "org.freedesktop.Test.Blah", NULL };
1213 const char *three_interfaces[] = { "org.freedesktop.Test.Blah",
1214 "org.freedesktop.Test.Baz",
1215 "org.freedesktop.Test.Foo",
1218 DBusMessage *message;
1221 while (i < N_OBJECTS)
1223 DBusCallbackObject *callback;
1224 const char **interfaces;
1226 callback = dbus_callback_object_new (noop_message_function, NULL, NULL);
1227 if (callback == NULL)
1234 interfaces = zero_interfaces;
1237 interfaces = one_interface;
1240 interfaces = three_interfaces;
1243 _dbus_assert (interfaces != NULL);
1245 if (!_dbus_object_registry_add_and_unlock (registry,
1247 dbus_callback_object_vtable,
1251 dbus_callback_object_unref (callback);
1255 dbus_callback_object_unref (callback);
1261 while (i < N_OBJECTS)
1263 if (i > (N_OBJECTS - 20) || (i % 3) == 0)
1265 _dbus_object_registry_remove_and_unlock (registry,
1267 dbus_object_id_set_null (&ids[i]);
1274 while (i < N_OBJECTS)
1276 if (dbus_object_id_is_null (&ids[i]))
1278 DBusCallbackObject *callback;
1279 const char **interfaces;
1281 callback = dbus_callback_object_new (noop_message_function, NULL, NULL);
1282 if (callback == NULL)
1292 interfaces = zero_interfaces;
1295 interfaces = one_interface;
1298 interfaces = three_interfaces;
1302 if (!_dbus_object_registry_add_and_unlock (registry,
1304 dbus_callback_object_vtable,
1308 dbus_callback_object_unref (callback);
1312 dbus_callback_object_unref (callback);
1318 message = dbus_message_new_method_call ("org.freedesktop.Test.Foo",
1320 if (message != NULL)
1322 if (_dbus_object_registry_handle_and_unlock (registry, message) !=
1323 DBUS_HANDLER_RESULT_HANDLED)
1324 _dbus_assert_not_reached ("message not handled\n");
1325 dbus_message_unref (message);
1328 message = dbus_message_new_method_call ("org.freedesktop.Test.Blah",
1330 if (message != NULL)
1332 if (_dbus_object_registry_handle_and_unlock (registry, message) !=
1333 DBUS_HANDLER_RESULT_HANDLED)
1334 _dbus_assert_not_reached ("message not handled\n");
1335 dbus_message_unref (message);
1338 message = dbus_message_new_method_call ("org.freedesktop.Test.NotRegisteredIface",
1340 if (message != NULL)
1342 if (_dbus_object_registry_handle_and_unlock (registry, message) !=
1343 DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
1344 _dbus_assert_not_reached ("message handled but no handler was registered\n");
1345 dbus_message_unref (message);
1349 while (i < (N_OBJECTS - 30))
1351 _dbus_assert (!dbus_object_id_is_null (&ids[i]));
1353 _dbus_object_registry_remove_and_unlock (registry,
1359 /* unregister the rest this way, to test this function */
1360 _dbus_object_registry_free_all_unlocked (registry);
1364 object_registry_test_iteration (void *data)
1366 DBusObjectRegistry *registry;
1368 registry = _dbus_object_registry_new (NULL);
1369 if (registry == NULL)
1372 /* we do this twice since realloc behavior will differ each time,
1373 * and the IDs will get recycled leading to slightly different
1376 add_and_remove_objects (registry);
1377 add_and_remove_objects (registry);
1379 _dbus_object_registry_unref (registry);
1385 * @ingroup DBusObjectRegistry
1386 * Unit test for DBusObjectRegistry
1387 * @returns #TRUE on success.
1390 _dbus_object_registry_test (void)
1392 _dbus_test_oom_handling ("object registry",
1393 object_registry_test_iteration,
1399 #endif /* DBUS_BUILD_TESTS */