diff options
Diffstat (limited to 'Source/DirectFB/lib/fusion/ref.c')
-rwxr-xr-x | Source/DirectFB/lib/fusion/ref.c | 849 |
1 files changed, 849 insertions, 0 deletions
diff --git a/Source/DirectFB/lib/fusion/ref.c b/Source/DirectFB/lib/fusion/ref.c new file mode 100755 index 0000000..6c278e6 --- /dev/null +++ b/Source/DirectFB/lib/fusion/ref.c @@ -0,0 +1,849 @@ +/* + (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 <dok@directfb.org>, + Andreas Hundt <andi@fischlustig.de>, + Sven Neumann <neo@directfb.org>, + Ville Syrjälä <syrjala@sci.fi> and + Claudio Ciccani <klan@users.sf.net>. + + 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 <config.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> + +#include <sys/param.h> +#include <sys/types.h> + +#include <fusion/build.h> + +#include <direct/debug.h> +#include <direct/messages.h> +#include <direct/util.h> + +#include <fusion/types.h> +#include <fusion/ref.h> + +#include "fusion_internal.h" + +#include <signal.h> + + +#if FUSION_BUILD_MULTI + +D_DEBUG_DOMAIN( Fusion_Ref, "Fusion/Ref", "Fusion's Reference Counter" ); + + +#if FUSION_BUILD_KERNEL + +DirectResult +fusion_ref_init (FusionRef *ref, + const char *name, + const FusionWorld *world) +{ + FusionEntryInfo info; + + D_ASSERT( ref != NULL ); + D_ASSERT( name != NULL ); + D_MAGIC_ASSERT( world, FusionWorld ); + + D_DEBUG_AT( Fusion_Ref, "fusion_ref_init( %p, '%s' )\n", ref, name ? : "" ); + + while (ioctl( world->fusion_fd, FUSION_REF_NEW, &ref->multi.id )) { + if (errno == EINTR) + continue; + + D_PERROR( "FUSION_REF_NEW" ); + return DR_FUSION; + } + + D_DEBUG_AT( Fusion_Ref, " -> new ref %p [%d]\n", ref, ref->multi.id ); + + info.type = FT_REF; + info.id = ref->multi.id; + + direct_snputs( info.name, name, sizeof(info.name) ); + + ioctl( world->fusion_fd, FUSION_ENTRY_SET_INFO, &info ); + + /* Keep back pointer to shared world data. */ + ref->multi.shared = world->shared; + ref->multi.creator = fusion_id( world ); + + return DR_OK; +} + +DirectResult +fusion_ref_set_name (FusionRef *ref, + const char *name) +{ + FusionEntryInfo info; + + D_ASSERT( ref != NULL ); + D_ASSERT( name != NULL ); + + info.type = FT_REF; + info.id = ref->multi.id; + + direct_snputs( info.name, name, sizeof(info.name) ); + + while (ioctl (_fusion_fd( ref->multi.shared ), FUSION_ENTRY_SET_INFO, &info)) { + switch (errno) { + case EINTR: + continue; + case EAGAIN: + return DR_LOCKED; + case EINVAL: + D_ERROR ("Fusion/Reference: invalid reference\n"); + return DR_DESTROYED; + default: + break; + } + + D_PERROR ("FUSION_ENTRY_SET_NAME"); + + return DR_FAILURE; + } + + return DR_OK; +} + +DirectResult +fusion_ref_up (FusionRef *ref, bool global) +{ + D_ASSERT( ref != NULL ); + + while (ioctl (_fusion_fd( ref->multi.shared ), global ? + FUSION_REF_UP_GLOBAL : FUSION_REF_UP, &ref->multi.id)) + { + switch (errno) { + case EINTR: + continue; + case EAGAIN: + return DR_LOCKED; + case EINVAL: + D_ERROR ("Fusion/Reference: invalid reference\n"); + return DR_DESTROYED; + default: + break; + } + + if (global) + D_PERROR ("FUSION_REF_UP_GLOBAL"); + else + D_PERROR ("FUSION_REF_UP"); + + return DR_FAILURE; + } + + return DR_OK; +} + +DirectResult +fusion_ref_down (FusionRef *ref, bool global) +{ + D_ASSERT( ref != NULL ); + + while (ioctl (_fusion_fd( ref->multi.shared ), global ? + FUSION_REF_DOWN_GLOBAL : FUSION_REF_DOWN, &ref->multi.id)) + { + switch (errno) { + case EINTR: + continue; + case EINVAL: + D_ERROR ("Fusion/Reference: invalid reference\n"); + return DR_DESTROYED; + default: + break; + } + + if (global) + D_PERROR ("FUSION_REF_DOWN_GLOBAL"); + else + D_PERROR ("FUSION_REF_DOWN"); + + return DR_FAILURE; + } + + return DR_OK; +} + +DirectResult +fusion_ref_stat (FusionRef *ref, int *refs) +{ + int val; + + D_ASSERT( ref != NULL ); + D_ASSERT( refs != NULL ); + + while ((val = ioctl (_fusion_fd( ref->multi.shared ), FUSION_REF_STAT, &ref->multi.id)) < 0) { + switch (errno) { + case EINTR: + continue; + case EINVAL: + D_ERROR ("Fusion/Reference: invalid reference\n"); + return DR_DESTROYED; + default: + break; + } + + D_PERROR ("FUSION_REF_STAT"); + + return DR_FAILURE; + } + + *refs = val; + + return DR_OK; +} + +DirectResult +fusion_ref_zero_lock (FusionRef *ref) +{ + D_ASSERT( ref != NULL ); + + while (ioctl (_fusion_fd( ref->multi.shared ), FUSION_REF_ZERO_LOCK, &ref->multi.id)) { + switch (errno) { + case EINTR: + continue; + case EINVAL: + D_ERROR ("Fusion/Reference: invalid reference\n"); + return DR_DESTROYED; + default: + break; + } + + D_PERROR ("FUSION_REF_ZERO_LOCK"); + + return DR_FAILURE; + } + + return DR_OK; +} + +DirectResult +fusion_ref_zero_trylock (FusionRef *ref) +{ + D_ASSERT( ref != NULL ); + + while (ioctl (_fusion_fd( ref->multi.shared ), FUSION_REF_ZERO_TRYLOCK, &ref->multi.id)) { + switch (errno) { + case EINTR: + continue; + case ETOOMANYREFS: + return DR_BUSY; + case EINVAL: + D_ERROR ("Fusion/Reference: invalid reference\n"); + return DR_DESTROYED; + default: + break; + } + + D_PERROR ("FUSION_REF_ZERO_TRYLOCK"); + + return DR_FAILURE; + } + + return DR_OK; +} + +DirectResult +fusion_ref_unlock (FusionRef *ref) +{ + D_ASSERT( ref != NULL ); + + while (ioctl (_fusion_fd( ref->multi.shared ), FUSION_REF_UNLOCK, &ref->multi.id)) { + switch (errno) { + case EINTR: + continue; + case EINVAL: + D_ERROR ("Fusion/Reference: invalid reference\n"); + return DR_DESTROYED; + default: + break; + } + + D_PERROR ("FUSION_REF_UNLOCK"); + + return DR_FAILURE; + } + + return DR_OK; +} + +DirectResult +fusion_ref_watch (FusionRef *ref, FusionCall *call, int call_arg) +{ + FusionRefWatch watch; + + D_ASSERT( ref != NULL ); + D_ASSERT( call != NULL ); + + watch.id = ref->multi.id; + watch.call_id = call->call_id; + watch.call_arg = call_arg; + + while (ioctl (_fusion_fd( ref->multi.shared ), FUSION_REF_WATCH, &watch)) { + switch (errno) { + case EINTR: + continue; + case EINVAL: + D_ERROR ("Fusion/Reference: invalid reference\n"); + return DR_DESTROYED; + default: + break; + } + + D_PERROR ("FUSION_REF_WATCH"); + + return DR_FAILURE; + } + + return DR_OK; +} + +DirectResult +fusion_ref_inherit (FusionRef *ref, FusionRef *from) +{ + FusionRefInherit inherit; + + D_ASSERT( ref != NULL ); + D_ASSERT( from != NULL ); + + inherit.id = ref->multi.id; + inherit.from = from->multi.id; + + while (ioctl (_fusion_fd( ref->multi.shared ), FUSION_REF_INHERIT, &inherit)) { + switch (errno) { + case EINTR: + continue; + case EINVAL: + D_ERROR ("Fusion/Reference: invalid reference\n"); + return DR_DESTROYED; + default: + break; + } + + D_PERROR ("FUSION_REF_INHERIT"); + + return DR_FAILURE; + } + + return DR_OK; +} + +DirectResult +fusion_ref_destroy (FusionRef *ref) +{ + D_ASSERT( ref != NULL ); + + D_DEBUG_AT( Fusion_Ref, "fusion_ref_destroy( %p [%d] )\n", ref, ref->multi.id ); + + while (ioctl (_fusion_fd( ref->multi.shared ), FUSION_REF_DESTROY, &ref->multi.id)) { + switch (errno) { + case EINTR: + continue; + case EINVAL: + D_ERROR ("Fusion/Reference: invalid reference\n"); + return DR_DESTROYED; + default: + break; + } + + D_PERROR ("FUSION_REF_DESTROY"); + + return DR_FAILURE; + } + + return DR_OK; +} + +#else /* FUSION_BUILD_KERNEL */ + +DirectResult +fusion_ref_init (FusionRef *ref, + const char *name, + const FusionWorld *world) +{ + D_ASSERT( ref != NULL ); + D_ASSERT( name != NULL ); + D_MAGIC_ASSERT( world, FusionWorld ); + + D_DEBUG_AT( Fusion_Ref, "fusion_ref_init( %p, '%s' )\n", ref, name ? : "" ); + + ref->multi.id = ++world->shared->ref_ids; + + ref->multi.builtin.local = 0; + ref->multi.builtin.global = 0; + + fusion_skirmish_init( &ref->multi.builtin.lock, name, world ); + + ref->multi.builtin.call = NULL; + + /* Keep back pointer to shared world data. */ + ref->multi.shared = world->shared; + ref->multi.creator = fusion_id( world ); + + return DR_OK; +} + +DirectResult +fusion_ref_set_name (FusionRef *ref, + const char *name) +{ + return DR_OK; +} + +DirectResult +_fusion_ref_change (FusionRef *ref, int add, bool global) +{ + DirectResult ret; + + D_ASSERT( ref != NULL ); + D_ASSERT( add != 0 ); + + ret = fusion_skirmish_prevail( &ref->multi.builtin.lock ); + if (ret) + return ret; + + if (global) { + if (ref->multi.builtin.global+add < 0) { + D_BUG( "ref has no global references" ); + fusion_skirmish_dismiss( &ref->multi.builtin.lock ); + return DR_BUG; + } + + ref->multi.builtin.global += add; + } + else { + if (ref->multi.builtin.local+add < 0) { + D_BUG( "ref has no local references" ); + fusion_skirmish_dismiss( &ref->multi.builtin.lock ); + return DR_BUG; + } + + ref->multi.builtin.local += add; + + _fusion_add_local( _fusion_world(ref->multi.shared), ref, add ); + } + + if (ref->multi.builtin.local+ref->multi.builtin.global == 0) { + fusion_skirmish_notify( &ref->multi.builtin.lock ); + + if (ref->multi.builtin.call) { + fusion_skirmish_dismiss( &ref->multi.builtin.lock ); + return fusion_call_execute( ref->multi.builtin.call, FCEF_ONEWAY, + ref->multi.builtin.call_arg, NULL, NULL ); + } + } + + fusion_skirmish_dismiss( &ref->multi.builtin.lock ); + + return DR_OK; +} + +DirectResult +fusion_ref_up (FusionRef *ref, bool global) +{ + return _fusion_ref_change( ref, +1, global ); +} + +DirectResult +fusion_ref_down (FusionRef *ref, bool global) +{ + return _fusion_ref_change( ref, -1, global ); +} + +DirectResult +fusion_ref_stat (FusionRef *ref, int *refs) +{ + D_ASSERT( ref != NULL ); + D_ASSERT( refs != NULL ); + + *refs = ref->multi.builtin.local + ref->multi.builtin.global; + + return DR_OK; +} + +DirectResult +fusion_ref_zero_lock (FusionRef *ref) +{ + DirectResult ret; + + D_ASSERT( ref != NULL ); + + ret = fusion_skirmish_prevail( &ref->multi.builtin.lock ); + if (ret) + return ret; + + if (ref->multi.builtin.call) { + ret = DR_ACCESSDENIED; + } + else { + if (ref->multi.builtin.local) + _fusion_check_locals( _fusion_world(ref->multi.shared), ref ); + + while (ref->multi.builtin.local+ref->multi.builtin.global) { + ret = fusion_skirmish_wait( &ref->multi.builtin.lock, 1000 ); /* 1 second */ + if (ret && ret != DR_TIMEOUT); + return ret; + + if (ref->multi.builtin.call) { + ret = DR_ACCESSDENIED; + break; + } + + if (ref->multi.builtin.local) + _fusion_check_locals( _fusion_world(ref->multi.shared), ref ); + } + } + + if (ret) + fusion_skirmish_dismiss( &ref->multi.builtin.lock ); + + return ret; +} + +DirectResult +fusion_ref_zero_trylock (FusionRef *ref) +{ + DirectResult ret; + + D_ASSERT( ref != NULL ); + + ret = fusion_skirmish_prevail( &ref->multi.builtin.lock ); + if (ret) + return ret; + + if (ref->multi.builtin.local) + _fusion_check_locals( _fusion_world(ref->multi.shared), ref ); + + if (ref->multi.builtin.local+ref->multi.builtin.global) + ret = DR_BUSY; + + if (ret) + fusion_skirmish_dismiss( &ref->multi.builtin.lock ); + + return ret; +} + +DirectResult +fusion_ref_unlock (FusionRef *ref) +{ + D_ASSERT( ref != NULL ); + + fusion_skirmish_dismiss( &ref->multi.builtin.lock ); + + return DR_OK; +} + +DirectResult +fusion_ref_watch (FusionRef *ref, FusionCall *call, int call_arg) +{ + DirectResult ret; + + D_ASSERT( ref != NULL ); + D_ASSERT( call != NULL ); + + ret = fusion_skirmish_prevail( &ref->multi.builtin.lock ); + if (ret) + return ret; + + if (ref->multi.builtin.local+ref->multi.builtin.global == 0) { + D_BUG( "ref has no references" ); + ret = DR_BUG; + } + else if (ref->multi.builtin.call) { + ret = DR_BUSY; + } + else { + ref->multi.builtin.call = call; + ref->multi.builtin.call_arg = call_arg; + fusion_skirmish_notify( &ref->multi.builtin.lock ); + } + + fusion_skirmish_dismiss( &ref->multi.builtin.lock ); + + return ret; +} + +DirectResult +fusion_ref_inherit (FusionRef *ref, FusionRef *from) +{ + D_ASSERT( ref != NULL ); + D_ASSERT( from != NULL ); + + D_UNIMPLEMENTED(); + + return fusion_ref_up( ref, true ); +} + +DirectResult +fusion_ref_destroy (FusionRef *ref) +{ + FusionSkirmish *skirmish; + + D_ASSERT( ref != NULL ); + + D_DEBUG_AT( Fusion_Ref, "fusion_ref_destroy( %p )\n", ref ); + + skirmish = &ref->multi.builtin.lock; + if (skirmish->multi.builtin.destroyed) + return DR_DESTROYED; + + _fusion_remove_all_locals( _fusion_world(ref->multi.shared), ref ); + + fusion_skirmish_destroy( skirmish ); + + return DR_OK; +} + +#endif /* FUSION_BUILD_KERNEL */ + +#else /* FUSION_BUILD_MULTI */ + +DirectResult +fusion_ref_init (FusionRef *ref, + const char *name, + const FusionWorld *world) +{ + D_ASSERT( ref != NULL ); + D_ASSERT( name != NULL ); + + direct_util_recursive_pthread_mutex_init (&ref->single.lock); + pthread_cond_init (&ref->single.cond, NULL); + + ref->single.refs = 0; + ref->single.destroyed = false; + ref->single.locked = 0; + + return DR_OK; +} + +DirectResult +fusion_ref_set_name (FusionRef *ref, + const char *name) +{ + return DR_OK; +} + +DirectResult +fusion_ref_up (FusionRef *ref, bool global) +{ + DirectResult ret = DR_OK; + + D_ASSERT( ref != NULL ); + + pthread_mutex_lock (&ref->single.lock); + + if (ref->single.destroyed) + ret = DR_DESTROYED; + else if (ref->single.locked) + ret = DR_LOCKED; + else + ref->single.refs++; + + pthread_mutex_unlock (&ref->single.lock); + + return ret; +} + +DirectResult +fusion_ref_down (FusionRef *ref, bool global) +{ + D_ASSERT( ref != NULL ); + + pthread_mutex_lock (&ref->single.lock); + + if (!ref->single.refs) { + D_BUG( "no more references" ); + pthread_mutex_unlock (&ref->single.lock); + return DR_BUG; + } + + if (ref->single.destroyed) { + pthread_mutex_unlock (&ref->single.lock); + return DR_DESTROYED; + } + + if (! --ref->single.refs) { + if (ref->single.call) { + FusionCall *call = ref->single.call; + + if (call->handler) { + int ret; + pthread_mutex_unlock (&ref->single.lock); + call->handler( 0, ref->single.call_arg, NULL, call->ctx, 0, &ret ); + return DR_OK; + } + } + else + pthread_cond_broadcast (&ref->single.cond); + } + + pthread_mutex_unlock (&ref->single.lock); + + return DR_OK; +} + +DirectResult +fusion_ref_stat (FusionRef *ref, int *refs) +{ + D_ASSERT( ref != NULL ); + D_ASSERT( refs != NULL ); + + if (ref->single.destroyed) + return DR_DESTROYED; + + *refs = ref->single.refs; + + return DR_OK; +} + +DirectResult +fusion_ref_zero_lock (FusionRef *ref) +{ + DirectResult ret = DR_OK; + + D_ASSERT( ref != NULL ); + + pthread_mutex_lock (&ref->single.lock); + + do { + if (ref->single.destroyed) + ret = DR_DESTROYED; + else if (ref->single.locked) + ret = DR_LOCKED; + else if (ref->single.refs) + pthread_cond_wait (&ref->single.cond, &ref->single.lock); + else { + ref->single.locked = direct_gettid(); + break; + } + } while (ret == DR_OK); + + pthread_mutex_unlock (&ref->single.lock); + + return ret; +} + +DirectResult +fusion_ref_zero_trylock (FusionRef *ref) +{ + DirectResult ret = DR_OK; + + D_ASSERT( ref != NULL ); + + pthread_mutex_lock (&ref->single.lock); + + if (ref->single.destroyed) + ret = DR_DESTROYED; + else if (ref->single.locked) + ret = DR_LOCKED; + else if (ref->single.refs) + ret = DR_BUSY; + else + ref->single.locked = direct_gettid(); + + pthread_mutex_unlock (&ref->single.lock); + + return ret; +} + +DirectResult +fusion_ref_unlock (FusionRef *ref) +{ + DirectResult ret = DR_OK; + + D_ASSERT( ref != NULL ); + + pthread_mutex_lock (&ref->single.lock); + + if (ref->single.locked == direct_gettid()) { + ref->single.locked = 0; + + pthread_cond_broadcast (&ref->single.cond); + } + else + ret = DR_ACCESSDENIED; + + pthread_mutex_unlock (&ref->single.lock); + + return ret; +} + +DirectResult +fusion_ref_watch (FusionRef *ref, FusionCall *call, int call_arg) +{ + DirectResult ret = DR_OK; + + D_ASSERT( ref != NULL ); + D_ASSERT( call != NULL ); + + pthread_mutex_lock (&ref->single.lock); + + if (ref->single.destroyed) + ret = DR_DESTROYED; + else if (!ref->single.refs) + ret = DR_BUG; + else if (ref->single.call) + ret = DR_BUSY; + else { + ref->single.call = call; + ref->single.call_arg = call_arg; + } + + pthread_mutex_unlock (&ref->single.lock); + + return ret; +} + +DirectResult +fusion_ref_inherit (FusionRef *ref, FusionRef *from) +{ + D_ASSERT( ref != NULL ); + D_ASSERT( from != NULL ); + + D_UNIMPLEMENTED(); + + /* FIXME */ + return fusion_ref_up( ref, true ); +} + +DirectResult +fusion_ref_destroy (FusionRef *ref) +{ + D_ASSERT( ref != NULL ); + + ref->single.destroyed = true; + + pthread_cond_broadcast (&ref->single.cond); + + pthread_mutex_destroy (&ref->single.lock); + pthread_cond_destroy (&ref->single.cond); + + return DR_OK; +} + +#endif + |