Ada's task freeze

How to freeze a task?

I mean, if I have a task

task body My_Task is begin accept Start; loop Put ("1"); Put ("2"); Put ("3"); ... Put ("n"); end loop; end My_Task; 

Is there a way so that I can “freeze” a task in its current state? If, for example, execution completed with Put ("2"); how can i freeze it and then i can rotate it to continue? I want to provoke freezing outside the task, as well as from the outside, so that it continues.

Update

I could definitely implement it if I had a type specification

 type State_Type is (RUN, FROZEN); task type My_Task (State : State_Type) is entry Start; end My_Task; 

body

 task body My_Task is begin accept Start; loop Put ("1"); Put ("2"); Put ("3"); ... Put ("n"); loop if State = RUN then exit; end if; end loop; end loop; end My_Task; 

but this would not be the case, because I had to wait for the line of the nth Put instruction (i.e. the task would not actually be frozen, because the inner loop would work).

+6
source share
7 answers

You can use "selective reception" so that your task can be interrupted by an external subscriber (another task or main program). You cannot (easily) interrupt a task at an arbitrary point in its execution; instead, the task itself must determine when it will receive recording calls.

So, you probably want to replace your sequence

 Put("1"); Put("2"); Put("3"); ... Put("n"); 

a loop that calls Put once at each iteration, with a select statement that runs every time.

Here is a demo program that I just threw together. The counter is incremented once per second (approximately). The counter value is displayed if Running is true; otherwise, the loop passes silently. The main program alternatively pauses and resumes every 3 seconds.

The else clause in the select statement causes it to continue execution if no records were called.

 with Ada.Text_IO; use Ada.Text_IO; procedure T is task Looper is entry Pause; entry Resume; end Looper; task body Looper is Running: Boolean := True; Count: Integer := 0; begin loop select accept Pause do Running := False; end; or accept Resume do Running := True; end; else null; end select; delay 1.0; if Running then Put_Line("Running, Count = " & Integer'Image(Count)); end if; Count := Count + 1; end loop; end Looper; begin -- T loop delay 3.0; Looper.Pause; delay 3.0; Looper.Resume; end loop; end T; 

There may be a more elegant approach than this. It has been a long time since I really used Ada.

+5
source

Wow, didn't see Ada's question after a while. In any case, when you need to pause a task, you use the reserved words delay or delay until .

you must indicate the time at which you want to resume execution, then say:

delay <time>

or

delay until <time>

I don’t remember the exact data, but here is an example of the Ada95 specification: http://www.adaic.org/resources/add_content/docs/95style/html/sec_6/6-1-7.html

+4
source

One aspect of the design intent behind Ada is that behavior must be explicit. The performing task, which suddenly turned out to be “frozen” at an arbitrary point for no apparent reason - nothing happened in the task that would lead to such behavior, at best it will be confusing. And, possibly, a mistake if the task was not designed to allow an unexpected, arbitrary suspension of execution. (I know that timing the OS imposes this behavior on program execution, but this is an OS function, not a programming language.)

In short, I would seriously question the constructive approach based on suspended external suspension of tasks. The task must know its own state, so know when it is safe to pause, and which invariants must be held to pause in order to ensure proper resumption.

+4
source

To get granularity, you need to correctly terminate the put("x") sequence, you should write this as a procedure that saves its state in task my_task (knowing which atomic instruction was executed).

Then I think that you could use guard on your State_Type and execute only if they are not frozen. of course, this will not stop the task, it will simply protect the put("x") sequence from execution (what you want, I think!)

+2
source

If you have a specific line in which you would always like your task to wait, the solution is quite simple: just put the accept statement there. The task will freeze until some other task calls this entry.

If you want to generalize this idea (so you can expect several tasks, or several different tasks can safely perform the release action), it might be better to encapsulate your State variable inside a protected object (as NWS suggested). The function of protected objects was put into the language specifically to facilitate the creation of synchronization objects for such tasks.

+1
source

You mentioned in response to one of the comments that you want this because of some shared resources. So the answer is that you want to wrap this shared resource in a protected object and call the procedures / records of the protected object from the task; while a task is queued (or executes) a call to a protected object, it is placed in a “locked” state until it returns, thereby “freezing” further processing.

Edit: added sample code.


 With Ada.Text_IO, Ada.Strings.Fixed, Ada.Numerics.Float_Random ; with ada.Integer_Text_IO; with Ada.Strings; Use Ada.Numerics.Float_Random; Procedure Experiment is Task Type Testing( Text : Not Null Access String ) is end Testing; protected Resource is procedure Write( Input : In String ); private end Resource; Task Body Testing is G : Ada.Numerics.Float_Random.Generator; D : Duration := Duration( Random(G) ); Begin delay D; Resource.Write( Text.ALL ); End Testing; protected body Resource is procedure Write( Input : In String ) is begin Ada.Text_IO.Put_Line( Input ); end Write; end Resource; Function Make(Input : in String) Return Access Testing is begin Return Result : Access Testing:= New Testing( New String'(Input) ); end Make; TaskList : Array (Positive Range <>) of Access Testing:= ( Make("Anger"), Make("Rage"), Make("Joy"), Make("Contentment") ); Use Ada.Text_IO; Begin Put_Line( "Terminating." ); End Experiment; 

Note. Using put_line is technically not thread safe, and [IIRC] potentially blocks ... so while the above should never be blocked, this is not really guaranteed.

+1
source

I also have an answer from Anh Vo in comp.lang.ada (Task Freeze) , which includes Rendevouz also with the completion of the task. With some settings, it gets:

 task type My_Task is entry Start; entry Pause; entry Quit; entry Stop; end My_Task; -- (...) task body My_Task is begin Outer_Loop : loop select accept Start; Main_Loop : loop select accept Pause; select accept Quit; or accept Stop; exit Outer_Loop; or terminate; end select; or accept Stop; exit Outer_Loop; else Put ("Main code here"); delay MYCYCLE; -- MYCYCLE is 2.0; (whatever) end select; end loop Main_Loop; or terminate; end select; end loop Outer_Loop; end My_Task; 

I confess, I think this solution is very elegant.

0
source

Source: https://habr.com/ru/post/901674/


All Articles