zmq源代码分析 - ypipe_t(2)

  1. ypipe_t源代码续
        //回滚已经写入的元素(对于单一元素是不能回滚的,对于多片段元素,在最后一片写入之前前n-1片是可以回滚的)。
        //参数:
        //    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;
        }

  4. 过程分析
     分析完了ypipe_t及其关联数据结构的源代码,下面通过图形展示一下各种操作的内存变动图。
     4.1 yqueue_t初始化完成后的内存布局如下图所示。
     这里写图片描述 
     4.2 ypipe_t初始化完成后内存布局如下图所示。
     这里写图片描述 
     4.3 ypipe_t::write()写入一个元素,这个元素分为三个片段A,B, C写入,A,B的incomplete_为true,C的incomplete_为false导致整个元素被flush。
     这里写图片描述 
     4.4 ypipe_t::flash()把元素刷新到队列中(也就是flash()之后此元素就被读线程可见了。)
     这里写图片描述
     4.5 ypipe_t::check_read()更新了r指针到新的第一个不可读位置。
这里写图片描述
4.6 调用三次ypipe_t::read()函数分三次读取了A,B,C。读取之后的内存布局如下图所示。
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值