/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. 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, see . */ #include "belle_sip_internal.h" static void _belle_sip_object_pool_remove_from_stack(belle_sip_object_pool_t *pool); static int _belle_sip_object_marshal_check_enabled = FALSE; static int has_type(belle_sip_object_t *obj, belle_sip_type_id_t id){ belle_sip_object_vptr_t *vptr=obj->vptr; while(vptr!=NULL){ if (vptr->id==id) return TRUE; vptr=vptr->get_parent(); } return FALSE; } int _belle_sip_object_is_instance_of(belle_sip_object_t * obj,belle_sip_type_id_t id) { return has_type(obj,id); } void belle_sip_object_enable_marshal_check(int enable) { _belle_sip_object_marshal_check_enabled = (enable) ? TRUE : FALSE; } static belle_sip_list_t *all_objects=NULL; static int belle_sip_leak_detector_enabled=FALSE; static int belle_sip_leak_detector_inhibited=FALSE; static void add_new_object(belle_sip_object_t *obj){ if (belle_sip_leak_detector_enabled && !belle_sip_leak_detector_inhibited){ all_objects=belle_sip_list_prepend(all_objects,obj); } } static void remove_free_object(belle_sip_object_t *obj){ if (belle_sip_leak_detector_enabled && !belle_sip_leak_detector_inhibited){ belle_sip_list_t* it; it=belle_sip_list_find(all_objects,obj); if (it) all_objects = belle_sip_list_delete_link(all_objects,it); /*it may fail if the leak detector was inhibitted at the time the object was created*/ } } void belle_sip_object_inhibit_leak_detector(int yes){ belle_sip_leak_detector_inhibited=yes ? TRUE : FALSE; } void belle_sip_object_enable_leak_detector(int enable){ belle_sip_leak_detector_enabled=enable; } int belle_sip_object_get_object_count(void){ return (int)belle_sip_list_size(all_objects); } void belle_sip_object_flush_active_objects(void){ //do not free objects so that they are still detected as leaked by valgrind and such all_objects = belle_sip_list_free(all_objects); } void belle_sip_object_dump_active_objects(void){ belle_sip_list_t *elem; if (all_objects){ belle_sip_warning("List of leaked objects:"); for(elem=all_objects;elem!=NULL;elem=elem->next){ belle_sip_object_t *obj=(belle_sip_object_t*)elem->data; char* content= belle_sip_object_to_string(obj); belle_sip_warning("%s(%p) ref=%i, content [%10s...]",obj->vptr->type_name,obj,obj->ref,content); belle_sip_free(content); } }else belle_sip_warning("No objects leaked."); } belle_sip_object_t * _belle_sip_object_new(size_t objsize, belle_sip_object_vptr_t *vptr){ belle_sip_object_t *obj=(belle_sip_object_t *)belle_sip_malloc0(vptr->size); obj->vptr = vptr; obj->ref = vptr->initially_unowned ? 0 : 1; if (obj->ref == 0) { belle_sip_object_pool_t *pool=belle_sip_object_pool_get_current(); if (pool) belle_sip_object_pool_add(pool,obj); } add_new_object(obj); return obj; } int belle_sip_object_is_initially_unowned(const belle_sip_object_t *obj){ return obj->vptr->initially_unowned; } belle_sip_object_t * belle_sip_object_ref(void *obj){ belle_sip_object_t *o=BELLE_SIP_OBJECT(obj); if (o->ref == 0 && o->pool) { belle_sip_object_pool_remove(o->pool,obj); } if (o->vptr->on_first_ref && (o->ref == 0 || (!o->vptr->initially_unowned && o->ref == 1))) o->vptr->on_first_ref(o); o->ref++; return obj; } void belle_sip_object_unref(void *ptr){ belle_sip_object_t *obj=BELLE_SIP_OBJECT(ptr); if (obj->ref <= -1) { belle_sip_error("Object [%p] freed twice or corrupted !",obj); if (obj->vptr && obj->vptr->type_name) belle_sip_error("Object type might be [%s]",obj->vptr->type_name); if (obj->name) belle_sip_error("Object name might be [%s]",obj->name); belle_sip_fatal("Fatal object error encountered, aborting."); return; } if (obj->vptr->initially_unowned && obj->ref==0){ if (obj->pool) belle_sip_object_pool_remove(obj->pool,obj); obj->ref=-1; belle_sip_object_delete(obj); return; } obj->ref--; if (obj->vptr->on_last_ref){ if ((obj->vptr->initially_unowned && obj->ref==0) || (!obj->vptr->initially_unowned && obj->ref == 1)){ obj->vptr->on_last_ref(obj); } } if (obj->ref == 0){ obj->ref = -1; belle_sip_object_delete(obj); } } static weak_ref_t *weak_ref_new(belle_sip_object_destroy_notify_t destroy_notify, void *userpointer){ weak_ref_t *r=belle_sip_new(weak_ref_t); r->next=NULL; r->notify=destroy_notify; r->userpointer=userpointer; return r; } belle_sip_object_t *belle_sip_object_weak_ref(void *obj, belle_sip_object_destroy_notify_t destroy_notify, void *userpointer){ belle_sip_object_t *o=BELLE_SIP_OBJECT(obj); weak_ref_t *old=o->weak_refs; o->weak_refs=weak_ref_new(destroy_notify,userpointer); o->weak_refs->next=old; return o; } void belle_sip_object_weak_unref(void *obj, belle_sip_object_destroy_notify_t destroy_notify, void *userpointer){ belle_sip_object_t *o=BELLE_SIP_OBJECT(obj); weak_ref_t *ref,*prevref=NULL,*next=NULL; int found=FALSE; if (o->ref==-1) return; /*too late and avoid recursions*/ for(ref=o->weak_refs;ref!=NULL;ref=next){ next=ref->next; if (ref->notify==destroy_notify && ref->userpointer==userpointer){ if (prevref==NULL) o->weak_refs=next; else prevref->next=next; belle_sip_free(ref); found=TRUE; /*do not break or return, someone could have put twice the same weak ref on the same object*/ }else{ prevref=ref; } } if (!found) belle_sip_fatal("Could not find weak_ref, you're a looser."); } static void belle_sip_object_loose_weak_refs(belle_sip_object_t *obj){ weak_ref_t *ref,*next; for(ref=obj->weak_refs;ref!=NULL;ref=next){ next=ref->next; ref->notify(ref->userpointer,obj); belle_sip_free(ref); } obj->weak_refs=NULL; } static void _belle_sip_object_uninit(belle_sip_object_t *obj){ if (obj->name) belle_sip_free(obj->name); } static void _belle_sip_object_clone(belle_sip_object_t *obj, const belle_sip_object_t *orig){ if (orig->name!=NULL) obj->name=belle_sip_strdup(orig->name); } static belle_sip_error_code _belle_object_marshal(belle_sip_object_t* obj, char* buff, size_t buff_size, size_t *offset) { return belle_sip_snprintf(buff,buff_size,offset,"{%s::%s %p}",obj->vptr->type_name,obj->name ? obj->name : "(no name)",obj); } static belle_sip_object_vptr_t *no_parent(void){ return NULL; } belle_sip_object_vptr_t belle_sip_object_t_vptr={ BELLE_SIP_TYPE_ID(belle_sip_object_t), sizeof(belle_sip_object_t), "belle_sip_object_t", FALSE, no_parent, /*no parent, it's god*/ NULL, _belle_sip_object_uninit, _belle_sip_object_clone, _belle_object_marshal, BELLE_SIP_DEFAULT_BUFSIZE_HINT }; belle_sip_object_vptr_t *belle_sip_object_t_vptr_get(void){ return &belle_sip_object_t_vptr; } void belle_sip_object_delete(void *ptr){ belle_sip_object_t *obj=BELLE_SIP_OBJECT(ptr); belle_sip_object_vptr_t *vptr; belle_sip_object_loose_weak_refs(obj); remove_free_object(obj); vptr=obj->vptr; while(vptr!=NULL){ if (vptr->destroy) vptr->destroy(obj); vptr=vptr->get_parent(); } belle_sip_object_data_clear(obj); belle_sip_free(obj); } static belle_sip_object_vptr_t *find_common_floor(belle_sip_object_vptr_t *vptr1, belle_sip_object_vptr_t *vptr2){ belle_sip_object_vptr_t *it1,*it2; for (it1=vptr1;it1!=NULL;it1=it1->get_parent()){ if (it1==vptr2) return vptr2; } for(it2=vptr2;it2!=NULL;it2=it2->get_parent()){ if (vptr1==it2) return vptr1; } return find_common_floor(vptr1->get_parent(),vptr2); } /*copy the content of ref object to new object, for the part they have in common in their inheritence diagram*/ void _belle_sip_object_copy(belle_sip_object_t *newobj, const belle_sip_object_t *ref){ belle_sip_object_vptr_t *vptr; vptr=find_common_floor(newobj->vptr,ref->vptr); if (vptr==NULL){ belle_sip_fatal("Should not happen"); } while(vptr!=NULL){ if (vptr->clone==NULL){ belle_sip_fatal("Object of type %s cannot be cloned, it does not provide a clone() implementation.",vptr->type_name); return; }else vptr->clone(newobj,ref); vptr=vptr->get_parent(); } } belle_sip_object_t *belle_sip_object_clone(const belle_sip_object_t *obj){ belle_sip_object_t *newobj; newobj=belle_sip_malloc0(obj->vptr->size); newobj->ref=obj->vptr->initially_unowned ? 0 : 1; newobj->vptr=obj->vptr; _belle_sip_object_copy(newobj,obj); if (newobj->ref==0){ belle_sip_object_pool_t *pool=belle_sip_object_pool_get_current(); if (pool) belle_sip_object_pool_add(pool,newobj); } add_new_object(newobj); return newobj; } belle_sip_object_t *belle_sip_object_clone_and_ref(const belle_sip_object_t *obj) { return belle_sip_object_ref(belle_sip_object_clone(obj)); } struct belle_sip_object_data{ char* name; void* data; belle_sip_data_destroy destroy_func; }; static int belle_sip_object_data_find(const void* a, const void* b) { struct belle_sip_object_data* da = (struct belle_sip_object_data*)a; return strcmp(da->name, (const char*)b); } static void belle_sip_object_data_destroy(void* data) { struct belle_sip_object_data* da = (struct belle_sip_object_data*)data; if (da->destroy_func) da->destroy_func(da->data); belle_sip_free(da->name); belle_sip_free(da); } int belle_sip_object_data_set( belle_sip_object_t *obj, const char* name, void* data, belle_sip_data_destroy destroy_func ) { int ret = 0; belle_sip_list_t* list_entry = belle_sip_list_find_custom(obj->data_store, belle_sip_object_data_find, name); struct belle_sip_object_data* entry = (list_entry)? list_entry->data : NULL; if( entry == NULL){ entry = belle_sip_malloc0(sizeof( struct belle_sip_object_data)); obj->data_store = belle_sip_list_append(obj->data_store, entry); } else { // clean previous data if( entry->destroy_func ) entry->destroy_func(entry->data); belle_sip_free(entry->name); ret = 1; } if( entry ){ entry->data = data; entry->name = belle_sip_strdup(name); entry->destroy_func = destroy_func; } else { ret = -1; } return ret; } void* belle_sip_object_data_get( belle_sip_object_t *obj, const char* name ) { belle_sip_list_t *list_entry = belle_sip_list_find_custom(obj->data_store, belle_sip_object_data_find, name); struct belle_sip_object_data* entry = (list_entry)? list_entry->data : NULL; return entry? entry->data : NULL; } int belle_sip_object_data_remove( belle_sip_object_t *obj, const char* name) { belle_sip_list_t *list_entry = belle_sip_list_find_custom(obj->data_store, belle_sip_object_data_find, name); struct belle_sip_object_data* entry = (list_entry)? list_entry->data : NULL; if( entry ){ belle_sip_free(entry->name); if( entry->destroy_func ) entry->destroy_func(entry->data); belle_sip_free(entry); } if( list_entry ) obj->data_store = belle_sip_list_remove_link(obj->data_store, list_entry); return !(list_entry!= NULL); } int belle_sip_object_data_exists( const belle_sip_object_t *obj, const char* name ) { return (belle_sip_list_find_custom(obj->data_store, belle_sip_object_data_find, name) != NULL); } void* belle_sip_object_data_grab( belle_sip_object_t* obj, const char* name) { belle_sip_list_t *list_entry = belle_sip_list_find_custom(obj->data_store, belle_sip_object_data_find, name); struct belle_sip_object_data* entry = (list_entry)? list_entry->data : NULL; void* data =NULL; if( entry ){ belle_sip_free(entry->name); data = entry->data; } obj->data_store = belle_sip_list_remove_link(obj->data_store, list_entry); belle_sip_free(entry); return data; } void belle_sip_object_data_clear( belle_sip_object_t* obj ) { belle_sip_list_for_each(obj->data_store, belle_sip_object_data_destroy); obj->data_store = belle_sip_list_free(obj->data_store); } void belle_sip_object_data_clone( const belle_sip_object_t* src, belle_sip_object_t* dst, belle_sip_data_clone clone_func) { belle_sip_object_data_clear(dst); belle_sip_object_data_merge(src, dst, clone_func); } void belle_sip_object_data_merge( const belle_sip_object_t* src, belle_sip_object_t* dst, belle_sip_data_clone clone_func) { belle_sip_list_t *list = src->data_store; struct belle_sip_object_data* it = NULL; void* cloned_data = NULL; while( list ){ it = list->data; if( it ){ cloned_data = (clone_func)? clone_func( it->name, it->data ) : it->data; belle_sip_object_data_set(dst, it->name, cloned_data, it->destroy_func); } list = list->next; } } struct belle_sip_object_foreach_data { void (*apply_func)(const char*, void*, void*); void* userdata; }; static void belle_sip_object_for_each_cb(void* data, void* pvdata) { struct belle_sip_object_data* it = (struct belle_sip_object_data*)data; struct belle_sip_object_foreach_data* fd = (struct belle_sip_object_foreach_data*)pvdata; if( it && fd->apply_func ){ fd->apply_func(it->name, it->data, fd->userdata); } } void belle_sip_object_data_foreach( const belle_sip_object_t* obj, void (*apply_func)(const char* key, void* data, void* userdata), void* userdata) { struct belle_sip_object_foreach_data fd = { apply_func, userdata }; belle_sip_list_for_each2(obj->data_store, belle_sip_object_for_each_cb, &fd); } void *belle_sip_object_cast(belle_sip_object_t *obj, belle_sip_type_id_t id, const char *castname, const char *file, int fileno){ if (obj!=NULL){ if (has_type(obj,id)==0){ belle_sip_fatal("Bad cast to %s at %s:%i",castname,file,fileno); return NULL; } } return obj; } void *belle_sip_object_get_interface_methods(belle_sip_object_t *obj, belle_sip_interface_id_t ifid){ if (obj!=NULL){ belle_sip_object_vptr_t *vptr; for (vptr=obj->vptr;vptr!=NULL;vptr=vptr->get_parent()){ belle_sip_interface_desc_t **ifaces=vptr->interfaces; if (ifaces!=NULL){ for(;*ifaces!=0;++ifaces){ if ((*ifaces)->id==ifid){ return *ifaces; } } } } } return NULL; } int belle_sip_object_implements(belle_sip_object_t *obj, belle_sip_interface_id_t id){ return belle_sip_object_get_interface_methods(obj,id)!=NULL; } void *belle_sip_object_interface_cast(belle_sip_object_t *obj, belle_sip_interface_id_t ifid, const char *castname, const char *file, int fileno){ if (obj!=NULL){ if (belle_sip_object_get_interface_methods(obj,ifid)==0){ belle_sip_fatal("Bad cast to interface %s at %s:%i",castname,file,fileno); return NULL; } } return obj; } void belle_sip_object_set_name(belle_sip_object_t* object,const char* name) { if (object->name) { belle_sip_free(object->name); object->name=NULL; } if (name) object->name=belle_sip_strdup(name); } const char* belle_sip_object_get_name(belle_sip_object_t* object) { return object->name; } static belle_sip_error_code checked_marshal(belle_sip_object_vptr_t *vptr, belle_sip_object_t* obj, char* buff, size_t buff_size, size_t *offset){ size_t tmp_buf_size=buff_size*2; char *p=(char*)belle_sip_malloc0(tmp_buf_size); size_t i; size_t initial_offset=*offset; belle_sip_error_code error=vptr->marshal(obj,p,buff_size,offset); size_t written; for (i=initial_offset;itype_name,(int)written,(int)(*offset-initial_offset)); } memcpy(buff+initial_offset,p+initial_offset,*offset-initial_offset); }else if (error==BELLE_SIP_BUFFER_OVERFLOW){ /* Case where the object aborted the marshalling because of not enough room. * Should this happen, it is not allowed to write past buffer end anyway */ if (written > buff_size){ belle_sip_fatal("Object of type %s commited a buffer overflow by marshalling %i bytes", vptr->type_name,(int)(*offset-initial_offset)); } }else{ belle_sip_error("Object of type %s produced an error during marshalling: %i", vptr->type_name,error); } belle_sip_free(p); return error; } belle_sip_error_code belle_sip_object_marshal(belle_sip_object_t* obj, char* buff, size_t buff_size, size_t *offset) { belle_sip_object_vptr_t *vptr=obj->vptr; while (vptr != NULL) { if (vptr->marshal != NULL) { if (_belle_sip_object_marshal_check_enabled == TRUE) return checked_marshal(vptr,obj,buff,buff_size,offset); else return vptr->marshal(obj,buff,buff_size,offset); } else { vptr=vptr->get_parent(); } } return BELLE_SIP_NOT_IMPLEMENTED; /*no implementation found*/ } static char * belle_sip_object_to_alloc_string(belle_sip_object_t *obj, int size_hint){ char *buf=belle_sip_malloc(size_hint); size_t offset=0; belle_sip_error_code error = belle_sip_object_marshal(obj,buf,size_hint-1,&offset); obj->vptr->tostring_bufsize_hint=size_hint; if (error==BELLE_SIP_BUFFER_OVERFLOW){ belle_sip_message("belle_sip_object_to_alloc_string(): hint buffer was too short while doing to_string() for %s, retrying", obj->vptr->type_name); belle_sip_free(buf); return belle_sip_object_to_alloc_string(obj,2*size_hint); } buf=belle_sip_realloc(buf,offset+1); buf[offset]='\0'; return buf; } static int get_hint_size(int size){ if (size<128) return 128; return size; } char* belle_sip_object_to_string(void* _obj) { belle_sip_object_t *obj=BELLE_SIP_OBJECT(_obj); if (obj->vptr->tostring_bufsize_hint!=0){ return belle_sip_object_to_alloc_string(obj,obj->vptr->tostring_bufsize_hint); }else{ char buff[BELLE_SIP_MAX_TO_STRING_SIZE]; size_t offset=0; belle_sip_error_code error = belle_sip_object_marshal(obj,buff,sizeof(buff)-1,&offset); if (error==BELLE_SIP_BUFFER_OVERFLOW){ belle_sip_message("belle_sip_object_to_string(): temporary buffer is too short while doing to_string() for %s, retrying", obj->vptr->type_name); return belle_sip_object_to_alloc_string(obj,get_hint_size(2*(int)offset)); } buff[offset]='\0'; obj->vptr->tostring_bufsize_hint=get_hint_size(2*(int)offset); return belle_sip_strdup(buff); } } char * _belle_sip_object_describe_type(belle_sip_object_vptr_t *vptr){ const int maxbufsize=2048; char *ret=belle_sip_malloc(maxbufsize); belle_sip_object_vptr_t *it; size_t pos=0; belle_sip_list_t *l=NULL,*elem; belle_sip_snprintf(ret,maxbufsize,&pos,"Ownership:\n"); belle_sip_snprintf(ret,maxbufsize,&pos,"\t%s is created initially %s\n",vptr->type_name, vptr->initially_unowned ? "unowned" : "owned"); belle_sip_snprintf(ret,maxbufsize,&pos,"\nInheritance diagram:\n"); for(it=vptr;it!=NULL;it=it->get_parent()){ l=belle_sip_list_prepend(l,it); } for(elem=l;elem!=NULL;elem=elem->next){ it=(belle_sip_object_vptr_t*)elem->data; belle_sip_snprintf(ret,maxbufsize,&pos,"\t%s\n",it->type_name); if (elem->next) belle_sip_snprintf(ret,maxbufsize,&pos,"\t |\n"); } belle_sip_list_free(l); belle_sip_snprintf(ret,maxbufsize,&pos,"\nImplemented interfaces:\n"); for(it=vptr;it!=NULL;it=it->get_parent()){ belle_sip_interface_desc_t **desc=it->interfaces; if (desc!=NULL){ for(;*desc!=NULL;desc++){ belle_sip_snprintf(ret,maxbufsize,&pos,"\t* %s\n",(*desc)->ifname); } } } return ret; } char *belle_sip_object_describe(void *obj){ belle_sip_object_t *o=BELLE_SIP_OBJECT(obj); return _belle_sip_object_describe_type(o->vptr); } #if !defined(_WIN32) #include char *belle_sip_object_describe_type_from_name(const char *name){ char *vptr_name; void *handle; void *symbol; belle_sip_object_get_vptr_t vptr_getter; handle=dlopen(NULL,RTLD_LAZY); if (handle==NULL){ belle_sip_error("belle_sip_object_describe_type_from_name: dlopen() failed: %s",dlerror()); return NULL; } vptr_name=belle_sip_strdup_printf("%s_vptr_get",name); symbol=dlsym(handle,vptr_name); belle_sip_free(vptr_name); dlclose(handle); if (symbol==NULL){ belle_sip_error("belle_sip_object_describe_type_from_name: could not find vptr for type %s",name); return NULL; } vptr_getter=(belle_sip_object_get_vptr_t)symbol; return _belle_sip_object_describe_type(vptr_getter()); } #else char *belle_sip_object_describe_type_from_name(const char *name){ return belle_sip_strdup_printf("Sorry belle_sip_object_describe_type_from_name() is not implemented on this platform."); } #endif struct belle_sip_object_pool{ belle_sip_object_t base; belle_sip_list_t *objects; unsigned long thread_id; }; static void belle_sip_object_pool_destroy(belle_sip_object_pool_t *pool){ belle_sip_object_pool_clean(pool); _belle_sip_object_pool_remove_from_stack(pool); } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_object_pool_t); BELLE_SIP_INSTANCIATE_VPTR(belle_sip_object_pool_t,belle_sip_object_t,belle_sip_object_pool_destroy,NULL,NULL,FALSE); belle_sip_object_pool_t *belle_sip_object_pool_new(void){ belle_sip_object_pool_t *pool=belle_sip_object_new(belle_sip_object_pool_t); pool->thread_id=belle_sip_thread_self_id(); return pool; } void belle_sip_object_pool_add(belle_sip_object_pool_t *pool, belle_sip_object_t *obj){ if (obj->pool!=NULL){ belle_sip_fatal("It is not possible to add an object to multiple pools."); } pool->objects=belle_sip_list_prepend(pool->objects,obj); obj->pool_iterator=pool->objects; obj->pool=pool; } void belle_sip_object_pool_remove(belle_sip_object_pool_t *pool, belle_sip_object_t *obj){ unsigned long tid=belle_sip_thread_self_id(); if (obj->pool!=pool){ belle_sip_fatal("Attempting to remove object from an incorrect pool: obj->pool=%p, pool=%p",obj->pool,pool); return; } if (tid!=pool->thread_id){ belle_sip_fatal("It is forbidden (and unsafe()) to ref()/unref() an unowned object outside of the thread that created it."); return; } pool->objects=belle_sip_list_delete_link(pool->objects,obj->pool_iterator); obj->pool_iterator=NULL; obj->pool=NULL; } int belle_sip_object_pool_cleanable(belle_sip_object_pool_t *pool){ return pool->thread_id!=0 && belle_sip_thread_self_id()==pool->thread_id; } void belle_sip_object_pool_clean(belle_sip_object_pool_t *pool){ belle_sip_list_t *elem,*next; if (!belle_sip_object_pool_cleanable(pool)){ belle_sip_warning("Thread pool [%p] cannot be cleaned from thread [%lu] because it was created for thread [%lu]", pool,belle_sip_thread_self_id(),(unsigned long)pool->thread_id); return; } for(elem=pool->objects;elem!=NULL;elem=next){ belle_sip_object_t *obj=(belle_sip_object_t*)elem->data; if (obj->ref==0){ belle_sip_message("Garbage collecting unowned object of type %s",obj->vptr->type_name); obj->ref=-1; belle_sip_object_delete(obj); next=elem->next; belle_sip_free(elem); }else { belle_sip_fatal("Object %p is in unowned list but with ref count %i, bug.",obj,obj->ref); next=elem->next; } } pool->objects=NULL; } static void belle_sip_object_pool_detach_from_thread(belle_sip_object_pool_t *pool){ belle_sip_object_pool_clean(pool); pool->thread_id=(unsigned long)0; } static void cleanup_pool_stack(void *data){ belle_sip_list_t **pool_stack=(belle_sip_list_t**)data; if (*pool_stack){ /* * We would expect the pool_stack to be empty when the thread terminates. * Otherwise that means the management of object pool is not properly done by the application. * Since the object pools might be still referenced by the application, we can't destroy them. * Instead, we mark them as detached, so that when the thread that will attempt to destroy them will do it, * we'll accept (since anyway these object pool are no longer needed. */ belle_sip_warning("There were still [%u] object pools for thread [%lu] while the thread exited. ", (unsigned int)belle_sip_list_size(*pool_stack),belle_sip_thread_self_id()); belle_sip_list_free_with_data(*pool_stack,(void (*)(void*)) belle_sip_object_pool_detach_from_thread); } *pool_stack=NULL; belle_sip_free(pool_stack); } static belle_sip_list_t** get_current_pool_stack(int *first_time){ static belle_sip_thread_key_t pools_key; static int pools_key_created=0; belle_sip_list_t **pool_stack; if (first_time) *first_time=0; if (!pools_key_created){ pools_key_created=1; if (belle_sip_thread_key_create(&pools_key, cleanup_pool_stack)!=0){ return NULL; } } pool_stack=(belle_sip_list_t**)belle_sip_thread_getspecific(pools_key); if (pool_stack==NULL){ pool_stack=belle_sip_new(belle_sip_list_t*); *pool_stack=NULL; belle_sip_thread_setspecific(pools_key,pool_stack); if (first_time) *first_time=1; } return pool_stack; } static void _belle_sip_object_pool_remove_from_stack(belle_sip_object_pool_t *pool){ belle_sip_list_t **pools=get_current_pool_stack(NULL); unsigned long tid=belle_sip_thread_self_id(); if (tid!=pool->thread_id){ belle_sip_fatal("It is forbidden to destroy a pool outside the thread that created it."); return; } if (pools==NULL) { belle_sip_fatal("Not possible to pop a pool."); return; } if (*pools==NULL){ belle_sip_fatal("There is no current pool in stack."); return; } *pools=belle_sip_list_remove(*pools,pool); } belle_sip_object_pool_t * belle_sip_object_pool_push(void){ belle_sip_list_t **pools=get_current_pool_stack(NULL); belle_sip_object_pool_t *pool; if (pools==NULL) { belle_sip_error("Not possible to create a pool."); return NULL; } pool=belle_sip_object_pool_new(); *pools=belle_sip_list_prepend(*pools,pool); return pool; } belle_sip_object_pool_t *belle_sip_object_pool_get_current(void){ int first_time; belle_sip_list_t **pools=get_current_pool_stack(&first_time); if (pools==NULL) return NULL; if (*pools==NULL ){ if (first_time) { belle_sip_warning("There is no object pool created in thread [%lu]. " "Use belle_sip_object_pool_push() to create one. Unowned objects not unref'd will be leaked.", belle_sip_thread_self_id()); } return NULL; } return (belle_sip_object_pool_t*)(*pools)->data; }