/* (c) Copyright 2006-2007 directfb.org All rights reserved. Written by Denis Oliver Kropp . This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include D_DEBUG_DOMAIN( Coma_Core, "Coma/Core", "Coma Core" ); /**********************************************************************************************************************/ struct __COMA_ComaShared { int magic; FusionSkirmish lock; FusionSHMPoolShared *shmpool; FusionObjectPool *component_pool; FusionHash *components; FusionObjectPool *thread_pool; FusionCall thread_mem_call; FusionHash *allocations; }; /**********************************************************************************************************************/ static int coma_arena_initialize( FusionArena *arena, void *ctx ); static int coma_arena_join ( FusionArena *arena, void *ctx ); static int coma_arena_leave ( FusionArena *arena, void *ctx, bool emergency ); static int coma_arena_shutdown ( FusionArena *arena, void *ctx, bool emergency ); /**********************************************************************************************************************/ static void tlshm_destroy( void *arg ); /**********************************************************************************************************************/ DirectResult coma_enter( FusionWorld *world, const char *name, Coma **ret_coma ) { Coma *coma; char buf[128]; int ret = DR_OK; D_ASSERT( world != NULL ); D_ASSERT( name != NULL ); D_ASSERT( ret_coma != NULL ); D_DEBUG_AT( Coma_Core, "%s()\n", __FUNCTION__ ); /* Allocate local coma structure. */ coma = D_CALLOC( 1, sizeof(Coma) ); if (!coma) return D_OOM(); coma->world = world; coma->fusion_id = fusion_id( world ); coma->name = D_STRDUP( name ); pthread_key_create( &coma->tlshm_key, tlshm_destroy ); D_MAGIC_SET( coma, Coma ); snprintf( buf, sizeof(buf), "Coma/%s", name ); /* Enter the Coma arena. */ if (fusion_arena_enter( world, buf, coma_arena_initialize, coma_arena_join, coma, &coma->arena, &ret ) || ret) { D_MAGIC_CLEAR( coma ); D_FREE( coma->name ); D_FREE( coma ); return ret ? : DR_FUSION; } /* Return the coma. */ *ret_coma = coma; return DR_OK; } DirectResult coma_exit( Coma *coma, bool emergency ) { D_MAGIC_ASSERT( coma, Coma ); D_DEBUG_AT( Coma_Core, "%s( %p, %semergency )\n", __FUNCTION__, coma, emergency ? "" : "no " ); /* Exit the Coma arena. */ fusion_arena_exit( coma->arena, coma_arena_shutdown, coma_arena_leave, coma, emergency, NULL ); D_FREE( coma->name ); D_MAGIC_CLEAR( coma ); /* Deallocate local coma structure. */ D_FREE( coma ); return DR_OK; } /**********************************************************************************************************************/ DirectResult coma_create_component( Coma *coma, const char *name, ComaMethodFunc func, int num_notifications, void *ctx, ComaComponent **ret_component ) { DirectResult ret; ComaShared *shared; ComaComponent *component; D_MAGIC_ASSERT( coma, Coma ); D_DEBUG_AT( Coma_Core, "%s( %p, '%s' )\n", __FUNCTION__, coma, name ); shared = coma->shared; D_MAGIC_ASSERT( shared, ComaShared ); ret = fusion_skirmish_prevail( &shared->lock ); if (ret) return ret; /* Check for existence first. */ component = fusion_hash_lookup( shared->components, name ); if (component) { D_MAGIC_ASSERT( component, ComaComponent ); D_WARN( "component '%s' already exists", name ); fusion_skirmish_dismiss( &shared->lock ); return DR_BUSY; } /* Create component object. */ component = (ComaComponent*) fusion_object_create( shared->component_pool, coma->world ); if (!component) { fusion_skirmish_dismiss( &shared->lock ); return DR_FUSION; } /* Initialize component object. */ ret = coma_component_init( component, coma, name, func, num_notifications, ctx ); if (ret) { fusion_skirmish_dismiss( &shared->lock ); fusion_object_destroy( &component->object ); return ret; } /* Activate component object. */ fusion_object_activate( &component->object ); /* Insert new component into hash table. */ ret = fusion_hash_insert( shared->components, component->name, component ); if (ret) { D_DERROR( ret, "Coma/Core: fusion_hash_insert( '%s', %p ) failed!\n", name, component ); fusion_skirmish_dismiss( &shared->lock ); coma_component_unref( component ); return ret; } ret = fusion_skirmish_notify( &shared->lock ); if (ret) D_DERROR( ret, "Coma/Core: fusion_skirmish_notify() failed!\n" ); fusion_skirmish_dismiss( &shared->lock ); *ret_component = component; return DR_OK; } DirectResult coma_get_component( Coma *coma, const char *name, unsigned int timeout, ComaComponent **ret_component ) { DirectResult ret; ComaShared *shared; ComaComponent *component; D_MAGIC_ASSERT( coma, Coma ); D_ASSERT( name != NULL ); D_ASSERT( ret_component != NULL ); D_DEBUG_AT( Coma_Core, "%s( %p, '%s' )\n", __FUNCTION__, coma, name ); shared = coma->shared; D_MAGIC_ASSERT( shared, ComaShared ); /* Lock the manager. */ ret = fusion_skirmish_prevail( &shared->lock ); if (ret) return ret; /* Wait for the component to be added. */ while ((component = fusion_hash_lookup( shared->components, name )) == NULL) { ret = fusion_skirmish_wait( &shared->lock, timeout ); if (ret) return ret; } D_MAGIC_ASSERT( component, ComaComponent ); /* Increase component's ref counter. */ ret = coma_component_ref( component ); if (ret) { fusion_skirmish_dismiss( &shared->lock ); return ret; } /* Unlock the manager. */ fusion_skirmish_dismiss( &shared->lock ); /* Lock the component. */ ret = fusion_skirmish_prevail( &component->lock ); if (ret) { coma_component_unref( component ); return ret; } /* Wait for component to become active? */ while (!component->active) { ret = fusion_skirmish_wait( &component->lock, timeout ); if (ret) { coma_component_unref( component ); return ret; } } /* Unlock the component. */ fusion_skirmish_dismiss( &component->lock ); *ret_component = component; return DR_OK; } /**********************************************************************************************************************/ DirectResult coma_allocate( Coma *coma, unsigned int bytes, void **ret_ptr ) { void *ptr; ptr = SHCALLOC( coma->shared->shmpool, 1, bytes ); if (!ptr) return D_OOSHM(); fusion_hash_insert( coma->shared->allocations, ptr, (void*) (unsigned long) bytes ); *ret_ptr = ptr; return DR_OK; } DirectResult coma_deallocate( Coma *coma, void *ptr ) { if (!fusion_hash_lookup( coma->shared->allocations, ptr )) return DR_ITEMNOTFOUND; fusion_hash_remove( coma->shared->allocations, ptr, NULL, NULL ); SHFREE( coma->shared->shmpool, ptr ); return DR_OK; } DirectResult coma_allocation_size ( Coma *coma, void *ptr, int *ret_size ) { int size; size = (unsigned long) fusion_hash_lookup( coma->shared->allocations, ptr ); if (!size) { D_WARN( "zero length from lookup of %p", ptr ); return DR_ITEMNOTFOUND; } *ret_size = size; return DR_OK; } /**********************************************************************************************************************/ static FusionCallHandlerResult thread_mem_call_handler( int caller, int call_arg, void *call_ptr, void *ctx, unsigned int serial, int *ret_val ) { ComaThread *thread = call_ptr; D_MAGIC_ASSERT( thread, ComaThread ); if (call_arg > 0) { void *ptr = SHCALLOC( thread->shmpool, 1, call_arg ); if (!ptr) { *ret_val = D_OOSHM(); return FCHR_RETURN; } if (thread->mem) SHFREE( thread->shmpool, thread->mem ); thread->mem = ptr; thread->mem_size = call_arg; } else { if (thread->mem) SHFREE( thread->shmpool, thread->mem ); thread->mem = NULL; thread->mem_size = 0; } *ret_val = 0; return FCHR_RETURN; } /**********************************************************************************************************************/ static void tlshm_destroy( void *arg ) { ComaThread *thread = arg; D_MAGIC_ASSERT( thread, ComaThread ); coma_thread_unref( thread ); } /**********************************************************************************************************************/ DirectResult coma_get_local( Coma *coma, unsigned int bytes, void **ret_ptr ) { int ret; ComaShared *shared; ComaThread *thread; D_MAGIC_ASSERT( coma, Coma ); D_ASSERT( bytes > 0 ); D_ASSERT( ret_ptr != NULL ); shared = coma->shared; D_MAGIC_ASSERT( shared, ComaShared ); thread = pthread_getspecific( coma->tlshm_key ); if (!thread) { /* Create thread object. */ thread = (ComaThread*) fusion_object_create( shared->thread_pool, coma->world ); if (!thread) return DR_FUSION; /* Initialize thread object. */ ret = coma_thread_init( thread, coma ); if (ret) { fusion_object_destroy( &thread->object ); return ret; } /* Activate component object. */ fusion_object_activate( &thread->object ); pthread_setspecific( coma->tlshm_key, thread ); } D_MAGIC_ASSERT( thread, ComaThread ); if (thread->mem_size < bytes) { ret = fusion_call_execute( &shared->thread_mem_call, FCEF_NONE, bytes, thread, &ret ); if (ret) { D_DERROR( ret, "Coma/Core: Thread memory call to allocate %d bytes failed!\n", bytes ); return ret; } } *ret_ptr = thread->mem; return DR_OK; } DirectResult coma_free_local( Coma *coma ) { int ret; ComaShared *shared; ComaThread *thread; D_MAGIC_ASSERT( coma, Coma ); shared = coma->shared; D_MAGIC_ASSERT( shared, ComaShared ); thread = pthread_getspecific( coma->tlshm_key ); if (!thread) return DR_ITEMNOTFOUND; D_MAGIC_ASSERT( thread, ComaThread ); if (!thread->mem) return DR_BUFFEREMPTY; ret = fusion_call_execute( &shared->thread_mem_call, FCEF_NONE, 0, thread, &ret ); if (ret) { D_DERROR( ret, "Coma/Core: Thread memory call to free %d bytes failed!\n", thread->mem_size ); return ret; } return DR_OK; } /**********************************************************************************************************************/ FusionSHMPoolShared * coma_shmpool( const Coma *coma ) { const ComaShared *shared; D_MAGIC_ASSERT( coma, Coma ); shared = coma->shared; D_MAGIC_ASSERT( shared, ComaShared ); return shared->shmpool; } FusionWorld * coma_world( const Coma *coma ) { D_MAGIC_ASSERT( coma, Coma ); return coma->world; } /**********************************************************************************************************************/ void _coma_internal_remove_component( Coma *coma, ComaComponent *component ) { DirectResult ret; ComaShared *shared; D_MAGIC_ASSERT( coma, Coma ); D_MAGIC_ASSERT( component, ComaComponent ); D_DEBUG_AT( Coma_Core, "%s( %p, '%s' )\n", __FUNCTION__, coma, component->name ); shared = coma->shared; D_MAGIC_ASSERT( shared, ComaShared ); ret = fusion_skirmish_prevail( &shared->lock ); if (ret) { D_DERROR( ret, "Coma/Core: Could not lock core to remove component!\n" ); return; } fusion_hash_remove( shared->components, component->name, NULL, NULL ); fusion_skirmish_dismiss( &shared->lock ); } /**********************************************************************************************************************/ static DirectResult coma_initialize( Coma *coma ) { DirectResult ret; ComaShared *shared; D_DEBUG_AT( Coma_Core, "%s()\n", __FUNCTION__ ); D_MAGIC_ASSERT( coma, Coma ); shared = coma->shared; D_MAGIC_ASSERT( shared, ComaShared ); ret = fusion_hash_create( shared->shmpool, HASH_STRING, HASH_PTR, 7, &shared->components ); if (ret) return ret; fusion_hash_set_autofree( shared->components, false, false ); fusion_skirmish_init( &shared->lock, coma->name, coma->world ); shared->component_pool = coma_component_pool_create( coma ); shared->thread_pool = coma_thread_pool_create( coma ); fusion_call_init( &shared->thread_mem_call, thread_mem_call_handler, coma, coma->world ); fusion_hash_create( shared->shmpool, HASH_PTR, HASH_INT, 23, &shared->allocations ); return DR_OK; } static DirectResult coma_join( Coma *coma ) { D_DEBUG_AT( Coma_Core, "%s()\n", __FUNCTION__ ); D_MAGIC_ASSERT( coma, Coma ); /* really nothing to be done here, yet ;) */ return DR_OK; } static DirectResult coma_leave( Coma *coma ) { D_DEBUG_AT( Coma_Core, "%s()\n", __FUNCTION__ ); D_MAGIC_ASSERT( coma, Coma ); /* really nothing to be done here, yet ;) */ return DR_OK; } static DirectResult coma_shutdown( Coma *coma ) { ComaShared *shared; D_DEBUG_AT( Coma_Core, "%s()\n", __FUNCTION__ ); D_MAGIC_ASSERT( coma, Coma ); shared = coma->shared; D_MAGIC_ASSERT( shared, ComaShared ); fusion_object_pool_destroy( shared->component_pool, coma->world ); fusion_object_pool_destroy( shared->thread_pool, coma->world ); fusion_skirmish_destroy( &shared->lock ); fusion_hash_destroy( shared->components ); fusion_call_destroy( &shared->thread_mem_call ); fusion_hash_destroy( shared->allocations ); return DR_OK; } /**********************************************************************************************************************/ static int coma_arena_initialize( FusionArena *arena, void *ctx ) { DirectResult ret; Coma *coma = ctx; ComaShared *shared; FusionSHMPoolShared *pool; D_DEBUG_AT( Coma_Core, "%s()\n", __FUNCTION__ ); D_MAGIC_ASSERT( coma, Coma ); /* Create the shared memory pool first! */ ret = fusion_shm_pool_create( coma->world, "Coma Core", fusiondale_config->coma_shmpool_size, fusion_config->debugshm, &pool ); if (ret) return ret; /* Allocate shared structure in the new pool. */ shared = SHCALLOC( pool, 1, sizeof(ComaShared) ); if (!shared) { fusion_shm_pool_destroy( coma->world, pool ); return D_OOSHM(); } D_MAGIC_SET( shared, ComaShared ); coma->shared = shared; shared->shmpool = pool; /* Initialize. */ ret = coma_initialize( coma ); if (ret) { SHFREE( pool, shared ); fusion_shm_pool_destroy( coma->world, pool ); return ret; } /* Register shared data. */ fusion_arena_add_shared_field( arena, "Core/Shared", shared ); return DR_OK; } static int coma_arena_shutdown( FusionArena *arena, void *ctx, bool emergency) { DirectResult ret; Coma *coma = ctx; ComaShared *shared; FusionSHMPoolShared *pool; D_DEBUG_AT( Coma_Core, "%s()\n", __FUNCTION__ ); D_MAGIC_ASSERT( coma, Coma ); shared = coma->shared; D_MAGIC_ASSERT( shared, ComaShared ); pool = shared->shmpool; /* Shutdown. */ ret = coma_shutdown( coma ); if (ret) return ret; D_MAGIC_CLEAR( shared ); SHFREE( pool, shared ); fusion_dbg_print_memleaks( pool ); fusion_shm_pool_destroy( coma->world, pool ); return DR_OK; } static int coma_arena_join( FusionArena *arena, void *ctx ) { DirectResult ret; Coma *coma = ctx; ComaShared *shared; D_DEBUG_AT( Coma_Core, "%s()\n", __FUNCTION__ ); D_MAGIC_ASSERT( coma, Coma ); /* Get shared data. */ if (fusion_arena_get_shared_field( arena, "Core/Shared", (void*)&shared )) return DR_FUSION; coma->shared = shared; /* Join. */ ret = coma_join( coma ); if (ret) return ret; return DR_OK; } static int coma_arena_leave( FusionArena *arena, void *ctx, bool emergency) { DirectResult ret; Coma *coma = ctx; D_DEBUG_AT( Coma_Core, "%s()\n", __FUNCTION__ ); D_MAGIC_ASSERT( coma, Coma ); /* Leave. */ ret = coma_leave( coma ); if (ret) return ret; return DR_OK; }