1

I have an Ada task, Do_Something, that needs to "invoke" itself. The way I think of it coming from C++, I have a thread running void Do_Something() and sometimes void Do_Something() needs to spawn more threads that also run void Do_Something().

Ada (which I'm learning) doesn't like that. This seems to be a M(non)WE:

task body A_Task is

  new_task: access A_Task;
  my_level: Natural;

begin

  accept Do_Something(level: in Natural) do
    my_level := level + 1;
  end Do_Something;

  if my_level < 4 then
    new_task := new A_Task;
    new_task.Do_Something(my_level);
  end if;

end A_Task;

GNAT at least doesn't like this, reporting an error at new_task.Do_Something(my_level); because

task type cannot be used as type mark within its own spec or body

Yet this is easily circumvented by adding a procedure like this outside the task:

procedure Circumvent(level: Natural) is
    new_task: access A_Task;
begin
    new_task := new A_Task;
    new_task.Do_Something(level + 1);
end Circumvent;

then modifying the if statement in A_Task's body to this:

  if my_level < 4 then
    Circumvent(my_level);
  end if;

This latter version passes GNAT (now we have a M***W***E) and the program runs just fine, even in a non-trivial example I was playing with last night.

This workaround seems so straightforward that I don't understand why the compiler should raise the first error at all! I imagine I must be attacking this problem in completely the wrong way. Is this considered good Ada technique, and if not, what would be the Ada-like way to do this sort of thing?

John Perry
  • 2,497
  • 2
  • 19
  • 28

1 Answers1

5

This behaviour is specified in RM 8.6(17/3): "If a usage name appears within the declarative region of a type_declaration and denotes that same type_declaration, then it denotes the current instance of the type (rather than the type itself);"

This implies that the actual name of the type cannot be used to instantiate a different object. A wrapper (like yours) would then be one way of doing it. A subtype should also work subtype Foo is A_Task;

Be aware, though, that the C++ way of thinking/doing things is rarely the Ada way of doing things

egilhh
  • 6,464
  • 1
  • 18
  • 19
  • "the current instance of the type" -- I was wondering what this means until I added `A_Task.my_level := 2;` to the body of `A_Task`. Hunh. The RM even gives a reason, "we need to be able to denote the current task or protected object." Thanks. – John Perry May 08 '18 at 18:26
  • "Be aware, though, that the C++ way of thinking/doing things is rarely the Ada way of doing things" You ain't kidding! Is there a different, more Ada-like approach you would recommend? A procedure/function calling itself is a common approach in computation (especially in what I do), and it seems natural to use a Task if you want to take advantage of concurrency. – John Perry May 08 '18 at 18:27
  • That depends on what you're actually trying to achieve (ie. what problem are you trying to solve) – egilhh May 08 '18 at 18:35
  • Loosely based on your question, maybe a workpool of tasks can wait on a protected entry until a job is available for processing and put more jobs into the protected queue when needed? – egilhh May 08 '18 at 18:38
  • 1
    This particular problem was implementing concurrency in a [PL/0 compiler & interpreter](https://en.wikipedia.org/wiki/PL/0). This is a stack machine with several stacks (that was the suggestion, anyway) and the stacks need to be started on occasion. So I have a task with `entry Start` and a procedure called `Run_Stack`; after `Start` finishes setup, it calls `Run_Stack`. I wanted `Run_Stack` to dispatch a new task when needed, but encountered this problem, so I wrote a new procedure outside the task to do it. Were I to redo it, I'd probably put `Run_Stack` outside the task. – John Perry May 08 '18 at 18:44
  • the subtype approach gives the same error, because I can't base a subtype on an instance name. – TamaMcGlinn Sep 19 '21 at 19:05
  • 1
    @TamaMcGlinn, because you put the subtype declaration inside the task? – egilhh Sep 19 '21 at 19:14
  • Indeed, it works fine once put before the task body. Thanks – TamaMcGlinn Nov 15 '21 at 04:02