[Sheepdog] [PATCH] count the number of requests to free a client_info safely
MORITA Kazutaka
morita.kazutaka at lab.ntt.co.jp
Tue Jul 6 09:46:03 CEST 2010
When a connection is closed, we cannot free a client_info if server is
processing a request which references the client_info.
This patch introduces a reference count to check whether we can free
the client_info safely.
Signed-off-by: MORITA Kazutaka <morita.kazutaka at lab.ntt.co.jp>
---
include/event.h | 2 +-
include/net.h | 4 ++--
lib/event.c | 9 ++++++---
lib/net.c | 8 ++++----
sheep/sdnet.c | 36 ++++++++++++++++++++++++++++++++----
sheep/sheep_priv.h | 2 ++
6 files changed, 47 insertions(+), 14 deletions(-)
diff --git a/include/event.h b/include/event.h
index d3f0004..b16c97a 100644
--- a/include/event.h
+++ b/include/event.h
@@ -10,7 +10,7 @@ typedef void (*event_handler_t)(int fd, int events, void *data);
int init_event(int nr);
int register_event(int fd, event_handler_t h, void *data);
void unregister_event(int fd);
-void modify_event(int fd, unsigned int events);
+int modify_event(int fd, unsigned int events);
void event_loop(int timeout);
struct timer {
diff --git a/include/net.h b/include/net.h
index b0bc399..539701e 100644
--- a/include/net.h
+++ b/include/net.h
@@ -25,8 +25,8 @@ struct connection {
struct sd_rsp tx_hdr;
};
-void conn_tx_off(struct connection *conn);
-void conn_tx_on(struct connection *conn);
+int conn_tx_off(struct connection *conn);
+int conn_tx_on(struct connection *conn);
int is_conn_dead(struct connection *conn);
int do_read(int sockfd, void *buf, int len);
int rx(struct connection *conn, enum conn_state next_state);
diff --git a/lib/event.c b/lib/event.c
index a24d3cb..6991b6c 100644
--- a/lib/event.c
+++ b/lib/event.c
@@ -141,7 +141,7 @@ void unregister_event(int fd)
free(ei);
}
-void modify_event(int fd, unsigned int events)
+int modify_event(int fd, unsigned int events)
{
int ret;
struct epoll_event ev;
@@ -150,7 +150,7 @@ void modify_event(int fd, unsigned int events)
ei = lookup_event(fd);
if (!ei) {
eprintf("can't find event info %d\n", fd);
- return;
+ return 1;
}
memset(&ev, 0, sizeof(ev));
@@ -158,8 +158,11 @@ void modify_event(int fd, unsigned int events)
ev.data.ptr = ei;
ret = epoll_ctl(efd, EPOLL_CTL_MOD, fd, &ev);
- if (ret)
+ if (ret) {
eprintf("can't del epoll event, %m\n");
+ return 1;
+ }
+ return 0;
}
void event_loop(int timeout)
diff --git a/lib/net.c b/lib/net.c
index 9b260ab..107408c 100644
--- a/lib/net.c
+++ b/lib/net.c
@@ -29,14 +29,14 @@
#include "net.h"
#include "logger.h"
-void conn_tx_off(struct connection *conn)
+int conn_tx_off(struct connection *conn)
{
- modify_event(conn->fd, EPOLLIN);
+ return modify_event(conn->fd, EPOLLIN);
}
-void conn_tx_on(struct connection *conn)
+int conn_tx_on(struct connection *conn)
{
- modify_event(conn->fd, EPOLLIN|EPOLLOUT);
+ return modify_event(conn->fd, EPOLLIN|EPOLLOUT);
}
int is_conn_dead(struct connection *conn)
diff --git a/sheep/sdnet.c b/sheep/sdnet.c
index 79ba3ff..0d3d4b1 100644
--- a/sheep/sdnet.c
+++ b/sheep/sdnet.c
@@ -238,6 +238,9 @@ static void queue_request(struct request *req)
start_cpg_event_work();
}
+static void client_incref(struct client_info *ci);
+static void client_decref(struct client_info *ci);
+
static struct request *alloc_request(struct client_info *ci, int data_length)
{
struct request *req;
@@ -247,6 +250,7 @@ static struct request *alloc_request(struct client_info *ci, int data_length)
return NULL;
req->ci = ci;
+ client_incref(ci);
if (data_length)
req->data = (char *)req + sizeof(*req);
@@ -258,6 +262,7 @@ static struct request *alloc_request(struct client_info *ci, int data_length)
static void free_request(struct request *req)
{
+ client_decref(req->ci);
list_del(&req->r_siblings);
free(req);
}
@@ -265,7 +270,10 @@ static void free_request(struct request *req)
static void req_done(struct request *req)
{
list_add(&req->r_wlist, &req->ci->done_reqs);
- conn_tx_on(&req->ci->conn);
+ if (conn_tx_on(&req->ci->conn)) {
+ dprintf("connection seems to be dead\n");
+ free_request(req);
+ }
}
static void init_rx_hdr(struct client_info *ci)
@@ -317,7 +325,12 @@ static void client_rx_handler(struct client_info *ci)
eprintf("BUG: unknown state %d\n", conn->c_rx_state);
}
- if (is_conn_dead(conn) || conn->c_rx_state != C_IO_END)
+ if (is_conn_dead(conn) && ci->rx_req) {
+ free_request(ci->rx_req);
+ return;
+ }
+
+ if (conn->c_rx_state != C_IO_END)
return;
/* now we have a complete command */
@@ -406,8 +419,10 @@ again:
opt = 0;
setsockopt(ci->conn.fd, SOL_TCP, TCP_CORK, &opt, sizeof(opt));
- if (is_conn_dead(&ci->conn) || ci->conn.c_tx_state != C_IO_END)
+ if (is_conn_dead(&ci->conn)) {
+ free_request(ci->tx_req);
return;
+ }
if (ci->conn.c_tx_state == C_IO_END) {
free_request(ci->tx_req);
@@ -422,6 +437,18 @@ static void destroy_client(struct client_info *ci)
free(ci);
}
+static void client_incref(struct client_info *ci)
+{
+ if (ci)
+ ci->refcnt++;
+}
+
+static void client_decref(struct client_info *ci)
+{
+ if (ci && --ci->refcnt == 0)
+ destroy_client(ci);
+}
+
static struct client_info *create_client(int fd, struct cluster_info *cluster)
{
struct client_info *ci;
@@ -431,6 +458,7 @@ static struct client_info *create_client(int fd, struct cluster_info *cluster)
return NULL;
ci->conn.fd = fd;
+ ci->refcnt = 1;
INIT_LIST_HEAD(&ci->reqs);
INIT_LIST_HEAD(&ci->done_reqs);
@@ -453,7 +481,7 @@ static void client_handler(int fd, int events, void *data)
if (is_conn_dead(&ci->conn)) {
dprintf("closed a connection, %d\n", fd);
unregister_event(fd);
- destroy_client(ci);
+ client_decref(ci);
}
}
diff --git a/sheep/sheep_priv.h b/sheep/sheep_priv.h
index 8d1c6c0..47c83c1 100644
--- a/sheep/sheep_priv.h
+++ b/sheep/sheep_priv.h
@@ -59,6 +59,8 @@ struct client_info {
struct list_head reqs;
struct list_head done_reqs;
+
+ int refcnt;
};
struct request;
--
1.5.6.5
More information about the sheepdog
mailing list