X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Flibelogind%2Fsd-bus%2Fbus-track.c;h=77a63a978c7e09d8b26cdf8a9bf49fd7664aafc1;hp=d2c26746ed5ca87a496e28346e0d4dc016b4cad5;hb=eaa22dd2515e82ba7de62259dab06fbd156284ca;hpb=11ec70371cc24612d14bb24ec9559b4f85e47de5 diff --git a/src/libelogind/sd-bus/bus-track.c b/src/libelogind/sd-bus/bus-track.c index d2c26746e..77a63a978 100644 --- a/src/libelogind/sd-bus/bus-track.c +++ b/src/libelogind/sd-bus/bus-track.c @@ -32,15 +32,19 @@ struct track_item { struct sd_bus_track { unsigned n_ref; + unsigned n_adding; /* are we in the process of adding a new name? */ sd_bus *bus; sd_bus_track_handler_t handler; void *userdata; Hashmap *names; LIST_FIELDS(sd_bus_track, queue); Iterator iterator; - bool in_queue:1; + bool in_list:1; /* In bus->tracks? */ + bool in_queue:1; /* In bus->track_queue? */ bool modified:1; bool recursive:1; + + LIST_FIELDS(sd_bus_track, tracks); }; #define MATCH_PREFIX \ @@ -80,12 +84,30 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(struct track_item*, track_item_free); static void bus_track_add_to_queue(sd_bus_track *track) { assert(track); + /* Adds the bus track object to the queue of objects we should dispatch next, subject to a number of + * conditions. */ + + /* Already in the queue? */ if (track->in_queue) return; + /* if we are currently in the process of adding a new name, then let's not enqueue this just yet, let's wait + * until the addition is complete. */ + if (track->n_adding > 0) + return; + + /* still referenced? */ + if (hashmap_size(track->names) > 0) + return; + + /* Nothing to call? */ if (!track->handler) return; + /* Already closed? */ + if (!track->in_list) + return; + LIST_PREPEND(queue, track->bus->track_queue, track); track->in_queue = true; } @@ -112,8 +134,7 @@ static int bus_track_remove_name_fully(sd_bus_track *track, const char *name) { track_item_free(i); - if (hashmap_isempty(track->names)) - bus_track_add_to_queue(track); + bus_track_add_to_queue(track); track->modified = true; return 1; @@ -142,6 +163,9 @@ _public_ int sd_bus_track_new( t->userdata = userdata; t->bus = sd_bus_ref(bus); + LIST_PREPEND(tracks, bus->tracks, t); + t->in_list = true; + bus_track_add_to_queue(t); *track = t; @@ -176,6 +200,9 @@ _public_ sd_bus_track* sd_bus_track_unref(sd_bus_track *track) { while ((i = hashmap_steal_first(track->names))) track_item_free(i); + if (track->in_list) + LIST_REMOVE(tracks, track->bus->tracks, track); + bus_track_remove_from_queue(track); hashmap_free(track->names); sd_bus_unref(track->bus); @@ -237,18 +264,30 @@ _public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) { /* First, subscribe to this name */ match = MATCH_FOR_NAME(name); + + bus_track_remove_from_queue(track); /* don't dispatch this while we work in it */ + + track->n_adding++; /* make sure we aren't dispatched while we synchronously add this match */ r = sd_bus_add_match(track->bus, &n->slot, match, on_name_owner_changed, track); - if (r < 0) + track->n_adding--; + if (r < 0) { + bus_track_add_to_queue(track); return r; + } r = hashmap_put(track->names, n->name, n); - if (r < 0) + if (r < 0) { + bus_track_add_to_queue(track); return r; + } /* Second, check if it is currently existing, or maybe doesn't, or maybe disappeared already. */ + track->n_adding++; /* again, make sure this isn't dispatch while we are working in it */ r = sd_bus_get_name_creds(track->bus, name, 0, NULL); + track->n_adding--; if (r < 0) { hashmap_remove(track->names, name); + bus_track_add_to_queue(track); return r; } @@ -377,7 +416,6 @@ void bus_track_dispatch(sd_bus_track *track) { int r; assert(track); - assert(track->in_queue); assert(track->handler); bus_track_remove_from_queue(track); @@ -394,6 +432,34 @@ void bus_track_dispatch(sd_bus_track *track) { } #if 0 /// UNNEEDED by elogind +void bus_track_close(sd_bus_track *track) { + struct track_item *i; + + assert(track); + + /* Called whenever our bus connected is closed. If so, and our track object is non-empty, dispatch it + * immediately, as we are closing now, but first flush out all names. */ + + if (!track->in_list) + return; /* We already closed this one, don't close it again. */ + + /* Remember that this one is closed now */ + LIST_REMOVE(tracks, track->bus->tracks, track); + track->in_list = false; + + /* If there's no name in this one anyway, we don't have to dispatch */ + if (hashmap_isempty(track->names)) + return; + + /* Let's flush out all names */ + while ((i = hashmap_steal_first(track->names))) + track_item_free(i); + + /* Invoke handler */ + if (track->handler) + bus_track_dispatch(track); +} + _public_ void *sd_bus_track_get_userdata(sd_bus_track *track) { assert_return(track, NULL);