Avoid double locking in g_intern_string (#322133, Benedikt Meurer)
[platform/upstream/glib.git] / glib / 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 Lesser 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  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser 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-2000.  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 ; except for g_data*_foreach()
31  */
32
33 #include "config.h"
34
35 #include <string.h>
36
37 #include "glib.h"
38 #include "galias.h"
39 #include "gdatasetprivate.h"
40
41
42 /* --- defines --- */
43 #define G_QUARK_BLOCK_SIZE                      (512)
44 #define G_DATA_CACHE_MAX                        (512)
45
46 /* datalist pointer modifications have to be done with the g_dataset_global mutex held */
47 #define G_DATALIST_GET_POINTER(datalist)                                                \
48   ((GData*) ((gsize) *(datalist) & ~(gsize) G_DATALIST_FLAGS_MASK))
49 #define G_DATALIST_SET_POINTER(datalist, pointer) G_STMT_START {                        \
50   *(datalist) = (GData*) (G_DATALIST_GET_FLAGS (datalist) |                             \
51                           (gsize) pointer);                                             \
52 } G_STMT_END
53
54
55 /* --- structures --- */
56 typedef struct _GDataset GDataset;
57 struct _GData
58 {
59   GData *next;
60   GQuark id;
61   gpointer data;
62   GDestroyNotify destroy_func;
63 };
64
65 struct _GDataset
66 {
67   gconstpointer location;
68   GData        *datalist;
69 };
70
71
72 /* --- prototypes --- */
73 static inline GDataset* g_dataset_lookup                (gconstpointer    dataset_location);
74 static inline void      g_datalist_clear_i              (GData          **datalist);
75 static void             g_dataset_destroy_internal      (GDataset        *dataset);
76 static inline gpointer  g_data_set_internal             (GData          **datalist,
77                                                          GQuark           key_id,
78                                                          gpointer         data,
79                                                          GDestroyNotify   destroy_func,
80                                                          GDataset        *dataset);
81 static void             g_data_initialize               (void);
82 static inline GQuark    g_quark_new                     (gchar          *string);
83
84
85 /* --- variables --- */
86 G_LOCK_DEFINE_STATIC (g_dataset_global);
87 static GHashTable   *g_dataset_location_ht = NULL;
88 static GDataset     *g_dataset_cached = NULL; /* should this be
89                                                  threadspecific? */
90 static GData        *g_data_cache = NULL;
91 static guint         g_data_cache_length = 0;
92
93 G_LOCK_DEFINE_STATIC (g_quark_global);
94 static GHashTable   *g_quark_ht = NULL;
95 static gchar       **g_quarks = NULL;
96 static GQuark        g_quark_seq_id = 0;
97
98 /* --- functions --- */
99
100 /* HOLDS: g_dataset_global_lock */
101 static inline void
102 g_datalist_clear_i (GData **datalist)
103 {
104   register GData *list;
105   
106   /* unlink *all* items before walking their destructors
107    */
108   list = G_DATALIST_GET_POINTER (datalist);
109   G_DATALIST_SET_POINTER (datalist, NULL);
110   
111   while (list)
112     {
113       register GData *prev;
114       
115       prev = list;
116       list = prev->next;
117       
118       if (prev->destroy_func)
119         {
120           G_UNLOCK (g_dataset_global);
121           prev->destroy_func (prev->data);
122           G_LOCK (g_dataset_global);
123         }
124       
125       if (g_data_cache_length < G_DATA_CACHE_MAX)
126         {
127           prev->next = g_data_cache;
128           g_data_cache = prev;
129           g_data_cache_length++;
130         }
131       else
132         g_slice_free (GData, prev);
133     }
134 }
135
136 void
137 g_datalist_clear (GData **datalist)
138 {
139   g_return_if_fail (datalist != NULL);
140   
141   G_LOCK (g_dataset_global);
142   if (!g_dataset_location_ht)
143     g_data_initialize ();
144
145   while (G_DATALIST_GET_POINTER (datalist))
146     g_datalist_clear_i (datalist);
147   G_UNLOCK (g_dataset_global);
148 }
149
150 /* HOLDS: g_dataset_global_lock */
151 static inline GDataset*
152 g_dataset_lookup (gconstpointer dataset_location)
153 {
154   register GDataset *dataset;
155   
156   if (g_dataset_cached && g_dataset_cached->location == dataset_location)
157     return g_dataset_cached;
158   
159   dataset = g_hash_table_lookup (g_dataset_location_ht, dataset_location);
160   if (dataset)
161     g_dataset_cached = dataset;
162   
163   return dataset;
164 }
165
166 /* HOLDS: g_dataset_global_lock */
167 static void
168 g_dataset_destroy_internal (GDataset *dataset)
169 {
170   register gconstpointer dataset_location;
171   
172   dataset_location = dataset->location;
173   while (dataset)
174     {
175       if (!dataset->datalist)
176         {
177           if (dataset == g_dataset_cached)
178             g_dataset_cached = NULL;
179           g_hash_table_remove (g_dataset_location_ht, dataset_location);
180           g_slice_free (GDataset, dataset);
181           break;
182         }
183       
184       g_datalist_clear_i (&dataset->datalist);
185       dataset = g_dataset_lookup (dataset_location);
186     }
187 }
188
189 void
190 g_dataset_destroy (gconstpointer  dataset_location)
191 {
192   g_return_if_fail (dataset_location != NULL);
193   
194   G_LOCK (g_dataset_global);
195   if (g_dataset_location_ht)
196     {
197       register GDataset *dataset;
198
199       dataset = g_dataset_lookup (dataset_location);
200       if (dataset)
201         g_dataset_destroy_internal (dataset);
202     }
203   G_UNLOCK (g_dataset_global);
204 }
205
206 /* HOLDS: g_dataset_global_lock */
207 static inline gpointer
208 g_data_set_internal (GData        **datalist,
209                      GQuark         key_id,
210                      gpointer       data,
211                      GDestroyNotify destroy_func,
212                      GDataset      *dataset)
213 {
214   register GData *list;
215   
216   list = G_DATALIST_GET_POINTER (datalist);
217   if (!data)
218     {
219       register GData *prev;
220       
221       prev = NULL;
222       while (list)
223         {
224           if (list->id == key_id)
225             {
226               gpointer ret_data = NULL;
227
228               if (prev)
229                 prev->next = list->next;
230               else
231                 {
232                   G_DATALIST_SET_POINTER (datalist, list->next);
233                   
234                   /* the dataset destruction *must* be done
235                    * prior to invocation of the data destroy function
236                    */
237                   if (!list->next && dataset)
238                     g_dataset_destroy_internal (dataset);
239                 }
240               
241               /* the GData struct *must* already be unlinked
242                * when invoking the destroy function.
243                * we use (data==NULL && destroy_func!=NULL) as
244                * a special hint combination to "steal"
245                * data without destroy notification
246                */
247               if (list->destroy_func && !destroy_func)
248                 {
249                   G_UNLOCK (g_dataset_global);
250                   list->destroy_func (list->data);
251                   G_LOCK (g_dataset_global);
252                 }
253               else
254                 ret_data = list->data;
255               
256               if (g_data_cache_length < G_DATA_CACHE_MAX)
257                 {
258                   list->next = g_data_cache;
259                   g_data_cache = list;
260                   g_data_cache_length++;
261                 }
262               else
263                 g_slice_free (GData, list);
264               
265               return ret_data;
266             }
267           
268           prev = list;
269           list = list->next;
270         }
271     }
272   else
273     {
274       while (list)
275         {
276           if (list->id == key_id)
277             {
278               if (!list->destroy_func)
279                 {
280                   list->data = data;
281                   list->destroy_func = destroy_func;
282                 }
283               else
284                 {
285                   register GDestroyNotify dfunc;
286                   register gpointer ddata;
287                   
288                   dfunc = list->destroy_func;
289                   ddata = list->data;
290                   list->data = data;
291                   list->destroy_func = destroy_func;
292                   
293                   /* we need to have updated all structures prior to
294                    * invocation of the destroy function
295                    */
296                   G_UNLOCK (g_dataset_global);
297                   dfunc (ddata);
298                   G_LOCK (g_dataset_global);
299                 }
300               
301               return NULL;
302             }
303           
304           list = list->next;
305         }
306       
307       if (g_data_cache)
308         {
309           list = g_data_cache;
310           g_data_cache = list->next;
311           g_data_cache_length--;
312         }
313       else
314         list = g_slice_new (GData);
315       list->next = G_DATALIST_GET_POINTER (datalist);
316       list->id = key_id;
317       list->data = data;
318       list->destroy_func = destroy_func;
319       G_DATALIST_SET_POINTER (datalist, list);
320     }
321
322   return NULL;
323 }
324
325 void
326 g_dataset_id_set_data_full (gconstpointer  dataset_location,
327                             GQuark         key_id,
328                             gpointer       data,
329                             GDestroyNotify destroy_func)
330 {
331   register GDataset *dataset;
332   
333   g_return_if_fail (dataset_location != NULL);
334   if (!data)
335     g_return_if_fail (destroy_func == NULL);
336   if (!key_id)
337     {
338       if (data)
339         g_return_if_fail (key_id > 0);
340       else
341         return;
342     }
343   
344   G_LOCK (g_dataset_global);
345   if (!g_dataset_location_ht)
346     g_data_initialize ();
347  
348   dataset = g_dataset_lookup (dataset_location);
349   if (!dataset)
350     {
351       dataset = g_slice_new (GDataset);
352       dataset->location = dataset_location;
353       g_datalist_init (&dataset->datalist);
354       g_hash_table_insert (g_dataset_location_ht, 
355                            (gpointer) dataset->location,
356                            dataset);
357     }
358   
359   g_data_set_internal (&dataset->datalist, key_id, data, destroy_func, dataset);
360   G_UNLOCK (g_dataset_global);
361 }
362
363 void
364 g_datalist_id_set_data_full (GData        **datalist,
365                              GQuark         key_id,
366                              gpointer       data,
367                              GDestroyNotify destroy_func)
368 {
369   g_return_if_fail (datalist != NULL);
370   if (!data)
371     g_return_if_fail (destroy_func == NULL);
372   if (!key_id)
373     {
374       if (data)
375         g_return_if_fail (key_id > 0);
376       else
377         return;
378     }
379
380   G_LOCK (g_dataset_global);
381   if (!g_dataset_location_ht)
382     g_data_initialize ();
383   
384   g_data_set_internal (datalist, key_id, data, destroy_func, NULL);
385   G_UNLOCK (g_dataset_global);
386 }
387
388 gpointer
389 g_dataset_id_remove_no_notify (gconstpointer  dataset_location,
390                                GQuark         key_id)
391 {
392   gpointer ret_data = NULL;
393
394   g_return_val_if_fail (dataset_location != NULL, NULL);
395   
396   G_LOCK (g_dataset_global);
397   if (key_id && g_dataset_location_ht)
398     {
399       GDataset *dataset;
400   
401       dataset = g_dataset_lookup (dataset_location);
402       if (dataset)
403         ret_data = g_data_set_internal (&dataset->datalist, key_id, NULL, (GDestroyNotify) 42, dataset);
404     } 
405   G_UNLOCK (g_dataset_global);
406
407   return ret_data;
408 }
409
410 gpointer
411 g_datalist_id_remove_no_notify (GData   **datalist,
412                                 GQuark    key_id)
413 {
414   gpointer ret_data = NULL;
415
416   g_return_val_if_fail (datalist != NULL, NULL);
417
418   G_LOCK (g_dataset_global);
419   if (key_id && g_dataset_location_ht)
420     ret_data = g_data_set_internal (datalist, key_id, NULL, (GDestroyNotify) 42, NULL);
421   G_UNLOCK (g_dataset_global);
422
423   return ret_data;
424 }
425
426 gpointer
427 g_dataset_id_get_data (gconstpointer  dataset_location,
428                        GQuark         key_id)
429 {
430   g_return_val_if_fail (dataset_location != NULL, NULL);
431   
432   G_LOCK (g_dataset_global);
433   if (key_id && g_dataset_location_ht)
434     {
435       register GDataset *dataset;
436       
437       dataset = g_dataset_lookup (dataset_location);
438       if (dataset)
439         {
440           register GData *list;
441           
442           for (list = dataset->datalist; list; list = list->next)
443             if (list->id == key_id)
444               {
445                 G_UNLOCK (g_dataset_global);
446                 return list->data;
447               }
448         }
449     }
450   G_UNLOCK (g_dataset_global);
451  
452   return NULL;
453 }
454
455 gpointer
456 g_datalist_id_get_data (GData    **datalist,
457                         GQuark     key_id)
458 {
459   gpointer data = NULL;
460   g_return_val_if_fail (datalist != NULL, NULL);
461   if (key_id)
462     {
463       register GData *list;
464       G_LOCK (g_dataset_global);
465       for (list = G_DATALIST_GET_POINTER (datalist); list; list = list->next)
466         if (list->id == key_id)
467           {
468             data = list->data;
469             break;
470           }
471       G_UNLOCK (g_dataset_global);
472     }
473   return data;
474 }
475
476 void
477 g_dataset_foreach (gconstpointer    dataset_location,
478                    GDataForeachFunc func,
479                    gpointer         user_data)
480 {
481   register GDataset *dataset;
482   
483   g_return_if_fail (dataset_location != NULL);
484   g_return_if_fail (func != NULL);
485
486   G_LOCK (g_dataset_global);
487   if (g_dataset_location_ht)
488     {
489       dataset = g_dataset_lookup (dataset_location);
490       G_UNLOCK (g_dataset_global);
491       if (dataset)
492         {
493           register GData *list, *next;
494           
495           for (list = dataset->datalist; list; list = next)
496             {
497               next = list->next;
498               func (list->id, list->data, user_data);
499             }
500         }
501     }
502   else
503     {
504       G_UNLOCK (g_dataset_global);
505     }
506 }
507
508 void
509 g_datalist_foreach (GData          **datalist,
510                     GDataForeachFunc func,
511                     gpointer         user_data)
512 {
513   register GData *list, *next;
514
515   g_return_if_fail (datalist != NULL);
516   g_return_if_fail (func != NULL);
517   
518   for (list = G_DATALIST_GET_POINTER (datalist); list; list = next)
519     {
520       next = list->next;
521       func (list->id, list->data, user_data);
522     }
523 }
524
525 void
526 g_datalist_init (GData **datalist)
527 {
528   g_return_if_fail (datalist != NULL);
529   
530   *datalist = NULL;
531 }
532
533 /**
534  * g_datalist_set_flags:
535  * @datalist: pointer to the location that holds a list
536  * @flags: the flags to turn on. The values of the flags are
537  *   restricted by %G_DATALIST_FLAGS_MASK (currently
538  *   3; giving two possible boolean flags).
539  *   A value for @flags that doesn't fit within the mask is
540  *   an error.
541  * 
542  * Turns on flag values for a data list. This function is used
543  * to keep a small number of boolean flags in an object with
544  * a data list without using any additional space. It is
545  * not generally useful except in circumstances where space
546  * is very tight. (It is used in the base #GObject type, for
547  * example.)
548  **/
549 void
550 g_datalist_set_flags (GData **datalist,
551                       guint   flags)
552 {
553   g_return_if_fail (datalist != NULL);
554   g_return_if_fail ((flags & ~G_DATALIST_FLAGS_MASK) == 0);
555
556   G_LOCK (g_dataset_global);
557   *datalist = (GData*) (flags | (gsize) *datalist);
558   G_UNLOCK (g_dataset_global);
559 }
560
561 /**
562  * g_datalist_unset_flags:
563  * @datalist: pointer to the location that holds a list
564  * @flags: the flags to turn off. The values of the flags are
565  *   restricted by %G_DATALIST_FLAGS_MASK (currently
566  *   3: giving two possible boolean flags).
567  *   A value for @flags that doesn't fit within the mask is
568  *   an error.
569  * 
570  * Turns off flag values for a data list. See g_datalist_unset_flags()
571  **/
572 void
573 g_datalist_unset_flags (GData **datalist,
574                         guint   flags)
575 {
576   g_return_if_fail (datalist != NULL);
577   g_return_if_fail ((flags & ~G_DATALIST_FLAGS_MASK) == 0);
578
579   G_LOCK (g_dataset_global);
580   *datalist = (GData*) (~(gsize) flags & (gsize) *datalist);
581   G_UNLOCK (g_dataset_global);
582 }
583
584 /**
585  * g_datalist_get_flags:
586  * @datalist: pointer to the location that holds a list
587  * 
588  * Gets flags values packed in together with the datalist.
589  * See g_datalist_set_flags().
590  * 
591  * Return value: the flags of the datalist
592  **/
593 guint
594 g_datalist_get_flags (GData **datalist)
595 {
596   g_return_val_if_fail (datalist != NULL, 0);
597
598   return G_DATALIST_GET_FLAGS (datalist); /* atomic macro */
599 }
600
601 /* HOLDS: g_dataset_global_lock */
602 static void
603 g_data_initialize (void)
604 {
605   g_return_if_fail (g_dataset_location_ht == NULL);
606
607   g_dataset_location_ht = g_hash_table_new (g_direct_hash, NULL);
608   g_dataset_cached = NULL;
609 }
610
611 GQuark
612 g_quark_try_string (const gchar *string)
613 {
614   GQuark quark = 0;
615   g_return_val_if_fail (string != NULL, 0);
616   
617   G_LOCK (g_quark_global);
618   if (g_quark_ht)
619     quark = GPOINTER_TO_UINT (g_hash_table_lookup (g_quark_ht, string));
620   G_UNLOCK (g_quark_global);
621   
622   return quark;
623 }
624
625 /* HOLDS: g_quark_global_lock */
626 static inline GQuark
627 g_quark_from_string_internal (const gchar *string, 
628                               gboolean     duplicate)
629 {
630   GQuark quark = 0;
631   
632   if (g_quark_ht)
633     quark = GPOINTER_TO_UINT (g_hash_table_lookup (g_quark_ht, string));
634   
635   if (!quark)
636     quark = g_quark_new (duplicate ? g_strdup (string) : (gchar *)string);
637   
638   return quark;
639 }
640
641 GQuark
642 g_quark_from_string (const gchar *string)
643 {
644   GQuark quark;
645   
646   g_return_val_if_fail (string != NULL, 0);
647   
648   G_LOCK (g_quark_global);
649   quark = g_quark_from_string_internal (string, TRUE);
650   G_UNLOCK (g_quark_global);
651   
652   return quark;
653 }
654
655 GQuark
656 g_quark_from_static_string (const gchar *string)
657 {
658   GQuark quark;
659   
660   g_return_val_if_fail (string != NULL, 0);
661   
662   G_LOCK (g_quark_global);
663   quark = g_quark_from_string_internal (string, FALSE);
664   G_UNLOCK (g_quark_global);
665
666   return quark;
667 }
668
669 G_CONST_RETURN gchar*
670 g_quark_to_string (GQuark quark)
671 {
672   gchar* result = NULL;
673
674   G_LOCK (g_quark_global);
675   if (quark > 0 && quark <= g_quark_seq_id)
676     result = g_quarks[quark];
677   G_UNLOCK (g_quark_global);
678
679   return result;
680 }
681
682 /* HOLDS: g_quark_global_lock */
683 static inline GQuark
684 g_quark_new (gchar *string)
685 {
686   GQuark quark;
687   
688   if (g_quark_seq_id % G_QUARK_BLOCK_SIZE == 0)
689     g_quarks = g_renew (gchar*, g_quarks, g_quark_seq_id + G_QUARK_BLOCK_SIZE);
690   
691   g_quarks[0] = NULL;
692   g_quark_seq_id++;
693   g_quarks[g_quark_seq_id] = string;
694   quark = g_quark_seq_id;
695
696   if (!g_quark_ht)
697     g_quark_ht = g_hash_table_new (g_str_hash, g_str_equal);
698
699   g_hash_table_insert (g_quark_ht, string, GUINT_TO_POINTER (quark));
700   
701   return quark;
702 }
703
704 /**
705  * g_intern_string:
706  * @string: a string
707  * 
708  * Returns a canonical representation for @string. Interned strings can
709  * be compared for equality by comparing the pointers, instead of using strcmp().
710  * 
711  * Returns: a canonical representation for the string
712  *
713  * Since: 2.10
714  */
715 G_CONST_RETURN gchar*
716 g_intern_string (const gchar *string)
717 {
718   const gchar *result;
719   GQuark quark;
720
721   if (!string)
722     return NULL;
723
724   G_LOCK (g_quark_global);
725   quark = g_quark_from_string_internal (string, TRUE);
726   result = g_quarks[quark];
727   G_UNLOCK (g_quark_global);
728
729   return result;
730 }
731
732 /**
733  * g_intern_static_string:
734  * @string: a static string
735  * 
736  * Returns a canonical representation for @string. Interned strings can
737  * be compared for equality by comparing the pointers, instead of using strcmp().
738  * g_intern_static_string() does not copy the string, therefore @string must
739  * not be freed or modified. 
740  * 
741  * Returns: a canonical representation for the string
742  *
743  * Since: 2.10
744  */
745 G_CONST_RETURN gchar*
746 g_intern_static_string (const gchar *string)
747 {
748   GQuark quark;
749   const gchar *result;
750
751   if (!string)
752     return NULL;
753
754   G_LOCK (g_quark_global);
755   quark = g_quark_from_string_internal (string, FALSE);
756   result = g_quarks[quark];
757   G_UNLOCK (g_quark_global);
758
759   return result;
760 }
761
762
763
764 #define __G_DATASET_C__
765 #include "galiasdef.c"