A simple way to avoid this program to hang?

Assume the following record defined with a “method k” in a package:

type rec1_type is record
  i : integer;
end record;
function k(rec1 : rec1_type) return Integer is (rec1.i + 1);
``
From "outside" this looks like a record with two components:
rec1.i and rec1.k   (where rec1.k = rec1.i + 1)

Then assume a protected object with a function f with argument of type rec1_type:

function f(rec1 : rec1_type) return Integer is (2*rec1.k);

Hence f makes a call to k outside the protected object and the program "hangs".
Did I understand correctly?  If so, have you any good advice what 
I should do to avoid the progamme to hang?

reinert

Please, post complete code. There is no reason why the program you described should hang.

Obs, the function k (in my real situation) calls back again to the protected object which is then blocked :slight_smile:
reinert
(department of impulsive posting - but your response helped)

So, you have: protected X.F that calls unprotected K which then calls protected X.S?

You can move K into the protected type. Call X.K from X.F. Call X.K from K. This would make X.S an internal call.

I think Ada forbids calling a potentially blocking call inside a protected operation, so if K blocks, then you can’t call it in F.

It’s hard to gen up a solution without a more complete example. In the above example you gave K doesn’t call back to the protected object. Can you flesh out the example a bit. In your example, K only pulls from the non protected rec1_type and we don’t know what the protected object looks like (or what the other function k calls looks like).

My first thought is that you shouldn’t need function F in the same protected object as the one K calls a function from. If you need another value via F then I would just add another function to the protected object and have k call those two functions separately and do the math unprotected. If instead you need access to rec1_type protected, I would create a separate mutex like protected object and protect rec1_type that way (otherwise you have the protected object of f doing two different jobs).

The relevant ARM section is 9.5 (41/5).

The background is that a protected action implementation may use a simple spin lock. If a call is not recognised as internal the implementation would try to lock again which would deadlock.

Given your definitions of Rec1_Type, function K, and

Rec1 : Rec1_Type;

then

Rec1.K

is illegal.

Why this hangs:

with Ada.Text_IO;
use  Ada.Text_IO;
procedure Rktest1 is

   function f_outside1 (i : Integer) return Integer;

   protected po1 is
      function f_inside1 (i : Integer) return Integer;
      function f_inside2 (i : Integer) return Integer;
   end po1;

   protected body po1 is
      function f_inside1 (i : Integer) return Integer is (f_outside1 (i));
      function f_inside2 (i : Integer) return Integer is (i + 10);
   end po1;

   function f_outside1 (i : Integer) return Integer is (po1.f_inside2 (i));

begin
   Put_Line ("Start");
   Put_Line (po1.f_inside1 (5)'Image);
   Put_Line ("Stop");
end Rktest1;

reinert

It hangs because f_outside1 makes an external call to po1.f_inside2 during a protected action initiated by po1.f_inside1.

ARM 9.5.1:

During a protected action, it is a bounded error to invoke an operation that is potentially blocking . The following are defined to be potentially blocking operations:

  • an external call on a protected subprogram (or an external requeue) with the same target object as that of the protected action;
1 Like

As Dmitry mentions, the program hangs because the call from f_outside1 is deemed external and therefore an attempt is made to acquire the monitor lock which is already held by f_inside1 so the end result is a kind of deadlock. I.e. the f_outside1 call is blocked waiting to get the lock (I believe more accurately it spin-waits as this is not avoidance synchronisation as it would be for entries) and the f_inside1 simply waits for the former call to complete.

If however, the call was made from within the protected object subprogram then that would be fine because protected objects in Ada are re-entrant. So, something like the following works:

protected body po1 is
   function f_inside1 (i : Integer) return Integer is (f_outside1 (i) + f_inside2 (i));
   function f_inside2 (i : Integer) return Integer is (i + 10);
end po1;

function f_outside1 (i : Integer) return Integer is (1);

One thing I’m wondering though is why is this happening for protected functions. AFAIK protected functions require a read-lock to be accepted (since they don’t modify any state) whilst procedures require read-write locks. So that there can be multiple readers accessing the protected object concurrently but there can only be just one writer changing the state of the object.

Isn’t the external call pretty much equivalent to say a separate task calling po1.f_inside1? Or am I missing/misinterpreting something?

1 Like

It is a really interesting question. It is implementation dependent I think. E.g. it does not hang under Windows x86_64.

Provided, that Linux has quite mediocre synchronization primitives and POSIX, well, POSIX is POSIX. Maybe the Linux run-time uses a simple lock rather than read lock. I believe Ada does not require for functions to be executed concurrently.

Maybe some of our “posh” friends could test it under Mac OS / aarch64?

1 Like

Oh, right! I was under the impression this is mandated by the RM.

Would be interesting to see what the behaviour on MacOS is indeed, I’ve only run this on a “common” Ubuntu machine. :slightly_smiling_face:

It hangs under arm64.

1 Like