/* (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 "fusion_internal.h" #include #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