Understanding Orocos's threads activation

Hello all,

I don't understand how task priorities are managed by RTT
running on top of a priority-based RTOS. I'm using
preemp-rt for the test.

I programmed a static deployment as follows :


int ORO_main(int argc, char** argv)
{
     // Create the tasks:
     Task1 task1("Task1");
     Task2 task2("Task2");
     TaskScheduler taskSched("TaskScheduler");

     // Create the activities which run the tasks' 
engines:
     task1.setActivity( new Activity(ORO_SCHED_RT, 1, 0, 
0, "Activity1") );
     task2.setActivity( new Activity(ORO_SCHED_RT, 25, 0, 
0, "Activity2") );
     taskSched.setActivity( new Activity(ORO_SCHED_RT, 49, 
1, 0, "ActivityScheduler") );

     // Assign peers
     taskSched.addPeer(&task1);
     taskSched.addPeer(&task2);

     // Start
     taskSched.configure();
     taskSched.start();

     // MainThread loops forever
     while(true);
     return 0;
}


Side question: am I allowed to do static deployments
without a TaskBrowser by placing a "loop forever"
directive at the end of the MainThread?

Getting back to the original question, Task1 and Task2 are
trivial tasks that just print the string "**Taski**" on
the screen. They are programmed as non-periodic activities
but they actually execute periodically with low priority
at 2 secs and with high priority at 1 sec, respectively.
They are scheduled by the TaskScheduler that runs
periodically at the base period (1 sec) with very high
priority. The TaskScheduler object has an activation map
that it queries for the task to be activated at every
cycle ([t2,t1], [t2], [t2,t1], [t2], ...). It starts its
peers with


     std::cout << "-------- Scheduler updateHook() begin 
--------" << std::endl;
     for(std::vector<TaskContext*>::iterator it = 
(peersSchedTable[tick]).begin();
             it != (peersSchedTable[tick]).end(); ++it)
         (*it)->start(); // <----- *START*
     std::cout << "-------- Scheduler updateHook()  end 
 --------" << std::endl;


the first time and with

(*it)->trigger(); // <----- *TRIGGER*

the remaining times.

When I execute the program, this is the output that I get
on the screen:
<output>
-------- Scheduler updateHook() begin --------
-------- Scheduler updateHook() end --------
**Task2**
**Task1**
-------- Scheduler updateHook() begin --------
-------- Scheduler updateHook() end --------
**Task2**
-------- Scheduler updateHook() begin --------
-------- Scheduler updateHook() end --------
**Task1**
**Task2**
-------- Scheduler updateHook() begin --------
-------- Scheduler updateHook() end --------
**Task2**
-------- Scheduler updateHook() begin --------
-------- Scheduler updateHook() end --------
**Task1**
**Task2**
-------- Scheduler updateHook() begin --------
-------- Scheduler updateHook() end --------
**Task2**
-------- Scheduler updateHook() begin --------
-------- Scheduler updateHook() end --------
**Task2**
**Task1**
...
</output>

The TaskScheduler always completes the updateHook()
function. That's fine, because it has an higher priority
than Task1 and Task2. And this also means that at the end
of the execution of TaskScheduler, Task1 and Task2 have
been triggered and are both ready to start. However, the
execution of Task1 and Task2 seems to happen randomly:
sometimes Task2 executes before Task1 (ok) and sometimes
Task1 runs before Task2. Why this? Shouldn't Task2 be
*always* the first one to execute, considering that it has
an higher priority wrt Task1? Am I doing something the
wrong way?

Thank you in advance for your help.

--
Matteo

Understanding Orocos's threads activation

Hi Matteo,

On Sun, Feb 17, 2013 at 05:56:37PM +0100, Matteo Morelli wrote:
>
> I don't understand how task priorities are managed by RTT
> running on top of a priority-based RTOS. I'm using
> preemp-rt for the test.
>
> I programmed a static deployment as follows :
>
> &#10;&gt; int ORO_main(int argc, char** argv)&#10;&gt; {&#10;&gt;      // Create the tasks:&#10;&gt;      Task1 task1(&quot;Task1&quot;);&#10;&gt;      Task2 task2(&quot;Task2&quot;);&#10;&gt;      TaskScheduler taskSched(&quot;TaskScheduler&quot;);&#10;&gt; &#10;&gt;      // Create the activities which run the tasks&#039; &#10;&gt; engines:&#10;&gt;      task1.setActivity( new Activity(ORO_SCHED_RT, 1, 0, &#10;&gt; 0, &quot;Activity1&quot;) );&#10;&gt;      task2.setActivity( new Activity(ORO_SCHED_RT, 25, 0, &#10;&gt; 0, &quot;Activity2&quot;) );&#10;&gt;      taskSched.setActivity( new Activity(ORO_SCHED_RT, 49, &#10;&gt; 1, 0, &quot;ActivityScheduler&quot;) );&#10;&gt; &#10;&gt;      // Assign peers&#10;&gt;      taskSched.addPeer(&amp;task1);&#10;&gt;      taskSched.addPeer(&amp;task2);&#10;&gt; &#10;&gt;      // Start&#10;&gt;      taskSched.configure();&#10;&gt;      taskSched.start();&#10;&gt; &#10;&gt;      // MainThread loops forever&#10;&gt;      while(true);&#10;&gt;      return 0;&#10;&gt; }&#10;&gt;
>
> Side question: am I allowed to do static deployments
> without a TaskBrowser by placing a "loop forever"
> directive at the end of the MainThread?

You are allowed, but I don't think it's a very good idea to burn cpu
cycles in a tight loop.

> Getting back to the original question, Task1 and Task2 are
> trivial tasks that just print the string "**Taski**" on
> the screen. They are programmed as non-periodic activities
> but they actually execute periodically with low priority
> at 2 secs and with high priority at 1 sec, respectively.
> They are scheduled by the TaskScheduler that runs
> periodically at the base period (1 sec) with very high
> priority. The TaskScheduler object has an activation map
> that it queries for the task to be activated at every
> cycle ([t2,t1], [t2], [t2,t1], [t2], ...). It starts its
> peers with
>
> &#10;&gt;      std::cout &lt;&lt; &quot;-------- Scheduler updateHook() begin &#10;&gt; --------&quot; &lt;&lt; std::endl;&#10;&gt;      for(std::vector&lt;TaskContext*&gt;::iterator it = &#10;&gt; (peersSchedTable[tick]).begin();&#10;&gt;              it != (peersSchedTable[tick]).end(); ++it)&#10;&gt;          (*it)-&gt;start(); // &lt;----- *START*&#10;&gt;      std::cout &lt;&lt; &quot;-------- Scheduler updateHook()  end &#10;&gt;  --------&quot; &lt;&lt; std::endl;&#10;&gt;
>
> the first time and with
>
> (*it)->trigger(); // <----- *TRIGGER*
>
> the remaining times.
>
> When I execute the program, this is the output that I get
> on the screen:
> <output>
> -------- Scheduler updateHook() begin --------
> -------- Scheduler updateHook() end --------
> **Task2**
> **Task1**
> -------- Scheduler updateHook() begin --------
> -------- Scheduler updateHook() end --------
> **Task2**
> -------- Scheduler updateHook() begin --------
> -------- Scheduler updateHook() end --------
> **Task1**
> **Task2**
> -------- Scheduler updateHook() begin --------
> -------- Scheduler updateHook() end --------
> **Task2**
> -------- Scheduler updateHook() begin --------
> -------- Scheduler updateHook() end --------
> **Task1**
> **Task2**
> -------- Scheduler updateHook() begin --------
> -------- Scheduler updateHook() end --------
> **Task2**
> -------- Scheduler updateHook() begin --------
> -------- Scheduler updateHook() end --------
> **Task2**
> **Task1**
> ...
> </output>
>
> The TaskScheduler always completes the updateHook()
> function. That's fine, because it has an higher priority
> than Task1 and Task2. And this also means that at the end
> of the execution of TaskScheduler, Task1 and Task2 have
> been triggered and are both ready to start. However, the
> execution of Task1 and Task2 seems to happen randomly:
> sometimes Task2 executes before Task1 (ok) and sometimes
> Task1 runs before Task2. Why this? Shouldn't Task2 be
> *always* the first one to execute, considering that it has
> an higher priority wrt Task1? Am I doing something the
> wrong way?

Yes, you are relying on priorites to enforce an order. As mentioned
before, this will not work reliably. If your updateHooks took longer
to compute, then you could rely on the higher priority thread to
preempt the lower priority one. In your case, all you do is to call a
non-realtime safe io function, so you really can't assume anything
about the resulting order.

If you require a particular order, you need to take care of it
yourself using one of the approaches previously suggested on this
list.

Markus

Understanding Orocos's threads activation

Hi Markus,

On Sun, 17 Feb 2013 20:07:06 +0100
Markus Klotzbuecher
<markus [dot] klotzbuecher [..] ...> wrote:
> Hi Matteo,
>
> On Sun, Feb 17, 2013 at 05:56:37PM +0100, Matteo Morelli
>wrote:
>>
>> I don't understand how task priorities are managed by
>>RTT
>> running on top of a priority-based RTOS. I'm using
>> preemp-rt for the test.
>>
>> I programmed a static deployment as follows :
>>
>> &#10;&gt;&gt; int ORO_main(int argc, char** argv)&#10;&gt;&gt; {&#10;&gt;&gt;      // Create the tasks:&#10;&gt;&gt;      Task1 task1(&quot;Task1&quot;);&#10;&gt;&gt;      Task2 task2(&quot;Task2&quot;);&#10;&gt;&gt;      TaskScheduler taskSched(&quot;TaskScheduler&quot;);&#10;&gt;&gt; &#10;&gt;&gt;      // Create the activities which run the tasks&#039; &#10;&gt;&gt; engines:&#10;&gt;&gt;      task1.setActivity( new Activity(ORO_SCHED_RT, 1, 0, &#10;&gt;&gt; 0, &quot;Activity1&quot;) );&#10;&gt;&gt;      task2.setActivity( new Activity(ORO_SCHED_RT, 25, &#10;&gt;&gt;0, &#10;&gt;&gt; 0, &quot;Activity2&quot;) );&#10;&gt;&gt;      taskSched.setActivity( new Activity(ORO_SCHED_RT, &#10;&gt;&gt;49, &#10;&gt;&gt; 1, 0, &quot;ActivityScheduler&quot;) );&#10;&gt;&gt; &#10;&gt;&gt;      // Assign peers&#10;&gt;&gt;      taskSched.addPeer(&amp;task1);&#10;&gt;&gt;      taskSched.addPeer(&amp;task2);&#10;&gt;&gt; &#10;&gt;&gt;      // Start&#10;&gt;&gt;      taskSched.configure();&#10;&gt;&gt;      taskSched.start();&#10;&gt;&gt; &#10;&gt;&gt;      // MainThread loops forever&#10;&gt;&gt;      while(true);&#10;&gt;&gt;      return 0;&#10;&gt;&gt; }&#10;&gt;&gt;
>>
>> Side question: am I allowed to do static deployments
>> without a TaskBrowser by placing a "loop forever"
>> directive at the end of the MainThread?
>
> You are allowed, but I don't think it's a very good idea
>to burn cpu
> cycles in a tight loop.
>
>> Getting back to the original question, Task1 and Task2
>>are
>> trivial tasks that just print the string "**Taski**" on
>> the screen. They are programmed as non-periodic
>>activities
>> but they actually execute periodically with low priority
>> at 2 secs and with high priority at 1 sec, respectively.
>> They are scheduled by the TaskScheduler that runs
>> periodically at the base period (1 sec) with very high
>> priority. The TaskScheduler object has an activation map
>> that it queries for the task to be activated at every
>> cycle ([t2,t1], [t2], [t2,t1], [t2], ...). It starts its
>> peers with
>>
>> &#10;&gt;&gt;      std::cout &lt;&lt; &quot;-------- Scheduler updateHook() begin &#10;&gt;&gt; --------&quot; &lt;&lt; std::endl;&#10;&gt;&gt;      for(std::vector&lt;TaskContext*&gt;::iterator it = &#10;&gt;&gt; (peersSchedTable[tick]).begin();&#10;&gt;&gt;              it != (peersSchedTable[tick]).end(); ++it)&#10;&gt;&gt;          (*it)-&gt;start(); // &lt;----- *START*&#10;&gt;&gt;      std::cout &lt;&lt; &quot;-------- Scheduler updateHook()  end &#10;&gt;&gt;  --------&quot; &lt;&lt; std::endl;&#10;&gt;&gt;
>>
>> the first time and with
>>
>> (*it)->trigger(); // <----- *TRIGGER*
>>
>> the remaining times.
>>
>> When I execute the program, this is the output that I
>>get
>> on the screen:
>> <output>
>> -------- Scheduler updateHook() begin --------
>> -------- Scheduler updateHook() end --------
>> **Task2**
>> **Task1**
>> -------- Scheduler updateHook() begin --------
>> -------- Scheduler updateHook() end --------
>> **Task2**
>> -------- Scheduler updateHook() begin --------
>> -------- Scheduler updateHook() end --------
>> **Task1**
>> **Task2**
>> -------- Scheduler updateHook() begin --------
>> -------- Scheduler updateHook() end --------
>> **Task2**
>> -------- Scheduler updateHook() begin --------
>> -------- Scheduler updateHook() end --------
>> **Task1**
>> **Task2**
>> -------- Scheduler updateHook() begin --------
>> -------- Scheduler updateHook() end --------
>> **Task2**
>> -------- Scheduler updateHook() begin --------
>> -------- Scheduler updateHook() end --------
>> **Task2**
>> **Task1**
>> ...
>> </output>
>>
>> The TaskScheduler always completes the updateHook()
>> function. That's fine, because it has an higher priority
>> than Task1 and Task2. And this also means that at the
>>end
>> of the execution of TaskScheduler, Task1 and Task2 have
>> been triggered and are both ready to start. However, the
>> execution of Task1 and Task2 seems to happen randomly:
>> sometimes Task2 executes before Task1 (ok) and sometimes
>> Task1 runs before Task2. Why this? Shouldn't Task2 be
>> *always* the first one to execute, considering that it
>>has
>> an higher priority wrt Task1? Am I doing something the
>> wrong way?
>
> Yes, you are relying on priorites to enforce an order.
> As mentioned before, this will not work reliably.
> ...
> If you require a particular order, you need to take care
> of it yourself using one of the approaches previously
> suggested on this list.

I already used an approach with explicit activation[1],
but we are investigating also other design options. This
example is different from the one in [1], because I do not
merely start Task1 and Task2 as periodic activities from
the MainThread. Here I run a scheduler component at the
*highest* priority and the scheduler has Tasks as peers
and it explicitly trigger them. IMHO, here priorities must
enforce an execution order, because both the Tasks are
ready-to-execute at the end of the scheduler execution, so
only the one with highest priority should be selected to
execute by the RTOS. I just couldn't explain *why* this
didn't happen, so I opened a new thread.

[1]:
http://www.orocos.org/forum/orocos/orocos-users/deterministic-execution-...

> If your updateHooks took longer to compute, then you
> could rely on the higher priority thread to preempt the
> lower priority one.

Aha! It was so simple... :-). I overlooked this during
the test. I just added a simple summation in the code and
all is ok now. So, can I assume that I can rely on
priorities if updateHook()s perform a minimum workload
(this is always the case in the real world)?

> In your case, all you do is to call a non-realtime
> safe io function, so you really can't assume anything
> about the resulting order.

Well, I actually simplified the test code. I immediately
realized this and switched to pushing the strings to a
RTT::base::BufferLocked object. But this didn't help.

> Markus

--
Matteo