home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / NEXTSTEP / Examples / ThreadedApp-1.0.1 / ThreadedApp.m < prev    next >
Encoding:
Text File  |  1997-04-03  |  19.7 KB  |  762 lines

  1. static char rcsid[] = "$Id: ThreadedApp.m,v 1.2 1997/04/03 20:09:26 croehrig Exp $";
  2. /*
  3.  *  ThreadedApp:   adds support for a multi-threaded application.
  4.  *  (c) 1997 Chris Roehrig <croehrig@House.ORG>
  5.  */     
  6. #import "ThreadedApp.h"
  7. #import <objc/objc-runtime.h>
  8.  
  9.  
  10. @implementation ThreadedApp : Application
  11.  
  12. // export global to identify the main thread...
  13. cthread_t mainThread;
  14.  
  15.  
  16. /*****************************************************************
  17.  * Basic Thread support
  18.  */
  19.  
  20. // simple "function control block" to invoke a method
  21. struct _fcb {
  22.     IMP    function;
  23.     id    target;
  24.     SEL    selector;
  25.     any_t    arg;
  26.     mutex_t    lock;
  27.     condition_t    cond;
  28.     volatile BOOL done;
  29. };
  30.  
  31.  
  32. static void method_dispatcher( struct _fcb *fcb )
  33. {
  34.     struct _fcb myfcb;
  35.  
  36.     // lock the fcb... 
  37.     mutex_lock( fcb->lock );
  38.  
  39.     // copy the args from parent's stack-based struct...
  40.     // (ignore the lock and cond; they are only copied by reference)
  41.     myfcb = *fcb;
  42.  
  43.     fcb->done =YES;
  44.     // let parent know it's ok to exit and destroy my args
  45.     condition_signal( fcb->cond );
  46.     mutex_unlock( fcb->lock );
  47.  
  48.     // call the method... 
  49.     myfcb.function( myfcb.target, myfcb.selector, myfcb.arg );
  50. }
  51.  
  52.  
  53. - (void)detachNewThreadSelector:(SEL)aSelector toTarget:(id)aTarget
  54.     withObject:(id)anArgument
  55. {
  56.     // allocate all data off stack...
  57.     struct _fcb fcb;
  58.     struct mutex m;
  59.     struct condition c;
  60.  
  61.     fcb.function = [aTarget methodFor:aSelector];
  62.     if( fcb.function ){
  63.     fcb.target = aTarget;
  64.     fcb.selector = aSelector;
  65.     fcb.arg = (any_t) anArgument;
  66.     mutex_init( &m );
  67.     fcb.lock = &m;
  68.     condition_init( &c );
  69.     fcb.cond = &c;
  70.     fcb.done = NO;
  71.     mutex_lock( &m );
  72.     cthread_detach(
  73.         cthread_fork((any_t(*)())method_dispatcher,(any_t)&fcb) );
  74.  
  75.     // wait for thread to copy its args before exiting and
  76.     // destroying them!
  77.     while( !fcb.done )
  78.         condition_wait( &c, &m );
  79.  
  80.     mutex_unlock( &m );
  81.     mutex_clear( &m );
  82.     condition_clear( &c );
  83.     }
  84. }
  85.  
  86.  
  87. - (void)exitCurrentThread
  88. {
  89.     cthread_exit(0);
  90. }
  91.  
  92.  
  93. - (void)sleepCurrentThread:(int)msec
  94. {
  95.     thread_switch(THREAD_NULL, SWITCH_OPTION_WAIT, msec);  
  96. }
  97.  
  98.  
  99.  
  100.  
  101. /*****************************************************************
  102.  * Callback support
  103.  *
  104.  *  Callbacks are done by sending the main AppKit thread a Mach message
  105.  *  requesting it to invoke a method.
  106.  *
  107.  *  The callback Mach message is faked as a simple message 
  108.  *  (i.e. no out-of-line data), since the message never crosses task/memory 
  109.  *  spaces, and so memory references are still valid.
  110.  *
  111.  *  The Mach message handler is invoked by the DPSGetEvent routine
  112.  *  called by the Application's event loop.  It monitors the port and
  113.  *  calls the handler when messages arrive on it.   If DPSGetEvent
  114.  *  (or DPSPeekEvent) is NOT being called, the messages will not be
  115.  *  handled.  The AppKit thread MUST remain responsive to events in order 
  116.  *  for messages to be handled.
  117.  *
  118.  *  For fast callbacks, the Mach message handler invokes the callback method
  119.  *  directly.  The DPSGetEvent routine is not reentrant and so the callback
  120.  *  method cannot consume events.   In particular, this means it cannot
  121.  *  use Locks since they use event loops (see Locks, below).   The callback
  122.  *  method can be blocking (wait) or non-blocking.
  123.  *
  124.  *  For safe and slow callbacks, the Mach message handler does not invoke the 
  125.  *  callback method directly.  Instead, it posts an (NX_APPDEFINED) 
  126.  *  EV_CALLBACK event to the Application's event queue which gets handled 
  127.  *  by the Application's applicationDefined: method.  This is to allow the 
  128.  *  called-back method to run modal event loops itself.  (In particular, this 
  129.  *  is necessary if the called-back method uses locks to communicate with 
  130.  *  other threads (see Locks, below).
  131.  *  The DPSGetEvent routine is not reentrant, so it is not possible for 
  132.  *  the message handler to eventually call DPSGetEvent again.  By posting
  133.  *  an event to the event queue, the message handler returns, DPSGetEvent
  134.  *  returns, and the EV_CALLBACK event is processed by the Application's 
  135.  *  event loop, as if it is any other event.  This allows the called-back
  136.  *  method to have full access to the AppKit's features and to run its 
  137.  *  own event loop if it desires.
  138.  *
  139.  *  Locks
  140.  *  It is possible for the main (AppKit) thread to use Locks and
  141.  *  ConditionLocks to synchronize with its other threads.  However, it
  142.  *  is forbidden for these Locks to block since that would prevent messages
  143.  *  from being received and may cause other threads to block waiting to
  144.  *  send messages to the main thread, potentially resulting in deadlock.
  145.  *  The AppKit thread MUST remain responsive to events!
  146.  * 
  147.  *  The CJRLock and CJRConditionLock objects handle the case where 
  148.  *  the main AppKit thread acquires a lock.   They do this by 
  149.  *  testing to see if they can acquire the lock (mutex_try_lock).  If
  150.  *  they can, they've succeeded.  If not, they start a modal loop getting
  151.  *  NX_APPDEFINED events, and looking for EV_UNLOCK events.  When one is
  152.  *  received, this indicates that another thread has unlocked a mutex.
  153.  *  and we try to acquire the lock again.  This loops until success.
  154.  *  This is a sort of non-blocking "block" since other events aren't
  155.  *  processed during the block (they remain on the queue, however).
  156.  *  The CJRLock's unlock methods send the required EV_UNLOCK messages.
  157.  *
  158.  *  In addition, EV_CALLBACK events are continued to be processed during
  159.  *  this "blocking" modal loop.   This has some bizarre consequences:
  160.  *  Another callback method can be dispatched and preempt a callback method
  161.  *  that is currently blocked waiting for lock to come free.  This
  162.  *  preemptive callback method can now also "block" waiting for a lock.
  163.  *  The ThreadedApp's lock methods are re-entrant, so this doesn't pose
  164.  *  a problem.   As in all threaded programming, you should not rely
  165.  *  on the order that callbacks might occur.
  166.  *
  167.  */
  168.  
  169. // data for wait callbacks
  170. struct waitdata {
  171.     struct mutex    lock;     // statically allocated
  172.     struct condition    cond;
  173.     volatile id        retval;
  174.     volatile BOOL    done;
  175. };
  176.  
  177. // thresholds for messages
  178. #define FAST_THRESHOLD    (NX_MODALRESPTHRESHOLD+1)
  179. #define BASE_THRESHOLD    NX_BASETHRESHOLD
  180.  
  181. // types and options for our Mach messages and events...
  182. //     message/event type (0-255)...
  183. #define EV_UNLOCK        1    // threadDidUnlock event
  184. #define EV_CALLBACK        2    // callback event
  185. //     message/event options (bit set)...
  186. #define EV_CALLBACK_ARG        0x0100    // callback method takes an arg
  187. #define EV_CALLBACK_WAIT    0x0200    // block and wait for method to return
  188. #define EV_CALLBACK_POST    0x0400    // post the callback as an event
  189.  
  190. // callback message...
  191. #define CALLBACK_DATA    4
  192. typedef struct {
  193.     msg_header_t    h;
  194.     msg_type_t        t;
  195.     id            target;
  196.     SEL            sel;
  197.     id            arg;
  198.     struct waitdata    *data;
  199. } callbackMsg;
  200.  
  201. // unlock message...
  202. #define UNLOCK_DATA    1
  203. typedef struct {
  204.     msg_header_t    h;
  205.     msg_type_t        t;
  206.     id            lock;
  207. } unlockMsg; 
  208.  
  209.  
  210. // global variables...
  211. static BOOL blocked;      // YES if the app is "blocked" in an event loop
  212.                   // waiting for an appropriate EV_UNLOCK event
  213. static int block_count;   // number of nested blocks (diagnostic)
  214. static BOOL fastPending;  // YES if a fast callback is in progress
  215.  
  216. // callback ports
  217. static port_t fastPort, basePort;
  218.  
  219.  
  220. static void post_unlock_event( unlockMsg *msg )
  221. {
  222.     NXEvent ev;
  223.  
  224.  
  225.     ev.type = NX_APPDEFINED; 
  226.     ev.location.x = 0;
  227.     ev.location.y = 0;
  228.     ev.time = 0L;
  229.     ev.flags = 0;
  230.     ev.window = 0;
  231.     ev.data.compound.subtype = msg->h.msg_id;
  232.     ev.data.compound.misc.L[0] = (long) msg->lock;   /* the lock object */
  233.     ev.data.compound.misc.L[1] = 0; /* unused */
  234.     ev.ctxt = [NXApp context];
  235.  
  236.     if( DPSPostEvent( &ev, FALSE ) != 0 ){
  237.     printf( "handle_unlock: DPSPostEvent cannot post EV_UNLOCK\n" );
  238.     }
  239. }
  240.  
  241.  
  242. static void post_callback_event( callbackMsg *msg )
  243. {
  244.     NXEvent ev;
  245.  
  246.     /*
  247.      *  ok, uck.  I've got 4 int-sized parms to send and here they go:
  248.      *  target goes in L[0]
  249.      *  data   goes in L[1]
  250.      *  sel    goes in flags
  251.      *  arg    goes in window
  252.      *
  253.      *  Doesn't matter what they are, since the window-manager didn't make
  254.      *  the message anyways.
  255.      */
  256.     ev.type = NX_APPDEFINED;
  257.     ev.location.x = 0;
  258.     ev.location.y = 0;
  259.     ev.time = 0L;
  260.     ev.flags = (int) msg->sel;
  261.     ev.window = (int) msg->arg;
  262.     ev.data.compound.subtype = msg->h.msg_id;
  263.     ev.data.compound.misc.L[0] = (long) msg->target;
  264.     ev.data.compound.misc.L[1] = (long) msg->data;
  265.  
  266.     ev.ctxt = [NXApp context];
  267.  
  268.     if( DPSPostEvent( &ev, FALSE ) != 0 ){
  269.     printf( "handle_callback: DPSPostEvent cannot post EV_CALLBACK\n" );
  270.     }
  271. }
  272.  
  273.  
  274. static void do_callback( callbackMsg *msg )
  275. {
  276.     int options;
  277.     id ret;
  278.     struct waitdata *d;
  279.  
  280.     options = msg->h.msg_id & 0xff00;
  281.  
  282.     if( options & EV_CALLBACK_WAIT ){
  283.         d = msg->data;
  284.         mutex_lock( &d->lock );
  285.     }
  286.  
  287.     fastPending = YES;
  288.     if( options & EV_CALLBACK_ARG ){
  289.         ret = [msg->target perform:msg->sel with:msg->arg];
  290.     } else {
  291.         ret = [msg->target perform:msg->sel];
  292.     }
  293.     fastPending = NO;
  294.  
  295.     if( options & EV_CALLBACK_WAIT ){
  296.         d->retval = ret;
  297.         d->done = YES;
  298.         condition_signal( &d->cond );
  299.         mutex_unlock( &d->lock );
  300.     }
  301. }
  302.  
  303.  
  304. static void callbackHandler( msg_header_t *msg )
  305. /*
  306.  * This is called from DPSGetEvent or DPSPeekEvent if there
  307.  * is a message on the queue.
  308.  * This handler converts the Mach message to the appropriate
  309.  * NXEvent and posts it to the event queue (similar to what the
  310.  * NXTrackingTimer does).
  311.  * For fast callbacks, it invokes the method directly.
  312.  */
  313. {
  314.     int msg_type, msg_options;
  315.  
  316.  
  317. #if 0
  318.     putchar('*');
  319.     fflush(stdout);
  320. #endif
  321.  
  322.     msg_type = msg->msg_id & 0x00ff;
  323.     msg_options = msg->msg_id & 0xff00;
  324.  
  325.     switch( msg_type ){
  326.     case EV_UNLOCK:
  327.     // no point sending UNLOCK events if no one cares...
  328.     if( blocked ){
  329.         post_unlock_event( (unlockMsg *)msg );
  330.     }
  331.     break;
  332.     case EV_CALLBACK:
  333.     if( msg_options & EV_CALLBACK_POST ){
  334.         // callbacks posted as events...
  335.         post_callback_event( (callbackMsg *)msg );
  336.     } else {
  337.         // fast callbacks; do them here...
  338.         do_callback( (callbackMsg *)msg );
  339.     }
  340.     break;
  341.     default:
  342.     printf( "unknown message: 0x04%x\n", msg->msg_id );
  343.     break;
  344.     }
  345. }
  346.  
  347.  
  348. - (NXEvent*)getNextEvent:(int)mask waitFor:(double)timeout threshold:(int)level
  349. /*
  350.  * Override the master NXApp event getter to get and dispatch NX_APPDEFINED 
  351.  * events if they aren't already included in the mask.  This allows
  352.  * callbacks and EV_UNLOCK events to get through unsuspecting modal loops
  353.  * such as for handling mouse-down events.
  354.  */
  355. {
  356.     NXEvent *ev;
  357.  
  358.     if( mask & NX_APPDEFINEDMASK ){
  359.     // caller must know what they're doing...
  360.     return [super getNextEvent:mask waitFor:timeout threshold:level];
  361.     }
  362.     mask |= NX_APPDEFINEDMASK;
  363.     for(;;){
  364.     ev = [super getNextEvent:mask waitFor:timeout threshold:level];
  365.     if( !ev || (ev->type != NX_APPDEFINED) ) break;
  366.     [self applicationDefined:ev];
  367.     }
  368.     
  369.     return ev;
  370. }
  371.  
  372.  
  373. - (NXEvent*)getNextEvent:(int)mask
  374. {
  375.     NXEvent *ev;
  376.  
  377.     if( mask & NX_APPDEFINEDMASK ){
  378.     // caller must know what they're doing...
  379.     return [super getNextEvent:mask ];
  380.     }
  381.     mask |= NX_APPDEFINEDMASK;
  382.     for(;;){
  383.     ev = [super getNextEvent:mask];
  384.     if( !ev || (ev->type != NX_APPDEFINED) ) break;
  385.     [self applicationDefined:ev];
  386.     }
  387.     
  388.     return ev;
  389. }
  390.  
  391.  
  392. - applicationDefined:(NXEvent *)ev
  393. /* 
  394.  *  This method receives the NX_APPDEFINED events dispatched by the 
  395.  *  Application's run: and sendEvent: methods (but not by other
  396.  *  AppKit modal loops: Button, Slider, etc.)
  397.  *  This is where we perform the slow callbacks.
  398.  */
  399. {
  400.     id ret;
  401.     int ev_type, ev_options;
  402.     id target, arg;
  403.     SEL sel;
  404.     struct waitdata *d;
  405.  
  406.     ev_type = ev->data.compound.subtype & 0x00ff;
  407.     ev_options = ev->data.compound.subtype & 0xff00;
  408.  
  409.     switch( ev_type ){
  410.     case EV_UNLOCK:
  411.     // Don't need this now; must be left on the queue from a
  412.     // previous lock: modal loop below...
  413.     // printf( "applicationDefined:  Received a rogue EV_UNLOCK!\n" );
  414.     break;
  415.     case EV_CALLBACK:
  416.     /* uck.  See handle_callback above */
  417.     target = (id) ev->data.compound.misc.L[0];
  418.     sel = (SEL) ev->flags;
  419.     arg = (id) ev->window;
  420.     
  421.     if( ev_options & EV_CALLBACK_WAIT ){
  422.         d = (struct waitdata *)ev->data.compound.misc.L[1];
  423.         mutex_lock( &d->lock );
  424.     }
  425.  
  426.     // The event data in ev can be invalidated by this method
  427.     // invocation if it recursively calls DPSGetEvent.
  428.     // That's ok; we've now got a local copy of everything we need.
  429.     if( ev_options & EV_CALLBACK_ARG ){
  430.         ret = [target perform:sel with:arg];
  431.     } else {
  432.         ret = [target perform:sel];
  433.     }
  434.  
  435.     if( ev_options & EV_CALLBACK_WAIT ){
  436.         d->retval = ret;
  437.         d->done = YES;
  438.         condition_signal( &d->cond );
  439.         mutex_unlock( &d->lock );
  440.     }
  441.     break;
  442.     default:
  443.     printf( "applicationDefined: Unknown event: %d\n",
  444.         ev->data.compound.subtype );
  445.     break;
  446.     }
  447.     return self;
  448. }
  449.  
  450.  
  451. /*
  452.  *  The "designated" callback function.
  453.  */
  454.  
  455. // option bits for the master callback method...
  456. #define WAIT    0x02
  457. #define ARG    0x08
  458. #define HIGH    0x01
  459. #define POST    0x04
  460. #define FAST    HIGH
  461. #define SAFE    (HIGH|POST)
  462. #define SLOW    POST
  463.  
  464. - (id)callback:target perform:(SEL)aSel with:anObject opt:(int)option
  465. {
  466.     // allocate all variables off stack so it's reentrant and thread-safe
  467.     callbackMsg msg;
  468.     struct waitdata d;
  469.  
  470.  
  471.     // if this is called from the main thread, just do a perform:
  472.     // otherwise 'wait' may deadlock 
  473.     if( cthread_self() == mainThread ){
  474.     if( option & ARG ){
  475.         return [target perform:aSel with:anObject];
  476.     } else {
  477.         return [target perform:aSel];
  478.     }
  479.     }
  480.  
  481.     // setup header
  482.     msg.h.msg_simple = TRUE;
  483.     msg.h.msg_size = sizeof(callbackMsg);
  484.     msg.h.msg_local_port = PORT_NULL;
  485.     msg.h.msg_remote_port = (option&HIGH) ? fastPort : basePort;
  486.     msg.h.msg_type = MSG_TYPE_NORMAL;
  487.  
  488.     msg.t.msg_type_name = MSG_TYPE_INTEGER_32;
  489.     msg.t.msg_type_size = 32;
  490.     msg.t.msg_type_number = CALLBACK_DATA;
  491.     msg.t.msg_type_inline = TRUE;
  492.     msg.t.msg_type_longform = FALSE;
  493.     msg.t.msg_type_deallocate = FALSE;
  494.  
  495.  
  496.     // set up message data
  497.     msg.h.msg_id = EV_CALLBACK;
  498.     msg.target = target;
  499.     msg.sel = aSel;
  500.  
  501.     // set options...
  502.     if( option & POST ){
  503.     msg.h.msg_id |= EV_CALLBACK_POST;
  504.     }
  505.     if( option & ARG ){
  506.     msg.h.msg_id |= EV_CALLBACK_ARG;
  507.     msg.arg = anObject;
  508.     }
  509.     if( option & WAIT ){
  510.     msg.h.msg_id |= EV_CALLBACK_WAIT;
  511.     msg.data = &d;
  512.     d.done = NO;
  513.     mutex_init( &d.lock );
  514.     condition_init( &d.cond );
  515.     mutex_lock( &d.lock );
  516.     }
  517.  
  518.     msg_send((msg_header_t *)&msg, MSG_OPTION_NONE,0);
  519.  
  520.     if( option & WAIT ){
  521.     // wait for method to return
  522.     while( !d.done ){
  523.         condition_wait( &d.cond, &d.lock );
  524.     }
  525.     mutex_unlock( &d.lock );
  526.     mutex_clear( &d.lock );
  527.     condition_clear( &d.cond );
  528.     return d.retval;
  529.     } else {
  530.     return nil;
  531.     }
  532. }
  533.  
  534.  
  535.  
  536. // Exported variations on the theme
  537.  
  538.  
  539. - (id)callbackAndWaitTarget:target perform:(SEL)aSel
  540. {
  541.     return [self callback:target perform:aSel with:nil opt:FAST|WAIT];
  542. }
  543.  
  544. - (id)callbackAndWaitTarget:target perform:(SEL)aSel with:anObject
  545. {
  546.     return [self callback:target perform:aSel with:anObject opt:FAST|WAIT|ARG];
  547. }
  548.  
  549. - (void)callbackTarget:target perform:(SEL)aSel
  550. {
  551.     [self callback:target perform:aSel with:nil opt:FAST];
  552. }
  553.  
  554. - (void)callbackTarget:target perform:(SEL)aSel with:anObject
  555. {
  556.     [self callback:target perform:aSel with:anObject opt:FAST|ARG];
  557. }
  558.  
  559. - (id)safeCallbackAndWaitTarget:target perform:(SEL)aSel
  560. {
  561.     return [self callback:target perform:aSel with:nil opt:SAFE|WAIT];
  562. }
  563.  
  564. - (id)safeCallbackAndWaitTarget:(id)target perform:(SEL)aSel with:(id)anObject
  565. {
  566.     return [self callback:target perform:aSel with:anObject opt:SAFE|WAIT|ARG];
  567. }
  568.  
  569. - (void)safeCallbackTarget:target perform:(SEL)aSel
  570. {
  571.     [self callback:target perform:aSel with:nil opt:SAFE];
  572. }
  573.  
  574. - (void)safeCallbackTarget:(id)target perform:(SEL)aSel with:(id)anObject
  575. {
  576.     [self callback:target perform:aSel with:anObject opt:SAFE|ARG];
  577. }
  578.  
  579. - (id)slowCallbackAndWaitTarget:target perform:(SEL)aSel
  580. {
  581.     return [self callback:target perform:aSel with:nil opt:SLOW|WAIT];
  582. }
  583.  
  584. - (id)slowCallbackAndWaitTarget:(id)target perform:(SEL)aSel with:(id)anObject
  585. {
  586.     return [self callback:target perform:aSel with:anObject opt:SLOW|WAIT|ARG];
  587. }
  588.  
  589. - (void)slowCallbackTarget:target perform:(SEL)aSel
  590. {
  591.     [self callback:target perform:aSel with:nil opt:SLOW];
  592. }
  593.  
  594. - (void)slowCallbackTarget:(id)target perform:(SEL)aSel with:(id)anObject
  595. {
  596.     [self callback:target perform:aSel with:anObject opt:SLOW|ARG];
  597. }
  598.  
  599.  
  600.  
  601. + new
  602. {
  603.     [super new];
  604.  
  605.     // register ports for receiving callback messages
  606.     port_allocate( task_self(), &fastPort );
  607.     DPSAddPort( fastPort, (DPSPortProc)callbackHandler, 
  608.         sizeof(callbackMsg), NULL, FAST_THRESHOLD );
  609.  
  610.     port_allocate( task_self(), &basePort );
  611.     DPSAddPort( basePort, (DPSPortProc)callbackHandler, 
  612.         sizeof(callbackMsg), NULL, BASE_THRESHOLD );
  613.  
  614.     // identify the main Thread
  615.     mainThread = cthread_self();
  616.  
  617.     // set runtime to be thread-safe...
  618.     objc_setMultithreaded(YES);
  619.  
  620.     return NXApp;
  621. }
  622.  
  623.  
  624. - free
  625. {
  626.     DPSRemovePort(fastPort);
  627.     DPSRemovePort(basePort);
  628.     port_deallocate( task_self(), fastPort );
  629.     port_deallocate( task_self(), basePort );
  630.  
  631.     return [super free];
  632. }
  633.  
  634.  
  635.  
  636. @end
  637.  
  638.  
  639.  
  640. /*****************************************************************
  641.  * Support for CJRLocks...
  642.  */
  643.  
  644.  
  645. @implementation ThreadedApp (CJRLock)
  646.  
  647. - (void)threadDidUnlock:sender
  648. // construct a EV_UNLOCK Mach message to send to fastPort...
  649. {
  650.     unlockMsg msg;
  651.  
  652.     if( !blocked ){
  653.     // no point in sending UNLOCK events if no one cares...
  654.     return;
  655.     }
  656.     // setup header
  657.     msg.h.msg_simple = TRUE;
  658.     msg.h.msg_size = sizeof(unlockMsg);
  659.     msg.h.msg_local_port = PORT_NULL;
  660.     msg.h.msg_remote_port = fastPort;
  661.     msg.h.msg_type = MSG_TYPE_NORMAL;
  662.  
  663.     // type field for zero data...
  664.     msg.t.msg_type_name = MSG_TYPE_INTEGER_32;
  665.     msg.t.msg_type_size = 32;
  666.     msg.t.msg_type_number = UNLOCK_DATA;
  667.     msg.t.msg_type_inline = TRUE;
  668.     msg.t.msg_type_longform = FALSE;
  669.     msg.t.msg_type_deallocate = FALSE;
  670.  
  671.     // message type...
  672.     msg.h.msg_id = EV_UNLOCK;
  673.     msg.lock = sender;   // the lock object that unlocked
  674.  
  675.     msg_send((msg_header_t *)&msg, MSG_OPTION_NONE,0);
  676. }
  677.  
  678.  
  679. - (void)lock: (mutex_t)mutex
  680. /*
  681.  *  This method is called from CJRLock and CJRConditionLock methods
  682.  *  and should only be invoked from the main thread.
  683.  *  It is possible for this routine to be called recursively in response
  684.  *  to servicing applicationDefined:
  685.  */
  686. {
  687.     NXEvent *ev;
  688.     int saved_block;
  689.  
  690.     if( fastPending ){
  691.     // this doesn't return
  692.     [self error: "Cannot use locks from a fast callback method\n"];
  693.     }
  694.  
  695.     if( !mutex_try_lock(mutex) ){
  696.     saved_block = blocked;
  697.     block_count++;
  698.     blocked = YES;
  699.     for(;;){
  700.         ev = [self getNextEvent:NX_APPDEFINEDMASK
  701.             waitFor:NX_FOREVER
  702.             threshold:FAST_THRESHOLD];
  703.         if( ev->data.compound.subtype == EV_UNLOCK ){
  704.         if( mutex_try_lock(mutex) ) break;
  705.         } else {
  706.         // handle the event...
  707.         [self applicationDefined:ev ];
  708.         }
  709.     }
  710.     blocked = saved_block;
  711.     block_count--;
  712.     }
  713. }
  714.  
  715.  
  716. - (void) lock:(mutex_t)mutex whenCondition:(condition_t)cond 
  717.     withData:(volatile int *)data is:(int)value
  718. /*
  719.  *  This method is called from CJRConditionLock methods
  720.  *  and should only be invoked from the main thread.
  721.  *  It is possible for this routine to be called recursively in response
  722.  *  to servicing applicationDefined:
  723.  */
  724. {
  725.     NXEvent *ev;
  726.     int saved_block;
  727.  
  728.     if( fastPending ){
  729.     // this doesn't return
  730.     [self error: "Cannot use locks from a fast callback method\n"];
  731.     }
  732.  
  733.     if( mutex_try_lock(mutex) ){
  734.     if( *data == value ) {
  735.         return;
  736.     }
  737.     mutex_unlock(mutex);
  738.     }
  739.     saved_block = blocked;
  740.     block_count++;
  741.     blocked = YES;
  742.     for(;;){
  743.     ev = [self getNextEvent:NX_APPDEFINEDMASK 
  744.             waitFor:NX_FOREVER
  745.             threshold:FAST_THRESHOLD];
  746.     if( ev->data.compound.subtype == EV_UNLOCK ){
  747.         if( mutex_try_lock(mutex) ){
  748.         if( *data == value ) break;
  749.         mutex_unlock(mutex);
  750.         }
  751.     } else {
  752.         // handle the event...
  753.         [self applicationDefined:ev ];
  754.     }
  755.     }
  756.  
  757.     blocked = saved_block;
  758.     block_count--;
  759. }
  760.  
  761. @end
  762.