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 }