9 Replies Latest reply on Jun 17, 2015 4:35 PM by jota_1939431 Branched from an earlier discussion.

    Connection Event Callback API (follow-on)

    legic_1490776

      This is a follow on discussion to the one started here in this thread: Connection Event Callback API (follow-on)

       

      Based on the behavior of my application, it appears that the connection event callback handler can be called asynchronously to the other callbacks in the system.

       

      That is, my code might be executing in a write handler or a timer, and while doing an IO operation, my connection event handler might get called.

       

      This was shocking to me because I have designed my application on the assumption that all application handlers are synchronous, that is, they are all serialized into a single "application" thread.

       

      Is this true in general, except for connection events?  Are any other handlers asynchronous?  Is there any support for a mutex to properly deal with asynchronous access?

       

      Here is why I believe this to be happening:  (Note this is compiled with SDK 2.1).

       

      I have a buffer which is modified from within my connection event handler, as well as elsewhere in the program.

      I have some code that writes this buffer to NVRAM, after first computing a checksum.

      I found that the buffer changed while my code was executing the call to write the buffer to NVRAM, resulting in the checksum being wrong.  The part of the buffer that changed is only written from within the connection event handler.

       

      Is this really possible or should I be looking for another culprit?

        • 1. Re: Connection Event Callback API (follow-on)
          userc_3543

          I am also wondering about this. I use this great circular buffer library which is thread-safe:

           

          https://github.com/dhylands/TimerUART/blob/master/CBUF.h

           

          However sometimes I wonder if other things are getting messed up, like if I'm in the middle of writing to a characteristic when the callback takes over and overwrites it. Haven't noticed anything messed up yet, but it's hard to tell.

          • 2. Re: Connection Event Callback API (follow-on)
            MichaelF_56

            legic_1490776 userc_3543 ShawnA_01

             

            I spoke with the developers and they confirmed that the connection event callback is serialized to the application thread; they suspect the problem is elsewhere.

            • 3. Re: Connection Event Callback API (follow-on)
              legic_1490776

              Are there synchronization primitives supported in the broadcom API?

               

              Otherwise how can something be made thread safe?

               

              My understanding was that there was only one thread, but now I am not sure.

              On Jun 4, 2015 5:46 PM, "odbol" <communities-list@community.broadcom.com>

              • 4. Re: Connection Event Callback API (follow-on)
                userc_3543

                Well, in the case of that circular buffer I posted, as long as you declare the buffer volatile, and there's only one writer, it should work fine.

                • 5. Re: Connection Event Callback API (follow-on)
                  legic_1490776

                  I'm sorry, but I am pretty certain the developers are incorrect.

                  I am running SDK 2.1.1, and I will quote the relevant portions of the code

                  and show the output.

                   

                  // this is the data I am writing to NVRAM - it is a 192 byte struct

                  extern volatile VT_State_t vehicle_tag_nv_state;

                   

                  // this is a psuedo-mutex.

                  extern volatile INT8 skipconnint;

                  #define CONNINT_LOCKED() (_skip_connint > 0)

                  #define CONNINT_LOCK() skipconnint++

                  #define CONNINT_UNLOCK() do { if (CONNINT_LOCKED()) skipconnint--; }

                  while(0)

                   

                  // This function is set as the handler for connection interval events.

                  void vehicle_tag_conn_event(void *ctx, UINT32 arg)

                  {

                    if (!CONNINT_LOCKED()) {

                      vehicle_tag_nv_state.count_conn_intervals++;

                      tm_conn_event_cb();

                      //ble_trace0("ce");

                    }

                    else {

                      // if 'skip_connint' is set, it will print 'whoops'

                      ble_trace0("Whoops");*

                    }

                  }

                   

                  .....

                      // ... in the connection up callback we register for connection events

                      // Set 2 slots after TX, default = 1000mS interval for the current connection.

                      blecm_connectionEventNotifiationEnable(vehicle_tag_conn_event, 0, -8,

                           500000/625, emconinfo_getConnHandle());

                  ....

                   

                  So, that handler code is ready to run when the event fires.

                  Now, from a timer callback, eventually the boot_update_nvram() function

                  is called.

                   

                  // i've included this for completeness, it isn't really important

                  UINT8 boot_cksum_status()

                  {

                    int i;

                    UINT8 cksum = 0;

                    for (i=0; i<sizeof(vehicle_tag_nv_state); i++)

                      cksum ^= ((UINT8 *)&vehicle_tag_nv_state)[i];

                    return cksum;

                  }

                   

                  // this function gets called from within a timer handler function.

                  void boot_update_nvram()

                  {

                    vehicle_tag_nv_state.nvram_writes++;

                   

                    // set skip_connint

                    CONNINT_LOCK();   

                    vehicle_tag_nv_state.status_checksum = 0;

                   

                    // print 'before'

                    ble_trace0("Before");


                    // print hexdump

                    ble_tracen(&vehicle_tag_nv_state, sizeof(vehicle_tag_nv_state)); 

                    vehicle_tag_nv_state.status_checksum = boot_cksum_status();

                   

                    // do the NVRAM write

                    int status = bleprofile_WriteNVRAM(NVRAM_BOOT_STATE, sizeof(VT_State_t),

                        (UINT8 *)&vehicle_tag_nv_state);    


                    // print 'NVW..'

                    ble_trace2("NVW S=%d,C=%d", status, boot_cksum_status());  


                    // print hexdump

                    ble_tracen(&vehicle_tag_nv_state, sizeof(vehicle_tag_nv_state)); 


                    // clear skip_connint

                    CONNINT_UNLOCK();  

                  }

                   

                  It should be impossible for the connint handler to run while that sequence

                  is being printed.

                   

                  BUT, here is the output:

                   

                  Before

                  010001000c004cb82c00004034010000

                  00000000000000000000000004000000

                  1c0000001d0000000b0000001d000000

                  00000000250000000100000009fc6700

                  811d0000010000000000000000000000

                  0000020002001100985Whoops

                  110fa6aff7055

                  02000000030000008601000001000000

                  00000000000000002c08000000000000

                  010000000000000000000000460b0000

                  be00000077000000c400000000000000

                  3f040000000000003d3d00007d000000

                  58018481ac5401000000000000000000

                  NVW S=192,C=0

                  010001000c004cb82c00004034010000

                  00000000000000000000000004000000

                  1c0000001d0000000b0000001d000000

                  00000000250000000100000009fc6700

                  811d0000010000000000000000000000

                  0000020002001100985110fa6aff7055

                  02000000030000008601000001000000

                  00000000000000002c08000000000000

                  010000000000000000000000460b0000

                  be0000007700001ac400000000000000

                  3f040000000000003d3d00007d000000

                  58018481ac5401000000000000000000

                   

                   

                  so..... how does that happen??????

                   

                   

                   

                  On Thu, Jun 4, 2015 at 7:05 PM, mwf_mmfae <

                  • 6. Re: Connection Event Callback API (follow-on)
                    legic_1490776

                    I understand now, sorry I was being dense.

                    So, have you observed unexpected concurrency?

                     

                    On Thu, Jun 4, 2015 at 8:35 PM, odbol <

                    • 7. Re: Connection Event Callback API (follow-on)
                      ArvindS_76

                      The callback registered with blecm_connectionEventNotifiationEnableis *not* serialized to the app thread. You should use bleappevt_serialize() from bleppevent.h. See running_speed_cadence sample app for usage.

                      • 8. Re: Connection Event Callback API (follow-on)
                        legic_1490776

                        OK, Thanks for clarifying that.

                         

                        Can you please confirm whether or not any of the following other handlers are called asynchronously?

                         

                            bleprofile_regAppEvtHandler(BLECM_APP_EVT_LINK_UP, tag_connection_up);

                            bleprofile_regAppEvtHandler(BLECM_APP_EVT_LINK_DOWN, tag_connection_down);

                            bleprofile_regAppEvtHandler(BLECM_APP_EVT_ADV_TIMEOUT, tag_advertisement_stopped);

                            bleprofile_regAppEvtHandler(BLECM_APP_EVT_ENTERING_HIDOFF, vt_enter_hidoff);

                            bleprofile_regAppEvtHandler(BLECM_APP_EVT_ABORTING_HIDOFF, vt_abort_hidoff);

                            legattdb_regWriteHandleCb((LEGATTDB_WRITE_CB)tag_write_handler);

                            gpio_registerForInterrupt(mask, b_cb, NULL);

                            bleprofile_regTimerCb(tm_fine_timer_cb, tm_coarse_timer_cb);

                            bt_clock_based_periodic_timer_Enable(tm_bt_timer_cb, NULL, 10);



                        ***In particular I am wondering about bt_clock_based_periodic_timer_Enable and gpio_registerForInterrupt ***


                        A long time ago we noticed problems with our app when we had gpio interrupts occurring while the system was connected - the connection would lock up after a short time.  Since we were unable to resolve this we worked around this by disabling the interrupts while the connection was active.

                        • 9. Re: Connection Event Callback API (follow-on)
                          jota_1939431

                          Hello legic_1490776

                           

                          These are all serialized

                           

                          Thanks

                          JT