MEDIUM: threads/xref: Convert xref function to a thread safe model
Ensure that the unlink is done safely between thread and that
the peer struct will not destroy between the usage of the peer.
diff --git a/include/common/xref.h b/include/common/xref.h
index b020280..6dfa7b6 100644
--- a/include/common/xref.h
+++ b/include/common/xref.h
@@ -1,6 +1,8 @@
#ifndef __XREF_H__
#define __XREF_H__
+#include <common/hathreads.h>
+
/* xref is used to create relation between two elements.
* Once an element is released, it breaks the relation. If the
* relation is already broken, it frees the xref struct.
@@ -13,25 +15,64 @@
struct xref *peer;
};
+#define XREF_BUSY ((struct xref *)1)
+
static inline void xref_create(struct xref *xref_a, struct xref *xref_b)
{
xref_a->peer = xref_b;
xref_b->peer = xref_a;
}
-static inline struct xref *xref_get_peer(struct xref *xref)
+static inline struct xref *xref_get_peer_and_lock(struct xref *xref)
{
- if (!xref->peer)
- return NULL;
- return xref->peer;
+ struct xref *local;
+ struct xref *remote;
+
+ while (1) {
+
+ /* Get the local pointer to the peer. */
+ local = HA_ATOMIC_XCHG(&xref->peer, XREF_BUSY);
+
+ /* If the local pointer is NULL, the peer no longer exists. */
+ if (local == NULL) {
+ xref->peer = NULL;
+ return NULL;
+ }
+
+ /* If the local pointeru is BUSY, the peer try to acquire the
+ * lock. We retry the process.
+ */
+ if (local == XREF_BUSY)
+ continue;
+
+ /* We are locked, the peer cant disapear, try to acquire
+ * the pper's lock. Note that remote can't be NULL.
+ */
+ remote = HA_ATOMIC_XCHG(&local->peer, XREF_BUSY);
+
+ /* The remote lock is BUSY, We retry the process. */
+ if (remote == XREF_BUSY) {
+ xref->peer = local;
+ continue;
+ }
+
+ /* We have the lock, we return the value of the xref. */
+ return local;
+ }
}
-static inline void xref_disconnect(struct xref *xref)
+static inline void xref_unlock(struct xref *xref, struct xref *peer)
{
- if (!xref->peer)
- return;
+ /* Release the peer. */
+ peer->peer = xref;
- xref->peer->peer = NULL;
+ /* Release myself. */
+ xref->peer = peer;
+}
+
+static inline void xref_disconnect(struct xref *xref, struct xref *peer)
+{
+ peer->peer = NULL;
xref->peer = NULL;
}