* libevent may report a deleted event inside same loop.
Avoid socket reuse for one loop.
* release_server() from disconnect_client() didnt look
it the packet was actually sent.
# keep autoconf stuff separate
-include config.mak
+CFLAGS += -DDBGVER="\"compiled by <$${USER}@`hostname`> at `date '+%Y-%m-%d %H:%M:%S'`\""
+
# calculate full-path values
OBJS = $(SRCS:.c=.o)
hdrs = $(addprefix $(srcdir)/src/, $(HDRS))
+2007-03-28 - PgBouncer 1.0.2 - "Supersonic Spoon"
+
+ * libevent may report a deleted event inside same loop.
+ Avoid socket reuse for one loop.
+ * release_server() from disconnect_client() didnt look
+ it the packet was actually sent.
+
2007-03-15 - PgBouncer 1.0.1 - "Alien technology"
= Fixes =
dnl Process this file with autoconf to produce a configure script.
-AC_INIT(pgbouncer, 1.0.1)
+AC_INIT(pgbouncer, 1.0.2)
AC_CONFIG_SRCDIR(src/bouncer.h)
AC_CONFIG_HEADER(config.h)
+pgbouncer (1.0.2) unstable; urgency=low
+
+ * 2 more bugs.
+
+ -- Marko Kreen <marko.kreen@skype.net> Wed, 28 Mar 2007 12:04:39 +0300
+
pgbouncer (1.0.1) unstable; urgency=low
* Couple hotfixes.
bool res;
SEND_generic(res, admin, 'N',
"ssss", "SNOTICE", "C00000",
- "MPgBouncer version " PACKAGE_VERSION, "");
+ "M" FULLVER, "");
if (res)
res = admin_ready(admin, "SHOW");
return res;
#include <event.h>
+#ifdef DBGVER
+#define FULLVER PACKAGE_NAME " version " PACKAGE_VERSION " (" DBGVER ")"
+#else
+#define FULLVER PACKAGE_NAME " version " PACKAGE_VERSION
+#endif
+
/* each state corresponts to a list */
enum SocketState {
CL_FREE, /* free_client_list */
+ CL_JUSTFREE, /* justfree_client_list */
CL_LOGIN, /* login_client_list */
CL_WAITING, /* pool->waiting_client_list */
CL_ACTIVE, /* pool->active_client_list */
CL_CANCEL, /* pool->cancel_req_list */
SV_FREE, /* free_server_list */
+ SV_JUSTFREE, /* justfree_server_list */
SV_LOGIN, /* pool->new_server_list */
SV_IDLE, /* pool->idle_server_list */
SV_ACTIVE, /* pool->active_server_list */
PgSocket *client = arg;
Assert(!is_server_socket(client));
- Assert(client->state != SV_FREE);
+ Assert(client->sbuf.sock);
+ Assert(client->state != CL_FREE);
+
+ if (client->state == CL_JUSTFREE) {
+ /* SBuf should catch the case */
+ slog_warning(client, "state=CL_JUSTFREE, should not happen");
+ return false;
+ }
switch (evtype) {
case SBUF_EV_CONNECT_OK:
/*
* this function is called for each event loop.
*/
-void per_loop_object_maint(void)
+void per_loop_maint(void)
{
List *item;
PgPool *pool;
void janitor_setup(void);
void config_postprocess(void);
void resume_all(void);
-void per_loop_object_maint(void);
+void per_loop_maint(void);
bool suspend_socket(PgSocket *sk);
return list->next;
}
+/* put all elems in one list in the start of another list */
+static inline void list_prepend_list(List *src, List *dst)
+{
+ if (list_empty(src))
+ return;
+ src->next->prev = dst;
+ src->prev->next = dst->next;
+ dst->next->prev = src->prev;
+ dst->next = src->next;
+
+ src->next = src->prev = src;
+}
+
+/* put all elems in one list in the end of another list */
+static inline void list_append_list(List *src, List *dst)
+{
+ if (list_empty(src))
+ return;
+ src->next->prev = dst->prev;
+ src->prev->next = dst;
+ dst->prev->next = src->next;
+ dst->prev = src->prev;
+
+ src->next = src->prev = src;
+}
+
/* remove first elem from list and return with casting */
#define list_pop_type(list, typ, field) \
(list_empty(list) ? NULL \
const char *name;
};
+static inline void statlist_inc_count(StatList *list, int val)
+{
+ list->cur_count += val;
+ if (list->cur_count > list->max_count)
+ list->max_count = list->cur_count;
+}
+
#define STATLIST(var) StatList var = { {&var.head, &var.head}, 0, 0, #var }
static inline void statlist_reset(StatList *list)
static inline void statlist_prepend(List *item, StatList *list)
{
list_prepend(item, &list->head);
- list->cur_count ++;
- if (list->cur_count > list->max_count)
- list->max_count = list->cur_count;
+ statlist_inc_count(list, 1);
}
static inline void statlist_append(List *item, StatList *list)
{
list_append(item, &list->head);
- list->cur_count ++;
- if (list->cur_count > list->max_count)
- list->max_count = list->cur_count;
+ statlist_inc_count(list, 1);
}
static inline void statlist_put_before(List *item, StatList *list, List *pos)
{
list_append(item, pos);
- list->cur_count++;
- if (list->cur_count > list->max_count)
- list->max_count = list->cur_count;
+ statlist_inc_count(list, 1);
}
static inline void statlist_remove(List *item, StatList *list)
return item;
}
+static inline void statlist_prepend_list(StatList *src, StatList *dst)
+{
+ list_prepend_list(&src->head, &dst->head);
+ statlist_inc_count(dst, src->cur_count);
+ src->cur_count = 0;
+}
+
+static inline void statlist_append_list(StatList *src, StatList *dst)
+{
+ list_append_list(&src->head, &dst->head);
+ statlist_inc_count(dst, src->cur_count);
+ src->cur_count = 0;
+}
+
static inline List *statlist_first(StatList *list)
{
return list_first(&list->head);
{
reset_time_cache();
event_loop(EVLOOP_ONCE);
- per_loop_object_maint();
+ per_loop_maint();
+ reuse_just_freed_objects();
}
/* boot everything */
cf_verbose++;
break;
case 'V':
- printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
+ printf("%s\n", FULLVER);
return 0;
case 'd':
cf_daemon = 1;
STATLIST(free_server_list);
STATLIST(login_client_list);
+/*
+ * libevent may still report events when event_del()
+ * is called from somewhere else. So hide just freed
+ * PgSockets for one loop.
+ */
+STATLIST(justfree_client_list);
+STATLIST(justfree_server_list);
+
/* how many client sockets are allocated */
static int absolute_client_count = 0;
/* how many server sockets are allocated */
case CL_FREE:
statlist_remove(&client->head, &free_client_list);
break;
+ case CL_JUSTFREE:
+ statlist_remove(&client->head, &justfree_client_list);
+ break;
case CL_LOGIN:
statlist_remove(&client->head, &login_client_list);
break;
/* put to new location */
switch (client->state) {
case CL_FREE:
- /* use LIFO the keep cache warm */
statlist_prepend(&client->head, &free_client_list);
break;
+ case CL_JUSTFREE:
+ statlist_append(&client->head, &justfree_client_list);
+ break;
case CL_LOGIN:
statlist_append(&client->head, &login_client_list);
break;
case SV_FREE:
statlist_remove(&server->head, &free_server_list);
break;
+ case SV_JUSTFREE:
+ statlist_remove(&server->head, &justfree_server_list);
+ break;
case SV_LOGIN:
statlist_remove(&server->head, &pool->new_server_list);
break;
/* put to new location */
switch (server->state) {
case SV_FREE:
- /* use LIFO the keep cache warm */
statlist_prepend(&server->head, &free_server_list);
break;
+ case SV_JUSTFREE:
+ statlist_append(&server->head, &justfree_server_list);
+ break;
case SV_LOGIN:
statlist_append(&server->head, &pool->new_server_list);
break;
case SV_USED:
- /* again, LIFO */
+ /* use LIFO */
statlist_prepend(&server->head, &pool->used_server_list);
break;
case SV_TESTED:
sbuf_answer(&server->sbuf, pkt_term, sizeof(pkt_term));
sbuf_close(&server->sbuf);
- change_server_state(server, SV_FREE);
+ change_server_state(server, SV_JUSTFREE);
}
/* drop client connection */
case CL_ACTIVE:
if (client->link) {
PgSocket *server = client->link;
- if (server->ready) {
+ /* ->ready may be set before all is sent */
+ if (server->ready && sbuf_has_no_state(&server->sbuf)) {
release_server(server);
} else {
server->link = NULL;
sbuf_close(&client->sbuf);
- change_client_state(client, CL_FREE);
+ change_client_state(client, CL_JUSTFREE);
}
/* the pool needs new connection, if possible */
SEND_CancelRequest(res, server, req->cancel_key);
- change_client_state(req, CL_FREE);
+ change_client_state(req, CL_JUSTFREE);
}
bool use_client_socket(int fd, PgAddr *addr,
}
}
+/* move objects from justfree_* to free_* lists */
+void reuse_just_freed_objects(void)
+{
+ List *tmp, *item;
+ PgSocket *sk;
+
+ /*
+ * Obviously, if state would be set to *_FREE,
+ * they could be moved in one go.
+ */
+ statlist_for_each_safe(item, &justfree_client_list, tmp) {
+ sk = container_of(item, PgSocket, head);
+ change_client_state(sk, CL_FREE);
+ }
+ statlist_for_each_safe(item, &justfree_server_list, tmp) {
+ sk = container_of(item, PgSocket, head);
+ change_server_state(sk, SV_FREE);
+ }
+}
+
void create_auth_cache(void);
+void reuse_just_freed_objects(void);
+
{
SBuf *sbuf = arg;
+ /* sbuf was closed before in this loop */
+ if (!sbuf->sock)
+ return;
+
/* prepare normal situation for sbuf_recv_cb() */
sbuf->wait_send = 0;
sbuf_wait_for_data(sbuf);
int res, avail;
uint8 *pos;
+ Assert(sbuf->dst || !sbuf->send_remain);
+
try_more:
/* how much data is available for sending */
avail = sbuf->recv_pos - sbuf->send_pos;
if (avail == 0)
return true;
+ if (sbuf->dst->sock == 0) {
+ log_error("sbuf_send_pending: no dst sock?");
+ return false;
+ }
+
/* actually send it */
pos = sbuf->buf + sbuf->send_pos;
res = safe_send(sbuf->dst->sock, pos, avail, 0);
int free, ok;
SBuf *sbuf = arg;
+ /* sbuf was closed before in this loop */
+ if (!sbuf->sock)
+ return;
+
/* reading should be disabled when waiting */
Assert(sbuf->wait_send == 0);
/* now handle it */
ok = sbuf_process_pending(sbuf);
+ if (!ok)
+ return;
/* if the buffer is full, there can be more data available */
- if (ok && sbuf->recv_pos == cf_sbuf_len)
+ if (sbuf->recv_pos == cf_sbuf_len)
goto try_more;
/* clean buffer */
sbuf_try_resync(sbuf);
/* notify proto that all is sent */
- if (sbuf->send_pos == sbuf->recv_pos && sbuf->pkt_remain == 0)
+ if (sbuf_has_no_state(sbuf))
sbuf_call_proto(sbuf, SBUF_EV_FLUSH);
}
Assert(is_server_socket(server));
Assert(server->state != SV_FREE);
+ if (server->state == SV_JUSTFREE) {
+ /* SBuf should catch the case */
+ slog_warning(server, "state=SV_JUSTFREE, should not happen");
+ return false;
+ }
+
switch (evtype) {
case SBUF_EV_RECV_FAILED:
disconnect_server(server, false, "server conn crashed?");