ypipe.hpp 7.01 KB
Newer Older
Martin Sustrik's avatar
Martin Sustrik committed
1
/*
2
    Copyright (c) 2007-2015 Contributors as noted in the AUTHORS file
Martin Sustrik's avatar
Martin Sustrik committed
3 4 5 6

    This file is part of 0MQ.

    0MQ is free software; you can redistribute it and/or modify it under
7
    the terms of the GNU Lesser General Public License as published by
Martin Sustrik's avatar
Martin Sustrik committed
8 9 10 11 12 13
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    0MQ is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
    GNU Lesser General Public License for more details.
Martin Sustrik's avatar
Martin Sustrik committed
15

16
    You should have received a copy of the GNU Lesser General Public License
Martin Sustrik's avatar
Martin Sustrik committed
17 18 19
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

Martin Sustrik's avatar
Martin Sustrik committed
20 21
#ifndef __ZMQ_YPIPE_HPP_INCLUDED__
#define __ZMQ_YPIPE_HPP_INCLUDED__
Martin Sustrik's avatar
Martin Sustrik committed
22 23 24 25

#include "atomic_ptr.hpp"
#include "yqueue.hpp"
#include "platform.hpp"
26
#include "ypipe_base.hpp"
Martin Sustrik's avatar
Martin Sustrik committed
27

Martin Sustrik's avatar
Martin Sustrik committed
28
namespace zmq
Martin Sustrik's avatar
Martin Sustrik committed
29 30 31 32 33 34
{

    //  Lock-free queue implementation.
    //  Only a single thread can read from the pipe at any specific moment.
    //  Only a single thread can write to the pipe at any specific moment.
    //  T is the type of the object in the queue.
35
    //  N is granularity of the pipe, i.e. how many items are needed to
36 37
    //  perform next memory allocation.

38
    template <typename T, int N> class ypipe_t : public ypipe_base_t <T>
Martin Sustrik's avatar
Martin Sustrik committed
39 40 41
    {
    public:

42 43
        //  Initialises the pipe.
        inline ypipe_t ()
Martin Sustrik's avatar
Martin Sustrik committed
44 45 46 47
        {
            //  Insert terminator element into the queue.
            queue.push ();

48
            //  Let all the pointers to point to the terminator.
Martin Sustrik's avatar
Martin Sustrik committed
49
            //  (unless pipe is dead, in which case c is set to NULL).
50
            r = w = f = &queue.back ();
51
            c.set (&queue.back ());
Martin Sustrik's avatar
Martin Sustrik committed
52 53
        }

54 55 56 57 58 59
        //  The destructor doesn't have to be virtual. It is mad virtual
        //  just to keep ICC and code checking tools from complaining.
        inline virtual ~ypipe_t ()
        {
        }

Martin Sustrik's avatar
Martin Sustrik committed
60
        //  Following function (write) deliberately copies uninitialised data
Martin Sustrik's avatar
Martin Sustrik committed
61
        //  when used with zmq_msg. Initialising the VSM body for
Martin Sustrik's avatar
Martin Sustrik committed
62 63 64 65 66 67 68
        //  non-VSM messages won't be good for performance.

#ifdef ZMQ_HAVE_OPENVMS
#pragma message save
#pragma message disable(UNINIT)
#endif

69 70 71 72 73
        //  Write an item to the pipe.  Don't flush it yet. If incomplete is
        //  set to true the item is assumed to be continued by items
        //  subsequently written to the pipe. Incomplete items are never
        //  flushed down the stream.
        inline void write (const T &value_, bool incomplete_)
Martin Sustrik's avatar
Martin Sustrik committed
74 75 76 77
        {
            //  Place the value to the queue, add new terminator element.
            queue.back () = value_;
            queue.push ();
78 79 80 81

            //  Move the "flush up to here" poiter.
            if (!incomplete_)
                f = &queue.back ();
Martin Sustrik's avatar
Martin Sustrik committed
82 83 84 85 86 87
        }

#ifdef ZMQ_HAVE_OPENVMS
#pragma message restore
#endif

88 89
        //  Pop an incomplete item from the pipe. Returns true is such
        //  item exists, false otherwise.
90 91
        inline bool unwrite (T *value_)
        {
92
            if (f == &queue.back ())
93 94
                return false;
            queue.unpush ();
95
            *value_ = queue.back ();
96 97 98
            return true;
        }

99 100 101
        //  Flush all the completed items into the pipe. Returns false if
        //  the reader thread is sleeping. In that case, caller is obliged to
        //  wake the reader up before using the pipe again.
Martin Sustrik's avatar
Martin Sustrik committed
102 103 104
        inline bool flush ()
        {
            //  If there are no un-flushed items, do nothing.
105
            if (w == f)
Martin Sustrik's avatar
Martin Sustrik committed
106 107
                return true;

108 109
            //  Try to set 'c' to 'f'.
            if (c.cas (w, f) != w) {
Martin Sustrik's avatar
Martin Sustrik committed
110 111 112 113 114 115

                //  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.
116 117
                c.set (f);
                w = f;
Martin Sustrik's avatar
Martin Sustrik committed
118 119 120 121
                return false;
            }

            //  Reader is alive. Nothing special to do now. Just move
122 123
            //  the 'first un-flushed item' pointer to 'f'.
            w = f;
Martin Sustrik's avatar
Martin Sustrik committed
124 125 126
            return true;
        }

127 128
        //  Check whether item is available for reading.
        inline bool check_read ()
Martin Sustrik's avatar
Martin Sustrik committed
129
        {
130
            //  Was the value prefetched already? If so, return.
131
            if (&queue.front () != r && r)
Martin Sustrik's avatar
Martin Sustrik committed
132 133 134
                 return true;

            //  There's no prefetched value, so let us prefetch more values.
135 136 137 138 139 140 141
            //  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).
            r = c.cas (&queue.front (), NULL);

            //  If there are no elements prefetched, exit.
            //  During pipe's lifetime r should never be NULL, however,
142
            //  it can happen during pipe shutdown when items
143 144 145
            //  are being deallocated.
            if (&queue.front () == r || !r)
                return false;
Martin Sustrik's avatar
Martin Sustrik committed
146

147 148 149 150 151 152 153 154 155 156 157 158
            //  There was at least one value prefetched.
            return true;
        }

        //  Reads an item from the pipe. Returns false if there is no value.
        //  available.
        inline bool read (T *value_)
        {
            //  Try to prefetch a value.
            if (!check_read ())
                return false;

Martin Sustrik's avatar
Martin Sustrik committed
159 160
            //  There was at least one value prefetched.
            //  Return it to the caller.
Martin Sustrik's avatar
Martin Sustrik committed
161 162 163 164 165
            *value_ = queue.front ();
            queue.pop ();
            return true;
        }

166 167 168
        //  Applies the function fn to the first elemenent in the pipe
        //  and returns the value returned by the fn.
        //  The pipe mustn't be empty or the function crashes.
169
        inline bool probe (bool (*fn)(const T &))
170
        {
Pieter Hintjens's avatar
Pieter Hintjens committed
171 172
            bool rc = check_read ();
            zmq_assert (rc);
173

Pieter Hintjens's avatar
Pieter Hintjens committed
174
            return (*fn) (queue.front ());
175 176
        }

Martin Sustrik's avatar
Martin Sustrik committed
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
    protected:

        //  Allocation-efficient queue to store pipe items.
        //  Front of the queue points to the first prefetched item, back of
        //  the pipe points to last un-flushed item. Front is used only by
        //  reader thread, while back is used only by writer thread.
        yqueue_t <T, N> queue;

        //  Points to the first un-flushed item. This variable is used
        //  exclusively by writer thread.
        T *w;

        //  Points to the first un-prefetched item. This variable is used
        //  exclusively by reader thread.
        T *r;

193 194 195
        //  Points to the first item to be flushed in the future.
        T *f;

Martin Sustrik's avatar
Martin Sustrik committed
196 197
        //  The single point of contention between writer and reader thread.
        //  Points past the last flushed item. If it is NULL,
Martin Sustrik's avatar
Martin Sustrik committed
198 199 200 201 202 203
        //  reader is asleep. This pointer should be always accessed using
        //  atomic operations.
        atomic_ptr_t <T> c;

        //  Disable copying of ypipe object.
        ypipe_t (const ypipe_t&);
204
        const ypipe_t &operator = (const ypipe_t&);
Martin Sustrik's avatar
Martin Sustrik committed
205 206 207 208 209
    };

}

#endif