zmq源码阅读笔记之基础数据结构

1,array
zmq实现了一套自己的数组,数组内部用一个std::vector   来保存数据
 

 template  class array_t
    {
    private:

        typedef array_item_t  item_t;

    public:

        typedef typename std::vector ::size_type size_type;
 private:

 typedef std::vector  items_t;
 items_t items;

 array_t (const array_t&);
 const array_t &operator = (const array_t&);
 };

}

 而数组内部的元素需要继承array_item_t,并提供了插入,删除等操作

 

  template  class array_item_t
    {
    public:

        inline array_item_t () :
            array_index (-1)
        {
        }

        //  The destructor doesn't have to be virtual. It is made virtual
        //  just to keep ICC and code checking tools from complaining.
        inline virtual ~array_item_t ()
        {
        }

        inline void set_array_index (int index_)
        {
            array_index = index_;
        }

        inline int get_array_index ()
        {
            return array_index;
        }

    private:

        int array_index;

        array_item_t (const array_item_t&);
        const array_item_t &operator = (const array_item_t&);
    };

2,yqueue
yqueue是zmq实现的一个队列,十分高效,内部定义了一个chunk_t类型的内存块节点,为了减少申请释放内存的次数,yqueue用一个chunk来保存多个元素,

 struct chunk_t
        {
             T values [N];
             chunk_t *prev;
             chunk_t *next;
        };

yqueue通过3个指针和3个标记位来操作元素,其中指针指向链表中的内存块,标记位记录内存块中的元素位置

        chunk_t *begin_chunk;
        int begin_pos;
        chunk_t *back_chunk;
        int back_pos;
        chunk_t *end_chunk;
        int end_pos;

提供了常规的队列操作,font,back,pop和push外,还提供了unpsuh操作 ,用来回滚上一次push操作
3,ypipe
ypipe是zmq中的一个无所队列的实现,经常用于线程之间的数据同步,继承ypipe_base_t

template  class ypipe_base_t
    {
    public:
        virtual ~ypipe_base_t () {}
        virtual void write (const T &value_, bool incomplete_) = 0; 
        virtual bool unwrite (T *value_) = 0;
        virtual bool flush () = 0;
        virtual bool check_read () = 0;
        virtual bool read (T *value_) = 0;
        virtual bool probe (bool (*fn)(const T &)) = 0;
    };

 内部通过一个yqueue来存储数据,还有有3个重要的指针成员变量来实现无锁操作


       T *w; // 指向第一个未刷新的元素  
       T *r; // 指向第一个未预取的元素   
       T *f; // 指向即将刷新的元素,其实从代码流程上来看应该是即将被刷新的元素的后面一个。。。  
       atomic_ptr_t  c; //读写线程共享的原子操作元素的指针。主要就完成刷新的工作的  

 

+-----------------------------------------------------------------------------+
|     已读取       |     已写入      | 已写入未冲刷     |     写入的不完整数据    |
+----------------- r -------------- w --------------- f ----------------------+

1“已读取段” 为预读取数据,读取线程可以直接读取该段数据,“已写入”段是已经写入的ypipe的数据,读取线程也可以读取,读取之后r指针会移到w处,“已写入未冲刷”段为已经写入管道,但是没有冲刷的数据,读取线程读取不到,需要调用flush方法,才可以读取,flush后w指针会移到f处,f之后的数据,用于回滚,flush后不会处理该数据。


        //参数:
        //    value_,被回滚的元素。
        //返回值:
        //    true,回滚成功返回true;false,回滚失败返回false。
        inline bool unwrite (T *value_)
        {
            //如果f == &queue.back(),那么说明没有未flush的元素,则回滚失败。
            //所以说,如果只write一个元素,那么必然write之后必然f==&queue.back()
            //如果write n个元素,那么在写入第n个元素之前,前n-1个元素都是可以回滚的。
            //第n个元素被写入的同时,所有n个元素被flush了,然后也就不能回滚了。
            if (f == &queue.back ())
                return false;
            queue.unpush ();
            *value_ = queue.back ();
            return true;
        }

        //flush之后c,w,f就指向同一个位置了。
        //实际的刷新操作其实只做了两件事,c=f,w=f。其中C是读写线程共享的指针。
        //如果只有写线程,那么c和w是永远相等的,永远等于f。
        //只有被读线程改写的情况下c!=w才成立,而地线程只在一种情况下改写c,那就是队列中没有数据的时候,读线程把c设置为null,
        //然后睡眠。所以说如果c不为w,那么肯定为null,则此时读线程睡眠。
        //那么flush()函数的返回值其实是表明:
        //    返回true,读线程活跃,不需要唤醒。
        //    返回false,读线程睡眠,在flush之后需要唤醒读线程读取数据。
        inline bool flush ()
        {
            //  If there are no un-flushed items, do nothing.
            //如果所有元素都已经flush了,那么直接返回,什么都不做。
            //当写完数据,f指针被write()更新,此时 w!=f,就需要刷新,
            //所以w,f配合起来表示是否需要执行刷新操作。
            if (w == f)
                return true;

            //  Try to set 'c' to 'f'.
            //如果c与w相同,则c设置为f,并返回c的ptr。
            //如果c与w不同,则c值不变,返回c的ptr。
            if (c.cas (w, f) != w) {
                //c要么为空,要么为w。
                //  Compare-and-swap was unseccessful because 'c' is NULL.
                //  This means that the reader is asleep. Therefore we don't
                //  care about thread-safeness and update c in non-atomic
                //  manner. We'll return false to let the caller know
                //  that reader is sleeping.
                c.set (f); //设置c为f
                w = f;  //设置w为f。
                return false;
            }

            //  Reader is alive. Nothing special to do now. Just move
            //  the 'first un-flushed item' pointer to 'f'.
            w = f;
            return true;
        //检查pipe是否可读。
        //返回值:
        //    true,可读;false,不可读。
        //check_read()是通过指针r的位置来判断是否有数据可读的;
        //如果指针指向的是队列头或者r没有指向任何元素,则说明队列中没有可读数据,这是check_read去尝试预取。
        //所谓预取就是令r=c,而c在write中被指向f。这时从queue_front()到f这个位置的数据都被预读出来了,然后调用read()
        //一块一块的读取。当c再次等于queue_front()的时候说明数据读取完了,这时c指针指向null,然后读线程睡眠。
        //c是否为null标志读线程是否睡眠。
        inline bool check_read ()
        {
            //  Was the value prefetched already? If so, return.
            //值已经被预取(所谓取,就是r被更新到了第一个不可读元素的位置,在r
            //之前的元素都是可以被读取的),返回true。
            if (&queue.front () != r && r)
                 return true;

            //  There's no prefetched value, so let us prefetch more values.
            //  Prefetching is to simply retrieve the
            //  pointer from c in atomic fashion. If there are no
            //  items to prefetch, set c to NULL (using compare-and-swap).
            //在此函数(check_read)中实现了预读操作。
            //最开始:在写入了n个数据并flush之后,c指向back_pos,与queue.front()
            //不相等,所有下面的一句话只返回c的当前值给r,c的值不变。
            //由于写线程和读线程可能同时存在,因此在每次check_read()的时候
            //c的位置可能又发生了向后移动,这样每次check_read()都会判断c的
            //当前位置,每次都把r更新为c的最新位置。
            //当所有元素都被读取完毕,此时begin_pos和back_pos位置相同,
            //这个时候c被设置为null,r的值不变。
            r = c.cas (&queue.front (), NULL);

            //  If there are no elements prefetched, exit.
            //  During pipe's lifetime r should never be NULL, however,
            //  it can happen during pipe shutdown when items
            //  are being deallocated.
            //当所有元素都读完,返回false。
            if (&queue.front () == r || !r)
                return false;
            //  There was at least one value prefetched.
            return true;
        }

        //  Reads an item from the pipe. Returns false if there is no value.
        //  available.
        //从pipe中读取一个T元素。
        //参数:
        //    value_,出参。
        //返回值:
        //    true,读取成功;false,读取失败。
        inline bool read (T *value_)
        {
            //  Try to prefetch a value.
            if (!check_read ())
                return false;

            //  There was at least one value prefetched.
            //  Return it to the caller.
            *value_ = queue.front ();
            queue.pop ();
            return true;
        }

 

 

你可能感兴趣的:(TCP/IP与网络编程)