Connection Event Callback API (follow-on)

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
legic_1490776
Level 5
Level 5
25 likes received 10 likes received First like received

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 Solution
Anonymous
Not applicable

Hello ldgirod

These are all serialized

Thanks

JT

View solution in original post

0 Likes
9 Replies
Anonymous
Not applicable

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.

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>

0 Likes
Anonymous
Not applicable

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.

0 Likes

I understand now, sorry I was being dense.

So, have you observed unexpected concurrency?

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

0 Likes
MichaelF_56
Moderator
Moderator
Moderator
250 sign-ins 25 comments on blog 10 comments on blog

ldgirod odbol santol

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.

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);

  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 <

0 Likes

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.

0 Likes

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.

Anonymous
Not applicable

Hello ldgirod

These are all serialized

Thanks

JT

0 Likes