Ada - try to call entry of element of array of protected type, if busy - try next element - arrays

I'm working on a multithreading project and i have one array of tasks, let's call it A and one array of protected, let's call it B.
Now every element of A at some point will want to access entry from one of the elements of B. Is there a way to iterate quickly through B's and find accesible one?
I was browsing some resources avalaible online and found only
select
call entry;
or
delay;
which won't do the job.
Is there a way to do that that i'm not aware of?
Thanks in advance!
EDIT
So i got really excited about that select else statements and tried it out, unfortunately it still doesnt work, if I try it this way - using Mr. Simon Wright's code sample to simulate effect i need, the
else
part of code never gets executed, they (the taskArray elements) all think they can acces Call_Me and get stuck in the queue.
How can i solve this issue? I also tried using 'Count thing, but it somehow always ended up being 0.
with Ada.Text_IO; use Ada.Text_IO;
procedure Array_Of_POs is
protected type PO is
entry Call_Me;
private
Callable : Boolean := True; --changed
end PO;
protected body PO is
entry Call_Me when Callable is
begin
Callable := False; --added
Put_Line("Doin stuff");
delay 1.0;
Callable := True;
end Call_Me;
end PO;
POs : array (1 .. 3) of PO;
Success : Boolean := False;
task type Test; --added
task body Test is
begin
for J in POs'Range loop -- '
select
POs (J).Call_Me;
Success := True;
else
Put_Line("I never get displayed!"); --added
end select;
end loop;
Put_Line ((if Success
then "succeeded, oh dear"
else "didn't succeed, good"));
end Test;
testArray : array (1..3) of Test;
end Array_Of_POs;

You could try finding an entry E whose Count attribute (E’Count, ARM 9.9(5)) is zero; but this is going to result in race conditions, since another task could sneak in between your checking the count and your making the call. And see the note at (7), ibid.
You need a conditional entry call, as in ARM 9.7.3.
It would look something like
with Ada.Text_IO; use Ada.Text_IO;
procedure Array_Of_POs is
protected type PO is
entry Call_Me;
private
Callable : Boolean := False;
end PO;
protected body PO is
entry Call_Me when Callable is
begin
null;
end Call_Me;
end PO;
POs : array (1 .. 3) of PO;
Success : Boolean := False;
begin
for J in POs'Range loop -- '
select
POs (J).Call_Me;
Success := True;
else
null;
end select;
end loop;
Put_Line ((if Success
then "succeeded, oh dear"
else "didn't succeed, good"));
end Array_Of_POs;
In your case, I suspect this construct would be in an outer loop, so you’d need to do something to avoid busy-waiting (on the lines of putting in a delay if not Success, perhaps).
EDIT
This is a response to your edited question.
ARM 9.5.1(8) states that your PO.Call_Me contains a bounded error. In the next paragraph,
If the bounded error is detected, Program_Error is raised. If not detected, the bounded error might result in deadlock or a (nested) protected action on the same target object.
At least GNAT hasn’t done either of those.
The issue is that the body of the entry is executed in the context (thread) of the caller. So what happens is
Test calls POs(1).Call_Me
The call is accepted
Callable is set False
Put_Line and delay are called (still in the context of Test)
Callable is set True
POs(1).Call_Me returns to Test
...
The approach you’ve adopted might work if instead of using an array of POs you used an array of tasks.

Related

Is this decision correct? PL/SQL

Can anyone help me with the following task:
Create a procedure SectionCount(instructor_ID) that, according to the instructor's ID, displays the numbers of those of its sections with the largest number of students enrolled. Display appropriate messages if an instructor with such an ID does not exist or if there are no sections to lead. Add a block to handle the necessary exceptions.
my solution is the following..whether it is correct:
CREATE OR REPLACE FUNCTION your_function_name(i_student_id NUMBER)
RETURN NUMBER AS v_sections_count NUMBER;
BEGIN
SELECT COUNT(SECTION_ID) into v_sections_count FROM ENROLLMENT WHERE STUDENT_ID = i_student_id;
IF v_sections_count > 3 THEN
RETURN -1;
ELSE
RETURN v_sections_count;
END IF;
EXCEPTION WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('An error occured.');
RETURN -2;
END;
A suggestion or two, if I may.
always use table's alias when referencing columns. In your case, that's only one table but - often you have to "fix" a query and add yet another table, and then you don't know which column belongs to which table and have to add aliases anyway.
try to use only one RETURN per function (two, if there's an exception handling section - as in your function) because you might do something wrong in code logic and RETURN (i.e. terminate further execution) although you didn't actually want to do that. Instead, add a new variable which will be used to "calculate" the return value (such as retval in code I posted); then return its value
DBMS_OUTPUT.PUT_LINE is OK for debugging; nobody else will ever see it, because end users won't call that function from SQL*Plus (or other tools which are capable of displaying that message). For example, it (DBMS_OUTPUT.PUT_LINE) won't raise error in Oracle Apex or Oracle Forms, but you won't see anything.
Also, add SQLERRM which will actually show you which error happened. Info "an error occurred" isn't very descriptive
So:
CREATE OR REPLACE FUNCTION your_function_name (i_student_id NUMBER)
RETURN NUMBER
AS
v_sections_count NUMBER;
retval NUMBER;
BEGIN
SELECT COUNT(e.section_id)
INTO v_sections_count
FROM enrollment e
WHERE e.student_id = i_student_id;
retval := CASE WHEN v_sections_count > 3 then -1
ELSE v_sections_count
END;
RETURN retval;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('An error occured: ' || SQLERRM);
retval := -2;
RETURN retval;
END your_function_name ;

Ada: Array of tasks

What is a good way to link an indexed task to a corresponding indexed protected type in SPARK?
For specifics, consider this setup:
subtype Thread_Range is Natural range 1..n;
protected type P is ... end P;
p: array(Thread_Range) of P;
For each p(i) I would like a task t(i) that monitors p(i) and, when it's ready, processes it. I can make this work pretty easily in Ada, but SPARK w/Ravenscar is more demanding. I've tried two approaches that appear to work fine when I run them:
Give T an Integer discriminant, then instantiate a T(i); for each i, but this grows burdensome with not-very-large i.
task type T(which: Integer);
t1: T(1);
t2: T(2);
...
Add an is_not_monitored function and a set_monitor procedure to P. Create an array of tasks without discriminant. When t(i) begins, it assigns itself to monitor the first p(j) it finds that hasn't already been assigned a monitor.
task type T;
task body T is
which: Integer;
available: Boolean;
begin
for i in Thread_Range loop
available := p(i).is_not_monitored;
if available then
p(i).set_monitor;
which := i;
end if;
end loop;
-- what the task does with p(i) follows
end T;
t: array(Thread_Range) of T;
I like the second one better, but not by much. In any case, SPARK "Prove" grumbles about potential data races, and I can see why (though I'm not sure it's actually due to this).
Hence the question.
This doesn’t cause gnatprove to choke.
And I think the main difference from your option 2 is that Claim checks whether the claim is possible and, if so, performs the claim in one protected call.
But I don’t quite see how to prove that the loop Claim in T exits with Ps (J) being claimed. I tried putting an assertion after the loop, but couldn’t get it to prove.
protected type P is
procedure Claim (Succeeded : out Boolean);
private
Claimed : Boolean := False;
end P;
subtype Thread_Range is Integer range 1 .. 2;
Ps : array (Thread_Range) of P;
Ts : array (Thread_Range) of T;
task body T is
Which : Integer;
begin
Claim:
for J in Thread_Range loop
declare
Claimed : Boolean;
begin
Ps (J).Claim (Succeeded => Claimed);
if Claimed then
Which := J;
exit Claim;
end if;
end;
end loop Claim;
loop -- having a loop keeps gnatprove quiet
delay until Ada.Real_Time.Time_Last;
end loop;
end T;
protected body P is
procedure Claim (Succeeded : out Boolean) is
begin
if not Claimed then
Claimed := True;
Succeeded := True;
else
Succeeded := False;
end if;
end Claim;
end P;
After out-of-band discussions with John, we’ve found that this postcondition can be proved:
procedure Claim (Succeeded : out Boolean)
with
Post =>
(Is_Claimed'Old or (Succeeded and Is_Claimed))
or
(not Succeeded and Is_Claimed);
Note that it’s not P’Old.Is_Claimed, mainly because ’Old requires a copy, and P is limited (because it’s a protected type).
We also found several alternative formulations that prove in GPL 2017 but not in CE 2018: for example,
(Is_Claimed
and
(Is_Claimed'Old xor Succeeded)
I'm not an expert in this, but it seems that you cannot show SPARK that there's a one-to-one relation between a task instance and a protected object instance unless you reference that protected object instance explicitly from a task instance. This is in particular to make SPARK prove that only one task will queue on the entry of a protected object; the Wait entry in the code below). Therefore (and while this might not be exactly what you're looking for), I could only solve the problem of connecting tasks and protected objects, and at the same time having a monitor functionality, by using a generic package that can be instantiated multiple times. This proves in GNAT CE 2018:
generic
package Generic_Worker with SPARK_Mode is
task T;
protected P is
entry Wait;
procedure Trigger;
private
Triggered : Boolean := False;
end P;
end Generic_Worker;
with body:
package body Generic_Worker with SPARK_Mode is
task body T is
begin
loop -- Ravenscar: Tasks must not terminate.
P.Wait;
end loop;
end T;
protected body P is
entry Wait when Triggered is
begin
Triggered := False;
-- Do some work.
end Wait;
procedure Trigger is
begin
Triggered := True;
end Trigger;
end P;
end Generic_Worker;
and instantiations:
with Generic_Worker;
pragma Elaborate_All (Generic_Worker);
package Workers with SPARK_Mode is
package Worker_0 is new Generic_Worker;
package Worker_1 is new Generic_Worker;
package Worker_2 is new Generic_Worker;
package Worker_3 is new Generic_Worker;
package Worker_4 is new Generic_Worker;
end Workers;

pascal illegal qualifier error when calling function from a procedure

function classes(o:integer): String;
var allclasses : array[1..7] of String;
begin
allclasses[1]:= 'class1';
allclasses[2]:= 'class2';
allclasses[3]:= 'class3';
allclasses[4]:= 'class4';
allclasses[5]:= 'class5';
allclasses[6]:= 'class6';
allclasses[7]:= 'class7';
classes := allclasses[o];
end;
Above you can see a function, which should receive an integer and give a result of string that was stored in array.
procedure loadthis(chosen : string);
var f: text;
i : integer;
begin
Assign(f, 'files\'+chosen+'.txt');
Reset(f);
ReadLn(f, i);
MyChar.clas := classes[i];
end;
When this procedure is called, it calls a "classes" function. Pleae note that Mychar ir a global variable.
begin
loadthis(FileName);
ReadLn;
Readln
end.
Ant this is the main program, which calls "loadthis" procedure.
I Have no idea whats wrong, but I am getting these errors:
Wrong amount of parameters specified
Illegal qualifier
Both errors come from this line:
MyChar.clas := classes[i];. I have really no idea what is wrong, maybe I can not call a function from a procedure ? Please help.
You're trying to access it as an array index, but it needs to be a function call:
MyChar.clas := classes(i); { note () instead of [] }
You should probably add some range checking, too. What happens if someone puts 20 in the text file? Your array only has items at indexes 1 through 7, so you'll get a runtime error when you call classes(20) with the out of range value.
(You could probably use a constant array for allclasses to lessen your code as well, but your instructor probably haven't gotten that far yet.)
Given your comment about not having an instructor, here's a suggestion about a better way to handle the function:
function classes(o:integer): String;
const
allclasses: array[1..7] of string = ('class1',
'class2',
'class3',
'class4',
'class5',
'class6',
'class7');
begin
{
Low() returns the lowest index of the array, and
High() returns the highest. The if statement makes sure
that o is between them. It is the range check I mentioned.
}
if (o >= Low(allclasses)) and (o <= High(allclasses)) then
classes := allclasses[o]
else
classes := '';
end;

Converting Ada closures to C callbacks (function + void*)

Most clean C APIs declare callback as a combination of callback function and a user data. User data is usually void*. WinAPI uses pointer-sized integer (lParam). During making a thick binding a natural desire is to allow Ada 2005 closures to be used in place of C callbacks.
I have a code. It works like a charm on GNAT (GPL 2012, x86-windows is tested at least), but generally there is no guarantee that Run_Closure_Adapter.X variable and Run_Closure.X argument will have the same internal structure.
The question is: is there a proper (standards-compliant) way to do this? Maybe a trick involving tagged types, interfaces or generics. There is at least one way of doing this: running closure executor and closures in different tasks and using rendezvous. But that's too slow.
Closure_Test.adb:
with Closure_Lib; use Closure_Lib;
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Strings.Fixed; use Ada.Strings.Fixed;
procedure Closure_Test is
procedure Closure_Tester is
Local_String : String := "Hello, world!";
procedure Closure is
begin
Put_Line (Local_String);
end Closure;
begin
Run_Closure (Closure'Access);
end Closure_Tester;
procedure Ada_Run_Closure (X : access procedure) is
begin
X.all;
end Ada_Run_Closure;
-- Nested_Closure fills the execution stack with
-- several activation records of Nested_Closure_Tester
-- Having done so (local I = 0) we start a Fibonacci
-- algorithm using Print_Closure access values of
-- different dynamic nesting levels
procedure Nested_Closure_Tester
(I : Integer;
Closure_Runner: access procedure (X : access procedure);
Prev_Closure, Prev_Closure2: access procedure)
is
procedure Print_Closure is
begin
if Prev_Closure /= null and Prev_Closure2 /= null then
Closure_Runner (Prev_Closure);
Closure_Runner (Prev_Closure2);
else
Put (".");
end if;
end Print_Closure;
procedure Nested_Closure is
begin
if I > 0 then
Nested_Closure_Tester (I - 1, Closure_Runner,
Print_Closure'Access, Prev_Closure);
else
Print_Closure;
end if;
end Nested_Closure;
begin
Closure_Runner (Nested_Closure'Access);
end Nested_Closure_Tester;
begin
-- Closure_Tester;
-- I = 6 gives 13 dots
Nested_Closure_Tester(6, Ada_Run_Closure'Access, null, null);
New_Line;
Nested_Closure_Tester(6, Run_Closure'Access, null, null);
end Closure_Test;
Closure_Lib.ads:
with Interfaces.C;
with System;
package Closure_Lib is
procedure Run_Closure (X : access procedure);
private
type Simple_Callback is access procedure(Data : in System.Address);
pragma Convention (C, Simple_Callback);
procedure Run_Callback (X : in Simple_Callback; Data : in System.Address);
pragma Import (C, Run_Callback, "Run_Callback");
procedure Sample_Callback (Data : in System.Address);
pragma Convention (C, Sample_Callback);
end Closure_Lib;
Closure_Lib.adb:
with Interfaces.C;
with System;
with System.Storage_Elements; use System.Storage_Elements;
with Ada.Text_IO; use Ada.Text_IO;
package body Closure_Lib is
procedure Sample_Callback (Data : in System.Address) is
begin
Ada.Text_IO.Put_Line ("Simple_Callback");
end Sample_Callback;
procedure Run_Closure_Adapter (Data : in System.Address);
pragma Convention (C, Run_Closure_Adapter);
procedure Run_Closure_Adapter (Data : in System.Address) is
X : access procedure;
for X'Address use Data;
pragma Import (Ada, X);
X_Size : constant Storage_Count := X'Size / System.Storage_Unit;
begin
-- Put_Line ("Variable access procedure size:" & Storage_Count'Image (X_Size));
X.all;
end Run_Closure_Adapter;
procedure Run_Closure (X : access procedure) is
X_Size : constant Storage_Count := X'Size / System.Storage_Unit;
X_Address : constant System.Address := X'Address;
begin
-- Put_Line ("Anonymous access procedure size:" & Storage_Count'Image (X_Size));
Run_Callback (Run_Closure_Adapter'Access, X_Address);
end Run_Closure;
end Closure_Lib;
closure_executor.c:
typedef void (*Simple_Callback)(void* Data);
void Run_Callback (Simple_Callback X, void* Data) {
(*X)(Data);
}
I think what you're looking for might be met by using a generic (by the way, I don't see how using a task can ensure that data types match?)
Maybe something like
generic
type Client_Data is private;
package Closure_G is
type Closure (<>) is private;
function Create (Proc : access procedure (Parameter : Client_Data);
And_Parameter : Client_Data) return Closure;
procedure Execute (The_Closure : Closure);
private
type Procedure_P is access procedure (Parameter : Client_Data);
type Closure is record
The_Procedure : Procedure_P;
And_Parameter : Client_Data;
end record;
end Closure_G;
When a user calls Execute (A_Closure), the Proc supplied to Create is called with the And_Parameter that was supplied then.
(The type Closure (<>) is private; makes sure tht users can only create a Closure object using the supplied Create.)
The main trouble with this, in your scenario of passing to a C library to be called-back when an event occurs, is that the Closure object is actually maintained by the C library.
Aside from the fact that you don't really need this Ada Closure, there's a potential problem caused by anonymous access-to-subprogram values, which is that the subprogram could be locally declared and have gone out of scope by the time the C library gets round to calling it. This would be Bad News.
In the Ada world, the compiler copes with this problem in two ways. First, you're not allowed to store anonymous access-to-subprogram values (hence the type Procedure_P above). Second, even if you work round this as in
function Create (Proc : access procedure (Parameter : Client_Data);
And_Parameter : Client_Data) return Closure is
begin
return (The_Procedure => Procedure_P'(Proc),
And_Parameter => And_Parameter);
end Create;
the actual 'accessibility levels' are checked at run time; if you get it wrong you'll get a Program_Error.
As an alternative, you might look at how GtkAda handles callbacks from GTK+. As shown in the GtkAda User’s Guide, and discussed in §4.2.2. Connecting via the Gtk.Handlers package,
The Gtk.Marshallers package provides a set of functions that can be used as callbacks directly for GtkAda…A set of To_Marshaller functions is found in every generic package in Gtk.Handlers. They take a single argument, the name of the function you want to call, and return a handler that can be used directly in Connect.
Interaction is an example that instantiates several such handlers and connects the corresponding callback using an access-to-subprogram parameter.

Problems with writing to a MS Access Database (Delphi)

I'm trying to write bits of code to a Microsoft access database from Delphi. I'm getting data from a TStringGrid. The first column has the ItemID, and the 2nd column has the Quantity. I'd like it to loop through the TStringGrid and save each row as a reperate row in my database and also save the Order ID with it on every column (The order ID stays the same for each order so that doesn't need to change) .
I'm getting an error when running which says
"Project Heatmat.exe raised an exception class EVarientInvalidArgError with message 'Invalid Argument'. Process Stopped."
I can't figure out why it's giving me this error, and as you can probably see i'm not very good at coding yet. Any help would be appreciated!
Thank you.
procedure TCreateNewOrder.btnSaveClick(Sender: TObject);
var
intNumber, count : integer;
begin
Count:= 0;
if messagedlg ('Are you sure?', mtWarning, [mbyes, mbno], 0) = mryes then
begin
with HeatmatConnection.HeatmatDatabase do
begin
intNumber:= TBLOrder.RecordCount;
TBLOrder.Append;
TBLOrder['CustomerID']:= CompanyName.ItemIndex+1;
TBLOrder['OrderID']:= intNumber +1;
for count:= 1 to StringGrid1.RowCount-1 do
begin
TBLOrderedItem.Append;
TBLOrderedItem['OrderID']:= intNumber+1;
TBLOrderedItem['ItemID']:= StringGrid1.Cells[1, count];
TBLOrderedItem['Quantity']:= StringGrid1.Cells[2, count];
TBLOrderedItem.Post;
end;
end;
end;
end;
TStringGrid cells are strings. trying to assign a string directly to a numeric field will raise an Exception.
So a good practice is to assign values to database fields via AsString, AsInteger, AsBoolean etc... this will make the correct conversion.
In your code use:
TBLOrderedItem.FieldByName('ItemID').AsString := StringGrid1.Cells[1, count];
The same is true for Quantity.
To assign an Integer value use:
TBLOrderedItem.FieldByName('OrderID').AsInteger := intNumber + 1;
BTW, you are forgetting TBLOrder.Post i.e:
....
TBLOrder.Append;
TBLOrder.FieldByName('CustomerID').AsInteger := CompanyName.ItemIndex + 1;
TBLOrder.FieldByName('OrderID').AsInteger := intNumber + 1;
TBLOrder.Post;
...
Finally, I would also suggest to rename TBLOrder to tblOrder so that it's name wont imply that it is a Type.

Resources