summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libsystemd-bus/bus-internal.h12
-rw-r--r--src/libsystemd-bus/sd-bus.c10
-rw-r--r--src/shared/refcnt.h34
3 files changed, 48 insertions, 8 deletions
diff --git a/src/libsystemd-bus/bus-internal.h b/src/libsystemd-bus/bus-internal.h
index 504dac7f09..0edb09764a 100644
--- a/src/libsystemd-bus/bus-internal.h
+++ b/src/libsystemd-bus/bus-internal.h
@@ -29,6 +29,7 @@
#include "prioq.h"
#include "list.h"
#include "util.h"
+#include "refcnt.h"
#include "sd-bus.h"
#include "bus-error.h"
@@ -77,7 +78,16 @@ enum bus_auth {
};
struct sd_bus {
- unsigned n_ref;
+ /* We use atomic ref counting here since sd_bus_message
+ objects retain references to their originating sd_bus but
+ we want to allow them to be processed in a different
+ thread. We won't provide full thread safety, but only the
+ bare minimum that makes it possible to use sd_bus and
+ sd_bus_message objects independently and on different
+ threads as long as each object is used only once at the
+ same time. */
+ RefCount n_ref;
+
enum bus_state state;
int input_fd, output_fd;
int message_version;
diff --git a/src/libsystemd-bus/sd-bus.c b/src/libsystemd-bus/sd-bus.c
index 7b937d9999..4a081778ac 100644
--- a/src/libsystemd-bus/sd-bus.c
+++ b/src/libsystemd-bus/sd-bus.c
@@ -103,7 +103,7 @@ int sd_bus_new(sd_bus **ret) {
if (!r)
return -ENOMEM;
- r->n_ref = 1;
+ r->n_ref = REFCNT_INIT;
r->input_fd = r->output_fd = -1;
r->message_version = 1;
r->negotiate_fds = true;
@@ -934,9 +934,8 @@ sd_bus *sd_bus_ref(sd_bus *bus) {
if (!bus)
return NULL;
- assert(bus->n_ref > 0);
+ assert_se(REFCNT_INC(bus->n_ref) >= 2);
- bus->n_ref++;
return bus;
}
@@ -944,10 +943,7 @@ sd_bus *sd_bus_unref(sd_bus *bus) {
if (!bus)
return NULL;
- assert(bus->n_ref > 0);
- bus->n_ref--;
-
- if (bus->n_ref <= 0)
+ if (REFCNT_DEC(bus->n_ref) <= 0)
bus_free(bus);
return NULL;
diff --git a/src/shared/refcnt.h b/src/shared/refcnt.h
new file mode 100644
index 0000000000..0502c20a2e
--- /dev/null
+++ b/src/shared/refcnt.h
@@ -0,0 +1,34 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+/* A type-safe atomic refcounter */
+
+typedef struct {
+ volatile unsigned _value;
+} RefCount;
+
+#define REFCNT_GET(r) ((r)._value)
+#define REFCNT_INC(r) (__sync_add_and_fetch(&(r)._value, 1))
+#define REFCNT_DEC(r) (__sync_sub_and_fetch(&(r)._value, 1))
+
+#define REFCNT_INIT ((RefCount) { ._value = 1 })