- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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?
Solved! Go to Solution.
- Labels:
-
SDK 2.X
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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>
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I understand now, sorry I was being dense.
So, have you observed unexpected concurrency?
On Thu, Jun 4, 2015 at 8:35 PM, odbol <
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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 <
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content