From 8ce1b25ebc6faf15d5dab6b83349afa9dc09ef91 Mon Sep 17 00:00:00 2001 From: Marko Kreen Date: Sat, 20 Oct 2007 09:18:25 +0000 Subject: [PATCH] common object allocator --- Makefile | 4 +- src/admin.c | 8 +-- src/bouncer.h | 1 + src/main.c | 2 + src/objects.c | 102 ++++++++++++++------------------------ src/objects.h | 7 +-- src/slab.c | 135 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/slab.h | 16 ++++++ 8 files changed, 200 insertions(+), 75 deletions(-) create mode 100644 src/slab.c create mode 100644 src/slab.h diff --git a/Makefile b/Makefile index 041a7fb..24fb257 100644 --- 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 diff --git a/src/admin.c b/src/admin.c index 1568d30..65d0e5f 100644 --- a/src/admin.c +++ b/src/admin.c @@ -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; } diff --git a/src/bouncer.h b/src/bouncer.h index 86cd6c0..d3c9b08 100644 --- a/src/bouncer.h +++ b/src/bouncer.h @@ -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" diff --git a/src/main.c b/src/main.c index 1de356a..88e353a 100644 --- a/src/main.c +++ b/src/main.c @@ -472,6 +472,8 @@ int main(int argc, char *argv[]) load_config(false); + init_caches(); + /* need to do that after loading config */ check_limits(); diff --git a/src/objects.c b/src/objects.c index f68a336..17c3282 100644 --- a/src/objects.c +++ b/src/objects.c @@ -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; diff --git a/src/objects.h b/src/objects.h index 8062308..9119000 100644 --- a/src/objects.h +++ b/src/objects.h @@ -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 index 0000000..30a485a --- /dev/null +++ b/src/slab.c @@ -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 index 0000000..eebcbe9 --- /dev/null +++ b/src/slab.h @@ -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); + -- 2.40.0