#include "DAThread.h"
#include <pthread.h>
#include <sysexits.h>
#include <mach/mach.h>
enum
{
__kDAThreadRunLoopSourceJobKindExecute = 0x00000001
};
typedef UInt32 __DAThreadRunLoopSourceJobKind;
struct __DAThreadRunLoopSourceJob
{
__DAThreadRunLoopSourceJobKind kind;
struct __DAThreadRunLoopSourceJob * next;
union
{
struct
{
Boolean exited;
int status;
pthread_t thread;
DAThreadExecuteCallback callback;
void * callbackContext;
DAThreadFunction function;
void * functionContext;
} execute;
};
};
typedef struct __DAThreadRunLoopSourceJob __DAThreadRunLoopSourceJob;
static __DAThreadRunLoopSourceJob * __gDAThreadRunLoopSourceJobs = NULL;
static pthread_mutex_t __gDAThreadRunLoopSourceLock = PTHREAD_MUTEX_INITIALIZER;
static CFMachPortRef __gDAThreadRunLoopSourcePort = NULL;
static void * __DAThreadFunction( void * context )
{
__DAThreadRunLoopSourceJob * job;
pthread_mutex_lock( &__gDAThreadRunLoopSourceLock );
for ( job = __gDAThreadRunLoopSourceJobs; job; job = job->next )
{
assert( job->kind == __kDAThreadRunLoopSourceJobKindExecute );
if ( pthread_equal( job->execute.thread, pthread_self( ) ) )
{
break;
}
}
pthread_mutex_unlock( &__gDAThreadRunLoopSourceLock );
if ( job )
{
mach_msg_header_t message;
job->execute.status = ( ( DAThreadFunction ) job->execute.function )( job->execute.functionContext );
pthread_mutex_lock( &__gDAThreadRunLoopSourceLock );
job->execute.exited = TRUE;
pthread_mutex_unlock( &__gDAThreadRunLoopSourceLock );
message.msgh_bits = MACH_MSGH_BITS( MACH_MSG_TYPE_COPY_SEND, 0 );
message.msgh_id = 0;
message.msgh_local_port = MACH_PORT_NULL;
message.msgh_remote_port = CFMachPortGetPort( __gDAThreadRunLoopSourcePort );
message.msgh_reserved = 0;
message.msgh_size = sizeof( message );
mach_msg( &message, MACH_SEND_MSG | MACH_SEND_TIMEOUT, message.msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL );
}
pthread_detach( pthread_self( ) );
return NULL;
}
static void __DAThreadRunLoopSourceCallback( CFMachPortRef port, void * message, CFIndex messageSize, void * info )
{
__DAThreadRunLoopSourceJob * job = NULL;
__DAThreadRunLoopSourceJob * jobLast = NULL;
pthread_mutex_lock( &__gDAThreadRunLoopSourceLock );
for ( job = __gDAThreadRunLoopSourceJobs; job; )
{
for ( job = __gDAThreadRunLoopSourceJobs; job; jobLast = job, job = job->next )
{
assert( job->kind == __kDAThreadRunLoopSourceJobKindExecute );
if ( job->execute.exited )
{
if ( jobLast )
{
jobLast->next = job->next;
}
else
{
__gDAThreadRunLoopSourceJobs = job->next;
}
pthread_mutex_unlock( &__gDAThreadRunLoopSourceLock );
if ( job->execute.callback )
{
( job->execute.callback )( job->execute.status, job->execute.callbackContext );
}
free( job );
pthread_mutex_lock( &__gDAThreadRunLoopSourceLock );
break;
}
}
}
pthread_mutex_unlock( &__gDAThreadRunLoopSourceLock );
}
CFRunLoopSourceRef DAThreadCreateRunLoopSource( CFAllocatorRef allocator, CFIndex order )
{
CFRunLoopSourceRef source = NULL;
if ( __gDAThreadRunLoopSourcePort == NULL )
{
__gDAThreadRunLoopSourcePort = CFMachPortCreate( kCFAllocatorDefault, __DAThreadRunLoopSourceCallback, NULL, NULL );
if ( __gDAThreadRunLoopSourcePort )
{
mach_port_limits_t limits = { 0 };
limits.mpl_qlimit = 1;
mach_port_set_attributes( mach_task_self( ),
CFMachPortGetPort( __gDAThreadRunLoopSourcePort ),
MACH_PORT_LIMITS_INFO,
( mach_port_info_t ) &limits,
MACH_PORT_LIMITS_INFO_COUNT );
}
}
if ( __gDAThreadRunLoopSourcePort )
{
source = CFMachPortCreateRunLoopSource( allocator, __gDAThreadRunLoopSourcePort, order );
}
return source;
}
void DAThreadExecute( DAThreadFunction function, void * functionContext, DAThreadExecuteCallback callback, void * callbackContext )
{
pthread_t thread;
int status;
assert( __gDAThreadRunLoopSourcePort );
pthread_mutex_lock( &__gDAThreadRunLoopSourceLock );
status = pthread_create( &thread, NULL, __DAThreadFunction, NULL );
if ( status == 0 )
{
__DAThreadRunLoopSourceJob * job;
job = malloc( sizeof( __DAThreadRunLoopSourceJob ) );
if ( job )
{
job->kind = __kDAThreadRunLoopSourceJobKindExecute;
job->next = __gDAThreadRunLoopSourceJobs;
job->execute.exited = FALSE;
job->execute.status = 0;
job->execute.thread = thread;
job->execute.callback = callback;
job->execute.callbackContext = callbackContext;
job->execute.function = function;
job->execute.functionContext = functionContext;
__gDAThreadRunLoopSourceJobs = job;
}
}
pthread_mutex_unlock( &__gDAThreadRunLoopSourceLock );
if ( status )
{
if ( callback )
{
( callback )( EX_OSERR, callbackContext );
}
}
}