home *** CD-ROM | disk | FTP | other *** search
/ Nebula / nebula.bin / SourceCode / Classes / SampleClasses / objectThreadPerform.m < prev    next >
Text File  |  1992-08-04  |  12KB  |  346 lines

  1. // -------------------------------------------------------------------------------------
  2. // objectThreadPerform.m
  3. // (see objectThreadPerform.h for usage information)
  4. // Martin D. Flynn, NeXT Computer, Inc.
  5. // -------------------------------------------------------------------------------------
  6. // Permission is granted to freely redistribute this source code, and to use fragments
  7. // of this code in your own applications if you find them to be useful.  This class,
  8. // along with the source code, come with no warranty of any kind, and the user assumes
  9. // all responsibility for its use.
  10. // -------------------------------------------------------------------------------------
  11. #import <stdio.h>
  12. #import <libc.h>
  13. #import <c.h>
  14. #import <dpsclient/dpsclient.h>
  15. #import <dpsclient/dpsNeXT.h>
  16. #import <servers/netname.h>
  17. #import <objc/zone.h>
  18. #import <mach/cthreads.h>
  19. #import <appkit/Application.h>
  20. #import <appkit/Panel.h>
  21. #import <appkit/Listener.h>
  22. #import <appkit/nextstd.h>
  23.  
  24. // -------------------------------------------------------------------------------------
  25. // misc defines
  26. #define appPortFORMAT   "port_%s"
  27. #define cpNil           (char*)nil
  28. #define vpNil           (void*)nil
  29.  
  30. // -------------------------------------------------------------------------------------
  31. // main thread performance information structure
  32.  
  33. /* target/action perform information */
  34. #define SIGNATURE       0xF121658F
  35. typedef struct targetAction_s {
  36.     long                    sig;            // structure signature
  37.     id                      target;
  38.     SEL                     action;
  39.     id                      arg[2];
  40.     int                     argCount;
  41.     any_t                   result;
  42.     mutex_t                 mutex;
  43.     condition_t             condition;
  44.     BOOL                    wait;
  45.     struct targetAction_s   *next;
  46. } targetAction_t;
  47.  
  48. /* mach port data structure */
  49. typedef struct {
  50.     msg_header_t            hdr;
  51.     msg_type_t              type;
  52.   void                      *data;
  53. } performMessage_t;
  54.  
  55. // -------------------------------------------------------------------------------------
  56. // global static variables
  57. static targetAction_t   *lastDp = (targetAction_t*)nil;     // last chained perform struct
  58. static mutex_t          performMutex = (mutex_t)nil;        // perform mutex
  59. static port_t           performPort = (port_t)nil;          // perform message port
  60. static cthread_t        mainThread = (cthread_t)nil;        // main thread
  61.  
  62. // -------------------------------------------------------------------------------------
  63. @implementation Object(ThreadPerform)
  64.  
  65. // -------------------------------------------------------------------------------------
  66. // target/action structure alloc/free
  67. // -------------------------------------------------------------------------------------
  68.  
  69. /* allocate target/action structure */
  70. static targetAction_t
  71. *allocActionStruct(id self, SEL aSel, id arg0, id arg1, int count, BOOL wait)
  72. {
  73.     targetAction_t  *dp = NXZoneMalloc([self zone], sizeof(targetAction_t));
  74.     dp->sig         = SIGNATURE;
  75.     dp->target      = self;
  76.     dp->action      = aSel;
  77.     dp->arg[0]      = arg0;
  78.     dp->arg[1]      = arg1;
  79.     dp->argCount    = count;
  80.     dp->wait        = wait;
  81.     dp->mutex       = mutex_alloc();
  82.     dp->condition   = condition_alloc();
  83.     dp->result      = (any_t)nil;
  84.     dp->next        = (targetAction_t*)nil;
  85.   return dp;
  86. }
  87.  
  88. /* free target/action structure */
  89. static targetAction_t *freeActionStruct(targetAction_t *dp)
  90. {
  91.     targetAction_t  *next;
  92.     mutex_lock(performMutex);
  93.     next = dp->next;
  94.     if (dp == lastDp) lastDp = (targetAction_t*)nil;
  95.     mutex_unlock(performMutex);
  96.     mutex_free(dp->mutex);
  97.     condition_free(dp->condition);
  98.     free(dp);
  99.     return next;
  100. }
  101.  
  102. // -------------------------------------------------------------------------------------
  103. // thread perform
  104. // -------------------------------------------------------------------------------------
  105.  
  106. /* perform with argCount specified */
  107. - perform:(SEL)selector with:arg1 with:arg2 argCount:(int)argCount
  108. {
  109.     switch (argCount) {
  110.         case 0: return [self perform:selector];                     break;
  111.         case 1: return [self perform:selector with:arg1];           break;
  112.         case 2: return [self perform:selector with:arg1 with:arg2]; break;
  113.     }
  114.     return (id)nil;
  115. }
  116.  
  117. /* perform target/action in structure */
  118. static id performActionStruct(targetAction_t *dp)
  119. {
  120.  
  121.     /* perform method */
  122.     mutex_lock(dp->mutex);
  123.     dp->result = [dp->target perform:dp->action with:dp->arg[0] with:dp->arg[1]
  124.                           argCount:dp->argCount];
  125.   
  126.     /* signal method completion */
  127.     condition_signal(dp->condition);
  128.     mutex_unlock(dp->mutex);
  129.   
  130.     /* return results */
  131.     return (id)dp->result;
  132. }
  133.  
  134. /* port message handler */
  135. static void _performProc(msg_header_t *msg, void *data)
  136. {
  137.     targetAction_t  *dp = ((performMessage_t*)msg)->data;
  138.     while (dp && (dp->sig == SIGNATURE)) {      // loop until no data, or wait
  139.         BOOL wait = dp->wait;                   // wait status save for later checking
  140.         performActionStruct(dp);                // execute action
  141.         if (wait) break;                        // break if waiting for return
  142.         dp = freeActionStruct(dp);              // free and get next structure
  143.     }
  144. }
  145.  
  146. /* port initialization (MUST BE EXECUTE FROM MAIN THREAD ONLY!) */
  147. // - This function will be called automatically from forkPerform:...
  148. #define doINIT   { if (mainThread == (cthread_t)nil) objectThreadPerformInit(); }
  149. void objectThreadPerformInit()
  150. {
  151.     char    sName[256];
  152.   
  153.     /* return if already initialized */
  154.     if (mainThread) return;
  155.     mainThread = cthread_self();
  156.   
  157.     /* allocate perform mutex */
  158.     performMutex = mutex_alloc();
  159.  
  160.     /* allocate perform port (port name is made public) */
  161.     sprintf(sName, appPortFORMAT, [NXApp appName]);
  162.     if ((port_allocate(task_self(),&performPort) == KERN_SUCCESS) &&
  163.         (port_set_backlog(task_self(),performPort,PORT_BACKLOG_MAX) == KERN_SUCCESS) &&
  164.         (netname_check_in(name_server_port,sName,PORT_NULL,performPort) == NETNAME_SUCCESS)) {
  165.         DPSAddPort(performPort, _performProc, MSG_SIZE_MAX, vpNil, NX_MODALRESPTHRESHOLD);
  166.     } else {
  167.         NXLogError("objectThreadPerfrom: Unable to allocate port for thread support");
  168.         performPort = (port_t)nil;
  169.         exit(255);
  170.     }
  171.     
  172. }
  173.  
  174. /* explicit initialization (MUST BE EXECUTE FROM MAIN THREAD ONLY!) */
  175. + initThreadSupport
  176. {
  177.     doINIT;
  178.     return self;
  179. }
  180.  
  181. /* return true if calling thread is main thread */
  182. + (BOOL)isMainThread { return (mainThread == cthread_self()); }
  183. - (BOOL)isMainThread { return (mainThread == cthread_self()); }
  184.  
  185. // -------------------------------------------------------------------------------------
  186. // port message perform support
  187. // multiple calls to 'mainThreadPerform:with:wait:' with wait:NO will be chained together
  188. // if the prior call has not completed execution.  This is done to reduce the load on
  189. // mach_port usage.
  190. // -------------------------------------------------------------------------------------
  191.  
  192. /* send perform message to port */
  193. static BOOL _sendPerformToPort(port_t portId, void *data)
  194. {
  195.     performMessage_t    pm;
  196.     msg_return_t        err;
  197.     msg_timeout_t       timeout = 45000;                    // 45 seconds 
  198.  
  199.     /* check for valid port */
  200.     if (!portId) return YES;
  201.   
  202.     /* set up the header */
  203.     pm.hdr.msg_simple           = TRUE;                 // data is inline
  204.     pm.hdr.msg_size             = sizeof(performMessage_t);
  205.     pm.hdr.msg_type             = MSG_TYPE_NORMAL;
  206.     pm.hdr.msg_remote_port      = portId;               // destination port 
  207.     pm.hdr.msg_local_port       = PORT_NULL;
  208.     pm.hdr.msg_id               = 0;                    // receiver message type
  209.  
  210.     /* set up the typeDescriptor */
  211.     pm.type.msg_type_name       = MSG_TYPE_CHAR;
  212.     pm.type.msg_type_size       = 8;                    // 8 bits / byte
  213.     pm.type.msg_type_inline     = TRUE;                 // data is inline
  214.     pm.type.msg_type_number     = sizeof(targetAction_t*);
  215.   
  216.     /* set up the data */
  217.     pm.type.msg_type_longform   = FALSE;
  218.     pm.type.msg_type_deallocate = FALSE;                // do not deallocate
  219.     pm.data                     = data;                 // the data
  220.  
  221.     /* send message and return results */
  222.     err = msg_send((msg_header_t*)&pm, (msg_option_t)SEND_TIMEOUT, timeout);
  223.     return (err == SEND_SUCCESS)? NO: YES;
  224.   
  225. }
  226.  
  227. /* local mainThreadPerform method */
  228. static id _mainThreadPerform(port_t portId, targetAction_t *dp)
  229. {
  230.     any_t   result;
  231.   
  232.     /* check non-wait request */
  233.     if (!dp->wait) {
  234.         mutex_lock(performMutex);
  235.         mutex_lock(dp->mutex);
  236.         if (lastDp) lastDp->next = dp;
  237.         else _sendPerformToPort(portId, (void*)dp);
  238.         lastDp = dp;
  239.         mutex_unlock(dp->mutex);
  240.         mutex_unlock(performMutex);
  241.         return (id)nil;
  242.     }
  243.  
  244.     /* send message and wait for return */
  245.     mutex_lock(dp->mutex);
  246.     if (!_sendPerformToPort(portId, (void*)dp)) condition_wait(dp->condition, dp->mutex);
  247.     mutex_unlock(dp->mutex);
  248.     result = dp->result;
  249.   
  250.     /* free structure */
  251.     freeActionStruct(dp);
  252.  
  253.     return (id)result;
  254.   
  255. }
  256.  
  257. /* perform selector from main thread (no args) */
  258. - mainThreadPerform:(SEL)aSelector wait:(BOOL)waitForReturn
  259. {
  260.     targetAction_t  *dp;
  261.     doINIT; /* just in case: we better be the main thread if this is executed! */
  262.     if ([self isMainThread]) return [self perform:aSelector];
  263.     dp = allocActionStruct(self, aSelector, (id)nil, (id)nil, 0, waitForReturn);
  264.     return _mainThreadPerform(performPort, dp);
  265. }
  266.  
  267. /* perform selector from main thread (1 arg) */
  268. - mainThreadPerform:(SEL)aSelector with:anArg wait:(BOOL)waitForReturn
  269. {
  270.     targetAction_t  *dp;
  271.     doINIT; /* just in case: we better be the main thread if this is executed! */
  272.     if ([self isMainThread]) return [self perform:aSelector with:anArg];
  273.     dp = allocActionStruct(self, aSelector, anArg, (id)nil, 1, waitForReturn);
  274.     return _mainThreadPerform(performPort, dp);
  275. }
  276.  
  277. /* perform selector from main thread (2 args) */
  278. - mainThreadPerform:(SEL)aSelector with:anArg0 with:anArg1 wait:(BOOL)waitForReturn
  279. {
  280.     targetAction_t  *dp;
  281.     doINIT; /* just in case: we better be the main thread if this is executed! */
  282.     if ([self isMainThread]) return [self perform:aSelector with:anArg0 with:anArg1];
  283.     dp = allocActionStruct(self, aSelector, anArg0, anArg1, 2, waitForReturn);
  284.     return _mainThreadPerform(performPort, dp);
  285. }
  286.  
  287. // -------------------------------------------------------------------------------------
  288. // fork thread support
  289. // -------------------------------------------------------------------------------------
  290.  
  291. /* forked method router */
  292. static void threadRouter(targetAction_t *dp)
  293. {
  294.   
  295.     /* wait here until parent thread is ready */
  296.     mutex_lock(dp->mutex);
  297.     mutex_unlock(dp->mutex);
  298.   
  299.     /* execute thread */
  300.     performActionStruct(dp);                        // execute action
  301.     freeActionStruct(dp);                           // free structure
  302.  
  303.     /* terminate thread */
  304.     cthread_exit(0);                                // terminate thread
  305.   
  306. }
  307.  
  308. /* fork method thread */
  309. - (cthread_t)forkPerform:(SEL)aSelector with:arg0 with:arg1 argc:(int)cnt detach:(BOOL)detach
  310. {
  311.     cthread_t       cthread;
  312.     targetAction_t  *dp = allocActionStruct(self, aSelector, arg0, arg1, cnt, NO);
  313.   
  314.     /* initialize if necessary (for the first time, we better be the main thread!)*/
  315.     doINIT;
  316.   
  317.     /* fork thread */
  318.     mutex_lock(dp->mutex);
  319.     cthread = cthread_fork((cthread_fn_t)threadRouter, (any_t)dp);
  320.     if (detach) cthread_detach(cthread);
  321.     mutex_unlock(dp->mutex);
  322.     cthread_yield();        // allow thread to run
  323.   
  324.     return (detach)? (cthread_t)nil: cthread;       // handle may not be valid if detached
  325. }
  326.  
  327. /* fork method thread */
  328. - (cthread_t)forkPerform:(SEL)aSelector detach:(BOOL)detach
  329. {
  330.     return [self forkPerform:aSelector with:(id)nil with:(id)nil argc:0 detach:detach];
  331. }
  332.  
  333. /* fork method thread */
  334. - (cthread_t)forkPerform:(SEL)aSelector with:anArg detach:(BOOL)detach
  335. {
  336.     return [self forkPerform:aSelector with:anArg with:(id)nil argc:1 detach:detach];
  337. }
  338.  
  339. /* fork method thread */
  340. - (cthread_t)forkPerform:(SEL)aSelector with:anArg0 with:anArg1 detach:(BOOL)detach
  341. {
  342.     return [self forkPerform:aSelector with:anArg0 with:anArg1 argc:2 detach:detach];
  343. }
  344.  
  345. @end
  346.