home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / pmos2002.zip / SRC / taskcont.mod < prev    next >
Text File  |  1997-11-04  |  22KB  |  667 lines

  1. IMPLEMENTATION MODULE TaskControl;
  2.  
  3.         (****************************************************************)
  4.         (*                                                              *)
  5.         (*   Data structures internal to the kernel of the operating    *)
  6.         (*     system; the dispatcher of the operating system; and      *)
  7.         (*                  related procedures.                         *)
  8.         (*                                                              *)
  9.         (*      Programmer:     P. Moylan                               *)
  10.         (*      Last edited:    4 November 1997                         *)
  11.         (*      Status:         Working                                 *)
  12.         (*                                                              *)
  13.         (*      There's so much debugging stuff in this module that     *)
  14.         (*      it's become a bit untidy.  Needs a general clean-up.    *)
  15.         (*                                                              *)
  16.         (****************************************************************)
  17.  
  18. FROM SYSTEM IMPORT
  19.     (* type *)  ADDRESS,
  20.     (* proc *)  CAST;
  21.  
  22. FROM Storage IMPORT
  23.     (* proc *)  ALLOCATE, DEALLOCATE;
  24.  
  25. FROM OS2 IMPORT
  26.     (* const*)  APIENTRY, SEM_INDEFINITE_WAIT, PRTYS_THREAD, PRTYC_REGULAR, EXIT_THREAD,
  27.                 EXIT_PROCESS, ERROR_TIMEOUT,
  28.     (* type *)  PTIB, PPIB, HMTX, HEV,
  29.     (* proc *)  DosCreateThread, DosSetPriority, DosSuspendThread, DosResumeThread,
  30.                 DosGetInfoBlocks, DosKillThread, DosExit,
  31.                 DosSleep,
  32.                 DosCreateMutexSem, DosCloseMutexSem, DosRequestMutexSem, DosReleaseMutexSem,
  33.                 DosQueryMutexSem,
  34.                 DosCreateEventSem, DosPostEventSem, DosWaitEventSem, DosResetEventSem,
  35.                 DosCloseEventSem;
  36.  
  37. FROM FinalExit IMPORT
  38.     (* proc *)  Crash;
  39.  
  40. (************************************************************************)
  41.  
  42. CONST NilProc = CAST(PROC, NIL);
  43.  
  44. TYPE
  45.     Lock = POINTER TO LockInfo;
  46.     LockInfo = RECORD
  47.                    next: Lock;
  48.                    holder: CARDINAL;
  49.                    mutex: HMTX;
  50.                    LockNumber: CARDINAL;
  51.                END (*RECORD*);
  52.  
  53.     TaskDescriptor = POINTER TO
  54.                           RECORD
  55.                               previous, next: TaskDescriptor;
  56.                               InternalID: TaskID;
  57.                               UserCode: PROC;
  58.                               UserCode1: PROC1;
  59.                               LockList: Lock;
  60.                               Name: NameString;
  61.                               WakeUp: HEV;
  62.                               suspended, MarkedForTermination: BOOLEAN;
  63.                               HasParameter: BOOLEAN;
  64.                               ParameterValue: ADDRESS;
  65.                           END (*RECORD*);
  66.  
  67. (************************************************************************)
  68.  
  69. VAR
  70.     TaskCount: CARDINAL;
  71.  
  72.     (* The list of all tasks we know about. *)
  73.  
  74.     MasterTaskList: TaskDescriptor;
  75.  
  76.     (* Mutual exclusion semaphore.  We must lock this for any access to *)
  77.     (* the task list (including access to individual task descriptors,  *)
  78.     (* if they are on the master task list), and for any access to      *)
  79.     (* TaskCount.                                                       *)
  80.  
  81.     TaskListAccess: HMTX;
  82.     LockCount: CARDINAL;
  83.     LockCountAccess: HMTX;
  84.  
  85. (************************************************************************)
  86. (*                KERNEL CRITICAL SECTION PROTECTION                    *)
  87. (************************************************************************)
  88.  
  89. PROCEDURE LockTaskList;
  90.  
  91.     BEGIN
  92.         DosRequestMutexSem (TaskListAccess, SEM_INDEFINITE_WAIT);
  93.     END LockTaskList;
  94.  
  95. (************************************************************************)
  96.  
  97. PROCEDURE UnlockTaskList;
  98.  
  99.     BEGIN
  100.         DosReleaseMutexSem (TaskListAccess);
  101.     END UnlockTaskList;
  102.  
  103. (************************************************************************)
  104. (*                         IDENTIFYING A TASK                           *)
  105. (************************************************************************)
  106.  
  107. PROCEDURE CurrentTaskID(): TaskID;
  108.  
  109.     (* Returns the TaskID of the calling task. *)
  110.  
  111.     VAR ptib: PTIB;  ppib: PPIB;
  112.  
  113.     BEGIN
  114.         DosGetInfoBlocks (ptib, ppib);
  115.         RETURN ptib^.tib_ptib2^.tib2_ultid;
  116.         END CurrentTaskID;
  117.  
  118. (************************************************************************)
  119.  
  120. PROCEDURE DescriptorOf (id: TaskID): TaskDescriptor;
  121.  
  122.     (* Translates a thread ID to a task descriptor.  The result is NIL  *)
  123.     (* for an unknown task.                                             *)
  124.  
  125.     VAR result: TaskDescriptor;
  126.  
  127.     BEGIN
  128.         LockTaskList;
  129.         result := MasterTaskList;
  130.         WHILE (result <> NIL) AND (result^.InternalID <> id) DO
  131.             result := result^.next;
  132.         END (*WHILE*);
  133.         UnlockTaskList;
  134.         RETURN result;
  135.     END DescriptorOf;
  136.  
  137. (************************************************************************)
  138.  
  139. PROCEDURE CurrentTask(): TaskDescriptor;
  140.  
  141.     (* Returns the descriptor of the calling task.  The result is NIL   *)
  142.     (* if the task wasn't created by this module.                       *)
  143.  
  144.     BEGIN
  145.         RETURN DescriptorOf (CurrentTaskID());
  146.     END CurrentTask;
  147.  
  148. (************************************************************************)
  149. (*                       DEBUGGING CODE                                 *)
  150. (************************************************************************)
  151.  
  152. (*
  153. PROCEDURE DumpTaskList;
  154.  
  155.     VAR current: TaskDescriptor;
  156.  
  157.     BEGIN
  158.         DumpString ("The current task list is");
  159.         DumpEOL;
  160.  
  161.         LockTaskList;
  162.         current := MasterTaskList;
  163.         WHILE current <> NIL DO
  164.             DumpCard (current^.InternalID);  DumpString ("  ");
  165.             DumpHex (current^.UserCode);  DumpString ("  ");
  166.             DumpHex (current^.LockList);  DumpString ("  ");
  167.             DumpString (current^.Name);
  168.             IF current^.suspended THEN
  169.                 DumpString ("  (suspended)");
  170.             END (*IF*);
  171.             current := current^.next;
  172.             DumpEOL;
  173.         END (*WHILE*);
  174.         UnlockTaskList;
  175.  
  176.     END DumpTaskList;
  177.  
  178. (************************************************************************)
  179.  
  180. PROCEDURE DumpLockList (T: TaskDescriptor);
  181.  
  182.     VAR current: Lock;
  183.  
  184.     BEGIN
  185.         DumpString ("Task ");  DumpCard (T^.InternalID);
  186.         DumpString (" now holds locks");
  187.  
  188.         current := T^.LockList;
  189.         WHILE current <> NIL DO
  190.             DumpString ("  ");  DumpCard (current^.LockNumber);
  191.             current := current^.next;
  192.         END (*WHILE*);
  193.         DumpEOL;
  194.  
  195.     END DumpLockList;
  196. *)
  197.  
  198. (************************************************************************)
  199. (*                     OPERATIONS ON THE TASK LIST                      *)
  200. (************************************************************************)
  201.  
  202. PROCEDURE CreateNewDescriptor(): TaskDescriptor;
  203.  
  204.     (* Creates a task descriptor, fills in default values for its       *)
  205.     (* fields, but does not yet add it to the task list.                *)
  206.  
  207.     VAR result: TaskDescriptor;
  208.  
  209.     BEGIN
  210.         NEW (result);
  211.         WITH result^ DO
  212.             previous := NIL;  next := NIL;
  213.             InternalID := 0;
  214.             UserCode := NilProc;
  215.             LockList := NIL;
  216.             Name := "Unknown";
  217.             DosCreateEventSem (NIL, WakeUp, 0, FALSE);
  218.             suspended := FALSE;  MarkedForTermination := FALSE;
  219.         END (*WITH*);
  220.         RETURN result;
  221.     END CreateNewDescriptor;
  222.  
  223. (************************************************************************)
  224.  
  225. PROCEDURE AddToTaskList (T: TaskDescriptor);
  226.  
  227.     (* Adds T to the master task list. *)
  228.  
  229.     BEGIN
  230.         LockTaskList;
  231.         IF MasterTaskList <> NIL THEN MasterTaskList^.previous := T END(*IF*);
  232.         T^.next := MasterTaskList;  MasterTaskList := T;
  233.         INC (TaskCount);
  234.         UnlockTaskList;
  235.     END AddToTaskList;
  236.  
  237. (************************************************************************)
  238.  
  239. PROCEDURE UnlinkDescriptor (T: TaskDescriptor);
  240.  
  241.     (* Removes T from the task list, without modifying it except for    *)
  242.     (* clearing the list links.                                         *)
  243.  
  244.     BEGIN
  245.         LockTaskList;
  246.         IF T^.previous = NIL THEN
  247.             MasterTaskList := T^.next;
  248.         ELSE
  249.             T^.previous^.next := T^.next;
  250.         END (*IF*);
  251.         IF T^.next <> NIL THEN
  252.             T^.next^.previous := T^.previous;
  253.         END (*IF*);
  254.         T^.previous := NIL;  T^.next := NIL;
  255.         DEC (TaskCount);
  256.         UnlockTaskList;
  257.     END UnlinkDescriptor;
  258.  
  259. (************************************************************************)
  260. (*                     RUNNING THE USER CODE                            *)
  261. (************************************************************************)
  262.  
  263. <* m2extensions + *>
  264.  
  265. PROCEDURE [APIENTRY] TaskWrapper (param: CARDINAL);
  266.  
  267. <* m2extensions - *>
  268.  
  269.     (* This, as far as OS/2 is concerned, is the actual task.  Its      *)
  270.     (* job is simply to run the user task code.                         *)
  271.  
  272.     VAR T: TaskDescriptor;
  273.  
  274.     BEGIN
  275.         T := CAST (TaskDescriptor, param);
  276.         T^.InternalID := CurrentTaskID();
  277.         IF T^.HasParameter THEN
  278.             T^.UserCode1 (T^.ParameterValue);
  279.         ELSE
  280.             T^.UserCode;
  281.         END (*IF*);
  282.         TaskExit;
  283.     END TaskWrapper;
  284.  
  285. (************************************************************************)
  286. (*                     CREATING A NEW TASK                              *)
  287. (************************************************************************)
  288.  
  289. PROCEDURE CreateTaskCommon (T: TaskDescriptor;  taskpriority: PriorityLevel;
  290.                      taskname: NameString;  PassParameter: BOOLEAN;  Param: ADDRESS);
  291.  
  292.     (* Common code for the two task creation procedures. *)
  293.  
  294.     CONST StackSize = 65536;
  295.  
  296.     BEGIN
  297.         WITH T^ DO
  298.             Name := taskname;
  299.             HasParameter := PassParameter;
  300.             ParameterValue := Param;
  301.             IF DosCreateThread (InternalID, TaskWrapper, CAST(CARDINAL,T), 0, StackSize) = 0 THEN
  302.                 AddToTaskList (T);
  303.  
  304.                 (* Next line commented out for now, because it was making the entire *)
  305.                 (* program run terribly slowly.  I haven't yet found out why.        *)
  306.  
  307.                 (*DosSetPriority (PRTYS_THREAD, PRTYC_REGULAR, taskpriority, T^.tid);*)
  308.  
  309.             ELSE
  310.                 Crash ("CreateTask failure.");
  311.             END (*IF*);
  312.         END (*WITH*);
  313.  
  314.     END CreateTaskCommon;
  315.  
  316. (************************************************************************)
  317.  
  318. PROCEDURE CreateTask (StartAddress: PROC;  taskpriority: PriorityLevel;
  319.                                                 taskname: NameString);
  320.  
  321.     (* Must be called to introduce a task to the system. The first      *)
  322.     (* parameter, which should be the name of a procedure containing    *)
  323.     (* the task code, gives the starting address.  The second parameter *)
  324.     (* is the task's base priority.  If this task has a higher priority *)
  325.     (* than its creator, it will run immediately.  Otherwise, it        *)
  326.     (* becomes ready.                                                   *)
  327.  
  328.     VAR T: TaskDescriptor;
  329.  
  330.     BEGIN
  331.         T := CreateNewDescriptor();
  332.         T^.UserCode := StartAddress;
  333.         CreateTaskCommon (T, taskpriority, taskname, FALSE, NIL);
  334.     END CreateTask;
  335.  
  336. (************************************************************************)
  337.  
  338. PROCEDURE CreateTask1 (StartAddress: PROC1;  taskpriority: PriorityLevel;
  339.                                    taskname: NameString;  param: ADDRESS);
  340.  
  341.     (* Like CreateTask, but allows the passing of a single parameter    *)
  342.     (* "param" to the task.                                             *)
  343.  
  344.     VAR T: TaskDescriptor;
  345.  
  346.     BEGIN
  347.         T := CreateNewDescriptor();
  348.         T^.UserCode1 := StartAddress;
  349.         CreateTaskCommon (T, taskpriority, taskname, TRUE, param);
  350.     END CreateTask1;
  351.  
  352. (************************************************************************)
  353.  
  354. PROCEDURE TaskExit;
  355.  
  356.     VAR me: TaskDescriptor;
  357.  
  358.     BEGIN
  359.         ReleaseAllLocks;
  360.         me := CurrentTask();
  361.         UnlinkDescriptor (me);
  362.         DISPOSE (me);
  363.         DosExit (EXIT_THREAD, 0);
  364.     END TaskExit;
  365.  
  366. (************************************************************************)
  367. (*                SUSPENDING AND RESUMING A TASK                        *)
  368. (************************************************************************)
  369.  
  370. PROCEDURE SuspendMe (id: TaskID;  TimeLimit: CARDINAL): BOOLEAN;
  371.  
  372.     (* Suspends the caller.  A TRUE result indicates that the time      *)
  373.     (* limit expired.                                                   *)
  374.  
  375.     VAR T: TaskDescriptor;  status: CARDINAL;  PostCount: CARDINAL;
  376.         TimedOut: BOOLEAN;  WakeUpEventSem: HEV;
  377.  
  378.     BEGIN
  379.         T := DescriptorOf (id);
  380.         LockTaskList;
  381.         T^.suspended := TRUE;
  382.         TimedOut := FALSE;
  383.         WakeUpEventSem := T^.WakeUp;
  384.         UnlockTaskList;
  385.         status := DosWaitEventSem (WakeUpEventSem, TimeLimit);
  386.         LockTaskList;
  387.         IF status = ERROR_TIMEOUT THEN
  388.             TimedOut := TRUE;  T^.suspended := FALSE;
  389.         END (*IF*);
  390.         IF T^.MarkedForTermination THEN
  391.             UnlockTaskList;
  392.             TaskExit;
  393.         END (*IF*);
  394.         IF NOT TimedOut THEN
  395.             status := DosResetEventSem (T^.WakeUp, PostCount);
  396.         END (*IF*);
  397.         UnlockTaskList;
  398.         RETURN TimedOut;
  399.     END SuspendMe;
  400.  
  401. (************************************************************************)
  402.  
  403. PROCEDURE ResumeTask (id: TaskID): BOOLEAN;
  404.  
  405.     (* Resumes a task specified by its thread ID.  The function result  *)
  406.     (* is normally TRUE, but is FALSE if the task couldn't be resumed   *)
  407.     (* (usually because that task no longer exists).                    *)
  408.  
  409.     VAR T: TaskDescriptor;  status: CARDINAL;
  410.  
  411.     BEGIN
  412.         T := DescriptorOf (id);
  413.         LockTaskList;
  414.         IF T = NIL THEN
  415.             UnlockTaskList;
  416.             RETURN FALSE;
  417.         END (*IF*);
  418.         T^.suspended := FALSE;
  419.         status := DosPostEventSem (T^.WakeUp);
  420.         UnlockTaskList;
  421.         RETURN TRUE;
  422.     END ResumeTask;
  423.  
  424. (************************************************************************)
  425. (*                                                                      *)
  426. (*                LOCKS FOR CRITICAL SECTION PROTECTION                 *)
  427. (*                                                                      *)
  428. (*  Note that we distinguish between a Lock and a Semaphore.            *)
  429. (*  A Semaphore is a general semaphore - whose operations are defined   *)
  430. (*  in module Semaphores - which can be used for general inter-task     *)
  431. (*  interlocking.  A Lock is similar to a binary semaphore (with a      *)
  432. (*  more efficient implementation than a Semaphore), but may be used    *)
  433. (*  only in a strictly nested fashion and is therefore useful only      *)
  434. (*  for critical section protection.  No task should perform a          *)
  435. (*  semaphore Wait while it holds a Lock.                               *)
  436. (*                                                                      *)
  437. (************************************************************************)
  438.  
  439. PROCEDURE CreateLock (VAR (*OUT*) L: Lock);
  440.  
  441.     (* Creates a new lock. *)
  442.  
  443.     BEGIN
  444.         NEW (L);
  445.         L^.next := NIL;  L^.holder := 0;
  446.         DosCreateMutexSem (NIL, L^.mutex, 0, FALSE);
  447.         DosRequestMutexSem (LockCountAccess, SEM_INDEFINITE_WAIT);
  448.         INC (LockCount);  L^.LockNumber := LockCount;
  449.         DosReleaseMutexSem (LockCountAccess);
  450.     END CreateLock;
  451.  
  452. (************************************************************************)
  453.  
  454. PROCEDURE DestroyLock (VAR (*INOUT*) L: Lock);
  455.  
  456.     (* Disposes of a lock. *)
  457.  
  458.     BEGIN
  459.         IF DosCloseMutexSem (L^.mutex) <> 0 THEN
  460.         END (*IF*);
  461.         DISPOSE (L);
  462.     END DestroyLock;
  463.  
  464. (************************************************************************)
  465.  
  466. PROCEDURE Obtain (L: Lock);
  467.  
  468.     (* Obtains lock L, waiting if necessary. *)
  469.  
  470.     VAR me: TaskDescriptor;
  471.  
  472.     BEGIN
  473.         me := CurrentTask();
  474.         IF me = NIL THEN
  475.             DosExit (EXIT_PROCESS, 0);
  476.         END (*IF*);
  477.         DosRequestMutexSem (L^.mutex, SEM_INDEFINITE_WAIT);
  478.         L^.next := me^.LockList;  me^.LockList := L;
  479.         L^.holder := me^.InternalID;
  480.     END Obtain;
  481.  
  482. (************************************************************************)
  483.  
  484. PROCEDURE Release (L: Lock);
  485.  
  486.     (* Releases lock L - which might unblock some other task. *)
  487.  
  488.     VAR me: TaskDescriptor;  previous, current: Lock;
  489.         status: CARDINAL;
  490.  
  491.     BEGIN
  492.         me := CurrentTask();
  493.         previous := NIL;  current := me^.LockList;
  494.         LOOP
  495.             IF current = NIL THEN
  496.                 Crash ("Releasing a lock we don't hold");
  497.             ELSIF current = L THEN
  498.                 IF previous = NIL THEN me^.LockList := current^.next
  499.                 ELSE previous^.next := current^.next;
  500.                 END (*IF*);
  501.                 L^.holder := 0;
  502.                 L^.next := NIL;
  503.                 status := DosReleaseMutexSem (L^.mutex);
  504.                 EXIT (*LOOP*);
  505.             ELSE
  506.                 previous := current;  current := current^.next;
  507.             END (*IF*);
  508.         END (*LOOP*);
  509.     END Release;
  510.  
  511. (************************************************************************)
  512.  
  513. (*
  514. PROCEDURE DumpLockState (L: Lock);
  515.  
  516.     (* Writes information about L to the dump file. *)
  517.  
  518.     VAR pid, tid, pulCount: CARDINAL;
  519.  
  520.     BEGIN
  521.         DosQueryMutexSem (L^.mutex, pid, tid, pulCount);
  522.         DumpCard (pulCount);
  523.         IF pulCount > 0 THEN
  524.             DumpString (" held by thread ");  DumpCard (tid);
  525.         END (*IF*);
  526.     END DumpLockState;
  527. *)
  528.  
  529. (************************************************************************)
  530. (*                       TERMINATION PROCESSING                         *)
  531. (************************************************************************)
  532.  
  533. PROCEDURE ReleaseLocks (T: TaskDescriptor);
  534.  
  535.     (* Releases all locks held by task T.  (We do this in preparation   *)
  536.     (* for killing task T.)                                             *)
  537.  
  538.     BEGIN
  539.         WHILE T^.LockList <> NIL DO
  540.             Release (T^.LockList);
  541.         END (*WHILE*);
  542.     END ReleaseLocks;
  543.  
  544. (************************************************************************)
  545.  
  546. PROCEDURE ReleaseAllLocks;
  547.  
  548.     (* Releases all locks held by the current task.  Application-level  *)
  549.     (* tasks normally won't need to call this procedure; it is          *)
  550.     (* provided to support the system shutdown function and for things  *)
  551.     (* like "emergency abort" operations.                               *)
  552.  
  553.     VAR me: TaskDescriptor;
  554.  
  555.     BEGIN
  556.         me := CurrentTask();
  557.         IF me <> NIL THEN
  558.             ReleaseLocks (me);
  559.         END (*IF*);
  560.     END ReleaseAllLocks;
  561.  
  562. (************************************************************************)
  563.  
  564. PROCEDURE KillTask (T: TaskDescriptor): BOOLEAN;
  565.  
  566.     (* Kills task T (*and releases all of its locks*).  *)
  567.     (* Assumption: the caller has locked the task list. *)
  568.     (* A FALSE result indicates failure to kill the task.*)
  569.  
  570.     VAR status: CARDINAL;
  571.  
  572.     BEGIN
  573.         status := DosKillThread (T^.InternalID);
  574.         RETURN status = 0;
  575.     END KillTask;
  576.  
  577. (************************************************************************)
  578.  
  579. PROCEDURE KillAllTasks;
  580.  
  581.     (* Shuts down all tasks that have been created by this module. *)
  582.     (* Note that this leaves the main thread still running.        *)
  583.  
  584.     VAR me, current, next: TaskDescriptor;  suicide: BOOLEAN;
  585.         status: CARDINAL;
  586.  
  587.     BEGIN
  588.         me := CurrentTask();  suicide := FALSE;
  589.         LockTaskList;
  590.         current := MasterTaskList;
  591.         WHILE current <> NIL DO
  592.             next := current^.next;
  593.             IF current^.UserCode = NilProc THEN
  594.  
  595.                 (* Main thread, don't kill it. *)
  596.  
  597.             ELSIF current = me THEN
  598.  
  599.                 suicide := TRUE;
  600.  
  601.             ELSIF current^.MarkedForTermination THEN
  602.  
  603.                 (* Do nothing *)
  604.  
  605.             ELSIF current^.suspended THEN
  606.  
  607.                 current^.MarkedForTermination := TRUE;
  608.                 status := DosPostEventSem (current^.WakeUp);
  609.  
  610.             ELSIF KillTask (current) THEN
  611.                 UnlinkDescriptor (current);
  612.             END (*IF*);
  613.  
  614.             current := next;
  615.  
  616.         END (*WHILE*);
  617.         UnlockTaskList;
  618.  
  619.         IF suicide THEN
  620.             TaskExit;
  621.         END (*IF*);
  622.  
  623.     END KillAllTasks;
  624.  
  625. (************************************************************************)
  626. (*                       MODULE INITIALISATION                          *)
  627. (************************************************************************)
  628.  
  629. VAR me: TaskDescriptor;
  630.  
  631. BEGIN
  632.     DosCreateMutexSem (NIL, TaskListAccess, 0, FALSE);
  633.     DosCreateMutexSem (NIL, LockCountAccess, 0, FALSE);
  634.     LockCount := 0;
  635.  
  636.     (* Create a task descriptor for the thread that's already running. *)
  637.  
  638.     NEW (MasterTaskList);
  639.     WITH MasterTaskList^ DO
  640.         next := NIL;  previous := NIL;
  641.         UserCode := NilProc;
  642.         InternalID := CurrentTaskID();
  643.         DosCreateEventSem (NIL, WakeUp, 0, FALSE);
  644.         LockList := NIL;
  645.         Name := "Main thread";
  646.         suspended := FALSE;  MarkedForTermination := FALSE;
  647.     END (*WITH*);
  648.     TaskCount := 1;
  649.     UnlockTaskList;
  650.  
  651. FINALLY
  652.     me := CurrentTask();
  653.     IF me^.suspended THEN
  654.          me^.suspended := FALSE;
  655.          DosPostEventSem (me^.WakeUp);
  656.          DosSleep (300);
  657.     END (*IF*);
  658.     ReleaseAllLocks;
  659.     KillAllTasks;
  660.     IF TaskCount > 1 THEN
  661.         DosSleep (100);
  662.         KillAllTasks;
  663.     END (*IF*);
  664.  
  665. END TaskControl.
  666.  
  667.