dbus/test/watch.c
Alexander Larsson fe40189411 2003-02-15 Alexander Larsson <alexl@redhat.com>
* dbus/dbus-threads.c:
	* dbus/dbus-threads.h:
	Add condvars. Remove static mutext from API.
	Implement static mutexes by initializing them from threads_init.

	* glib/dbus-gthread.c:
	* qt/dbus-qthread.cpp:
	Update with the thread api changes.


	* dbus/dbus-list.c:
	* dbus/dbus-list.h:
	Turn StaticMutex into normal mutex + init function.
	Export new functions _dbus_list_alloc_link, _dbus_list_free_link,
	_dbus_list_append_link, _dbus_list_prepend_link


	* dbus/dbus-sysdeps.c:
	* dbus/dbus-sysdeps.h:
	New type dbus_atomic_t, and new functions _dbus_atomic_inc,
	_dbus_atomic_dec. Only slow fallback implementation at the moment.

	* dbus/dbus-protocol.h:
	Add DBUS_MESSAGE_LOCAL_DISCONNECT define

	* dbus/dbus-message.c:
	Make ref/unref atomic.
	Fix some docs.

	* dbus/dbus-connection-internal.h:
	* dbus/dbus-connection.c:
	* dbus/dbus-connection.h:
	Make threadsafe.
	Change _peek to _borrow,_return & _steal_borrowed.
	Change disconnect callback to event.
	Make dbus_connection_dispatch_messages reentrant.

	* dbus/dbus-transport.c:
	Don't ref the connection on calls to the transport
	implementation.

	* dbus/dbus-message-handler.c:
	Make threadsafe.

	* glib/dbus-gmain.c:
	Don't use peek_message anymore

	* test/Makefile.am:
	* test/debug-thread.c:
	* test/debug-thread.h:
	Simple thread implementation that asserts() on deadlocks in
	single-threaded code.

	* test/bus-test.c:
	(main) Call debug_threads_init.

	* test/watch.c:
	Use disconnect message instead of disconnect callback.

	* bus/connection.c:
	* bus/connection.h:
	Don't call dbus_connection_set_disconnect_function. Instead export
	bus_connection_disconnect.

	* bus/dispatch.c:
	Call bus_connection_disconnect when we get a disconnected message.
2003-02-15 16:25:08 +00:00

345 lines
8.2 KiB
C

#include "watch.h"
#include <stdio.h>
#define DBUS_COMPILATION /* cheat and use DBusList */
#include <dbus/dbus-list.h>
#undef DBUS_COMPILATION
#include <sys/types.h>
#include <unistd.h>
/* Cheesy main loop used in test programs. Any real app would use the
* GLib or Qt or other non-sucky main loops.
*/
#undef MAX
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
static int watch_list_serial = 0;
static DBusList *watches = NULL;
static dbus_bool_t exited = FALSE;
static DBusList *connections = NULL;
typedef enum
{
WATCH_CONNECTION,
WATCH_SERVER
} WatchType;
typedef struct
{
WatchType type;
void *data;
} WatchData;
static void
free_watch_data (void *data)
{
WatchData *wd = data;
if (wd->type == WATCH_CONNECTION)
dbus_connection_unref (wd->data);
else if (wd->type == WATCH_SERVER)
dbus_server_unref (wd->data);
dbus_free (wd);
}
static void
add_connection_watch (DBusWatch *watch,
DBusConnection *connection)
{
WatchData *wd;
wd = dbus_new0 (WatchData, 1);
wd->type = WATCH_CONNECTION;
wd->data = connection;
dbus_connection_ref (connection);
_dbus_list_append (&watches, watch);
dbus_watch_set_data (watch, wd, free_watch_data);
watch_list_serial += 1;
#if 0
printf ("Added connection %swatch for fd %d\n",
dbus_watch_get_flags (watch) & DBUS_WATCH_WRITABLE ? "write " : "",
dbus_watch_get_fd (watch));
#endif
}
static void
remove_connection_watch (DBusWatch *watch,
DBusConnection *connection)
{
if (!_dbus_list_remove (&watches, watch))
_dbus_assert_not_reached ("removed nonexistent watch");
dbus_watch_set_data (watch, NULL, NULL);
watch_list_serial += 1;
#if 0
printf ("Removed connection watch for fd %d\n",
dbus_watch_get_fd (watch));
#endif
}
static void
add_server_watch (DBusWatch *watch,
DBusServer *server)
{
WatchData *wd;
wd = dbus_new0 (WatchData, 1);
wd->type = WATCH_SERVER;
wd->data = server;
dbus_server_ref (server);
_dbus_list_append (&watches, watch);
dbus_watch_set_data (watch, wd, free_watch_data);
watch_list_serial += 1;
#if 0
printf ("Added server %swatch for fd %d\n",
dbus_watch_get_flags (watch) & DBUS_WATCH_WRITABLE ? "write " : "",
dbus_watch_get_fd (watch));
#endif
}
static void
remove_server_watch (DBusWatch *watch,
DBusServer *server)
{
if (!_dbus_list_remove (&watches, watch))
_dbus_assert_not_reached ("removed nonexistent server watch");
dbus_watch_set_data (watch, NULL, NULL);
watch_list_serial += 1;
#if 0
printf ("Removed server watch for fd %d\n",
dbus_watch_get_fd (watch));
#endif
}
static int count = 0;
static void
disconnect (DBusConnection *connection)
{
fprintf (stderr, "Disconnected\n");
_dbus_list_remove (&connections, connection);
dbus_connection_unref (connection);
quit_mainloop ();
}
static void
check_messages (void)
{
DBusList *link;
link = _dbus_list_get_first_link (&connections);
while (link != NULL)
{
DBusList *next = _dbus_list_get_next_link (&connections, link);
DBusConnection *connection = link->data;
DBusMessage *message;
const char *name;
while ((message = dbus_connection_pop_message (connection)))
{
DBusMessage *reply;
name = dbus_message_get_name (message);
if (name && strcmp (name, DBUS_MESSAGE_LOCAL_DISCONNECT) == 0)
{
disconnect (connection);
}
else
{
fprintf (stderr, "Received message %d, sending reply\n", count);
reply = dbus_message_new ("org.freedesktop.DBus.Test", "org.freedesktop.DBus.Test");
dbus_connection_send_message (connection,
reply,
NULL,
NULL);
dbus_message_unref (reply);
dbus_message_unref (message);
count += 1;
if (count > 100)
{
printf ("Saw %d messages, exiting\n", count);
quit_mainloop ();
}
}
}
link = next;
}
}
void
do_mainloop (void)
{
/* Of course with any real app you'd use GMainLoop or
* QSocketNotifier and not have to see all this crap.
*/
while (!exited && watches != NULL)
{
fd_set read_set;
fd_set write_set;
fd_set err_set;
int max_fd;
DBusList *link;
int initial_watch_serial;
check_messages ();
if (exited)
break;
FD_ZERO (&read_set);
FD_ZERO (&write_set);
FD_ZERO (&err_set);
max_fd = -1;
link = _dbus_list_get_first_link (&watches);
while (link != NULL)
{
DBusList *next = _dbus_list_get_next_link (&watches, link);
int fd;
DBusWatch *watch;
unsigned int flags;
watch = link->data;
fd = dbus_watch_get_fd (watch);
flags = dbus_watch_get_flags (watch);
max_fd = MAX (max_fd, fd);
if (flags & DBUS_WATCH_READABLE)
FD_SET (fd, &read_set);
if (flags & DBUS_WATCH_WRITABLE)
FD_SET (fd, &write_set);
FD_SET (fd, &err_set);
link = next;
}
select (max_fd + 1, &read_set, &write_set, &err_set, NULL);
initial_watch_serial = watch_list_serial;
link = _dbus_list_get_first_link (&watches);
while (link != NULL)
{
DBusList *next = _dbus_list_get_next_link (&watches, link);
int fd;
DBusWatch *watch;
unsigned int flags;
unsigned int condition;
if (initial_watch_serial != watch_list_serial)
{
/* Watches were added/removed,
* hosing our list; break out of here
*/
/* A more elegant solution might be to ref
* all watches, then check which have fd >= 0
* as we iterate over them, since removed
* watches have their fd invalidated.
*/
printf ("Aborting watch iteration due to serial increment\n");
break;
}
watch = link->data;
fd = dbus_watch_get_fd (watch);
flags = dbus_watch_get_flags (watch);
condition = 0;
if ((flags & DBUS_WATCH_READABLE) &&
FD_ISSET (fd, &read_set))
condition |= DBUS_WATCH_READABLE;
if ((flags & DBUS_WATCH_WRITABLE) &&
FD_ISSET (fd, &write_set))
condition |= DBUS_WATCH_WRITABLE;
if (FD_ISSET (fd, &err_set))
condition |= DBUS_WATCH_ERROR;
if (condition != 0)
{
WatchData *wd;
wd = dbus_watch_get_data (watch);
if (wd->type == WATCH_CONNECTION)
{
DBusConnection *connection = wd->data;
dbus_connection_handle_watch (connection,
watch,
condition);
}
else if (wd->type == WATCH_SERVER)
{
DBusServer *server = wd->data;
dbus_server_handle_watch (server,
watch,
condition);
}
}
link = next;
}
}
}
void
quit_mainloop (void)
{
exited = TRUE;
}
void
setup_connection (DBusConnection *connection)
{
dbus_connection_set_watch_functions (connection,
(DBusAddWatchFunction) add_connection_watch,
(DBusRemoveWatchFunction) remove_connection_watch,
connection,
NULL);
dbus_connection_ref (connection);
_dbus_list_append (&connections, connection);
}
void
setup_server (DBusServer *server)
{
dbus_server_set_watch_functions (server,
(DBusAddWatchFunction) add_server_watch,
(DBusRemoveWatchFunction) remove_server_watch,
server,
NULL);
}