Mailing Lists: Apple Mailing Lists
Image of Mac OS face in stamp
re: WaitForMultipleObjects emulation with pthreads
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

re: WaitForMultipleObjects emulation with pthreads



On Sun, 12 Aug 2007 20:50:23 +0300, Eyal Redler <email@hidden>
wrote:
> I'm currently porting a Windows application to the mac. The
> application relies on Event Objects (both automatic and manual) that
> are passed to WaitForMultipleObjects and I need to emulate this
> behavior on the mac. After investigating the documentation I came to
> realize that there's nothing in the Mac APIs (Cocoa, MP or pthreads)
> that can accurately map to the windows APIs even when considering
> that all the objects passed to WaitForMultipleObjects are of the same
> kind.

This is a very common request that we get from developers from that _other_
OS. As mentioned in Kai BrĂ¼ning's response boost seems to be the solution
favored by those developers that choose to ignore why event objects aren't a
good idea. ;-)

> So far, the only solution I can think of is implementing this myself
> using pthreads but before I do this I'm wondering whether there are
> any open source projects that did something similar. If not, I would
> appreciate any tips regarding the implementation of this functionality.

I assume that by pthreads you mean the POSIX threading APIs. If you have 32
or fewer events to wait on you might consider using MP event groups. If you
need to wait for more than 32 events then you can expand the follow code to
handle more (basically just make the fMPEventFlags field larger).

For anyone interested in seeing the POSIX equivalent code for most of the MP
API's I've got an Xcode project on my iDisk at:

    <http://homepage.mac.com/geowar1/.Public/Sample Code/MP2POSIX.zip>.

Sorry about the line wrap.

[BEGIN]

typedef struct MP2P_event_group_struct {
    MPOpaqueIDClass        fKind;            // == kOpaqueEventID
    pthread_mutex_t        fMutex;            // protects the data struct
    pthread_cond_t        fCondition;        // used for signaling
    int                    fWaiting;        // is anyone waiting?
    MPEventFlags        fMPEventFlags;
} MP2P_event_group_rec, *MP2P_event_group_ptr;

//*************************************************************************
//
// MP2P_CreateEvent( ioMPEventID )
//
// Purpose: Creates an event group
//
// Inputs:    ioMPEventID - address where to store the event group ID
//
// Returns:    OSStatus        - error code (0 == no error)
//
OSStatus MP2P_CreateEvent(    MPEventID * ioMPEventID )
{
    OSStatus result = paramErr;

    if ( ioMPEventID ) {
        *ioMPEventID = kInvalidID;

        MP2P_event_group_ptr tMP2P_event_group_ptr = ( MP2P_event_group_ptr
) calloc( sizeof( MP2P_event_group_rec ), 1 );
        if ( tMP2P_event_group_ptr ) {
            tMP2P_event_group_ptr->fKind = kOpaqueEventID;
            int error = pthread_mutex_init( &tMP2P_event_group_ptr->fMutex,
NULL );
            if ( !error ) {
                error = pthread_cond_init(
&tMP2P_event_group_ptr->fCondition, NULL );
                if ( error ) {
                    (void) pthread_mutex_destroy(
&tMP2P_event_group_ptr->fMutex );
                }
            }
            result = P2MP_Error( error );    // translate POSIX error to MP
error

            if ( noErr == result ) {
                tMP2P_event_group_ptr->fMPEventFlags = 0L;
                *ioMPEventID = ( MPEventID ) tMP2P_event_group_ptr;
            }
        } else {
            result = memFullErr;
        }
    }
    return result;
}    // MP2P_CreateEvent

//*************************************************************************
//
// MP2P_DeleteEvent( inMPEventID )
//
// Purpose: Removes an event group
//
// Inputs:    inMPEventID - the event group to be released
//
// Returns:    OSStatus        - error code (0 == no error)
//
OSStatus MP2P_DeleteEvent( MPEventID inMPEventID )
{
    MP2P_event_group_ptr tMP2P_event_group_ptr = ( MP2P_event_group_ptr )
inMPEventID;
    OSStatus result = kMPInvalidIDErr;

    if ( tMP2P_event_group_ptr && ( kOpaqueEventID ==
tMP2P_event_group_ptr->fKind ) ) {
        int error = pthread_mutex_destroy( &tMP2P_event_group_ptr->fMutex );
        result = P2MP_Error( error );    // translate POSIX error to MP
error

        error = pthread_cond_destroy( &tMP2P_event_group_ptr->fCondition );
        if ( noErr == result ) {
            result = P2MP_Error( error );    // translate POSIX error to MP
error
        }

        free( ( void * ) tMP2P_event_group_ptr );
    }
    return result;
}    // MP2P_DeleteEvent

//*************************************************************************
//
// MP2P_SetEvent( inMPEventID, inMPEventFlags )
//
// Purpose: Merges event flags into a specified event group
//
// Inputs:    inMPEventID - the event group to be set
//            inMPEventFlags - the bits to set in the event group
//
// Returns:    OSStatus        - error code (0 == no error)
//
OSStatus MP2P_SetEvent( MPEventID inMPEventID, MPEventFlags inMPEventFlags )
{
    MP2P_event_group_ptr tMP2P_event_group_ptr = ( MP2P_event_group_ptr )
inMPEventID;
    OSStatus result = kMPInvalidIDErr;

    if ( tMP2P_event_group_ptr && ( kOpaqueEventID ==
tMP2P_event_group_ptr->fKind ) ) {
        // lock the mutex
        int error = pthread_mutex_lock( &tMP2P_event_group_ptr->fMutex );
        result = P2MP_Error( error );    // translate POSIX error to MP
error
        if ( noErr == result ) {
            // we only need to broadcast if event flags are non-zero
            if ( inMPEventFlags != ( inMPEventFlags &
tMP2P_event_group_ptr->fMPEventFlags ) ) {
                result = noErr;    // assume success!
                tMP2P_event_group_ptr->fMPEventFlags |= inMPEventFlags;
                // we only need to broadcast if someone's waiting
                if ( tMP2P_event_group_ptr->fWaiting ) {
                    tMP2P_event_group_ptr->fWaiting = FALSE;
                    error = pthread_cond_broadcast(
&tMP2P_event_group_ptr->fCondition );
                    result = P2MP_Error( error );    // translate POSIX
error to MP error
                }
            }
            // release the mutex
            error = pthread_mutex_unlock( &tMP2P_event_group_ptr->fMutex );
            if ( noErr == result ) {
                result = P2MP_Error( error );    // translate POSIX error to
MP error
            }
        }
    }
    return result;
}    // MP2P_SetEvent

//*************************************************************************
//
// MP2P_WaitForEvent( inMPEventID, ioMPEventFlags, inTimeout )
//
// Purpose: Retrieves event flags from a specified event group
//
// Inputs:    inMPEventID - the event group to wait on
//            ioMPEventFlags - the address where to store the flags from the
event group
//            inTimeout - the duration to wait
//
// Returns:    OSStatus        - error code (0 == no error)
//
OSStatus MP2P_WaitForEvent( MPEventID inMPEventID, MPEventFlags *
ioMPEventFlags, Duration inTimeout )
{
    MP2P_event_group_ptr tMP2P_event_group_ptr = ( MP2P_event_group_ptr )
inMPEventID;
    OSStatus result = kMPInvalidIDErr;

    if ( tMP2P_event_group_ptr && ( kOpaqueEventID ==
tMP2P_event_group_ptr->fKind ) ) {
        // lock the mutex
        int error = pthread_mutex_lock( &tMP2P_event_group_ptr->fMutex );
        result = P2MP_Error( error );    // translate POSIX error to MP
error
        if ( noErr == result ) {
            switch ( inTimeout ) {
                case kDurationImmediate: {
                    if ( tMP2P_event_group_ptr->fMPEventFlags ) {
                        result = noErr;
                    } else {
                        result = kMPTimeoutErr;
                    }
                    break;
                }
                case kDurationForever: {
                    while ( !error && !tMP2P_event_group_ptr->fMPEventFlags
) {
                        tMP2P_event_group_ptr->fWaiting = TRUE;
                        error = pthread_cond_wait(
&tMP2P_event_group_ptr->fCondition, &tMP2P_event_group_ptr->fMutex );
                    }
                    result = P2MP_Error( error );    // translate POSIX
error to MP error
                    break;
                }
                default: {
                    struct timespec tval;
                    ConvertMPDurationToTimespec( inTimeout, &tval );

                    while ( !error && !tMP2P_event_group_ptr->fMPEventFlags
) {
                        tMP2P_event_group_ptr->fWaiting = TRUE;
                        error = pthread_cond_timedwait(
&tMP2P_event_group_ptr->fCondition, &tMP2P_event_group_ptr->fMutex, &tval );
                    }
                    result = P2MP_Error( error );    // translate POSIX
error to MP error
                    break;
                }
            }
            if ( noErr == result ) {
                if ( ioMPEventFlags ) {
                    *ioMPEventFlags = tMP2P_event_group_ptr->fMPEventFlags;
                }
                tMP2P_event_group_ptr->fMPEventFlags = 0L;
            }
            // release the mutex
            error = pthread_mutex_unlock( &tMP2P_event_group_ptr->fMutex );
            if ( noErr == result ) {
                result = P2MP_Error( error );    // translate POSIX error to
MP error
            }
        }
    }
    return result;
}    // MP2P_WaitForEvent


//*************************************************************************
//
// P2MP_Error( inPosixErr )
//
// Purpose: translate POSIX error to MP error
//
// Inputs:    inPosixErr    - the POSIX error
//
// Returns: OSStatus - the MP error
//

static OSStatus P2MP_Error(int inPosixErr )
{
    OSStatus result = noErr;

    switch ( inPosixErr ) {
        case EINVAL: {    // a parameter is invalid
            result = paramErr;
            break;
        }
        case ENOMEM: {    // The process cannot allocate enough memory to
create another mutex or condition variable
            result = memFullErr;
            break;
        }
        case EAGAIN: {    // The system temporarily lacks the resources to
create another mutex or condition variable
            result = kMPInsufficientResourcesErr;
            break;
        }
        case ETIMEDOUT: {    // the operation timed out
            result = kMPTimeoutErr;
            break;
        }
        case EDEADLK: {    // A deadlock would occur if the thread blocked
waiting for mutex
            result = kMPTaskBlockedErr;
            break;
        }
        case EBUSY: {    // This resource is locked by another thread
            result = kMPInsufficientResourcesErr;
        }
        default: {
            result = inPosixErr;    // unknown error
        }
    }
    return result;
}    // P2MP_Error

//*************************************************************************
//
// ConvertMPDurationToTimespec( inDuration, ioTimespec)
//
// Purpose: convert a MP duration to a timespec
//
// Inputs:    inDuration    - the duration
//            ioTimespec    - the address where to store the output timespec
//
// Outputs:    *ioTimespec    - the timespec
//
// Returns: none
//

static void ConvertMPDurationToTimespec( Duration inDuration, struct
timespec * ioTimespec )
{
    if ( ioTimespec ) {
        switch ( inDuration ) {
            case kDurationImmediate: {
                ioTimespec->tv_sec = 0;
                ioTimespec->tv_nsec = 0;
                break;
            }
            case kDurationForever: {
                ioTimespec->tv_sec = LONG_MAX;
                ioTimespec->tv_nsec = LONG_MAX;
                break;
            }
            default: {
                // Convert the duration to nanoseconds
                UInt64 durationInNS = UnsignedWideToUInt64(
DurationToNanoseconds( inDuration ) );

                // Get the current time as a timespec
                struct timeval tval;
                struct timezone junkTZ;

                int junk = gettimeofday( &tval, &junkTZ );
                assert( 0 == junk );
                struct timespec now;
                TIMEVAL_TO_TIMESPEC( &tval, &now );

                // Add the duration to the current time
                ioTimespec->tv_sec = now.tv_sec +  ( durationInNS /
1000000000 );
                ioTimespec->tv_nsec = now.tv_nsec + ( durationInNS %
1000000000 );
                if ( ioTimespec->tv_nsec > 1000000000 ) {
                    ioTimespec->tv_sec += 1;
                    ioTimespec->tv_nsec -= 1000000000;
                }
                break;
            }
        }
    }
}    // ConvertMPDurationToTimespec

[END]
--
Enjoy,
George Warner,
Schizophrenic Optimization Scientist
Apple Developer Technical Support (DTS)


 _______________________________________________
Do not post admin requests to the list. They will be ignored.
Mt-smp mailing list      (email@hidden)
Help/Unsubscribe/Update your Subscription:

This email sent to email@hidden



Visit the Apple Store online or at retail locations.
1-800-MY-APPLE

Contact Apple | Terms of Use | Privacy Policy

Copyright © 2011 Apple Inc. All rights reserved.