Commit c620c945 authored by Simon Morlat's avatar Simon Morlat

implement thread-safe garbage collection of unowned objects.

parent e94fb368
......@@ -102,7 +102,8 @@ BELLE_SIP_DECLARE_TYPES_BEGIN(belle_sip,1)
BELLE_SIP_TYPE_ID(belle_sip_header_refer_to_t),
BELLE_SIP_TYPE_ID(belle_sip_header_referred_by_t),
BELLE_SIP_TYPE_ID(belle_sip_header_replaces_t),
BELLE_SIP_TYPE_ID(belle_sip_hop_t)
BELLE_SIP_TYPE_ID(belle_sip_hop_t),
BELLE_SIP_TYPE_ID(belle_sip_object_pool_t)
BELLE_SIP_DECLARE_TYPES_END
......
......@@ -134,6 +134,8 @@ struct _belle_sip_object{
int ref;
char* name;
struct weak_ref *weak_refs;
struct belle_sip_object_pool *pool;
struct _belle_sip_list *pool_iterator;
};
......@@ -305,7 +307,14 @@ typedef struct belle_sip_interface_desc{
}
/**
* Object holding unowned objects - used as a kind of garbage collector for temporary objects.
**/
typedef struct belle_sip_object_pool belle_sip_object_pool_t;
belle_sip_object_pool_t * belle_sip_object_pool_push(void);
void belle_sip_object_pool_pop(void);
void belle_sip_object_pool_clean(belle_sip_object_pool_t *obj);
#endif
......@@ -76,6 +76,11 @@ BELLESIP_EXPORT void belle_sip_stack_set_resolver_tx_delay(belle_sip_stack_t *st
**/
BELLESIP_EXPORT void belle_sip_stack_set_resolver_send_error(belle_sip_stack_t *stack, int send_error);
void belle_sip_stack_push_pool(belle_sip_stack_t *stack);
void belle_sip_stack_pop_pool(belle_sip_stack_t *stack);
BELLE_SIP_END_DECLS
#endif
......
......@@ -113,9 +113,11 @@ typedef struct weak_ref{
void *belle_sip_object_get_interface_methods(belle_sip_object_t *obj, belle_sip_interface_id_t ifid);
void belle_sip_object_delete_unowned(void);
/*used internally by unref()*/
void belle_sip_object_delete(void *obj);
belle_sip_object_pool_t *belle_sip_object_pool_get_current(void);
void belle_sip_object_pool_add(belle_sip_object_pool_t *pool, belle_sip_object_t *obj);
void belle_sip_object_pool_remove(belle_sip_object_pool_t *pool, belle_sip_object_t *obj);
#define BELLE_SIP_OBJECT_VPTR(obj,object_type) ((BELLE_SIP_OBJECT_VPTR_TYPE(object_type)*)(((belle_sip_object_t*)obj)->vptr))
......@@ -183,6 +185,7 @@ BELLE_SIP_DECLARE_VPTR(belle_sip_header_refer_to_t);
BELLE_SIP_DECLARE_VPTR(belle_sip_header_referred_by_t);
BELLE_SIP_DECLARE_VPTR(belle_sip_header_replaces_t);
BELLE_SIP_DECLARE_VPTR(belle_sip_hop_t);
BELLE_SIP_DECLARE_VPTR(belle_sip_object_pool_t);
typedef void (*belle_sip_source_remove_callback_t)(belle_sip_source_t *);
......
......@@ -230,6 +230,7 @@ belle_sip_socket_t belle_sip_source_get_socket(const belle_sip_source_t* source)
struct belle_sip_main_loop{
belle_sip_object_t base;
belle_sip_list_t *sources;
belle_sip_object_pool_t *pool;
int nsources;
int run;
};
......@@ -250,7 +251,7 @@ static void belle_sip_main_loop_destroy(belle_sip_main_loop_t *ml){
while (ml->sources){
belle_sip_main_loop_remove_source(ml,(belle_sip_source_t*)ml->sources->data);
}
belle_sip_object_delete_unowned();
belle_sip_object_pool_pop();
}
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_main_loop_t);
......@@ -258,6 +259,7 @@ BELLE_SIP_INSTANCIATE_VPTR(belle_sip_main_loop_t,belle_sip_object_t,belle_sip_ma
belle_sip_main_loop_t *belle_sip_main_loop_new(void){
belle_sip_main_loop_t*m=belle_sip_object_new(belle_sip_main_loop_t);
m->pool=belle_sip_object_pool_push();
return m;
}
......@@ -411,7 +413,7 @@ void belle_sip_main_loop_iterate(belle_sip_main_loop_t *ml){
}else belle_sip_main_loop_remove_source(ml,s);
}
belle_sip_list_free_with_data(copy,belle_sip_object_unref);
belle_sip_object_delete_unowned();
belle_sip_object_pool_clean(ml->pool);
}
void belle_sip_main_loop_run(belle_sip_main_loop_t *ml){
......
......@@ -18,7 +18,6 @@
#include "belle_sip_internal.h"
static belle_sip_list_t *unowned_objects=NULL;
static int has_type(belle_sip_object_t *obj, belle_sip_type_id_t id){
belle_sip_object_vptr_t *vptr=obj->vptr;
......@@ -40,33 +39,20 @@ belle_sip_object_t * _belle_sip_object_new(size_t objsize, belle_sip_object_vptr
obj->vptr=vptr;
obj->size=objsize;
if (obj->ref==0){
unowned_objects=belle_sip_list_prepend(unowned_objects,obj);
belle_sip_object_pool_t *pool=belle_sip_object_pool_get_current();
if (pool) belle_sip_object_pool_add(pool,obj);
}
return obj;
}
void belle_sip_object_delete_unowned(void){
belle_sip_list_t *elem,*next;
for(elem=unowned_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;
unowned_objects=belle_sip_list_delete_link(unowned_objects,elem);
}else next=elem->next;
}
}
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){
unowned_objects=belle_sip_list_remove(unowned_objects,obj);
if (o->ref==0 && o->pool){
belle_sip_object_pool_remove(o->pool,obj);
}
o->ref++;
return obj;
......@@ -74,9 +60,9 @@ belle_sip_object_t * belle_sip_object_ref(void *obj){
void belle_sip_object_unref(void *ptr){
belle_sip_object_t *obj=BELLE_SIP_OBJECT(ptr);
if (obj->ref==-1) belle_sip_fatal("Object of type [%s] freed twice !",obj->name);
if (obj->ref==0){
unowned_objects=belle_sip_list_remove(unowned_objects,obj);
if (obj->ref==-1) belle_sip_fatal("Object with name [%s] freed twice !",obj->name);
if (obj->ref==0 && obj->pool){
belle_sip_object_pool_remove(obj->pool,obj);
obj->ref=-1;
belle_sip_object_delete(obj);
return;
......@@ -208,7 +194,8 @@ belle_sip_object_t *belle_sip_object_clone(const belle_sip_object_t *obj){
newobj->size=obj->size;
_belle_sip_object_copy(newobj,obj);
if (newobj->ref==0){
unowned_objects=belle_sip_list_prepend(unowned_objects,newobj);
belle_sip_object_pool_t *pool=belle_sip_object_pool_get_current();
if (pool) belle_sip_object_pool_add(pool,newobj);
}
return newobj;
}
......@@ -361,4 +348,132 @@ char *belle_sip_object_describe_type_from_name(const char *name){
#endif
struct belle_sip_object_pool{
belle_sip_object_t base;
belle_sip_list_t *objects;
belle_sip_thread_t thread_id;
};
static void belle_sip_object_pool_destroy(belle_sip_object_pool_t *pool){
belle_sip_object_pool_clean(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();
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){
belle_sip_thread_t tid=belle_sip_thread_self();
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;
}
void belle_sip_object_pool_clean(belle_sip_object_pool_t *pool){
belle_sip_list_t *elem,*next;
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 cleanup_pool_stack(void *data){
belle_sip_list_t **pool_stack=(belle_sip_list_t**)data;
belle_sip_list_free_with_data(*pool_stack, belle_sip_object_unref);
belle_sip_message("Object pools for thread [%u] cleaned while exiting",(unsigned long)belle_sip_thread_self());
*pool_stack=NULL;
belle_sip_free(pool_stack);
}
static belle_sip_list_t** get_current_pool_stack(void){
static belle_sip_thread_key_t pools_key;
static int pools_key_created=0;
belle_sip_list_t **pool_stack;
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);
}
return pool_stack;
}
belle_sip_object_pool_t * belle_sip_object_pool_push(void){
belle_sip_list_t **pools=get_current_pool_stack();
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;
}
void belle_sip_object_pool_pop(void){
belle_sip_list_t **pools=get_current_pool_stack();
belle_sip_object_pool_t *pool;
if (pools==NULL) {
belle_sip_error("Not possible to pop a pool.");
return;
}
if (*pools==NULL){
belle_sip_error("There is no current pool in stack.");
return;
}
pool=(belle_sip_object_pool_t*)(*pools)->data;
*pools=belle_sip_list_remove_link(*pools,*pools);
belle_sip_object_unref(pool);
}
belle_sip_object_pool_t *belle_sip_object_pool_get_current(void){
belle_sip_list_t **pools=get_current_pool_stack();
if (pools==NULL) return NULL;
if (*pools==NULL){
belle_sip_warning("There is no object pool created. Use belle_sip_stack_push_pool() to create one. Unowned objects not unref'd will be leaked.");
return NULL;
}
return (belle_sip_object_pool_t*)(*pools)->data;
}
......@@ -68,6 +68,28 @@ const char *belle_sip_get_socket_error_string_from_code(int code){
return (const char *)msgBuf;
}
int belle_sip_thread_key_create(belle_sip_thread_key_t *key, void (*destructor)(void*) ){
*key=TlsAlloc();
if (*key==TLS_OUT_OF_INDEXES){
belle_sip_error("TlsAlloc(): TLS_OUT_OF_INDEXES.");
return -1;
}
return 0;
}
int belle_sip_thread_setspecific(belle_sip_thread_key_t key,const void *value){
return TlsSetValue(key,value) ? 0 : -1;
}
const void* belle_sip_thread_getspecific(belle_sip_thread_key_t key){
return TlsGetValue(key);
}
int belle_sip_thread_key_delete(belle_sip_thread_key_t key){
return TlsFree(key) ? 0 : -1;
}
#ifdef WINAPI_FAMILY_PHONE_APP
void belle_sip_sleep(unsigned int ms) {
HANDLE sleepEvent = CreateEventEx(NULL, NULL, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS);
......
......@@ -28,6 +28,7 @@
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <pthread.h>
#else
......@@ -72,6 +73,17 @@ int belle_sip_socket_set_nonblocking (belle_sip_socket_t sock);
#if defined(WIN32)
typedef HANDLE belle_sip_thread_t;
#define belle_sip_thread_self() GetCurrentThread()
typedef DWORD belle_sip_thread_key_t;
int belle_sip_thread_key_create(belle_sip_thread_key_t *key, void (*destructor)(void*) );
int belle_sip_thread_setspecific(belle_sip_thread_key_t key,const void *value);
const void* belle_sip_thread_getspecific(belle_sip_thread_key_t key);
int belle_sip_thread_key_delete(belle_sip_thread_key_t key);
static inline void close_socket(belle_sip_socket_t s){
closesocket(s);
}
......@@ -100,6 +112,15 @@ static inline int inet_aton(const char *ip, struct in_addr *p){
#else
typedef pthread_t belle_sip_thread_t;
#define belle_sip_thread_self() pthread_self()
typedef pthread_key_t belle_sip_thread_key_t;
#define belle_sip_thread_key_create(key,destructor) pthread_key_create(key,destructor)
#define belle_sip_thread_setspecific(key,value) pthread_setspecific(key,value)
#define belle_sip_thread_getspecific(key) pthread_getspecific(key)
#define belle_sip_thread_key_delete(key) pthread_key_delete(key)
static inline void close_socket(belle_sip_socket_t s){
close(s);
}
......
......@@ -164,3 +164,4 @@ void belle_sip_stack_set_resolver_send_error(belle_sip_stack_t *stack, int send_
const char* belle_sip_version_to_string() {
return PACKAGE_VERSION;
}
......@@ -129,7 +129,8 @@ int belle_sip_tester_run_tests(const char *suite_name, const char *test_name) {
for (i = 0; i < belle_sip_tester_nb_test_suites(); i++) {
run_test_suite(test_suite[i]);
}
belle_sip_object_pool_push();
#if HAVE_CU_GET_SUITE
if (suite_name){
CU_pSuite suite;
......@@ -157,6 +158,8 @@ int belle_sip_tester_run_tests(const char *suite_name, const char *test_name) {
}
}
belle_sip_object_pool_pop();
CU_cleanup_registry();
return CU_get_error();
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment