21 // init lists |
21 // init lists |
22 LIST_INIT(&server->conn_list); |
22 LIST_INIT(&server->conn_list); |
23 TAILQ_INIT(&server->req_queue); |
23 TAILQ_INIT(&server->req_queue); |
24 |
24 |
25 // grow connpool so as to have a connection ready |
25 // grow connpool so as to have a connection ready |
26 if (memcache_server_grow_connpool(server)) |
26 memcache_server_grow_connpool(server); |
27 ERROR("error opening initial connection"); |
|
28 |
27 |
29 // success |
28 // success |
30 return server; |
29 return server; |
31 |
30 |
32 error: |
31 error: |
33 free(server); |
32 free(server); |
34 |
33 |
35 return NULL; |
34 return NULL; |
36 } |
35 } |
37 |
36 |
38 int memcache_server_grow_connpool (struct memcache_server *server) { |
37 void memcache_server_grow_connpool (struct memcache_server *server) { |
39 struct memcache_conn *conn; |
38 struct memcache_conn *conn; |
40 int count; |
39 int count = 0; |
41 |
40 |
42 // count connections |
41 // count connections |
43 for (count = 0, conn = server->conn_list.lh_first; conn != NULL; conn = conn->connlist_node.le_next, count++) ; |
42 LIST_FOREACH(conn, &server->conn_list, connlist_node) { |
|
43 count++; |
|
44 } |
44 |
45 |
45 // room for more? |
46 // room for more? |
46 if (count < server->max_connections) { |
47 if (count < server->max_connections) { |
47 // create a new one |
48 // create a new one |
48 if ((conn = memcache_conn_open(server)) == NULL) |
49 if ((conn = memcache_conn_open(server)) == NULL) |
49 return -1; |
50 ERROR("failed to grow the connpool"); |
50 |
51 |
51 // enlist it |
52 // enlist it |
52 LIST_INSERT_HEAD(&server->conn_list, conn, connlist_node); |
53 LIST_INSERT_HEAD(&server->conn_list, conn, connlist_node); |
53 |
54 |
54 // the connection will call memcache_server_coon_ready once it's ready for use... |
55 // the connection will call memcache_server_coon_ready once it's ready for use... |
55 } |
56 } |
56 |
57 |
57 // success |
58 // ok |
58 return 0; |
59 return; |
|
60 |
|
61 error: |
|
62 // XXX: we might be deadlocked now... requests queued, but no connections! |
|
63 if (LIST_EMPTY(&server->conn_list) && !TAILQ_EMPTY(&server->req_queue)) |
|
64 FATAL("deadlock; requests queued, but no connections"); |
|
65 |
|
66 // XXX: harmless... but need some retry logic |
59 } |
67 } |
60 |
68 |
61 int memcache_server_add_req (struct memcache_server *server, struct memcache_req *req) { |
69 int memcache_server_add_req (struct memcache_server *server, struct memcache_req *req) { |
62 struct memcache_conn *conn; |
70 struct memcache_conn *conn; |
63 |
71 |
64 // look for an idle connection |
72 // look for an idle connection |
65 for (conn = server->conn_list.lh_first; conn != NULL; conn = conn->connlist_node.le_next) { |
73 LIST_FOREACH(conn, &server->conn_list, connlist_node) { |
66 if (memcache_conn_is_available(conn)) { |
74 if (memcache_conn_is_available(conn)) { |
67 // we found an idle connection |
75 // we found an idle connection |
68 break; |
76 break; |
69 } |
77 } |
70 } |
78 } |
83 TAILQ_INSERT_TAIL(&server->req_queue, req, reqqueue_node); |
91 TAILQ_INSERT_TAIL(&server->req_queue, req, reqqueue_node); |
84 |
92 |
85 // notify the req |
93 // notify the req |
86 memcache_req_queued(req); |
94 memcache_req_queued(req); |
87 |
95 |
|
96 // grow the connpool, as we apparently don't have enough connections |
|
97 memcache_server_grow_connpool(server); |
|
98 |
88 return 0; |
99 return 0; |
89 } |
100 } |
90 } |
101 } |
91 |
102 |
|
103 /* |
|
104 * We might have available connections, process any queued requests, or try and grow the connpool if non available |
|
105 */ |
|
106 void memache_server_dequeue (struct memcache_server *server) { |
|
107 struct memcache_conn *conn; |
|
108 struct memcache_req *req; |
|
109 |
|
110 // if no requests are queued, nothing needs doing |
|
111 if ((req = TAILQ_FIRST(&server->req_queue)) == NULL) |
|
112 return; |
|
113 |
|
114 // look for idle connections to service the request |
|
115 LIST_FOREACH(conn, &server->conn_list, connlist_node) { |
|
116 if (memcache_conn_is_available(conn)) { |
|
117 // remove the req from the queue and execute it |
|
118 TAILQ_REMOVE(&server->req_queue, req, reqqueue_node); |
|
119 |
|
120 // this will take care of any error handling by itself |
|
121 memcache_conn_do_req(conn, req); |
|
122 |
|
123 // if that was the last req, return, otherwise continue |
|
124 if ((req = TAILQ_FIRST(&server->req_queue)) == NULL) |
|
125 return; |
|
126 } |
|
127 } |
|
128 |
|
129 // no idle connections remaining, try and grow if applicable |
|
130 memcache_server_grow_connpool(server); |
|
131 } |
|
132 |
92 void memcache_server_conn_ready (struct memcache_server *server, struct memcache_conn *conn) { |
133 void memcache_server_conn_ready (struct memcache_server *server, struct memcache_conn *conn) { |
93 assert(server == conn->server); |
134 assert(server == conn->server); |
94 |
135 |
95 // do we have any queued requests waiting? |
136 // grab the next queued request |
96 struct memcache_req *req = server->req_queue.tqh_first; |
137 memache_server_dequeue(server); |
97 |
|
98 if (req) { |
|
99 // remove it from the queue and execute it |
|
100 TAILQ_REMOVE(&server->req_queue, req, reqqueue_node); |
|
101 |
|
102 // this will take care of any error handling by itself |
|
103 memcache_conn_do_req(conn, req); |
|
104 } |
|
105 } |
138 } |
106 |
139 |
107 void memcache_server_conn_dead (struct memcache_server *server, struct memcache_conn *conn) { |
140 void memcache_server_conn_dead (struct memcache_server *server, struct memcache_conn *conn) { |
108 assert(server == conn->server); |
141 assert(server == conn->server); |
109 |
142 |
110 // XXX: reconnect/error out requests? |
|
111 |
|
112 // remove it from the list |
143 // remove it from the list |
113 LIST_REMOVE(conn, connlist_node); |
144 LIST_REMOVE(conn, connlist_node); |
114 |
145 |
115 // free it |
146 // free it |
116 memcache_conn_free(conn); |
147 memcache_conn_free(conn); |
|
148 |
|
149 // this should grow the connpool back again |
|
150 memache_server_dequeue(server); |
117 } |
151 } |
118 |
152 |