From 7fe60435bce6595a9c58a9bfd8244d74b5320e96 Mon Sep 17 00:00:00 2001 From: Benjamin Franzke Date: Tue, 15 Jan 2013 08:46:13 +0100 Subject: Import DirectFB141_2k11R3_beta5 --- Source/DirectFB/lib/fusion/arena.c | 566 +++++++++++++++++++++++++++++++++++++ 1 file changed, 566 insertions(+) create mode 100755 Source/DirectFB/lib/fusion/arena.c (limited to 'Source/DirectFB/lib/fusion/arena.c') diff --git a/Source/DirectFB/lib/fusion/arena.c b/Source/DirectFB/lib/fusion/arena.c new file mode 100755 index 0000000..9d86dca --- /dev/null +++ b/Source/DirectFB/lib/fusion/arena.c @@ -0,0 +1,566 @@ +/* + (c) Copyright 2001-2009 The world wide DirectFB Open Source Community (directfb.org) + (c) Copyright 2000-2004 Convergence (integrated media) GmbH + + All rights reserved. + + Written by Denis Oliver Kropp , + Andreas Hundt , + Sven Neumann , + Ville Syrjälä and + Claudio Ciccani . + + 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 "fusion_internal.h" + + +#if FUSION_BUILD_MULTI + +D_DEBUG_DOMAIN( Fusion_Arena, "Fusion/Arena", "Fusion Arena" ); + +struct __Fusion_FusionArena { + DirectLink link; + + int magic; + + FusionWorldShared *shared; + + FusionSkirmish lock; + FusionRef ref; + + char *name; + + FusionHash *field_hash; +}; + +/**********************************************************************************************************************/ + +static FusionArena *lock_arena ( FusionWorld *world, + const char *name, + bool add ); + +static void unlock_arena( FusionArena *arena ); + +/**********************************************************************************************************************/ + +DirectResult +fusion_arena_enter (FusionWorld *world, + const char *name, + ArenaEnterFunc initialize, + ArenaEnterFunc join, + void *ctx, + FusionArena **ret_arena, + int *ret_error) +{ + FusionArena *arena; + FusionWorldShared *shared; + ArenaEnterFunc func; + int error = 0; + + D_MAGIC_ASSERT( world, FusionWorld ); + + D_ASSERT( name != NULL ); + D_ASSERT( initialize != NULL ); + D_ASSERT( join != NULL ); + D_ASSERT( ret_arena != NULL ); + + D_DEBUG_AT( Fusion_Arena, "%s( '%s' )\n", __FUNCTION__, name ); + + shared = world->shared; + + D_MAGIC_ASSERT( shared, FusionWorldShared ); + + /* Lookup arena and lock it. If it doesn't exist create it. */ + arena = lock_arena( world, name, true ); + if (!arena) + return DR_FAILURE; + + /* Check if we are the first. */ + if (fusion_ref_zero_trylock( &arena->ref ) == DR_OK) { + D_DEBUG ("Fusion/Arena: entering arena '%s' (establishing)\n", name); + + /* Call 'initialize' later. */ + func = initialize; + + /* Unlock the reference counter. */ + fusion_ref_unlock( &arena->ref ); + } + else { + D_DEBUG ("Fusion/Arena: entering arena '%s' (joining)\n", name); + + fusion_shm_attach_unattached( world ); + + /* Call 'join' later. */ + func = join; + } + + /* Increase reference counter. */ + fusion_ref_up (&arena->ref, false); + + /* Return the arena. */ + *ret_arena = arena; + + /* Call 'initialize' or 'join'. */ + error = func (arena, ctx); + + /* Return the return value of the callback. */ + if (ret_error) + *ret_error = error; + + if (error) { + fusion_ref_down (&arena->ref, false); + + if (func == initialize) { + /* Destroy fields. */ + fusion_hash_destroy( arena->field_hash ); + + /* Destroy reference counter. */ + fusion_ref_destroy( &arena->ref ); + + /* Destroy the arena lock. This has to happen before + locking the list. Otherwise a dead lock with lock_arena() + below could occur. */ + fusion_skirmish_destroy( &arena->lock ); + + /* Lock the list and remove the arena. */ + fusion_skirmish_prevail( &shared->arenas_lock ); + direct_list_remove( &shared->arenas, &arena->link ); + fusion_skirmish_dismiss( &shared->arenas_lock ); + + D_MAGIC_CLEAR( arena ); + + /* Free allocated memory. */ + SHFREE( shared->main_pool, arena->name ); + SHFREE( shared->main_pool, arena ); + + return DR_OK; + } + } + + /* Unlock the arena. */ + unlock_arena( arena ); + + return DR_OK; +} + +DirectResult +fusion_arena_add_shared_field (FusionArena *arena, + const char *name, + void *data) +{ + DirectResult ret; + FusionWorldShared *shared; + char *shname; + + D_ASSERT( arena != NULL ); + D_ASSERT( data != NULL ); + D_ASSERT( name != NULL ); + + D_MAGIC_ASSERT( arena, FusionArena ); + + D_DEBUG_AT( Fusion_Arena, "%s( '%s', '%s' -> %p )\n", __FUNCTION__, arena->name, name, data ); + + shared = arena->shared; + D_MAGIC_ASSERT( shared, FusionWorldShared ); + + /* Lock the arena. */ + ret = fusion_skirmish_prevail( &arena->lock ); + if (ret) + return ret; + + /* Give it the requested name. */ + shname = SHSTRDUP( shared->main_pool, name ); + if (shname) + ret = fusion_hash_replace( arena->field_hash, shname, data, NULL, NULL ); + else + ret = D_OOSHM(); + + /* Unlock the arena. */ + fusion_skirmish_dismiss( &arena->lock ); + + return ret; +} + +DirectResult +fusion_arena_get_shared_field (FusionArena *arena, + const char *name, + void **data) +{ + void *ptr; + + D_ASSERT( arena != NULL ); + D_ASSERT( name != NULL ); + D_ASSERT( data != NULL ); + + D_MAGIC_ASSERT( arena, FusionArena ); + + D_DEBUG_AT( Fusion_Arena, "%s( '%s', '%s' )\n", __FUNCTION__, arena->name, name ); + + /* Lock the arena. */ + if (fusion_skirmish_prevail( &arena->lock )) + return DR_FAILURE; + + /* Lookup entry. */ + ptr = fusion_hash_lookup( arena->field_hash, name ); + + D_DEBUG_AT( Fusion_Arena, " -> %p\n", ptr ); + + /* Unlock the arena. */ + fusion_skirmish_dismiss( &arena->lock ); + + if (!ptr) + return DR_ITEMNOTFOUND; + + *data = ptr; + + return DR_OK; +} + +DirectResult +fusion_arena_exit (FusionArena *arena, + ArenaExitFunc shutdown, + ArenaExitFunc leave, + void *ctx, + bool emergency, + int *ret_error) +{ + int error = 0; + FusionWorldShared *shared; + + D_MAGIC_ASSERT( arena, FusionArena ); + + D_DEBUG_AT( Fusion_Arena, "%s( '%s' )\n", __FUNCTION__, arena->name ); + + D_ASSERT( shutdown != NULL ); + + shared = arena->shared; + + D_MAGIC_ASSERT( shared, FusionWorldShared ); + + /* Lock the arena. */ + if (fusion_skirmish_prevail( &arena->lock )) + return DR_FAILURE; + + /* Decrease reference counter. */ + fusion_ref_down( &arena->ref, false ); + + /* If we are the last... */ + if (fusion_ref_zero_trylock( &arena->ref ) == DR_OK) { + /* Deinitialize everything. */ + error = shutdown( arena, ctx, emergency ); + + /* Destroy fields. */ + fusion_hash_destroy( arena->field_hash ); + + /* Destroy reference counter. */ + fusion_ref_destroy( &arena->ref ); + + /* Destroy the arena lock. This has to happen before + locking the list. Otherwise a dead lock with lock_arena() + below could occur. */ + fusion_skirmish_destroy( &arena->lock ); + + /* Lock the list and remove the arena. */ + fusion_skirmish_prevail( &shared->arenas_lock ); + direct_list_remove( &shared->arenas, &arena->link ); + fusion_skirmish_dismiss( &shared->arenas_lock ); + + D_MAGIC_CLEAR( arena ); + + /* Free allocated memory. */ + SHFREE( shared->main_pool, arena->name ); + SHFREE( shared->main_pool, arena ); + } + else { + if (!leave) { + fusion_ref_up( &arena->ref, false ); + fusion_skirmish_dismiss( &arena->lock ); + return DR_BUSY; + } + + /* Simply leave the arena. */ + error = leave( arena, ctx, emergency ); + + /* Unlock the arena. */ + fusion_skirmish_dismiss( &arena->lock ); + } + + /* Return the return value of the callback. */ + if (ret_error) + *ret_error = error; + + return DR_OK; +} + + +/***************************** + * File internal functions * + *****************************/ + +static FusionArena * +create_arena( FusionWorld *world, + const char *name ) +{ + DirectResult ret; + char buf[64]; + FusionArena *arena; + FusionWorldShared *shared; + + D_MAGIC_ASSERT( world, FusionWorld ); + D_ASSERT( name != NULL ); + + shared = world->shared; + D_MAGIC_ASSERT( shared, FusionWorldShared ); + + arena = SHCALLOC( shared->main_pool, 1, sizeof(FusionArena) ); + if (!arena) { + D_OOSHM(); + return NULL; + } + + arena->shared = shared; + + snprintf( buf, sizeof(buf), "Arena '%s'", name ); + + /* Initialize lock and reference counter. */ + ret = fusion_skirmish_init( &arena->lock, buf, world ); + if (ret) + goto error; + + ret = fusion_ref_init( &arena->ref, buf, world ); + if (ret) + goto error_ref; + + /* Give it the requested name. */ + arena->name = SHSTRDUP( shared->main_pool, name ); + if (!arena->name) { + D_OOSHM(); + goto error_prevail; + } + + ret = fusion_hash_create( shared->main_pool, HASH_STRING, HASH_PTR, 7, &arena->field_hash ); + if (ret) + goto error_hash; + + fusion_hash_set_autofree( arena->field_hash, true, false ); + + /* Add it to the list. */ + direct_list_prepend( &shared->arenas, &arena->link ); + + /* Lock the newly created arena. */ + ret = fusion_skirmish_prevail( &arena->lock ); + if (ret) + goto error_prevail; + + D_MAGIC_SET( arena, FusionArena ); + + /* Returned locked new arena. */ + return arena; + + +error_prevail: + fusion_hash_destroy( arena->field_hash ); + +error_hash: + if (arena->name) + SHFREE( shared->main_pool, arena->name ); + + fusion_ref_destroy( &arena->ref ); + +error_ref: + fusion_skirmish_destroy( &arena->lock ); + +error: + SHFREE( shared->main_pool, arena ); + + return NULL; +} + +static FusionArena * +lock_arena( FusionWorld *world, + const char *name, + bool add ) +{ + FusionArena *arena; + FusionWorldShared *shared; + + D_MAGIC_ASSERT( world, FusionWorld ); + D_ASSERT( name != NULL ); + + shared = world->shared; + D_MAGIC_ASSERT( shared, FusionWorldShared ); + + /* Lock the list. */ + if (fusion_skirmish_prevail( &shared->arenas_lock )) + return NULL; + + /* For each exisiting arena... */ + direct_list_foreach (arena, shared->arenas) { + /* Lock the arena. + This would fail if the arena has been + destroyed while waiting for the lock. */ + if (fusion_skirmish_prevail( &arena->lock )) + continue; + + D_MAGIC_ASSERT( arena, FusionArena ); + + /* Check if the name matches. */ + if (! strcmp( arena->name, name )) { + /* Check for an orphaned arena. */ + if (fusion_ref_zero_trylock( &arena->ref ) == DR_OK) { + D_ERROR( "Fusion/Arena: orphaned arena '%s'!\n", name ); + + fusion_ref_unlock( &arena->ref ); + +// arena = NULL; + } + + /* Unlock the list. */ + fusion_skirmish_dismiss( &shared->arenas_lock ); + + /* Return locked arena. */ + return arena; + } + + /* Unlock mismatched arena. */ + fusion_skirmish_dismiss( &arena->lock ); + } + + /* If no arena name matched, create a new arena + before unlocking the list again. */ + arena = add ? create_arena( world, name ) : NULL; + + /* Unlock the list. */ + fusion_skirmish_dismiss( &shared->arenas_lock ); + + return arena; +} + +static void +unlock_arena( FusionArena *arena ) +{ + D_ASSERT( arena != NULL ); + + D_MAGIC_ASSERT( arena, FusionArena ); + + /* Unlock the arena. */ + fusion_skirmish_dismiss( &arena->lock ); +} + +#else + +DirectResult +fusion_arena_enter (FusionWorld *world, + const char *name, + ArenaEnterFunc initialize, + ArenaEnterFunc join, + void *ctx, + FusionArena **ret_arena, + int *ret_error) +{ + int error; + + D_ASSERT( name != NULL ); + D_ASSERT( initialize != NULL ); + D_ASSERT( join != NULL ); + D_ASSERT( ret_arena != NULL ); + + /* Always call 'initialize'. */ + error = initialize (NULL, ctx); + + /* Return the return value of the callback. */ + if (ret_error) + *ret_error = error; + + return DR_OK; +} + +DirectResult +fusion_arena_add_shared_field (FusionArena *arena, + const char *name, + void *data) +{ + D_ASSERT( data != NULL ); + D_ASSERT( name != NULL ); + + return DR_OK; +} + +DirectResult +fusion_arena_get_shared_field (FusionArena *arena, + const char *name, + void **data) +{ + D_ASSERT( data != NULL ); + D_ASSERT( name != NULL ); + + D_BUG( "should not call this in fake mode" ); + + /* No field by that name has been found. */ + return DR_ITEMNOTFOUND; +} + +DirectResult +fusion_arena_exit (FusionArena *arena, + ArenaExitFunc shutdown, + ArenaExitFunc leave, + void *ctx, + bool emergency, + int *ret_error) +{ + int error = 0; + + D_ASSERT( shutdown != NULL ); + + /* Deinitialize everything. */ + error = shutdown( arena, ctx, emergency ); + + /* Return the return value of the callback. */ + if (ret_error) + *ret_error = error; + + return DR_OK; +} + +#endif + -- cgit