process.c

Go to the documentation of this file.
00001 
00002 /*  $Id: process.c 1328 2006-04-24 20:02:59Z sethdill $    */
00003 
00004 /******************************************************************************
00005 
00006     UserLand Frontier(tm) -- High performance Web content management,
00007     object database, system-level and Internet scripting environment,
00008     including source code editing and debugging.
00009 
00010     Copyright (C) 1992-2004 UserLand Software, Inc.
00011 
00012     This program is free software; you can redistribute it and/or modify
00013     it under the terms of the GNU General Public License as published by
00014     the Free Software Foundation; either version 2 of the License, or
00015     (at your option) any later version.
00016 
00017     This program is distributed in the hope that it will be useful,
00018     but WITHOUT ANY WARRANTY; without even the implied warranty of
00019     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020     GNU General Public License for more details.
00021 
00022     You should have received a copy of the GNU General Public License
00023     along with this program; if not, write to the Free Software
00024     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00025 
00026 ******************************************************************************/
00027 
00028 #include "frontier.h"
00029 #include "standard.h"
00030 
00031 #ifdef MACVERSION
00032 #include <land.h>
00033 #include "mac.h"
00034 #endif
00035 
00036 #include "memory.h"
00037 #include "dialogs.h"
00038 #include "error.h"
00039 #include "file.h"
00040 #include "launch.h"
00041 #include "kb.h"
00042 #include "ops.h"
00043 #include "strings.h"
00044 #include "threads.h"
00045 #include "timedate.h"
00046 
00047 #include "lang.h"
00048 #include "langinternal.h"
00049 #include "shell.h"
00050 #include "shellmenu.h"
00051 #include "shellprivate.h"
00052 #include "shellhooks.h"
00053 #include "command.h"
00054 
00055 #include "about.h"
00056 #include "shell.rsrc.h"
00057 #include "scripts.h"
00058 #include "tablestructure.h"
00059 #include "cancoon.h"
00060 #include "process.h"
00061 #include "processinternal.h"
00062 #include "tableinternal.h"
00063 
00064 #include "frontierdebug.h" //6.2b7 AR
00065 #include "oplist.h" //6.2b11 AR
00066 #include "langsystem7.h"
00067 
00068 
00069 /*
00070 #include <profile.h>
00071 */
00072 
00073 hdlprocessrecord currentprocess = nil; /*process that's currently running*/
00074 
00075 hdlprocessrecord newlyaddedprocess = nil;
00076 
00077 unsigned short fldisableyield = 0;
00078 
00079 boolean flthreadkilled = false;
00080 
00081 boolean flcanusethreads = false;
00082 
00083 
00084 #ifdef WIN95VERSION
00085     
00086 static CRITICAL_SECTION processlistsection;
00087 
00088 static boolean processlistsectioninitialized = false;
00089 
00090 
00091 static void _entercriticalprocesssection (void) {
00092 
00093     if (!processlistsectioninitialized) {
00094 
00095         InitializeCriticalSection (&processlistsection);
00096 
00097         processlistsectioninitialized = true;
00098         }
00099     
00100     EnterCriticalSection (&processlistsection);
00101     }
00102 
00103 static void _leavecriticalprocesssection (void) {
00104 
00105     LeaveCriticalSection (&processlistsection);
00106     }
00107 
00108 #else
00109 
00110 #define _entercriticalprocesssection()
00111 
00112 #define _leavecriticalprocesssection()
00113 
00114 #endif
00115 
00116 
00117 typedef struct tythreadlist {
00118     
00119     hdlthreadglobals hfirst;
00120     } tythreadlist, **hdlthreadlist;
00121 
00122 
00123 static hdlthreadlist processthreadlist;
00124 
00125 /*
00126 xxx maintain a pool of two threads at all times
00127 */
00128 
00129 #define threadpoolsize 2
00130 
00131 /*
00132 static ThreadID threadpool [threadpoolsize] = {kNoThread, kNoThread}
00133 */
00134 
00135 
00136 static hdlprocessthread agentthread = 0;
00137 
00138 static boolean agentthreadsleeping = false;
00139 
00140 #define getthreadglobals(hthread) hthread
00141 
00142 static short ctprocessthreads = 0;
00143 
00144 
00145 static hdlprocesslist processlist = nil; /*the current process list*/
00146 
00147 static hdlprocesslist agentprocesslist = nil; /*the process list that agents came from*/
00148 
00149 static boolean flprocesscodedisposed = false; /*was process dispose code called?*/
00150 
00151 static hdltreenode honeshotcode = nil; /*used as a semaphore and real data*/
00152 
00153 static boolean flagentsenabled = true; /*the user's setting*/
00154 
00155 static int flagentsdisabled = 0; /*temporary, internal disable*/
00156 
00157 static boolean flpostedmemorymessage = false;
00158 
00159 static hdlthreadglobals hthreadglobals = nil;
00160 
00161 static typrocessstack processstack = {0};
00162 
00163 static boolean flpostedthreadsmessage = false;
00164 
00165 static boolean flcreatedagentprocess = false;
00166 
00167 static boolean flexitingthread = false;
00168 
00169 static boolean flinhibiterrorclear = false; // avoid adding a parm to several layers of global functions
00170 
00171 static long processonehottimeslice = 6;
00172 
00173 static long processagenttimeslice = 4;
00174 
00175 
00176 boolean setagentsenable (boolean flagents) {
00177     
00178     /*
00179     call this routine to permanently change the agents setting.  this is not 
00180     a push or a pop -- it's the global setting
00181     */
00182     
00183     langsetuserflag (idagentsenabledscript, flagents);
00184     
00185     flagentsenabled = flagents;
00186     
00187     return (true);
00188     } /*setagentsenable*/
00189 
00190 
00191 boolean pushprocess (register hdlprocessrecord hp) {
00192     
00193     /*
00194     4.1b3 dmb: carry new hthread field from current process to new process
00195 
00196     5.0a11 dmb: don't dereference nil hp to grab hthread field
00197     */
00198     
00199     typrocessstackrecord item;
00200     
00201     if (processstack.top >= ctprocesses) {
00202         
00203         shellinternalerror (idprocessstackfull, BIGSTRING ("\x17" "process stack overflow!"));
00204         
00205         return (false);
00206         }
00207     
00208     if ((currentprocess != nil) && (hp != nil))
00209         (**hp).hthread = (**currentprocess).hthread;
00210     
00211     item.hprocess = currentprocess;
00212     
00213     item.errormessagecallback = langcallbacks.errormessagecallback;
00214     
00215     item.debugerrormessagecallback = langcallbacks.debugerrormessagecallback;
00216     
00217 //  item.clearerrorcallback = langcallbacks.clearerrorcallback;
00218     
00219     item.herrorstack = langcallbacks.scripterrorstack;
00220     
00221     item.htablestack = hashtablestack;
00222     
00223     processstack.stack [processstack.top++] = item;
00224     
00225     currentprocess = hp;
00226     
00227     if (hp != nil) {
00228         
00229         langcallbacks.errormessagecallback = (**hp).errormessagecallback;
00230         
00231         langcallbacks.debugerrormessagecallback = (**hp).debugerrormessagecallback;
00232         
00233     //  langcallbacks.clearerrorcallback = (**hp).clearerrorcallback;
00234         
00235         langcallbacks.scripterrorstack = (**hp).herrorstack;
00236         
00237         hashtablestack = (**hp).htablestack;
00238         }
00239     
00240     return (true);
00241     } /*pushprocess*/
00242 
00243 
00244 boolean popprocess (void) {
00245     
00246     typrocessstackrecord item;
00247     
00248     assert (processstack.top > 0);
00249     
00250     item = processstack.stack [--processstack.top];
00251     
00252     currentprocess = item.hprocess;
00253     
00254     langcallbacks.scripterrorstack = item.herrorstack;
00255     
00256     langcallbacks.errormessagecallback = item.errormessagecallback;
00257     
00258     langcallbacks.debugerrormessagecallback = item.debugerrormessagecallback;
00259     
00260 //  langcallbacks.clearerrorcallback = item.clearerrorcallback;
00261     
00262     hashtablestack = item.htablestack;
00263     
00264     return (true);
00265     } /*popprocess*/
00266 
00267 
00268 void disposeprocess (hdlprocessrecord hprocess) {
00269     
00270     /*
00271     8/26/90 DW: we don't dispose of the code tree anymore -- it's up to scripts.c
00272     to dispose of it when the user installs a new version of the script.
00273     
00274     8/28/90 DW: this is a source of a memory leak.  new policy -- dispose of the code
00275     tree if it is a one-shot process.  does this make sense?  well -- if the process
00276     is one-shot then it must come from someone who wants us to manage the code, the
00277     guy is no longer around to dispose of it.  check it out, oneshots come from the
00278     menubar, and from the IPC back-door.
00279     
00280     12/27/90 dmb: if it's the current process, clear global
00281     
00282     1/8/91 dmb: instead of doing above (12/27/90), assert that process list 
00283     protection should avoid deletion of a running process
00284     
00285     5.0.2b15 dmb: profiling
00286     */
00287     
00288     register hdlprocessrecord hp = hprocess;
00289     tyvaluerecord val;
00290     
00291     assert (!(**hp).flrunning);
00292     
00293     if ((**hp).floneshot)
00294         langdisposetree ((**hp).hcode);
00295     
00296     langdisposetree ((**hp).holdcode);
00297     
00298     disposehandle ((Handle) (**hp).htablestack);
00299     
00300     disposehandle ((Handle) (**hp).herrorstack);
00301     
00302     if ((**hp).hprofiledata != nil) {
00303         
00304         assert ((**hp).flprofiling);
00305         
00306         setexternalvalue ((Handle) (**hp).hprofiledata, &val);
00307         
00308         disposevaluerecord (val, false);
00309         }
00310     
00311     disposehandle ((Handle) hp);
00312     } /*disposeprocess*/
00313 
00314 
00315 boolean newprocess (hdltreenode hcode, boolean floneshot, langerrorcallback errorcallback, long errorrefcon, hdlprocessrecord *hprocess) {
00316     
00317     /*
00318     create a new process with the indicated attributes
00319     
00320     12/25/91 dmb: dispose all handles on error
00321     
00322     1/2/92 dmb: check result of pushprocess
00323 
00324     5.0a22 dmb: xxxadded clearerrorcallback to process record, and flinhibiterrorclear
00325                 xxxrenamed process's errorcallback field to errormessagecallback
00326     */
00327     
00328     register hdlprocessrecord hp;
00329     hdlerrorstack herrorstack;
00330     hdltablestack htablestack;
00331     
00332     if (!newclearhandle (sizeof (typrocessrecord), (Handle *) hprocess))
00333         return (false);
00334     
00335     hp = *hprocess; /*copy into register*/
00336     
00337     if (!newclearhandle (sizeof (tyerrorstack), (Handle *) &herrorstack)) {
00338         
00339         disposehandle ((Handle) hp);
00340         
00341         return (false);
00342         }
00343     
00344     if (!newclearhandle (sizeof (tytablestack), (Handle *) &htablestack)) {
00345         
00346         disposehandle ((Handle) hp);
00347         
00348         disposehandle ((Handle) herrorstack);
00349         
00350         return (false);
00351         }
00352     
00353     (**hp).hcode = hcode;
00354     
00355     (**hp).floneshot = floneshot;
00356     
00357     (**hp).errormessagecallback = &langerrordialog;
00358 
00359     (**hp).debugerrormessagecallback = (langerrormessagecallback) &truenoop;
00360     
00361     (**hp).htablestack = htablestack;
00362     
00363     (**hp).herrorstack = herrorstack;
00364     
00365     (**hp).processstartedroutine = (langvoidcallback) &truenoop;
00366     
00367     (**hp).processkilledroutine = (langvoidcallback) &truenoop;
00368     
00369     if (!pushprocess (hp)) {
00370         
00371         disposeprocess (hp);
00372         
00373         return (false);
00374         }
00375     
00376     langpusherrorcallback (errorcallback, errorrefcon);
00377     
00378     popprocess ();
00379     
00380     #if 1
00381     {
00382     hdlhashtable htable;
00383     bigstring bsname;
00384     
00385     setemptystring (bsname);
00386     
00387     if (errorcallback != nil)
00388         (*errorcallback) (errorrefcon, 0, 0, &htable, bsname);
00389     
00390     if (isemptystring (bsname))
00391         langgetstringlist (anomynousthreadstring, bsname);
00392 
00393     copystring (bsname, (**hp).bsname);
00394     }
00395     #endif
00396 
00397     if (!floneshot)
00398         flcreatedagentprocess = true; /*for thread error reporting*/
00399     
00400     return (true);
00401     } /*newprocess*/
00402 
00403 
00404 static boolean deletefromprocesslist (hdlprocessrecord hprocess) {
00405     
00406     /*
00407     remove the process record from the process list.  don't dispose of 
00408     anything
00409     */
00410     
00411     register hdlprocessrecord hp = hprocess;
00412     boolean fl;
00413     
00414     _entercriticalprocesssection();
00415 
00416     fl = listunlink ((hdllinkedlist) (**hp).hprocesslist, (hdllinkedlist) hp);
00417 
00418     _leavecriticalprocesssection();
00419     
00420     if (!fl)
00421         return (false);
00422     
00423     (**hp).hprocesslist = nil; /*it's not in a list anymore*/
00424     
00425     return (true);
00426     } /*deletefromprocesslist*/
00427 
00428 
00429 static boolean deleteprocess (hdlprocessrecord hprocess) {
00430     
00431     deletefromprocesslist (hprocess);
00432     
00433     disposeprocess (hprocess);
00434     
00435     return (true);
00436     } /*deleteprocess*/
00437 
00438 
00439 boolean newprocesslist (hdlprocesslist *hlist) {
00440         
00441     return (newclearhandle (sizeof (typrocesslist), (Handle *) hlist));
00442     } /*newprocesslist*/
00443 
00444 
00445 void setcurrentprocesslist (hdlprocesslist hlist) {
00446     
00447     _entercriticalprocesssection ();
00448 
00449     processlist = hlist;
00450 
00451     _leavecriticalprocesssection ();
00452     } /*setcurrentprocesslist*/
00453 
00454 
00455 boolean disposeprocesslist (hdlprocesslist hprocesslist) {
00456     
00457     register hdlprocesslist hlist = hprocesslist;
00458     register hdlprocessrecord hp;
00459     register hdlprocessrecord hnext;
00460     register boolean flbusy;
00461     
00462     if (hlist == nil) /*empty list*/
00463         return (true);
00464     
00465     flbusy = (**hlist).ctrunning > 0; /*something in list is running now*/
00466     
00467     hp = (**hlist).hfirstprocess;
00468     
00469     while (hp != nil) {
00470         
00471         hnext = (**hp).hnextprocess;
00472         
00473         if (flbusy)
00474             (**hp).fldisposewhenidle = true;
00475         else
00476             disposeprocess (hp);
00477         
00478         hp = hnext; /*advance to next node*/
00479         } /*while*/
00480     
00481     if (flbusy)
00482         (**hlist).fldisposewhenidle = true;
00483     else
00484         disposehandle ((Handle) hlist);
00485     
00486     return (true);
00487     } /*disposeprocesslist*/
00488 
00489 
00490 boolean processfindcode (hdltreenode hcode, hdlprocessrecord *hprocess) {
00491     
00492     /*
00493     search the process list for a record that points to the indicated code.
00494     */
00495     
00496     register hdlprocessrecord x;
00497     register hdltreenode hsearch = hcode;
00498     
00499     if (processlist == nil)
00500         return (false);
00501     
00502     x = (**processlist).hfirstprocess;
00503     
00504     while (x != nil) { /*list is nil-terminated*/
00505         
00506         if ((**x).hcode == hsearch) { /*perform replacement*/
00507             
00508             *hprocess = x;
00509             
00510             return (true);
00511             }
00512         
00513         x = (**x).hnextprocess;
00514         } /*while*/
00515     
00516     return (false);
00517     } /*processfindcode*/
00518 
00519 
00520 static boolean flvisitingthreads = false; // *** debug
00521 
00522 static boolean visitprocessthreads (pascal boolean (*visit) (hdlthreadglobals, long), long refcon) {
00523     
00524     /*
00525     visit all process threads until the visit routine returns true
00526 
00527     6.26.97 dmb: keep globals in sync for current thread. visit routine must
00528     no longer operate directly on globals
00529     */
00530     
00531     register hdlthreadglobals hg;
00532     hdlthreadglobals hnext, hprev = NULL;
00533     boolean fl = false;
00534     
00535 #ifdef landinclude
00536     if (landvisitsleepingthreads ((landqueuepopcallback) visit, refcon))
00537         return (true);
00538 #endif
00539     
00540     flvisitingthreads = true;
00541 
00542     for (hg = (**processthreadlist).hfirst; hg != nil; hg = hnext) {
00543         
00544         hnext = (**hg).hnextglobals;
00545 
00546         if (hg == hthreadglobals)
00547             copythreadglobals (hg);
00548 
00549         fl = (*visit) (hg, refcon);
00550         
00551         if (hg == hthreadglobals)
00552             swapinthreadglobals (hg);
00553         
00554         if (fl)
00555             break;
00556 
00557         hprev = hg;
00558         }
00559     
00560     flvisitingthreads = false;
00561     
00562     return (fl);
00563     } /*visitprocessthreads*/
00564 
00565 
00566 boolean processkill (hdlprocessrecord hprocess) {
00567     
00568     /*
00569     8/31/92 dmb: wake up the thread if necessary
00570     
00571     4.1b3 dmb: use wakeprocessthread to wake any sleeping thread, not 
00572     just threads waiting for AE replies (managed by landsystem7)
00573     */
00574     
00575     register hdlprocessrecord hp = hprocess;
00576     
00577     if (hp == nil) /*defensive driving*/
00578         return (false);
00579     
00580     if ((**hp).flscheduled || (**hp).flrunning) {
00581     
00582         (**hp).fldisposewhenidle = true;
00583         
00584         wakeprocessthread ((**hp).hthread);
00585         }
00586     else {
00587         (*(**hp).processkilledroutine) (); /*6.13.97 dmb*/
00588         
00589         deleteprocess (hp);
00590         }
00591     
00592     return (true);
00593     } /*processkill*/
00594 
00595 
00596 static pascal boolean codedisposedvisit (hdlthreadglobals hthread, long refcon) {
00597     
00598     /*
00599     search the thread's error stack for a refcon that matches the one 
00600     of the disposed code.  if one is found, kill the thread's process 
00601     and return true.
00602     
00603     2.1b1 dmb: never return true; there may be more threads to kill
00604     
00605     2.1b13 dmb: for runtime, check the process' code before going thru 
00606     the stack.
00607 
00608     5.1.3 dmb: check for nil process. I notice that this is weird; we're 
00609     walking the thread list, looking for processes, while other similar 
00610     routines walk the processlist. the role of the process list might 
00611     need some rethinking
00612     */
00613     
00614     register hdlthreadglobals hg = getthreadglobals (hthread);
00615     register hdlerrorstack hs;
00616     short ix, ixtop;
00617     
00618     if ((**hg).hprocess == nil) // 5.1.3 dmb: if nil, error stack isn't valid
00619         return (false);
00620     
00621     hs = (**hg).langcallbacks.scripterrorstack;
00622     
00623     if (hs == nil)
00624         return (false);
00625     
00626     ixtop = (**hs).toperror;
00627     
00628     for (ix = 0; ix < ixtop; ++ix) {
00629         
00630         if ((**hs).stack [ix].errorrefcon == refcon) {
00631             
00632             (**hg).flthreadkilled = true; /*maim it*/
00633             
00634             (**hg).flretryagent = true;
00635             
00636             break;
00637             }
00638         }
00639     
00640     return (false); /*keep visiting*/
00641     } /*codedisposedvisit*/
00642 
00643 
00644 void processcodedisposed (long refcon) {
00645     
00646     /*
00647     disable all process threads who have code with the indicated refcon
00648     in their runtime stack (the error stack).
00649     
00650     it might be better to generate an error instead of allowing this to 
00651     occur, but for now we don't
00652     
00653     7/25/92 dmb: must check current thread for code, and kill the script if 
00654     found.
00655 
00656     6.26.97 dmb: visitprocessthreads now handles the current thread fine; no 
00657     need to call langfinderrorrefcon directly too.
00658     */
00659     
00660     visitprocessthreads (&codedisposedvisit, refcon); /*terminates if visit proc returns true*/
00661     } /*processcodedisposed*/
00662 
00663 
00664 boolean processreplacecode (hdltreenode holdcode, hdltreenode hnewcode) {
00665     
00666     /*
00667     search the process list for a record that points to the indicated "old" code.
00668     
00669     if we find it, replace with the new code handle.
00670     
00671     search the whole list -- allow for possibility of fork verb someday that
00672     clones a process.
00673     
00674     3/12/91 dmb: if process is sleeping, wake it up to let new code run
00675     
00676     9/25/91 dmb: even when old code isn't found, call the codereplacedcallback
00677     */
00678     
00679     register hdlprocessrecord x;
00680     register boolean flreplaced = false;
00681     register hdltreenode hsearch = holdcode;
00682     
00683     if (processlist == nil)
00684         return (false);
00685     
00686     x = (**processlist).hfirstprocess;
00687     
00688     while (x != nil) { /*list is nil-terminated*/
00689         
00690         if ((**x).hcode == hsearch) { /*perform replacement*/
00691             
00692             if ((**x).flrunning)
00693                 (**x).holdcode = hsearch;
00694             else
00695                 langdisposetree (hsearch);
00696             
00697             (**x).hcode = hnewcode;
00698             
00699             (**x).sleepuntil = 0; /*wake agent up if sleeping*/
00700             
00701             flreplaced = true;
00702             
00703             /*
00704             (*langcallbacks.codereplacedcallback) (hsearch, hnewcode);
00705             */
00706             }
00707         
00708         x = (**x).hnextprocess;
00709         } /*while*/
00710     
00711     (*langcallbacks.codereplacedcallback) (hsearch, hnewcode); /*always notify callback*/
00712     
00713     return (flreplaced);
00714     } /*processreplacecode*/
00715 
00716 
00717 boolean processdisposecode (hdltreenode hcode) {
00718     
00719     /*
00720     search the process list for a record that points to the indicated code.
00721     
00722     if we find it, dispose of the record and the code, and return true.
00723     
00724     search the whole list -- allow for possibility of fork verb someday that clones
00725     a process.
00726     
00727     1/8/91 dmb: if the process turns out to be running, generate an assert failure 
00728     for now.  we could possibly generate an langerror if neccessary.  the only 
00729     way I can see this happening is if the user (or a script) deletes a running 
00730     script from a table in the background -- possibly easy to do while debugging, 
00731     but also possibly a well-deserved crash!
00732     
00733     7/2/92 dmb: we've come a long way since 1/8/91; there's enough action going on 
00734     in the background that we have to handle the case where the code is running 
00735     without crashing -- into the debugger or otherwise.  fortunately, we have a nice 
00736     mechanisms for this now: processkill.
00737     */
00738     
00739     register hdlprocessrecord x;
00740     register hdlprocessrecord hnext;
00741     register boolean flfound = false;
00742     register hdltreenode hsearch = hcode;
00743     
00744     if (processlist == nil)
00745         return (false);
00746     
00747     x = (**processlist).hfirstprocess;
00748     
00749     while (x != nil) { /*list is nil-terminated*/
00750         
00751         hnext = (**x).hnextprocess;
00752         
00753         if ((**x).hcode == hsearch) { /*remove it*/
00754             
00755             processkill (x); /*dispose or mark as killed for later disposal*/
00756             
00757             /*
00758             assert (!(**x).flrunning); /%if this can happen, maybe generate langerror?%/
00759             
00760             deletefromprocesslist (x);
00761             
00762             disposehandle ((Handle) x);
00763             */
00764             
00765             flprocesscodedisposed = true;
00766             
00767             flfound = true;
00768             }
00769         
00770         x = hnext;
00771         } /*while*/
00772     
00773     return (flfound);
00774     } /*processdisposecode*/
00775 
00776 
00777 boolean processagentsleep (long ctseconds) {
00778     
00779     /*
00780     2/12/92 dmb: added flsleepinbackground logic
00781     
00782     4/21/92 dmb: removed flsleepinbackground logic -- too ugly
00783     
00784     8/14/92 dmb: handle overflow. also ensure that negative value causes no sleep.
00785     */
00786     
00787     register hdlprocessrecord hp = currentprocess;
00788     register unsigned long x;
00789     unsigned long now;
00790     
00791     if (hp == nil) 
00792         return (false);
00793     
00794     /*
00795     fldontbackground = ctseconds < 0;
00796     
00797     if (fldontbackground)
00798         ctseconds = -ctseconds;
00799     */
00800     
00801     now = timenow ();
00802     
00803     if (ctseconds < 0)
00804         x = now;
00805     
00806     else {
00807         x = 0xFFFFFFFF;
00808         
00809         if ((unsigned) ctseconds < (x - now)) /*check for less-than-infinite sleep, avoiding overflow*/
00810             x = now + ctseconds;
00811         }
00812     
00813     (**hp).sleepuntil = x;
00814     
00815     /*
00816     (**hp).flsleepinbackground = fldontbackground;
00817     */
00818     
00819     return (true);
00820     } /*processagentsleep*/
00821 
00822 
00823 boolean processisoneshot (boolean flnilok) {
00824     
00825     /*
00826     the interpreter is allowed to know if it's running a one-shot process or not.
00827     
00828     example: if it's a one shot process, then cmd-period will allow it to be killed.
00829     
00830     9/20/90 dmb:  check currentprocess for nil
00831     
00832     1/3/90 dmb: added flnilok parameter.  if true, nil process is considered one-shot
00833     */
00834     
00835     if (currentprocess == nil)
00836         return (flnilok);
00837     
00838     return ((**currentprocess).floneshot);
00839     } /*processisoneshot*/
00840 
00841 
00842 static boolean processbackgroundtask (boolean flresting) {
00843     
00844     /*
00845     return true if background tasks should be given a shot while 
00846     interpreting a script.
00847     
00848     7/4/91 dmb: don't background while langerror is disabled.  (otherwise, 
00849     would need to save in thread state)
00850     */
00851     
00852     if (langerrorenabled ())
00853         if (flagentsenabled || processisoneshot (false))
00854             return (scriptbackgroundtask (flresting));
00855     
00856     return (true);
00857     } /*processbackgroundtask*/
00858 
00859 
00860 boolean debuggingcurrentprocess (void) {
00861     
00862     register hdlprocessrecord hp = currentprocess;
00863     
00864     if (hp == nil)
00865         return (false);
00866     
00867     if (!(**hp).floneshot) /*no debugging inside background processes*/
00868         return (false);
00869     
00870     return ((**hp).fldebugging);
00871     } /*debuggingcurrentprocess*/
00872 
00873 
00874 boolean processstartprofiling (boolean fltimesliced) {
00875 
00876     /*
00877     5.0.2b15 dmb: enable profiling. create a new table if needed. (profiling can 
00878     be "paused" without losing data)
00879     */
00880     
00881     hdlprocessrecord hp = currentprocess;
00882     tyvaluerecord val;
00883     hdltablevariable hvariable;
00884     short errcode;
00885     
00886     if (hp == nil)
00887         return (false);
00888     
00889     (**hp).flprofiling = true;
00890     
00891     (**hp).flprofilesliced = fltimesliced;
00892     
00893     if ((**hp).hprofiledata == nil) {// create table to store data
00894 
00895         if (!langexternalnewvalue (idtableprocessor, nil, &val))
00896             return (false);
00897         
00898         gettablevariable (val, &hvariable, &errcode);
00899         
00900         (**hp).hprofiledata = (hdlexternalvariable) hvariable;
00901         }
00902     
00903     langstartprofiling ();
00904     
00905     return (true);
00906     } /*processstartprofiling*/
00907 
00908 
00909 boolean processstopprofiling (void) {
00910 
00911     /*
00912     5.0.2b15 dmb: disable profiling. don't automatically throw away the table, 
00913     so that profiling can be "paused" without losing data.
00914     */
00915     
00916     hdlprocessrecord hp = currentprocess;
00917     
00918     if (!hp || !(**hp).flprofiling)
00919         return (false);
00920     
00921     langstopprofiling ();
00922     
00923     (**hp).flprofiling = false;
00924     
00925     return (true);
00926     } /*processstopprofiling*/
00927 
00928 
00929 boolean profilingcurrentprocess (void) {
00930     
00931     register hdlprocessrecord hp = currentprocess;
00932     
00933     if (hp == nil)
00934         return (false);
00935     
00936     return ((**hp).flprofiling);
00937     } /*profilingcurrentprocess*/
00938 
00939 
00940 static boolean processdebugger (hdltreenode hnode) {
00941     
00942     /*
00943     1/8/91 dmb: hopefully, at this point, we no longer have to disable 
00944     agents before entering the debugger
00945     */
00946     
00947     if (!debuggingcurrentprocess ())
00948         return (true);
00949     
00950     return (scriptdebugger (hnode)); /*pass the message to the script editor*/
00951     } /*processdebugger*/
00952 
00953 
00954 static boolean processscriptkilled (boolean flchecknow) {
00955     
00956     /*
00957     returns true if the current running script should be killed by the
00958     interpreter.  it's called back from within the interpreter.
00959     
00960     7/2/91: this used to be in scripts.c, but is now more appropriate 
00961     at this level.  the debugger takes care of it's own killing
00962     
00963     9/27/92 dmb: added flichecknow parameter
00964     */
00965     
00966     register hdlprocessrecord hp = currentprocess;
00967     
00968     if (flthreadkilled)
00969         return (true);
00970     
00971     if ((hp != nil) && (**hp).fldisposewhenidle)
00972         return (true);
00973     
00974     if (!flchecknow || fldisableyield)
00975         return (false);
00976     
00977     if (processisoneshot (true)) { /*cmd-period doesn't kill background tasks*/
00978         
00979         if (keyboardescape ()) { /*user pressed cmd-period or something like that*/
00980         
00981             keyboardclearescape (); /*consume it*/
00982             
00983             return (true);
00984             }
00985         }
00986     
00987     return (false);
00988     
00989     /*
00990     return (scriptkilled ());
00991     */
00992     } /*processscriptkilled*/
00993 
00994 
00995 static boolean processpushsourcecode (hdlhashtable htable, hdlhashnode hnode, bigstring bsname) {
00996     
00997     return (scriptpushsourcecode (htable, hnode, bsname));
00998     } /*processpushsourcecode*/
00999 
01000 
01001 static boolean processpopsourcecode (void) {
01002     
01003     return (scriptpopsourcecode ());
01004     } /*processpopsourcecode*/
01005 
01006 
01007 static boolean processpushtable (hdlhashtable *htable) {
01008     
01009     if (!debuggingcurrentprocess ())
01010         return (langdefaultpushtable (htable));
01011     
01012     return (scriptpushtable (htable));
01013     } /*processpushtable*/
01014 
01015 
01016 static boolean processpoptable (hdlhashtable htable) {
01017     
01018     if (!debuggingcurrentprocess ())
01019         return (langdefaultpoptable (htable));
01020     
01021     return (scriptpoptable (htable));
01022     } /*processpoptable*/
01023 
01024 
01025 boolean goodthread (hdlprocessthread hthread) {
01026     
01027     /*
01028     make sure that the thread hasn't been killed.  (see killdependentprocesses)
01029     */
01030     
01031     register hdlthreadglobals hp = (hdlthreadglobals) hthread;
01032     
01033     if (hp == nil)
01034         return (false);
01035     
01036     return (!(**getthreadglobals (hp)).flthreadkilled);
01037     } /*goodthread*/
01038 
01039 
01040 boolean ingoodthread (void) {
01041     
01042     return (goodthread (getcurrentthread ()));
01043     } /*ingoodthread*/
01044 
01045 
01046 #ifndef version42orgreater
01047 
01048 boolean inmainthread (void) {
01049     
01050     /*
01051     2.1a7 dmb: the main thread's true id isn't idapplicationthread, so 
01052     check the id stored in the current globals record instead
01053     
01054     5.0b17 dmb: handle nil globals
01055     */
01056     
01057     return (hthreadglobals && (**hthreadglobals).idthread == idapplicationthread);
01058     
01059     /*
01060     ThreadID id;
01061     
01062     if (!flcanusethreads)
01063         return (true);
01064     
01065     if (GetCurrentThread (&id) != noErr)
01066         return (true);
01067     
01068     return (id == idapplicationthread);
01069     */
01070     } /*inmainthread*/
01071 
01072 #endif
01073 
01074 
01075 boolean infrontierthread (void) {
01076     
01077     /*
01078     3.0.4b8 dmb: return true if we're in _any_ frontier thread, as 
01079     opposed to an osaclient process
01080     */
01081     
01082     return (!hthreadglobals || (**hthreadglobals).idthread);
01083     } /*infrontierthread*/
01084 
01085 
01086 boolean processsleep (hdlprocessthread hthread, unsigned long timeout) {
01087     
01088     /*
01089     sleep until another thread wakes us.  when control returns, call 
01090     ingoodthread to verify that we haven't been maimed while sleeping
01091     
01092     4.1b5 dmb: make sure that TickCount + timeout don't overflow unsigned long.
01093     
01094     5.1.5b16 dmb: like threads.c, we now accept nil to mean the current thread.
01095 
01096     6.2b7 AR: Properly handle wrapping around of tick count (happens every 16.5 days on Win32).
01097     If timeout is 0xffffffff, processchecktimeouts will never wake the thread.
01098     */
01099     
01100     unsigned long timetowake;
01101     boolean fl;
01102     unsigned long ticks = gettickcount ();
01103     
01104     if (hthread == nil) //sleep the current thread
01105         hthread = hthreadglobals;
01106     
01107     if (processissleeping (hthread)) {
01108     
01109         if (hthread == agentthread)
01110             agentthreadsleeping = false;    /*no longer in standard, every-second sleep*/
01111         
01112         return (true);
01113         }
01114     
01115     if (((long) timeout >= 0) && (ticks + timeout > timeout)) /*positive, not wrapping around*/
01116         timetowake = ticks + timeout;
01117     else
01118         timetowake = 0xffffffff;
01119     
01120     (**(hdlthreadglobals) hthread).timetowake = timetowake;
01121     
01122     (**(hdlthreadglobals) hthread).sleepticks = timeout;
01123 
01124     (**(hdlthreadglobals) hthread).timebeginsleep = ticks;
01125 
01126     #if flruntime
01127     
01128         ++ctsleepingthreads;
01129     
01130     #else
01131         
01132         if (processlist != nil)
01133             ++(**processlist).ctsleeping;
01134         
01135     #endif
01136     
01137     if (hthread == hthreadglobals)
01138         fl = threadsleep (nil);
01139     else
01140         fl = threadsleep ((**(hdlthreadglobals) hthread).idthread);
01141     
01142     return (fl && goodthread (hthread));
01143     } /*processsleep*/
01144 
01145 
01146 boolean processissleeping (hdlprocessthread hthread) {
01147     
01148     /*
01149     see if the thread is sleeping
01150     
01151     5.0a19 dmb: use our own data to find the answer; don't know
01152     now to query the thread's state directly (or if it's possible)
01153     */
01154     
01155     if (hthread == nil)
01156         return (false);
01157     
01158     if ((hthread == agentthread) && agentthreadsleeping)
01159         return (true);
01160 
01161     return ((**(hdlthreadglobals) hthread).timetowake != 0);
01162     } /*processissleeping*/
01163 
01164 
01165 boolean processwake (hdlprocessthread hthread) {
01166     
01167     /*
01168     wake the thread
01169 
01170     5.0b12 dmb: do the thread releasing here, after we've made
01171     sure that the thread is actually asleep. otherwise, it can 
01172     die after we release, before we kill it. also, we must update
01173     the data structure (timetowake) before we wake it. it may 
01174     disappear immediately
01175 
01176     6.2b11 AR: We sometimes see threads hanging spontaneously (e.g. on www.weblogs.com).
01177     Our theory is that for some reason, the first attempt at waking the thread fails.
01178     But since we reset the timetowake field before we attempt to wake the thread,
01179     we never get a second chance at waking it. Our data structures tell us its alive
01180     when its actually still sleeping.
01181     Now, we only reset the timetowake field if we successfully woke the thread.
01182     Since we no longer release the global semaphore, the thread can no longer die
01183     before we've reset the timetowake field. It won't have a chance to die until
01184     our caller eventually releases releases the global semaphore.
01185     */
01186     
01187     boolean fl;
01188     hdlthread idthread;
01189     
01190     if (!processissleeping (hthread))
01191         return (false);
01192 
01193 /*  (**(hdlthreadglobals) hthread).timetowake = 0; //first -- 6.2b11 AR: commented out
01194     
01195     (**(hdlthreadglobals) hthread).sleepticks = 0;
01196 
01197     (**(hdlthreadglobals) hthread).timebeginsleep = 0;
01198 */
01199     idthread = (**(hdlthreadglobals) hthread).idthread;
01200 
01201     //releasethreadglobals (); /*6.1b5 AR: commented out*/
01202     //releasethreadglobalsnopriority ();/*6.1b12 AR;  commented out again in 6.2b8 AR*/
01203 
01204     fl = threadwake (idthread, hthread != agentthread);
01205     
01206     //grabthreadglobalsnopriority ();
01207     //grabthreadglobals ();
01208     
01209     if (fl) {
01210 
01211         (**(hdlthreadglobals) hthread).timetowake = 0; //6.2b11 AR: only if successful
01212         
01213         (**(hdlthreadglobals) hthread).sleepticks = 0;
01214 
01215         (**(hdlthreadglobals) hthread).timebeginsleep = 0;
01216         
01217         #if flruntime
01218         
01219             --ctsleepingthreads;
01220         
01221         #else
01222             
01223             if (processlist != nil)
01224                 --(**processlist).ctsleeping;
01225             
01226         #endif
01227         }
01228     
01229     THREADS_ASSERT_1 (fl);
01230     
01231     return (fl);
01232     } /*processwake*/
01233 
01234 
01235 boolean processyield (void) {
01236     
01237     /*
01238     yield to other process threads.  when control returns, check the first 
01239     long in the userbytes array to verify that we haven't been maimed by 
01240     the loss of our globals.  (see killdependentprocesses)
01241     
01242     8/21/91 dmb: preserve shellwindow by popping it off the stack, pushing 
01243     it back on after we've yielded.  for now, we only handle a single window on 
01244     the stack; it we can't assume this, it may be reasonable to refuse to yield 
01245     if there are more globals pushed
01246     
01247     11/6/92 dmb: must check for nil processlist on pre-yield reference too
01248 
01249     4.30.97 dmb: big change for the PC: the main thread never needs to call 
01250     threadyield. why? because if the main thread is running a script, 
01251     fldisableyield is always true. if it's _not_ running a script, it isn't 
01252     using any of the thread globals, so it shouldn't block waiting to possess 
01253     them. this implies that, to run a script, the main thread must first 
01254     grab the thread globals semaphore.
01255     */
01256     
01257     WindowPtr oldwindow;
01258     boolean flscriptwasrunning;
01259     
01260     if (fldisableyield)
01261         return (true);
01262     
01263     if (!flcanusethreads)
01264         return (true);
01265     
01266     if (!infrontierthread()) {  // should never happen
01267         
01268         #ifdef fldebug
01269         DebugStr (BIGSTRING ("\x0c" "don't yield!"));
01270         #endif
01271         
01272         return (true);
01273         }
01274     
01275     /*
01276     if (_profile)
01277         return (true);
01278     */
01279     
01280     #if flruntime
01281     
01282         if (flscriptresting)
01283             ++ctsleepingthreads;
01284     
01285     #else
01286     
01287         oldwindow = shellwindow;
01288         
01289         flscriptwasrunning = flscriptrunning;
01290         
01291         if (flscriptresting && (processlist != nil))
01292             ++(**processlist).ctsleeping;
01293         
01294     #endif
01295     
01296     threadyield (flscriptresting);
01297     
01298     #if flruntime
01299     
01300         if (flscriptresting)
01301             --ctsleepingthreads;
01302         
01303     #else
01304     
01305         if (flscriptresting && (processlist != nil))
01306             --(**processlist).ctsleeping;
01307         
01308         assert ((shellwindow == oldwindow) || (shellwindow == nil));
01309         
01310         assert (flscriptwasrunning == flscriptrunning);
01311     
01312     #endif
01313     
01314     return (ingoodthread ());
01315     } /*processyield*/
01316 
01317 
01318 boolean processyieldtoagents (void) {
01319     
01320     /*
01321     yield the processor until the current agent thread has completed.
01322     
01323     call this routine when you want to do something that doesn't sit well 
01324     with agents, like throwing away code trees.
01325     
01326     10/23/91 dmb: if we're an agent, return true so caller doesn't think 
01327     yield actually failed (in which case it would terminate).
01328     
01329     6/1/93 dmb: save/restore flagentsenabled instead of using agentsdisable, 
01330     with was never really doing anything anyway.
01331 
01332     5.0a25 dmb: if agent thread is nil, we're done
01333     */
01334     
01335     boolean fl = true;
01336     
01337     if (fldisableyield)
01338         return (true);
01339     
01340     if (!processisoneshot (true)) /*we're an agent!*/
01341         return (true);
01342     
01343     if (agentthread == nil)
01344         return (true);
01345     
01346     #if flruntime
01347     
01348         fl = processyield ();
01349     
01350     #else
01351         
01352         ++flagentsdisabled;
01353         
01354         wakeprocessthread (agentthread);
01355         
01356         while (agentthread != nil) {
01357             
01358         //  #ifdef MACVERSION
01359             if (!processyield ()) {
01360                 
01361                 fl = false;
01362                 
01363                 break;
01364                 }
01365         //  #endif
01366             }
01367         
01368         --flagentsdisabled;
01369     
01370     #endif
01371     
01372     return (fl);
01373     } /*processyieldtoagents*/
01374 
01375 
01376 void disposethreadglobals (hdlthreadglobals hglobals) {
01377     
01378     /*
01379     3.0.4b8 dmb: if this is hthreadglobals, set it to nil
01380     */
01381     
01382     register hdlthreadglobals hg = hglobals;
01383     
01384     if (flvisitingthreads)
01385         assert (false);
01386     else
01387         listunlink ((hdllinkedlist) processthreadlist, (hdllinkedlist) hg);
01388     
01389     if (hg == hthreadglobals) {
01390 
01391         #ifdef WIN95VERSION
01392             if ( flcominitialized )
01393                 shutdownCOM();
01394         #endif
01395         
01396         disposehandle ((Handle) hashtablestack);
01397         
01398         hashtablestack = nil;
01399         
01400         hthreadglobals = nil;
01401 
01402         /*
01403         //6.1b4 AR: Prevent us from crashing after the serial number dialog was cancelled
01404         
01405         assert (!shellwindow);
01406         
01407         assert (!outlinedata && !topoutlinestack);
01408         */
01409         
01410         /*
01411         while (shellpopglobals ())
01412             ;
01413 
01414         while (oppopoutline ())
01415             ;
01416 
01417         opsetoutline (nil);
01418         */
01419         }
01420     else {
01421         
01422         assert (!(**hg).shellwindow);
01423 
01424         assert (!(**hg).outlinedata && !(**hg).topoutlinestack);
01425 
01426         disposehandle ((Handle) (**hg).htablestack);
01427         }
01428     
01429     disposehandle ((Handle) hg);
01430     } /*disposethreadglobals*/
01431 
01432 
01433 boolean newthreadglobals (hdlthreadglobals *hglobals) {
01434     
01435     register hdlthreadglobals hg;
01436     hdltablestack htablestack;
01437 #ifdef landinclude
01438     tyapplicationid id;
01439 #endif
01440     
01441     if (!newclearhandle (sizeof (tythreadglobals), (Handle *) hglobals))
01442         return (false);
01443     
01444     hg = *hglobals;
01445     
01446     if (!newclearhandle (sizeof (tytablestack), (Handle *) &htablestack)) {
01447         
01448         disposehandle ((Handle) hg);
01449         
01450         return (false);
01451         }
01452     
01453     (**hg).htablestack = htablestack;
01454     
01455     (**hg).langcallbacks = langcallbacks;
01456     
01457     (**hg).timesliceticks = processonehottimeslice;
01458 
01459     /*
01460         2006-04-24 smd
01461         has COM been initialized in this thread?
01462     */
01463     (**hg).flcominitialized = false;
01464 
01465 #ifdef landinclude  
01466     id = getprocesscreator ();
01467     
01468     (**hg).applicationid = id;
01469     
01470     (**hg).eventcreatecallback = &landsystem7defaultcreate;
01471     
01472     (**hg).eventsendcallback = &landsystem7defaultsend;
01473     
01474     (**hg).eventsettings.timeoutticks = kNoTimeOut;
01475 #endif
01476 
01477     listlink ((hdllinkedlist) processthreadlist, (hdllinkedlist) hg);
01478     
01479     return (true);
01480     } /*newthreadglobals*/
01481 
01482 
01483 hdlthreadglobals getcurrentthreadglobals (void) {
01484     
01485     return (hthreadglobals);
01486     } /*getcurrentthreadglobals*/
01487 
01488 #define flprofile
01489 #ifdef flprofile
01490 static unsigned long totalticksin = 0;
01491 static unsigned long totalticksout = 0;
01492 static unsigned long lastswapticks = 0;
01493 #endif
01494 
01495 void copythreadglobals (hdlthreadglobals hglobals) {
01496     
01497     /*
01498     6/27/91 dmb: once this stuff has settled down, we should probably define 
01499     a global thread record that contains all the globals that need to be 
01500     swapped, instead of doing it piecemeal like this.
01501     
01502     7/15/91 dmb: now use threadglobals record
01503     
01504     1/28/92 dmb: processstack and multiple error hooks are now maintained in context
01505     
01506     5.1.5b10 dmb: don't set shellwindow, etc. unless context has been swapped in
01507     */
01508     
01509     register hdlthreadglobals hg = hglobals;
01510 #ifdef landinclude
01511     register hdllandglobals hlg;
01512 #endif
01513 
01514     assert (!flvisitingthreads || hg == hthreadglobals);
01515 
01516     #ifdef flprofile
01517     if (cancoonglobals && !flvisitingthreads) { 
01518         unsigned long ticks;
01519     
01520         aboutsetthreadstring (nil, false);
01521 
01522         #ifdef MACVERSION
01523         ticks = gettickcount ();
01524         #else
01525         ticks = GetTickCount ();
01526         #endif
01527 
01528         totalticksin += ticks - lastswapticks;
01529         lastswapticks = ticks;
01530         }
01531 
01532     #endif
01533     
01534     (**hg).hccglobals = cancoonglobals;
01535     
01536     if ((**hg).timestarted != 0) { /*context has been swapped in*/
01537         
01538         (**hg).hprocess = currentprocess;
01539         
01540         (**hg).htable = currenthashtable;
01541         
01542         (**hg).flthreadkilled = flthreadkilled;
01543         
01544         (**hg).fldisableyield = fldisableyield;
01545         
01546         (**hg).htablestack = hashtablestack;
01547         
01548         (**hg).processstack = processstack;
01549         
01550         (**hg).globalsstack = globalsstack;
01551         
01552         (**hg).flscriptrunning = flscriptrunning;
01553         
01554         (**hg).flscriptresting = flscriptresting;
01555         
01556         (**hg).cterrorhooks = cterrorhooks;
01557         
01558         moveleft (errorhooks, (**hg).errorhooks, sizeof (errorhookcallback) * maxerrorhooks);
01559         
01560         (**hg).shellwindow = shellwindow;
01561         
01562     //  if (shellwindow == nil && topoutlinestack == 0) // if we're not working in a window, outlinedata is stray
01563     //      assert (outlinedata == nil); //(**hg).outlinedata = nil;
01564         
01565         (**hg).outlinedata = outlinedata;
01566         
01567         (**hg).topoutlinestack = topoutlinestack;
01568         
01569         moveleft  (outlinestack, (**hg).outlinestack, sizeof (hdloutlinerecord) * ctoutlinestack);
01570         
01571         outlinedata = nil; //don't let anyone else mess with us
01572 
01573         topoutlinestack = 0;
01574         
01575         (**hg).ctscanlines = ctscanlines;
01576         
01577         (**hg).ctscanchars = ctscanchars;
01578         
01579         (**hg).herrornode = herrornode;
01580         
01581         (**hg).flreturn = flreturn;
01582         
01583         (**hg).flbreak = flbreak;
01584         
01585         (**hg).flcontinue = flcontinue;
01586         
01587         (**hg).fllangerror = fllangerror;
01588         
01589         (**hg).langerrordisable = langerrordisable;  /*6.1.1b2 AR*/
01590         
01591         (**hg).tryerror = tryerror;
01592 
01593         (**hg).tryerrorstack = tryerrorstack;
01594 
01595         /*
01596             2006-04-24 smd
01597             has COM been initialized in this thread?
01598         */
01599         (**hg).flcominitialized = flcominitialized;
01600         }
01601     
01602     (**hg).langcallbacks = langcallbacks;
01603     
01604     #ifdef flcomponent
01605     
01606     hlg = landgetglobals ();
01607     
01608     (**hg).eventcreatecallback = (**hlg).eventcreatecallback;
01609     
01610     (**hg).eventsendcallback = (**hlg).eventsendcallback;
01611     
01612     (**hg).applicationid = (**hlg).applicationid;
01613     
01614     (**hg).eventsettings = (**hlg).eventsettings;
01615     
01616     #endif
01617     
01618     } /*copythreadglobals*/
01619 
01620 
01621 void swapinthreadglobals (hdlthreadglobals hglobals) {
01622     
01623     /*
01624     1/28/92 dmb: processstack and multiple error hooks are now maintained 
01625     in context
01626     
01627     1/20/93 dmb: if thread doesn't have a set of cancoon globals, leave 
01628     everything alone.  (currently, this occurs in component threads.)
01629     
01630     5.0b17 dmb: handle nil for dbverbs
01631     
01632     5.0.2b20 dmb: added tryerror to globals
01633 
01634     6.0b5 rab: added tryerrorstack
01635     */
01636     
01637     register hdlthreadglobals hg = hglobals;
01638     register hdlcancoonrecord hc;
01639 #ifdef landinclude
01640     register hdllandglobals hlg;
01641 #endif
01642     long ticksnow = gettickcount ();
01643     
01644     assert (!flvisitingthreads || hg == hthreadglobals);
01645 
01646     hthreadglobals = hg; /*so current thread can easily access its globals*/
01647     
01648     if (hg == nil)
01649         return;
01650     
01651     hashtablestack = (**hg).htablestack;
01652     
01653     hc = (hdlcancoonrecord) (**hg).hccglobals;
01654     
01655     if (hc != cancoonglobals) {
01656         
01657         if (hc == nil)
01658             /*cancoonglobals = nil*/; /*clearcancoonglobals ();*/
01659         else
01660             setcancoonglobals (hc);
01661         }
01662     
01663     if ((**hg).timestarted == 0) /*first time being swapped in*/
01664         (**hg).timestarted = ticksnow;
01665     
01666     (**hg).timeswappedin = ticksnow;
01667 
01668     flthreadkilled = (**hg).flthreadkilled;
01669     
01670     fldisableyield = (**hg).fldisableyield;
01671     
01672     currentprocess = (**hg).hprocess;
01673     
01674     processstack = (**hg).processstack;
01675     
01676     currenthashtable = (**hg).htable;
01677     
01678     langcallbacks = (**hg).langcallbacks;
01679         
01680     #if !flruntime
01681     
01682         cterrorhooks = (**hg).cterrorhooks;
01683         
01684         moveleft ((**hg).errorhooks, errorhooks, sizeof (errorhookcallback) * maxerrorhooks);
01685         
01686         globalsstack = (**hg).globalsstack;
01687         
01688         if (shellwindow != (**hg).shellwindow) {
01689             
01690             outlinedata = nil; //set current outlinedata won't lose ctpushes
01691 
01692             shellsetglobals ((**hg).shellwindow);
01693 
01694             if (outlinedata)
01695                 --(**outlinedata).ctpushes; //it just got a free one
01696             }
01697         
01698         outlinedata = (**hg).outlinedata;
01699         
01700         topoutlinestack = (**hg).topoutlinestack;
01701         
01702         moveleft  ((**hg).outlinestack, outlinestack, sizeof (hdloutlinerecord) * ctoutlinestack);
01703 
01704     #endif
01705     
01706     ctscanlines = (**hg).ctscanlines;
01707 
01708     ctscanchars = (**hg).ctscanchars;
01709     
01710     flreturn = (**hg).flreturn;
01711     
01712     flbreak = (**hg).flbreak;
01713     
01714     flcontinue = (**hg).flcontinue;
01715     
01716     fllangerror = (**hg).fllangerror;
01717     
01718     langerrordisable = (**hg).langerrordisable; /*6.1.1b2 AR*/
01719 
01720     tryerror = (**hg).tryerror;
01721 
01722     tryerrorstack = (**hg).tryerrorstack;
01723     
01724     flscriptrunning = (**hg).flscriptrunning;
01725     
01726     flscriptresting = (**hg).flscriptresting;
01727     
01728     herrornode = (**hg).herrornode;
01729     
01730 #ifdef landinclude
01731     
01732     hlg = landgetglobals ();
01733     
01734     (**hlg).eventcreatecallback = (**hg).eventcreatecallback;
01735     
01736     (**hlg).eventsendcallback = (**hg).eventsendcallback;
01737     
01738     (**hlg).applicationid = (**hg).applicationid;
01739     
01740     (**hlg).eventsettings = (**hg).eventsettings;
01741     
01742 #endif
01743     
01744 
01745     #ifdef flprofile
01746     if (cancoonglobals && !flvisitingthreads) { 
01747         unsigned long ticks;
01748         
01749         aboutsetthreadstring (hg, true);
01750 
01751         #ifdef MACVERSION
01752         ticks = gettickcount ();
01753         #else
01754         ticks = GetTickCount ();
01755         #endif
01756 
01757         if (lastswapticks)
01758             totalticksout += ticks - lastswapticks;
01759         lastswapticks = ticks;
01760         }
01761 
01762     #endif
01763     
01764     } /*swapinthreadglobals*/
01765 
01766 
01767 boolean getprocesstimeslice (unsigned long *ticks) {
01768     
01769     hdlthreadglobals hg = getcurrentthread ();
01770 
01771     if (hg == nil) {
01772 
01773         if (processisoneshot (true))
01774             *ticks = processonehottimeslice;
01775         else
01776             *ticks = processagenttimeslice;
01777 
01778         return (false);
01779         }
01780 
01781     *ticks = (**hg).timesliceticks;
01782 
01783     return (true);
01784     } /*getprocesstimeslice*/
01785 
01786 
01787 boolean setprocesstimeslice (unsigned long ticks) {
01788     
01789     hdlthreadglobals hg = getcurrentthread ();
01790 
01791     if (hg == nil)
01792         return (false);
01793 
01794     (**hg).timesliceticks = ticks;
01795     
01796     return (true);
01797     } /*setprocesstimeslice*/
01798 
01799 
01800 boolean getdefaulttimeslice (unsigned long *ticks) {
01801     
01802     *ticks = processonehottimeslice;
01803 
01804     return (true);
01805     } /*setprocesstimeslice*/
01806 
01807 
01808 boolean setdefaulttimeslice (unsigned long ticks) {
01809     
01810     processonehottimeslice = max (1, ticks);
01811     
01812     processagenttimeslice = max (1, ticks * 2 / 3);
01813 
01814     return (true);
01815     } /*setprocesstimeslice*/
01816 
01817 
01818 boolean processtimesliceelapsed (void) {
01819 
01820     hdlthreadglobals hg = getcurrentthread ();
01821     
01822     assert (hg != nil);
01823 
01824     return (gettickcount () >= (**hg).timeswappedin + (**hg).timesliceticks);
01825     } /*processtimesliceelapsed*/
01826 
01827 
01828 static pascal boolean maimprocessvisit (hdlthreadglobals hthread, long hcancoon) {
01829     
01830     /*
01831     if this process is dependent on hcancoon, "maim" it by clearing its global 
01832     data field.  actually killing the process at this point would require much 
01833     more substantial code changes outside of this file, since processyield would 
01834     never return.
01835     
01836     for runtime, all threads other than the main thread must be maimed.
01837 
01838     6.1b12 AR: Made 5.1.5b7 change Mac-only because we can't swap in the new thread's
01839     globals while flvisitingthreads == true.
01840     */
01841     
01842     register hdlthreadglobals hg = getthreadglobals (hthread);
01843     
01844     #if flruntime
01845         
01846         if ((**hg).idthread != idapplicationthread)
01847             (**hg).flthreadkilled = true;
01848     
01849     #else
01850         
01851         if (hcancoon == (long) (**hg).hccglobals) /*it's dependent; maim it*/ {
01852             
01853             (**hg).hccglobals = nil; /*no longer valid*/
01854             
01855             (**hg).flthreadkilled = true;
01856 
01857 //#ifdef MACVERSION         
01858             if (hg != agentthread)
01859                 wakeprocessthread (hg); //5.1.5b7
01860 //#endif
01861             }
01862         
01863     #endif
01864     
01865     return (false); /*keep visiting*/
01866     } /*maimprocessvisit*/
01867 
01868 
01869 void killdependentprocesses (long hcancoon) {
01870     
01871     /*
01872     disable all process threads whose cancoonglobals field matches hcancoon.
01873     */
01874     
01875     ++flagentsdisabled;
01876     
01877     visitprocessthreads (&maimprocessvisit, (long) hcancoon); /*terminates if visit proc returns true*/
01878     
01879     processyield (); //give threads a chance to die
01880     
01881     --flagentsdisabled;
01882     } /*killdependentprocesses*/
01883 
01884 
01885 static boolean gettracebacklist (Handle herrorstack, tyvaluerecord *v) {
01886 
01887     register hdlerrorstack hs = (hdlerrorstack) herrorstack;
01888     register short ix;
01889     register short ixtop;
01890     bigstring bsname;
01891     bigstring bspath;
01892     hdlhashtable htable;
01893     tyerrorrecord *pe;
01894     hdllistrecord outerlist;
01895     
01896     setnilvalue (v);
01897     
01898     if ((hs == nil) || ((**hs).toperror == 0))
01899         return (true); /*no info available -- not an error*/
01900     
01901     if (!opnewlist (&outerlist, false))
01902         return (false);
01903 
01904     ixtop = (**hs).toperror;
01905     
01906     for (ix = ixtop - 1; ix >= 0; --ix) {
01907         
01908         hdllistrecord innerlist;
01909         tyvaluerecord val;
01910 
01911         pe = &(**hs).stack [ix];
01912         
01913         if ((*pe).errorcallback == nil ||
01914             !(*(*pe).errorcallback) ((*pe).errorrefcon, 0, 0, &htable, bsname) ||
01915             !langexternalgetquotedpath (htable, bsname, bspath)) {
01916         
01917             langgetstringlist (anomynousthreadstring, bspath); 
01918             }
01919         
01920         if (!opnewlist (&innerlist, false))
01921             goto exit;
01922         
01923         if (!langpushlistlong (innerlist, (*pe).errorline) ||
01924             !langpushlistlong (innerlist, (*pe).errorchar) ||
01925             !langpushliststring (innerlist, bspath))
01926             
01927             goto exit;
01928         
01929         setheapvalue ((Handle) innerlist, listvaluetype, &val);
01930         
01931         if (!langpushlistval (outerlist, nil, &val))
01932             goto exit;
01933         }
01934     
01935     setheapvalue ((Handle) outerlist, listvaluetype, v);
01936     
01937     return (true);
01938 
01939 exit:
01940     
01941     disposehandle ((Handle) outerlist);
01942     
01943     return (false);
01944     }/*gettracebacklist*/
01945 
01946 
01947 static boolean getstatsvisit (hdlthreadglobals hthread, long refcon) {
01948 
01949     register hdlthreadglobals hg = getthreadglobals (hthread);
01950     register hdlprocessrecord hp = (hdlprocessrecord) (**hg).hprocess;
01951     hdlhashtable ht = (hdlhashtable) refcon;
01952     bigstring bsname, bsdata;
01953     long id = (long) (**hg).idthread;
01954     tyvaluerecord vlist;
01955     
01956     /*init bsdata*/
01957     
01958     setemptystring (bsdata);
01959     
01960     /*fill bsdata with info*/
01961     
01962     pushlong (id, bsdata);
01963     
01964     pushchar (',', bsdata);
01965 
01966     pushlong ((**hg).timestarted, bsdata);
01967     
01968     pushchar (',', bsdata);
01969     
01970     pushlong ((**hg).timetowake, bsdata);
01971     
01972     pushchar (',', bsdata);
01973     
01974     pushlong ((**hg).timeswappedin, bsdata);
01975     
01976     pushchar (',', bsdata);
01977     
01978     pushlong ((**hg).timesliceticks, bsdata);
01979     
01980     if (hp != nil) {
01981 
01982         pushchar (',', bsdata);
01983     
01984         pushlong ((**hp).sleepuntil, bsdata);
01985         
01986         pushchar (',', bsdata);
01987         
01988         pushlong ((**hp).flsleepinbackground, bsdata);
01989         
01990         pushchar (',', bsdata);
01991         
01992         pushlong ((**hp).flscheduled, bsdata);
01993         
01994         pushchar (',', bsdata);
01995         
01996         pushlong ((**hp).floneshot, bsdata);
01997         
01998         pushchar (',', bsdata);
01999         
02000         pushlong ((**hp).flrunning, bsdata);
02001         }
02002         
02003     pushchar (',', bsdata);
02004         
02005     pushlong ((**hg).debugthreadingcookie, bsdata);
02006 
02007     /*compute name for table entry*/
02008 
02009     copystring (BIGSTRING ("\x06" "thread"), bsname);
02010 
02011     pushlong (id, bsname);
02012 
02013     /*create subtable*/
02014     
02015     if (!langsuretablevalue (ht, bsname, &ht))
02016         return (false);
02017     
02018     /*assign info string*/
02019     
02020     if (!langassignstringvalue (ht, BIGSTRING ("\x0b" "threadstats"), bsdata))
02021         return (false);
02022 
02023     /*create and assign stack traceback*/
02024     
02025     if (hp != nil && (**hp).herrorstack != nil) {
02026     
02027         if (!gettracebacklist ((Handle) (**hp).herrorstack, &vlist))
02028             return (false);
02029 
02030         if (!hashtableassign (ht, BIGSTRING ("\x09" "traceback"), vlist))
02031             return (false);
02032         
02033         exemptfromtmpstack (&vlist);
02034         }
02035     
02036     return (false);
02037     }/*getstatsvisit*/
02038 
02039 
02040 boolean processgetstats (hdlhashtable ht) {
02041     
02042     /*
02043     6.2b6 AR: For debugging dead/hung thread problems
02044     */
02045 
02046     visitprocessthreads (&getstatsvisit, (long) ht);
02047 
02048     return (true);
02049     }/*processgetstats*/
02050 
02051 
02052 static unsigned long latesttime;
02053 
02054 static hdlthreadglobals hlatest1shot;
02055 
02056 static pascal boolean findlatest1shotvisit (hdlthreadglobals hthread, long refcon) {
02057 #pragma unused (refcon)
02058 
02059     /*
02060     if this thread contains a 1-shot process, kill it and stop the visit
02061     */
02062     
02063     register hdlthreadglobals hg = getthreadglobals (hthread);
02064     register hdlprocessrecord hp = (hdlprocessrecord) (**hg).hprocess;
02065     
02066     //if ((hp != nil) && ((**hg).hccglobals != nil)) { /*a living process*/
02067     
02068     if ((hp != nil) && (**hp).floneshot && !(**hg).flthreadkilled) { /*a living 1shot process*/
02069         
02070         register unsigned long timestarted = (**hg).timestarted;
02071         
02072         if (timestarted > latesttime) { /*most recent seen so far*/
02073             
02074             latesttime = timestarted;
02075             
02076             hlatest1shot = hg;
02077             }
02078         }
02079     
02080     return (false); /*keep visiting*/
02081     } /*findlatest1shotvisit*/
02082 
02083 
02084 boolean abort1shotprocess (void) {
02085 
02086     /*
02087     kill the most recently sharted 1-shot process we find.  maybe there's a 
02088     better way to  connect cmd-period with a particular process, but this 
02089     is a reasonable heuristic -- better than 1.0
02090     */
02091     
02092     register hdlthreadglobals hg;
02093     
02094     latesttime = 0;
02095     
02096     hlatest1shot = nil; /*clear global*/
02097     
02098     visitprocessthreads (&findlatest1shotvisit, (long) 0); /*terminates if visit proc returns true*/
02099     
02100     hg = hlatest1shot; /*copy global into register*/
02101     
02102     if (hg == nil) /*no living 1shot found to kill*/
02103         return (false);
02104     
02105     killprocessthread ((hdlprocessthread) hg);
02106     
02107     return (true);
02108     } /*abort1shotprocess*/
02109 
02110 
02111 static void setprocesslangcallbacks (void) {
02112     
02113     /*
02114     3/23/93 dmb: by setting these callbacks only when appropriate, instead of 
02115     at init time, lang.c doesn't have to work around them when being called 
02116     in other contexts
02117     */
02118     
02119     langcallbacks.backgroundtaskcallback = &processbackgroundtask; 
02120     
02121     langcallbacks.pushsourcecodecallback = &processpushsourcecode;
02122     
02123     langcallbacks.popsourcecodecallback = &processpopsourcecode;
02124     
02125     #if !flruntime
02126     
02127     langcallbacks.debuggercallback = &processdebugger;
02128     
02129     langcallbacks.pushtablecallback = &processpushtable;
02130     
02131     langcallbacks.poptablecallback = &processpoptable;
02132     
02133     #endif
02134     
02135     langcallbacks.scriptkilledcallback = &processscriptkilled;
02136     } /*setprocesslangcallbacks*/
02137 
02138 
02139 /*
02140 2.1a6, 6/8/93 dmb: ok, the new thread manager is a whole new ball of wax...
02141 
02142 designed to be portable and pre-emptive, the data structures are competely 
02143 hidden, and the API is simpler and lower-level.  we now use our own thread 
02144 globals handle as the main way to identify a thread, and define the psueudo-
02145 type hdlprocessthread for use by clients other than the OSA routine, which are more 
02146 intimately familiar with the globals structure.
02147 */
02148 
02149 static boolean findthreadvisit (bigstring bs, hdlhashnode hnode, tyvaluerecord val, ptrvoid refcon) {
02150 #pragma unused (bs, hnode, refcon)
02151 
02152     return (val.data.longvalue == (long) (**hthreadglobals).idthread);
02153     } /*findvaluevisit*/
02154 
02155 
02156 boolean initprocessthread (bigstring bsname) {
02157     
02158     /*
02159     8/22/91 dmb: start each thread off with an empty globals stack
02160     
02161     3/30/93 dmb: set up process-specific langcallbacks here so we don't 
02162     apply them to the main thread
02163     
02164     6/8/93 dmb: newly-packaged routine for thread manager 1.1. this must 
02165     be called at the beginning of every process thread main.  [we could 
02166     internalize this by calling it from the swappin routine the first 
02167     time only, but that adds extra overhead to the swap, and is a little 
02168     less clean]
02169     
02170     4.0.1b1 dmb: added bsname parameter, agenttable stuff
02171     
02172     9.1b3 AR: Changed code for computing unique name of entry in threadstable
02173     to not get stuck in an infinite loop if there are already 256 threads
02174     with the same bsname present in that table.
02175     */
02176     
02177 #if threadverbs
02178     tyvaluerecord val;
02179     bigstring bs;
02180 #endif
02181 
02182     if (!threadstartup ())
02183         return (false);
02184     
02185 #if threadverbs
02186     assert (threadtable);
02187 
02188     setlongvalue ((long) (**hthreadglobals).idthread, &val);
02189     
02190     copystring (bsname, bs);
02191     
02192     pushhashtable (threadtable);
02193 
02194     if (hashsymbolexists (bs)) {    /* 9.1b3 AR */
02195     
02196         int len = stringlength (bs) + 1;
02197         int x = 0;
02198 
02199         pushchar ('-', bs);
02200         
02201         do {
02202             setstringlength (bs, len);
02203             
02204             pushlong (++x, bs);
02205             
02206             } while (hashsymbolexists (bs));
02207 
02208         /*
02209         char c = '1';
02210         pushchar ('-', bs);
02211         pushchar (c, bs);
02212         while (hashsymbolexists (bs))
02213             bs [stringlength (bs)] = ++c;
02214         */
02215         }
02216 
02217     hashinsert (bs, val);
02218     
02219     pophashtable ();
02220 #endif
02221 
02222     (**hthreadglobals).timestarted = gettickcount ();
02223     
02224     #if !flruntime
02225     
02226         clearbytes (&globalsstack, sizeof (globalsstack));
02227         
02228         shellwindow = nil;
02229         
02230     #endif
02231     
02232     setprocesslangcallbacks ();
02233     
02234     return (true);
02235     } /*initprocessthread*/
02236 
02237 
02238 void exitprocessthread (void) {
02239     /*
02240     4.0.1b1 dmb: new routine
02241 
02242     5.0a18 dmb: make sure threadtable isn't nil
02243     
02244     9.1b3 AR: Nuke currentprocess to prevent opCursorMoved callback
02245     from running if the system.compiler.threads table is visible.
02246     We can't run the callbacks at this point because all the infrastructure
02247     for executing scripts has already been disposed of at this point.
02248     */
02249     
02250 #if threadverbs
02251     bigstring bsname;
02252     
02253     currentprocess = nil;   /* 91.b3 AR */
02254     
02255     if (threadtable &&
02256             hashinversesearch (threadtable, &findthreadvisit, nil, bsname)) { /*symbol is defined in htable*/
02257         
02258         flexitingthread = true;
02259         
02260         hashtabledelete (threadtable, bsname);
02261         
02262         flexitingthread = false;
02263         }
02264 #endif
02265     
02266     threadshutdown ();
02267     } /*exitprocessthread*/
02268 
02269 
02270 /*
02271 void exitprocessthread (void *result) {
02272     
02273     register short i;
02274     ThreadID idthread;
02275     boolean flrecycle = false; /%default%/
02276     
02277     GetCurrentThread (&idthread);
02278     
02279     for (i = 0; i < threadpoolsize; ++i) {
02280         
02281         if (threadpool [i] == idthread) { /%came from the pool...%/
02282             
02283             threadpool [i] = kNoThread;
02284             
02285             flrecycle = true; /%...return to the pool%/
02286             
02287             break;
02288             }
02289         }
02290     
02291     DisposeThread (kCurrentThread, result, flrecycle)
02292     } /%exitprocessthread%/
02293 */
02294 
02295 
02296 hdlprocessthread getcurrentthread (void) {
02297     
02298     return (getcurrentthreadglobals ());
02299     } /*getcurrentthread*/
02300 
02301 
02302 boolean processpsuedothread (tythreadmaincallback threadmain, tythreadmainparams threadparams) {
02303     
02304     /*
02305     2.1b5 dmb: set the thread id to identify it as the application thread, so 
02306     that inmainthread will return true
02307     
02308     3.0b16 dmb: must copythreadglobals (hglobals) before swapping them out.
02309     */
02310     
02311     hdlthreadglobals hglobals;
02312     hdlthreadglobals mainglobals = hthreadglobals;
02313     
02314     if (!newthreadglobals (&hglobals))
02315         return (false);
02316     
02317     (**hglobals).idthread = idapplicationthread; /*make it look like main thread*/
02318     
02319     copythreadglobals (mainglobals);
02320     
02321     swapinthreadglobals (hglobals);
02322     
02323     threadmain (threadparams);
02324     
02325     copythreadglobals (hglobals); /*3.0b16*/
02326     
02327     swapinthreadglobals (mainglobals);
02328     
02329     disposethreadglobals (hglobals);
02330     
02331     return (true);
02332     } /*processpsuedothread*/
02333 
02334 
02335 static void disposeprocessthread (hdlthreadglobals htread) {
02336     
02337     disposethreadglobals (htread);
02338     
02339     --ctprocessthreads;
02340     } /*disposeprocessthread*/
02341 
02342 
02343 boolean newprocessthread (tythreadmaincallback threadmain, tythreadmainparams threadparams, hdlthreadglobals *hthread) {
02344     
02345     /*
02346     7/10/92 dmb: call shellforcebackgroundtask
02347     
02348     6/8/93 dmb: newly-packaged routine for new thread manager. call this to 
02349     create a new thread
02350     
02351     2.1b2 dmb: use macmemoryconfig.minstacksize when creating thread pool
02352     
02353     5.1.1 dmb: limit trial size thread count
02354     */
02355     
02356     hdlthread idthread;
02357     hdlthreadglobals hglobals;
02358     
02359     if (!flcanusethreads)
02360         return (false);
02361     
02362     #ifdef fltrialsize
02363     
02364         if (ctprocessthreads > 2) {
02365         
02366             shelltrialerror (threadlimitstring);
02367             
02368             return (false);
02369             }
02370     
02371     #endif
02372     
02373     if (!newthreadglobals (&hglobals))
02374         return (false);
02375     
02376     if (!newthread (threadmain, threadparams, hglobals, &idthread)) {
02377         
02378         disposethreadglobals (hglobals);
02379         
02380         return (false);
02381         }
02382     
02383     (**hglobals).idthread = idthread;
02384     
02385     /*
02386     for (i = 0; i < threadpoolsize; ++i) {
02387         
02388         if (threadpool [i] == kNoThread) { /%must have come from the pool...%/
02389             
02390             threadpool [i] = idthread; /%...keep track of who owns this slot%/
02391             
02392             break;
02393             }
02394         }
02395     */
02396     
02397     shellforcebackgroundtask (); /*make sure new thread gets control before shell yields to other apps*/
02398     
02399     *hthread = hglobals;
02400     
02401     ++ctprocessthreads;
02402     
02403     return (true);
02404     } /*newprocessthread*/
02405 
02406 
02407 short processthreadcount (void) {
02408     
02409     /*
02410     return the number of threads, not counting the main thread
02411     */
02412     
02413     return (ctprocessthreads);
02414     } /*processthreadcount*/
02415 
02416 
02417 long getthreadid (hdlprocessthread hthread) {
02418 
02419     if (hthread == nil)
02420         return ((long) idnullthread);
02421     
02422     return ((long) (**(hdlthreadglobals) hthread).idthread);
02423     } /*getthreadid*/
02424 
02425 
02426 hdlprocessthread getprocessthread (long id) {
02427 
02428     register hdlthreadglobals hg;
02429 
02430     for (hg = (**processthreadlist).hfirst; hg != nil; hg = (**hg).hnextglobals) {
02431         
02432         if ((long) (**hg).idthread == id)
02433             return (hg);
02434         }
02435     
02436     return (nil);
02437     } /*getprocessthread*/
02438 
02439 
02440 hdlprocessthread nthprocessthread (long n) {
02441 
02442     register hdlthreadglobals hg;
02443 
02444     for (hg = (**processthreadlist).hfirst; hg != nil; hg = (**hg).hnextglobals) {
02445         
02446         if ((**hg).idthread > idapplicationthread) {
02447         
02448             if (--n == 0)
02449                 return (hg);
02450             }
02451         }
02452     
02453     return (nil);
02454     } /*nthprocessthread*/
02455 
02456 
02457 static pascal boolean wakeupvisit (Handle hthread, long refcon) {
02458     
02459     /*
02460     8/31/92 dmb: make sure that the thread we're trying to kill isn't 
02461     sleeping through its time or reckoning
02462     */
02463     
02464     return ((long) hthread == refcon);
02465     } /*wakeupvisit*/
02466 
02467 
02468 boolean wakeprocessthread (hdlprocessthread hthread) {
02469     
02470     /*
02471     wake any thread. if its sleep is being managed by landsystem7, make 
02472     sure it's woken by that system
02473     */
02474     
02475     boolean fl;
02476     
02477 #ifdef landinclude
02478     if (landvisitsleepingthreads (&wakeupvisit, (long) hthread))
02479         fl = true;
02480     else
02481 #endif
02482     if (shellcallwakeuphooks (hthread)) //hook didn't wake it up
02483         fl = processwake (hthread);
02484     else
02485         fl = true;
02486     
02487     if (fl)
02488         shellforcebackgroundtask (); /*make sure new thread gets control asap*/
02489 
02490     return (fl);
02491     } /*wakeprocessthread*/
02492 
02493 
02494 boolean killprocessthread (hdlprocessthread hthread) {
02495     
02496     /*
02497     5.0.1 dmb: don't wake here before kill. Under Windows, it could 
02498     be gone when wake is done. processkill will do the wake itself.
02499     */
02500 
02501     register hdlthreadglobals hg = (hdlthreadglobals) hthread;
02502 
02503     if (hg == nil)
02504         return (false);
02505     
02506     (**hg).flthreadkilled = true; /*maim the thread*/
02507     
02508     if (hg == hthreadglobals)   /*mirror to current global*/
02509         flthreadkilled = true;
02510     
02511     if (!processkill ((hdlprocessrecord) (**hg).hprocess))
02512         wakeprocessthread (hg);
02513     
02514     return (true);
02515     } /*killprocessthread*/
02516     
02517 
02518 static boolean initmainprocessthread (void) {
02519     
02520     /*
02521     2.1a6, 6/8/93 dmb: new thread manager 1.1 code. make sure the thread manager 
02522     is present, and pre-allocate two threads -- one for agents, plus a one-shot.
02523     also, install the custom swapping routine into the main thread
02524     
02525     2.1b1 dmb: set hthreadglobals so that it's never nil, and make sure idthread 
02526     is set even when threads aren't available
02527     
02528     2.1b2 dmb: use macmemoryconfig.minstacksize when creating thread pool
02529     
02530     2.1b5 dmb: don't create a thread pool; all it does is fragment our heap, 
02531     since we don't end up recycling threads. there's new code to do the 
02532     recycling commented out (search for "threadpool"), but it's never been 
02533     tested. to be safe, we'd really need to be more careful about how much memory
02534     a script can consume, since right now when a script runs out of memory & stops, 
02535     it's thread is released, creating room to Save, etc.
02536     
02537     2.1b13 dmb: don't post needthreadmanager error at startup. wait until an 
02538     agent tries to run.
02539     */
02540     
02541     hdlthreadglobals hglobals;
02542     
02543     if (!newthreadglobals (&hglobals))
02544         return (false);
02545     
02546     hthreadglobals = hglobals;
02547     
02548     if (!initthreads ()) {
02549         
02550         (**hglobals).idthread = idapplicationthread;
02551         
02552         /*
02553         alertstring (needthreadmanagerstring);
02554         */
02555         
02556         return (false);
02557         }
02558     
02559     threadcallbacks.disposecallback = (tythreadglobalscallback) &disposeprocessthread;
02560     
02561     threadcallbacks.swapincallback = (tythreadglobalscallback) &swapinthreadglobals;
02562     
02563     threadcallbacks.swapoutcallback = (tythreadglobalscallback) &copythreadglobals;
02564     
02565     initmainthread (hglobals);
02566     
02567     (**hglobals).idthread = idapplicationthread;
02568     
02569     (**hglobals).timestarted = gettickcount ();
02570     
02571     flcanusethreads = true;
02572     
02573     return (true);
02574     } /*initmainprocessthread*/
02575 
02576 
02577 boolean processruncode (hdlprocessrecord hprocess, tyvaluerecord *vreturned) {
02578     
02579     /*
02580     7/2/91 dmb: use new process callback routines for processtarted, processkilled
02581     
02582     12/2/91 dmb: don't force menu adjust if processnotbusy has been called.
02583     
02584     3/10/92 dmb: call new fifcloseallfiles for clean up on error
02585     */
02586     
02587     register hdlprocessrecord hp = hprocess;
02588     register boolean floneshot = (**hp).floneshot;
02589     register hdltreenode hcode = (**hp).hcode;
02590     register boolean fl;
02591     
02592     if (hcode == nil) /*defensive driving*/
02593         return (false);
02594     
02595     if (!pushprocess (hp)) /*error setting the current process to this one*/
02596         return (false);
02597     
02598     if (floneshot) {
02599         
02600         /*
02601         _profile = true;
02602         */
02603         
02604         honeshotcode = hcode; /*set global so other threads can kill script*/
02605         
02606         (*(**hp).processstartedroutine) ();
02607         }
02608     
02609     (**hp).flrunning = true;
02610     
02611     fl = langruncode (hcode, (**hp).hcontext, vreturned); 
02612     
02613     (**hp).flrunning = false;
02614     
02615     if ((**hp).holdcode != nil) { /*code was replace while process was running*/
02616         
02617         langdisposetree ((**hp).holdcode); /*in case code was replaced*/
02618         
02619         (**hp).holdcode = nil;
02620         }
02621     
02622     if (floneshot) {
02623         
02624         if (!fl) { /*an error occurred*/
02625         
02626             fifcloseallfiles ((long) hp);
02627             
02628             langreleasesemaphores (hp);
02629             }
02630         
02631         (*(**hp).processkilledroutine) ();
02632         
02633         if (processbusy ())
02634             processnotbusy ();
02635         
02636         /*
02637         _profile = false;
02638         
02639         DumpProfile ();
02640         */
02641         }
02642     
02643     popprocess ();
02644     
02645     return (fl);
02646     } /*processruncode*/
02647 
02648 
02649 boolean processruntext (Handle htext) {
02650     
02651     /*
02652     schedule the text to run in the background, in its own thread
02653     htext is consumed
02654     */
02655     
02656     hdltreenode hcode;
02657     
02658     if (!langbuildtree (htext, false, &hcode)) /*consumes htext*/
02659         return (false);
02660     
02661     if (!flinhibiterrorclear)
02662         langerrorclear (); /*compilation produced no error, be sure error window is empty*/
02663     
02664     if (!addnewprocess (hcode, true, nil, (long) 0)) {
02665         
02666         langdisposetree (hcode);
02667         
02668         return (false);
02669         }
02670     
02671     return (true);
02672     } /*processruntext*/
02673 
02674 
02675 boolean processrunstring (bigstring bs) {
02676     
02677     /*
02678     schedule the string to run in the background, in its own thread
02679     
02680     5.0a6 dmb: check processlist for nil. the shell calls us directly now
02681     */
02682     
02683     Handle htext;
02684     
02685     if (processlist == nil)
02686         return (false);
02687     
02688     if (!newtexthandle (bs, &htext))
02689         return (false);
02690     
02691     return (processruntext (htext));
02692     } /*processrunstring*/
02693 
02694 
02695 boolean processrunstringnoerrorclear (bigstring bs) {
02696     
02697     /*
02698     5.0a22 dmb: new function, for automatically-run scripts that 
02699     shouldn't clear the error info window
02700     */
02701 
02702     boolean fl;
02703     
02704     flinhibiterrorclear = true;
02705 
02706     fl = processrunstring (bs);
02707     
02708     flinhibiterrorclear = false;
02709 
02710     return (fl);
02711     } /*processrunstringnoerrorclear*/
02712 
02713 
02714 boolean processbusy (void) {
02715     
02716     /*
02717     return true if we're busy running a one-shot process.  although we 
02718     can run many such processes at once, user interface simplification 
02719     calls for limiting the number to one.
02720     */
02721     
02722     return (honeshotcode != nil);
02723     } /*processbusy*/
02724 
02725 
02726 boolean processnotbusy (void) {
02727     
02728     /*
02729     the caller is indicating that we should no longer consider the 
02730     current process to be tying us up; event though we may be 
02731     running a 1-shot process, we're not busy.
02732     */
02733     
02734     #if !flruntime
02735     
02736     if (honeshotcode != nil) {
02737         
02738         honeshotcode = nil;
02739         
02740         shellforcemenuadjust ();
02741         }
02742     
02743     #endif
02744     
02745     return (true);
02746     } /*processnotbusy*/
02747 
02748 
02749 boolean processrunning (void) {
02750     
02751     #if flruntime
02752     
02753     return (ctprocessthreads - ctsleepingthreads > 0);
02754     
02755     #else
02756     
02757     register hdlprocesslist hlist = processlist;
02758     
02759     if (hlist == nil)
02760         return (false);
02761     
02762     return ((**hlist).ctrunning - (**hlist).ctsleeping > 0);
02763     
02764     #endif
02765     
02766     } /*processrunning*/
02767 
02768 
02769 unsigned long processstackspace (void) {
02770     
02771     /*
02772     3.0.1b2 dmb: new routine. using this instead of StackSpace yields 
02773     correct result, fixes incompatibility with RAMDoubler and Mac SE
02774     
02775     3.0.4b8 dmb: don't use ThreadCurrentStackSpace when we're in an osa 
02776     client process. On the PPC, it appears to drop to zero randomly, 
02777     possibly due to the strange heap/globals context. Neither ThreadCurrentStackSpace
02778     nor GetCurrentThread returns an error, but the space returned is bogus.
02779     */
02780     
02781 #ifdef MACVERSION
02782     #if __powerc
02783     if (infrontierthread ())    // not in an osa client
02784     #endif
02785     
02786         if (flcanusethreads) {
02787             
02788             unsigned long space;
02789             unsigned long currentThread;
02790             //Changed this to wored in OSX kCurrentThread wasn't working
02791             //for some reason.
02792             GetCurrentThread(&currentThread);
02793             if (ThreadCurrentStackSpace (currentThread, &space) == noErr)
02794                 return (space);
02795             }
02796     
02797     return (StackSpace ());
02798 #endif
02799 
02800 #ifdef WIN95VERSION
02801     return (0x04000);   // a lot; *** don't know how to check this under Windows
02802 #endif
02803     } /*processstackspace*/
02804 
02805 
02806 static void postthreadsmessage (void) {
02807     
02808     /*
02809     2.1b13 dmb: new routine for alerting the user of the lack of threads. 
02810     we're only called when an agent would normally be run, so removing 
02811     all agents will avoid the message. also, we check system.startup.firsttime 
02812     to avoid scaring the user when they haven't even run the installer
02813     
02814     with runtime, since agents often don't exist, we want to put up a warning 
02815     when trying to run a non-agent thread. but we'll use a "softer" warning, 
02816     a message in the main window, instead of the alert.
02817     */
02818     
02819     bigstring bs;
02820     
02821     if (!flpostedthreadsmessage) {
02822         
02823         flpostedthreadsmessage = true; /*whether we actually do or not, we won't try again*/
02824         
02825         if (getsystemtablescript (idisfirsttimescript, bs)) { /*system.startup.firsttime*/
02826             
02827             if (langrunstringnoerror (bs, bs) && equalstrings (bs, bstrue)) /*first time*/
02828                 return;
02829             }
02830         
02831         #if flruntime
02832         
02833         getstringlist (alertstringlistnumber, needthreadmanagerstring, bs);
02834         
02835         setwindowmessage (bs);
02836         
02837         #else
02838         
02839         alertstring (needthreadmanagerstring);
02840         
02841         #endif
02842         }
02843     } /*postthreadsmessage*/
02844 
02845 
02846 boolean addnewprocess (hdltreenode hcode, boolean floneshot, langerrorcallback errorcallback, long errorrefcon) {
02847     
02848     /*
02849     a wrapper for conventional process creation
02850     */
02851     
02852     hdlprocessrecord hnewnode;
02853     
02854     if (!newprocess (hcode, floneshot, errorcallback, errorrefcon, &hnewnode))
02855         return (false);
02856     
02857     addprocess (hnewnode);
02858     
02859     return (true);
02860     } /*addnewprocess*/
02861 
02862 
02863 boolean addprocess (hdlprocessrecord hprocess) {
02864     
02865     /*
02866     add a new process to the process list.  if floneshot is true then it will
02867     be run once and then disposed of.  if fldebugging is true, the debugger will
02868     get a shot at the process before every "meaty" instruction.
02869     
02870     2/20/91 dmb: insert one-shot processes in front of other (agent) processes 
02871     for faster response to user commands
02872     
02873     5/23/91 dmb: after inserting a one-shot process, force a shell background 
02874     task to minimize delay.
02875     
02876     7/31/91 dmb: ***now that we have the one-shot scheduler loop, we don't 
02877     need to do the fancy insertion; we should be able to use the generic 
02878     listinsert routine.  no time to test it now, though, so just this comment
02879     */
02880     
02881     register hdlprocessrecord hp = hprocess; /*copy into register*/
02882     register hdlprocessrecord nomad;
02883     register hdlprocessrecord prevnomad = nil;
02884     
02885     if (hp == nil)
02886         return (false);
02887 
02888     _entercriticalprocesssection ();
02889 
02890     if (processlist == nil) {
02891         
02892         _leavecriticalprocesssection ();
02893 
02894         return (false);
02895         }
02896 
02897     nomad = (**processlist).hfirstprocess;
02898 
02899     while (nomad != nil) {
02900         
02901         if ((**hp).floneshot && !(**nomad).floneshot) /*insert 1-shot before agent*/
02902             break;
02903         
02904         prevnomad = nomad;
02905         
02906         nomad = (**nomad).hnextprocess;
02907         }
02908 
02909     if (prevnomad == nil)
02910         (**processlist).hfirstprocess = hp; 
02911     else
02912         (**prevnomad).hnextprocess =  hp;
02913     
02914     (**hp).hnextprocess = nomad;
02915     
02916     (**hp).hprocesslist = processlist;
02917     
02918     _leavecriticalprocesssection ();
02919 
02920     if ((**hp).floneshot)
02921         shellforcebackgroundtask ();
02922     
02923     newlyaddedprocess = hp; /*set global -- needed by landipcmenus and shellsysverbs.c*/
02924     
02925     return (true);
02926     } /*addprocess*/
02927 
02928 
02929 static boolean processtimeslice (hdlprocessrecord hprocess) {
02930     
02931     /*
02932     10/27/91 dmb: don't dispose result value if processruncode returns false
02933     */
02934     
02935     register hdlprocessrecord hp = hprocess;
02936     register boolean fl;
02937     tyvaluerecord val;
02938     
02939     if ((**hp).flrunning) /*this guy's already running!*/
02940         return (true);
02941     
02942     fl = processruncode (hp, &val);
02943     
02944     if (fl)
02945         disposetmpvalue (&val);
02946     
02947     return (fl);
02948     } /*processtimeslice*/
02949 
02950 
02951 static pascal void *oneshotthreadmain (void *hprocess) {
02952     
02953     /*
02954     4.0.1b1 dmb: pass name of thread to initprocessthread; call exitprocessthread
02955     */
02956     
02957     register hdlprocessrecord hp = (hdlprocessrecord) hprocess;
02958     register hdlprocesslist hlist = (**hp).hprocesslist;
02959     bigstring bsname;
02960 
02961     #if 0
02962 
02963     hdlerrorstack hs = (**hp).herrorstack;
02964     hdlhashtable htable;
02965     
02966     tyerrorrecord *pe = &(**hs).stack [(**hs).toperror - 1];
02967     
02968     setemptystring (bsname);
02969     
02970     if ((*pe).errorcallback != nil)
02971         (*(*pe).errorcallback) ((*pe).errorrefcon, 0, 0, &htable, bsname);
02972     
02973     if (isemptystring (bsname))
02974         langgetstringlist (anomynousthreadstring, bsname); 
02975     #else
02976 
02977     copystring ((**hp).bsname, bsname);
02978     #endif
02979 
02980     if (!initprocessthread (bsname)) /*must call from every thread main, before using globals*/
02981         return (nil);
02982     
02983     if (!(**hp).fldisposewhenidle)
02984         processtimeslice (hp);
02985     
02986     deleteprocess (hp);
02987     
02988     (**hlist).ctrunning--;
02989     
02990     if ((**hlist).fldisposewhenidle) /*try disposing now; will check ctrunning again*/
02991         disposeprocesslist (hlist);
02992     
02993     exitprocessthread ();
02994     
02995     return (nil);
02996     } /*oneshotthreadmain*/
02997 
02998 
02999 boolean scheduleprocess (hdlprocessrecord hprocess, hdlprocessthread *hnewthread) {
03000 
03001     /*
03002     4.1b3 dmb: now callable externally, for the thread verbs in shellsysverbs.c
03003     
03004     return true if we actually schedule the process and give (**hp).hthread a value.
03005     */
03006     
03007     register hdlprocessrecord hp = hprocess;
03008     
03009     *hnewthread = nil;
03010     
03011     if ((**hp).flscheduled)
03012         return (false);
03013     
03014     (**hp).flscheduled = true;
03015     
03016     if (flcanusethreads) {
03017         
03018         if (newprocessthread (&oneshotthreadmain, (tythreadmainparams) hp, hnewthread)) {
03019             
03020             (**hp).hthread = *hnewthread;
03021             
03022             (**processlist).ctrunning++;
03023             
03024             return (true);
03025             }
03026         else {
03027             
03028             deleteprocess (hp);
03029             
03030             return (false);
03031             }
03032         }
03033     else {
03034         
03035         postthreadsmessage ();
03036         
03037         return (processpsuedothread (&oneshotthreadmain, (tythreadmainparams) hp));
03038         }
03039     } /*scheduleprocess*/
03040 
03041 
03042 static void oneshotscheduler (void) {
03043     
03044     /*
03045     06/26/91 dmb: a special loop for one-shots
03046     
03047     6/9/93 dmb: updated for new thread. note that we try to run oneshots event if 
03048     threads are not available, except under the debugger where we let it fail.
03049     
03050     4.1b3 dmb: set new hthread field in process record
03051     */
03052     
03053     register hdlprocesslist hlist = processlist;
03054     register hdlprocessrecord hp;
03055     hdlprocessrecord hnext;
03056     hdlprocessthread hthread;
03057     
03058     if (hlist == nil)
03059         return;
03060     
03061     for (hp = (**hlist).hfirstprocess; hp != nil; hp = hnext) { /*walk through process list*/
03062         
03063         if (!(**hp).floneshot) /*we're done*/
03064             break;
03065         
03066         hnext = (**hp).hnextprocess;
03067         
03068         scheduleprocess (hp, &hthread);
03069         }
03070     } /*oneshotscheduler*/
03071 
03072 
03073 static void postmemorymessage (void) {
03074     
03075     /*
03076     12/25/91 dmb: this is done in its own routine to that every thread doesn't 
03077     brear the brunt of an extra bigstring on the stack.
03078     */
03079     
03080     bigstring bs;
03081     
03082     if (!flpostedmemorymessage) { /*didn't already put up message*/
03083         
03084         if (shellgetstring (outofmemorystring, bs))
03085             shellfrontrootwindowmessage (bs);
03086         
03087         flpostedmemorymessage = true;
03088         }
03089     } /*postmemorymessage*/
03090 
03091 
03092 static void agentscheduler (void) {
03093     
03094     /*
03095     the new process scheduler -- not round-robin.
03096     
03097     we visit every process in the list, and if it isn't blocked from running,
03098     we give it a shot at the processor.
03099     
03100     this is designed to be invoked once every second, and we must give each
03101     process a shot once per second.
03102     
03103     9/24/90 dmb: only give the first one-shot in the queue any time
03104     
03105     1/4/90 dmb: new rules.  don't re-enter loop if there is a process 
03106     running that isn't being debugged.  otherwise, allow all one-shot 
03107     processes in the queue to run.
03108     
03109     2/12/92 dmb: added flsleepinbackground logic
03110     */
03111     
03112     register hdlprocesslist hlist = processlist;
03113     register hdlprocessrecord hp;
03114     register unsigned long x;
03115     register hdlprocessrecord hnext;
03116     
03117     if (hlist == nil)
03118         return;
03119     
03120     flprocesscodedisposed = false; /*must reset every time*/
03121     
03122     x = timenow ();
03123     
03124     for (hp = (**hlist).hfirstprocess; hp != nil; hp = hnext) { /*find a process that's not sleeping*/
03125         
03126         register unsigned long sleepuntil = (**hp).sleepuntil;
03127         
03128         hnext = (**hp).hnextprocess;
03129         
03130         if ((**hp).floneshot) /*we don't deal with one-shots here*/
03131             continue;
03132         
03133         if ((sleepuntil != 0) && (sleepuntil > x)) /*process is sleeping*/
03134             continue;       
03135 
03136         /*
03137         if ((**hp).flsleepinbackground && !shellisactive ())
03138             continue;
03139         */
03140         
03141         //(**hp).sleepuntil++; //6.1b8 AR: old rescheduling code by dmb -- commented out
03142         
03143         (**hp).sleepuntil = x + 1; /*6.1b8 AR: reschedule for a second later*/
03144 
03145         (**hlist).ctrunning++;
03146         
03147         assert (!(**hthreadglobals).flretryagent);
03148         
03149         if (!processtimeslice (hp)) {
03150             
03151             if ((**hthreadglobals).flretryagent)
03152                 (**hthreadglobals).flretryagent = false; /*reset*/
03153             else
03154                 deleteprocess (hp);
03155             }
03156         
03157         (**hlist).ctrunning--;
03158         
03159         if (processlist != hlist) /*massive context change occured*/
03160             break;
03161         
03162         if (flprocesscodedisposed) /*may have deleted code for nextnomad; safest to exit*/
03163             break;
03164         } /*while*/
03165     
03166     if ((**hlist).fldisposewhenidle) /*try disposing now; will check ctrunning again*/
03167         disposeprocesslist (hlist);
03168     } /*agentscheduler*/
03169 
03170 
03171 static pascal void *agentthreadmain (void *ignore) {
03172 #pragma unused (ignore)
03173 
03174     /*
03175     4.0.1b1 dmb: pass name of thread to initprocessthread; call exitprocessthread
03176     
03177     4.1b2 dmb: instead of running the agentscheduler just once and quitting, 
03178     run it in a loop, sleeping after each time. now the processscheduler can 
03179     just wake us each second instead of having the thread destroyed and re-
03180     created every time. if flagentsenabled is turned off while we're sleeping,
03181     the processscheduler will still wake us to we can terminate.
03182     
03183     4.1b5 dmb: added agentthreadsleeping flag. processscheduler should only 
03184     wake the agent thread if this is true, i.e. it's asleep because it has 
03185     finished a loop. If it's asleep for any other reason, i.e. a script called 
03186     thread.sleep or is sending an Apple event, it should be left sleeping. At 
03187     the very least, processscheduler would have to call wakeprocessthread instead 
03188     of processwake to handle AE sleep. But it really shouldn't have to.
03189 
03190     5.0b16 dmb: need to check state between scheduling and sleeping, or we 
03191     can hang if someone is trying to kill us
03192     */
03193     
03194     initprocessthread (BIGSTRING ("\x06" "agents")); /*must call from every thread main*/
03195     
03196     while (flagentsenabled && ingoodthread () && !flagentsdisabled && !flshellclosingall) {
03197         
03198         setprocesstimeslice (processagenttimeslice); //do this every time (new in 5.1.5)
03199 
03200         agentscheduler ();
03201         
03202         if (!ingoodthread () || flagentsdisabled) //check again now, before we sleep
03203             break;
03204         
03205         agentthreadsleeping = true;
03206         
03207         threadsleep (nil);
03208         
03209         agentthreadsleeping = false;
03210         
03211         flagentsenabled = langgetuserflag (idagentsenabledscript, flagentsenabled);
03212         }
03213     
03214     agentthread = nil; /*clear global / semaphore*/
03215     
03216     exitprocessthread ();
03217     
03218     return (nil);
03219     } /*agentthreadmain*/
03220 
03221 
03222 void processscheduler (void) {
03223     
03224     /*
03225     06/26/91 dmb: new version for multithreaded environment.
03226     
03227     10/16/91 dmb: don't do anything if the process list is nil
03228     
03229     2.1b13 dmb: if threads aren't available, tell the user here, when 
03230     we're actually about to try to run an agent.
03231     
03232     3.0b15 dmb: use macmemoryconfig.minstacksize, not the Thread Manager's 
03233     default stack size when preflighting agent thread. also, add 2K to that 
03234     number, not 1K.
03235     
03236     5.0a23 dmb: don't create agent thread until startup scripts are done
03237 
03238     5.1.5b15 dmb: don't schedule anything if we're closing all
03239     */
03240     
03241     #ifdef MACVERSION
03242         long stacksize;
03243     #endif
03244     
03245     if (processlist == nil)
03246         return;
03247     
03248     if (currentprocess != nil) { /*this call is nested under language execution*/
03249         
03250         shellforcebackgroundtask (); /*reset, since we're waiting for better context*/
03251         
03252         return;
03253         }
03254     
03255     /*
03256     if (globalsstack.top > 1) { /%can't keep things clean if more than 1 window pushed%/
03257         
03258         return;
03259         }
03260     */
03261     
03262     oneshotscheduler (); /*do all the one-shots*/
03263     
03264     if (processlist == nil) /*file was closed during background processing*/
03265         return;
03266     
03267     #ifdef MACVERSION
03268     /*
03269         //6.1b8 AR: This bit of code stopped the agents from being called
03270         //          on the Mac while the scriptdebugger was active and
03271         //          while the Find & Replace dialog was open.
03272         
03273         if (threadiswaiting ()) {
03274             
03275             processyield ();
03276             
03277             return;
03278             }
03279     */
03280     #endif
03281     
03282     if (agentthread != nil) { /*agent scheduler is not to be re-entered*/
03283         
03284         if (processlist != agentprocesslist) { /*we've swapped active roots*/
03285         
03286             killprocessthread (agentthread);
03287             }
03288         else {
03289             
03290             if (agentthreadsleeping)
03291                 processwake (agentthread);
03292             }
03293         
03294         #ifdef MACVERSION
03295             processyield (); /*let previous thread die, reawaken, or try to finish, respectively*/
03296         #endif
03297         
03298         return;
03299         }
03300     
03301     #ifdef version5orgreater
03302         flagentsenabled = langgetuserflag (idagentsenabledscript, flagentsenabled);
03303     #endif
03304     
03305     if (!flagentsenabled || flagentsdisabled)
03306         return;
03307     
03308     if (specialoneshotscriptsrunning () > 0)
03309         return;
03310     
03311     if (!flcreatedagentprocess) /*2.1b13: never created an agent*/
03312         return;
03313     
03314     if (!flcanusethreads) { /*2.1b13: now's the time to tell user threads aren't available*/
03315         
03316         postthreadsmessage ();
03317         
03318         return;
03319         }
03320     
03321     /*
03322     if (GetDefaultThreadStackSize (kCooperativeThread, &stacksize) != noErr)
03323         return;
03324     */
03325 
03326     #ifdef MACVERSION   
03327         stacksize = macmemoryconfig.minstacksize;
03328         
03329         if (!haveheapspace (stacksize + 2 * 1024)) { /*not enough memory to run agents*/
03330             
03331             postmemorymessage (); /*use subroutine to avoid bigstring on stack here*/
03332             
03333             return;
03334             }
03335     #endif
03336 
03337     if (flpostedmemorymessage)
03338         shellfrontrootwindowmessage (zerostring); /*clear it*/
03339     
03340     newprocessthread (&agentthreadmain, (tythreadmainparams) 0, &agentthread);
03341     
03342     agentprocesslist = processlist;
03343     } /*processscheduler*/
03344 
03345 
03346 static boolean processkeyboardhook (void) {
03347     
03348     /*
03349     if a one-shot process is running, intercept command-period to kill it
03350     
03351     return false if we consume the keystroke
03352     
03353     10/29/91 dmb: watch for escape key too
03354     */
03355     
03356     if (processlist == nil)
03357         return (true);
03358     
03359     if ((keyboardstatus.chkb == chescape) || (keyboardstatus.flcmdkey && (keyboardstatus.chkb == '.'))) {
03360         
03361         abort1shotprocess (); /*abort the first 1-shot process we find*/
03362         
03363         return (false); /*consumed*/
03364         }
03365     
03366     return (true);
03367     } /*processkeyboardhook*/
03368 
03369 
03370 #ifdef MACVERSION
03371     #define maxticks (0xffffffffUL)
03372 #endif
03373 #ifdef WIN95VERSION
03374     #define maxticks (0xffffffffUL / 50UL)
03375 #endif
03376 
03377 void processchecktimeouts (void) {
03378 
03379     /*
03380     5.0b15 dmb: only wake one process at a time. the act of waking can 
03381     break our list walk under Windows
03382 
03383     6.2b7 AR: If the system tick count just wrapped around, the current
03384     tick count will be less than the tick count when the thread was last
03385     swapped in. In that case, we want to wake the thread if it's sleeping.
03386     
03387     11/21/01 7.1b31 dmb: don't break out of the wake loop when a thread for
03388     waking is found. This behavior as archaic, thinking that waking on thread 
03389     at a time would be all we can really handle. But this can cause deadlock,
03390     which we found with the script debugger.
03391     */
03392 
03393     register hdlthreadglobals hg;
03394     unsigned long ticks = gettickcount ();
03395     static unsigned long lastprocesscheckticks = 0L;
03396 
03397     if (ticks < lastprocesscheckticks) { /*tick count wrapped around, re-compute sleep times*/
03398 
03399         for (hg = (**processthreadlist).hfirst; hg != nil; hg = (**hg).hnextglobals) {
03400             
03401             unsigned long timetowake = (**hg).timetowake;
03402             unsigned long sleepticks = (**hg).sleepticks;
03403             unsigned long timebeginsleep = (**hg).timebeginsleep;
03404             
03405             if ((timetowake > 0) && (timebeginsleep > ticks) && (sleepticks != 0xffffffffUL)) {
03406 
03407                 unsigned long deltaticks = maxticks - timebeginsleep;
03408 
03409                 (**hg).timetowake = (sleepticks > deltaticks) ? (sleepticks - deltaticks) : 1UL;
03410                 }
03411             }
03412         }
03413 
03414     lastprocesscheckticks = ticks;
03415     
03416     for (hg = (**processthreadlist).hfirst; hg != nil; hg = (**hg).hnextglobals) {
03417         
03418         unsigned long timetowake = (**hg).timetowake;
03419         
03420         if ((timetowake > 0) && (ticks > timetowake) && ((**hg).sleepticks != 0xffffffffUL)) {
03421         
03422             wakeprocessthread ((hdlprocessthread) hg);
03423             
03424             shellforcebackgroundtask ();
03425             
03426             //break; //7.1b31
03427             }
03428         }
03429     } /*processchecktimeouts*/
03430 
03431 
03432 boolean processsymbolunlinking (hdlhashtable htable, hdlhashnode hnode) {
03433     
03434     /*
03435     4.1b3 dmb: new langhash callback allows us to examine the hashnode 
03436     before its been deleted. makes it a lot easier for us to respond to 
03437     user deletion of an item in the threads table
03438     */
03439     
03440     if (htable == threadtable && !flexitingthread) {
03441         
03442         long idthread = (**hnode).val.data.longvalue;
03443         
03444         killprocessthread (getprocessthread (idthread));
03445         }
03446     
03447     return (true);
03448     } /*processsymbolunlinking*/
03449 
03450 
03451 
03452 void processclose (void) {
03453     
03454     } /*processclose*/
03455 
03456 
03457 boolean initprocess (void) {
03458     
03459     /*
03460     called once when the program is booting -- do dynamic linking stuff
03461     for the language.
03462     */
03463     
03464     /*
03465     InitProfile (200, 200);
03466     
03467     _profile = false;
03468     */
03469     
03470     #if !flruntime
03471     
03472         shellpushkeyboardhook (&processkeyboardhook);
03473     
03474     #endif
03475     
03476     if (!newclearhandle (sizeof (tythreadlist), (Handle *) &processthreadlist))
03477         return (false);
03478     
03479     return (initmainprocessthread ());
03480     } /*initprocess*/
03481 
03482 
03483 
03484 

Generated on Wed May 31 18:20:00 2006 for frontierkernel 10.1.10a by  doxygen 1.4.6