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;
 }