160ac17fbSs.makeev_local // The MIT License (MIT) 260ac17fbSs.makeev_local // 360ac17fbSs.makeev_local // Copyright (c) 2015 Sergey Makeev, Vadim Slyusarev 460ac17fbSs.makeev_local // 560ac17fbSs.makeev_local // Permission is hereby granted, free of charge, to any person obtaining a copy 660ac17fbSs.makeev_local // of this software and associated documentation files (the "Software"), to deal 760ac17fbSs.makeev_local // in the Software without restriction, including without limitation the rights 860ac17fbSs.makeev_local // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 960ac17fbSs.makeev_local // copies of the Software, and to permit persons to whom the Software is 1060ac17fbSs.makeev_local // furnished to do so, subject to the following conditions: 1160ac17fbSs.makeev_local // 1260ac17fbSs.makeev_local // The above copyright notice and this permission notice shall be included in 1360ac17fbSs.makeev_local // all copies or substantial portions of the Software. 1460ac17fbSs.makeev_local // 1560ac17fbSs.makeev_local // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1660ac17fbSs.makeev_local // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1760ac17fbSs.makeev_local // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1860ac17fbSs.makeev_local // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1960ac17fbSs.makeev_local // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 2060ac17fbSs.makeev_local // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 2160ac17fbSs.makeev_local // THE SOFTWARE. 2260ac17fbSs.makeev_local 2360ac17fbSs.makeev_local #pragma once 2460ac17fbSs.makeev_local 2560ac17fbSs.makeev_local #include <MTTools.h> 264da58e65Ss.makeev_local #include <utility> 2760ac17fbSs.makeev_local 2860ac17fbSs.makeev_local namespace MT 2960ac17fbSs.makeev_local { 3060ac17fbSs.makeev_local /// \class ConcurrentQueueLIFO 31*b23bdf5aSs.makeev_local /// \brief Lock-Free Multi-Producer Multi-Consumer Queue with fixed capacity. 3260ac17fbSs.makeev_local /// 3360ac17fbSs.makeev_local /// based on Bounded MPMC queue article by Dmitry Vyukov 3460ac17fbSs.makeev_local /// http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue 3560ac17fbSs.makeev_local /// 3660ac17fbSs.makeev_local template<typename T, uint32 CAPACITY> 3760ac17fbSs.makeev_local class LockFreeQueueMPMC 3860ac17fbSs.makeev_local { 3960ac17fbSs.makeev_local static const int32 ALIGNMENT = 16; 4060ac17fbSs.makeev_local static const int32 ALIGNMENT_MASK = (ALIGNMENT-1); 4160ac17fbSs.makeev_local static const uint32 MASK = (CAPACITY - 1); 4260ac17fbSs.makeev_local 4360ac17fbSs.makeev_local struct Cell 4460ac17fbSs.makeev_local { 4560ac17fbSs.makeev_local Atomic32<uint32> sequence; 4660ac17fbSs.makeev_local T data; 4760ac17fbSs.makeev_local }; 4860ac17fbSs.makeev_local 4960ac17fbSs.makeev_local // Raw memory buffer 5060ac17fbSs.makeev_local byte rawMemory[ sizeof(Cell) * CAPACITY + ALIGNMENT ]; 5160ac17fbSs.makeev_local 5260ac17fbSs.makeev_local // Prevent false sharing between threads 5360ac17fbSs.makeev_local uint8 cacheline0[64]; 5460ac17fbSs.makeev_local 5560ac17fbSs.makeev_local Cell* const buffer; 5660ac17fbSs.makeev_local 5760ac17fbSs.makeev_local // Prevent false sharing between threads 5860ac17fbSs.makeev_local uint8 cacheline1[64]; 5960ac17fbSs.makeev_local 6060ac17fbSs.makeev_local Atomic32<uint32> enqueuePos; 6160ac17fbSs.makeev_local 6260ac17fbSs.makeev_local // Prevent false sharing between threads 6360ac17fbSs.makeev_local uint8 cacheline2[64]; 6460ac17fbSs.makeev_local 6560ac17fbSs.makeev_local Atomic32<uint32> dequeuePos; 6660ac17fbSs.makeev_local MoveCtor(T * element,T && val)6760ac17fbSs.makeev_local inline void MoveCtor(T* element, T && val) 6860ac17fbSs.makeev_local { 6960ac17fbSs.makeev_local new(element) T(std::move(val)); 7060ac17fbSs.makeev_local } 7160ac17fbSs.makeev_local 7260ac17fbSs.makeev_local 7360ac17fbSs.makeev_local public: 7460ac17fbSs.makeev_local 7560ac17fbSs.makeev_local MT_NOCOPYABLE(LockFreeQueueMPMC); 7660ac17fbSs.makeev_local LockFreeQueueMPMC()7760ac17fbSs.makeev_local LockFreeQueueMPMC() 7860ac17fbSs.makeev_local : buffer( (Cell*)( ( (uintptr_t)&rawMemory[0] + ALIGNMENT_MASK ) & ~(uintptr_t)ALIGNMENT_MASK ) ) 7960ac17fbSs.makeev_local { 8060ac17fbSs.makeev_local static_assert( MT::StaticIsPow2<CAPACITY>::result, "LockFreeQueueMPMC capacity must be power of 2"); 8160ac17fbSs.makeev_local 8260ac17fbSs.makeev_local for (uint32 i = 0; i < CAPACITY; i++) 8360ac17fbSs.makeev_local { 8460ac17fbSs.makeev_local buffer[i].sequence.StoreRelaxed(i); 8560ac17fbSs.makeev_local } 8660ac17fbSs.makeev_local 8760ac17fbSs.makeev_local enqueuePos.StoreRelaxed(0); 8860ac17fbSs.makeev_local dequeuePos.StoreRelaxed(0); 8960ac17fbSs.makeev_local } 9060ac17fbSs.makeev_local TryPush(T && data)9160ac17fbSs.makeev_local bool TryPush(T && data) 9260ac17fbSs.makeev_local { 9360ac17fbSs.makeev_local Cell* cell = nullptr; 9460ac17fbSs.makeev_local 9560ac17fbSs.makeev_local uint32 pos = enqueuePos.LoadRelaxed(); 9660ac17fbSs.makeev_local for(;;) 9760ac17fbSs.makeev_local { 9860ac17fbSs.makeev_local cell = &buffer[pos & MASK]; 9960ac17fbSs.makeev_local 10060ac17fbSs.makeev_local uint32 seq = cell->sequence.Load(); 10160ac17fbSs.makeev_local int32 dif = (int32)seq - (int32)pos; 10260ac17fbSs.makeev_local 10360ac17fbSs.makeev_local if (dif == 0) 10460ac17fbSs.makeev_local { 10560ac17fbSs.makeev_local uint32 nowPos = enqueuePos.CompareAndSwap(pos, pos + 1); 10660ac17fbSs.makeev_local if (nowPos == pos) 10760ac17fbSs.makeev_local { 10860ac17fbSs.makeev_local break; 10960ac17fbSs.makeev_local } else 11060ac17fbSs.makeev_local { 11160ac17fbSs.makeev_local pos = nowPos; 11260ac17fbSs.makeev_local } 11360ac17fbSs.makeev_local } else 11460ac17fbSs.makeev_local { 11560ac17fbSs.makeev_local if (dif < 0) 11660ac17fbSs.makeev_local { 11760ac17fbSs.makeev_local return false; 11860ac17fbSs.makeev_local } else 11960ac17fbSs.makeev_local { 12060ac17fbSs.makeev_local pos = enqueuePos.LoadRelaxed(); 12160ac17fbSs.makeev_local } 12260ac17fbSs.makeev_local } 12360ac17fbSs.makeev_local } 12460ac17fbSs.makeev_local 12560ac17fbSs.makeev_local // successfully found a cell 12660ac17fbSs.makeev_local MoveCtor( &cell->data, std::move(data) ); 12760ac17fbSs.makeev_local cell->sequence.Store(pos + 1); 12860ac17fbSs.makeev_local return true; 12960ac17fbSs.makeev_local } 13060ac17fbSs.makeev_local 13160ac17fbSs.makeev_local TryPop(T & data)13260ac17fbSs.makeev_local bool TryPop(T& data) 13360ac17fbSs.makeev_local { 13460ac17fbSs.makeev_local Cell* cell = nullptr; 13560ac17fbSs.makeev_local uint32 pos = dequeuePos.LoadRelaxed(); 13660ac17fbSs.makeev_local 13760ac17fbSs.makeev_local for (;;) 13860ac17fbSs.makeev_local { 13960ac17fbSs.makeev_local cell = &buffer[pos & MASK]; 14060ac17fbSs.makeev_local 14160ac17fbSs.makeev_local uint32 seq = cell->sequence.Load(); 14260ac17fbSs.makeev_local int32 dif = (int32)seq - (int32)(pos + 1); 14360ac17fbSs.makeev_local 14460ac17fbSs.makeev_local if (dif == 0) 14560ac17fbSs.makeev_local { 14660ac17fbSs.makeev_local uint32 nowPos = dequeuePos.CompareAndSwap(pos, pos + 1); 14760ac17fbSs.makeev_local if (nowPos == pos) 14860ac17fbSs.makeev_local { 14960ac17fbSs.makeev_local break; 15060ac17fbSs.makeev_local } else 15160ac17fbSs.makeev_local { 15260ac17fbSs.makeev_local pos = nowPos; 15360ac17fbSs.makeev_local } 15460ac17fbSs.makeev_local } else 15560ac17fbSs.makeev_local { 15660ac17fbSs.makeev_local if (dif < 0) 15760ac17fbSs.makeev_local { 15860ac17fbSs.makeev_local return false; 15960ac17fbSs.makeev_local } else 16060ac17fbSs.makeev_local { 16160ac17fbSs.makeev_local pos = dequeuePos.LoadRelaxed(); 16260ac17fbSs.makeev_local } 16360ac17fbSs.makeev_local } 16460ac17fbSs.makeev_local } 16560ac17fbSs.makeev_local 16660ac17fbSs.makeev_local // successfully found a cell 16760ac17fbSs.makeev_local MoveCtor( &data, std::move(cell->data) ); 16860ac17fbSs.makeev_local cell->sequence.Store(pos + MASK + 1); 16960ac17fbSs.makeev_local return true; 17060ac17fbSs.makeev_local } 17160ac17fbSs.makeev_local 17260ac17fbSs.makeev_local }; 17360ac17fbSs.makeev_local 17460ac17fbSs.makeev_local 17560ac17fbSs.makeev_local }