mirror of
https://github.com/transmission/transmission
synced 2024-12-25 09:13:06 +00:00
fix very annoying recursive mutex + pthread cond bug reported by setatg, Waldorf, and many others
This commit is contained in:
parent
7b068ed926
commit
c68af92039
3 changed files with 5 additions and 202 deletions
|
@ -109,7 +109,6 @@ struct tr_fd_s
|
|||
int normal;
|
||||
int normalMax;
|
||||
tr_lock * lock;
|
||||
tr_cond * cond;
|
||||
struct tr_openfile open[TR_MAX_OPEN_FILES];
|
||||
};
|
||||
|
||||
|
@ -180,7 +179,6 @@ TrCloseFile( int i )
|
|||
close( o->fd );
|
||||
o->fd = -1;
|
||||
o->isCheckedOut = 0;
|
||||
tr_condSignal( gFd->cond );
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -215,7 +213,7 @@ tr_fdFileCheckout( const char * filename, int write )
|
|||
|
||||
if( fileIsCheckedOut( o ) ) {
|
||||
dbgmsg( "found it! it's open, but checked out. waiting..." );
|
||||
tr_condWait( gFd->cond, gFd->lock );
|
||||
tr_wait( 100 );
|
||||
i = -1; /* reloop */
|
||||
continue;
|
||||
}
|
||||
|
@ -262,7 +260,7 @@ tr_fdFileCheckout( const char * filename, int write )
|
|||
|
||||
/* All used! Wait a bit and try again */
|
||||
dbgmsg( "everything's full! waiting for someone else to finish something" );
|
||||
tr_condWait( gFd->cond, gFd->lock );
|
||||
tr_wait( 100 );
|
||||
}
|
||||
|
||||
done:
|
||||
|
@ -309,7 +307,6 @@ tr_fdFileReturn( int fd )
|
|||
break;
|
||||
}
|
||||
|
||||
tr_condSignal( gFd->cond );
|
||||
tr_lockUnlock( gFd->lock );
|
||||
}
|
||||
|
||||
|
@ -335,7 +332,6 @@ tr_fdFileClose( const char * filename )
|
|||
}
|
||||
}
|
||||
|
||||
tr_condSignal( gFd->cond );
|
||||
tr_lockUnlock( gFd->lock );
|
||||
}
|
||||
|
||||
|
@ -462,7 +458,6 @@ tr_fdInit( void )
|
|||
|
||||
gFd = tr_new0( struct tr_fd_s, 1 );
|
||||
gFd->lock = tr_lockNew( );
|
||||
gFd->cond = tr_condNew( );
|
||||
|
||||
/* count the max number of sockets we can use */
|
||||
for( i=0; i<TR_MAX_SOCKETS; ++i )
|
||||
|
@ -490,7 +485,6 @@ tr_fdClose( void )
|
|||
TrCloseFile( i );
|
||||
|
||||
tr_lockFree( gFd->lock );
|
||||
tr_condFree( gFd->cond );
|
||||
|
||||
tr_list_free( &reservedSockets, NULL );
|
||||
tr_free( gFd );
|
||||
|
|
|
@ -248,10 +248,11 @@ tr_lockLock( tr_lock * l )
|
|||
#else
|
||||
pthread_mutex_lock( &l->lock );
|
||||
#endif
|
||||
assert( l->depth >= 0 );
|
||||
if( l->depth )
|
||||
assert( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread() ) );
|
||||
l->lockThread = tr_getCurrentThread( );
|
||||
++l->depth;
|
||||
/* fprintf( stderr, "thread %lu acquired lock %p... depth is now %d\n", (unsigned long)l->lockThread, l, l->depth ); */
|
||||
assert( l->depth >= 1 );
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -264,11 +265,8 @@ tr_lockHave( const tr_lock * l )
|
|||
void
|
||||
tr_lockUnlock( tr_lock * l )
|
||||
{
|
||||
/* fprintf( stderr, "thread %lu releasing lock %p... depth before release is %d\n", (unsigned long)l->lockThread, l, l->depth ); */
|
||||
|
||||
assert( l->depth > 0 );
|
||||
assert( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread() ));
|
||||
assert( tr_lockHave( l ) );
|
||||
|
||||
--l->depth;
|
||||
assert( l->depth >= 0 );
|
||||
|
@ -281,188 +279,6 @@ tr_lockUnlock( tr_lock * l )
|
|||
#endif
|
||||
}
|
||||
|
||||
/***
|
||||
**** COND
|
||||
***/
|
||||
|
||||
struct tr_cond
|
||||
{
|
||||
#ifdef __BEOS__
|
||||
sem_id sem;
|
||||
thread_id threads[BEOS_MAX_THREADS];
|
||||
int start, end;
|
||||
#elif defined(WIN32)
|
||||
tr_list * events;
|
||||
tr_lock * lock;
|
||||
#else
|
||||
pthread_cond_t cond;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef WIN32
|
||||
static DWORD getContEventTLS( void )
|
||||
{
|
||||
static int inited = FALSE;
|
||||
static DWORD event_tls;
|
||||
if( !inited ) {
|
||||
inited = TRUE;
|
||||
event_tls = TlsAlloc();
|
||||
}
|
||||
return event_tls;
|
||||
}
|
||||
#endif
|
||||
|
||||
tr_cond*
|
||||
tr_condNew( void )
|
||||
{
|
||||
tr_cond * c = tr_new0( tr_cond, 1 );
|
||||
#ifdef __BEOS__
|
||||
c->sem = create_sem( 1, "" );
|
||||
c->start = 0;
|
||||
c->end = 0;
|
||||
#elif defined(WIN32)
|
||||
c->events = NULL;
|
||||
c->lock = tr_lockNew( );
|
||||
#else
|
||||
pthread_cond_init( &c->cond, NULL );
|
||||
#endif
|
||||
return c;
|
||||
}
|
||||
|
||||
void
|
||||
tr_condWait( tr_cond * c, tr_lock * l )
|
||||
{
|
||||
#ifdef __BEOS__
|
||||
|
||||
/* Keep track of that thread */
|
||||
acquire_sem( c->sem );
|
||||
c->threads[c->end] = find_thread( NULL );
|
||||
c->end = ( c->end + 1 ) % BEOS_MAX_THREADS;
|
||||
assert( c->end != c->start ); /* We hit BEOS_MAX_THREADS, arggh */
|
||||
release_sem( c->sem );
|
||||
|
||||
release_sem( l->lock );
|
||||
suspend_thread( find_thread( NULL ) ); /* Wait for signal */
|
||||
acquire_sem( l->lock );
|
||||
|
||||
#elif defined(WIN32)
|
||||
|
||||
/* get this thread's cond event */
|
||||
DWORD key = getContEventTLS ( );
|
||||
HANDLE hEvent = TlsGetValue( key );
|
||||
if( !hEvent ) {
|
||||
hEvent = CreateEvent( 0, FALSE, FALSE, 0 );
|
||||
TlsSetValue( key, hEvent );
|
||||
}
|
||||
|
||||
/* add it to the list of events waiting to be signaled */
|
||||
tr_lockLock( c->lock );
|
||||
tr_list_append( &c->events, hEvent );
|
||||
tr_lockUnlock( c->lock );
|
||||
|
||||
/* now wait for it to be signaled */
|
||||
tr_lockUnlock( l );
|
||||
WaitForSingleObject( hEvent, INFINITE );
|
||||
tr_lockLock( l );
|
||||
|
||||
/* remove it from the list of events waiting to be signaled */
|
||||
tr_lockLock( c->lock );
|
||||
tr_list_remove_data( &c->events, hEvent );
|
||||
tr_lockUnlock( c->lock );
|
||||
|
||||
#else
|
||||
|
||||
pthread_cond_wait( &c->cond, &l->lock );
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef __BEOS__
|
||||
static int condTrySignal( tr_cond * c )
|
||||
{
|
||||
if( c->start == c->end )
|
||||
return 1;
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
thread_info info;
|
||||
get_thread_info( c->threads[c->start], &info );
|
||||
if( info.state == B_THREAD_SUSPENDED )
|
||||
{
|
||||
resume_thread( c->threads[c->start] );
|
||||
c->start = ( c->start + 1 ) % BEOS_MAX_THREADS;
|
||||
break;
|
||||
}
|
||||
/* The thread is not suspended yet, which can happen since
|
||||
* tr_condWait does not atomically suspends after releasing
|
||||
* the semaphore. Wait a bit and try again. */
|
||||
snooze( 5000 );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
void
|
||||
tr_condSignal( tr_cond * c )
|
||||
{
|
||||
#ifdef __BEOS__
|
||||
|
||||
acquire_sem( c->sem );
|
||||
condTrySignal( c );
|
||||
release_sem( c->sem );
|
||||
|
||||
#elif defined(WIN32)
|
||||
|
||||
tr_lockLock( c->lock );
|
||||
if( c->events != NULL )
|
||||
SetEvent( (HANDLE)c->events->data );
|
||||
tr_lockUnlock( c->lock );
|
||||
|
||||
#else
|
||||
|
||||
pthread_cond_signal( &c->cond );
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
tr_condBroadcast( tr_cond * c )
|
||||
{
|
||||
#ifdef __BEOS__
|
||||
|
||||
acquire_sem( c->sem );
|
||||
while( !condTrySignal( c ) );
|
||||
release_sem( c->sem );
|
||||
|
||||
#elif defined(WIN32)
|
||||
|
||||
tr_list * l;
|
||||
tr_lockLock( c->lock );
|
||||
for( l=c->events; l!=NULL; l=l->next )
|
||||
SetEvent( (HANDLE)l->data );
|
||||
tr_lockUnlock( c->lock );
|
||||
|
||||
#else
|
||||
|
||||
pthread_cond_broadcast( &c->cond );
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
tr_condFree( tr_cond * c )
|
||||
{
|
||||
#ifdef __BEOS__
|
||||
delete_sem( c->sem );
|
||||
#elif defined(WIN32)
|
||||
tr_list_free( &c->events, NULL );
|
||||
tr_lockFree( c->lock );
|
||||
#else
|
||||
pthread_cond_destroy( &c->cond );
|
||||
#endif
|
||||
tr_free( c );
|
||||
}
|
||||
|
||||
|
||||
/***
|
||||
**** PATHS
|
||||
***/
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
#define TR_PLATFORM_H
|
||||
|
||||
typedef struct tr_lock tr_lock;
|
||||
typedef struct tr_cond tr_cond;
|
||||
typedef struct tr_thread tr_thread;
|
||||
|
||||
const char * tr_getHomeDirectory( void );
|
||||
|
@ -42,12 +41,6 @@ void tr_lockLock ( tr_lock * );
|
|||
void tr_lockUnlock ( tr_lock * );
|
||||
int tr_lockHave ( const tr_lock * );
|
||||
|
||||
tr_cond * tr_condNew ( void );
|
||||
void tr_condFree ( tr_cond * );
|
||||
void tr_condSignal ( tr_cond * );
|
||||
void tr_condBroadcast ( tr_cond * );
|
||||
void tr_condWait ( tr_cond *, tr_lock * );
|
||||
|
||||
struct in_addr; /* forward declaration to calm gcc down */
|
||||
int
|
||||
tr_getDefaultRoute( struct in_addr * addr );
|
||||
|
|
Loading…
Reference in a new issue