Orocos DataPort 解析: orocos lock free data object
来源:互联网 发布:印度军事 知乎 编辑:程序博客网 时间:2024/05/20 18:18
Orocos有两种 dataPort (Input, Output),在 conncet(Output, Input) 之后,往 Output 中写的数据能够传递到 Input 中并读取, 一个OutputPort可以连接多个InputPort(类似于ROS中的Pub&Sub),这极大地提高了模块化设计的数据交换。
其中能够实现数据交换的关键是 ChannelDataElement 数据结构,在 connect 两个端口的同时,会 new 一个 ChannelDataElement 数据结构,然后在两个DataPort中分别保存其指针,这个 ChannelDataElement 承担着数据中转站的角色,有 write 和 read 方法分别供 OutputPort 和 InputPort 使用。而在其中起到关键作用的成员变量是 DataObject 对象,它可以是 DATA,BUFFER,CIRCULAR_BUFFER 三种类型之一,提供暂存数据的作用。
ChannelDataElement 数据结构的代码如下:
template<typename T>class ChannelDataElement : public base::ChannelElement<T>{ bool written, mread; typename base::DataObjectInterface<T>::shared_ptr data;public: typedef typename base::ChannelElement<T>::param_t param_t; typedef typename base::ChannelElement<T>::reference_t reference_t; ChannelDataElement(typename base::DataObjectInterface<T>::shared_ptr sample) : written(false), mread(false), data(sample) {} /** Update the data sample stored in this element. * It always returns true. */ virtual bool write(param_t sample) { data->Set(sample); written = true; mread = false; return this->signal(); } /** Reads the last sample given to write() * * @return false if no sample has ever been written, true otherwise */ virtual FlowStatus read(reference_t sample, bool copy_old_data) { if (written) { if ( !mread ) { data->Get(sample); mread = true; return NewData; // 枚举变量 }if(copy_old_data) data->Get(sample); return OldData; // 枚举变量 } return NoData; // 枚举变量 } /** Resets the stored sample. After clear() has been called, read() * returns false */ virtual void clear() { written = false; mread = false; base::ChannelElement<T>::clear(); } virtual bool data_sample(param_t sample) { data->data_sample(sample); return base::ChannelElement<T>::data_sample(sample); } virtual T data_sample() { return data->Get(); }};
其中一个DataObjectLockFree 结构如下:lock free 的 circular bufer, 一个 producer + 多个 consumer
这里还有一个Lock free 的 Mulity Writer Single Reader 队列数据结构 see link
相比于使用mutex, lock free 的好处就是,当获得这个共享资源的线程被挂起的时候,其他线程照样能够正常读写该资源,可以说是 real-time 的读写。
关于多线程 lock_free 的设计: In general, any time you have a small amount of data protected by a mutex, and you can pack that data entirely into a 32- or 64-bit integer type, you can always convert your mutex-based operations into lock-free RMW operations, no matter what those operations actually do! 但是很多情况用户自定义数据结构(类)有很多数据成员,比64-bit要大得多,而且不一定是 natively aligned,所以读写操作不可能是 atomic 的,存在读写自定义数据的过程中被其他线程中断(如再次进行写操作)的情况。不过,我们可以设计
std::atomic<MyClass*>
read_ptr 和std::atomic<MyClass*> write_ptr
,通过 compare_exchange_weak 局部变量来实现安全的指针交换。
Orocos DataObjectLockFree:
namespace RTT{ namespace base { /** * @brief This DataObject is a Lock-Free implementation, * such that reads and writes can happen concurrently without priority * inversions. * * When there are more writes than reads, the last write will * be returned. The internal buffer can get full if too many * concurrent reads are taking to long. In that case, each new * read will read the element the previous read returned. * * @verbatim * The following Truth table applies when a Low Priority thread is * preempted by a High Priority thread : * * L\H | Set | Get | * Set | Ok | Ok | * Get | Ok | Ok | * * legend : L : Low Priority thread * H : High Priority thread * Blk: Blocks High Priority thread (bad!) * internal::NA : Not allowed ! * @endverbatim * Further, multiple reads may occur before, during and after * a write operation simultaneously. The buffer needs readers+2*writers * elements to be guaranteed non blocking. * @ingroup PortBuffers */ template<class T> class DataObjectLockFree : public DataObjectInterface<T> { public: /** * The type of the data. */ typedef T DataType; /** * @brief The maximum number of threads. * * When used in data flow, this is always 2. */ const unsigned int MAX_THREADS; // = 2 private: /** * Conversion of number of threads to size of buffer. */ const unsigned int BUF_LEN; // = MAX_THREADS+2 /** * Internal buffer structure. * Both the read and write pointers pointing to this struct * must be declared volatile, since they are modified in other threads. * I did not declare data as volatile, * since we only read/write it in secured buffers. */ struct DataBuf { DataBuf() : data(), counter(), next() { oro_atomic_set(&counter, 0); } DataType data; mutable oro_atomic_t counter; DataBuf* next; }; typedef DataBuf* volatile VolPtrType; typedef DataBuf ValueType; typedef DataBuf* PtrType; VolPtrType read_ptr; VolPtrType write_ptr; /** * A 3 element Data buffer */ DataBuf* data; public: /** * Construct a DataObjectLockFree by name. * * @param _name The name of this DataObject. * @param initial_value The initial value of this DataObject. */ DataObjectLockFree( const T& initial_value = T(), unsigned int max_threads = 2 ) : MAX_THREADS(max_threads), BUF_LEN( max_threads + 2), read_ptr(0), write_ptr(0) { data = new DataBuf[BUF_LEN]; read_ptr = &data[0]; write_ptr = &data[1]; data_sample(initial_value); } ~DataObjectLockFree() { delete[] data; } /** * Get a copy of the data. * This method will allocate memory twice if data is not a value type. * Use Get(DataType&) for the non-allocating version. * * @return A copy of the data. */ virtual DataType Get() const {DataType cache; Get(cache); return cache; } /** * Get a copy of the Data (non allocating). * If pull has reserved enough memory to store the copy, * no memory will be allocated. * * @param pull A copy of the data. */ virtual void Get( DataType& pull ) const { PtrType reading; // loop to combine Read/Modify of counter // This avoids a race condition where read_ptr // could become write_ptr ( then we would read corrupted data). do { reading = read_ptr; // copy buffer location oro_atomic_inc(&reading->counter); // lock buffer, no more writes 不能在读的过程中进行写操作 // XXX smp_mb if ( reading != read_ptr ) // if read_ptr changed, oro_atomic_dec(&reading->counter); // better to start over. else break; } while ( true ); // from here on we are sure that 'reading' // is a valid buffer to read from. // 这里是关键,由于用户自定义数据不一定是natively aligned,所以读操作不一定是atomic的,可能在读取自定义数据的过程中被其他线程中断(如再次进行写操作)。 pull = reading->data; // takes some time // XXX smp_mb oro_atomic_dec(&reading->counter); // release buffer } /** * Set the data to a certain value (non blocking). * * @param push The data which must be set. */ virtual void Set( const DataType& push ) { /** * This method can not be called concurrently (only one * producer). With a minimum of 3 buffers, if the * write_ptr+1 field is not occupied, it will remain so * because the read_ptr is at write_ptr-1 (and can * not increment the counter on write_ptr+1). Hence, no * locking is needed. */ // writeout in any case,只有一个producer,并且write_ptr!=read_ptr,所以永远是安全的! write_ptr->data = push; PtrType wrote_ptr = write_ptr; // if next field is occupied (by read_ptr or counter), // go to next and check again... // 环形buffer,如果是前一个还没有读完,这时候新的写进去又加进来新的读取就会有这种事情发生; // 如果前一个读取没有完成(就是那个takes some time的地方),后续最多能再进行两次写操作(buffer大小为4). while ( oro_atomic_read( &write_ptr ->next->counter ) != 0 || write_ptr->next == read_ptr ) { write_ptr = write_ptr->next; if (write_ptr == wrote_ptr) return; // nothing found, to many readers ! } // we will be able to move, so replace read_ptr read_ptr = wrote_ptr; write_ptr = write_ptr->next; // we checked this in the while loop } virtual void data_sample( const DataType& sample ) { // prepare the buffer. for (unsigned int i = 0; i < BUF_LEN-1; ++i) { data[i].data = sample; data[i].next = &data[i+1]; } data[BUF_LEN-1].data = sample; data[BUF_LEN-1].next = &data[0]; } };}}
reference link:
1. https://msdn.microsoft.com/en-us/library/windows/desktop/ee418650(v=vs.85).aspx
2. http://www.boost.org/doc/libs/1_60_0/doc/html/lockfree.html
3. http://preshing.com/20150402/you-can-do-any-kind-of-atomic-read-modify-write-operation/
4. http://preshing.com/20120612/an-introduction-to-lock-free-programming/
5. https://en.wikipedia.org/wiki/ABA_problem
- Orocos DataPort 解析: orocos lock free data object
- Orocos Activity&ExecutionEngine 解析
- Orocos OperationCaller 解析
- OROCOS 网上注释文档
- orocos 类型系统分析
- 机器人开源项目:orocos
- orocos Logger 类的设计
- Lock-Free Data Structures
- Lock-Free Data Structures
- 开源机器人控制软件OROCOS
- Orocos Real-Time Toolkit 2.6.0
- ROS节点与OROCOS组件通信--安装
- orocos 模块的构造 和 析构 顺序
- orocos xenomai dlopen 内存权限问题:
- 开源机器人控制软件OROCOS
- ROS节点与OROCOS组件通信--构建组件
- typeinfo dynamic_cast & 模板编程 & orocos OperationCaller 类的设计
- Orocos ExecutionEngine 对函数Operation调用的实现
- 彻底删除Delphi中ActiveX面板中的第三方控件
- 反省与自勉
- C++连接mysql数据库的两种方法
- Freeform SQL (FFSQL) - Tip - How to create TEMP(Temporary) tables
- handsontable一个Excel一样的外观数据网格组件
- Orocos DataPort 解析: orocos lock free data object
- 我是一个线程
- 设计模式C++学习笔记之九(Template Method模板方法模式)
- Linux-34-linux基础重要命令11(L005-16)
- Oracle 11g之LogMiner常用场景
- 第十一周项目1-点-圆-圆柱类族的设计(2)
- poj1466——Girls and Boys(最大独立点集)
- [一起学Hive]之五-Hive的视图和分区
- c++第五次上机作业