Atrinik Server 2.5
server/mempool.c
Go to the documentation of this file.
00001 /************************************************************************
00002 *            Atrinik, a Multiplayer Online Role Playing Game            *
00003 *                                                                       *
00004 *    Copyright (C) 2009-2011 Alex Tokar and Atrinik Development Team    *
00005 *                                                                       *
00006 * Fork from Daimonin (Massive Multiplayer Online Role Playing Game)     *
00007 * and Crossfire (Multiplayer game for X-windows).                       *
00008 *                                                                       *
00009 * This program is free software; you can redistribute it and/or modify  *
00010 * it under the terms of the GNU General Public License as published by  *
00011 * the Free Software Foundation; either version 2 of the License, or     *
00012 * (at your option) any later version.                                   *
00013 *                                                                       *
00014 * This program is distributed in the hope that it will be useful,       *
00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00017 * GNU General Public License for more details.                          *
00018 *                                                                       *
00019 * You should have received a copy of the GNU General Public License     *
00020 * along with this program; if not, write to the Free Software           *
00021 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.             *
00022 *                                                                       *
00023 * The author can be reached at admin@atrinik.org                        *
00024 ************************************************************************/
00025 
00061 #include <global.h>
00062 
00063 #ifdef MEMPOOL_OBJECT_TRACKING
00064 /* for debugging only! */
00065 static struct mempool_chunk *used_object_list = NULL;
00066 static uint32 chunk_tracking_id = 1;
00067 #define MEMPOOL_OBJECT_FLAG_FREE 1
00068 #define MEMPOOL_OBJECT_FLAG_USED 2
00069 #endif
00070 
00075 struct mempool_chunk end_marker;
00076 
00077 int nrof_mempools = 0;
00078 struct mempool *mempools[MAX_NROF_MEMPOOLS];
00079 
00080 #ifdef MEMPOOL_TRACKING
00081 struct mempool *pool_puddle;
00082 #endif
00083 
00084 struct mempool *pool_object, *pool_objectlink, *pool_player, *pool_bans, *pool_parties;
00085 
00089 uint32 nearest_pow_two_exp(uint32 n)
00090 {
00091     static const uint32 exp_lookup[65]  =
00092     {
00093         0, 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6,
00094         6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6
00095     };
00096     uint32 i;
00097 
00098     if (n <= 64)
00099     {
00100         return exp_lookup[n];
00101     }
00102 
00103     for (i = 7; (uint32) (1 << i) < n; i++)
00104     {
00105     }
00106 
00107     return i;
00108 }
00109 
00116 void setup_poolfunctions(struct mempool *pool, chunk_constructor constructor, chunk_destructor destructor)
00117 {
00118     pool->constructor = constructor;
00119     pool->destructor = destructor;
00120 }
00121 
00133 struct mempool *create_mempool(const char *description, uint32 expand, uint32 size, uint32 flags, chunk_initialisator initialisator, chunk_deinitialisator deinitialisator, chunk_constructor constructor, chunk_destructor destructor)
00134 {
00135     int i;
00136     struct mempool *pool;
00137 
00138     if (nrof_mempools >= MAX_NROF_MEMPOOLS)
00139     {
00140         LOG(llevError, "Too many memory pools registered. Please increase the MAX_NROF_MEMPOOLS constant in mempools.h\n");
00141     }
00142 
00143     pool = calloc(1, sizeof(struct mempool));
00144 
00145     mempools[nrof_mempools] = pool;
00146 
00147     pool->chunk_description = description;
00148     pool->expand_size = expand;
00149     pool->chunksize = size;
00150     pool->flags = flags;
00151     pool->initialisator = initialisator;
00152     pool->deinitialisator = deinitialisator;
00153     pool->constructor = constructor;
00154     pool->destructor = destructor;
00155 
00156 #if MEMORY_DEBUG
00157     pool->flags |= MEMPOOL_BYPASS_POOLS;
00158 #endif
00159 
00160     for (i = 0; i < MEMPOOL_NROF_FREELISTS; i++)
00161     {
00162         pool->freelist[i] = &end_marker;
00163         pool->nrof_free[i] = 0;
00164         pool->nrof_allocated[i] = 0;
00165     }
00166 
00167 #ifdef MEMPOOL_TRACKING
00168     pool->first_puddle_info = NULL;
00169 #endif
00170 
00171     nrof_mempools++;
00172 
00173     return pool;
00174 }
00175 
00178 void init_mempools()
00179 {
00180 #ifdef MEMPOOL_TRACKING
00181     pool_puddle = create_mempool("puddles", 10, sizeof(struct puddle_info), MEMPOOL_ALLOW_FREEING, NULL, NULL, NULL, NULL);
00182 #endif
00183     pool_object = create_mempool("objects", OBJECT_EXPAND, sizeof(object), 0, NULL, NULL, (chunk_constructor) initialize_object, (chunk_destructor) destroy_object);
00184     pool_player = create_mempool("players", 25, sizeof(player), MEMPOOL_BYPASS_POOLS, NULL, NULL, NULL, NULL);
00185     pool_objectlink = create_mempool("object links", 500, sizeof(objectlink), 0, NULL, NULL, NULL, NULL);
00186     pool_bans = create_mempool("bans", 25, sizeof(_ban_struct), 0, NULL, NULL, NULL, NULL);
00187     pool_parties = create_mempool("parties", 25, sizeof(party_struct), 0, NULL, NULL, NULL, NULL);
00188 
00189     /* Initialize end-of-list pointers and a few other values*/
00190     removed_objects = &end_marker;
00191 
00192     /* Set up container for "loose" objects */
00193     initialize_object(&void_container);
00194     void_container.type = VOID_CONTAINER;
00195     FREE_AND_COPY_HASH(void_container.name, "<void container>");
00196 }
00197 
00201 static void free_mempool(struct mempool *pool)
00202 {
00203     free(pool);
00204 }
00205 
00208 void free_mempools()
00209 {
00210     LOG(llevDebug, "Freeing memory pools.\n");
00211 #ifdef MEMPOOL_TRACKING
00212     free_mempool(pool_puddle);
00213 #endif
00214     free_mempool(pool_object);
00215     free_mempool(pool_player);
00216     free_mempool(pool_objectlink);
00217     free_mempool(pool_bans);
00218     free_mempool(pool_parties);
00219 
00220     FREE_AND_CLEAR_HASH2(void_container.name);
00221 }
00222 
00230 static void expand_mempool(struct mempool *pool, uint32 arraysize_exp)
00231 {
00232     uint32 i;
00233     struct mempool_chunk *first, *ptr;
00234     int chunksize_real, nrof_arrays;
00235 
00236     if (pool->nrof_free[arraysize_exp] > 0)
00237     {
00238         LOG(llevBug, "expand_mempool() called with chunks still available in pool\n");
00239     }
00240 
00241     nrof_arrays = pool->expand_size >> arraysize_exp;
00242 
00243     if (nrof_arrays == 0)
00244     {
00245         LOG(llevDebug, "expand_mempool() called with too big array size for its expand_size\n");
00246         nrof_arrays = 1;
00247     }
00248 
00249     chunksize_real = sizeof(struct mempool_chunk) + (pool->chunksize << arraysize_exp);
00250     first = (struct mempool_chunk *) calloc(1, nrof_arrays * chunksize_real);
00251 
00252     if (first == NULL)
00253     {
00254         LOG(llevError, "expand_mempool(): Out of memory.\n");
00255     }
00256 
00257     pool->freelist[arraysize_exp] = first;
00258     pool->nrof_allocated[arraysize_exp] += nrof_arrays;
00259     pool->nrof_free[arraysize_exp] = nrof_arrays;
00260 
00261     /* Set up the linked list */
00262     ptr = first;
00263 
00264     for (i = 0; (int) i < nrof_arrays - 1; i++)
00265     {
00266 #ifdef DEBUG_MEMPOOL_OBJECT_TRACKING
00267         ptr->obj_next = ptr->obj_prev = 0;
00268         ptr->pool = pool;
00269         /* This is a real, unique object ID. Allows tracking beyond
00270          * get/free objects */
00271         ptr->id = chunk_tracking_id++;
00272         ptr->flags |= MEMPOOL_OBJECT_FLAG_FREE;
00273 #endif
00274         if (pool->initialisator)
00275         {
00276             pool->initialisator(MEM_USERDATA(ptr));
00277         }
00278 
00279         ptr = ptr->next = (struct mempool_chunk *) (((char *) ptr) + chunksize_real);
00280     }
00281 
00282     /* And the last element */
00283     ptr->next = &end_marker;
00284 
00285     if (pool->initialisator)
00286     {
00287         pool->initialisator(MEM_USERDATA(ptr));
00288     }
00289 
00290 #ifdef DEBUG_MEMPOOL_OBJECT_TRACKING
00291     ptr->obj_next = ptr->obj_prev = 0; /* secure */
00292     ptr->pool = pool;
00293     /* This is a real, unique object ID. Allows tracking beyond get/free
00294      * objects */
00295     ptr->id = chunk_tracking_id++;
00296     ptr->flags |= MEMPOOL_OBJECT_FLAG_FREE;
00297 #endif
00298 
00299 #ifdef MEMPOOL_TRACKING
00300     /* Track the allocation of puddles */
00301     {
00302         struct puddle_info *p = get_poolchunk(pool_puddle);
00303         p->first_chunk = first;
00304         p->next = pool->first_puddle_info;
00305         pool->first_puddle_info = p;
00306     }
00307 #endif
00308 }
00309 
00316 void *get_poolchunk_array_real(struct mempool *pool, uint32 arraysize_exp)
00317 {
00318     struct mempool_chunk *new_obj;
00319 
00320     if (pool->flags & MEMPOOL_BYPASS_POOLS)
00321     {
00322         new_obj = calloc(1, sizeof(struct mempool_chunk) + (pool->chunksize << arraysize_exp));
00323         pool->nrof_allocated[arraysize_exp]++;
00324     }
00325     else
00326     {
00327         if (pool->nrof_free[arraysize_exp] == 0)
00328         {
00329             expand_mempool(pool, arraysize_exp);
00330         }
00331 
00332         new_obj = pool->freelist[arraysize_exp];
00333         pool->freelist[arraysize_exp] = new_obj->next;
00334         pool->nrof_free[arraysize_exp]--;
00335     }
00336 
00337     new_obj->next = NULL;
00338 
00339     if (pool->constructor)
00340     {
00341         pool->constructor(MEM_USERDATA(new_obj));
00342     }
00343 
00344 #ifdef DEBUG_MEMPOOL_OBJECT_TRACKING
00345     if (new_obj->obj_prev || new_obj->obj_next)
00346     {
00347         LOG(llevDebug, "get_poolchunk_array_real() object >%d< is in used_object list!!\n", new_obj->id);
00348     }
00349 
00350     /* Put it in front of the used object list */
00351     new_obj->obj_next = used_object_list;
00352 
00353     if (new_obj->obj_next)
00354     {
00355         new_obj->obj_next->obj_prev = new_obj;
00356     }
00357 
00358     used_object_list = new_obj;
00359     new_obj->flags &= ~MEMPOOL_OBJECT_FLAG_FREE;
00360     new_obj->flags |= MEMPOOL_OBJECT_FLAG_USED;
00361 #endif
00362 
00363     return MEM_USERDATA(new_obj);
00364 }
00365 
00374 void return_poolchunk_array_real(void *data, uint32 arraysize_exp, struct mempool *pool)
00375 {
00376     struct mempool_chunk *old = MEM_POOLDATA(data);
00377 
00378     if (CHUNK_FREE(data))
00379     {
00380         LOG(llevBug, "return_poolchunk_array_real() on already free chunk (pool \"%s\")\n", pool->chunk_description);
00381         return;
00382     }
00383 
00384 #ifdef DEBUG_MEMPOOL_OBJECT_TRACKING
00385     if (old->obj_next)
00386     {
00387         old->obj_next->obj_prev = old->obj_prev;
00388     }
00389 
00390     if (old->obj_prev)
00391     {
00392         old->obj_prev->obj_next = old->obj_next;
00393     }
00394     else
00395     {
00396         used_object_list = old->obj_next;
00397     }
00398 
00399     old->obj_next = old->obj_prev = 0;
00400     old->flags &= ~MEMPOOL_OBJECT_FLAG_USED;
00401     old->flags |= MEMPOOL_OBJECT_FLAG_FREE;
00402 #endif
00403 
00404     if (pool->destructor)
00405     {
00406         pool->destructor(data);
00407     }
00408 
00409     if (pool->flags & MEMPOOL_BYPASS_POOLS)
00410     {
00411         if (pool->deinitialisator)
00412         {
00413             pool->deinitialisator(MEM_USERDATA(old));
00414         }
00415 
00416         free(old);
00417         pool->nrof_allocated[arraysize_exp]--;
00418     }
00419     else
00420     {
00421         old->next = pool->freelist[arraysize_exp];
00422         pool->freelist[arraysize_exp] = old;
00423         pool->nrof_free[arraysize_exp]++;
00424     }
00425 }
00426 
00435 void dump_mempool_statistics(object *op, int *sum_used, int *sum_alloc)
00436 {
00437     int j, k;
00438     char buf[MAX_BUF];
00439 
00440     for (j = 0; j < nrof_mempools; j++)
00441     {
00442         for (k = 0; k < MEMPOOL_NROF_FREELISTS; k++)
00443         {
00444             if (mempools[j]->nrof_allocated[k] > 0)
00445             {
00446                 int ob_used = mempools[j]->nrof_allocated[k] - mempools[j]->  nrof_free[k], ob_free = mempools[j]->nrof_free[k];
00447                 int mem_used = ob_used * ((mempools[j]->chunksize << k) + sizeof(struct mempool_chunk));
00448                 int mem_free = ob_free * ((mempools[j]->chunksize << k) + sizeof(struct mempool_chunk));
00449 
00450                 snprintf(buf, sizeof(buf), "%4d used (%4d free) %s[%3d]: %d (%d)", ob_used, ob_free, mempools[j]->chunk_description, 1 << k, mem_used, mem_free);
00451 
00452                 if (op)
00453                 {
00454                     new_draw_info(0, COLOR_WHITE, op, buf);
00455                 }
00456 
00457                 LOG(llevSystem, "%s\n", buf);
00458 
00459                 if (sum_used)
00460                 {
00461                     *sum_used += mem_used;
00462                 }
00463 
00464                 if (sum_alloc)
00465                 {
00466                     *sum_alloc += mem_used + mem_free;
00467                 }
00468             }
00469         }
00470     }
00471 }
00472 
00473 #ifdef DEBUG_MEMPOOL_OBJECT_TRACKING
00474 
00481 void check_use_object_list()
00482 {
00483     struct mempool_chunk *chunk;
00484 
00485     for (chunk = used_object_list; chunk; chunk = chunk->obj_next)
00486     {
00487 #ifdef MEMPOOL_TRACKING
00488         /* ignore for now */
00489         if (chunk->pool == pool_puddle)
00490         {
00491         }
00492         else
00493 #endif
00494         if (chunk->pool == pool_object)
00495         {
00496             object *tmp2, *tmp = MEM_USERDATA(chunk);
00497 
00498             if (QUERY_FLAG(tmp, FLAG_REMOVED))
00499             {
00500                 LOG(llevDebug, "check_use_object_list(): object >%s< (%d) has removed flag set!\n", query_name(tmp), chunk->id);
00501             }
00502 
00503             /* We are on a map */
00504             if (tmp->map)
00505             {
00506                 if (tmp->map->in_memory != MAP_IN_MEMORY)
00507                 {
00508                     LOG(llevDebug, "check_use_object_list(): object >%s< (%d) has invalid map! >%d<!\n", query_name(tmp), tmp->map->name ? tmp->map->name : "NONE", chunk->id);
00509                 }
00510                 else
00511                 {
00512                     for (tmp2 = get_map_ob(tmp->map, tmp->x, tmp->y); tmp2; tmp2 = tmp2->above)
00513                     {
00514                         if (tmp2 == tmp)
00515                         {
00516                             goto goto_object_found;
00517                         }
00518                     }
00519 
00520                     LOG(llevDebug, "check_use_object_list(): object >%s< (%d) has invalid map! >%d<!\n", query_name(tmp), tmp->map->name ? tmp->map->name : "NONE", chunk->id);
00521                 }
00522             }
00523             else if (tmp->env)
00524             {
00525                 /* Object claims to be here... Let's check it IS here */
00526                 for (tmp2 = tmp->env->inv; tmp2; tmp2 = tmp2->below)
00527                 {
00528                     if (tmp2 == tmp)
00529                     {
00530                         goto goto_object_found;
00531                     }
00532                 }
00533 
00534                 LOG(llevDebug, "check_use_object_list(): object >%s< (%d) has invalid env >%d<!\n", query_name(tmp), query_name(tmp->env), chunk->id);
00535             }
00536             /* Where are we? */
00537             else
00538             {
00539                 LOG(llevDebug, "check_use_object_list(): object >%s< (%d) has no env/map\n", query_name(tmp), chunk->id);
00540             }
00541         }
00542         else if (chunk->pool == pool_player)
00543         {
00544             player *tmp = MEM_USERDATA(chunk);
00545         }
00546         else
00547         {
00548             LOG(llevDebug, "check_use_object_list(): wrong pool ID! (%s - %d)", chunk->pool->chunk_description, chunk->id);
00549         }
00550 
00551         goto_object_found:;
00552     }
00553 }
00554 #endif