Recursive Calls to app_sched_execute()

Hi,

I am coming across an issue with app_scheduler and FDS.

While waiting for an event in FDS to complete (write, read, init, gc, etc) I go into a while loop calling app_sched_execute() until the event handler is invoked and I can continue. However, I am running into an issue where I am calling an fds Write from a scheduler event. The repeated call to app_sched_execute() means the same command is executed over and over again until the FDS runs out of Queue Space and I hit the Error Handler.

Is there a way to safely wait for the FDS to complete a task without causing this recursion?

Thanks

  • Hi,

    It is not possible to call app_sched_execute() from a scheduled event, as the event is first popped from the queue first after the event is done, as the event data pointer needs to be kept throughout the event.

    I'm not sure I fully understand how you use the scheduler to wait for completion of FDS operations, can you give a code example showing how this works?

    Do you have to wait for the completion of the operation by calling app_sched_execute(), or could you wait for a flag from the FDS event handler instead?

    Best regards,
    Jørgen

  • Hi Jørgen,

    Sorry for my slow reply.

    I am waiting for a flag from the FDS event handler. I have put partial code below showing my initialisation, event handler and wait function. If I replace line 70 with anything other than app_sched_execute, the event handler is not invoked and I am stuck in the while loop forever. I am looking for a way to make FDS blocking and this was the only way I could get it to work with the scheduler. However, recursive calls are now causing havoc with this.

    Thanks,

    #define MAX_EVENT_SIZE 128
    #define QUEUE_SIZE	   180
    
    static uint32_t _eventBuffer[CEIL_DIV( APP_SCHED_BUF_SIZE( MAX_EVENT_SIZE, QUEUE_SIZE ), sizeof( uint32_t ) )] __ALIGN( 4 );
    
    #define TEST_FILE ( 0xBFFE )
    #define TEST_KEY  ( 0x0001 )
    
    static bool volatile _fdsBusy = false;
    static bool volatile m_fds_initialised = false;
    
    static char const* fds_evt_str[] = {
    	"FDS_EVT_INIT", "FDS_EVT_WRITE", "FDS_EVT_UPDATE", "FDS_EVT_DEL_RECORD", "FDS_EVT_DEL_FILE", "FDS_EVT_GC",
    };
    
    static void fds_evt_handler( fds_evt_t const* p_evt );
    static void Storage_Wait( void );
    const char* fds_err_str( ret_code_t ret );
    
    const char* fds_err_str( ret_code_t ret )
    {
    	// Array to map FDS return values to strings.
    	static char const* err_str[] = {
    		"FDS_ERR_OPERATION_TIMEOUT", "FDS_ERR_NOT_INITIALIZED",
    		"FDS_ERR_UNALIGNED_ADDR",	 "FDS_ERR_INVALID_ARG",
    		"FDS_ERR_NULL_ARG",			 "FDS_ERR_NO_OPEN_RECORDS",
    		"FDS_ERR_NO_SPACE_IN_FLASH", "FDS_ERR_NO_SPACE_IN_QUEUES",
    		"FDS_ERR_RECORD_TOO_LARGE",	 "FDS_ERR_NOT_FOUND",
    		"FDS_ERR_NO_PAGES",			 "FDS_ERR_USER_LIMIT_REACHED",
    		"FDS_ERR_CRC_CHECK_FAILED",	 "FDS_ERR_BUSY",
    		"FDS_ERR_INTERNAL",
    	};
    
    	return err_str[ret - NRF_ERROR_FDS_ERR_BASE];
    }
    
    static void fds_evt_handler( fds_evt_t const* p_evt )
    {
    	if( p_evt->result == NRF_SUCCESS )
    	{
    		Log_Info( "Event: %s received (NRF_SUCCESS)", fds_evt_str[p_evt->id] );
    	}
    	else
    	{
    		Log_Info( "Event: %s received (%s)", fds_evt_str[p_evt->id], fds_err_str( p_evt->result ) );
    	}
    
    	switch( p_evt->id )
    	{
    		case FDS_EVT_INIT:
    			if( p_evt->result == NRF_SUCCESS )
    			{
    				m_fds_initialised = true;
    			}
    			break;
    		default:
    			break;
    	}
    
    	_fdsBusy = false;
    
    	while( nrf_log_frontend_dequeue( ) )
    		;
    }
    
    static void Storage_Wait( void )
    {
    	while( _fdsBusy )
    	{
    		sd_app_sched_execute( );
    	}
    }
    
    int main( void )
    {
    	// Initialise clocks and power
    	ret_code_t rc = nrf_drv_clock_init( );
    	APP_ERROR_CHECK( rc );
    
    	if( !nrf_drv_power_init_check( ) )
    	{
    		nrf_drv_power_init( NULL );
    	}
    
    	// Initialise Scheduler
    	rc = nrf_pwr_mgmt_init( );
    	APP_ERROR_CHECK( rc );
    
    	rc = app_sched_init( MAX_EVENT_SIZE, QUEUE_SIZE, ( void* )_eventBuffer );
    	APP_ERROR_CHECK( rc );
    
    	uint32_t   ram_start;
    
    	// Enable the SoftDevice
    	rc = nrf_sdh_enable_request( );
    	APP_ERROR_CHECK( rc );
    
    	rc = nrf_sdh_ble_default_cfg_set( 1, &ram_start );
    	APP_ERROR_CHECK( rc );
    
    	rc = nrf_sdh_ble_enable( &ram_start );
    	APP_ERROR_CHECK( rc );
    
    	// Initialise Logging
    	rc = NRF_LOG_INIT( NULL );
    	APP_ERROR_CHECK( rc );
    
    	// Initialise the default backends as enabled in the configuration
    	NRF_LOG_DEFAULT_BACKENDS_INIT( );
    
    	NRF_LOG_INFO( "Application Started" );
    
    	// Initialise Storage
    
    	rc = fds_register( fds_evt_handler );
    	APP_ERROR_CHECK( rc );
    
    	rc = fds_init( );
    	APP_ERROR_CHECK( rc );
    
    	while( !m_fds_initialised )
    	{
    		sd_app_evt_wait();
    	}
    
    	Log_Info( "FDS Initialised" );
    
    	fds_stat_t stat = { 0 };
    
    	rc = fds_stat( &stat );
    	APP_ERROR_CHECK( rc );
    
    	NRF_LOG_INFO( "Found %d valid records.", stat.valid_records );
    	NRF_LOG_INFO( "Found %d dirty records (ready to be garbage collected).", stat.dirty_records );
    
    	if( stat.dirty_records > 0 )
    	{
    		_fdsBusy = true;
    
    		rc = fds_gc( );
    		APP_ERROR_CHECK( rc );
    
    		Storage_Wait( );
    	}
    
    	fds_record_desc_t desc = { 0 };
    	fds_find_token_t  tok  = { 0 };
    
    
    	// Test Write
    
    	static uint32_t const m_deadbeef = 0xDEADBEEF;
    
    	fds_record_t	  record;
    	fds_record_desc_t record_desc;
    
    	record.file_id			 = TEST_FILE;
    	record.key				 = TEST_KEY;
    	record.data.p_data		 = &m_deadbeef;
    	record.data.length_words = 1;
    
    	_fdsBusy = true;
    	rc = fds_record_write( &record_desc, &record );
    	APP_ERROR_CHECK( rc );
    	
    	Storage_Wait( );
    
    	while(1)
    	{
    		app_sched_execute( );
    
    		if( nrf_log_frontend_dequeue( ) == false )
    		{
    			nrf_pwr_mgmt_run( );
    		}
    	}
    }

  • Hi,

    It you have The softdevice handler library configured to use NRF_SDH_DISPATCH_MODEL_APPSH, you are correct that you need to call app_sched_execute() to process the events from the Softdevice, which in turn will trigger the Fstorage/FDS event handlers.

    Is sd_app_sched_execute( ); on line 70 a typo? I could not find this function in the SDK.

    I'm also not sure I understand where the recursive call issue is coming from. In the code you posted, I can't see app_sched_execute() being called from any scheduler event handlers?

    Best regards,
    Jørgen

  • Hi Jørgen,

    My NRF_SDH_DISPATCH_MODEL is configured as _APPSH.

    Line 70 is a typo, it should read app_sched_execute();, There was a mistake made when creating the code to post on here.

    The code above is an extract of how I am setting up my FDS. The recursive issue comes when data is written to a BLE characteristic. This causes an event to be posted onto the scheduler and within this event there is a call to write to storage. Within this write function (which I want to be blocking) there is a call to Storage_Wait() which is where the recursive issue happens.

    Thanks

  • Hi,

    It is not possible to make the FDS function blocking. The flash writes are ultimately always executed by the Softdevice when the Softdevice is enabled, and you need to process the Softdevice events in the application when using the applications scheduler dispatch model. The FDS libraries does not implement any support for the application scheduler, so it would not be able to process the events from the Softdevice.

    You will have to exit the scheduler event processing before doing the blocking wait for the FDS write operation to complete.

    Best regards,
    Jørgen

Related