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/lock.c | 687 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 687 insertions(+) create mode 100755 Source/DirectFB/lib/fusion/lock.c (limited to 'Source/DirectFB/lib/fusion/lock.c') diff --git a/Source/DirectFB/lib/fusion/lock.c b/Source/DirectFB/lib/fusion/lock.c new file mode 100755 index 0000000..33945c5 --- /dev/null +++ b/Source/DirectFB/lib/fusion/lock.c @@ -0,0 +1,687 @@ +/* + (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 "fusion_internal.h" + + +#if FUSION_BUILD_MULTI + +D_DEBUG_DOMAIN( Fusion_Skirmish, "Fusion/Skirmish", "Fusion's Skirmish (Mutex)" ); + + +#if FUSION_BUILD_KERNEL + +DirectResult +fusion_skirmish_init( FusionSkirmish *skirmish, + const char *name, + const FusionWorld *world ) +{ + FusionEntryInfo info; + + D_ASSERT( skirmish != NULL ); + D_ASSERT( name != NULL ); + D_MAGIC_ASSERT( world, FusionWorld ); + + D_DEBUG_AT( Fusion_Skirmish, "fusion_skirmish_init( %p, '%s' )\n", skirmish, name ? : "" ); + + while (ioctl( world->fusion_fd, FUSION_SKIRMISH_NEW, &skirmish->multi.id )) { + if (errno == EINTR) + continue; + + D_PERROR( "FUSION_SKIRMISH_NEW" ); + return DR_FUSION; + } + + D_DEBUG_AT( Fusion_Skirmish, " -> new skirmish %p [%d]\n", skirmish, skirmish->multi.id ); + + info.type = FT_SKIRMISH; + info.id = skirmish->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. */ + skirmish->multi.shared = world->shared; + + return DR_OK; +} + +DirectResult +fusion_skirmish_prevail( FusionSkirmish *skirmish ) +{ + D_ASSERT( skirmish != NULL ); + + while (ioctl (_fusion_fd( skirmish->multi.shared ), FUSION_SKIRMISH_PREVAIL, &skirmish->multi.id)) { + switch (errno) { + case EINTR: + continue; + + case EINVAL: + D_ERROR ("Fusion/Lock: invalid skirmish\n"); + return DR_DESTROYED; + } + + D_PERROR ("FUSION_SKIRMISH_PREVAIL"); + return DR_FUSION; + } + + return DR_OK; +} + +DirectResult +fusion_skirmish_swoop( FusionSkirmish *skirmish ) +{ + D_ASSERT( skirmish != NULL ); + + while (ioctl (_fusion_fd( skirmish->multi.shared ), FUSION_SKIRMISH_SWOOP, &skirmish->multi.id)) { + switch (errno) { + case EINTR: + continue; + + case EAGAIN: + return DR_BUSY; + + case EINVAL: + D_ERROR ("Fusion/Lock: invalid skirmish\n"); + return DR_DESTROYED; + } + + D_PERROR ("FUSION_SKIRMISH_SWOOP"); + return DR_FUSION; + } + + return DR_OK; +} + +DirectResult +fusion_skirmish_lock_count( FusionSkirmish *skirmish, int *lock_count ) +{ + int data[2]; + + D_ASSERT( skirmish != NULL ); + + data[0] = skirmish->multi.id; + data[1] = 0; + + while (ioctl (_fusion_fd( skirmish->multi.shared ), FUSION_SKIRMISH_LOCK_COUNT, data)) { + switch (errno) { + case EINTR: + continue; + + case EINVAL: + D_ERROR ("Fusion/Lock: invalid skirmish\n"); + return DR_DESTROYED; + } + + D_PERROR ("FUSION_SKIRMISH_LOCK_COUNT"); + return DR_FUSION; + } + + *lock_count = data[1]; + return DR_OK; +} + +DirectResult +fusion_skirmish_dismiss (FusionSkirmish *skirmish) +{ + D_ASSERT( skirmish != NULL ); + + while (ioctl (_fusion_fd( skirmish->multi.shared ), FUSION_SKIRMISH_DISMISS, &skirmish->multi.id)) { + switch (errno) { + case EINTR: + continue; + + case EINVAL: + D_ERROR ("Fusion/Lock: invalid skirmish\n"); + return DR_DESTROYED; + } + + D_PERROR ("FUSION_SKIRMISH_DISMISS"); + return DR_FUSION; + } + + return DR_OK; +} + +DirectResult +fusion_skirmish_destroy (FusionSkirmish *skirmish) +{ + D_ASSERT( skirmish != NULL ); + + D_DEBUG_AT( Fusion_Skirmish, "fusion_skirmish_destroy( %p [%d] )\n", skirmish, skirmish->multi.id ); + + while (ioctl( _fusion_fd( skirmish->multi.shared ), FUSION_SKIRMISH_DESTROY, &skirmish->multi.id )) { + switch (errno) { + case EINTR: + continue; + + case EINVAL: + D_ERROR ("Fusion/Lock: invalid skirmish\n"); + return DR_DESTROYED; + } + + D_PERROR ("FUSION_SKIRMISH_DESTROY"); + return DR_FUSION; + } + + return DR_OK; +} + +DirectResult +fusion_skirmish_wait( FusionSkirmish *skirmish, unsigned int timeout ) +{ + FusionSkirmishWait wait; + + D_ASSERT( skirmish != NULL ); + + wait.id = skirmish->multi.id; + wait.timeout = timeout; + wait.lock_count = 0; + + while (ioctl (_fusion_fd( skirmish->multi.shared ), FUSION_SKIRMISH_WAIT, &wait)) { + switch (errno) { + case EINTR: + continue; + + case ETIMEDOUT: + return DR_TIMEOUT; + + case EINVAL: + D_ERROR ("Fusion/Lock: invalid skirmish\n"); + return DR_DESTROYED; + } + + D_PERROR ("FUSION_SKIRMISH_WAIT"); + return DR_FUSION; + } + + return DR_OK; +} + +DirectResult +fusion_skirmish_notify( FusionSkirmish *skirmish ) +{ + D_ASSERT( skirmish != NULL ); + + while (ioctl (_fusion_fd( skirmish->multi.shared ), FUSION_SKIRMISH_NOTIFY, &skirmish->multi.id)) { + switch (errno) { + case EINTR: + continue; + + case EINVAL: + D_ERROR ("Fusion/Lock: invalid skirmish\n"); + return DR_DESTROYED; + } + + D_PERROR ("FUSION_SKIRMISH_NOTIFY"); + return DR_FUSION; + } + + return DR_OK; +} + +#else /* FUSION_BUILD_KERNEL */ + +#include +#include +#include + +typedef struct { + DirectLink link; + + pid_t pid; + bool notified; +} WaitNode; + + +DirectResult +fusion_skirmish_init( FusionSkirmish *skirmish, + const char *name, + const FusionWorld *world ) +{ + D_ASSERT( skirmish != NULL ); + //D_ASSERT( name != NULL ); + D_MAGIC_ASSERT( world, FusionWorld ); + + D_DEBUG_AT( Fusion_Skirmish, "fusion_skirmish_init( %p, '%s' )\n", + skirmish, name ? : "" ); + + skirmish->multi.id = ++world->shared->lock_ids; + + /* Set state to unlocked. */ + skirmish->multi.builtin.locked = 0; + skirmish->multi.builtin.owner = 0; + + skirmish->multi.builtin.waiting = NULL; + + skirmish->multi.builtin.requested = false; + skirmish->multi.builtin.destroyed = false; + + /* Keep back pointer to shared world data. */ + skirmish->multi.shared = world->shared; + + return DR_OK; +} + +DirectResult +fusion_skirmish_prevail( FusionSkirmish *skirmish ) +{ + D_ASSERT( skirmish != NULL ); + + if (skirmish->multi.builtin.destroyed) + return DR_DESTROYED; + + asm( "" ::: "memory" ); + + if (skirmish->multi.builtin.locked && + skirmish->multi.builtin.owner != direct_gettid()) + { + int count = 0; + + while (skirmish->multi.builtin.locked) { + /* Check whether owner exited without unlocking. */ + if (kill( skirmish->multi.builtin.owner, 0 ) < 0 && errno == ESRCH) { + skirmish->multi.builtin.locked = 0; + skirmish->multi.builtin.requested = false; + break; + } + + skirmish->multi.builtin.requested = true; + + asm( "" ::: "memory" ); + + if (++count > 1000) { + usleep( 10000 ); + count = 0; + } + else { + direct_sched_yield(); + } + + if (skirmish->multi.builtin.destroyed) + return DR_DESTROYED; + } + } + + skirmish->multi.builtin.locked++; + skirmish->multi.builtin.owner = direct_gettid(); + + asm( "" ::: "memory" ); + + return DR_OK; +} + +DirectResult +fusion_skirmish_swoop( FusionSkirmish *skirmish ) +{ + D_ASSERT( skirmish != NULL ); + + if (skirmish->multi.builtin.destroyed) + return DR_DESTROYED; + + asm( "" ::: "memory" ); + + if (skirmish->multi.builtin.locked && + skirmish->multi.builtin.owner != direct_gettid()) { + /* Check whether owner exited without unlocking. */ + if (kill( skirmish->multi.builtin.owner, 0 ) < 0 && errno == ESRCH) { + skirmish->multi.builtin.locked = 0; + skirmish->multi.builtin.requested = false; + } + else + return DR_BUSY; + } + + skirmish->multi.builtin.locked++; + skirmish->multi.builtin.owner = direct_gettid(); + + asm( "" ::: "memory" ); + + return DR_OK; +} + +DirectResult +fusion_skirmish_lock_count( FusionSkirmish *skirmish, int *lock_count ) +{ + D_ASSERT( skirmish != NULL ); + + if (skirmish->multi.builtin.destroyed) { + *lock_count = 0; + return DR_DESTROYED; + } + + *lock_count = skirmish->multi.builtin.locked; + + return DR_OK; +} + +DirectResult +fusion_skirmish_dismiss (FusionSkirmish *skirmish) +{ + D_ASSERT( skirmish != NULL ); + + if (skirmish->multi.builtin.destroyed) + return DR_DESTROYED; + + asm( "" ::: "memory" ); + + if (skirmish->multi.builtin.locked) { + if (skirmish->multi.builtin.owner != direct_gettid()) { + D_ERROR( "Fusion/Skirmish: " + "Tried to dismiss a skirmish not owned by current process!\n" ); + return DR_ACCESSDENIED; + } + + if (--skirmish->multi.builtin.locked == 0) { + skirmish->multi.builtin.owner = 0; + + if (skirmish->multi.builtin.requested) { + skirmish->multi.builtin.requested = false; + direct_sched_yield(); + } + } + } + + asm( "" ::: "memory" ); + + return DR_OK; +} + +DirectResult +fusion_skirmish_destroy (FusionSkirmish *skirmish) +{ + D_ASSERT( skirmish != NULL ); + + D_DEBUG_AT( Fusion_Skirmish, "fusion_skirmish_destroy( %p )\n", skirmish ); + + if (skirmish->multi.builtin.destroyed) + return DR_DESTROYED; + + if (skirmish->multi.builtin.waiting) + fusion_skirmish_notify( skirmish ); + + skirmish->multi.builtin.destroyed = true; + + return DR_OK; +} + +#ifdef SIGRTMAX +# define SIGRESTART SIGRTMAX +#else +# define SIGRESTART SIGCONT +#endif + +static void restart_handler( int s ) {} + +DirectResult +fusion_skirmish_wait( FusionSkirmish *skirmish, unsigned int timeout ) +{ + WaitNode *node; + long long stop; + struct sigaction act, oldact; + sigset_t mask, set; + DirectResult ret = DR_OK; + + D_ASSERT( skirmish != NULL ); + + if (skirmish->multi.builtin.destroyed) + return DR_DESTROYED; + + /* Set timeout. */ + stop = direct_clock_get_micros() + timeout * 1000ll; + + /* Add ourself to the list of waiting processes. */ + node = SHMALLOC( skirmish->multi.shared->main_pool, sizeof(WaitNode) ); + if (!node) + return D_OOSHM(); + + node->pid = direct_gettid(); + node->notified = false; + + direct_list_append( &skirmish->multi.builtin.waiting, &node->link ); + + /* Install a (fake) signal handler for SIGRESTART. */ + act.sa_handler = restart_handler; + act.sa_flags = SA_RESETHAND | SA_RESTART | SA_NOMASK; + + sigaction( SIGRESTART, &act, &oldact ); + + /* Unblock SIGRESTART. */ + sigprocmask( SIG_SETMASK, NULL, &mask ); + sigdelset( &mask, SIGRESTART ); + + fusion_skirmish_dismiss( skirmish ); + + while (!node->notified) { + if (timeout) { + long long now = direct_clock_get_micros(); + + if (now >= stop) { + /* Stop notifying us. */ + node->notified = true; + ret = DR_TIMEOUT; + break; + } + + sigprocmask( SIG_SETMASK, &mask, &set ); + usleep( stop - now ); + sigprocmask( SIG_SETMASK, &set, NULL ); + } + else { + sigsuspend( &mask ); + } + } + + /* Flush pending signals. */ + if (!sigpending( &set ) && sigismember( &set, SIGRESTART ) > 0) + sigsuspend( &mask ); + + if (fusion_skirmish_prevail( skirmish )) + ret = DR_DESTROYED; + + direct_list_remove( &skirmish->multi.builtin.waiting, &node->link ); + + SHFREE( skirmish->multi.shared->main_pool, node ); + + sigaction( SIGRESTART, &oldact, NULL ); + + return ret; +} + +DirectResult +fusion_skirmish_notify( FusionSkirmish *skirmish ) +{ + WaitNode *node, *temp; + + D_ASSERT( skirmish != NULL ); + + if (skirmish->multi.builtin.destroyed) + return DR_DESTROYED; + + direct_list_foreach_safe (node, temp, skirmish->multi.builtin.waiting) { + if (node->notified) + continue; + + node->notified = true; + + if (kill( node->pid, SIGRESTART ) < 0) { + if (errno == ESRCH) { + /* Remove dead process. */ + direct_list_remove( &skirmish->multi.builtin.waiting, &node->link ); + SHFREE( skirmish->multi.shared->main_pool, node ); + } + else { + D_PERROR( "Fusion/Skirmish: Couldn't send notification signal!\n" ); + } + } + } + + return DR_OK; +} + +#endif /* FUSION_BUILD_KERNEL */ + +#else /* FUSION_BUILD_MULTI */ + +DirectResult +fusion_skirmish_init( FusionSkirmish *skirmish, + const char *name, + const FusionWorld *world ) +{ + D_ASSERT( skirmish != NULL ); + + direct_util_recursive_pthread_mutex_init( &skirmish->single.lock ); + pthread_cond_init( &skirmish->single.cond, NULL ); + + return DR_OK; +} + +DirectResult +fusion_skirmish_prevail (FusionSkirmish *skirmish) +{ + D_ASSERT( skirmish != NULL ); + + if (pthread_mutex_lock( &skirmish->single.lock )) + return errno2result( errno ); + + skirmish->single.count++; + + return DR_OK; +} + +DirectResult +fusion_skirmish_swoop (FusionSkirmish *skirmish) +{ + D_ASSERT( skirmish != NULL ); + + if (pthread_mutex_trylock( &skirmish->single.lock )) + return errno2result( errno ); + + skirmish->single.count++; + + return DR_OK; +} + +DirectResult +fusion_skirmish_lock_count( FusionSkirmish *skirmish, int *lock_count ) +{ + D_ASSERT( skirmish != NULL ); + D_ASSERT( lock_count != NULL ); + + if (pthread_mutex_trylock( &skirmish->single.lock )) { + *lock_count = 0; + return errno2result( errno ); + } + + *lock_count = skirmish->single.count; + + pthread_mutex_unlock( &skirmish->single.lock ); + + return DR_OK; +} + +DirectResult +fusion_skirmish_dismiss (FusionSkirmish *skirmish) +{ + D_ASSERT( skirmish != NULL ); + + skirmish->single.count--; + + if (pthread_mutex_unlock( &skirmish->single.lock )) + return errno2result( errno ); + + return DR_OK; +} + +DirectResult +fusion_skirmish_destroy (FusionSkirmish *skirmish) +{ + D_ASSERT( skirmish != NULL ); + + pthread_cond_broadcast( &skirmish->single.cond ); + pthread_cond_destroy( &skirmish->single.cond ); + + return pthread_mutex_destroy( &skirmish->single.lock ); +} + +DirectResult +fusion_skirmish_wait( FusionSkirmish *skirmish, unsigned int timeout ) +{ + D_ASSERT( skirmish != NULL ); + + if (timeout) { + struct timespec ts; + struct timeval tv; + int ret; + + gettimeofday( &tv, NULL ); + + ts.tv_nsec = tv.tv_usec*1000 + (timeout%1000)*1000000; + ts.tv_sec = tv.tv_sec + timeout/1000 + ts.tv_nsec/1000000000; + ts.tv_nsec = ts.tv_nsec % 1000000000; + + ret = pthread_cond_timedwait( &skirmish->single.cond, + &skirmish->single.lock, &ts ); + + return (ret == ETIMEDOUT) ? DR_TIMEOUT : DR_OK; + } + + return pthread_cond_wait( &skirmish->single.cond, &skirmish->single.lock ); +} + +DirectResult +fusion_skirmish_notify( FusionSkirmish *skirmish ) +{ + D_ASSERT( skirmish != NULL ); + + pthread_cond_broadcast( &skirmish->single.cond ); + + return DR_OK; +} + +#endif + -- cgit