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