123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 |
- /*
- Copyright (c) 2007-2016 Contributors as noted in the AUTHORS file
- This file is part of libzmq, the ZeroMQ core engine in C++.
- libzmq is free software; you can redistribute it and/or modify it under
- the terms of the GNU Lesser General Public License (LGPL) as published
- by the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
- As a special exception, the Contributors give you permission to link
- this library with independent modules to produce an executable,
- regardless of the license terms of these independent modules, and to
- copy and distribute the resulting executable under terms of your choice,
- provided that you also meet, for each linked independent module, the
- terms and conditions of the license of that module. An independent
- module is a module which is not derived from or based on this library.
- If you modify this library, you must extend this exception to your
- version of the library.
- libzmq 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 GNU Lesser General Public
- License for more details.
- You should have received a copy of the GNU Lesser General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- #ifndef __ZMQ_YPIPE_HPP_INCLUDED__
- #define __ZMQ_YPIPE_HPP_INCLUDED__
- #include "atomic_ptr.hpp"
- #include "yqueue.hpp"
- #include "ypipe_base.hpp"
- namespace zmq
- {
- // 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.
- // N is granularity of the pipe, i.e. how many items are needed to
- // perform next memory allocation.
- template <typename T, int N> class ypipe_t ZMQ_FINAL : public ypipe_base_t<T>
- {
- public:
- // Initialises the pipe.
- ypipe_t ()
- {
- // Insert terminator element into the queue.
- _queue.push ();
- // Let all the pointers to point to the terminator.
- // (unless pipe is dead, in which case c is set to NULL).
- _r = _w = _f = &_queue.back ();
- _c.set (&_queue.back ());
- }
- // Following function (write) deliberately copies uninitialised data
- // when used with zmq_msg. Initialising the VSM body for
- // non-VSM messages won't be good for performance.
- #ifdef ZMQ_HAVE_OPENVMS
- #pragma message save
- #pragma message disable(UNINIT)
- #endif
- // 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.
- void write (const T &value_, bool incomplete_)
- {
- // Place the value to the queue, add new terminator element.
- _queue.back () = value_;
- _queue.push ();
- // Move the "flush up to here" pointer.
- if (!incomplete_)
- _f = &_queue.back ();
- }
- #ifdef ZMQ_HAVE_OPENVMS
- #pragma message restore
- #endif
- // Pop an incomplete item from the pipe. Returns true if such
- // item exists, false otherwise.
- bool unwrite (T *value_)
- {
- if (_f == &_queue.back ())
- return false;
- _queue.unpush ();
- *value_ = _queue.back ();
- return true;
- }
- // 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.
- bool flush ()
- {
- // If there are no un-flushed items, do nothing.
- if (_w == _f)
- return true;
- // Try to set 'c' to 'f'.
- if (_c.cas (_w, _f) != _w) {
- // Compare-and-swap was unsuccessful 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);
- _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;
- }
- // Check whether item is available for reading.
- bool check_read ()
- {
- // Was the value prefetched already? If so, return.
- 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).
- _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.
- 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.
- 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;
- }
- // Applies the function fn to the first element in the pipe
- // and returns the value returned by the fn.
- // The pipe mustn't be empty or the function crashes.
- bool probe (bool (*fn_) (const T &))
- {
- const bool rc = check_read ();
- zmq_assert (rc);
- return (*fn_) (_queue.front ());
- }
- 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;
- // Points to the first item to be flushed in the future.
- T *_f;
- // The single point of contention between writer and reader thread.
- // Points past the last flushed item. If it is NULL,
- // reader is asleep. This pointer should be always accessed using
- // atomic operations.
- atomic_ptr_t<T> _c;
- ZMQ_NON_COPYABLE_NOR_MOVABLE (ypipe_t)
- };
- }
- #endif
|