This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Issue with app_scheduler or queue_is_empty

SDK 14.2, SoftDevice 140, NRF52840.

My code is below. I am putting stuff on a queue (q1 in the code below) inside an interrupt. This stuff is then processed in the main loop via function2. 

Normally, this works perfectly fine. But when I have a bunch of things queued up inside the app_scheduler, so that app_sched_execute() actually does something when called, function2 doesn't seem to run properly to dequeue the stuff that has built up in q1. 

What I queue up using the app scheduler is function5. When function5 is done running, I check a condition to see if it needs to run again. If it does, then I add it again to the app_scheduler as you can see in the code. 

I thought the app_scheduler just processes one queued function at a time, each time the main loop is run through (e.g., my intent is to run through function1, function2, function3, function4, then function5, and if there are more function5 to process, it will go again through the sequence 1, 2, 3, 4, 5. (I will call this "CASE 1")

However, it seems like maybe it processes all functions that might be in the queue all at once, and since function5 queues up another function5 (if needed) before it returns, what I really get is 1, 2, 3, 4, 5, 5, 5, 5, 5 ... etc (I will call this "CASE 2"). (I am not sure about this, because it hard to set a print statement in function2 since it runs so often when the main loop runs, so it fills up all the logs, and i don't want to set a breakpoint for the same reason) So I wanted to ask about how the app_scheduler works. Is it like CASE1 or CASE2? 

If it is like CASE1, then it seems like queue_is_empty is not evaluating properly, since function2 never runs properly until there are no more function5 to process. 

If it is like CASE2, this is a bit of a problem, because sometimes I need function5 to run for quite some time and don't want to block function2. How can I make it run more like CASE1? 

Thanks!

// Enter main loop.
    for (;;)
    { 
        function1();
        function2();
        function3();
        function4();
        app_sched_execute(); //queues function5()
    }

...


void function2()
{
    if(!q1_queue_is_empty) {
        NRF_LOG_INFO("in func2, queue not empty");
        DATA_S dat;
		ret = q1_queue_pop(&dat);
		APP_ERROR_CHECK(ret);
		
        // do stuff with dat
    }
}

void function5(...)
{
    // do stuff
    
    // check exit condition
    if(!time_to_exit){
        uint32_t err_code = app_sched_event_put(&bdata, sizeof(bdata), function5);
		APP_ERROR_CHECK(err_code);
    }
}

Parents
  • Hello,

    Is it like CASE1 or CASE2? 

    The app_scheduler is First-In-First-Out (FIFO), so this is why you are seeing the behavior you describe as CASE 2. 

    I thought the app_scheduler just processes one queued function at a time, each time the main loop is run through

    This might be where the misunderstanding lies. The app_sched_execute function processes all events that have been scheduled since the last time it was called. So, if you at the end of function5 keeps scheduling function5, you will see just as many being executed once the app_sched_execute is ran again.

    (I am not sure about this, because it hard to set a print statement in function2 since it runs so often when the main loop runs, so it fills up all the logs, and i don't want to set a breakpoint for the same reason)

    I do not understand why you would not like to set a breakpoint in your code. It is a powerful debugging tool.
    If you are having issues with the amount of logs printed, you could change the logging severity level, if you are using the nrf logger module. This way, you can decide just how verbose the logging will be.

    If it is like CASE2, this is a bit of a problem, because sometimes I need function5 to run for quite some time and don't want to block function2. How can I make it run more like CASE1? 
    (e.g., my intent is to run through function1, function2, function3, function4, then function5, and if there are more function5 to process, it will go again through the sequence 1, 2, 3, 4, 5. (I will call this "CASE 1")

    I am not sure I have understood your entire intent behind this, but if I have understood your intentions correctly, you want to keep running the series of functions 1 through 5, until the boolean variable time_to_exit is set, possibly interrupted by a function call to function2? Cant you just loop over the functions with the time_to_exit as the loop exit condition, and have function2 queue function5 for execution once the loop is broken?

    Should I have misunderstood your desired functionality and issue, please clarify further.

    Best regards,
    Karl

  • Hi Karl,

    Thanks for the speedy reply. 

    Re: breakpoint - it just wouldn't be helpful here, because function5 is initially queued from an interrupt, so I would hit the breakpoint in function2 a million times before I even get something from function5. And the act of setting a breakpoint would actually make it impossible to get function5 because function5 is queued from a softdevice interrupt after connecting the device to a central (phone), and the breakpoint would mean that a connection couldn't be established, if a long pause happens every time through the main loop. Normally, I set breakpoints to debug all the time. It just isn't viable here. 

    I was able to set a flag and a conditional print statement inside function2 to tell that it is indeed not being called at all (the scenario in CASE 2. Which is the same as what you are saying.

    I need to call function5 from the app scheduler because of the result of this ticket -- basically, I need to have all my SPI transfers handled in a sequential way, or else I will end up in an infinite loop inside the SPI driver, due to a resource conflict. function5 is one of my SPI transfer calls. function2's function is to process data received on the UART, which is completely unrelated to function5. So I don't want to depend on funtion2 for calling function5, because it is theoretically possible that function2 wouldn't need to run (eg if there is nothing in q1). But in the case there IS something in q1 while function5 needs to run, I would like the things in q1 to be dequeued in a timely manner, instead of waiting for all of the function5 to run. 

    Does this make my situation clearer? Thanks so much for your help. 

Reply
  • Hi Karl,

    Thanks for the speedy reply. 

    Re: breakpoint - it just wouldn't be helpful here, because function5 is initially queued from an interrupt, so I would hit the breakpoint in function2 a million times before I even get something from function5. And the act of setting a breakpoint would actually make it impossible to get function5 because function5 is queued from a softdevice interrupt after connecting the device to a central (phone), and the breakpoint would mean that a connection couldn't be established, if a long pause happens every time through the main loop. Normally, I set breakpoints to debug all the time. It just isn't viable here. 

    I was able to set a flag and a conditional print statement inside function2 to tell that it is indeed not being called at all (the scenario in CASE 2. Which is the same as what you are saying.

    I need to call function5 from the app scheduler because of the result of this ticket -- basically, I need to have all my SPI transfers handled in a sequential way, or else I will end up in an infinite loop inside the SPI driver, due to a resource conflict. function5 is one of my SPI transfer calls. function2's function is to process data received on the UART, which is completely unrelated to function5. So I don't want to depend on funtion2 for calling function5, because it is theoretically possible that function2 wouldn't need to run (eg if there is nothing in q1). But in the case there IS something in q1 while function5 needs to run, I would like the things in q1 to be dequeued in a timely manner, instead of waiting for all of the function5 to run. 

    Does this make my situation clearer? Thanks so much for your help. 

Children
  • nordev said:
    Thanks for the speedy reply. 

    No problem at all, I am happy to help!

    nordev said:
    setting a breakpoint would actually make it impossible to get function5 because function5 is queued from a softdevice interrupt after connecting the device to a central (phone),

    Thank you for clarifying - from your initial ticket I was not sure whether SoftDevice usage was the reason you could not set breakpoints. I concur that this makes debugging with breakpoints infeasible.

    nordev said:
    Does this make my situation clearer?

    Yes, this, in combination with the other ticket makes things clearer, thank you.
    To get the full picture, I will discuss this with Einar tomorrow to hear his opinion on a possible solve for this.

    Best regards,
    Karl

  • Hello again Nordev,

    Having read through your ticket with Einar again, I am left with the impression that you need to continuously read a single byte received over SPI over and over again, with the only possible byte values signaling "OK" or "not OK". Is this correct? As in, there is only ever the status of the other device being sent, and never actually any other data?
    If so, I am not convinced that SPI is the best serial protocol choice for this task.

    nordev said:
    I would like the things in q1 to be dequeued in a timely manner, instead of waiting for all of the function5 to run. 

    If my understanding is correct, your function5 will potentially have to run continuously for a very long time - is this correct?
    If so, you will need to handle the UART part as an interrupt, rather than putting it into the queue. This way you can ensure that the UART data is handled 'in a timely matter'.
    However, if you would like the UART data to be handled periodically, you could use the UARTE (UART with easyDMA) peripheral to have the UART receive transfers without requiring CPU cycles to receive them, and then have a timer trigger every so often to check whether there is something that needs processing in the UARTE RX buffer. How does this sound?

    For orders sake I will have to mention that I feel like a part of the design rationale is missing from your explanation, and I do not feel confident that I have fully understood the design choices and requirements of your application.

    Best regards,
    Karl

  • I am left with the impression that you need to continuously read a single byte received over SPI over and over again, with the only possible byte values signaling "OK" or "not OK". Is this correct?

    My SPI transactions are as follows: 

    * write data to external flash memory

    * read data from external flash memory

    * poll external flash memory to find out whether it is busy or whether we are free to read/write

    * function5 = a combination of these: poll first --> if free, read out data --> process data --> write the data back to flash

    My UART transactions:

    * each time a byte is received, I process it in an interrupt

    * but each byte is part of a longer message, and there are different kinds of messages that could be sent

    * these messages contain data, and sometimes they also need a response. if a response is needed, it must be sent out promptly (within a couple hundred ms). this part is all handled in the interrupt. 

    * However, the processing of the actual data isn't needed as promptly (within 1 second, vs. ms), and if everything were processed in an interrupt, maybe we would miss the next byte that arrives. So after the response (if needed) is sent, I queue the full message with all the data to be processed in the main loop. 

    function1 = processing these messages for their data

    function2 = sending the data to the phone

    The first function5 is queued from an interrupt when the central connects. Function5 then checks to see whether there is more data that needs to be read / processed / rewritten and if so, queues more operations to do so.

    I suppose one way I could go about this is instead of queuing function5 with the app_scheduler, I could put function5 in the main loop directly, and all the times it is not running, I can set a flag that tells function5 whether to run or not (which then gets updated directly in function5 iteslf). The only downside here is it seems less elegant to have a function that gets called every time through the main loop, but only runs in the beginning upon a new connection, which I expect to be relatively infrequent (e.g. if the typical use case is that a person leaves their phone connected to the device). There is no way to make the app_scheduler execute any other way, is there? Not even in a newer SDK? 

  • Hello again,

    Thank you for your patience, and the more in-depth explanation of your function2 and function5 - this makes the issue a lot easier to understand and advice on.

    nordev said:
    * However, the processing of the actual data isn't needed as promptly (within 1 second, vs. ms), and if everything were processed in an interrupt, maybe we would miss the next byte that arrives.

    Are you here using the UART or the UARTE driver?
    With easyDMA (UARTE), you would not require CPU interaction to receive new data, so long as there is still room in the buffer. It sounds to me like this could alleviate this issue all together. This way, you only have to check your UARTE buffers every so often, to make sure they are not overflowing.
    Furthermore, if you have a couple of hundred ms as your processing window, that's great - that's an ocean of time to process and respond to a single byte.

    nordev said:
    I could put function5 in the main loop directly, and all the times it is not running, I can set a flag that tells function5 whether to run or not (which then gets updated directly in function5 iteslf). The only downside here is it seems less elegant to have a function that gets called every time through the main loop, but only runs in the beginning upon a new connection, which I expect to be relatively infrequent

    I think this could very well be a good way of handling it. You could implement it like the idle_state_handler is implement it in most of the examples. In the default idle_state_handler the very last thing the device does before going to SYSTEM ON sleep (CPU idle) is to check whether there are logs to process. You can change this to instead check whether there are function5 calls need doing (checking a flag, for instance), and if not, go straight to SYSTEM ON sleep.
    This way, function5 will be executed until there is nothing more to process(with interruptions every time function2 or the SoftDevice needs the CPU for brief periods), and after that it will not be called again until the connection is reset (and the function5 flag is reset).
    How does this sound?

    Best regards,
    Karl

  • that's an ocean of time to process and respond to a single byte

    Not a single byte, but each string of bytes that comprises a message, which could be around 20 or so bytes. Still, I think your point stands; I have not had issues with processing the UART input in time. 

    This way, function5 will be executed until there is nothing more to process(with interruptions every time function2 or the SoftDevice needs the CPU for brief periods), and after that it will not be called again until the connection is reset (and the function5 flag is reset).

    This sounds like a great plan. Can you share a reference to the documentation? I can't seem to find it: 

    While I have your attention, since you have been so wonderfully helpful and responsive, can you help me with this other ticket? It has been open for 2 months now, and I have been having a hard time getting a response even after many follow-ups -- it has been usually 2 or 3 weeks in between replies from the rep. I keep having to send reminders or he will forget about my ticket, and sometimes I had to repeat the same info because it was so long in between responses. 

Related