comp.lang.ada
 help / color / mirror / Atom feed
From: Patrick Noffke <patrick.noffke@gmail.com>
Subject: Re: Ravenscar and context switching for Cortex-M4
Date: Thu, 19 Feb 2015 12:14:42 -0800 (PST)
Date: 2015-02-19T12:14:42-08:00	[thread overview]
Message-ID: <247a5033-337c-4bf9-8b37-c82759d8a2dd@googlegroups.com> (raw)
In-Reply-To: <ly8ufxczrt.fsf@pushface.org>

On Monday, February 16, 2015 at 3:28:08 PM UTC-6, Simon Wright wrote:
> Patrick Noffke writes: 
>  
> > Here's what happens now (the order of the interrupts may change 
> > between runs, but this is for one capture): 
> > 
> > 1. UART interrupt triggers.  2. PO1's entry executes. 
>  
> because the entry body is executed in interrupt context. See 
> below. 
>  
> > 3. SPI interrupt triggers twice (see below).  4. PO2's entry 
> > executes.  5. T1 (UART task) executes.  This is the first thing 
> > wrong.  T2 is higher priority than T1 so T2 should run first. 
> > 6. T2 (SPI task) executes twice.  Upon the second execution, I 
> > get a program error because Object.Entry_Queue is null.  The 
> > exception is 
>  
> Entry_Queue is *not* null, as you said in the next post. 
>  
> > raised in s-tposen-raven.adb (line 167 in my copy) in 
> > Protected_Single_Entry_Call. 
> > 
> > This may be relevant -- the SPI interrupt triggers twice.  This 
> > is because the interrupt is for a DMA completion, and it fires 
> > both when TX and RX complete (since it's SPI, they complete at 
> > the same time).  I take care in my interrupt handler to release 
> > the entry from only one of the two interrupts.  Perhaps with the 
> > interrupt firing twice, the runtime may get confused and 
> > activate the task twice (even though the entry only executes 
> > once).  But for the above run, the entry was released during the 
> > second SPI interrupt. 
>  
> The RTS does this (I hope I have it right): 
>  
>    The entry call (Protected_Single_Entry_Call):
> 
>      locks the entry
>      if the barrier is open then
>        asserts that Call_In_Progress isn't set
>        sets Call_In_Progress
>        calls the entry body wrapper
>        clears Call_In_Progress
>        unlocks the entry
>      else
>        if the Entry_Queue isn't null then
>          unlocks the entry
>          raises PE
>        end if
>        sets the Entry_Queue
>        unlocks the entry
>        sleeps
>      end if
> 
>    The handler wrapper:
> 
>      locks the entry
>      calls another wrapper for the handler itself
>      calls Service_Entry
>      exits
> 
>    Service_Entry:
>      if the Entry_Queue is set and the barrier is open then
>        clears the Entry_Queue
>        asserts that Call_In_Progress isn't set
>        sets Call_In_Progress
>        calls the entry body wrapper
>        clears Call_In_Progress
>        saves the caller task_id
>        unlocks the entry
>        wakes the caller
>      else
>        unlocks the entry
>      end if
> 
> I really don't see how the sequnece you describe happens!
> 
> One thing that puzzles me is the locking/unlocking of the entry: this is
> done (in that RTS) by raising the caller task's priority to the ceiling
> priority of the task, if necessary. So what about interrupts? And when
> the handler wrapper (you can see this by compiling the package with the
> PO in with -gnatdg) locks the entry, it seems to raise the current
> task's priority, where the current task has nothing to do with the PO at
> all!
> 

Thank you for all this!  It helps a lot.  I didn't know about -gnatdg -- very useful.

I suspect the problem may stem from the fact that Leave_Kernel in s-bbprot.adb can insert a suspended task into the thread queue.  I am guessing somehow this is tripping up the runtime when multiple tasks become runnable at the same time.

I stepped through the debugger at startup, and I can see the suspended task going into the queue.  Then another task is woken up and put at the front of the queue, so the first task is Runnable and the Next task is Suspended.  Then when Leave_Kernel resumes running (it enables interrupts after inserting the suspended task) it may not call Extract (since the running thread state is Runnable) -- it never considers that the Suspended task it inserted might be later in the queue.

I haven't yet been able to directly correlate the Leave_Kernel behavior with the task incorrectly waking up twice.  I'll keep trying things, but I wanted to share what I found so far.

What I have confirmed is that I can get the system into a state where there are two Runnable tasks in the thread queue, and the "Next" field of the last one points to the first one.  That is:

First_Thread_Table (CPU_Id) = First_Thread_Table (CPU_Id).Next.Next

Right before this happens is when the two tasks are woken up at the same time.

It appears the task that runs twice (when I get the Program_Error I reported earlier) is the one that gets put in the queue in the suspended state at startup (an empirical data point after changing priorities of my tasks).

Best regards,
Pat


  reply	other threads:[~2015-02-19 20:14 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-02-12 20:25 Ravenscar and context switching for Cortex-M4 Patrick Noffke
2015-02-12 21:28 ` Niklas Holsti
2015-02-13 12:41   ` G.B.
2015-02-13 16:25     ` Simon Wright
2015-02-13 18:08     ` Niklas Holsti
2015-02-13 19:01       ` Simon Wright
2015-02-13 23:45       ` Georg Bauhaus
2015-02-16 16:27 ` Patrick Noffke
2015-02-16 16:34   ` Patrick Noffke
2015-02-16 21:28   ` Simon Wright
2015-02-19 20:14     ` Patrick Noffke [this message]
2015-02-19 21:03       ` Bob Duff
2015-02-20 13:05         ` Simon Wright
2015-02-19 22:13       ` Patrick Noffke
2015-02-19 22:44         ` Patrick Noffke
2015-02-20  8:31           ` Simon Wright
2015-06-24 15:20           ` Patrick Noffke
2015-08-06 21:05     ` Patrick Noffke
2015-08-06 21:43       ` Patrick Noffke
2015-08-07 20:34         ` Patrick Noffke
replies disabled

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox