diff options
Diffstat (limited to 'camel/camel-object.c')
-rw-r--r-- | camel/camel-object.c | 1834 |
1 files changed, 0 insertions, 1834 deletions
diff --git a/camel/camel-object.c b/camel/camel-object.c deleted file mode 100644 index 744e9f7566..0000000000 --- a/camel/camel-object.c +++ /dev/null @@ -1,1834 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- * - * - * Author: - * Michael Zucchi <notzed@ximian.com> - * - * Copyright 2000-2003 Ximian, Inc. (www.ximian.com) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU General Public - * License as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <stdio.h> -#include <string.h> -#include <pthread.h> -#include <semaphore.h> - -#include "camel-object.h" -#include "camel-file-utils.h" - -#include <e-util/e-memory.h> -#include <e-util/e-msgport.h> - -#define d(x) - -/* I just mashed the keyboard for these... */ -#define CAMEL_OBJECT_MAGIC 0x77A344ED -#define CAMEL_OBJECT_CLASS_MAGIC 0xEE26A997 -#define CAMEL_OBJECT_FINALISED_MAGIC 0x84AC365F -#define CAMEL_OBJECT_CLASS_FINALISED_MAGIC 0x7621ABCD - -/* ** Quickie type system ************************************************* */ - -/* A 'locked' hooklist, that is only allocated on demand */ -typedef struct _CamelHookList { - EMutex *lock; - - unsigned int depth:30; /* recursive event depth */ - unsigned int flags:2; /* flags, see below */ - - unsigned int list_length; - struct _CamelHookPair *list; -} CamelHookList; - -#define CAMEL_HOOK_PAIR_REMOVED (1<<0) - -/* a 'hook pair', actually a hook tuple, we just store all hooked events in the same list, - and just comapre as we go, rather than storing separate lists for each hook type - - the name field just points directly to the key field in the class's preplist hashtable. - This way we can just use a direct pointer compare when scanning it, and also saves - copying the string */ -typedef struct _CamelHookPair -{ - struct _CamelHookPair *next; /* next MUST be the first member */ - - unsigned int id:30; - unsigned int flags:2; /* removed, etc */ - - const char *name; /* points to the key field in the classes preplist, static memory */ - union { - CamelObjectEventHookFunc event; - CamelObjectEventPrepFunc prep; - char *filename; - } func; - void *data; -} CamelHookPair; - -struct _CamelObjectBag { - GHashTable *object_table; /* object by key */ - GHashTable *key_table; /* key by object */ - CamelCopyFunc copy_key; - GFreeFunc free_key; - pthread_t owner; /* the thread that has reserved the bag for a new entry */ - sem_t reserve_sem; /* used to track ownership */ -}; - -/* used to tag a bag hookpair */ -static const char *bag_name = "object:bag"; - -/* meta-data stuff */ -static void co_metadata_free(CamelObject *obj, CamelObjectMeta *meta); -static CamelObjectMeta *co_metadata_get(CamelObject *obj); -static CamelHookPair *co_metadata_pair(CamelObject *obj, int create); - -static const char *meta_name = "object:meta"; -#define CAMEL_OBJECT_STATE_FILE_MAGIC "CLMD" - -/* ********************************************************************** */ - -static CamelHookList *camel_object_get_hooks(CamelObject *o); -static void camel_object_free_hooks(CamelObject *o); -static void camel_object_bag_remove_unlocked(CamelObjectBag *inbag, CamelObject *o, CamelHookList *hooks); - -#define camel_object_unget_hooks(o) (e_mutex_unlock((CAMEL_OBJECT(o)->hooks->lock))) - - -/* ********************************************************************** */ - -static pthread_mutex_t chunks_lock = PTHREAD_MUTEX_INITIALIZER; - -static EMemChunk *pair_chunks; -static EMemChunk *hook_chunks; -static unsigned int pair_id = 1; - -static EMutex *type_lock; - -static GHashTable *type_table; -static EMemChunk *type_chunks; - -CamelType camel_object_type; - -#define P_LOCK(l) (pthread_mutex_lock(&l)) -#define P_UNLOCK(l) (pthread_mutex_unlock(&l)) -#define E_LOCK(l) (e_mutex_lock(l)) -#define E_UNLOCK(l) (e_mutex_unlock(l)) -#define CLASS_LOCK(k) (g_mutex_lock((((CamelObjectClass *)k)->lock))) -#define CLASS_UNLOCK(k) (g_mutex_unlock((((CamelObjectClass *)k)->lock))) - - -static struct _CamelHookPair * -pair_alloc(void) -{ - CamelHookPair *pair; - - P_LOCK(chunks_lock); - pair = e_memchunk_alloc(pair_chunks); - pair->id = pair_id++; - if (pair_id == 0) - pair_id = 1; - P_UNLOCK(chunks_lock); - - return pair; -} - -static void -pair_free(CamelHookPair *pair) -{ - g_assert(pair_chunks != NULL); - - P_LOCK(chunks_lock); - e_memchunk_free(pair_chunks, pair); - P_UNLOCK(chunks_lock); -} - -static struct _CamelHookList * -hooks_alloc(void) -{ - CamelHookList *hooks; - - P_LOCK(chunks_lock); - hooks = e_memchunk_alloc(hook_chunks); - P_UNLOCK(chunks_lock); - - return hooks; -} - -static void -hooks_free(CamelHookList *hooks) -{ - g_assert(hook_chunks != NULL); - - P_LOCK(chunks_lock); - e_memchunk_free(hook_chunks, hooks); - P_UNLOCK(chunks_lock); -} - -/* not checked locked, who cares, only required for people that want to redefine root objects */ -void -camel_type_init(void) -{ - static int init = FALSE; - - if (init) - return; - - init = TRUE; - pair_chunks = e_memchunk_new(16, sizeof(CamelHookPair)); - hook_chunks = e_memchunk_new(16, sizeof(CamelHookList)); - type_lock = e_mutex_new(E_MUTEX_REC); - type_chunks = e_memchunk_new(32, sizeof(CamelType)); - type_table = g_hash_table_new(NULL, NULL); -} - -/* ************************************************************************ */ - -/* Should this return the object to the caller? */ -static void -cobject_init (CamelObject *o, CamelObjectClass *klass) -{ - o->klass = klass; - o->magic = CAMEL_OBJECT_MAGIC; - o->ref_count = 1; - o->flags = 0; -} - -static void -cobject_finalise(CamelObject *o) -{ - /*printf("%p: finalise %s\n", o, o->klass->name);*/ - - g_assert(o->ref_count == 0); - - camel_object_free_hooks(o); - - o->magic = CAMEL_OBJECT_FINALISED_MAGIC; - o->klass = NULL; -} - -static int -cobject_getv(CamelObject *o, CamelException *ex, CamelArgGetV *args) -{ - int i; - guint32 tag; - - for (i=0;i<args->argc;i++) { - CamelArgGet *arg = &args->argv[i]; - - tag = arg->tag; - - switch (tag & CAMEL_ARG_TAG) { - case CAMEL_OBJECT_ARG_DESCRIPTION: - *arg->ca_str = (char *)o->klass->name; - break; - case CAMEL_OBJECT_ARG_METADATA: - *arg->ca_ptr = co_metadata_get(o); - break; - case CAMEL_OBJECT_ARG_STATE_FILE: { - CamelHookPair *pair = co_metadata_pair(o, FALSE); - - if (pair) { - *arg->ca_str = g_strdup(pair->func.filename); - camel_object_unget_hooks(o); - } - break; } - } - } - - /* could have flags or stuff here? */ - return 0; -} - -static int -cobject_setv(CamelObject *o, CamelException *ex, CamelArgV *args) -{ - int i; - guint32 tag; - - for (i=0;i<args->argc;i++) { - CamelArg *arg = &args->argv[i]; - - tag = arg->tag; - - switch (tag & CAMEL_ARG_TAG) { - case CAMEL_OBJECT_ARG_STATE_FILE: { - CamelHookPair *pair; - - /* We store the filename on the meta-data hook-pair */ - pair = co_metadata_pair(o, TRUE); - g_free(pair->func.filename); - pair->func.filename = g_strdup(arg->ca_str); - camel_object_unget_hooks(o); - break; } - } - } - - /* could have flags or stuff here? */ - return 0; -} - -static void -cobject_free(CamelObject *o, guint32 tag, void *value) -{ - switch(tag & CAMEL_ARG_TAG) { - case CAMEL_OBJECT_ARG_METADATA: - co_metadata_free(o, value); - break; - case CAMEL_OBJECT_ARG_STATE_FILE: - g_free(value); - break; - case CAMEL_OBJECT_ARG_PERSISTENT_PROPERTIES: - g_slist_free((GSList *)value); - break; - } -} - -static char * -cobject_meta_get(CamelObject *obj, const char * name) -{ - CamelHookPair *pair; - CamelObjectMeta *meta; - char *res = NULL; - - g_return_val_if_fail(CAMEL_IS_OBJECT (obj), 0); - g_return_val_if_fail(name != NULL, 0); - - pair = co_metadata_pair(obj, FALSE); - if (pair) { - meta = pair->data; - while (meta) { - if (!strcmp(meta->name, name)) { - res = g_strdup(meta->value); - break; - } - meta = meta->next; - } - camel_object_unget_hooks(obj); - } - - return res; -} - -static gboolean -cobject_meta_set(CamelObject *obj, const char * name, const char *value) -{ - CamelHookPair *pair; - int changed = FALSE; - CamelObjectMeta *meta, *metap; - - g_return_val_if_fail(CAMEL_IS_OBJECT (obj), FALSE); - g_return_val_if_fail(name != NULL, FALSE); - - if (obj->hooks == NULL && value == NULL) - return FALSE; - - pair = co_metadata_pair(obj, TRUE); - meta = pair->data; - metap = (CamelObjectMeta *)&pair->data; - while (meta) { - if (!strcmp(meta->name, name)) - break; - metap = meta; - meta = meta->next; - } - - /* TODO: The camelobjectmeta structure is identical to - CamelTag, they could be merged or share common code */ - if (meta == NULL) { - if (value == NULL) - goto done; - meta = g_malloc(sizeof(*meta) + strlen(name)); - meta->next = pair->data; - pair->data = meta; - strcpy(meta->name, name); - meta->value = g_strdup(value); - changed = TRUE; - } else if (value == NULL) { - metap->next = meta->next; - g_free(meta->value); - g_free(meta); - changed = TRUE; - } else if (strcmp(meta->value, value) != 0) { - g_free(meta->value); - meta->value = g_strdup(value); - changed = TRUE; - } - -done: - camel_object_unget_hooks(obj); - - return changed; -} - -/* State file for CamelObject data. Any later versions should only append data. - - version:uint32 - - Version 0 of the file: - - version:uint32 = 0 - count:uint32 -- count of meta-data items - ( name:string value:string ) *count -- meta-data items - - Version 1 of the file adds: - count:uint32 -- count of persistent properties - ( tag:uing32 value:tagtype ) *count -- persistent properties - -*/ - -static int -cobject_state_read(CamelObject *obj, FILE *fp) -{ - guint32 i, count, version; - - /* NB: for later versions, just check the version is 1 .. known version */ - if (camel_file_util_decode_uint32(fp, &version) == -1 - || version > 1 - || camel_file_util_decode_uint32(fp, &count) == -1) - return -1; - - for (i=0;i<count;i++) { - char *name = NULL, *value = NULL; - - if (camel_file_util_decode_string(fp, &name) == 0 - && camel_file_util_decode_string(fp, &value) == 0) { - camel_object_meta_set(obj, name, value); - g_free(name); - g_free(value); - } else { - g_free(name); - g_free(value); - - return -1; - } - } - - if (version > 0) { - CamelArgV *argv; - - if (camel_file_util_decode_uint32(fp, &count) == -1 - || count == 0) { - /* maybe it was just version 0 afterall */ - return 0; - } - - /* we batch up the properties and set them in one go */ - argv = g_malloc(sizeof(*argv) + (count - CAMEL_ARGV_MAX) * sizeof(argv->argv[0])); - argv->argc = 0; - for (i=0;i<count;i++) { - if (camel_file_util_decode_uint32(fp, &argv->argv[argv->argc].tag) == -1) - goto cleanup; - - /* so far,only do strings and ints, doubles could be added, - object's would require a serialisation interface */ - - switch(argv->argv[argv->argc].tag & CAMEL_ARG_TYPE) { - case CAMEL_ARG_INT: - case CAMEL_ARG_BOO: - if (camel_file_util_decode_uint32(fp, &argv->argv[argv->argc].ca_int) == -1) - goto cleanup; - break; - case CAMEL_ARG_STR: - if (camel_file_util_decode_string(fp, &argv->argv[argv->argc].ca_str) == -1) - goto cleanup; - break; - default: - goto cleanup; - } - - argv->argc++; - } - - camel_object_setv(obj, NULL, argv); - cleanup: - for (i=0;i<argv->argc;i++) { - if ((argv->argv[i].tag & CAMEL_ARG_TYPE) == CAMEL_ARG_STR) - g_free(argv->argv[i].ca_str); - } - g_free(argv); - } - - return 0; -} - -/* TODO: should pass exception around */ -static int -cobject_state_write(CamelObject *obj, FILE *fp) -{ - gint32 count, i; - CamelObjectMeta *meta = NULL, *scan; - int res = -1; - GSList *props = NULL, *l; - CamelArgGetV *arggetv = NULL; - CamelArgV *argv = NULL; - - camel_object_get(obj, NULL, CAMEL_OBJECT_METADATA, &meta, NULL); - - count = 0; - scan = meta; - while (scan) { - count++; - scan = scan->next; - } - - /* current version is 1 */ - if (camel_file_util_encode_uint32(fp, 1) == -1 - || camel_file_util_encode_uint32(fp, count) == -1) - goto abort; - - scan = meta; - while (scan) { - if (camel_file_util_encode_string(fp, meta->name) == -1 - || camel_file_util_encode_string(fp, meta->value) == -1) - goto abort; - scan = scan->next; - } - - camel_object_get(obj, NULL, CAMEL_OBJECT_PERSISTENT_PROPERTIES, &props, NULL); - - /* we build an arggetv to query the object atomically, - we also need an argv to store the results - bit messy */ - - count = g_slist_length(props); - - arggetv = g_malloc0(sizeof(*arggetv) + (count - CAMEL_ARGV_MAX) * sizeof(arggetv->argv[0])); - argv = g_malloc0(sizeof(*argv) + (count - CAMEL_ARGV_MAX) * sizeof(argv->argv[0])); - l = props; - i = 0; - while (l) { - CamelProperty *prop = l->data; - - argv->argv[i].tag = prop->tag; - arggetv->argv[i].tag = prop->tag; - arggetv->argv[i].ca_ptr = &argv->argv[i].ca_ptr; - - i++; - l = l->next; - } - arggetv->argc = i; - argv->argc = i; - - camel_object_getv(obj, NULL, arggetv); - - if (camel_file_util_encode_uint32(fp, count) == -1) - goto abort; - - for (i=0;i<argv->argc;i++) { - CamelArg *arg = &argv->argv[i]; - - if (camel_file_util_encode_uint32(fp, arg->tag) == -1) - goto abort; - - switch (arg->tag & CAMEL_ARG_TYPE) { - case CAMEL_ARG_INT: - case CAMEL_ARG_BOO: - if (camel_file_util_encode_uint32(fp, arg->ca_int) == -1) - goto abort; - break; - case CAMEL_ARG_STR: - if (camel_file_util_encode_string(fp, arg->ca_str) == -1) - goto abort; - break; - } - } - - res = 0; -abort: - for (i=0;i<argv->argc;i++) { - CamelArg *arg = &argv->argv[i]; - - if ((argv->argv[i].tag & CAMEL_ARG_TYPE) == CAMEL_ARG_STR) - camel_object_free(obj, arg->tag, arg->ca_str); - } - - g_free(argv); - g_free(arggetv); - - if (props) - camel_object_free(obj, CAMEL_OBJECT_PERSISTENT_PROPERTIES, props); - - if (meta) - camel_object_free(obj, CAMEL_OBJECT_METADATA, meta); - - return res; -} - - -static void -cobject_class_init(CamelObjectClass *klass) -{ - klass->magic = CAMEL_OBJECT_CLASS_MAGIC; - - klass->getv = cobject_getv; - klass->setv = cobject_setv; - klass->free = cobject_free; - - klass->meta_get = cobject_meta_get; - klass->meta_set = cobject_meta_set; - klass->state_read = cobject_state_read; - klass->state_write = cobject_state_write; - - camel_object_class_add_event(klass, "finalize", NULL); - camel_object_class_add_event(klass, "meta_changed", NULL); -} - -static void -cobject_class_finalise(CamelObjectClass * klass) -{ - klass->magic = CAMEL_OBJECT_CLASS_FINALISED_MAGIC; - - g_free(klass); -} - -CamelType -camel_object_get_type (void) -{ - if (camel_object_type == CAMEL_INVALID_TYPE) { - camel_type_init(); - - camel_object_type = camel_type_register(NULL, "CamelObject", /*, 0, 0*/ - sizeof(CamelObject), sizeof(CamelObjectClass), - cobject_class_init, cobject_class_finalise, - cobject_init, cobject_finalise); - } - - return camel_object_type; -} - -static void -camel_type_class_init(CamelObjectClass *klass, CamelObjectClass *type) -{ - if (type->parent) - camel_type_class_init(klass, type->parent); - - if (type->klass_init) - type->klass_init(klass); -} - -CamelType -camel_type_register (CamelType parent, const char * name, - /*unsigned int ver, unsigned int rev,*/ - size_t object_size, size_t klass_size, - CamelObjectClassInitFunc class_init, - CamelObjectClassFinalizeFunc class_finalise, - CamelObjectInitFunc object_init, - CamelObjectFinalizeFunc object_finalise) -{ - CamelObjectClass *klass; - /*int offset; - size_t size;*/ - - if (parent != NULL && parent->magic != CAMEL_OBJECT_CLASS_MAGIC) { - g_warning("camel_type_register: invalid junk parent class for '%s'", name); - return NULL; - } - - E_LOCK(type_lock); - - /* Have to check creation, it might've happened in another thread before we got here */ - klass = g_hash_table_lookup(type_table, name); - if (klass != NULL) { - if (klass->klass_size != klass_size || klass->object_size != object_size - || klass->klass_init != class_init || klass->klass_finalise != class_finalise - || klass->init != object_init || klass->finalise != object_finalise) { - g_warning("camel_type_register: Trying to re-register class '%s'", name); - klass = NULL; - } - E_UNLOCK(type_lock); - return klass; - } - - /* this is for objects with no parent as part of their struct ('interfaces'?) */ - /*offset = parent?parent->klass_size:0; - offset = (offset + 3) & (~3); - - size = offset + klass_size; - - klass = g_malloc0(size); - - klass->klass_size = size; - klass->klass_data = offset; - - offset = parent?parent->object_size:0; - offset = (offset + 3) & (~3); - - klass->object_size = offset + object_size; - klass->object_data = offset;*/ - - if (parent - && klass_size < parent->klass_size) { - g_warning("camel_type_register: '%s' has smaller class size than parent '%s'", name, parent->name); - E_UNLOCK(type_lock); - return NULL; - } - - klass = g_malloc0(klass_size); - klass->klass_size = klass_size; - klass->object_size = object_size; - klass->lock = g_mutex_new(); - klass->instance_chunks = e_memchunk_new(8, object_size); - - klass->parent = parent; - if (parent) { - klass->next = parent->child; - parent->child = klass; - } - klass->name = name; - - /*klass->version = ver; - klass->revision = rev;*/ - - klass->klass_init = class_init; - klass->klass_finalise = class_finalise; - - klass->init = object_init; - klass->finalise = object_finalise; - - /* setup before class init, incase class init func uses the type or looks it up ? */ - g_hash_table_insert(type_table, (void *)name, klass); - - camel_type_class_init(klass, klass); - - E_UNLOCK(type_lock); - - return klass; -} - -static void -camel_object_init(CamelObject *o, CamelObjectClass *klass, CamelType type) -{ - if (type->parent) - camel_object_init(o, klass, type->parent); - - if (type->init) - type->init(o, klass); -} - -CamelObject * -camel_object_new(CamelType type) -{ - CamelObject *o; - - if (type == NULL) - return NULL; - - if (type->magic != CAMEL_OBJECT_CLASS_MAGIC) - return NULL; - - CLASS_LOCK(type); - - o = e_memchunk_alloc0(type->instance_chunks); - -#ifdef CAMEL_OBJECT_TRACK_INSTANCES - if (type->instances) - type->instances->prev = o; - o->next = type->instances; - o->prev = NULL; - type->instances = o; -#endif - - CLASS_UNLOCK(type); - - camel_object_init(o, type, type); - - d(printf("%p: new %s()\n", o, o->klass->name)); - - return o; -} - -void -camel_object_ref(void *vo) -{ - register CamelObject *o = vo; - - g_return_if_fail(CAMEL_IS_OBJECT(o)); - - E_LOCK(type_lock); - - o->ref_count++; - d(printf("%p: ref %s(%d)\n", o, o->klass->name, o->ref_count)); - - E_UNLOCK(type_lock); -} - -void -camel_object_unref(void *vo) -{ - register CamelObject *o = vo; - register CamelObjectClass *klass, *k; - CamelHookList *hooks = NULL; - - g_return_if_fail(CAMEL_IS_OBJECT(o)); - - klass = o->klass; - - if (o->hooks) - hooks = camel_object_get_hooks(o); - - E_LOCK(type_lock); - - o->ref_count--; - - d(printf("%p: unref %s(%d)\n", o, o->klass->name, o->ref_count)); - - if (o->ref_count > 0 - || (o->flags & CAMEL_OBJECT_DESTROY)) { - E_UNLOCK(type_lock); - if (hooks) - camel_object_unget_hooks(o); - return; - } - - o->flags |= CAMEL_OBJECT_DESTROY; - - if (hooks) - camel_object_bag_remove_unlocked(NULL, o, hooks); - - E_UNLOCK(type_lock); - - if (hooks) - camel_object_unget_hooks(o); - - camel_object_trigger_event(o, "finalize", NULL); - - k = klass; - while (k) { - if (k->finalise) - k->finalise(o); - k = k->parent; - } - - o->magic = CAMEL_OBJECT_FINALISED_MAGIC; - - CLASS_LOCK(klass); -#ifdef CAMEL_OBJECT_TRACK_INSTANCES - if (o->prev) - o->prev->next = o->next; - else - klass->instances = o->next; - if (o->next) - o->next->prev = o->prev; -#endif - e_memchunk_free(klass->instance_chunks, o); - CLASS_UNLOCK(klass); -} - -const char * -camel_type_to_name (CamelType type) -{ - if (type == NULL) - return "(NULL class)"; - - if (type->magic == CAMEL_OBJECT_CLASS_MAGIC) - return type->name; - - return "(Junk class)"; -} - -CamelType camel_name_to_type(const char *name) -{ - /* TODO: Load a class off disk (!) */ - - return g_hash_table_lookup(type_table, name); -} - -static char * -desc_data(CamelObject *o, int ok) -{ - char *what; - - if (o == NULL) - what = g_strdup("NULL OBJECT"); - else if (o->magic == ok) - what = NULL; - else if (o->magic == CAMEL_OBJECT_MAGIC) - what = g_strdup_printf("CLASS '%s'", ((CamelObjectClass *)o)->name); - else if (o->magic == CAMEL_OBJECT_CLASS_MAGIC) - what = g_strdup_printf("CLASS '%s'", ((CamelObjectClass *)o)->name); - else if (o->magic == CAMEL_OBJECT_FINALISED_MAGIC) - what = g_strdup_printf("finalised OBJECT"); - else if (o->magic == CAMEL_OBJECT_CLASS_FINALISED_MAGIC) - what = g_strdup_printf("finalised CLASS"); - else - what = g_strdup_printf("junk data"); - - return what; -} - -static gboolean -check_magic(void *o, CamelType ctype, int isob) -{ - char *what, *to; - - what = desc_data(o, isob?CAMEL_OBJECT_MAGIC:CAMEL_OBJECT_CLASS_MAGIC); - to = desc_data((CamelObject *)ctype, CAMEL_OBJECT_CLASS_MAGIC); - - if (what || to) { - if (what == NULL) { - if (isob) - what = g_strdup_printf("OBJECT '%s'", ((CamelObject *)o)->klass->name); - else - what = g_strdup_printf("OBJECT '%s'", ((CamelObjectClass *)o)->name); - } - if (to == NULL) - to = g_strdup_printf("OBJECT '%s'", ctype->name); - g_warning("Trying to check %s is %s", what, to); - g_free(what); - g_free(to); - - return FALSE; - } - - return TRUE; -} - -gboolean -camel_object_is (CamelObject *o, CamelType ctype) -{ - CamelObjectClass *k; - - g_return_val_if_fail(check_magic(o, ctype, TRUE), FALSE); - - k = o->klass; - while (k) { - if (k == ctype) - return TRUE; - k = k->parent; - } - - return FALSE; -} - -gboolean -camel_object_class_is (CamelObjectClass *k, CamelType ctype) -{ - g_return_val_if_fail(check_magic(k, ctype, FALSE), FALSE); - - while (k) { - if (k == ctype) - return TRUE; - k = k->parent; - } - - return FALSE; -} - -CamelObject * -camel_object_cast(CamelObject *o, CamelType ctype) -{ - CamelObjectClass *k; - - g_return_val_if_fail(check_magic(o, ctype, TRUE), NULL); - - k = o->klass; - while (k) { - if (k == ctype) - return o; - k = k->parent; - } - - g_warning("Object %p (class '%s') doesn't have '%s' in its hierarchy", o, o->klass->name, ctype->name); - - return NULL; -} - -CamelObjectClass * -camel_object_class_cast(CamelObjectClass *k, CamelType ctype) -{ - CamelObjectClass *r = k; - - g_return_val_if_fail(check_magic(k, ctype, FALSE), NULL); - - while (k) { - if (k == ctype) - return r; - k = k->parent; - } - - g_warning("Class '%s' doesn't have '%s' in its hierarchy", r->name, ctype->name); - - return NULL; -} - -void -camel_object_class_add_event(CamelObjectClass *klass, const char *name, CamelObjectEventPrepFunc prep) -{ - CamelHookPair *pair; - - g_return_if_fail (name); - - pair = klass->hooks; - while (pair) { - if (strcmp(pair->name, name) == 0) { - g_warning("camel_object_class_add_event: `%s' is already declared for '%s'\n", - name, klass->name); - return; - } - pair = pair->next; - } - - pair = pair_alloc(); - pair->name = name; - pair->func.prep = prep; - pair->flags = 0; - - pair->next = klass->hooks; - klass->hooks = pair; -} - -/* free hook data */ -static void -camel_object_free_hooks (CamelObject *o) -{ - CamelHookPair *pair, *next; - - if (o->hooks) { - g_assert(o->hooks->depth == 0); - g_assert((o->hooks->flags & CAMEL_HOOK_PAIR_REMOVED) == 0); - - pair = o->hooks->list; - while (pair) { - next = pair->next; - - if (pair->name == meta_name) { - co_metadata_free(o, pair->data); - g_free(pair->func.filename); - } - - pair_free(pair); - pair = next; - } - e_mutex_destroy(o->hooks->lock); - hooks_free(o->hooks); - o->hooks = NULL; - } -} - -/* return (allocate if required) the object's hook list, locking at the same time */ -static CamelHookList * -camel_object_get_hooks (CamelObject *o) -{ - static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; - CamelHookList *hooks; - - /* if we have it, we dont have to do any other locking, - otherwise use a global lock to setup the object's hook data */ - if (o->hooks == NULL) { - pthread_mutex_lock(&lock); - if (o->hooks == NULL) { - hooks = hooks_alloc(); - hooks->lock = e_mutex_new(E_MUTEX_REC); - hooks->flags = 0; - hooks->depth = 0; - hooks->list_length = 0; - hooks->list = NULL; - o->hooks = hooks; - } - pthread_mutex_unlock(&lock); - } - - e_mutex_lock(o->hooks->lock); - - return o->hooks; -} - -unsigned int -camel_object_hook_event(void *vo, const char * name, CamelObjectEventHookFunc func, void *data) -{ - CamelObject *obj = vo; - CamelHookPair *pair, *hook; - CamelHookList *hooks; - int id; - - g_return_val_if_fail(CAMEL_IS_OBJECT (obj), 0); - g_return_val_if_fail(name != NULL, 0); - g_return_val_if_fail(func != NULL, 0); - - hook = obj->klass->hooks; - while (hook) { - if (strcmp(hook->name, name) == 0) - goto setup; - hook = hook->next; - } - - g_warning("camel_object_hook_event: trying to hook event `%s' in class `%s' with no defined events.", - name, obj->klass->name); - - return 0; - -setup: - /* setup hook pair */ - pair = pair_alloc(); - pair->name = hook->name; /* effectively static! */ - pair->func.event = func; - pair->data = data; - pair->flags = 0; - id = pair->id; - - /* get the hook list object, locked, link in new event hook, unlock */ - hooks = camel_object_get_hooks(obj); - pair->next = hooks->list; - hooks->list = pair; - hooks->list_length++; - camel_object_unget_hooks(obj); - - return id; -} - -void -camel_object_remove_event(void *vo, unsigned int id) -{ - CamelObject *obj = vo; - CamelHookList *hooks; - CamelHookPair *pair, *parent; - - g_return_if_fail (CAMEL_IS_OBJECT (obj)); - g_return_if_fail (id != 0); - - if (obj->hooks == NULL) { - g_warning("camel_object_unhook_event: trying to unhook `%d` from an instance of `%s' with no hooks", - id, obj->klass->name); - return; - } - - /* scan hooks for this event, remove it, or flag it if we're busy */ - hooks = camel_object_get_hooks(obj); - parent = (CamelHookPair *)&hooks->list; - pair = parent->next; - while (pair) { - if (pair->id == id - && (pair->flags & CAMEL_HOOK_PAIR_REMOVED) == 0) { - if (hooks->depth > 0) { - pair->flags |= CAMEL_HOOK_PAIR_REMOVED; - hooks->flags |= CAMEL_HOOK_PAIR_REMOVED; - } else { - parent->next = pair->next; - pair_free(pair); - hooks->list_length--; - } - camel_object_unget_hooks(obj); - return; - } - parent = pair; - pair = pair->next; - } - camel_object_unget_hooks(obj); - - g_warning("camel_object_unhook_event: cannot find hook id %d in instance of `%s'", - id, obj->klass->name); -} - -void -camel_object_unhook_event(void *vo, const char * name, CamelObjectEventHookFunc func, void *data) -{ - CamelObject *obj = vo; - CamelHookList *hooks; - CamelHookPair *pair, *parent; - - g_return_if_fail (CAMEL_IS_OBJECT (obj)); - g_return_if_fail (name != NULL); - g_return_if_fail (func != NULL); - - if (obj->hooks == NULL) { - g_warning("camel_object_unhook_event: trying to unhook `%s` from an instance of `%s' with no hooks", - name, obj->klass->name); - return; - } - - /* scan hooks for this event, remove it, or flag it if we're busy */ - hooks = camel_object_get_hooks(obj); - parent = (CamelHookPair *)&hooks->list; - pair = parent->next; - while (pair) { - if (pair->func.event == func - && pair->data == data - && strcmp(pair->name, name) == 0 - && (pair->flags & CAMEL_HOOK_PAIR_REMOVED) == 0) { - if (hooks->depth > 0) { - pair->flags |= CAMEL_HOOK_PAIR_REMOVED; - hooks->flags |= CAMEL_HOOK_PAIR_REMOVED; - } else { - parent->next = pair->next; - pair_free(pair); - hooks->list_length--; - } - camel_object_unget_hooks(obj); - return; - } - parent = pair; - pair = pair->next; - } - camel_object_unget_hooks(obj); - - g_warning("camel_object_unhook_event: cannot find hook/data pair %p/%p in an instance of `%s' attached to `%s'", - func, data, obj->klass->name, name); -} - -void -camel_object_trigger_event(void *vo, const char * name, void *event_data) -{ - CamelObject *obj = vo; - CamelHookList *hooks; - CamelHookPair *pair, **pairs, *parent, *hook; - int i, size; - const char *prepname; - - g_return_if_fail (CAMEL_IS_OBJECT (obj)); - g_return_if_fail (name); - - hook = obj->klass->hooks; - while (hook) { - if (strcmp(hook->name, name) == 0) - goto trigger; - hook = hook->next; - } - - g_warning("camel_object_trigger_event: trying to trigger unknown event `%s' in class `%s'", - name, obj->klass->name); - - return; - -trigger: - /* try prep function, if false, then quit */ - if (hook->func.prep != NULL && !hook->func.prep(obj, event_data)) - return; - - /* also, no hooks, dont bother going further */ - if (obj->hooks == NULL) - return; - - /* lock the object for hook emission */ - camel_object_ref(obj); - hooks = camel_object_get_hooks(obj); - - if (hooks->list) { - /* first, copy the items in the list, and say we're in an event */ - hooks->depth++; - pair = hooks->list; - size = 0; - pairs = alloca(sizeof(pairs[0]) * hooks->list_length); - prepname = hook->name; - while (pair) { - if (pair->name == prepname) - pairs[size++] = pair; - pair = pair->next; - } - - /* now execute the events we have, if they haven't been removed during our calls */ - for (i=size-1;i>=0;i--) { - pair = pairs[i]; - if ((pair->flags & CAMEL_HOOK_PAIR_REMOVED) == 0) - (pair->func.event) (obj, event_data, pair->data); - } - hooks->depth--; - - /* and if we're out of any events, then clean up any pending removes */ - if (hooks->depth == 0 && (hooks->flags & CAMEL_HOOK_PAIR_REMOVED)) { - parent = (CamelHookPair *)&hooks->list; - pair = parent->next; - while (pair) { - if (pair->flags & CAMEL_HOOK_PAIR_REMOVED) { - parent->next = pair->next; - pair_free(pair); - hooks->list_length--; - } else { - parent = pair; - } - pair = parent->next; - } - hooks->flags &= ~CAMEL_HOOK_PAIR_REMOVED; - } - } - - camel_object_unget_hooks(obj); - camel_object_unref(obj); -} - -/* get/set arg methods */ -int camel_object_set(void *vo, CamelException *ex, ...) -{ - CamelArgV args; - CamelObject *o = vo; - CamelObjectClass *klass = o->klass; - int ret = 0; - - g_return_val_if_fail(CAMEL_IS_OBJECT(o), -1); - - camel_argv_start(&args, ex); - - while (camel_argv_build(&args) && ret == 0) - ret = klass->setv(o, ex, &args); - if (ret == 0) - ret = klass->setv(o, ex, &args); - - camel_argv_end(&args); - - return ret; -} - -int camel_object_setv(void *vo, CamelException *ex, CamelArgV *args) -{ - g_return_val_if_fail(CAMEL_IS_OBJECT(vo), -1); - - return ((CamelObject *)vo)->klass->setv(vo, ex, args); -} - -int camel_object_get(void *vo, CamelException *ex, ...) -{ - CamelObject *o = vo; - CamelArgGetV args; - CamelObjectClass *klass = o->klass; - int ret = 0; - - g_return_val_if_fail(CAMEL_IS_OBJECT(o), -1); - - camel_argv_start(&args, ex); - - while (camel_arggetv_build(&args) && ret == 0) - ret = klass->getv(o, ex, &args); - if (ret == 0) - ret = klass->getv(o, ex, &args); - - camel_argv_end(&args); - - return ret; -} - -int camel_object_getv(void *vo, CamelException *ex, CamelArgGetV *args) -{ - g_return_val_if_fail(CAMEL_IS_OBJECT(vo), -1); - - return ((CamelObject *)vo)->klass->getv(vo, ex, args); -} - -/* NB: If this doesn't return NULL, then you must unget_hooks when done */ -static CamelHookPair * -co_metadata_pair(CamelObject *obj, int create) -{ - CamelHookPair *pair; - CamelHookList *hooks; - - if (obj->hooks == NULL && !create) - return NULL; - - hooks = camel_object_get_hooks(obj); - pair = hooks->list; - while (pair) { - if (pair->name == meta_name) - return pair; - - pair = pair->next; - } - - if (create) { - pair = pair_alloc(); - pair->name = meta_name; - pair->data = NULL; - pair->flags = 0; - pair->func.filename = NULL; - pair->next = hooks->list; - hooks->list = pair; - hooks->list_length++; - } else { - camel_object_unget_hooks(obj); - } - - return pair; -} - -static CamelObjectMeta * -co_metadata_get(CamelObject *obj) -{ - CamelHookPair *pair; - CamelObjectMeta *meta = NULL, *metaout = NULL, *metalast; - - pair = co_metadata_pair(obj, FALSE); - if (pair) { - meta = pair->data; - - while (meta) { - CamelObjectMeta *m; - - m = g_malloc(sizeof(*metalast) + strlen(meta->name)); - m->next = NULL; - strcpy(m->name, meta->name); - m->value = g_strdup(meta->value); - if (metaout == NULL) - metalast = metaout = m; - else { - metalast->next = m; - metalast = m; - } - meta = meta->next; - } - - camel_object_unget_hooks(obj); - } - - return metaout; -} - -static void -co_metadata_free(CamelObject *obj, CamelObjectMeta *meta) -{ - while (meta) { - CamelObjectMeta *metan = meta->next; - - g_free(meta->value); - g_free(meta); - meta = metan; - } -} - -/** - * camel_object_meta_get: - * @vo: - * @name: - * - * Get a meta-data on an object. - * - * Return value: NULL if the meta-data is not set. - **/ -char * -camel_object_meta_get(void *vo, const char * name) -{ - CamelObject *obj = vo; - - g_return_val_if_fail(CAMEL_IS_OBJECT (obj), 0); - g_return_val_if_fail(name != NULL, 0); - - return obj->klass->meta_get(obj, name); -} - -/** - * camel_object_meta_set: - * @vo: - * @name: Name of meta-data. Should be prefixed with class of setter. - * @value: Value to set. If NULL, then the meta-data is removed. - * - * Set a meta-data item on an object. If the object supports persistent - * data, then the meta-data will be persistent across sessions. - * - * If the meta-data changes, is added, or removed, then a - * "meta_changed" event will be triggered with the name of the changed - * data. - * - * Return Value: TRUE if the setting caused a change to the object's - * metadata. - **/ -gboolean -camel_object_meta_set(void *vo, const char * name, const char *value) -{ - CamelObject *obj = vo; - - g_return_val_if_fail(CAMEL_IS_OBJECT (obj), FALSE); - g_return_val_if_fail(name != NULL, FALSE); - - if (obj->klass->meta_set(obj, name, value)) { - camel_object_trigger_event(obj, "meta_changed", (void *)name); - return TRUE; - } - - return FALSE; -} - -/** - * camel_object_state_read: - * @vo: - * - * Read persistent object state from object_set(CAMEL_OBJECT_STATE_FILE). - * - * Return value: -1 on error. - **/ -int camel_object_state_read(void *vo) -{ - CamelObject *obj = vo; - int res = -1; - char *file; - FILE *fp; - char magic[4]; - - camel_object_get(vo, NULL, CAMEL_OBJECT_STATE_FILE, &file, NULL); - if (file == NULL) - return 0; - - fp = fopen(file, "r"); - if (fp != NULL) { - if (fread(magic, 4, 1, fp) == 1 - && memcmp(magic, CAMEL_OBJECT_STATE_FILE_MAGIC, 4) == 0) - res = obj->klass->state_read(obj, fp); - else - res = -1; - fclose(fp); - } - - camel_object_free(vo, CAMEL_OBJECT_STATE_FILE, file); - - return res; -} - -/** - * camel_object_state_write: - * @vo: - * - * Write persistent state to the file as set by object_set(CAMEL_OBJECT_STATE_FILE). - * - * Return value: -1 on error. - **/ -int camel_object_state_write(void *vo) -{ - CamelObject *obj = vo; - int res = -1; - char *file, *savename; - FILE *fp; - - camel_object_get(vo, NULL, CAMEL_OBJECT_STATE_FILE, &file, NULL); - if (file == NULL) - return 0; - - savename = camel_file_util_savename(file); - fp = fopen(savename, "w"); - if (fp != NULL) { - if (fwrite(CAMEL_OBJECT_STATE_FILE_MAGIC, 4, 1, fp) == 1 - && obj->klass->state_write(obj, fp) == 0) { - if (fclose(fp) == 0) { - res = 0; - rename(savename, file); - } - } else { - fclose(fp); - } - } - - g_free(savename); - camel_object_free(vo, CAMEL_OBJECT_STATE_FILE, file); - - return res; -} - -/* free an arg object, you can only free objects 1 at a time */ -void camel_object_free(void *vo, guint32 tag, void *value) -{ - g_return_if_fail(CAMEL_IS_OBJECT(vo)); - - /* We could also handle freeing of args differently - - Add a 'const' bit to the arg type field, - specifying that the object should not be freed. - - And, add free handlers for any pointer objects which are - not const. The free handlers would be hookpairs off of the - class. - - Then we handle the basic types OBJ,STR here, and pass PTR - types to their appropriate handler, without having to pass - through the invocation heirarchy of the free method. - - This would also let us copy and do other things with args - we can't do, but i can't see a use for that yet ... */ - - ((CamelObject *)vo)->klass->free(vo, tag, value); -} - -static void -object_class_dump_tree_rec(CamelType root, int depth) -{ - char *p; -#ifdef CAMEL_OBJECT_TRACK_INSTANCES - struct _CamelObject *o; -#endif - - p = alloca(depth*2+1); - memset(p, ' ', depth*2); - p[depth*2] = 0; - - while (root) { - CLASS_LOCK(root); - printf("%sClass: %s\n", p, root->name); - /*printf("%sVersion: %u.%u\n", p, root->version, root->revision);*/ - if (root->hooks) { - CamelHookPair *pair = root->hooks; - - while (pair) { - printf("%s event '%s' prep %p\n", p, pair->name, pair->func.prep); - pair = pair->next; - } - } -#ifdef CAMEL_OBJECT_TRACK_INSTANCES - o = root->instances; - while (o) { - printf("%s instance %p [%d]\n", p, o, o->ref_count); - /* todo: should lock hooks while it scans them */ - if (o->hooks) { - CamelHookPair *pair = o->hooks->list; - - while (pair) { - printf("%s hook '%s' func %p data %p\n", p, pair->name, pair->func.event, pair->data); - pair = pair->next; - } - } - o = o->next; - } -#endif - CLASS_UNLOCK(root); - - if (root->child) - object_class_dump_tree_rec(root->child, depth+1); - - root = root->next; - } -} - -void -camel_object_class_dump_tree(CamelType root) -{ - object_class_dump_tree_rec(root, 0); -} - -CamelObjectBag * -camel_object_bag_new (GHashFunc hash, GEqualFunc equal, CamelCopyFunc keycopy, GFreeFunc keyfree) -{ - CamelObjectBag *bag; - - bag = g_malloc(sizeof(*bag)); - bag->object_table = g_hash_table_new(hash, equal); - bag->copy_key = keycopy; - bag->free_key = keyfree; - bag->key_table = g_hash_table_new(NULL, NULL); - bag->owner = 0; - - /* init the semaphore to 1 owner, this is who has reserved the bag for adding */ - sem_init(&bag->reserve_sem, 0, 1); - - return bag; -} - -static void -save_object(void *key, CamelObject *o, GPtrArray *objects) -{ - g_ptr_array_add(objects, o); -} - -void -camel_object_bag_destroy (CamelObjectBag *bag) -{ - GPtrArray *objects = g_ptr_array_new(); - int i; - - sem_getvalue(&bag->reserve_sem, &i); - g_assert(i == 1); - - g_hash_table_foreach(bag->object_table, (GHFunc)save_object, objects); - for (i=0;i<objects->len;i++) - camel_object_bag_remove(bag, objects->pdata[i]); - - g_ptr_array_free(objects, TRUE); - g_hash_table_destroy(bag->object_table); - g_hash_table_destroy(bag->key_table); - sem_destroy(&bag->reserve_sem); - g_free(bag); -} - -void -camel_object_bag_add (CamelObjectBag *bag, const void *key, void *vo) -{ - CamelObject *o = vo; - CamelHookList *hooks; - CamelHookPair *pair; - void *k; - - hooks = camel_object_get_hooks(o); - E_LOCK(type_lock); - - pair = hooks->list; - while (pair) { - if (pair->name == bag_name && pair->data == bag) { - E_UNLOCK(type_lock); - camel_object_unget_hooks(o); - return; - } - pair = pair->next; - } - - pair = pair_alloc(); - pair->name = bag_name; - pair->data = bag; - pair->flags = 0; - - pair->next = hooks->list; - hooks->list = pair; - hooks->list_length++; - - k = bag->copy_key(key); - g_hash_table_insert(bag->object_table, k, vo); - g_hash_table_insert(bag->key_table, vo, k); - - if (bag->owner == pthread_self()) { - bag->owner = 0; - sem_post(&bag->reserve_sem); - } - - E_UNLOCK(type_lock); - camel_object_unget_hooks(o); -} - -void * -camel_object_bag_get (CamelObjectBag *bag, const void *key) -{ - CamelObject *o; - - E_LOCK(type_lock); - - o = g_hash_table_lookup(bag->object_table, key); - if (o) { - /* we use the same lock as the refcount */ - o->ref_count++; - } else if (bag->owner != pthread_self()) { - E_UNLOCK(type_lock); - sem_wait(&bag->reserve_sem); - E_LOCK(type_lock); - /* re-check if it slipped in */ - o = g_hash_table_lookup(bag->object_table, key); - if (o) - o->ref_count++; - /* we dont want to reserve the bag */ - sem_post(&bag->reserve_sem); - } - - E_UNLOCK(type_lock); - - return o; -} - -/* like get, but also reserve a spot for key if it isn't there */ -/* After calling reserve, you MUST call bag_abort or bag_add */ -/* Also note that currently you can only reserve a single key - at any one time in a given thread */ -void * -camel_object_bag_reserve (CamelObjectBag *bag, const void *key) -{ - CamelObject *o; - - E_LOCK(type_lock); - - o = g_hash_table_lookup(bag->object_table, key); - if (o) { - o->ref_count++; - } else { - g_assert(bag->owner != pthread_self()); - E_UNLOCK(type_lock); - sem_wait(&bag->reserve_sem); - E_LOCK(type_lock); - /* incase its slipped in while we were waiting */ - o = g_hash_table_lookup(bag->object_table, key); - if (o) { - o->ref_count++; - /* in which case we dont need to reserve the bag either */ - sem_post(&bag->reserve_sem); - } else { - bag->owner = pthread_self(); - } - } - - E_UNLOCK(type_lock); - - return o; -} - -/* abort a reserved key */ -void -camel_object_bag_abort (CamelObjectBag *bag, const void *key) -{ - g_assert(bag->owner == pthread_self()); - - bag->owner = 0; - sem_post(&bag->reserve_sem); -} - -static void -save_bag(void *key, CamelObject *o, GPtrArray *list) -{ - /* we have the refcount lock already */ - o->ref_count++; - g_ptr_array_add(list, o); -} - -/* get a list of all objects in the bag, ref'd - ignores any reserved keys */ -GPtrArray * -camel_object_bag_list (CamelObjectBag *bag) -{ - GPtrArray *list; - - list = g_ptr_array_new(); - - E_LOCK(type_lock); - g_hash_table_foreach(bag->object_table, (GHFunc)save_bag, list); - E_UNLOCK(type_lock); - - return list; -} - -/* if bag is NULL, remove all bags from object */ -static void -camel_object_bag_remove_unlocked (CamelObjectBag *inbag, CamelObject *o, CamelHookList *hooks) -{ - CamelHookPair *pair, *parent; - void *oldkey; - CamelObjectBag *bag; - - parent = (CamelHookPair *)&hooks->list; - pair = parent->next; - while (pair) { - if (pair->name == bag_name - && (inbag == NULL || inbag == pair->data)) { - bag = pair->data; - /* lookup object in table? */ - oldkey = g_hash_table_lookup(bag->key_table, o); - if (oldkey) { - g_hash_table_remove(bag->key_table, o); - g_hash_table_remove(bag->object_table, oldkey); - bag->free_key(oldkey); - } - parent->next = pair->next; - pair_free(pair); - hooks->list_length--; - } else { - parent = pair; - } - pair = parent->next; - } -} - -void -camel_object_bag_remove (CamelObjectBag *inbag, void *vo) -{ - CamelObject *o = vo; - CamelHookList *hooks; - - if (o->hooks == NULL) - return; - - hooks = camel_object_get_hooks(o); - E_LOCK(type_lock); - - camel_object_bag_remove_unlocked(inbag, o, hooks); - - E_UNLOCK(type_lock); - camel_object_unget_hooks(o); -} |