]> rtime.felk.cvut.cz Git - frescor/forb.git/commitdiff
Added reference counting to peers.
authorMichal Sojka <sojkam1@fel.cvut.cz>
Wed, 1 Oct 2008 12:35:34 +0000 (14:35 +0200)
committerMichal Sojka <sojkam1@fel.cvut.cz>
Wed, 1 Oct 2008 12:35:34 +0000 (14:35 +0200)
This is not final version, some things doesn't work yet.

Makefile.omk
iop.c
misc.h [new file with mode: 0644]
peer.c
peer.h
port.c
refcnt.c [new file with mode: 0644]
refcnt.h [new file with mode: 0644]

index 883eb198c45050b6768ce186015651b75945be45..4935b1544767311ae78fe00a4c212ed67d0b5e75 100644 (file)
@@ -7,7 +7,7 @@ IDL_COMPILER += --pidl
 
 lib_LIBRARIES += forb
 forb_SOURCES = forb.c cdr.c sha1.c uuid.c iop.c proto.c syncobj.c      \
-              request.c executor.c object.c peer.c port.c
+              request.c executor.c object.c peer.c port.c refcnt.c
 forb_CLIENT_IDL = types.idl iop-idl.idl
 
 to_forb_subdir=$(1)->forb/$(strip $(1))
diff --git a/iop.c b/iop.c
index d0fdad338e78d28ba525e2cdb1ca8492bf133039..bfdb838e742710f8e6bc07b8c7784cc28a9c8e8d 100644 (file)
--- a/iop.c
+++ b/iop.c
@@ -151,7 +151,7 @@ send_reply(forb_t *forb,
        }
        forb_proto_send(peer, codec);
 err:
-       ;
+       forb_peer_put(peer);
 }
 
 
@@ -266,17 +266,20 @@ process_hello(forb_port_t *port, CDR_Codec *codec)
                peer = forb_peer_find(forb, &server_id);
                if (peer) {
                        /* TODO: Update last hello receive time */
+                       forb_peer_put(peer);
                } else {
                        /* New peer discovered */
 /*                     char str[100]; */
 /*                     printf("New peer %s discovered port %p\n", */
 /*                            forb_server_id_to_string(str, &server_id, sizeof(str)), port); */
-                       peer = forb_malloc(sizeof(*peer));
-                       peer->server_id = server_id;
-                       peer->port = port;
-                       peer->addr = addr;
-                       forb_peer_insert(forb, peer);
-
+                       peer = forb_peer_new();
+                       if (peer) {
+                               peer->server_id = server_id;
+                               peer->port = port;
+                               peer->addr = addr;
+                               forb_peer_insert(forb, peer);
+                               forb_peer_put(peer);
+                       }
                        /* Broadcast our hello packet now */
                        forb_syncobj_signal(&port->hello);
                }
diff --git a/misc.h b/misc.h
new file mode 100644 (file)
index 0000000..87e2ef8
--- /dev/null
+++ b/misc.h
@@ -0,0 +1,18 @@
+#ifndef FORB_MISC_H
+#define FORB_MISC_H
+
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+/** 
+ * Cast a member of a structure out to the containing structure.
+ * 
+ * @param ptr the pointer to the member.
+ * @param type the type of the container struct this is embedded in.
+ * @param member the name of the member within the struct.
+ */
+#define container_of(ptr, type, member) ({                     \
+       const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
+       (type *)( (char *)__mptr - offsetof(type,member) );})
+
+
+#endif
diff --git a/peer.c b/peer.c
index eb1f0f38755844880be87eaee91507879898fa02..1a2cfb1f3c37abbec83b5b60b3da73c9b1475ec7 100644 (file)
--- a/peer.c
+++ b/peer.c
@@ -1,4 +1,6 @@
 #include "peer.h"
+#include "misc.h"
+#include "proto.h"
 
 GAVL_CUST_NODE_INT_IMP(forb_peer_nolock,/* cust_prefix */
                       forb_t,          /* cust_root_t */
@@ -10,3 +12,25 @@ GAVL_CUST_NODE_INT_IMP(forb_peer_nolock,/* cust_prefix */
                       forb_server_id_cmp);/* cust_cmp_fnc */
 
 
+
+forb_peer_t *
+forb_peer_new(void)
+{
+       forb_peer_t *peer;
+       
+       peer = forb_malloc(sizeof(*peer));
+       if (peer) {
+               forb_ref_init(&peer->ref);
+       }
+       return peer;
+}
+
+void
+forb_peer_release(forb_ref_t *ref)
+{
+       forb_peer_t *peer = container_of(ref, forb_peer_t, ref);
+       if (peer->port->proto->peer_destroy) {
+               peer->port->proto->peer_destroy(peer);
+       }
+       forb_free(peer);
+}
diff --git a/peer.h b/peer.h
index c1a730dc0f00abd91ddb843cf99eeb45971630eb..d0d878b41e0ecc9c5ad237bce298d18bb7e2ff39 100644 (file)
--- a/peer.h
+++ b/peer.h
@@ -3,11 +3,16 @@
 
 #include <forb/forb-internal.h>
 #include "port.h"
+#include "refcnt.h"
 
 
 /**
  * Description of a peer. We consider peers only in one-hop
  * distance. For more distant forbs, we need to use a routing table.
+ *
+ * @note This structure is reference counted. Use forb_peer_get() when
+ * you copy a pointer to peer and forb_peer_put() when the pointer is
+ * not needed.
  */
  struct forb_peer {
         forb_server_id server_id; /**< Server_id of the peer */
@@ -15,6 +20,7 @@
         void *addr;            /**< Protocol specific address of the peer */
         void *proto_priv;      /**< Protocol private data (e.g. info about established connection) */
         gavl_node_t node;      /**< Node of port's peers tree */
+        forb_ref_t ref;        /**< Reference count */
 };
 
 typedef struct forb_peer forb_peer_t;
@@ -28,10 +34,29 @@ GAVL_CUST_NODE_INT_DEC(forb_peer_nolock,/* cust_prefix */
                       server_id,       /* cust_item_key */
                       forb_server_id_cmp)/* cust_cmp_fnc */
 
+forb_peer_t *
+forb_peer_new(void);
+
+void
+forb_peer_release(forb_ref_t *ref);
+
+static inline void
+forb_peer_get(forb_peer_t *peer)
+{
+       forb_ref_get(&peer->ref);
+}
+       
+static inline void
+forb_peer_put(forb_peer_t *peer)
+{
+       forb_ref_put(&peer->ref, forb_peer_release);
+}
+
 static inline void
 forb_peer_insert(forb_t *forb, forb_peer_t *peer)
 {
        fosa_mutex_lock(&forb->peer_mutex);
+       forb_peer_get(peer);
        forb_peer_nolock_insert(forb, peer);
        fosa_mutex_unlock(&forb->peer_mutex);
 }
@@ -41,15 +66,29 @@ forb_peer_delete(forb_t *forb, forb_peer_t *peer)
 {
        fosa_mutex_lock(&forb->peer_mutex);
        forb_peer_nolock_delete(forb, peer);
+       forb_peer_put(peer);
        fosa_mutex_unlock(&forb->peer_mutex);
 }
 
+/** 
+ * Finds peer with given @a server_id.
+ * 
+ * @param forb 
+ * @param server_id 
+ * 
+ * @return The found peer or NULL if no peer is found. You have to
+ * call forb_peer_put() after you finish working with the non NULL
+ * retuned value.
+ */
 static inline forb_peer_t *
 forb_peer_find(forb_t *forb, forb_server_id *server_id)
 {
        forb_peer_t *ret;
        fosa_mutex_lock(&forb->peer_mutex);
        ret = forb_peer_nolock_find(forb, server_id);
+       if (ret) {
+               forb_peer_get(ret);
+       }
        fosa_mutex_unlock(&forb->peer_mutex);
        return ret;
 }
diff --git a/port.c b/port.c
index 5376899f571e9177aff66f86fa1b81182d5d7d25..62c240266f973f6f28cc6f0d3b7c0cb18629ee76 100644 (file)
--- a/port.c
+++ b/port.c
@@ -74,13 +74,12 @@ void forb_destroy_port(forb_port_t *port)
        }
 
        fosa_mutex_lock(&port->forb->peer_mutex);
+       /* FIXME: Is gavl_cust_for_each() deletion safe? What about
+        * recursive locking of mutex. */
+#warning Delete during GAVL traversal is problbly not correct.
        gavl_cust_for_each(forb_peer_nolock, forb, peer) {
                if (peer->port == port) {
-                       if (port->proto->peer_destroy) {
-                               port->proto->peer_destroy(peer);
-                       }
-                       /* TODO: Reference counting */
-                       forb_free(peer);
+                       forb_peer_delete(forb, peer);
                }
        }
        fosa_mutex_unlock(&port->forb->peer_mutex);
diff --git a/refcnt.c b/refcnt.c
new file mode 100644 (file)
index 0000000..4688bd9
--- /dev/null
+++ b/refcnt.c
@@ -0,0 +1,36 @@
+/**
+ * @file   refcnt.c
+ * @author Michal Sojka <sojkam1@fel.cvut.cz>
+ * @date   Wed Oct  1 09:23:40 2008
+ * 
+ * @brief  Atomic reference counting operations.
+ *
+ * @note: To provide portable atomicity, we use GCC's builtin
+ * functions introduced in GCC 4.1.
+ * 
+ */
+#include "refcnt.h"
+
+void forb_ref_set(struct forb_ref *ref, int num)
+{
+       ref->refcount = num;
+}
+
+void forb_ref_init(struct forb_ref *ref)
+{
+       forb_ref_set(ref, 1);
+}
+
+void forb_ref_get(struct forb_ref *ref)
+{
+       __sync_add_and_fetch(&ref->refcount, 1);
+}
+
+int forb_ref_put(struct forb_ref *ref, void (*release) (struct forb_ref *ref))
+{
+       if (__sync_add_and_fetch(&ref->refcount, -1) == 0) {
+               release(ref);
+               return 1;
+       }
+       return 0;
+}
diff --git a/refcnt.h b/refcnt.h
new file mode 100644 (file)
index 0000000..178f584
--- /dev/null
+++ b/refcnt.h
@@ -0,0 +1,16 @@
+#ifndef FORB_REFCNT_H
+#define FORB_REFCNT_H
+
+struct forb_ref {
+       unsigned refcount;
+};
+
+typedef struct forb_ref forb_ref_t;
+
+void forb_ref_set(struct forb_ref *ref, int num);
+void forb_ref_init(struct forb_ref *ref);
+void forb_ref_get(struct forb_ref *ref);
+int forb_ref_put(struct forb_ref *ref, void (*release) (struct forb_ref *ref));
+       
+
+#endif