home *** CD-ROM | disk | FTP | other *** search
- // -------------------------------------------------------------------------------------
- // objectThreadPerform.m
- // author: Martin D. Flynn
- // (see objectThreadPerform.h for usage information)
- // -------------------------------------------------------------------------------------
- // Permission is granted to freely redistribute this source code, and to use fragments
- // of this code in your own applications if you find them to be useful. This module,
- // along with the source code, come with no warranty of any kind, and the user assumes
- // all responsibility for its use.
- // -------------------------------------------------------------------------------------
-
- #import <objc/objc.h>
- #import <appkit/appkit.h>
- #import <libc.h>
- #import <stdio.h>
- #import <c.h>
- #import <dpsclient/dpsclient.h>
- #import <dpsclient/dpsNeXT.h>
- #import <servers/netname.h>
- #import <mach/cthreads.h>
-
- // -------------------------------------------------------------------------------------
- // misc defines
- #define appPortFORMAT "port_%s"
- #define cpNil (char*)nil
- #define vpNil (void*)nil
-
- // -------------------------------------------------------------------------------------
- // main thread performance information structure
-
- /* target/action perform information */
- #define SIGNATURE 0xF121658F
- typedef struct targetAction_s {
- long sig; // structure signature
- id target;
- SEL action;
- id arg[2];
- int argCount;
- any_t result;
- mutex_t mutex;
- condition_t condition;
- BOOL wait;
- struct targetAction_s *next;
- } targetAction_t;
-
- /* mach port data structure */
- typedef struct {
- msg_header_t hdr;
- msg_type_t type;
- void *data;
- } performMessage_t;
-
- // -------------------------------------------------------------------------------------
- // global static variables
- static targetAction_t *lastDp = (targetAction_t*)nil; // last chained perform struct
- static mutex_t performMutex = (mutex_t)nil; // perform mutex
- static port_t performPort = (port_t)nil; // perform message port
- static cthread_t mainThread = (cthread_t)nil; // main thread
-
- // -------------------------------------------------------------------------------------
- @implementation Object(ThreadPerform)
-
- // -------------------------------------------------------------------------------------
- // target/action structure alloc/free
- // -------------------------------------------------------------------------------------
-
- /* allocate target/action structure */
- static targetAction_t
- *allocActionStruct(id self, SEL aSel, id arg0, id arg1, int count, BOOL wait)
- {
- targetAction_t *dp = NXZoneMalloc([self zone], sizeof(targetAction_t));
- dp->sig = SIGNATURE;
- dp->target = self;
- dp->action = aSel;
- dp->arg[0] = arg0;
- dp->arg[1] = arg1;
- dp->argCount = count;
- dp->wait = wait;
- dp->mutex = mutex_alloc();
- dp->condition = condition_alloc();
- dp->result = (any_t)nil;
- dp->next = (targetAction_t*)nil;
- return dp;
- }
-
- /* free target/action structure */
- static targetAction_t *freeActionStruct(targetAction_t *dp)
- {
- targetAction_t *next;
- mutex_lock(performMutex);
- next = dp->next;
- if (dp == lastDp) lastDp = (targetAction_t*)nil;
- mutex_unlock(performMutex);
- mutex_free(dp->mutex);
- condition_free(dp->condition);
- free(dp);
- return next;
- }
-
- // -------------------------------------------------------------------------------------
- // thread perform
- // -------------------------------------------------------------------------------------
-
- /* perform with argCount specified */
- - perform:(SEL)selector with:arg1 with:arg2 argCount:(int)argCount
- {
- switch (argCount) {
- case 0: return [self perform:selector]; break;
- case 1: return [self perform:selector with:arg1]; break;
- case 2: return [self perform:selector with:arg1 with:arg2]; break;
- }
- return (id)nil;
- }
-
- /* perform target/action in structure */
- static id performActionStruct(targetAction_t *dp)
- {
-
- /* perform method */
- mutex_lock(dp->mutex);
- dp->result = [dp->target perform:dp->action with:dp->arg[0] with:dp->arg[1]
- argCount:dp->argCount];
-
- /* signal method completion */
- condition_signal(dp->condition);
- mutex_unlock(dp->mutex);
-
- /* return results */
- return (id)dp->result;
- }
-
- /* port message handler */
- static void _performProc(msg_header_t *msg, void *data)
- {
- targetAction_t *dp = ((performMessage_t*)msg)->data;
- while (dp && (dp->sig == SIGNATURE)) { // loop until no data, or wait
- BOOL wait = dp->wait; // wait status save for later checking
- performActionStruct(dp); // execute action
- if (wait) break; // break if waiting for return
- dp = freeActionStruct(dp); // free and get next structure
- }
- }
-
- /* port initialization (MUST BE EXECUTE FROM MAIN THREAD ONLY!) */
- // - This function will be called automatically from forkPerform:...
- #define doINIT { if (mainThread == (cthread_t)nil) _doInit(); }
- static void _doInit()
- {
- char sName[256];
-
- /* return if already initialized */
- if (mainThread) return;
- mainThread = cthread_self();
-
- /* allocate perform mutex */
- performMutex = mutex_alloc();
-
- /* allocate perform port (port name is made public) */
- sprintf(sName, appPortFORMAT, [NXApp appName]);
- if ((port_allocate(task_self(),&performPort) == KERN_SUCCESS) &&
- (port_set_backlog(task_self(),performPort,PORT_BACKLOG_MAX) == KERN_SUCCESS) &&
- (netname_check_in(name_server_port,sName,PORT_NULL,performPort) == NETNAME_SUCCESS)) {
- DPSAddPort(performPort, _performProc, MSG_SIZE_MAX, vpNil, NX_MODALRESPTHRESHOLD);
- } else {
- NXLogError("objectThreadPerfrom: Unable to allocate port for thread support");
- performPort = (port_t)nil;
- exit(255);
- }
-
- }
-
- /* explicit initialization (MUST BE EXECUTE FROM MAIN THREAD ONLY!) */
- + initThreadSupport
- {
- doINIT;
- return self;
- }
-
- /* return true if calling thread is main thread */
- + (BOOL)isMainThread { return (mainThread == cthread_self()); }
- - (BOOL)isMainThread { return (mainThread == cthread_self()); }
-
- /* return cthread_self() */
- - (cthread_t)cthreadSelf { return cthread_self(); }
-
- // -------------------------------------------------------------------------------------
- // port message perform support
- // multiple calls to 'mainThreadPerform:with:wait:' with wait:NO will be chained together
- // if the prior call has not completed execution. This is done to reduce the load on
- // mach_port usage.
- // -------------------------------------------------------------------------------------
-
- /* send perform message to port */
- static BOOL _sendPerformToPort(port_t portId, void *data)
- {
- performMessage_t pm;
- msg_return_t err;
- msg_timeout_t timeout = 45000; // 45 seconds
-
- /* check for valid port */
- if (!portId) return YES;
-
- /* set up the header */
- pm.hdr.msg_simple = TRUE; // data is inline
- pm.hdr.msg_size = sizeof(performMessage_t);
- pm.hdr.msg_type = MSG_TYPE_NORMAL;
- pm.hdr.msg_remote_port = portId; // destination port
- pm.hdr.msg_local_port = PORT_NULL;
- pm.hdr.msg_id = 0; // receiver message type
-
- /* set up the typeDescriptor */
- pm.type.msg_type_name = MSG_TYPE_CHAR;
- pm.type.msg_type_size = 8; // 8 bits / byte
- pm.type.msg_type_inline = TRUE; // data is inline
- pm.type.msg_type_number = sizeof(targetAction_t*);
-
- /* set up the data */
- pm.type.msg_type_longform = FALSE;
- pm.type.msg_type_deallocate = FALSE; // do not deallocate
- pm.data = data; // the data
-
- /* send message and return results */
- err = msg_send((msg_header_t*)&pm, (msg_option_t)SEND_TIMEOUT, timeout);
- return (err == SEND_SUCCESS)? NO: YES;
-
- }
-
- /* local mainThreadPerform method */
- static id _mainThreadPerform(port_t portId, targetAction_t *dp)
- {
- any_t result;
-
- /* check non-wait request */
- if (!dp->wait) {
- mutex_lock(performMutex);
- mutex_lock(dp->mutex);
- if (lastDp) lastDp->next = dp;
- else _sendPerformToPort(portId, (void*)dp);
- lastDp = dp;
- mutex_unlock(dp->mutex);
- mutex_unlock(performMutex);
- return (id)nil;
- }
-
- /* send message and wait for return */
- mutex_lock(dp->mutex);
- if (!_sendPerformToPort(portId, (void*)dp)) condition_wait(dp->condition, dp->mutex);
- mutex_unlock(dp->mutex);
- result = dp->result;
-
- /* free structure */
- freeActionStruct(dp);
-
- return (id)result;
-
- }
-
- /* perform selector from main thread (no args) */
- - mainThreadPerform:(SEL)aSelector wait:(BOOL)waitForReturn
- {
- targetAction_t *dp;
- doINIT; /* just in case: we better be the main thread if this is executed! */
- if ([self isMainThread]) return [self perform:aSelector];
- dp = allocActionStruct(self, aSelector, (id)nil, (id)nil, 0, waitForReturn);
- return _mainThreadPerform(performPort, dp);
- }
-
- /* perform selector from main thread (1 arg) */
- - mainThreadPerform:(SEL)aSelector with:anArg wait:(BOOL)waitForReturn
- {
- targetAction_t *dp;
- doINIT; /* just in case: we better be the main thread if this is executed! */
- if ([self isMainThread]) return [self perform:aSelector with:anArg];
- dp = allocActionStruct(self, aSelector, anArg, (id)nil, 1, waitForReturn);
- return _mainThreadPerform(performPort, dp);
- }
-
- /* perform selector from main thread (2 args) */
- - mainThreadPerform:(SEL)aSelector with:anArg0 with:anArg1 wait:(BOOL)waitForReturn
- {
- targetAction_t *dp;
- doINIT; /* just in case: we better be the main thread if this is executed! */
- if ([self isMainThread]) return [self perform:aSelector with:anArg0 with:anArg1];
- dp = allocActionStruct(self, aSelector, anArg0, anArg1, 2, waitForReturn);
- return _mainThreadPerform(performPort, dp);
- }
-
- // -------------------------------------------------------------------------------------
- // fork thread support
- // -------------------------------------------------------------------------------------
-
- /* forked method router */
- static void threadRouter(targetAction_t *dp)
- {
-
- /* wait here until parent thread is ready */
- mutex_lock(dp->mutex);
- mutex_unlock(dp->mutex);
-
- /* execute thread */
- performActionStruct(dp); // execute action
- freeActionStruct(dp); // free structure
-
- /* terminate thread */
- cthread_exit(0); // terminate thread
-
- }
-
- /* fork method thread */
- - (cthread_t)forkPerform:(SEL)aSelector with:arg0 with:arg1 argc:(int)cnt detach:(BOOL)detach
- {
- cthread_t cthread;
- targetAction_t *dp = allocActionStruct(self, aSelector, arg0, arg1, cnt, NO);
-
- /* initialize if necessary (for the first time, we better be the main thread!)*/
- doINIT;
-
- /* fork thread */
- mutex_lock(dp->mutex);
- cthread = cthread_fork((cthread_fn_t)threadRouter, (any_t)dp);
- if (detach) cthread_detach(cthread);
- mutex_unlock(dp->mutex);
- cthread_yield(); // allow thread to run
-
- return (detach)? (cthread_t)nil: cthread; // handle may not be valid if detached
- }
-
- /* fork method thread */
- - (cthread_t)forkPerform:(SEL)aSelector detach:(BOOL)detach
- {
- return [self forkPerform:aSelector with:(id)nil with:(id)nil argc:0 detach:detach];
- }
-
- /* fork method thread */
- - (cthread_t)forkPerform:(SEL)aSelector with:anArg detach:(BOOL)detach
- {
- return [self forkPerform:aSelector with:anArg with:(id)nil argc:1 detach:detach];
- }
-
- /* fork method thread */
- - (cthread_t)forkPerform:(SEL)aSelector with:anArg0 with:anArg1 detach:(BOOL)detach
- {
- return [self forkPerform:aSelector with:anArg0 with:anArg1 argc:2 detach:detach];
- }
-
- // -------------------------------------------------------------------------------------
- // return cthread status
- // -------------------------------------------------------------------------------------
-
- /* return true if thread is active */
- #define threadMAIN 0x1
- #define threadRETURNED 0x2
- #define threadDETACHED 0x4
- - (BOOL)threadIsActive:(cthread_t)theCthread
- {
- if (!theCthread) return NO;
- return (theCthread->state & threadRETURNED)? NO: YES;
- }
-
- // -------------------------------------------------------------------------------------
- // terminate thread support
- // -------------------------------------------------------------------------------------
-
- /* terminate calling thread (may be overridden by subclass) */
- - terminateThread
- {
- cthread_exit(0);
- return (id)nil;
- }
-
- /* terminate specified thread */
- // IMPORTANT WARNING: Thist method of thread termination should only be considered as a
- // last resort. This method of thread termination doesn't allow for resource
- // deallocation and cleanup, and could cause unpredictable results.
- - terminateThread:(cthread_t)theCthread
- {
-
- /* check for invalid cthread handle */
- if (!theCthread) return (id)nil;
-
- /* check for terminating cthread_self() */
- if (theCthread == cthread_self()) {
- cthread_detach(theCthread);
- return [self terminateThread];
- }
-
- /* this method is not really safe, but it does seem to work */
- thread_terminate(cthread_thread(theCthread));
-
- return self;
- }
-
- @end
-