1 /****************************************************************\
2 *                                                                *
3 *  Efficient Memory Allocation Routines                          *
4 *                                                                *
5 *  Guy St.C. Slater..   mailto:guy@ebi.ac.uk                     *
6 *  Copyright (C) 2000-2009.  All Rights Reserved.                *
7 *                                                                *
8 *  This source code is distributed under the terms of the        *
9 *  GNU General Public License, version 3. See the file COPYING   *
10 *  or http://www.gnu.org/licenses/gpl.txt for details            *
11 *                                                                *
12 *  If you use this code, please keep this notice intact.         *
13 *                                                                *
14 \****************************************************************/
15 
16 #include <string.h>
17 #include "recyclebin.h"
18 
19 #ifndef USE_PTHREADS
20 static GTree *global_recycle_bin_tree = NULL;
21 #endif /* USE_PTHREADS */
22 
23 #ifndef USE_PTHREADS
RecycleBin_compare(gconstpointer a,gconstpointer b)24 static gint RecycleBin_compare(gconstpointer a,
25                                gconstpointer b){
26     return a - b;
27     }
28 #endif /* USE_PTHREADS */
29 
RecycleBin_create(gchar * name,gsize node_size,gint nodes_per_chunk)30 RecycleBin *RecycleBin_create(gchar *name, gsize node_size,
31                               gint nodes_per_chunk){
32     register RecycleBin *recycle_bin = g_new(RecycleBin, 1);
33     g_assert(name);
34     g_assert(node_size >= sizeof(gpointer));
35     recycle_bin->ref_count = 1;
36     recycle_bin->name = g_strdup(name);
37     recycle_bin->chunk_list = g_ptr_array_new();
38     recycle_bin->chunk_pos = nodes_per_chunk;
39     recycle_bin->nodes_per_chunk = nodes_per_chunk;
40     recycle_bin->node_size = node_size;
41     recycle_bin->count = 0;
42     recycle_bin->recycle = NULL;
43 #ifndef USE_PTHREADS
44     if(!global_recycle_bin_tree)
45         global_recycle_bin_tree = g_tree_new(RecycleBin_compare);
46     g_tree_insert(global_recycle_bin_tree, recycle_bin, recycle_bin);
47 #endif /* USE_PTHREADS */
48     return recycle_bin;
49     }
50 
RecycleBin_destroy(RecycleBin * recycle_bin)51 void RecycleBin_destroy(RecycleBin *recycle_bin){
52     register gint i;
53     if(--recycle_bin->ref_count)
54         return;
55 #ifndef USE_PTHREADS
56     g_assert(global_recycle_bin_tree);
57     g_assert(g_tree_lookup(global_recycle_bin_tree, recycle_bin));
58     g_tree_remove(global_recycle_bin_tree, recycle_bin);
59     if(!g_tree_nnodes(global_recycle_bin_tree)){
60         g_tree_destroy(global_recycle_bin_tree);
61         global_recycle_bin_tree = NULL;
62         }
63 #endif /* USE_PTHREADS */
64     for(i = 0; i < recycle_bin->chunk_list->len; i++)
65         g_free(recycle_bin->chunk_list->pdata[i]);
66     g_ptr_array_free(recycle_bin->chunk_list, TRUE);
67     g_free(recycle_bin->name);
68     g_free(recycle_bin);
69     return;
70     }
71 
RecycleBin_share(RecycleBin * recycle_bin)72 RecycleBin *RecycleBin_share(RecycleBin *recycle_bin){
73     g_assert(recycle_bin);
74     recycle_bin->ref_count++;
75     return recycle_bin;
76     }
77 
RecycleBin_memory_usage(RecycleBin * recycle_bin)78 gsize RecycleBin_memory_usage(RecycleBin *recycle_bin){
79     g_assert(recycle_bin);
80     return recycle_bin->node_size
81          * recycle_bin->nodes_per_chunk
82          * recycle_bin->chunk_list->len;
83     }
84 
RecycleBin_alloc(RecycleBin * recycle_bin)85 gpointer RecycleBin_alloc(RecycleBin *recycle_bin){
86     register RecycleBin_Node *node;
87     register gchar *chunk;
88     g_assert(recycle_bin);
89     if(recycle_bin->recycle){
90         node = (gpointer)recycle_bin->recycle;
91         recycle_bin->recycle = node->next;
92     } else {
93         if(recycle_bin->chunk_pos == recycle_bin->nodes_per_chunk){
94             chunk = g_malloc(recycle_bin->nodes_per_chunk
95                            * recycle_bin->node_size);
96             g_ptr_array_add(recycle_bin->chunk_list, chunk);
97             recycle_bin->chunk_pos = 1;
98             node = (RecycleBin_Node*)chunk;
99         } else {
100             chunk = recycle_bin->chunk_list->pdata
101                    [recycle_bin->chunk_list->len-1];
102             node = (RecycleBin_Node*) (chunk
103                     + (recycle_bin->node_size
104                      * recycle_bin->chunk_pos++));
105             }
106         }
107     recycle_bin->count++;
108     return (gpointer)node;
109     }
110 
RecycleBin_alloc_blank(RecycleBin * recycle_bin)111 gpointer RecycleBin_alloc_blank(RecycleBin *recycle_bin){
112     register gpointer data = RecycleBin_alloc(recycle_bin);
113     memset(data, 0, recycle_bin->node_size);
114     return data;
115     }
116 
RecycleBin_recycle(RecycleBin * recycle_bin,gpointer data)117 void RecycleBin_recycle(RecycleBin *recycle_bin, gpointer data){
118     register RecycleBin_Node *node = data;
119     g_assert(node != recycle_bin->recycle);
120     node->next = recycle_bin->recycle;
121     recycle_bin->recycle = node;
122     recycle_bin->count--;
123     return;
124     }
125 
126 #ifndef USE_PTHREADS
RecycleBin_profile_traverse(gpointer key,gpointer value,gpointer data)127 static gint RecycleBin_profile_traverse(gpointer key,
128                                         gpointer value,
129                                         gpointer data){
130     register RecycleBin *recycle_bin = value;
131     g_assert(recycle_bin);
132     g_message("  RecycleBin [%s] %d Mb (%d items)",
133               recycle_bin->name,
134               (gint)(RecycleBin_memory_usage(recycle_bin)>>20),
135               recycle_bin->count);
136     return FALSE;
137     }
138 #endif /* USE_PTHREADS */
139 
RecycleBin_profile(void)140 void RecycleBin_profile(void){
141     g_message("BEGIN RecycleBin profile");
142 #ifdef USE_PTHREADS
143     g_message("multi-threaded RecycleBin_profile() not implemented");
144 #else /* USE_PTHREADS */
145     if(global_recycle_bin_tree)
146         g_tree_traverse(global_recycle_bin_tree,
147                         RecycleBin_profile_traverse, G_IN_ORDER, NULL);
148     else
149         g_message("no active RecycleBins");
150 #endif /* USE_PTHREADS */
151     g_message("END RecycleBin profile");
152     return;
153     }
154 
155 /* FIXME: could use Trash Stacks when migrating to glib-2 */
156 
157