]> granicus.if.org Git - pgbouncer/commitdiff
common object allocator
authorMarko Kreen <markokr@gmail.com>
Sat, 20 Oct 2007 09:18:25 +0000 (09:18 +0000)
committerMarko Kreen <markokr@gmail.com>
Sat, 20 Oct 2007 09:18:25 +0000 (09:18 +0000)
Makefile
src/admin.c
src/bouncer.h
src/main.c
src/objects.c
src/objects.h
src/slab.c [new file with mode: 0644]
src/slab.h [new file with mode: 0644]

index 041a7fb1ad2f6e079e88057967cbfd6989419cf9..24fb257c90e5d5055a39625a2cfb8bd01c61366c 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2,10 +2,10 @@
 # sources
 SRCS = client.c loader.c objects.c pooler.c proto.c sbuf.c server.c util.c \
        admin.c stats.c takeover.c md5.c janitor.c pktbuf.c system.c main.c \
-       varcache.c aatree.c hash.c
+       varcache.c aatree.c hash.c slab.c
 HDRS = client.h loader.h objects.h pooler.h proto.h sbuf.h server.h util.h \
        admin.h stats.h takeover.h md5.h janitor.h pktbuf.h system.h bouncer.h \
-       list.h mbuf.h varcache.h aatree.h hash.h
+       list.h mbuf.h varcache.h aatree.h hash.h slab.h
 
 # data & dirs to include in tgz
 DOCS = doc/overview.txt doc/usage.txt doc/config.txt doc/todo.txt
index 1568d30bd4c6e3f62d839729a29d26a41ee13258..65d0e5fb8051ac29af9ccc1809f43ffee4551fae 100644 (file)
@@ -376,11 +376,11 @@ static bool admin_show_lists(PgSocket *admin, const char *arg)
        SENDLIST("databases", statlist_count(&database_list));
        SENDLIST("users", statlist_count(&user_list));
        SENDLIST("pools", statlist_count(&pool_list));
-       SENDLIST("free_clients", statlist_count(&free_client_list));
-       SENDLIST("used_clients", get_active_client_count());
+       SENDLIST("free_clients", objcache_free_count(client_cache));
+       SENDLIST("used_clients", objcache_active_count(client_cache));
        SENDLIST("login_clients", statlist_count(&login_client_list));
-       SENDLIST("free_servers", statlist_count(&free_server_list));
-       SENDLIST("used_servers", get_active_server_count());
+       SENDLIST("free_servers", objcache_free_count(server_cache));
+       SENDLIST("used_servers", objcache_active_count(server_cache));
        admin_flush(admin, buf, "SHOW");
        return true;
 }
index 86cd6c0011f8f623c2268bcf72eccf78bb9e171f..d3c9b08c787dc3518c181bc97c5b6738243508ca 100644 (file)
@@ -74,6 +74,7 @@ typedef struct PktHdr PktHdr;
 #include "sbuf.h"
 #include "pktbuf.h"
 #include "varcache.h"
+#include "slab.h"
 
 #include "admin.h"
 #include "loader.h"
index 1de356a1eb983c45177127892e30ccced2d9c8a8..88e353a158a783d903e730665177a34146431170 100644 (file)
@@ -472,6 +472,8 @@ int main(int argc, char *argv[])
 
        load_config(false);
 
+       init_caches();
+
        /* need to do that after loading config */
        check_limits();
 
index f68a33653ac1da03abfceac5fb9736036098401e..17c32827cb2d054c0b18fb98a9635421d0b3125e 100644 (file)
@@ -34,10 +34,11 @@ Tree user_tree;
  * they are always in either active or free lists
  * in addition to others.
  */
-STATLIST(free_client_list);
-STATLIST(free_server_list);
 STATLIST(login_client_list);
 
+ObjectCache *server_cache;
+ObjectCache *client_cache;
+
 /*
  * libevent may still report events when event_del()
  * is called from somewhere else.  So hide just freed
@@ -46,26 +47,23 @@ STATLIST(login_client_list);
 static STATLIST(justfree_client_list);
 static STATLIST(justfree_server_list);
 
-/* how many client sockets are allocated */
-static int absolute_client_count = 0;
-/* how many server sockets are allocated */
-static int absolute_server_count = 0;
-
 /* fast way to get number of active clients */
 int get_active_client_count(void)
 {
-       return absolute_client_count - statlist_count(&free_client_list);
+       return objcache_active_count(client_cache);
 }
 
 /* fast way to get number of active servers */
 int get_active_server_count(void)
 {
-       return absolute_server_count - statlist_count(&free_server_list);
+       return objcache_active_count(server_cache);
 }
 
 /* this should be called on free socket that is put into use */
-static void clean_socket(PgSocket *sk)
+static void clean_socket(void *obj)
 {
+       PgSocket *sk = obj;
+
        sk->link = NULL;
        sk->pool = NULL;
 
@@ -86,56 +84,43 @@ static void clean_socket(PgSocket *sk)
        sk->setting_vars = 0;
 }
 
-/* allocate & fill client socket */
-static PgSocket *new_client(void)
+static void construct_client(void *obj)
 {
-       PgSocket *client;
-
-       /* get free PgSocket */
-       client = first_socket(&free_client_list);
-       if (client) {
-               clean_socket(client);
-               return client;
-       }
-
-       client = zmalloc(sizeof(*client) + cf_sbuf_len);
-       if (!client)
-               return NULL;
+       PgSocket *client = obj;
 
        list_init(&client->head);
        sbuf_init(&client->sbuf, client_proto, client);
-       statlist_prepend(&client->head, &free_client_list);
        client->state = CL_FREE;
-
-       absolute_client_count++;
-
-       return client;
 }
 
-/* allocate & fill server socket */
-static PgSocket *new_server(void)
+static void construct_server(void *obj)
 {
-       PgSocket *server;
-
-       /* get free PgSocket */
-       server = first_socket(&free_server_list);
-       if (server) {
-               clean_socket(server);
-               return server;
-       }
-
-       server = zmalloc(sizeof(*server) + cf_sbuf_len);
-       if (!server)
-               return NULL;
+       PgSocket *server = obj;
 
        list_init(&server->head);
        sbuf_init(&server->sbuf, server_proto, server);
-       statlist_prepend(&server->head, &free_server_list);
        server->state = SV_FREE;
+}
 
-       absolute_server_count++;
+/* compare string with PgUser->name, for usage with btree */
+static int user_node_cmp(long userptr, Node *node)
+{
+       const char *name = (const char *)userptr;
+       PgUser *user = container_of(node, PgUser, tree_node);
+       return strcmp(name, user->name);
+}
+
+void init_objects(void)
+{
+       tree_init(&user_tree, user_node_cmp, NULL);
+}
 
-       return server;
+void init_caches(void)
+{
+       server_cache = objcache_create("server_cache", sizeof(PgSocket) + cf_sbuf_len, 8,
+                                      construct_server, clean_socket);
+       client_cache = objcache_create("client_cache", sizeof(PgSocket) + cf_sbuf_len, 8,
+                                      construct_client, clean_socket);
 }
 
 /* state change means moving between lists */
@@ -146,7 +131,6 @@ void change_client_state(PgSocket *client, SocketState newstate)
        /* remove from old location */
        switch (client->state) {
        case CL_FREE:
-               statlist_remove(&client->head, &free_client_list);
                break;
        case CL_JUSTFREE:
                statlist_remove(&client->head, &justfree_client_list);
@@ -172,7 +156,7 @@ void change_client_state(PgSocket *client, SocketState newstate)
        /* put to new location */
        switch (client->state) {
        case CL_FREE:
-               statlist_prepend(&client->head, &free_client_list);
+               obj_free(client_cache, client);
                break;
        case CL_JUSTFREE:
                statlist_append(&client->head, &justfree_client_list);
@@ -202,7 +186,6 @@ void change_server_state(PgSocket *server, SocketState newstate)
        /* remove from old location */
        switch (server->state) {
        case SV_FREE:
-               statlist_remove(&server->head, &free_server_list);
                break;
        case SV_JUSTFREE:
                statlist_remove(&server->head, &justfree_server_list);
@@ -231,7 +214,7 @@ void change_server_state(PgSocket *server, SocketState newstate)
        /* put to new location */
        switch (server->state) {
        case SV_FREE:
-               statlist_prepend(&server->head, &free_server_list);
+               obj_free(server_cache, server);
                break;
        case SV_JUSTFREE:
                statlist_append(&server->head, &justfree_server_list);
@@ -378,19 +361,6 @@ PgDatabase *find_database(const char *name)
        return NULL;
 }
 
-/* compare string with PgUser->name, for usage with btree */
-static int user_node_cmp(long userptr, Node *node)
-{
-       const char *name = (const char *)userptr;
-       PgUser *user = container_of(node, PgUser, tree_node);
-       return strcmp(name, user->name);
-}
-
-void init_objects(void)
-{
-       tree_init(&user_tree, user_node_cmp, NULL);
-}
-
 /* find existing user */
 PgUser *find_user(const char *name)
 {
@@ -728,7 +698,7 @@ void launch_new_connection(PgPool *pool)
        }
 
        /* get free conn object */
-       server = new_server();
+       server = obj_alloc(server_cache);
        if (!server) {
                log_debug("launch_new_connection: no memory");
                return;
@@ -762,7 +732,7 @@ PgSocket * accept_client(int sock,
        PgSocket *client;
 
        /* get free PgSocket */
-       client = new_client();
+       client = obj_alloc(client_cache);
        if (!client)
                return NULL;
 
@@ -943,7 +913,7 @@ bool use_server_socket(int fd, PgAddr *addr,
        if (!pool)
                return false;
 
-       server = new_server();
+       server = obj_alloc(server_cache);
        if (!server)
                return false;
 
index 80623083bd03d1652d714c8933bbaa605c4d19c3..9119000f166072d019da3f9b7062bd718a7a5ad1 100644 (file)
@@ -21,9 +21,8 @@ extern Tree user_tree;
 extern StatList pool_list;
 extern StatList database_list;
 extern StatList login_client_list;
-extern StatList free_server_list;
-extern StatList free_client_list;
-extern StatList login_client_list;
+extern ObjectCache *client_cache;
+extern ObjectCache *server_cache;
 
 PgDatabase *find_database(const char *name);
 PgUser *find_user(const char *name);
@@ -66,3 +65,5 @@ void reuse_just_freed_objects(void);
 
 void init_objects(void);
 
+void init_caches(void);
+
diff --git a/src/slab.c b/src/slab.c
new file mode 100644 (file)
index 0000000..30a485a
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * PgBouncer - Lightweight connection pooler for PostgreSQL.
+ * 
+ * Copyright (c) 2007 Marko Kreen, Skype Technologies OÜ
+ * 
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * SLAB-like allocator, but without the complexities of deallocation...
+ *
+ * - On first alloc initializer is called for all objects.
+ * - On each release, cleaner is called.
+ * - When giving object out, nothing is done.
+ * - Writes obj header with List struct, expects it to be overwritten on use.
+ */
+
+#include "system.h"
+#include "list.h"
+#include "slab.h"
+
+/*
+ * Stores pre-initialized objects of one type.
+ */
+struct ObjectCache {
+       List head;
+       StatList freelist;
+       const char *name;
+       int obj_size;
+       int align;
+       int total_count;
+       obj_init_fn  init_func;
+       obj_clean_fn clean_func;
+};
+
+/* keep track of all caches */
+static STATLIST(objcache_list);
+
+/* make new cache */
+ObjectCache * objcache_create(const char *name, int obj_size, int align,
+                             obj_init_fn init_func,
+                             obj_clean_fn clean_func)
+{
+       ObjectCache *cache = malloc(sizeof(*cache));
+       if (!cache)
+               return NULL;
+       list_init(&cache->head);
+       statlist_init(&cache->freelist, name);
+       cache->name = name;
+       cache->obj_size = obj_size;
+       cache->align = align;
+       cache->total_count = 0;
+       cache->init_func = init_func;
+       cache->clean_func = clean_func;
+       statlist_append(&cache->head, &objcache_list);
+
+       return cache;
+}
+
+/* add new block of objects to cache */
+static void grow(ObjectCache *cache)
+{
+       int count, i, real_size;
+       char *area;
+       
+       real_size = (cache->obj_size + cache->align - 1) & ~(cache->align - 1);
+
+       count = 8192 / real_size;
+       if (count < 20)
+               count = 20;
+
+       area = malloc(count * real_size);
+       if (!area)
+               return;
+
+       memset(area, 0, count * real_size);
+       for (i = 0; i < count; i++) {
+               void *obj = area + i * real_size;
+               cache->init_func(obj);
+               statlist_append((List *)obj, &cache->freelist);
+       }
+
+       cache->total_count += count;
+}
+
+/* get free object from cache */
+void *obj_alloc(ObjectCache *cache)
+{
+       List *item;
+
+       item = statlist_pop(&cache->freelist);
+       if (item)
+               return item;
+
+       grow(cache);
+
+       return statlist_pop(&cache->freelist);
+}
+
+/* put object back to cache */
+void obj_free(ObjectCache *cache, void *obj)
+{
+       List *item = obj;
+       cache->clean_func(obj);
+       statlist_prepend(item, &cache->freelist);
+}
+
+/* total number of objects allocated from cache */
+int objcache_total_count(ObjectCache *cache)
+{
+       return cache->total_count;
+}
+
+/* free objects in cache */
+int objcache_free_count(ObjectCache *cache)
+{
+       return statlist_count(&cache->freelist);
+}
+
+/* number of objects in use */
+int objcache_active_count(ObjectCache *cache)
+{
+       return objcache_total_count(cache) - objcache_free_count(cache);
+}
+
diff --git a/src/slab.h b/src/slab.h
new file mode 100644 (file)
index 0000000..eebcbe9
--- /dev/null
@@ -0,0 +1,16 @@
+
+typedef struct ObjectCache ObjectCache;
+
+typedef void (*obj_init_fn)(void *obj);
+typedef void (*obj_clean_fn)(void *obj);
+
+ObjectCache *objcache_create(const char *name, int obj_size, int align,
+                            obj_init_fn init_func, obj_clean_fn clean_func);
+
+void * obj_alloc(ObjectCache *cache);
+void obj_free(ObjectCache *cache, void *obj);
+
+int objcache_total_count(ObjectCache *cache);
+int objcache_free_count(ObjectCache *cache);
+int objcache_active_count(ObjectCache *cache);
+