home *** CD-ROM | disk | FTP | other *** search
- // -------------------------------------------------------------------------------------
- // objectThreadPerform.hm
- // author: Martin D. Flynn
- //
- // This category of Object provides a general solution for thread support which includes
- // a solution for allowing threads to communicate with the window/event manager.
- //
- // Using 'forkPerform:detach:', a thread can be created to perform some action in the
- // background while the window manager (main thread) is free to handle events.
- //
- // In addition to creating new threads, support is provided to allow a forked thread to
- // communicate with the main thread to tell it to execute actions, such as drawing in a
- // view, and even returning values. This is accomplished through calls to
- // 'mainThreadPerform:with:wait:'. This method can be called from any thread, making sure
- // that the main thread is the only thread that will process the specified action. These
- // methods are object independent, meaning that 'mainThreadPerform:wait:' can be sent to
- // ANY object from ANY thread. For instance, a forked thread could set the title of some
- // button with the following:
- // [someButtonId mainThreadPerform:@selector(setTitleNoCopy:) with:aStaticTitle wait:NO];
- // (A forked thread should not try to set the title of a button itself, or execute any
- // other method that causes drawing to occur. This is because it has no way of knowing
- // what drawing may already be occurring, and it may in fact cause drawing to occur in a
- // view other than where it was originally intended.)
- //
- // The return value can also be obtained from the method executed from the main thread by
- // setting 'wait:YES'. For example, to wait for a return value from a method executed
- // from the main thread you could issue the following message:
- // rtn = [myObject mainThreadPerform:@selector(showErrorPanel:) with:errorMsg wait:YES];
- // The value that 'showErrorPanel:' returned in the main thread would be returned to the
- // calling thread. This is helpful when a thread wishes to display an error panel, then
- // act on the response from the user.
- //
- // -------------------------------------------------------------------------------------
- #import <mach/cthreads.h>
- #import <objc/Object.h>
-
- // -------------------------------------------------------------------------------------
- // thread perform macros (Object category enhancements)
- // (see 'mainThreadPerform:wait:' below for more informaiton)
- #define isMainTHREAD [Object isMainThread]
- #define _cmdPERFORM(P) [self mainThreadPerform:_cmd with:(id)(P) wait:NO]
- #define _cmdPERFORM2(P1,P2) [self mainThreadPerform:_cmd with:(id)P1 with:(id)P2 wait:NO]
- #define _cmdPERFORMw(P) [self mainThreadPerform:_cmd with:(id)(P) wait:YES]
- #define _cmdINVOKE(P) { if (!isMainTHREAD) return _cmdPERFORM(P); }
- #define _cmdINVOKE2(P1,P2) { if (!isMainTHREAD) return _cmdPERFORM2(P1,P2); }
- #define _cmdINVOKEw(P) { if (!isMainTHREAD) return _cmdPERFORMw(P); }
-
- // -------------------------------------------------------------------------------------
- @interface Object(ThreadPerform)
- // -------------------------------------------------------------------------------------
- + initThreadSupport;
- // This initialization will occur automatically when 'forkPerform:...' is executed from
- // the main thread. You may call this method to explicitly initialize this thread
- // support prior to calling 'forkPerform:...'
- //
- // -------------------------------------------------------------------------------------
- + (BOOL)isMainThread;
- - (BOOL)isMainThread;
- // These methods return true if called from the main thread, else they return false.
- // Valid only after a call to 'initThreadSupport', or 'forkPerform:...', has been made.
- //
- // -------------------------------------------------------------------------------------
- - (cthread_t)cthreadSelf;
- // cthreadSelf returns the equivalent of cthread_self(). This method allows easier
- // access to the thread handle when running the application under 'gdb'.
- //
- // -------------------------------------------------------------------------------------
- - perform:(SEL)selector with:arg1 with:arg2 argCount:(int)argCount;
- // This method is a shell for the individual 'perform:[with:[with:]]' with the specified
- // number of arguments.
- // An argCount of 0 would send [self perform:selector];
- // An argCount of 1 would send [self perform:selector with:arg1];
- // An argCount of 2 would send [self perform:selector with:arg1 with:arg2];
- // Any other value for argCount will be ignored.
- //
- // -------------------------------------------------------------------------------------
- - mainThreadPerform:(SEL)aSelector wait:(BOOL)waitFlag;
- - mainThreadPerform:(SEL)aSelector with:anArg wait:(BOOL)waitFlag;
- - mainThreadPerform:(SEL)aSelector with:anArg0 with:anArg1 wait:(BOOL)waitFlag;
- // This method will cause aSelector to be executed by the main thread. If the calling
- // thread is not the main thread, this method sends an aSelector message through a Mach
- // port to the main thread to be executed. If the calling thread is the main thread,
- // then the aSelector message is sent directly. If waitFlag is true, then the calling
- // thread will be suspended until the main thread completes the execution of the message,
- // and the return value will be that of the aSelector message executed from the main
- // thread. If waitFlag is false, then this method returns (id)nil.
- // Macros: _cmdPERFORM(anArg) /* [self mainThreadPerform:_cmd with:anArg wait:NO] */
- // _cmdPERFORMw(anArg) /* [self mainThreadPerform:_cmd with:anArg wait:YES] */
- // _cmdINVOKE(anArg) /* { if (!isMainTHREAD) return _cmdPERFORM(anArg); } */
- // _cmdINVOKEw(anArg) /* { if (!isMainTHREAD) return _cmdPERFORMw(anArg); } */
- //
- // Note on use of _cmdINVOKE(anArg) and _cmdINVOKEw(anArg) :
- // To guarentee that a method is executed from the main thread the method need only
- // include '_cmdINVOKE(anArg);' at the beginning of the method. When this macro is
- // executed, it checks to see if it is the main thread. If it is not then it is sent
- // to the main thread and the macro causes the method to return. If it is executed
- // from the main thread, as it would be after being sent to _cmdPERFORM(anArg), then
- // the method is allowed to execute normally. Using _cmdINVOKEw(anArg) performs
- // the same as _cmdINVOKE(anArg) except that it waits for the main thread to finish
- // execution of the method before returning. It is important to note that if this
- // method is implemented in a subclass, it is important that the subclass implementation
- // duplicate the the call to the macro at the beginning of the method. Otherwise
- // unpredictable results may occur.
- //
- // Example use of _cmdINVOKE(anArg);
- // - setButtonState:(BOOL)newState
- // {
- // _cmdINVOKE(newState);
- // [myButton setState:newState];
- // // ... other code here ...
- // return self;
- // }
- //
- // -------------------------------------------------------------------------------------
- - (cthread_t)forkPerform:(SEL)aSelector detach:(BOOL)detach;
- - (cthread_t)forkPerform:(SEL)aSelector with:anArg detach:(BOOL)detach;
- - (cthread_t)forkPerform:(SEL)aSelector with:anArg0 with:anArg1 detach:(BOOL)detach;
- // This method creates a new thread in which the aSelector message is executed. The
- // cthread handle for the created thread is returned if 'detach:NO' was specified, and
- // normal cthread functions may be performed on this handle. Nil is returned if
- // 'detach:YES' was specified, since there is no guarantee that the returned value would
- // ever be valid (ie. The thread could terminate before you use the handle).
- //
- // -------------------------------------------------------------------------------------
- - (BOOL)threadIsActive:(cthread_t)theCthread;
- // threadIsActive: returns YES if theCthread is still active, otherwise NO.
- // This function queries the cthread_t structure which contains information concerning
- // the state of the thread, including whether theCthread has completed, or not. If
- // theCthread is nil, then threadIsActive will return NO.
- //
- // -------------------------------------------------------------------------------------
- - terminateThread;
- // This method will terminate the calling thread. This method does not return. If
- // this method is subclassed, then the subclass should make sure to call
- // [super terminateThread] to terminate the thread.
- //
- // -------------------------------------------------------------------------------------
- @end
-