1/* 2 * Copyright (C) 2015 William Yu <williamyu@gnome.org> 3 * 4 * This library is free software: you can redistribute it and/or modify it 5 * under the terms of version 2.1. of the GNU Lesser General Public License 6 * as published by the Free Software Foundation. 7 * 8 * This library is distributed in the hope that it will be useful, but 9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License 11 * for more details. 12 * 13 * You should have received a copy of the GNU Lesser General Public License 14 * along with this library. If not, see <https://www.gnu.org/licenses/>. 15 */ 16 17#ifdef HAVE_CONFIG_H 18#include <config.h> 19#endif 20 21#include "i-cal-object.h" 22 23#define LOCK_PROPS(x) g_mutex_lock (&((x)->priv->props_lock)) 24#define UNLOCK_PROPS(x) g_mutex_unlock (&((x)->priv->props_lock)) 25 26typedef struct _GlobalData { 27 GType object_type; 28 gpointer native; 29} GlobalData; 30 31static guint global_data_hash(gconstpointer ptr) 32{ 33 const GlobalData *gd = ptr; 34 35 if (!gd) { 36 return 0; 37 } 38 return g_direct_hash(GINT_TO_POINTER(gd->object_type)) ^ g_direct_hash(gd->native); 39} 40 41static gboolean global_data_equal(gconstpointer ptr1, gconstpointer ptr2) 42{ 43 const GlobalData *gd1 = ptr1, *gd2 = ptr2; 44 45 if (!gd1 || !gd2) { 46 return gd1 == gd2; 47 } 48 return gd1->object_type == gd2->object_type && gd1->native == gd2->native; 49} 50 51G_LOCK_DEFINE_STATIC(global_objects); 52static GHashTable *global_objects; /* GlobalData * ~> ICalObject * */ 53 54static void global_data_object_freed_cb(gpointer user_data, G_GNUC_UNUSED GObject *freed_object) 55{ 56 GlobalData *gd = user_data; 57 58 G_LOCK(global_objects); 59 60 if (global_objects) { 61 if (g_hash_table_steal(global_objects, gd)) { 62 g_free(gd); 63 } 64 65 if (!g_hash_table_size(global_objects)) { 66 g_hash_table_destroy(global_objects); 67 global_objects = NULL; 68 } 69 } 70 71 G_UNLOCK(global_objects); 72} 73 74/** 75 * i_cal_object_free_global_objects: 76 * 77 * Frees all global objects. Any references to them are invalidated 78 * by this call, unless they had been g_object_ref()-ed manually. 79 * 80 * Since: 3.0.5 81 **/ 82void i_cal_object_free_global_objects(void) 83{ 84 GHashTable *objects; 85 86 G_LOCK(global_objects); 87 88 objects = global_objects; 89 global_objects = NULL; 90 91 G_UNLOCK(global_objects); 92 93 if (objects) { 94 GHashTableIter iter; 95 gpointer key, value; 96 97 g_hash_table_iter_init(&iter, objects); 98 while (g_hash_table_iter_next(&iter, &key, &value)) { 99 g_object_weak_unref(value, global_data_object_freed_cb, key); 100 } 101 102 g_hash_table_destroy (objects); 103 } 104} 105 106struct _ICalObjectPrivate 107{ 108 GMutex props_lock; /* to guard all the below members */ 109 110 gpointer native; 111 GDestroyNotify native_destroy_func; 112 gboolean is_global_memory; 113 GObject *owner; 114 GSList *dependers; /* referenced GObject-s */ 115}; 116 117G_DEFINE_ABSTRACT_TYPE(ICalObject, i_cal_object, G_TYPE_OBJECT) 118 119enum 120{ 121 PROP_0, 122 PROP_NATIVE, 123 PROP_NATIVE_DESTROY_FUNC, 124 PROP_IS_GLOBAL_MEMORY, 125 PROP_OWNER 126}; 127 128static void i_cal_object_set_property(GObject *object, guint property_id, 129 const GValue * value, GParamSpec * pspec) 130{ 131 ICalObject *iobject; 132 133 g_return_if_fail(I_CAL_IS_OBJECT(object)); 134 135 iobject = I_CAL_OBJECT(object); 136 137 switch (property_id) { 138 case PROP_NATIVE: 139 /* no need for LOCK_PROPS() here, these can be set only during construction time */ 140 g_return_if_fail(iobject->priv->native == NULL); 141 iobject->priv->native = g_value_get_pointer(value); 142 return; 143 144 case PROP_NATIVE_DESTROY_FUNC: 145 i_cal_object_set_native_destroy_func(iobject, g_value_get_pointer(value)); 146 return; 147 148 case PROP_IS_GLOBAL_MEMORY: 149 /* no need for LOCK_PROPS() here, these can be set only during construction time */ 150 iobject->priv->is_global_memory = g_value_get_boolean(value); 151 return; 152 153 case PROP_OWNER: 154 i_cal_object_set_owner(iobject, g_value_get_object(value)); 155 return; 156 } 157 158 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); 159} 160 161static void i_cal_object_get_property(GObject *object, guint property_id, 162 GValue * value, GParamSpec * pspec) 163{ 164 ICalObject *iobject; 165 166 g_return_if_fail(I_CAL_IS_OBJECT(object)); 167 168 iobject = I_CAL_OBJECT(object); 169 170 switch (property_id) { 171 case PROP_NATIVE: 172 g_value_set_pointer(value, i_cal_object_get_native(iobject)); 173 return; 174 175 case PROP_NATIVE_DESTROY_FUNC: 176 g_value_set_pointer(value, i_cal_object_get_native_destroy_func(iobject)); 177 return; 178 179 case PROP_IS_GLOBAL_MEMORY: 180 g_value_set_boolean(value, i_cal_object_get_is_global_memory(iobject)); 181 return; 182 183 case PROP_OWNER: 184 g_value_take_object(value, i_cal_object_ref_owner(iobject)); 185 return; 186 } 187 188 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); 189} 190 191static void i_cal_object_finalize(GObject *object) 192{ 193 ICalObject *iobject = I_CAL_OBJECT(object); 194 195 if (!iobject->priv->owner && !iobject->priv->is_global_memory && 196 iobject->priv->native && iobject->priv->native_destroy_func) { 197 iobject->priv->native_destroy_func(iobject->priv->native); 198 } 199 200 if (iobject->priv->owner) { 201 g_object_unref(iobject->priv->owner); 202 } 203 204 g_slist_free_full(iobject->priv->dependers, g_object_unref); 205 206 g_mutex_clear(&iobject->priv->props_lock); 207 208 /* Chain up to parent's method. */ 209 G_OBJECT_CLASS(i_cal_object_parent_class)->finalize(object); 210} 211 212static void i_cal_object_class_init(ICalObjectClass * class) 213{ 214 GObjectClass *object_class; 215 216 g_type_class_add_private(class, sizeof(ICalObjectPrivate)); 217 218 object_class = G_OBJECT_CLASS(class); 219 object_class->set_property = i_cal_object_set_property; 220 object_class->get_property = i_cal_object_get_property; 221 object_class->finalize = i_cal_object_finalize; 222 223 /** 224 * ICalObject:native: 225 * 226 * The native libical structure for this ICalObject. 227 **/ 228 g_object_class_install_property( 229 object_class, 230 PROP_NATIVE, 231 g_param_spec_pointer( 232 "native", 233 "Native", 234 "Native libical structure", 235 G_PARAM_READWRITE | 236 G_PARAM_CONSTRUCT_ONLY | 237 G_PARAM_STATIC_STRINGS)); 238 239 /** 240 * ICalObject:native-destroy-func: 241 * 242 * GDestroyNotify function to use to destroy the native libical pointer. 243 **/ 244 g_object_class_install_property( 245 object_class, 246 PROP_NATIVE_DESTROY_FUNC, 247 g_param_spec_pointer( 248 "native-destroy-func", 249 "Native-Destroy-Func", 250 "GDestroyNotify function to use to destroy the native libical structure", 251 G_PARAM_READWRITE | 252 G_PARAM_STATIC_STRINGS)); 253 254 /** 255 * ICalObject:is-global-memory: 256 * 257 * Whether the native libical structure is from a global shared memory. 258 * If TRUE, then it is not freed on #ICalObject's finalize. 259 **/ 260 g_object_class_install_property( 261 object_class, 262 PROP_IS_GLOBAL_MEMORY, 263 g_param_spec_boolean( 264 "is-global-memory", 265 "Is-Global-Memory", 266 "Whether the native libical structure is from a global shared memory", 267 FALSE, 268 G_PARAM_READWRITE | 269 G_PARAM_CONSTRUCT_ONLY | 270 G_PARAM_STATIC_STRINGS)); 271 272 /** 273 * ICalObject:owner: 274 * 275 * Owner of the native libical structure. If set, then it is 276 * responsible for a free of the native libical structure. 277 **/ 278 g_object_class_install_property( 279 object_class, 280 PROP_OWNER, 281 g_param_spec_object( 282 "owner", 283 "Owner", 284 "The native libical structure owner", 285 G_TYPE_OBJECT, 286 G_PARAM_READWRITE | 287 G_PARAM_STATIC_STRINGS)); 288} 289 290static void i_cal_object_init(ICalObject *iobject) 291{ 292 iobject->priv = G_TYPE_INSTANCE_GET_PRIVATE(iobject, I_CAL_TYPE_OBJECT, ICalObjectPrivate); 293 294 g_mutex_init(&iobject->priv->props_lock); 295} 296 297/** 298 * i_cal_object_construct: (skip) 299 * @object_type: a GType of an #ICalObject descendat to construct 300 * @native: a native libical structure 301 * @native_destroy_func: a function to be called on @native when it should be freed 302 * @is_global_memory: whether @native is a global shared memory structure 303 * @owner: (allow-none): an owner of @native 304 * 305 * Creates an #ICalObject descendant of type @type and initialize private members 306 * of it. The descendants should call this function in their _new() function, or use 307 * corresponding properties during the construction time. This should not be mixed, 308 * either use properties or this function. 309 * 310 * The @is_global_memory defines whether the returned object is a singleton, 311 * in which case the object is owned by the libical-glib and should not be freed, 312 * or, when %FALSE, the returned object is a newly created object and the caller 313 * is responsible to free it with g_object_unref(). 314 * 315 * Returns: (transfer full): an #ICalObject descendant of type @type 316 * 317 * Since: 1.0 318 **/ 319gpointer 320i_cal_object_construct(GType object_type, 321 gpointer native, 322 GDestroyNotify native_destroy_func, 323 gboolean is_global_memory, 324 GObject *owner) 325{ 326 ICalObject *iobject; 327 328 g_return_val_if_fail(object_type != G_TYPE_INVALID, NULL); 329 g_return_val_if_fail(native != NULL, NULL); 330 if (owner) 331 g_return_val_if_fail(G_IS_OBJECT(owner), NULL); 332 333 if (is_global_memory) { 334 G_LOCK(global_objects); 335 336 if (global_objects) { 337 GlobalData tmp_gd; 338 339 tmp_gd.object_type = object_type; 340 tmp_gd.native = native; 341 342 iobject = g_hash_table_lookup(global_objects, &tmp_gd); 343 344 if (iobject) { 345 G_UNLOCK(global_objects); 346 return iobject; 347 } 348 } 349 } 350 351 iobject = g_object_new(object_type, NULL); 352 353 /* LOCK_PROPS (iobject); */ 354 355 g_warn_if_fail(iobject->priv->native == NULL); 356 357 iobject->priv->native = native; 358 iobject->priv->native_destroy_func = native_destroy_func; 359 iobject->priv->is_global_memory = is_global_memory; 360 i_cal_object_set_owner(iobject, owner); 361 362 /* UNLOCK_PROPS (iobject); */ 363 364 if (is_global_memory) { 365 GlobalData *gd; 366 367 gd = g_new0(GlobalData, 1); 368 gd->object_type = object_type; 369 gd->native = native; 370 371 g_object_weak_ref(G_OBJECT(iobject), global_data_object_freed_cb, gd); 372 373 if (!global_objects) { 374 global_objects = 375 g_hash_table_new_full(global_data_hash, global_data_equal, 376 g_free, g_object_unref); 377 } 378 379 g_hash_table_insert(global_objects, gd, iobject); 380 381 G_UNLOCK(global_objects); 382 } 383 384 return iobject; 385} 386 387/** 388 * i_cal_object_get_native: (skip) 389 * @iobject: an #ICalObject 390 * 391 * Obtain native libical structure pointer associated with this @iobject. 392 * 393 * Returns: (transfer none): Native libical structure pointer associated with this @iobject. 394 * 395 * Since: 1.0 396 **/ 397gpointer i_cal_object_get_native(ICalObject *iobject) 398{ 399 gpointer native; 400 401 g_return_val_if_fail(I_CAL_IS_OBJECT(iobject), NULL); 402 403 LOCK_PROPS(iobject); 404 405 native = iobject->priv->native; 406 407 UNLOCK_PROPS(iobject); 408 409 return native; 410} 411 412/** 413 * i_cal_object_steal_native: 414 * @iobject: an #ICalObject 415 * 416 * Obtain native libical structure pointer associated with this @iobject and sets the one 417 * at @iobject to NULL, thus it's invalid since now on. 418 * 419 * Returns: (transfer full): Native libical structure pointer associated with this @iobject. 420 * 421 * Since: 1.0 422 **/ 423gpointer i_cal_object_steal_native(ICalObject *iobject) 424{ 425 gpointer native; 426 427 g_return_val_if_fail(I_CAL_IS_OBJECT(iobject), NULL); 428 429 LOCK_PROPS(iobject); 430 431 native = iobject->priv->native; 432 iobject->priv->native = NULL; 433 434 UNLOCK_PROPS(iobject); 435 436 return native; 437} 438 439/** 440 * i_cal_object_get_native_destroy_func: (skip) 441 * @iobject: an #ICalObject 442 * 443 * Obtain a pointer to a function responsible to free the libical native structure. 444 * 445 * Returns: (transfer none): Pointer to a function responsible to free 446 * the libical native structure. 447 * 448 * Since: 1.0 449 **/ 450GDestroyNotify i_cal_object_get_native_destroy_func(ICalObject *iobject) 451{ 452 GDestroyNotify func; 453 454 g_return_val_if_fail(I_CAL_IS_OBJECT(iobject), NULL); 455 456 LOCK_PROPS(iobject); 457 458 func = iobject->priv->native_destroy_func; 459 460 UNLOCK_PROPS(iobject); 461 462 return func; 463} 464 465/** 466 * i_cal_object_set_native_destroy_func: 467 * @iobject: an #ICalObject 468 * @native_destroy_func: Function to be used to destroy the native libical structure 469 * 470 * Sets a function to be used to destroy the native libical structure. 471 * 472 * Since: 1.0 473 **/ 474void i_cal_object_set_native_destroy_func(ICalObject *iobject, GDestroyNotify native_destroy_func) 475{ 476 g_return_if_fail(I_CAL_IS_OBJECT(iobject)); 477 478 LOCK_PROPS(iobject); 479 480 if (native_destroy_func == iobject->priv->native_destroy_func) { 481 UNLOCK_PROPS(iobject); 482 return; 483 } 484 485 iobject->priv->native_destroy_func = native_destroy_func; 486 487 UNLOCK_PROPS(iobject); 488 489 g_object_notify(G_OBJECT(iobject), "native-destroy-func"); 490} 491 492/** 493 * i_cal_object_get_is_global_memory: 494 * @iobject: an #ICalObject 495 * 496 * Obtains whether the native libical structure is a global shared memory, 497 * thus should not be destroyed. This can be set only during contruction time. 498 * 499 * Returns: Whether the native libical structure is a global shared memory. 500 * 501 * Since: 1.0 502 **/ 503gboolean i_cal_object_get_is_global_memory(ICalObject *iobject) 504{ 505 gboolean is_global_memory; 506 507 g_return_val_if_fail(I_CAL_IS_OBJECT(iobject), FALSE); 508 509 LOCK_PROPS(iobject); 510 511 is_global_memory = iobject->priv->is_global_memory; 512 513 UNLOCK_PROPS(iobject); 514 515 return is_global_memory; 516} 517 518/** 519 * i_cal_object_set_owner: 520 * @iobject: an #ICalObject 521 * @owner: Owner of the native libical structure 522 * 523 * Sets an owner of the native libical structure, that is an object responsible 524 * for a destroy of the native libical structure. 525 * 526 * Since: 1.0 527 **/ 528void i_cal_object_set_owner(ICalObject *iobject, GObject *owner) 529{ 530 g_return_if_fail(I_CAL_IS_OBJECT(iobject)); 531 if (owner) 532 g_return_if_fail(G_IS_OBJECT(owner)); 533 534 LOCK_PROPS(iobject); 535 536 if (owner == iobject->priv->owner) { 537 UNLOCK_PROPS(iobject); 538 return; 539 } 540 541 if (owner) { 542 g_object_ref(owner); 543 } 544 g_clear_object(&iobject->priv->owner); 545 iobject->priv->owner = owner; 546 547 UNLOCK_PROPS(iobject); 548 549 g_object_notify(G_OBJECT(iobject), "owner"); 550} 551 552/** 553 * i_cal_object_ref_owner: 554 * @iobject: an #ICalObject 555 * 556 * Obtain current owner of the native libical structure. The returned pointer, 557 * if not NULL, is referenced for thread safety. Unref it with g_object_unref 558 * when done with it. 559 * 560 * Returns: (transfer full) (allow-none): Current owner of the libical 561 * native structure. returns NULL, when there is no owner. 562 * 563 * Since: 1.0 564 **/ 565GObject *i_cal_object_ref_owner(ICalObject *iobject) 566{ 567 GObject *owner; 568 569 g_return_val_if_fail(I_CAL_IS_OBJECT(iobject), NULL); 570 571 LOCK_PROPS(iobject); 572 573 owner = iobject->priv->owner; 574 if (owner) 575 g_object_ref(owner); 576 577 UNLOCK_PROPS(iobject); 578 579 return owner; 580} 581 582/** 583 * i_cal_object_remove_owner: 584 * @iobject: an #ICalObject 585 * 586 * Unref and remove the owner. 587 * 588 * Since: 1.0 589 **/ 590void i_cal_object_remove_owner(ICalObject *iobject) 591{ 592 GObject *owner; 593 594 g_return_if_fail(I_CAL_IS_OBJECT(iobject)); 595 596 LOCK_PROPS(iobject); 597 598 owner = iobject->priv->owner; 599 if (owner) { 600 g_object_unref(owner); 601 iobject->priv->owner = NULL; 602 } 603 604 UNLOCK_PROPS(iobject); 605} 606 607/** 608 * i_cal_object_add_depender: 609 * @iobject: an #ICalObject 610 * @depender: a #GObject depender 611 * 612 * Adds a @depender into the list of objects which should not be destroyed before 613 * this @iobject. It's usually used for cases where the @iobject uses native libical 614 * structure from the @depender. The @depender is referenced. It's illegal to try 615 * to add one @depender multiple times. 616 * 617 * Since: 1.0 618 **/ 619void i_cal_object_add_depender(ICalObject *iobject, GObject *depender) 620{ 621 g_return_if_fail(I_CAL_IS_OBJECT(iobject)); 622 g_return_if_fail(G_IS_OBJECT(depender)); 623 624 LOCK_PROPS(iobject); 625 626 if (g_slist_find(iobject->priv->dependers, depender)) { 627 g_warn_if_reached(); 628 UNLOCK_PROPS(iobject); 629 return; 630 } 631 632 iobject->priv->dependers = g_slist_prepend(iobject->priv->dependers, g_object_ref(depender)); 633 634 UNLOCK_PROPS(iobject); 635} 636 637/** 638 * i_cal_object_remove_depender: 639 * @iobject: an #ICalObject 640 * @depender: a #GObject depender 641 * 642 * Removes a @depender from the list of objects which should not be destroyed before 643 * this @iobject, previously added with i_cal_object_add_depender(). It's illegal to try 644 * to remove the @depender which is not in the internal list. 645 * 646 * Since: 1.0 647 **/ 648void i_cal_object_remove_depender(ICalObject *iobject, GObject *depender) 649{ 650 g_return_if_fail(I_CAL_IS_OBJECT(iobject)); 651 g_return_if_fail(G_IS_OBJECT(depender)); 652 653 LOCK_PROPS(iobject); 654 655 if (!g_slist_find(iobject->priv->dependers, depender)) { 656 g_warn_if_reached(); 657 UNLOCK_PROPS(iobject); 658 return; 659 } 660 661 iobject->priv->dependers = g_slist_remove(iobject->priv->dependers, depender); 662 g_object_unref(depender); 663 664 UNLOCK_PROPS(iobject); 665} 666