summaryrefslogtreecommitdiff
path: root/debian/patches/0006-include-add-refcounts_demote-that-demotes-a-hard-ref.patch
blob: 2c1fcd3bd52cc5d77dcfbf6cfae3e582f3b7ceea (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
From a1f4af1716aa9c00358816ab7f82dac2cabdd961 Mon Sep 17 00:00:00 2001
From: Justus Winter <4winter@informatik.uni-hamburg.de>
Date: Tue, 13 May 2014 23:04:40 +0200
Subject: [PATCH 06/13] include: add refcounts_demote that demotes a hard
 reference

refcounts_demote atomically demotes a hard reference to a weak one.
This reduces the number of required atomic operations from three to
two when a hard reference is dropped and a dropweak routine is
installed.

* include/refcount.h (refcounts_demote): New function.
* libports/port-deref.c (ports_port_deref): Use the new function.
---
 include/refcount.h    | 39 +++++++++++++++++++++++++++++++++++++++
 libports/port-deref.c | 18 +++++++++---------
 2 files changed, 48 insertions(+), 9 deletions(-)

diff --git a/include/refcount.h b/include/refcount.h
index 0816220..2aafb9e 100644
--- a/include/refcount.h
+++ b/include/refcount.h
@@ -77,8 +77,18 @@ typedef union _references refcounts_t;
 /* Instead, the functions manipulating refcounts_t values write the
    results into this kind of objects.  */
 struct references {
+  /* We chose the layout of this struct so that when it is used in the
+     union _references, the hard reference counts occupy the least
+     significant bits.  This way we can implement atomically demoting
+     a hard reference to a weak reference.  See refcounts_demote for
+     details.  */
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
   uint32_t hard;
   uint32_t weak;
+#else
+  uint32_t weak;
+  uint32_t hard;
+#endif
 };
 
 /* We use a union to convert struct reference values to uint64_t which
@@ -127,6 +137,35 @@ refcounts_deref (refcounts_t *ref, struct references *result)
     *result = r.references;
 }
 
+/* Demote a hard reference to a weak reference.  If RESULT is not
+   NULL, the result of the operation is written there.  This function
+   uses atomic operations.  It is not required to serialize calls to
+   this function.  */
+static inline void
+refcounts_demote (refcounts_t *ref, struct references *result)
+{
+  /* To demote a hard reference, we need to atomically subtract 1 from
+     the hard reference count, and add 1 to the weak reference
+     count.
+
+     We can subtract by 1 by adding the two's complement of 1 = ~0 to
+     a fixed-width value, discarding the overflow.
+
+     We do the same in our uint64_t value, but we have chosen the
+     layout of struct references so that when it is used in the union
+     _references, the hard reference counts occupy the least
+     significant bits.  When we add ~0 to the hard references, it will
+     overflow into the weak references.  This is the desired
+     operation.  */
+  const union _references op = { .references = { .hard = ~0 } };
+  union _references r;
+  r.value = __atomic_add_fetch (&ref->value, op.value, __ATOMIC_RELAXED);
+  assert (r.references.hard != UINT32_MAX
+          && r.references.weak != UINT32_MAX);
+  if (result)
+    *result = r.references;
+}
+
 /* Increment the weak reference count of REF.  If RESULT is not NULL,
    the result of the operation is written there.  This function uses
    atomic operations.  It is not required to serialize calls to this
diff --git a/libports/port-deref.c b/libports/port-deref.c
index dd38f55..b97dd13 100644
--- a/libports/port-deref.c
+++ b/libports/port-deref.c
@@ -27,21 +27,21 @@ ports_port_deref (void *portstruct)
   struct port_info *pi = portstruct;
   struct references result;
 
-  /* If we need to call the dropweak routine, we need to hold one
-     reference while doing so.  We use a weak reference for this
-     purpose, which we acquire before we release our hard reference.
-     The order is important here to prevent a race.  */
-  if (pi->class->dropweak_routine)
-    refcounts_ref_weak (&pi->refcounts, NULL);
-
-  refcounts_deref (&pi->refcounts, &result);
-
   if (pi->class->dropweak_routine)
     {
+      /* If we need to call the dropweak routine, we need to hold one
+         reference while doing so.  We use a weak reference for this
+         purpose, which we acquire by demoting our hard reference to a
+         weak one.  */
+      refcounts_demote (&pi->refcounts, &result);
+
       if (result.hard == 0 && result.weak > 1)
         (*pi->class->dropweak_routine) (pi);
+
       refcounts_deref_weak (&pi->refcounts, &result);
     }
+  else
+    refcounts_deref (&pi->refcounts, &result);
 
   if (result.hard == 0 && result.weak == 0)
     _ports_complete_deallocate (pi);
-- 
2.0.0.rc0