Merge branch 'bus_transaction_xxx-docs' into 'main'

Document bus_transaction_send() and bus_transaction_send_from_driver()

Closes #298

See merge request dbus/dbus!160
This commit is contained in:
Ralf Habacker 2025-09-16 14:52:50 +00:00
commit 480e23f788
3 changed files with 141 additions and 4 deletions

View file

@ -53,6 +53,10 @@
#include <signal.h>
#endif
/** @defgroup BusInternals
* @brief Internals of the dbus server implementation
*/
struct BusContext
{
int refcount;

View file

@ -2257,6 +2257,65 @@ free_cancel_hooks (BusTransaction *transaction)
_dbus_list_clear (&transaction->cancel_hooks);
}
/**
* @defgroup BusTransaction
* @ingroup BusInternals
* @brief Symbolizes a unit of work performed within the dbus server
* and treated in a coherent and reliable way independent of other transactions
*
* The purpose of BusTransaction is to avoid this situation:
*
* - We dispatch a message
* - Dispatching that message results in new messages
* -# it could be a message from a sender, through the dbus-daemon,
* to one or more destinations (broadcast signals can go to several
* destinations, and legacy eavesdropping and the more recent
* monitoring can also result in unicast messages having to go
* to more than one destination)
* -# or it could be a message from a sender to the dbus-daemon itself
* (the "bus driver", org.freedesktop.DBus) that sends other
* messages as a side-effect (for example a successful RequestName()
* results in NameOwnerChanged and NameAcquired signals, in addition
* to the reply to RequestName)
* - We send one of the resulting messages (let's say M1), and it succeeds
* - We try to send another of the resulting messages (let's say M2), but
* we run out of memory and cannot send it
* - Now what?
* -# We can't take back M1, because we already sent it
* -# We can't send M2, because we don't have enough memory
* -# Sending M1 but not M2 would leave recipients with an inconsistent
* view of the state of the world
*
* So, instead, we use transactions. They're conceptually similar to
* database transactions: you start with an empty transaction, you add
* actions (in our case messages), and eventually you either execute the
* transaction or cancel it (similar to committing or rolling back a
* database transaction).
* To make sure we have enough memory, every time we add a message, we
* preallocate enough memory to "send" that message. When we execute
* (commit) the transaction, we use that memory to "send" the messages.
* I say "send" here because dbus-daemon is not actually aware of the
* OS-level reality of what is happening to the messages: it's just
* using the libdbus abstractions. Even after we execute a BusTransaction,
* we still aren't actually physically sending the message! All we are
* doing is moving the message from the linked list in the BusTransaction
* to the linked list in the DBusConnection. We cannot actually start
* sending the message until there is enough space for at least its first
* byte in the AF_UNIX or TCP socket's buffers, which is determined by
* poll(), select() or epoll_wait() - and we cannot finish sending the
* message until there is enough space for its last byte, which might
* take a lot of iterations for a large message.
* This gap between dbus_connection_send() and OS-level send() is described
* in the @ref use_dbus_connection_send "DBusConnection introduction".
* @{
*/
/**
* Creates a new instance of type @ref BusTransaction
*
* @param context bus context to add to the transaction
* @return allocated memory, or #NULL if the allocation fails.
*/
BusTransaction*
bus_transaction_new (BusContext *context)
{
@ -2271,6 +2330,12 @@ bus_transaction_new (BusContext *context)
return transaction;
}
/**
* Return bus context from the given @ref BusTransaction instance
*
* @param transaction transaction to get the context from
* @return bus context
*/
BusContext*
bus_transaction_get_context (BusTransaction *transaction)
{
@ -2361,6 +2426,27 @@ out:
return ret;
}
/**
* @brief Adds a message from the bus driver to be sent when the transaction is executed
*
* The function prepends a message from a bus driver to a transcation
* to be sent in a later main-loop iteration and does not expect
* to get a reply.
*
* If security policy doesn't allow the message, we would silently
* eat it; the driver doesn't care about getting a reply. However,
* if we're actively capturing messages, it's nice to log that we
* tried to send it and did not allow ourselves to do so.
*
*
* @param transaction transaction to prepend to message
* @param connection connection to send the message to
* @param message the message to send
* @return TRUE message is added to transaction or ignored
* @return FALSE message is not added to the transaction
*
* See bus_transaction_send() and @ref BusTransaction for further details.
*/
dbus_bool_t
bus_transaction_send_from_driver (BusTransaction *transaction,
DBusConnection *connection,
@ -2441,6 +2527,25 @@ bus_transaction_send_from_driver (BusTransaction *transaction,
return bus_transaction_send (transaction, NULL, connection, message);
}
/**
* @brief Adds a message to be sent when the transaction is executed
*
* Add a message from @sender to the transaction. If there is enough
* memory to process the entire transaction, the message will be sent to
* @destination when the transaction is executed. If not, it will be dropped
* when the transaction is cancelled.
*
* If the destination is disconnected, the message is silently ignored.
* This is treated as a success, and #TRUE is returned.
*
* @param transaction transaction to prepend to message
* @param sender sender for the message
* @param destination where to send the message to
* @param message the message to send
* @return #TRUE on success, or #FALSE if not enough memory.
*
* See @ref BusTransaction for further details.
*/
dbus_bool_t
bus_transaction_send (BusTransaction *transaction,
DBusConnection *sender,
@ -2550,8 +2655,13 @@ bus_transaction_send (BusTransaction *transaction,
return TRUE;
}
/**
* Destroy @ref BusTransaction instance
*
* @param transaction instance to destroy
*/
static void
transaction_free (BusTransaction *transaction)
bus_transaction_free (BusTransaction *transaction)
{
_dbus_assert (transaction->connections == NULL);
@ -2560,6 +2670,8 @@ transaction_free (BusTransaction *transaction)
dbus_free (transaction);
}
/** @} */ /* end of BusTransaction */
static void
connection_cancel_transaction (DBusConnection *connection,
BusTransaction *transaction)
@ -2588,6 +2700,11 @@ connection_cancel_transaction (DBusConnection *connection,
}
}
/**
* @addtogroup BusTransaction
* @{
*/
void
bus_transaction_cancel_and_free (BusTransaction *transaction)
{
@ -2601,9 +2718,11 @@ bus_transaction_cancel_and_free (BusTransaction *transaction)
_dbus_list_foreach (&transaction->cancel_hooks,
cancel_hook_cancel, NULL);
transaction_free (transaction);
bus_transaction_free (transaction);
}
/** @} */ /* end of BusTransaction */
static void
connection_execute_transaction (DBusConnection *connection,
BusTransaction *transaction)
@ -2642,6 +2761,11 @@ connection_execute_transaction (DBusConnection *connection,
}
}
/**
* @addtogroup BusTransaction
* @{
*/
void
bus_transaction_execute_and_free (BusTransaction *transaction)
{
@ -2655,9 +2779,11 @@ bus_transaction_execute_and_free (BusTransaction *transaction)
while ((connection = _dbus_list_pop_first (&transaction->connections)))
connection_execute_transaction (connection, transaction);
transaction_free (transaction);
bus_transaction_free (transaction);
}
/** @} */ /* end of BusTransaction */
static void
bus_connection_remove_transactions (DBusConnection *connection)
{
@ -2678,6 +2804,11 @@ bus_connection_remove_transactions (DBusConnection *connection)
}
}
/**
* @addtogroup BusTransaction
* @{
*/
/**
* Converts the DBusError to a message reply
*/
@ -2743,6 +2874,8 @@ bus_transaction_add_cancel_hook (BusTransaction *transaction,
return TRUE;
}
/** @} */ /* end of BusTransaction */
int
bus_connections_get_n_active (BusConnections *connections)
{

View file

@ -154,7 +154,7 @@
* dbus_connection_add_filter(),
* dbus_connection_register_object_path() for more on handlers).
*
* When you use dbus_connection_send() or one of its variants to send
* @anchor use_dbus_connection_send When you use dbus_connection_send() or one of its variants to send
* a message, the message is added to the outgoing queue. It's
* actually written to the network later; either in
* dbus_watch_handle() invoked by your main loop, or in