Rendezvous ADA - how it works - task

I'm trying to understand how the R-V in ADA works:
server (pseudo)code:
task test is
entry put (num: in integer);
entry get (num: out integer);
end test;
task body test is
local_num: integer;
begin
accept put (num: in integer) do
local_num := num;
end put;
// some next processing of local_num, eg:
local_num := local_num * 2;
accept get (num: out integer) do
num := local_num;
end get;
end test;
and the client a server are communicating as following:
client (a) | server (b)
================================
| // sleep
(call_entry) | // sleep
a.put(num) | // sleep
// sleep | (accept_entry)
// sleep | b.accept put(num)
// sleep | local_num := num;
// sleep | local_num := local_num * 2;
| ??? <--------------------- what is happening after that point?
(call_entry) | // sleep
a.get(num) | // sleep
// sleep | (accept_entry)
// sleep | b.accept get(num)
Is it correct?
Can you describe me how it continues?
Who awakes the client to get the result? How the client know, that it can get the result?
update: (correct process?)
client | server (test)
================================
test.put(num) | // sleep ----------> call entry
test.get(A) | accept put(num) ----------> accept entry PUT (call entry GET to the QUEUE)
// sleep | local_num := num;
// sleep | local_num := local_num * 2;
test.get(A) | accept get(A) ----------> accept entry (removed from the QUEUE)
| // sleep

Ada Rendez-Vous are really simple, in fact you have a task test with 2 entries get and put. Once you run the program, the test task is started, so the local_num value is created (with no defined initial value) and the body is executed BUT the first statement is an accept so the task is waiting for an entry call.
procedure ... is
task test is
...
end test ;
task body test is
...
end test ;
A : Integer ;
begin
-- 1. The 'test' task is started here, waiting for a call to `put`
-- 2. You call the 'put' entry, which mean that the current task (the main task)
-- is stopped until the end of the entry call.
test.put(33) ;
-- 3. Here the entry 'put' is finished, so the 2 tasks are executing
-- simultaneously
-- 4. Here we call the 'get' entry, there is a non predictive behaviour:
-- We do not know if the instruction local_num := local_num * 2; has
-- been executed or not, so 2 case:
-- - The instruction has been executed, so the 'test' task is waiting
-- and directly accept the 'get' entry, while the main task wait
-- - The instruction has not been executed, so the main 'task' wait
-- until there is someone to accept is call
-- What you're sure is that the entry 'get' won't be executed before
-- the instruction, and that someone calling 'get' will have to wait
-- until the 'test' task get to the 'accept get' instruction.
test.get(A) ;
-- 5. Here, the 'get' entry is finished, so you're sure that you have ended
-- the 'test' task
end ... ;
In fact, when you call a entry like test.get, you loose the control until the test.get entry has been fully executed. If there is no task waiting for this entry, you will wait until a task request this entry.
procedure BadTask is
-- Stupid task that will accept a 'put' entry and then a 'get' (only once)
task T is
entry get (A : out Integer);
entry put (A : in Integer);
end T ;
task body T is
LocalInteger : Integer := 0 ;
begin
accept put (A : in Integer) do
LocalInteger := A ;
end put ;
accept get (A : ouInteger) do
A := LocalInteger ;
end get ;
end T ;
A : Integer ;
begin
-- Here T is waiting for 'put'
T.get (A) ;
-- You will never get here and your program is blocked because the two tasks
-- are waiting but no one can't get the control.
end BadTask ;

Related

Can array of registers can be used in tasks in verilog?

i am trying to implement a task for a Ripple carry adder in Verilog HDL. There is an error showing:"root scope declaration is not allowed in Verilog 95/2K mode" at line no-1
task rca; `<---line 1`
input [15:0]in1,in2;
output reg [15:0]out2;
reg [15:0]c;
integer i;
begin
c=16'b0;
for(i=0;i<16;i=i+1)
begin
out2[i]=in1[i]^in2[i]^c[i];
c[i+1]=(in1[i]&in2[i])|(in2[i]&c[i])|(c[i]&in1[0]);
end
end
endtask
You have to declare your task inside a module. I suggest to create file with .vh extension (Verilog Header), place you task inside this file and include your task by `include directive. For example:
module example_module(
// list of ports
);
`include "tasks.vh"
// Use your task here
endmodule

Ada - How to implement an asynchronous task that allows the main thread to poll it?

I want to create a task that reads from a file for a few minutes while the main thread does other things. But I'd like the main thread to be able to poll the task to see if it is "Busy" or not (a Boolean value) without blocking the main thread.
I have a naive attempt here, which does work but it leaves the Busy flag completely exposed to be toggled at will by the main thread (this is not safe)...
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
task type Non_Blocking_Reader_Task (Busy : access Boolean) is
entry Read (Destination : in Natural);
end Non_Blocking_Reader_Task;
task body Non_Blocking_Reader_Task is
begin
loop
select
when not Busy.all =>
accept Read (Destination : in Natural) do
Busy.all := True;
end Read;
for i in 1 .. 50 loop
Put ("."); -- pretend to do something useful
delay 0.1; -- while wasting time
end loop;
Busy.all := False;
end select;
end loop;
end Non_Blocking_Reader_Task;
Reader_Busy_Volatile : aliased Boolean;
Reader : Non_Blocking_Reader_Task (Reader_Busy_Volatile'Access);
begin
Put_Line (Reader_Busy_Volatile'Image);
Reader.Read (123);
for i in 1 .. 15 loop
Put_Line (Reader_Busy_Volatile'Image);
delay 0.5;
end loop;
abort Reader;
end Main;
My second idea was to create a protected type and hide the flag and the task inside it, but this is not permitted by the language.
Question
How can I create a protected "task is busy" flag that can is read-only from the main thread and read/write from the task (which does not cause the main thread to block)?
Edit:
The solution!
My revised (working) solution based on the stirling advice of #flyx :)
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
task type Reader_Task is
entry Read (Destination : in Natural);
entry Join;
entry Ready;
end Reader_Task;
task body Reader_Task is
Dest : Natural;
begin
loop
select
accept Read (Destination : in Natural) do
Dest := Destination;
end Read;
-- we only get here after a Read has been received.
for i in 1 .. 5 loop
Put ("."); -- pretend to do something useful
delay 1.0; -- while wasting time
end loop;
or
accept Join;
or
accept Ready;
or
terminate;
end select;
end loop;
end Reader_Task;
Reader : Reader_Task;
begin
-- first call will not block.
Reader.Read (123);
Put_Line ("MAIN: Reading in progress on second thread");
for i in 1 .. 12 loop
select
-- NON-BLOCKING CALL!
Reader.Ready; -- test if task is busy
Put_Line ("MAIN: NON-BLOCKING CALL SUCCEEDED -- TASK IS NOT BUSY");
else
Put_Line ("MAIN: NON-BLOCKING CALL FAILED -- TASK IS BUSY");
end select;
delay 1.0;
end loop;
Put_Line ("Main: Waiting for Reader (BLOCKING CALL)...");
Reader.Join;
Put_Line ("Main: all finished!");
end Main;
I've added two more entries to the task: Join and Ready which are basically the same but for the names. Join reminds me to do a blocking call to it, and Ready indicates that a non-blocking call is suitable for testing task availability. I've done this because there are times when I want to know if the previous run of Read() has finished without firing off a new one. This lets me do this neatly and all without any discrete flags at all! Awesome.
In Ada, the caller decides whether an entry call is blocking or not. You should not try and implement code for checking this inside the task.
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
task type Reader_Task is
entry Read (Destination : in Natural);
end Reader_Task;
task body Reader_Task is
begin
loop
select
accept Read (Destination : in Natural) do
null;
-- store Destination (?)
end Read;
or
-- allow task to be terminated while waiting
terminate;
end select;
-- we only get here after a Read has been received.
for i in 1 .. 50 loop
Put ("."); -- pretend to do something useful
delay 0.1; -- while wasting time
end loop;
end loop;
end Reader_Task;
Reader : Reader_Task;
begin
-- first call will not block.
Reader.Read (123);
for i in 1 .. 15 loop
-- check whether Read can be called immediately and if yes,
-- call it.
select
Reader.Read (456);
else
-- Read is not available, do something else.
null;
end select;
delay 0.5;
end loop;
-- don't call abort; let Reader finish its current task.
-- Reader will be terminated once it waits on the terminate alternative
-- since the parent is finished.
end Main;
The select … else … end select structure is Ada's way of doing a non-blocking call. You don't use a flag for signalling that the task is ready to receive an entry call because potentially, this state could change between the query of the flag and the actual call of the entry. select has been designed to avoid this problem.

How to send and receive in a PROMELA process without timeout / deadlock?

I cannot wrap my head around this PROMELA problem: I have N processes ("pc") which may both send and receive messages over a channel ("to_pc"). Each process has its own channel over which it receives messages.
For a process to be able to receive, I have to keep it in a loop which checks the channel for incoming messages. As a second loop option the process sends a message to all other channels.
However, in simulation mode, this always causes a timeout, without anything being sent at all. My theory so far is that I created a deadlock where all processes want to send at once, causing them all to be unable to receive (since they are stuck in their "send" part of the code).
So far I have been unable to resolve this problem. I have tried to use a global variable as a semaphore to "forbid" sending, so that only one channel may send. However, this did not change the results. My only other idea is to use a timeout as the trigger for the sending, but this does not seem right to me at all.
Any ideas? Thanks in advance!
#define N 4
mtype={request,reply}
typedef message {
mtype type;
byte target;
byte sender;
};
chan to_pc[N] = [0] of {message}
inline send() {
byte j = 0;
for (j : 0 .. N-1) {
if
:: j != address ->
to_pc[j]!msg;
:: else;
fi
}
}
active [N] proctype pc(){
byte address = _pid;
message msg;
do
:: to_pc[address]?msg -> /* Here I am receiving a message. */
if
::msg.type == request->
if
:: msg.target == address ->
d_step {
msg.target = msg.sender
msg.sender = address;
msg.type = reply;
}
send();
:: else
fi
:: msg.type == reply;
:: else;
fi
:: /* Here I want to send a message! */
d_step {
msg.target = (address + 1) % N;
msg.sender = address;
msg.type = request;
}
send();
od
};
I can write a full-fledged working version of your source code if you want, but perhaps it is sufficient to highlight the source of the issue you are dealing with and let you have fun solving it.
Branching Rules
any branch with an executable condition can be taken, non-deterministically
if there is no branch with an executable condition, the else branch is taken
if there is no branch with an executable condition and no else branch, then the process hangs till when one of the conditions becomes true
Consider this
1: if
2: :: in?stuff -> ...
3: :: out!stuff -> ...
4: fi
where in and out are both synchronous channels (size is 0).
Then
if someone is sending on the other end of in then in?stuff is executable, otherwise it is not
if someone is receiving on the other end of out then out!stuff is executable, otherwise it is not
the process blocks at line 1: up until when at least one of the two conditions is executable.
Compare that code to this
1: if
2: :: true -> in?stuff; ...
3: :: true -> out!stuff; ...
4: fi
where in and out are again synchronous channels (size is 0).
Then
both branches have an executable condition (true)
the process immediately commits itself to either send or receive something, by non-deterministically choosing to execute a branch either at line 2: or 3:
if the process chooses 2: then it blocks if in?stuff is not executable, even when out!stuff would be executable
if the process chooses 3: then it blocks if out!stuff is not executable, even when in!stuff would be executable
Your code falls in the latter situation, since all the instructions within d_step { } are executable and your process commits to send way too early.
To sum up: in order to fix your model, you should refactor your code so that it's always possible to jump from send to receive mode and viceversa. Hint: get rid of that inline code, separate the decision to send from actual sending.

Ada - Task termination error - "Statement expected"

I have the following task
task body auto is
begin
Put_Line( licencepalte.all & " set off.");
delay traveltime.all;
Put_Line( licencepalte.all & " arrived.");
loop
select
indicator.passthrough;
terminate; -- error for this line: 'statement expected'
or
delay 0.2;
Put_Line( licencepalte.all & " is waiting.");
end select;
end loop;
end auto;
where we represent an indicator light and some cars (auto ~ automobiles) with tasks and protecteds. My main issue is, that I don't know, how to terminate, in case the indicator accepts the entry of the auto. You can see what I'm currently trying to do, and it pops up an error (see inline). How do I stop the task once the entry gets accepted? Why does this terminate not work? Thank you!
terminate is not an "action" that you perform. That is, you can't use a terminate statement anywhere you choose in the task body to terminate the task. The way to terminate a task is for the execution to reach the end that ends the body; in your case, exit to exit the loop works, as in Jacob's answer.
The purpose of or terminate is tell the program that a task is eligible for termination (I don't know if there's a better technical term for this). Suppose your task looks like:
task body Task_Type_1 is
begin
loop
select
accept Entry_1(...parameters...) do
-- something
end Entry_1;
or
accept Entry_2(...parameters...) do
-- something
end Entry_2;
end select;
end loop;
end Task_Type_1;
If the "something" code of the accept statements never exits the loop, the task will never terminate. This means that other constructs enclosing the task can never terminate. For example:
procedure Proc is
T1 : Task_Type_1;
begin
-- do some stuff
-- now we're at the end, and we have to wait for T1 to complete
end Proc;
The procedure creates a task of type Task_Type_1 and starts it. Then the body of the procedure is executed. When end Proc; is reached, the procedure doesn't terminate immediately, because it has to wait until the task finishes its job. But the way the task is written, the task will never complete. Therefore Proc will never return, and the program will probably deadlock.
or terminate is how to say that the task could terminate:
task body Task_Type_1 is
begin
loop
select
accept Entry_1(...parameters...) do
-- something
end Entry_1;
or
accept Entry_2(...parameters...) do
-- something
end Entry_2;
or
terminate;
end select;
end loop;
end Task_Type_1;
In this small example, where we have a procedure that just creates this one task, or terminate means: if this task reaches a point where it's blocked in the select because there aren't any entry calls waiting, and if Proc has reached the end of its code, then we terminate the task. The task body exits, any finalization that needs to be done is done, and then Proc can complete.
or terminate can be used only in a "selective accept". If you say select Some_Other_Task.Entry_2(...); so that it blocks until the other task's entry is available, you can't use or terminate in that kind of select.
In a more complex case, a procedure could create two or more tasks. When that procedure reaches its end statement, it won't return until (roughly speaking) all the tasks it creates are completed or all of the tasks that haven't completed are blocked on select statements that have or terminate clauses. If the latter happens, then all of those tasks complete and then the procedure can return.
The rule about "terminate alternatives" is in RM 9.3(6). It speaks in terms of depending on a master; in the example I showed above, Proc is the master.
If I understand your question correctly, an exit would do nicely instead of terminate.

Handling error from one stored procedure into another in Sybase ASE 15.0

I need to execute a password reset on a Sybase ASE dataserver based on certain conditions:
if validations_here
begin
exec sp_password 'sso_passw', 'new_passw', #userid
end
sp_password might raise some errors, e.g. 10316 - "New password supplied is the same as the previous password". Although I couldn't find any documentation, I think they shouldn't be fatal errors and it should be possible to emulate them with raiserror.
Since it would be easier for the caller to handle it that way, I would like to get the error code and return it as part of a resultset, so I thought about SELECTing ##error. My code is as follows (I transcribed only those parts I think are relevant to the problem):
create procedure sp_desbloc_blanqueo_usr
#userid sysname,
#sso_pass varchar(20),
#new_pass varchar(20)
as
begin
declare #ret_code int
declare #ret_msg varchar(100)
declare #myerror int
select #ret_code = 0, #ret_msg = 'OK'
exec sp_password #sso_pass, #new_pass, #userid
set #myerror = ##error
if #myerror <> 0
begin
select #ret_code = #myerror, #ret_msg = 'Error occurred changing password'
-- It would be nice to have the actual error message as well
goto fin
end
fin:
select #ret_code as ret_code, #ret_msg as ret_msg
end
However, whenever I execute the stored procedure, I get 0 as ret_code and OK as ret_msg (even if parameters to sp_password are wrong).
How can I "catch" the error code of sp_password from my stored procedure?
Many "sp_" stored procedures set a nonzero return code when something goes wrong. Usually it is better to handle this return code than trying to catch errors raised inside the stored procedure. IIRC, this catching would not be possible with Transact-SQL; a 3rd generation language such as C would be required.
To get the return code of myproc stored procedure into variable #myvar, the syntax is
exec #myvar = myproc [arguments]
A simple example with sp_password:
declare #spreturn int
exec #spreturn = sp_password 'notmyoldpw', 'notmynewpw'
print "Return from sp_password is %1!", #spreturn
go
Server Message: Number 10315, Severity 14
Server 'SDSTRHA01_SY01', Procedure 'sp_password', Line 148:
Invalid caller's password specified, password left unchanged.
Server Message: Number 17720, Severity 16
Server 'SDSTRHA01_SY01', Procedure 'sp_password', Line 158:
Error: Unable to set the Password.
(1 row affected)
Return from sp_password is 1
(return status = 1)
The int variable #spreturn defined in the first line got sp_password return code, whose value was one as shown by (return status = 1) in the last message line. The reason why it was not zero is clear: there were two errors inside sp_password, 10315 and 17720. The point is to focus in this nonzero return code and ignore 10315 and 17720. In your stored proc, #spreturn ought to be checked against zero. If zero it ran OK, else something failed.

Resources