Behavior of xStreamBufferSend / xStreamBufferReceive

hubpav wrote on Saturday, May 19, 2018:

Hi,

I am not 100% sure how xStreamBufferSend / xStreamBufferReceive works. Is it safe to assume that xDataLengthBytes are guaranteed to be sent / received when portMAX_DELAY is specified when the call is finished?

If that is the case, then the following wrapper is not necessary. Is that corret?

size_t os_stream_write(os_stream_t *stream, const void *buffer, size_t length)
{
    size_t bytes_written = 0;

    while (true)
    {
        size_t chunk_length = xStreamBufferSend(stream->_handle, buffer, length, portMAX_DELAY);

        bytes_written += chunk_length;

        length -= chunk_length;

        if (length == 0)
        {
            break;
        }

        buffer = (uint8_t *) buffer + chunk_length;
    }

    return bytes_written;
}

Thank you. Cheers, Pavel

rtel wrote on Sunday, May 20, 2018:

I am not 100% sure how xStreamBufferSend / xStreamBufferReceive
works. Is it safe to assume that xDataLengthBytes are guaranteed to be
sent / received when portMAX_DELAY is specified when the call is finished?

You have all the source code, and xStreamBufferSend() is a small
function to read. Lines 520 and 549 (at the time of writing) show a
loop with the task waiting under the required amount of space is
available:
https://sourceforge.net/p/freertos/code/HEAD/tree/trunk/FreeRTOS/Source/stream_buffer.c

hubpav wrote on Sunday, May 20, 2018:

Hi Richard, thank you!

Apparently xStreamBufferSend handles that for you inside the do { } while loop, so no wrapper is necessary there.

With xStreamBufferReceive, the situation is different though… the call can return without having the desired length available under normal circumstances (as task can get notified from various reasons) - even if portMAX_DELAY has been specified.

This is the wrapper (with timeout support) I have been using for reference (feedback is welcome):

size_t os_stream_timed_read(os_stream_t *stream, void *buffer, size_t length, uint32_t milliseconds)
{
    if (milliseconds == 0)
    {
        return xStreamBufferReceive(stream->_handle, buffer, length, 0);
    }

    size_t bytes_read = 0;

    uint64_t now = os_time_get();

    uint64_t timeout = now + milliseconds;

    while (now < timeout)
    {
        size_t chunk_length = xStreamBufferReceive(stream->_handle, buffer, length, pdMS_TO_TICKS(timeout - now));

        bytes_read += chunk_length;

        length -= chunk_length;

        if (length == 0)
        {
            break;
        }

        buffer = (uint8_t *) buffer + chunk_length;

        now = os_time_get();
    }

    return bytes_read;
}

Thank you. Cheers, Pavel

rtel wrote on Sunday, May 20, 2018:

Correct, in xStreamBufferReceive() the second parameter is the size of
the buffer into which data is to be copied, not the number of bytes to
receive, so xSteamBufferRecevie() will return as soon as it has ‘some’
data, the length of the data it receives in one go being capped to the
buffer length. See the xBufferLengthBytes description here:
https://www.freertos.org/xStreamBufferReceive.html

Ref your implementation of os_stream_timed_read() - from a brief look it
seems you have have an issue if the tick count overflows, the likelihood
of which you have avoided by using a uint64_t - access to which will
probably not be atomic. It may be easier to use code similar to that
shown on this page: https://www.freertos.org/xTaskCheckForTimeOut.html
which uses FreeRTOS API calls to take possible overflows into account
for you.

hubpav wrote on Sunday, May 20, 2018:

Thank you, Richard. I was not aware of the xTaskCheckForTimeout function and I will look into it.

You are correct - I use uint64_t for my application ticks as it will “never” overflow… :slight_smile:

However, I have been trying to make the access to this variable safe. Here is the snippet including the vApplicationTickHook callback implementation:

static uint64_t _os_time = 0;

uint64_t os_time_get(void)
{
    taskENTER_CRITICAL();

    uint64_t ret = _os_time;

    taskEXIT_CRITICAL();

    return ret;
}

void vApplicationTickHook(void)
{
    static TickType_t xTickLast = 0;

    TickType_t xTickNow = xTaskGetTickCountFromISR();

    _os_time += xTickNow - xTickLast;

    xTickLast = xTickNow;
}

I hope this is sufficient for safe tick counter operation.

Thanks! Regards, Pavel