From 7265423411609c14ddb9e6643463b840afcaa09b Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Wed, 26 Feb 2003 06:42:57 +0000 Subject: 2003-02-26 Havoc Pennington * dbus/dbus-connection.c (dbus_connection_send_message_with_reply_and_block): fix crash where we ref'd the outgoing message instead of the returned reply * dbus/dbus-transport-unix.c (do_authentication): check read watch at the end of this function, so if we didn't need to read for authentication, we reinstall it for receiving messages * dbus/dbus-message.c (dbus_message_new_reply): allow replies to a NULL sender for peer-to-peer case * dbus/dbus-transport-unix.c (check_read_watch): handle !authenticated case correctly * glib/dbus-gmain.c: add support for DBusServer * dbus/dbus-server.c: add data slot support * glib/dbus-gmain.c (dbus_connection_setup_with_g_main): check return values and handle errors * dbus/dbus-dataslot.c: factor out the data slot stuff from DBusConnection * Doxyfile.in (INPUT): add glib subdir * glib/dbus-gmain.c (dbus_connection_setup_with_g_main): rename setup_with_g_main instead of hookup_with_g_main; write docs --- dbus/Makefile.am | 2 + dbus/dbus-connection.c | 263 +++++++++++------------------- dbus/dbus-dataslot.c | 372 +++++++++++++++++++++++++++++++++++++++++++ dbus/dbus-dataslot.h | 76 +++++++++ dbus/dbus-internals.h | 14 +- dbus/dbus-keyring.c | 2 +- dbus/dbus-message.c | 24 ++- dbus/dbus-server-protected.h | 3 + dbus/dbus-server.c | 136 +++++++++++++++- dbus/dbus-server.h | 9 ++ dbus/dbus-test.c | 4 + dbus/dbus-test.h | 23 +-- dbus/dbus-threads.c | 3 +- dbus/dbus-transport-unix.c | 38 ++++- dbus/dbus-transport.c | 3 + 15 files changed, 769 insertions(+), 203 deletions(-) create mode 100644 dbus/dbus-dataslot.c create mode 100644 dbus/dbus-dataslot.h (limited to 'dbus') diff --git a/dbus/Makefile.am b/dbus/Makefile.am index eb7f5c93..7a064ce8 100644 --- a/dbus/Makefile.am +++ b/dbus/Makefile.am @@ -73,6 +73,8 @@ libdbus_1_la_SOURCES= \ noinst_LTLIBRARIES=libdbus-convenience.la libdbus_convenience_la_SOURCES= \ + dbus-dataslot.c \ + dbus-dataslot.h \ dbus-hash.c \ dbus-hash.h \ dbus-internals.c \ diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c index 64e83d11..6fb4e84c 100644 --- a/dbus/dbus-connection.c +++ b/dbus/dbus-connection.c @@ -33,6 +33,7 @@ #include "dbus-message-handler.h" #include "dbus-threads.h" #include "dbus-protocol.h" +#include "dbus-dataslot.h" /** * @defgroup DBusConnection DBusConnection @@ -66,15 +67,6 @@ static dbus_bool_t _dbus_modify_sigpipe = TRUE; -/** Opaque typedef for DBusDataSlot */ -typedef struct DBusDataSlot DBusDataSlot; -/** DBusDataSlot is used to store application data on the connection */ -struct DBusDataSlot -{ - void *data; /**< The application data */ - DBusFreeFunction free_data_func; /**< Free the application data */ -}; - /** * Implementation details of DBusConnection. All fields are private. */ @@ -105,8 +97,8 @@ struct DBusConnection DBusHashTable *handler_table; /**< Table of registered DBusMessageHandler */ DBusList *filter_list; /**< List of filters. */ - DBusDataSlot *data_slots; /**< Data slots */ - int n_slots; /**< Slots allocated so far. */ + + DBusDataSlotList slot_list; /**< Data stored by allocated integer ID */ DBusHashTable *pending_replies; /**< Hash of message serials and their message handlers. */ DBusCounter *connection_counter; /**< Counter that we decrement when finalized */ @@ -130,7 +122,6 @@ typedef struct static void reply_handler_data_free (ReplyHandlerData *data); -static void _dbus_connection_free_data_slots_nolock (DBusConnection *connection); static void _dbus_connection_remove_timeout_locked (DBusConnection *connection, DBusTimeout *timeout); @@ -572,8 +563,8 @@ _dbus_connection_new_for_transport (DBusTransport *transport) connection->pending_replies = pending_replies; connection->filter_list = NULL; - connection->data_slots = NULL; - connection->n_slots = 0; + _dbus_data_slot_list_init (&connection->slot_list); + connection->client_serial = 1; connection->disconnect_message_link = disconnect_link; @@ -798,8 +789,9 @@ _dbus_connection_last_unref (DBusConnection *connection) _dbus_timeout_list_free (connection->timeouts); connection->timeouts = NULL; - - _dbus_connection_free_data_slots_nolock (connection); + + /* calls out to application code... */ + _dbus_data_slot_list_free (&connection->slot_list); _dbus_hash_iter_init (connection->handler_table, &iter); while (_dbus_hash_iter_next (&iter)) @@ -1078,6 +1070,10 @@ reply_handler_data_free (ReplyHandlerData *data) * you want a very short or very long timeout. There is no way to * avoid a timeout entirely, other than passing INT_MAX for the * timeout to postpone it indefinitely. + * + * @todo I think we should rename this function family + * dbus_connection_send(), send_with_reply(), etc. (i.e. + * drop the "message" part), the names are too long. * * @param connection the connection * @param message the message to send @@ -1086,13 +1082,6 @@ reply_handler_data_free (ReplyHandlerData *data) * @param result return location for result code * @returns #TRUE if the message is successfully queued, #FALSE if no memory. * - * @todo this function isn't implemented because we need message serials - * and other slightly more rich DBusMessage implementation in order to - * implement it. The basic idea will be to keep a hash of serials we're - * expecting a reply to, and also to add a way to tell GLib or Qt to - * install a timeout. Then install a timeout which is the shortest - * timeout of any pending reply. - * */ dbus_bool_t dbus_connection_send_message_with_reply (DBusConnection *connection, @@ -1227,10 +1216,9 @@ dbus_connection_send_message_with_reply (DBusConnection *connection, * wrong, result is set to whatever is appropriate, such as * #DBUS_RESULT_NO_MEMORY. * - * @todo I believe if we get EINTR or otherwise interrupt the - * do_iteration call in here, we won't block the required length of - * time. I think there probably has to be a loop: "while (!timeout_elapsed) - * { check_for_reply_in_queue(); iterate_with_remaining_timeout(); }" + * @todo could use performance improvements (it keeps scanning + * the whole message queue for example) and has thread issues, + * see comments in source * * @param connection the connection * @param message the message to send @@ -1247,22 +1235,47 @@ dbus_connection_send_message_with_reply_and_block (DBusConnection *connectio { dbus_int32_t client_serial; DBusList *link; + long start_tv_sec, start_tv_usec; + long end_tv_sec, end_tv_usec; + long tv_sec, tv_usec; if (timeout_milliseconds == -1) timeout_milliseconds = DEFAULT_TIMEOUT_VALUE; + + /* it would probably seem logical to pass in _DBUS_INT_MAX + * for infinite timeout, but then math below would get + * all overflow-prone, so smack that down. + */ + if (timeout_milliseconds > _DBUS_ONE_HOUR_IN_MILLISECONDS * 6) + timeout_milliseconds = _DBUS_ONE_HOUR_IN_MILLISECONDS * 6; if (!dbus_connection_send_message (connection, message, &client_serial, result)) return NULL; + message = NULL; + /* Flush message queue */ dbus_connection_flush (connection); dbus_mutex_lock (connection->mutex); + + _dbus_get_current_time (&start_tv_sec, &start_tv_usec); + end_tv_sec = start_tv_sec + timeout_milliseconds / 1000; + end_tv_usec = start_tv_usec + (timeout_milliseconds % 1000) * 1000; + end_tv_sec += end_tv_usec / _DBUS_USEC_PER_SECOND; + end_tv_usec = end_tv_usec % _DBUS_USEC_PER_SECOND; + + _dbus_verbose ("will block %d milliseconds from %ld sec %ld usec to %ld sec %ld usec\n", + timeout_milliseconds, + start_tv_sec, start_tv_usec, + end_tv_sec, end_tv_usec); /* Now we wait... */ /* THREAD TODO: This is busted. What if a dispatch_message or pop_message * gets the message before we do? */ + block_again: + _dbus_connection_do_iteration (connection, DBUS_ITERATION_DO_READING | DBUS_ITERATION_BLOCK, @@ -1278,7 +1291,7 @@ dbus_connection_send_message_with_reply_and_block (DBusConnection *connectio if (_dbus_message_get_reply_serial (reply) == client_serial) { _dbus_list_remove (&connection->incoming_messages, link); - dbus_message_ref (message); + dbus_message_ref (reply); if (result) *result = DBUS_RESULT_SUCCESS; @@ -1289,8 +1302,27 @@ dbus_connection_send_message_with_reply_and_block (DBusConnection *connectio link = _dbus_list_get_next_link (&connection->incoming_messages, link); } - if (result) - *result = DBUS_RESULT_NO_REPLY; + _dbus_get_current_time (&tv_sec, &tv_usec); + + if (tv_sec < start_tv_sec) + ; /* clock set backward, bail out */ + else if (connection->disconnect_message_link == NULL) + ; /* we're disconnected, bail out */ + else if (tv_sec < end_tv_sec || + (tv_sec == end_tv_sec && tv_usec < end_tv_usec)) + { + timeout_milliseconds = (end_tv_sec - tv_sec) * 1000 + + (end_tv_usec - tv_usec) / 1000; + _dbus_verbose ("%d milliseconds remain\n", timeout_milliseconds); + _dbus_assert (timeout_milliseconds > 0); + + goto block_again; /* not expired yet */ + } + + if (dbus_connection_get_is_connected (connection)) + dbus_set_result (result, DBUS_RESULT_NO_REPLY); + else + dbus_set_result (result, DBUS_RESULT_DISCONNECTED); dbus_mutex_unlock (connection->mutex); @@ -1850,6 +1882,12 @@ dbus_connection_remove_filter (DBusConnection *connection, * this function with the name of a message that already has a * handler. If the function returns #FALSE, the handlers were not * registered due to lack of memory. + * + * @todo the messages_to_handle arg may be more convenient if it's a + * single string instead of an array. Though right now MessageHandler + * is sort of designed to say be associated with an entire object with + * multiple methods, that's why for example the connection only + * weakrefs it. So maybe the "manual" API should be different. * * @param connection the connection * @param handler the handler @@ -1969,25 +2007,23 @@ dbus_connection_unregister_handler (DBusConnection *connection, dbus_mutex_unlock (connection->mutex); } -static int *allocated_slots = NULL; -static int n_allocated_slots = 0; -static int n_used_slots = 0; -static DBusMutex *allocated_slots_lock = NULL; +static DBusDataSlotAllocator slot_allocator; /** - * Initialize the mutex used for #DBusConnecton data + * Initialize the mutex used for #DBusConnection data * slot reservations. * * @returns the mutex */ DBusMutex * -_dbus_allocated_slots_init_lock (void) +_dbus_connection_slots_init_lock (void) { - allocated_slots_lock = dbus_mutex_new (); - return allocated_slots_lock; + if (!_dbus_data_slot_allocator_init (&slot_allocator)) + return NULL; + else + return slot_allocator.lock; } - /** * Allocates an integer ID to be used for storing application-specific * data on any DBusConnection. The allocated ID may then be used @@ -2001,50 +2037,7 @@ _dbus_allocated_slots_init_lock (void) int dbus_connection_allocate_data_slot (void) { - int slot; - - if (!dbus_mutex_lock (allocated_slots_lock)) - return -1; - - if (n_used_slots < n_allocated_slots) - { - slot = 0; - while (slot < n_allocated_slots) - { - if (allocated_slots[slot] < 0) - { - allocated_slots[slot] = slot; - n_used_slots += 1; - break; - } - ++slot; - } - - _dbus_assert (slot < n_allocated_slots); - } - else - { - int *tmp; - - slot = -1; - tmp = dbus_realloc (allocated_slots, - sizeof (int) * (n_allocated_slots + 1)); - if (tmp == NULL) - goto out; - - allocated_slots = tmp; - slot = n_allocated_slots; - n_allocated_slots += 1; - n_used_slots += 1; - allocated_slots[slot] = slot; - } - - _dbus_assert (slot >= 0); - _dbus_assert (slot < n_allocated_slots); - - out: - dbus_mutex_unlock (allocated_slots_lock); - return slot; + return _dbus_data_slot_allocator_alloc (&slot_allocator); } /** @@ -2061,22 +2054,7 @@ dbus_connection_allocate_data_slot (void) void dbus_connection_free_data_slot (int slot) { - dbus_mutex_lock (allocated_slots_lock); - - _dbus_assert (slot < n_allocated_slots); - _dbus_assert (allocated_slots[slot] == slot); - - allocated_slots[slot] = -1; - n_used_slots -= 1; - - if (n_used_slots == 0) - { - dbus_free (allocated_slots); - allocated_slots = NULL; - n_allocated_slots = 0; - } - - dbus_mutex_unlock (allocated_slots_lock); + _dbus_data_slot_allocator_free (&slot_allocator, slot); } /** @@ -2100,50 +2078,25 @@ dbus_connection_set_data (DBusConnection *connection, { DBusFreeFunction old_free_func; void *old_data; + dbus_bool_t retval; dbus_mutex_lock (connection->mutex); - _dbus_assert (slot < n_allocated_slots); - _dbus_assert (allocated_slots[slot] == slot); - - if (slot >= connection->n_slots) - { - DBusDataSlot *tmp; - int i; - - tmp = dbus_realloc (connection->data_slots, - sizeof (DBusDataSlot) * (slot + 1)); - if (tmp == NULL) - { - dbus_mutex_unlock (connection->mutex); - return FALSE; - } - - connection->data_slots = tmp; - i = connection->n_slots; - connection->n_slots = slot + 1; - while (i < connection->n_slots) - { - connection->data_slots[i].data = NULL; - connection->data_slots[i].free_data_func = NULL; - ++i; - } - } - - _dbus_assert (slot < connection->n_slots); - - old_data = connection->data_slots[slot].data; - old_free_func = connection->data_slots[slot].free_data_func; - - connection->data_slots[slot].data = data; - connection->data_slots[slot].free_data_func = free_data_func; + retval = _dbus_data_slot_list_set (&slot_allocator, + &connection->slot_list, + slot, data, free_data_func, + &old_free_func, &old_data); + dbus_mutex_unlock (connection->mutex); - /* Do the actual free outside the connection lock */ - if (old_free_func) - (* old_free_func) (old_data); + if (retval) + { + /* Do the actual free outside the connection lock */ + if (old_free_func) + (* old_free_func) (old_data); + } - return TRUE; + return retval; } /** @@ -2161,15 +2114,11 @@ dbus_connection_get_data (DBusConnection *connection, void *res; dbus_mutex_lock (connection->mutex); - - _dbus_assert (slot < n_allocated_slots); - _dbus_assert (allocated_slots[slot] == slot); - - if (slot >= connection->n_slots) - res = NULL; - else - res = connection->data_slots[slot].data; + res = _dbus_data_slot_list_get (&slot_allocator, + &connection->slot_list, + slot); + dbus_mutex_unlock (connection->mutex); return res; @@ -2187,30 +2136,6 @@ dbus_connection_set_change_sigpipe (dbus_bool_t will_modify_sigpipe) _dbus_modify_sigpipe = will_modify_sigpipe; } -/* This must be called with the connection lock not held to avoid - * holding it over the free_data callbacks, so it can basically - * only be called at last unref - */ -static void -_dbus_connection_free_data_slots_nolock (DBusConnection *connection) -{ - int i; - - i = 0; - while (i < connection->n_slots) - { - if (connection->data_slots[i].free_data_func) - (* connection->data_slots[i].free_data_func) (connection->data_slots[i].data); - connection->data_slots[i].data = NULL; - connection->data_slots[i].free_data_func = NULL; - ++i; - } - - dbus_free (connection->data_slots); - connection->data_slots = NULL; - connection->n_slots = 0; -} - /** * Specifies the maximum size message this connection is allowed to * receive. Larger messages will result in disconnecting the diff --git a/dbus/dbus-dataslot.c b/dbus/dbus-dataslot.c new file mode 100644 index 00000000..a5909ffc --- /dev/null +++ b/dbus/dbus-dataslot.c @@ -0,0 +1,372 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-dataslot.c storing data on objects + * + * Copyright (C) 2003 Red Hat, Inc. + * + * Licensed under the Academic Free License version 1.2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include "dbus-dataslot.h" +#include "dbus-threads.h" + +/** + * @defgroup DBusDataSlot Data slots + * @ingroup DBusInternals + * @brief Storing data by ID + * + * Types and functions related to storing data by an + * allocated ID. This is used for dbus_connection_set_data(), + * dbus_server_set_data(), etc. + * @{ + */ + +/** + * Initializes a data slot allocator object, used to assign + * integer IDs for data slots. + * + * @param allocator the allocator to initialize + */ +dbus_bool_t +_dbus_data_slot_allocator_init (DBusDataSlotAllocator *allocator) +{ + allocator->allocated_slots = NULL; + allocator->n_allocated_slots = 0; + allocator->n_used_slots = 0; + allocator->lock = dbus_mutex_new (); + if (allocator->lock == NULL) + return FALSE; + else + return TRUE; +} + +/** + * Allocates an integer ID to be used for storing data + * in a #DBusDataSlotList. + * + * @param allocator the allocator + * @returns the integer ID, or -1 on failure + */ +int +_dbus_data_slot_allocator_alloc (DBusDataSlotAllocator *allocator) +{ + int slot; + + if (!dbus_mutex_lock (allocator->lock)) + return -1; + + if (allocator->n_used_slots < allocator->n_allocated_slots) + { + slot = 0; + while (slot < allocator->n_allocated_slots) + { + if (allocator->allocated_slots[slot] < 0) + { + allocator->allocated_slots[slot] = slot; + allocator->n_used_slots += 1; + break; + } + ++slot; + } + + _dbus_assert (slot < allocator->n_allocated_slots); + } + else + { + int *tmp; + + slot = -1; + tmp = dbus_realloc (allocator->allocated_slots, + sizeof (int) * (allocator->n_allocated_slots + 1)); + if (tmp == NULL) + goto out; + + allocator->allocated_slots = tmp; + slot = allocator->n_allocated_slots; + allocator->n_allocated_slots += 1; + allocator->n_used_slots += 1; + allocator->allocated_slots[slot] = slot; + } + + _dbus_assert (slot >= 0); + _dbus_assert (slot < allocator->n_allocated_slots); + + out: + dbus_mutex_unlock (allocator->lock); + return slot; +} + +/** + * Deallocates an ID previously allocated with + * _dbus_data_slot_allocator_alloc(). Existing data stored on + * existing #DBusDataList objects with this ID will be freed when the + * data list is finalized, but may not be retrieved (and may only be + * replaced if someone else reallocates the slot). + * + * @param allocator the allocator + * @param slot the slot to deallocate + */ +void +_dbus_data_slot_allocator_free (DBusDataSlotAllocator *allocator, + int slot) +{ + dbus_mutex_lock (allocator->lock); + + _dbus_assert (slot < allocator->n_allocated_slots); + _dbus_assert (allocator->allocated_slots[slot] == slot); + + allocator->allocated_slots[slot] = -1; + allocator->n_used_slots -= 1; + + if (allocator->n_used_slots == 0) + { + dbus_free (allocator->allocated_slots); + allocator->allocated_slots = NULL; + allocator->n_allocated_slots = 0; + } + + dbus_mutex_unlock (allocator->lock); +} + +/** + * Initializes a slot list. + * @param list the list to initialize. + */ +void +_dbus_data_slot_list_init (DBusDataSlotList *list) +{ + list->slots = NULL; + list->n_slots = 0; +} + +/** + * Stores a pointer in the data slot list, along with an optional + * function to be used for freeing the data when the data is set + * again, or when the slot list is finalized. The slot number must + * have been allocated with _dbus_data_slot_allocator_alloc() for the + * same allocator passed in here. The same allocator has to be used + * with the slot list every time. + * + * @param allocator the allocator to use + * @param list the data slot list + * @param slot the slot number + * @param data the data to store + * @param free_data_func finalizer function for the data + * @param old_free_func free function for any previously-existing data + * @param old_data previously-existing data, should be freed with old_free_func + * @returns #TRUE if there was enough memory to store the data + */ +dbus_bool_t +_dbus_data_slot_list_set (DBusDataSlotAllocator *allocator, + DBusDataSlotList *list, + int slot, + void *data, + DBusFreeFunction free_data_func, + DBusFreeFunction *old_free_func, + void **old_data) +{ + _dbus_assert (slot < allocator->n_allocated_slots); + _dbus_assert (allocator->allocated_slots[slot] == slot); + + if (slot >= list->n_slots) + { + DBusDataSlot *tmp; + int i; + + tmp = dbus_realloc (list->slots, + sizeof (DBusDataSlot) * (slot + 1)); + if (tmp == NULL) + return FALSE; + + list->slots = tmp; + i = list->n_slots; + list->n_slots = slot + 1; + while (i < list->n_slots) + { + list->slots[i].data = NULL; + list->slots[i].free_data_func = NULL; + ++i; + } + } + + _dbus_assert (slot < list->n_slots); + + *old_data = list->slots[slot].data; + *old_free_func = list->slots[slot].free_data_func; + + list->slots[slot].data = data; + list->slots[slot].free_data_func = free_data_func; + + return TRUE; +} + +/** + * Retrieves data previously set with _dbus_data_slot_list_set_data(). + * The slot must still be allocated (must not have been freed). + * + * @param allocator the allocator slot was allocated from + * @param list the data slot list + * @param slot the slot to get data from + * @returns the data, or #NULL if not found + */ +void* +_dbus_data_slot_list_get (DBusDataSlotAllocator *allocator, + DBusDataSlotList *list, + int slot) +{ + _dbus_assert (slot < allocator->n_allocated_slots); + _dbus_assert (allocator->allocated_slots[slot] == slot); + + if (slot >= list->n_slots) + return NULL; + else + return list->slots[slot].data; +} + +/** + * Frees the data slot list and all data slots contained + * in it, calling application-provided free functions + * if they exist. + * + * @param list the list to free + */ +void +_dbus_data_slot_list_free (DBusDataSlotList *list) +{ + int i; + + i = 0; + while (i < list->n_slots) + { + if (list->slots[i].free_data_func) + (* list->slots[i].free_data_func) (list->slots[i].data); + list->slots[i].data = NULL; + list->slots[i].free_data_func = NULL; + ++i; + } + + dbus_free (list->slots); + list->slots = NULL; + list->n_slots = 0; +} + +/** @} */ + +#ifdef DBUS_BUILD_TESTS +#include "dbus-test.h" +#include + +static int free_counter; + +static void +test_free_slot_data_func (void *data) +{ + int i = _DBUS_POINTER_TO_INT (data); + + _dbus_assert (free_counter == i); + ++free_counter; +} + +/** + * Test function for data slots + */ +dbus_bool_t +_dbus_data_slot_test (void) +{ + DBusDataSlotAllocator allocator; + DBusDataSlotList list; + int i; + DBusFreeFunction old_free_func; + void *old_data; + + if (!_dbus_data_slot_allocator_init (&allocator)) + _dbus_assert_not_reached ("no memory for allocator"); + + _dbus_data_slot_list_init (&list); + +#define N_SLOTS 100 + + i = 0; + while (i < N_SLOTS) + { + /* we don't really want apps to rely on this ordered + * allocation, but it simplifies things to rely on it + * here. + */ + if (_dbus_data_slot_allocator_alloc (&allocator) != i) + _dbus_assert_not_reached ("did not allocate slots in numeric order\n"); + + ++i; + } + + i = 0; + while (i < N_SLOTS) + { + if (!_dbus_data_slot_list_set (&allocator, &list, + i, + _DBUS_INT_TO_POINTER (i), + test_free_slot_data_func, + &old_free_func, &old_data)) + _dbus_assert_not_reached ("no memory to set data"); + + _dbus_assert (old_free_func == NULL); + _dbus_assert (old_data == NULL); + + _dbus_assert (_dbus_data_slot_list_get (&allocator, &list, i) == + _DBUS_INT_TO_POINTER (i)); + + ++i; + } + + free_counter = 0; + i = 0; + while (i < N_SLOTS) + { + if (!_dbus_data_slot_list_set (&allocator, &list, + i, + _DBUS_INT_TO_POINTER (i), + test_free_slot_data_func, + &old_free_func, &old_data)) + _dbus_assert_not_reached ("no memory to set data"); + + _dbus_assert (old_free_func == test_free_slot_data_func); + _dbus_assert (_DBUS_POINTER_TO_INT (old_data) == i); + + (* old_free_func) (old_data); + _dbus_assert (i == (free_counter - 1)); + + _dbus_assert (_dbus_data_slot_list_get (&allocator, &list, i) == + _DBUS_INT_TO_POINTER (i)); + + ++i; + } + + free_counter = 0; + _dbus_data_slot_list_free (&list); + + _dbus_assert (N_SLOTS == free_counter); + + i = 0; + while (i < N_SLOTS) + { + _dbus_data_slot_allocator_free (&allocator, i); + ++i; + } + + return TRUE; +} + +#endif /* DBUS_BUILD_TESTS */ diff --git a/dbus/dbus-dataslot.h b/dbus/dbus-dataslot.h new file mode 100644 index 00000000..4bb6091f --- /dev/null +++ b/dbus/dbus-dataslot.h @@ -0,0 +1,76 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-dataslot.h storing data on objects + * + * Copyright (C) 2003 Red Hat, Inc. + * + * Licensed under the Academic Free License version 1.2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef DBUS_DATASLOT_H +#define DBUS_DATASLOT_H + +#include + +DBUS_BEGIN_DECLS; + +typedef struct DBusDataSlotAllocator DBusDataSlotAllocator; +typedef struct DBusDataSlotList DBusDataSlotList; + +/** Opaque typedef for DBusDataSlot */ +typedef struct DBusDataSlot DBusDataSlot; +/** DBusDataSlot is used to store application data on the connection */ +struct DBusDataSlot +{ + void *data; /**< The application data */ + DBusFreeFunction free_data_func; /**< Free the application data */ +}; + +struct DBusDataSlotAllocator +{ + int *allocated_slots; /**< Allocated slots */ + int n_allocated_slots; /**< number of slots malloc'd */ + int n_used_slots; /**< number of slots used */ + DBusMutex *lock; /**< thread lock */ +}; + +struct DBusDataSlotList +{ + DBusDataSlot *slots; /**< Data slots */ + int n_slots; /**< Slots we have storage for in data_slots */ +}; + +dbus_bool_t _dbus_data_slot_allocator_init (DBusDataSlotAllocator *allocator); +int _dbus_data_slot_allocator_alloc (DBusDataSlotAllocator *allocator); +void _dbus_data_slot_allocator_free (DBusDataSlotAllocator *allocator, + int slot_id); + +void _dbus_data_slot_list_init (DBusDataSlotList *list); +dbus_bool_t _dbus_data_slot_list_set (DBusDataSlotAllocator *allocator, + DBusDataSlotList *list, + int slot, + void *data, + DBusFreeFunction free_data_func, + DBusFreeFunction *old_free_func, + void **old_data); +void* _dbus_data_slot_list_get (DBusDataSlotAllocator *allocator, + DBusDataSlotList *list, + int slot); +void _dbus_data_slot_list_free (DBusDataSlotList *list); + +DBUS_END_DECLS; + +#endif /* DBUS_DATASLOT_H */ diff --git a/dbus/dbus-internals.h b/dbus/dbus-internals.h index 551a377f..d928a5c8 100644 --- a/dbus/dbus-internals.h +++ b/dbus/dbus-internals.h @@ -118,6 +118,8 @@ char* _dbus_strdup (const char *str); #define _DBUS_MAX_SUN_PATH_LENGTH 99 #define _DBUS_ONE_KILOBYTE 1024 #define _DBUS_ONE_MEGABYTE 1024 * _DBUS_ONE_KILOBYTE +#define _DBUS_ONE_HOUR_IN_MILLISECONDS (1000 * 60 * 60) +#define _DBUS_USEC_PER_SECOND (1000000) #undef MAX #define MAX(a, b) (((a) > (b)) ? (a) : (b)) @@ -155,12 +157,12 @@ dbus_bool_t _dbus_decrement_fail_alloc_counter (void); #endif /* !DBUS_BUILD_TESTS */ /* Thread initializers */ -DBusMutex *_dbus_list_init_lock (void); -DBusMutex *_dbus_allocated_slots_init_lock (void); -DBusMutex *_dbus_atomic_init_lock (void); -DBusMutex *_dbus_message_handler_init_lock (void); -DBusMutex *_dbus_user_info_init_lock (void); - +DBusMutex *_dbus_list_init_lock (void); +DBusMutex *_dbus_connection_slots_init_lock (void); +DBusMutex *_dbus_server_slots_init_lock (void); +DBusMutex *_dbus_atomic_init_lock (void); +DBusMutex *_dbus_message_handler_init_lock (void); +DBusMutex *_dbus_user_info_init_lock (void); DBUS_END_DECLS; diff --git a/dbus/dbus-keyring.c b/dbus/dbus-keyring.c index ea20c9c4..2162e296 100644 --- a/dbus/dbus-keyring.c +++ b/dbus/dbus-keyring.c @@ -168,7 +168,7 @@ free_keys (DBusKey *keys, */ /** Maximum number of timeouts waiting for lock before we decide it's stale */ -#define MAX_LOCK_TIMEOUTS 16 +#define MAX_LOCK_TIMEOUTS 32 /** Length of each timeout while waiting for a lock */ #define LOCK_TIMEOUT_MILLISECONDS 250 diff --git a/dbus/dbus-message.c b/dbus/dbus-message.c index 584acc7e..355d6310 100644 --- a/dbus/dbus-message.c +++ b/dbus/dbus-message.c @@ -184,8 +184,12 @@ get_string_field (DBusMessage *message, int field, int *len) { - int offset = message->header_fields[field].offset; + int offset; const char *data; + + offset = message->header_fields[field].offset; + + _dbus_assert (field < FIELD_LAST); if (offset < 0) return NULL; @@ -209,13 +213,17 @@ get_string_field (DBusMessage *message, static dbus_int32_t get_int_field (DBusMessage *message, - int field) + int field) { - int offset = message->header_fields[field].offset; + int offset; + + _dbus_assert (field < FIELD_LAST); + + offset = message->header_fields[field].offset; if (offset < 0) return -1; /* useless if -1 is a valid value of course */ - + return _dbus_demarshal_int32 (&message->header, message->byte_order, offset, @@ -798,8 +806,8 @@ dbus_message_new_reply (DBusMessage *original_message) FIELD_SENDER, NULL); name = get_string_field (original_message, FIELD_NAME, NULL); - - _dbus_assert (sender != NULL); + + /* sender is allowed to be null here in peer-to-peer case */ message = dbus_message_new (sender, name); @@ -1703,6 +1711,10 @@ dbus_message_get_args_valist (DBusMessage *message, * ref/unref is kind of annoying to deal with, and slower too. * This implies not ref'ing the message from the iter. * + * @todo I'd also name this dbus_message_iter_new() or + * for the static object dbus_message_iter_init() rather + * than making it a method on the message + * * @param message the message * @returns a new iter. */ diff --git a/dbus/dbus-server-protected.h b/dbus/dbus-server-protected.h index f155bbb8..24b805c4 100644 --- a/dbus/dbus-server-protected.h +++ b/dbus/dbus-server-protected.h @@ -28,6 +28,7 @@ #include #include #include +#include DBUS_BEGIN_DECLS; @@ -61,6 +62,8 @@ struct DBusServer */ int max_connections; /**< Max number of connections allowed at once. */ + + DBusDataSlotList slot_list; /**< Data stored by allocated integer ID */ DBusNewConnectionFunction new_connection_function; /**< Callback to invoke when a new connection is created. */ diff --git a/dbus/dbus-server.c b/dbus/dbus-server.c index 01435e1a..0065e510 100644 --- a/dbus/dbus-server.c +++ b/dbus/dbus-server.c @@ -1,7 +1,7 @@ /* -*- mode: C; c-file-style: "gnu" -*- */ /* dbus-server.c DBusServer object * - * Copyright (C) 2002 Red Hat Inc. + * Copyright (C) 2002, 2003 Red Hat Inc. * * Licensed under the Academic Free License version 1.2 * @@ -37,6 +37,9 @@ * A DBusServer represents a server that other applications * can connect to. Each connection from another application * is represented by a DBusConnection. + * + * @todo Thread safety hasn't been looked at for #DBusServer + * @todo Need notification to apps of disconnection, may matter for some transports */ /** @@ -86,6 +89,8 @@ _dbus_server_init_base (DBusServer *server, } server->max_connections = 256; /* same as an X server, seems like a nice default */ + + _dbus_data_slot_list_init (&server->slot_list); return TRUE; } @@ -99,6 +104,9 @@ _dbus_server_init_base (DBusServer *server, void _dbus_server_finalize_base (DBusServer *server) { + /* calls out to application code... */ + _dbus_data_slot_list_free (&server->slot_list); + dbus_server_set_new_connection_function (server, NULL, NULL, NULL); if (!server->disconnected) @@ -510,5 +518,131 @@ dbus_server_get_n_connections (DBusServer *server) return _dbus_counter_get_value (server->connection_counter); } + +static DBusDataSlotAllocator slot_allocator; + +/** + * Initialize the mutex used for #DBusConnection data + * slot reservations. + * + * @returns the mutex + */ +DBusMutex * +_dbus_server_slots_init_lock (void) +{ + if (!_dbus_data_slot_allocator_init (&slot_allocator)) + return NULL; + else + return slot_allocator.lock; +} + +/** + * Allocates an integer ID to be used for storing application-specific + * data on any DBusServer. The allocated ID may then be used + * with dbus_server_set_data() and dbus_server_get_data(). + * If allocation fails, -1 is returned. Again, the allocated + * slot is global, i.e. all DBusServer objects will + * have a slot with the given integer ID reserved. + * + * @returns -1 on failure, otherwise the data slot ID + */ +int +dbus_server_allocate_data_slot (void) +{ + return _dbus_data_slot_allocator_alloc (&slot_allocator); +} + +/** + * Deallocates a global ID for server data slots. + * dbus_server_get_data() and dbus_server_set_data() + * may no longer be used with this slot. + * Existing data stored on existing DBusServer objects + * will be freed when the server is finalized, + * but may not be retrieved (and may only be replaced + * if someone else reallocates the slot). + * + * @param slot the slot to deallocate + */ +void +dbus_server_free_data_slot (int slot) +{ + _dbus_data_slot_allocator_free (&slot_allocator, slot); +} + +/** + * Stores a pointer on a DBusServer, along + * with an optional function to be used for freeing + * the data when the data is set again, or when + * the server is finalized. The slot number + * must have been allocated with dbus_server_allocate_data_slot(). + * + * @param server the server + * @param slot the slot number + * @param data the data to store + * @param free_data_func finalizer function for the data + * @returns #TRUE if there was enough memory to store the data + */ +dbus_bool_t +dbus_server_set_data (DBusServer *server, + int slot, + void *data, + DBusFreeFunction free_data_func) +{ + DBusFreeFunction old_free_func; + void *old_data; + dbus_bool_t retval; + +#if 0 + dbus_mutex_lock (server->mutex); +#endif + + retval = _dbus_data_slot_list_set (&slot_allocator, + &server->slot_list, + slot, data, free_data_func, + &old_free_func, &old_data); + +#if 0 + dbus_mutex_unlock (server->mutex); +#endif + + if (retval) + { + /* Do the actual free outside the server lock */ + if (old_free_func) + (* old_free_func) (old_data); + } + + return retval; +} + +/** + * Retrieves data previously set with dbus_server_set_data(). + * The slot must still be allocated (must not have been freed). + * + * @param server the server + * @param slot the slot to get data from + * @returns the data, or #NULL if not found + */ +void* +dbus_server_get_data (DBusServer *server, + int slot) +{ + void *res; + +#if 0 + dbus_mutex_lock (server->mutex); +#endif + + res = _dbus_data_slot_list_get (&slot_allocator, + &server->slot_list, + slot); + +#if 0 + dbus_mutex_unlock (server->mutex); +#endif + + return res; +} + /** @} */ diff --git a/dbus/dbus-server.h b/dbus/dbus-server.h index 68d95006..fc3a57e7 100644 --- a/dbus/dbus-server.h +++ b/dbus/dbus-server.h @@ -71,6 +71,15 @@ int dbus_server_get_max_connections (DBusServer *server) int dbus_server_get_n_connections (DBusServer *server); +int dbus_server_allocate_data_slot (void); +void dbus_server_free_data_slot (int slot); +dbus_bool_t dbus_server_set_data (DBusServer *server, + int slot, + void *data, + DBusFreeFunction free_data_func); +void* dbus_server_get_data (DBusServer *server, + int slot); + DBUS_END_DECLS; #endif /* DBUS_SERVER_H */ diff --git a/dbus/dbus-test.c b/dbus/dbus-test.c index b1f8ac1d..29ad855d 100644 --- a/dbus/dbus-test.c +++ b/dbus/dbus-test.c @@ -59,6 +59,10 @@ dbus_internal_do_not_use_run_tests (const char *test_data_dir) if (!_dbus_string_test ()) die ("strings"); + printf ("%s: running data slot tests\n", "dbus-test"); + if (!_dbus_data_slot_test ()) + die ("dataslot"); + printf ("%s: running keyring tests\n", "dbus-test"); if (!_dbus_keyring_test ()) die ("keyring"); diff --git a/dbus/dbus-test.h b/dbus/dbus-test.h index b5e38480..2fc2901b 100644 --- a/dbus/dbus-test.h +++ b/dbus/dbus-test.h @@ -35,17 +35,18 @@ typedef enum _DBUS_MESSAGE_UNKNOWN } DBusMessageValidity; -dbus_bool_t _dbus_hash_test (void); -dbus_bool_t _dbus_list_test (void); -dbus_bool_t _dbus_marshal_test (void); -dbus_bool_t _dbus_mem_pool_test (void); -dbus_bool_t _dbus_string_test (void); -dbus_bool_t _dbus_address_test (void); -dbus_bool_t _dbus_message_test (const char *test_data_dir); -dbus_bool_t _dbus_auth_test (const char *test_data_dir); -dbus_bool_t _dbus_md5_test (void); -dbus_bool_t _dbus_sha_test (const char *test_data_dir); -dbus_bool_t _dbus_keyring_test (void); +dbus_bool_t _dbus_hash_test (void); +dbus_bool_t _dbus_list_test (void); +dbus_bool_t _dbus_marshal_test (void); +dbus_bool_t _dbus_mem_pool_test (void); +dbus_bool_t _dbus_string_test (void); +dbus_bool_t _dbus_address_test (void); +dbus_bool_t _dbus_message_test (const char *test_data_dir); +dbus_bool_t _dbus_auth_test (const char *test_data_dir); +dbus_bool_t _dbus_md5_test (void); +dbus_bool_t _dbus_sha_test (const char *test_data_dir); +dbus_bool_t _dbus_keyring_test (void); +dbus_bool_t _dbus_data_slot_test (void); void dbus_internal_do_not_use_run_tests (const char *test_data_dir); dbus_bool_t dbus_internal_do_not_use_try_message_file (const DBusString *filename, diff --git a/dbus/dbus-threads.c b/dbus/dbus-threads.c index 2a903a27..08cebf50 100644 --- a/dbus/dbus-threads.c +++ b/dbus/dbus-threads.c @@ -205,7 +205,8 @@ init_static_locks(void) DBusMutex *mutex; } static_locks[] = { {&_dbus_list_init_lock}, - {&_dbus_allocated_slots_init_lock}, + {&_dbus_server_slots_init_lock}, + {&_dbus_connection_slots_init_lock}, {&_dbus_atomic_init_lock}, {&_dbus_message_handler_init_lock}, {&_dbus_user_info_init_lock} diff --git a/dbus/dbus-transport-unix.c b/dbus/dbus-transport-unix.c index b30dc1d6..81c18b4a 100644 --- a/dbus/dbus-transport-unix.c +++ b/dbus/dbus-transport-unix.c @@ -157,7 +157,12 @@ check_write_watch (DBusTransport *transport) _dbus_watch_unref (unix_transport->write_watch); unix_transport->write_watch = NULL; } - + else + { + _dbus_verbose ("Write watch is unchanged from %p on fd %d\n", + unix_transport->write_watch, unix_transport->fd); + } + out: _dbus_transport_unref (transport); } @@ -173,9 +178,16 @@ check_read_watch (DBusTransport *transport) _dbus_transport_ref (transport); - need_read_watch = - _dbus_counter_get_value (transport->live_messages_size) < transport->max_live_messages_size; + if (_dbus_transport_get_is_authenticated (transport)) + need_read_watch = + _dbus_counter_get_value (transport->live_messages_size) < transport->max_live_messages_size; + else + need_read_watch = transport->receive_credentials_pending || + _dbus_auth_do_work (transport->auth) == DBUS_AUTH_STATE_WAITING_FOR_INPUT; + _dbus_verbose ("need_read_watch = %d authenticated = %d\n", + need_read_watch, _dbus_transport_get_is_authenticated (transport)); + if (transport->disconnected) need_read_watch = FALSE; @@ -213,7 +225,12 @@ check_read_watch (DBusTransport *transport) _dbus_watch_unref (unix_transport->read_watch); unix_transport->read_watch = NULL; } - + else + { + _dbus_verbose ("Read watch is unchanged from %p on fd %d\n", + unix_transport->read_watch, unix_transport->fd); + } + out: _dbus_transport_unref (transport); } @@ -552,6 +569,7 @@ do_authentication (DBusTransport *transport, } out: + check_read_watch (transport); check_write_watch (transport); _dbus_transport_unref (transport); } @@ -902,10 +920,12 @@ unix_do_iteration (DBusTransport *transport, dbus_bool_t do_select; int select_res; - _dbus_verbose (" iteration flags = %s%s timeout = %d\n", + _dbus_verbose (" iteration flags = %s%s timeout = %d read_watch = %p write_watch = %p\n", flags & DBUS_ITERATION_DO_READING ? "read" : "", flags & DBUS_ITERATION_DO_WRITING ? "write" : "", - timeout_milliseconds); + timeout_milliseconds, + unix_transport->read_watch, + unix_transport->write_watch); /* "again" has to be up here because on EINTR the fd sets become * undefined @@ -948,13 +968,15 @@ unix_do_iteration (DBusTransport *transport, auth_state = _dbus_auth_do_work (transport->auth); - if (auth_state == DBUS_AUTH_STATE_WAITING_FOR_INPUT) + if (transport->receive_credentials_pending || + auth_state == DBUS_AUTH_STATE_WAITING_FOR_INPUT) { FD_SET (unix_transport->fd, &read_set); do_select = TRUE; } - if (auth_state == DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND) + if (transport->send_credentials_pending || + auth_state == DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND) { FD_SET (unix_transport->fd, &write_set); do_select = TRUE; diff --git a/dbus/dbus-transport.c b/dbus/dbus-transport.c index 1f34b3bb..fc7fd33f 100644 --- a/dbus/dbus-transport.c +++ b/dbus/dbus-transport.c @@ -485,6 +485,9 @@ _dbus_transport_do_iteration (DBusTransport *transport, { _dbus_assert (transport->vtable->do_iteration != NULL); + _dbus_verbose ("Transport iteration flags 0x%x timeout %d connected = %d\n", + flags, timeout_milliseconds, !transport->disconnected); + if ((flags & (DBUS_ITERATION_DO_WRITING | DBUS_ITERATION_DO_READING)) == 0) return; /* Nothing to do */ -- cgit