bt_gatt_write_without_response_cb / bt_gatt_notify: how to prevent blocking

Hello,

calls to the above functions block the caller if internal queues become full.  Because my application is running in a big loop, I need those functions without blocking (a specific return code would suffice).

Is there any way to prevent those functions from blocking?  Or is there another non-blocking API doing similar things?

Thanks & regards

Hardy

PS: my current workaround is a small extra thread for packet transmission which is allowed to block (extra buffer, extra stack, extra copy operation).  But this seems to be like using a sledge-hammer to crack a nut

Parents
  • I have to answer myself... ;-)

    My current trick is to delegate the actual call to bt_gatt_notify() into the system workqueue.  And how is this done?:

    where the actual call is done:

     tx_work.buf = ptrToPacketData;
     tx_work.len = numberOfBytesToWrite;
     k_work_submit_to_queue( &k_sys_work_q, &tx_work.work);
     if ( !tx_work.result)
     {
     m_hadTxPacketFifoOverflow = true;
     }
    
    The corresponding working function does roughly
    
    static void tx_work_fn(struct k_work *item)
    /**
     * Send packet to stack.
     *
     * \return
     * \a true if packet successfully put into TX queue
     */
    {
     int err;
    
     if (m_hadTxPacketFifoOverflow)
     {
     return false;
     }
     err = bt_gatt_notify(m_conn, m_attrOfSlaveSendsChar, buf, len);
     if (err != 0)
     {
     if (err == -ENOTCONN)
     {
     LOG_WRN("WAL: bt_gatt_notify_cb() not connected -> discarding");
     return true;
     }
     else if (err == -ENOMEM)
     {
     LOG_WRN("WAL: bt_gatt_notify_cb() not blocking");
     return false;
     }
     else
     {
     // we are dead
     LOG_ERR("WAL: bt_gatt_notify_cb() failed(err %d), len:%d", err, len);
     return true;
     }
     }
     return true;

    because the main thread (at least in my case) has lower priority then the system workqueue thread the first thing is more or less a synchronous call which does not block and return true/false depending on successful packet queuing.

    Simple but awkward.

    H.

    PS: data structure

    struct s_tx_work {
     k_work work;
     uint8_t *buf;
     uint16_t len;
     bool result;
    };
    
    plus
    
     k_work_init(&tx_work.work, tx_work_fn);

    PPS: don't expect the above snippets to compile. They are just there to give you an idea.

    PPPS: e.g. parameter/result have to be assigned accordingly

    PPPPS: don't make me responsible for "formatting"

Reply
  • I have to answer myself... ;-)

    My current trick is to delegate the actual call to bt_gatt_notify() into the system workqueue.  And how is this done?:

    where the actual call is done:

     tx_work.buf = ptrToPacketData;
     tx_work.len = numberOfBytesToWrite;
     k_work_submit_to_queue( &k_sys_work_q, &tx_work.work);
     if ( !tx_work.result)
     {
     m_hadTxPacketFifoOverflow = true;
     }
    
    The corresponding working function does roughly
    
    static void tx_work_fn(struct k_work *item)
    /**
     * Send packet to stack.
     *
     * \return
     * \a true if packet successfully put into TX queue
     */
    {
     int err;
    
     if (m_hadTxPacketFifoOverflow)
     {
     return false;
     }
     err = bt_gatt_notify(m_conn, m_attrOfSlaveSendsChar, buf, len);
     if (err != 0)
     {
     if (err == -ENOTCONN)
     {
     LOG_WRN("WAL: bt_gatt_notify_cb() not connected -> discarding");
     return true;
     }
     else if (err == -ENOMEM)
     {
     LOG_WRN("WAL: bt_gatt_notify_cb() not blocking");
     return false;
     }
     else
     {
     // we are dead
     LOG_ERR("WAL: bt_gatt_notify_cb() failed(err %d), len:%d", err, len);
     return true;
     }
     }
     return true;

    because the main thread (at least in my case) has lower priority then the system workqueue thread the first thing is more or less a synchronous call which does not block and return true/false depending on successful packet queuing.

    Simple but awkward.

    H.

    PS: data structure

    struct s_tx_work {
     k_work work;
     uint8_t *buf;
     uint16_t len;
     bool result;
    };
    
    plus
    
     k_work_init(&tx_work.work, tx_work_fn);

    PPS: don't expect the above snippets to compile. They are just there to give you an idea.

    PPPS: e.g. parameter/result have to be assigned accordingly

    PPPPS: don't make me responsible for "formatting"

Children
No Data
Related