remove g_dataset_global_lock around destroy() notification here as well.
[platform/upstream/glib.git] / gdataset.c
1 /* GLIB - Library of useful routines for C programming
2  * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * gdataset.c: Generic dataset mechanism, similar to GtkObject data.
5  * Copyright (C) 1998 Tim Janik
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
20  */
21
22 /*
23  * Modified by the GLib Team and others 1997-1999.  See the AUTHORS
24  * file for a list of people on the GLib Team.  See the ChangeLog
25  * files for a list of changes.  These files are distributed with
26  * GLib at ftp://ftp.gtk.org/pub/gtk/. 
27  */
28
29 /* 
30  * MT safe ; FIXME: might still freeze, watch out, not thoroughly
31  * looked at yet.  
32  */
33
34 #include        <string.h>
35 #include        "glib.h"
36
37
38
39 /* --- defines --- */
40 #define G_QUARK_BLOCK_SIZE                      (512)
41 #define G_DATA_MEM_CHUNK_PREALLOC               (128)
42 #define G_DATA_CACHE_MAX                        (512)
43 #define G_DATASET_MEM_CHUNK_PREALLOC            (32)
44
45
46 /* --- structures --- */
47 typedef struct _GDataset GDataset;
48 struct _GData
49 {
50   GData *next;
51   GQuark id;
52   gpointer data;
53   GDestroyNotify destroy_func;
54 };
55
56 struct _GDataset
57 {
58   gconstpointer location;
59   GData        *datalist;
60 };
61
62
63 /* --- prototypes --- */
64 static inline GDataset* g_dataset_lookup                (gconstpointer    dataset_location);
65 static inline void      g_datalist_clear_i              (GData          **datalist);
66 static void             g_dataset_destroy_internal      (GDataset        *dataset);
67 static inline void      g_data_set_internal             (GData          **datalist,
68                                                          GQuark           key_id,
69                                                          gpointer         data,
70                                                          GDestroyNotify   destroy_func,
71                                                          GDataset        *dataset);
72 static void             g_data_initialize               (void);
73 static inline GQuark    g_quark_new                     (gchar          *string);
74
75
76 /* --- variables --- */
77 G_LOCK_DEFINE_STATIC (g_dataset_global);
78 static GHashTable   *g_dataset_location_ht = NULL;
79 static GDataset     *g_dataset_cached = NULL; /* should this be
80                                                  threadspecific? */
81 static GMemChunk    *g_dataset_mem_chunk = NULL;
82 static GMemChunk    *g_data_mem_chunk = NULL;
83 static GData        *g_data_cache = NULL;
84 static guint         g_data_cache_length = 0;
85
86 G_LOCK_DEFINE_STATIC (g_quark_global);
87 static GHashTable   *g_quark_ht = NULL;
88 static gchar       **g_quarks = NULL;
89 static GQuark        g_quark_seq_id = 0;
90
91
92 /* --- functions --- */
93
94 /* HOLDS: g_dataset_global_lock */
95 static inline void
96 g_datalist_clear_i (GData **datalist)
97 {
98   register GData *list;
99   
100   /* unlink *all* items before walking their destructors
101    */
102   list = *datalist;
103   *datalist = NULL;
104   
105   while (list)
106     {
107       register GData *prev;
108       
109       prev = list;
110       list = prev->next;
111       
112       if (prev->destroy_func)
113         {
114           G_UNLOCK (g_dataset_global);
115           prev->destroy_func (prev->data);
116           G_LOCK (g_dataset_global);
117         }
118       
119       if (g_data_cache_length < G_DATA_CACHE_MAX)
120         {
121           prev->next = g_data_cache;
122           g_data_cache = prev;
123           g_data_cache_length++;
124         }
125       else
126         g_mem_chunk_free (g_data_mem_chunk, prev);
127     }
128 }
129
130 void
131 g_datalist_clear (GData **datalist)
132 {
133   g_return_if_fail (datalist != NULL);
134   
135   G_LOCK (g_dataset_global);
136   if (!g_dataset_location_ht)
137     g_data_initialize ();
138
139   while (*datalist)
140     g_datalist_clear_i (datalist);
141   G_UNLOCK (g_dataset_global);
142 }
143
144 /* HOLDS: g_dataset_global_lock */
145 static inline GDataset*
146 g_dataset_lookup (gconstpointer dataset_location)
147 {
148   register GDataset *dataset;
149   
150   if (g_dataset_cached && g_dataset_cached->location == dataset_location)
151     return g_dataset_cached;
152   
153   dataset = g_hash_table_lookup (g_dataset_location_ht, dataset_location);
154   if (dataset)
155     g_dataset_cached = dataset;
156   
157   return dataset;
158 }
159
160 /* HOLDS: g_dataset_global_lock */
161 static void
162 g_dataset_destroy_internal (GDataset *dataset)
163 {
164   register gconstpointer dataset_location;
165   
166   dataset_location = dataset->location;
167   while (dataset)
168     {
169       if (!dataset->datalist)
170         {
171           if (dataset == g_dataset_cached)
172             g_dataset_cached = NULL;
173           g_hash_table_remove (g_dataset_location_ht, dataset_location);
174           g_mem_chunk_free (g_dataset_mem_chunk, dataset);
175           break;
176         }
177       
178       g_datalist_clear_i (&dataset->datalist);
179       dataset = g_dataset_lookup (dataset_location);
180     }
181 }
182
183 void
184 g_dataset_destroy (gconstpointer  dataset_location)
185 {
186   g_return_if_fail (dataset_location != NULL);
187   
188   G_LOCK (g_dataset_global);
189   if (g_dataset_location_ht)
190     {
191       register GDataset *dataset;
192
193       dataset = g_dataset_lookup (dataset_location);
194       if (dataset)
195         g_dataset_destroy_internal (dataset);
196     }
197   G_UNLOCK (g_dataset_global);
198 }
199
200 /* HOLDS: g_dataset_global_lock */
201 static inline void
202 g_data_set_internal (GData        **datalist,
203                      GQuark         key_id,
204                      gpointer       data,
205                      GDestroyNotify destroy_func,
206                      GDataset      *dataset)
207 {
208   register GData *list;
209   
210   list = *datalist;
211   if (!data)
212     {
213       register GData *prev;
214       
215       prev = NULL;
216       while (list)
217         {
218           if (list->id == key_id)
219             {
220               if (prev)
221                 prev->next = list->next;
222               else
223                 {
224                   *datalist = list->next;
225                   
226                   /* the dataset destruction *must* be done
227                    * prior to invokation of the data destroy function
228                    */
229                   if (!*datalist && dataset)
230                     g_dataset_destroy_internal (dataset);
231                 }
232               
233               /* the GData struct *must* already be unlinked
234                * when invoking the destroy function.
235                * we use (data==NULL && destroy_func!=NULL) as
236                * a special hint combination to "steal"
237                * data without destroy notification
238                */
239               if (list->destroy_func && !destroy_func)
240                 {
241                   G_UNLOCK (g_dataset_global);
242                   list->destroy_func (list->data);
243                   G_LOCK (g_dataset_global);
244                 }
245               
246               if (g_data_cache_length < G_DATA_CACHE_MAX)
247                 {
248                   list->next = g_data_cache;
249                   g_data_cache = list;
250                   g_data_cache_length++;
251                 }
252               else
253                 g_mem_chunk_free (g_data_mem_chunk, list);
254               
255               return;
256             }
257           
258           prev = list;
259           list = list->next;
260         }
261     }
262   else
263     {
264       while (list)
265         {
266           if (list->id == key_id)
267             {
268               if (!list->destroy_func)
269                 {
270                   list->data = data;
271                   list->destroy_func = destroy_func;
272                 }
273               else
274                 {
275                   register GDestroyNotify dfunc;
276                   register gpointer ddata;
277                   
278                   dfunc = list->destroy_func;
279                   ddata = list->data;
280                   list->data = data;
281                   list->destroy_func = destroy_func;
282                   
283                   /* we need to have updated all structures prior to
284                    * invokation of the destroy function
285                    */
286                   G_UNLOCK (g_dataset_global);
287                   dfunc (ddata);
288                   G_LOCK (g_dataset_global);
289                 }
290               
291               return;
292             }
293           
294           list = list->next;
295         }
296       
297       if (g_data_cache)
298         {
299           list = g_data_cache;
300           g_data_cache = list->next;
301           g_data_cache_length--;
302         }
303       else
304         list = g_chunk_new (GData, g_data_mem_chunk);
305       list->next = *datalist;
306       list->id = key_id;
307       list->data = data;
308       list->destroy_func = destroy_func;
309       *datalist = list;
310     }
311 }
312
313 void
314 g_dataset_id_set_data_full (gconstpointer  dataset_location,
315                             GQuark         key_id,
316                             gpointer       data,
317                             GDestroyNotify destroy_func)
318 {
319   register GDataset *dataset;
320   
321   g_return_if_fail (dataset_location != NULL);
322   if (!data)
323     g_return_if_fail (destroy_func == NULL);
324   if (!key_id)
325     {
326       if (data)
327         g_return_if_fail (key_id > 0);
328       else
329         return;
330     }
331   
332   G_LOCK (g_dataset_global);
333   if (!g_dataset_location_ht)
334     g_data_initialize ();
335  
336   dataset = g_dataset_lookup (dataset_location);
337   if (!dataset)
338     {
339       dataset = g_chunk_new (GDataset, g_dataset_mem_chunk);
340       dataset->location = dataset_location;
341       g_datalist_init (&dataset->datalist);
342       g_hash_table_insert (g_dataset_location_ht, 
343                            (gpointer) dataset->location,
344                            dataset);
345     }
346   
347   g_data_set_internal (&dataset->datalist, key_id, data, destroy_func, dataset);
348   G_UNLOCK (g_dataset_global);
349 }
350
351 void
352 g_datalist_id_set_data_full (GData        **datalist,
353                              GQuark         key_id,
354                              gpointer       data,
355                              GDestroyNotify destroy_func)
356 {
357   g_return_if_fail (datalist != NULL);
358   if (!data)
359     g_return_if_fail (destroy_func == NULL);
360   if (!key_id)
361     {
362       if (data)
363         g_return_if_fail (key_id > 0);
364       else
365         return;
366     }
367
368   G_LOCK (g_dataset_global);
369   if (!g_dataset_location_ht)
370     g_data_initialize ();
371   
372   g_data_set_internal (datalist, key_id, data, destroy_func, NULL);
373   G_UNLOCK (g_dataset_global);
374 }
375
376 void
377 g_dataset_id_remove_no_notify (gconstpointer  dataset_location,
378                                GQuark         key_id)
379 {
380   g_return_if_fail (dataset_location != NULL);
381   
382   G_LOCK (g_dataset_global);
383   if (key_id && g_dataset_location_ht)
384     {
385       GDataset *dataset;
386   
387       dataset = g_dataset_lookup (dataset_location);
388       if (dataset)
389         g_data_set_internal (&dataset->datalist, key_id, NULL, (GDestroyNotify) 42, dataset);
390     } 
391   G_UNLOCK (g_dataset_global);
392 }
393
394 void
395 g_datalist_id_remove_no_notify (GData   **datalist,
396                                 GQuark    key_id)
397 {
398   g_return_if_fail (datalist != NULL);
399
400   G_LOCK (g_dataset_global);
401   if (key_id && g_dataset_location_ht)
402     g_data_set_internal (datalist, key_id, NULL, (GDestroyNotify) 42, NULL);
403   G_UNLOCK (g_dataset_global);
404 }
405
406 gpointer
407 g_dataset_id_get_data (gconstpointer  dataset_location,
408                        GQuark         key_id)
409 {
410   g_return_val_if_fail (dataset_location != NULL, NULL);
411   
412   G_LOCK (g_dataset_global);
413   if (key_id && g_dataset_location_ht)
414     {
415       register GDataset *dataset;
416       
417       dataset = g_dataset_lookup (dataset_location);
418       if (dataset)
419         {
420           register GData *list;
421           
422           for (list = dataset->datalist; list; list = list->next)
423             if (list->id == key_id)
424               {
425                 G_UNLOCK (g_dataset_global);
426                 return list->data;
427               }
428         }
429     }
430   G_UNLOCK (g_dataset_global);
431  
432   return NULL;
433 }
434
435 gpointer
436 g_datalist_id_get_data (GData    **datalist,
437                         GQuark     key_id)
438 {
439   g_return_val_if_fail (datalist != NULL, NULL);
440   
441   if (key_id)
442     {
443       register GData *list;
444       
445       for (list = *datalist; list; list = list->next)
446         if (list->id == key_id)
447           return list->data;
448     }
449   
450   return NULL;
451 }
452
453 void
454 g_dataset_foreach (gconstpointer    dataset_location,
455                    GDataForeachFunc func,
456                    gpointer         user_data)
457 {
458   register GDataset *dataset;
459   
460   g_return_if_fail (dataset_location != NULL);
461   g_return_if_fail (func != NULL);
462
463   G_LOCK (g_dataset_global);
464   if (g_dataset_location_ht)
465     {
466       dataset = g_dataset_lookup (dataset_location);
467       G_UNLOCK (g_dataset_global);
468       if (dataset)
469         {
470           register GData *list;
471           
472           for (list = dataset->datalist; list; list = list->next)
473               func (list->id, list->data, user_data);
474         }
475     }
476   else
477     {
478       G_UNLOCK (g_dataset_global);
479     }
480 }
481
482 void
483 g_datalist_foreach (GData          **datalist,
484                     GDataForeachFunc func,
485                     gpointer         user_data)
486 {
487   register GData *list;
488
489   g_return_if_fail (datalist != NULL);
490   g_return_if_fail (func != NULL);
491   
492   for (list = *datalist; list; list = list->next)
493     func (list->id, list->data, user_data);
494 }
495
496 void
497 g_datalist_init (GData **datalist)
498 {
499   g_return_if_fail (datalist != NULL);
500   
501   *datalist = NULL;
502 }
503
504 /* HOLDS: g_dataset_global_lock */
505 static void
506 g_data_initialize (void)
507 {
508   g_return_if_fail (g_dataset_location_ht == NULL);
509
510   g_dataset_location_ht = g_hash_table_new (g_direct_hash, NULL);
511   g_dataset_cached = NULL;
512   g_dataset_mem_chunk =
513     g_mem_chunk_new ("GDataset MemChunk",
514                      sizeof (GDataset),
515                      sizeof (GDataset) * G_DATASET_MEM_CHUNK_PREALLOC,
516                      G_ALLOC_AND_FREE);
517   g_data_mem_chunk =
518     g_mem_chunk_new ("GData MemChunk",
519                      sizeof (GData),
520                      sizeof (GData) * G_DATA_MEM_CHUNK_PREALLOC,
521                      G_ALLOC_AND_FREE);
522 }
523
524 GQuark
525 g_quark_try_string (const gchar *string)
526 {
527   GQuark quark = 0;
528   g_return_val_if_fail (string != NULL, 0);
529   
530   G_LOCK (g_quark_global);
531   if (g_quark_ht)
532     quark = GPOINTER_TO_UINT (g_hash_table_lookup (g_quark_ht, string));
533   G_UNLOCK (g_quark_global);
534   
535   return quark;
536 }
537
538 GQuark
539 g_quark_from_string (const gchar *string)
540 {
541   GQuark quark;
542   
543   g_return_val_if_fail (string != NULL, 0);
544   
545   G_LOCK (g_quark_global);
546   if (g_quark_ht)
547     quark = (gulong) g_hash_table_lookup (g_quark_ht, string);
548   else
549     {
550       g_quark_ht = g_hash_table_new (g_str_hash, g_str_equal);
551       quark = 0;
552     }
553   
554   if (!quark)
555     quark = g_quark_new (g_strdup (string));
556   G_UNLOCK (g_quark_global);
557   
558   return quark;
559 }
560
561 GQuark
562 g_quark_from_static_string (const gchar *string)
563 {
564   GQuark quark;
565   
566   g_return_val_if_fail (string != NULL, 0);
567   
568   G_LOCK (g_quark_global);
569   if (g_quark_ht)
570     quark = (gulong) g_hash_table_lookup (g_quark_ht, string);
571   else
572     {
573       g_quark_ht = g_hash_table_new (g_str_hash, g_str_equal);
574       quark = 0;
575     }
576
577   if (!quark)
578     quark = g_quark_new ((gchar*) string);
579   G_UNLOCK (g_quark_global);
580  
581   return quark;
582 }
583
584 gchar*
585 g_quark_to_string (GQuark quark)
586 {
587   gchar* result = NULL;
588   G_LOCK (g_quark_global);
589   if (quark > 0 && quark <= g_quark_seq_id)
590     result = g_quarks[quark - 1];
591   G_UNLOCK (g_quark_global);
592
593   return result;
594 }
595
596 /* HOLDS: g_quark_global_lock */
597 static inline GQuark
598 g_quark_new (gchar *string)
599 {
600   GQuark quark;
601   
602   if (g_quark_seq_id % G_QUARK_BLOCK_SIZE == 0)
603     g_quarks = g_renew (gchar*, g_quarks, g_quark_seq_id + G_QUARK_BLOCK_SIZE);
604   
605   g_quarks[g_quark_seq_id] = string;
606   g_quark_seq_id++;
607   quark = g_quark_seq_id;
608   g_hash_table_insert (g_quark_ht, string, GUINT_TO_POINTER (quark));
609   
610   return quark;
611 }