【FastDDS源码剖析】CacheChange_t

17 篇文章 18 订阅 ¥99.90 ¥299.90
本文深入探讨了Fast DDS中的CacheChange_t结构,它作为数据管理的关键组件,用于表示数据交换单元并存储元数据。文章详细介绍了CacheChange_t的内部实现,包括copy、copy_not_memcpy、setFragmentSize等核心功能,揭示了其在数据分片传输和重组中的作用,以及如何确保可靠通信和数据一致性。
摘要由CSDN通过智能技术生成

前言

CacheChange_t在Fast DDS中扮演着重要的角色,并且对于实时通信和数据发布-订阅模型的实现具有关键意义。
它是Fast DDS中用于管理数据变化的关键组件之一,用于表示在数据发布者和订阅者之间交换的数据单元,并在DDS中的发布-订阅过程中起到关键的作用。CacheChange_t结构中并没有包含实际的数据,而是包含了数据的指针(参考SerializedPayload_t),以及与数据相关的元数据,如时间戳、序列号等。通过CacheChange_t,Fast DDS能够有效地跟踪和传输数据更新,以保证可靠的通信和数据一致性。
在本文中,我将深入探讨CacheChange_t结构的内部实现细节和关键功能。通过对CacheChange_t的深入分析,读者将能够更好地理解Fast DDS内部的数据管理机制。

CacheChange_t

struct CacheChangeWriterInfo_t
{
    //!Number of DATA / DATA_FRAG submessages sent to the transport (only used in Writers)
    size_t num_sent_submessages = 0;
    //! Used to link with previous node in a list. Used by FlowControllerImpl.
    //! Cannot be cached because there are several comparisons without locking.
    CacheChange_t* volatile previous = nullptr;
    //! Used to link with next node in a list. Used by FlowControllerImpl.
    //! Cannot be cached because there are several comparisons without locking.
    CacheChange_t* volatile next = nullptr;
};

/*!
 * Specific information for a reader.
 */
struct CacheChangeReaderInfo_t
{
    //!Reception TimeStamp (only used in Readers)
    Time_t receptionTimestamp;
    //! Disposed generation of the instance when this entry was added to it
    int32_t disposed_generation_count;
    //! No-writers generation of the instance when this entry was added to it
    int32_t no_writers_generation_count;
};

struct RTPS_DllAPI CacheChange_t
{
    //!Kind of change, default value ALIVE.
    ChangeKind_t kind = ALIVE;
    //!GUID_t of the writer that generated this change.
    GUID_t writerGUID{};
    //!Handle of the data associated with this change.
    InstanceHandle_t instanceHandle{};
    //!SequenceNumber of the change
    SequenceNumber_t sequenceNumber{};
    //!Serialized Payload associated with the change.
    SerializedPayload_t serializedPayload{};
    //!CDR serialization of inlined QoS for this change.
    SerializedPayload_t inline_qos{};
    //!Indicates if the cache has been read (only used in READERS)
    bool isRead = false;
    //!Source TimeStamp
    Time_t sourceTimestamp{};
    union
    {
        CacheChangeReaderInfo_t reader_info;
        CacheChangeWriterInfo_t writer_info;
    };

    WriteParams write_params{};
    bool is_untyped_ = true;
    ...
}

copy

copy数据,如果is_untyped_并且现在data的size不够大会返回false。

    bool copy(
            const CacheChange_t* ch_ptr)
    {
        kind = ch_ptr->kind;
        writerGUID = ch_ptr->writerGUID;
        instanceHandle = ch_ptr->instanceHandle;
        sequenceNumber = ch_ptr->sequenceNumber;
        sourceTimestamp = ch_ptr->sourceTimestamp;
        reader_info.receptionTimestamp = ch_ptr->reader_info.receptionTimestamp;
        write_params = ch_ptr->write_params;
        isRead = ch_ptr->isRead;
        fragment_size_ = ch_ptr->fragment_size_;
        fragment_count_ = ch_ptr->fragment_count_;
        first_missing_fragment_ = ch_ptr->first_missing_fragment_;

        return serializedPayload.copy(&ch_ptr->serializedPayload, !ch_ptr->is_untyped_);
    }

copy_not_memcpy

只copy信息,不copy数据。

void copy_not_memcpy(
            const CacheChange_t* ch_ptr)
    {
        kind = ch_ptr->kind;
        writerGUID = ch_ptr->writerGUID;
        instanceHandle = ch_ptr->instanceHandle;
        sequenceNumber = ch_ptr->sequenceNumber;
        sourceTimestamp = ch_ptr->sourceTimestamp;
        reader_info.receptionTimestamp = ch_ptr->reader_info.receptionTimestamp;
        write_params = ch_ptr->write_params;
        isRead = ch_ptr->isRead;

        // Copy certain values from serializedPayload
        serializedPayload.encapsulation = ch_ptr->serializedPayload.encapsulation;

        // Copy fragment size and calculate fragment count
        setFragmentSize(ch_ptr->fragment_size_, false);
    }

setFragmentSize

收到change的第一个fragment的时候,create_fragment_list应该传true。如果是发送,我们肯定是先把数据整个给到change,然后再发送的,也就是说没有丢任何一个fragment,此时first_missing_fragment_ = fragment_count_。
这里算fragment_count_的时候有个小技巧fragment_count_ = (serializedPayload.length + fragment_size - 1) / fragment_size,就是先加上fragment_size - 1再除以fragment_size,类似std::ceil。

如果create_fragment_list为true,假设fragment size = 8,在用户data的内存上,第一个fragment开始位置的四个字节,存入missing的frament是1,在第二个fragment的开头四个字节会存入missing的frament是2,以此类推。
next missing fragment

void setFragmentSize(
            uint16_t fragment_size,
            bool create_fragment_list = false)
    {
        fragment_size_ = fragment_size;
        fragment_count_ = 0;
        first_missing_fragment_ = 0;

        if (fragment_size > 0)
        {
            // This follows RTPS 8.3.7.3.5
            fragment_count_ = (serializedPayload.length + fragment_size - 1) / fragment_size;

            if (create_fragment_list)
            {
                // Keep index of next fragment on the payload portion at the beginning of each fragment. Last
                // fragment will have fragment_count_ as 'next fragment index'
                size_t offset = 0;
                for (uint32_t i = 1; i <= fragment_count_; i++, offset += fragment_size_)
                {
                    set_next_missing_fragment(i - 1, i);  // index to next fragment in missing list
                }
            }
            else
            {
                // List not created. This means we are going to send this change fragmented, so it is already
                // assembled, and the missing list is empty (i.e. first missing points to fragment count)
                first_missing_fragment_ = fragment_count_;
            }
        }
    }

set_next_missing_fragment

    void set_next_missing_fragment(
            uint32_t fragment_index,
            uint32_t next_fragment_index)
    {
        uint32_t* ptr = next_fragment_pointer(fragment_index);
        *ptr = next_fragment_index;
    }

get_next_missing_fragment

    uint32_t get_next_missing_fragment(
            uint32_t fragment_index)
    {
        uint32_t* ptr = next_fragment_pointer(fragment_index);
        return *ptr;
    }

next_fragment_pointer

最后返回的serializedPayload.data[offset],是一个用来存储用户data的内存地址。这里offset = (offset + 3u) & ~3u;与前面说的std::ceil类似,四字节对齐。

    uint32_t* next_fragment_pointer(
            uint32_t fragment_index)
    {
        size_t offset = fragment_size_;
        offset *= fragment_index;
        offset = (offset + 3u) & ~3u;
        return reinterpret_cast<uint32_t*>(&serializedPayload.data[offset]);
    }

get_missing_fragments

循环获取所有missing的fragment,开始初始化的时候fragment 0的位置存的next missing 是1,fragment 1位置存2,fragment 2位置存3,再收到fragment的时候一定会更新这个next missing,比如第一次收到的是fragment 1,那么fragment 0的位置一定会被更新为2。其实这里就是在用户的data数据中隐藏了一个missing fragment的列表。

    /*!
     * Fills a FragmentNumberSet_t with the list of missing fragments.
     * @param [out] frag_sns FragmentNumberSet_t where result is stored.
     */
    void get_missing_fragments(
            FragmentNumberSet_t& frag_sns)
    {
        // Note: Fragment numbers are 1-based but we keep them 0 based.
        frag_sns.base(first_missing_fragment_ + 1);

        // Traverse list of missing fragments, adding them to frag_sns
        uint32_t current_frag = first_missing_fragment_;
        while (current_frag < fragment_count_)
        {
            frag_sns.add(current_frag + 1);
            current_frag = get_next_missing_fragment(current_frag);
        }
    }

received_fragments

if (initial_fragment <= first_missing_fragment_) 说明新来的fragments之前没有missing的fragment了,只需要将first_missing_fragment_后移即可。否则需要更新隐藏在用户data里的 missing fragment 列表。

    /*!
     * Mark a set of consecutive fragments as received.
     * This will remove a set of consecutive fragments from the missing list.
     * Should be called BEFORE copying the received data into the serialized payload.
     *
     * @param initial_fragment Index (0-based) of first received fragment.
     * @param num_of_fragments Number of received fragments. Should be strictly positive.
     * @return true if the list of missing fragments was modified, false otherwise.
     */
    bool received_fragments(
            uint32_t initial_fragment,
            uint32_t num_of_fragments)
    {
        bool at_least_one_changed = false;

        if ((fragment_size_ > 0) && (initial_fragment < fragment_count_))
        {
            uint32_t last_fragment = initial_fragment + num_of_fragments;
            if (last_fragment > fragment_count_)
            {
                last_fragment = fragment_count_;
            }

            if (initial_fragment <= first_missing_fragment_)
            {
                // Perform first = *first until first >= last_received
                while (first_missing_fragment_ < last_fragment)
                {
                    first_missing_fragment_ = get_next_missing_fragment(first_missing_fragment_);
                    at_least_one_changed = true;
                }
            }
            else
            {
                // Find prev in missing list
                uint32_t current_frag = first_missing_fragment_;
                while (current_frag < initial_fragment)
                {
                    uint32_t next_frag = get_next_missing_fragment(current_frag);
                    if (next_frag >= initial_fragment)
                    {
                        // This is the fragment previous to initial_fragment.
                        // Find future value for next by repeating next = *next until next >= last_fragment.
                        uint32_t next_missing_fragment = next_frag;
                        while (next_missing_fragment < last_fragment)
                        {
                            next_missing_fragment = get_next_missing_fragment(next_missing_fragment);
                            at_least_one_changed = true;
                        }

                        // Update next and finish loop
                        if (at_least_one_changed)
                        {
                            set_next_missing_fragment(current_frag, next_missing_fragment);
                        }
                        break;
                    }
                    current_frag = next_frag;
                }
            }
        }

        return at_least_one_changed;
    }

add_fragments

将fragment加入到serializedPayload.data中,返回是否收全整个消息。


    bool add_fragments(
            const SerializedPayload_t& incoming_data,
            uint32_t fragment_starting_num,
            uint32_t fragments_in_submessage)
    {
        uint32_t original_offset = (fragment_starting_num - 1) * fragment_size_;
        uint32_t incoming_length = fragment_size_ * fragments_in_submessage;
        uint32_t last_fragment_index = fragment_starting_num + fragments_in_submessage - 1;

        // Validate fragment indexes
        if (last_fragment_index > fragment_count_)
        {
            return false;
        }

        // validate lengths
        if (last_fragment_index < fragment_count_)
        {
            if (incoming_data.length < incoming_length)
            {
                return false;
            }
        }
        else
        {
            incoming_length = serializedPayload.length - original_offset;
        }

        if (original_offset + incoming_length > serializedPayload.length)
        {
            return false;
        }

        if (received_fragments(fragment_starting_num - 1, fragments_in_submessage))
        {
            memcpy(
                &serializedPayload.data[original_offset],
                incoming_data.data, incoming_length);
        }

        return is_fully_assembled();
    }

总结

CacheChange_t在DDS中的发布-订阅过程中起到关键的作用,所有的消息传递都要使用这个结构,单次传递消息过大时,CacheChange_t会把它分割成多个小块数据传输,收到后再组装成一个完成的消息。里面的时间信息可以用来计算系统延时,这对于计算一个实时通信系统的性能指标非常重要,其他字段我们在使用的地方详细的说明。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

桃花朵朵~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值