using mpmc bounded q for async and many async optimizations
This commit is contained in:
		
							parent
							
								
									754cac85ac
								
							
						
					
					
						commit
						52d02af950
					
				| @ -33,15 +33,14 @@ | |||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <thread> |  | ||||||
| #include <chrono> | #include <chrono> | ||||||
|  | #include <thread> | ||||||
| #include <atomic> | #include <atomic> | ||||||
| 
 | 
 | ||||||
| #include "../sinks/sink.h" | #include "../sinks/sink.h" | ||||||
| #include "../logger.h" | #include "./mpmc_bounded_q.h" | ||||||
| #include "../details/mpcs_q.h" | #include "./log_msg.h" | ||||||
| #include "../details/log_msg.h" | #include "./format.h" | ||||||
| #include "../details/format.h" |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| namespace spdlog | namespace spdlog | ||||||
| @ -49,42 +48,67 @@ namespace spdlog | |||||||
| namespace details | namespace details | ||||||
| { | { | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| class async_log_helper | class async_log_helper | ||||||
| { | { | ||||||
|  | 	// Async msg to move to/from the queue
 | ||||||
|  | 	// Movable only. should never be copied
 | ||||||
|     struct async_msg |     struct async_msg | ||||||
|     { |     { | ||||||
|         std::string logger_name; |         std::string logger_name; | ||||||
|         level::level_enum level; |         level::level_enum level; | ||||||
|         log_clock::time_point time; |         log_clock::time_point time; | ||||||
|         std::string raw_msg_str; |         std::string txt; | ||||||
| 
 | 
 | ||||||
| 		async_msg() = default; | 		async_msg() = default; | ||||||
|  | 		~async_msg() = default; | ||||||
|  | 
 | ||||||
|  | 		async_msg(const async_msg&) = delete; | ||||||
|  | 		async_msg& operator=(async_msg& other) = delete; | ||||||
| 
 | 
 | ||||||
|         async_msg(const details::log_msg& m) : |         async_msg(const details::log_msg& m) : | ||||||
|             logger_name(m.logger_name), |             logger_name(m.logger_name), | ||||||
|             level(m.level), |             level(m.level), | ||||||
|             time(m.time), |             time(m.time), | ||||||
|             raw_msg_str(m.raw.data(), m.raw.size()) |             txt(m.raw.data(), m.raw.size()) | ||||||
|         {} |         {} | ||||||
| 
 | 
 | ||||||
|         log_msg to_log_msg() | 		async_msg(async_msg&& other) : | ||||||
|  |             logger_name(std::move(other.logger_name)), | ||||||
|  |             level(std::move(other.level)), | ||||||
|  |             time(std::move(other.time)), | ||||||
|  |             txt(std::move(other.txt)) | ||||||
|  |         {} | ||||||
|  | 
 | ||||||
|  | 		async_msg& operator=(async_msg&& other) | ||||||
| 		{ | 		{ | ||||||
|             log_msg msg; | 			logger_name = std::move(other.logger_name); | ||||||
|  | 			level = other.level; | ||||||
|  | 			time = std::move(other.time); | ||||||
|  | 			txt = std::move(other.txt); | ||||||
|  | 			return *this; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         void fill_log_msg(log_msg &msg) | ||||||
|  |         { | ||||||
|  | 			msg.clear(); | ||||||
|             msg.logger_name = logger_name; |             msg.logger_name = logger_name; | ||||||
|             msg.level = level; |             msg.level = level; | ||||||
|             msg.time = time; |             msg.time = time; | ||||||
|             msg.raw << raw_msg_str; |             msg.raw << txt; | ||||||
|             return msg; |  | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
| 
 | 
 | ||||||
|     using q_type = details::mpsc_q < std::unique_ptr<async_msg> >; | 	using item_type = async_msg; | ||||||
|  |     using q_type = details::mpmc_bounded_queue<item_type>; | ||||||
|  | 
 | ||||||
|     using clock = std::chrono::steady_clock; |     using clock = std::chrono::steady_clock; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     explicit async_log_helper(size_t max_queue_size); |     explicit async_log_helper(size_t queue_size); | ||||||
|     void log(const details::log_msg& msg); |     void log(const details::log_msg& msg); | ||||||
| 
 | 
 | ||||||
|     //Stop logging and join the back thread
 |     //Stop logging and join the back thread
 | ||||||
| @ -97,8 +121,6 @@ public: | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| private: | private: | ||||||
|     std::vector<std::shared_ptr<sinks::sink>> _sinks; |     std::vector<std::shared_ptr<sinks::sink>> _sinks; | ||||||
|     std::atomic<bool> _active; |     std::atomic<bool> _active; | ||||||
| @ -113,18 +135,19 @@ private: | |||||||
|     formatter_ptr _formatter; |     formatter_ptr _formatter; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     // will throw last back thread exception or if worker hread no active
 |     // will throw last worker thread exception or if worker thread no active
 | ||||||
|     void _push_sentry(); |     void throw_if_bad_worker(); | ||||||
| 
 | 
 | ||||||
|     // worker thread loop
 |     // worker thread loop
 | ||||||
|     void _thread_loop(); |     void thread_loop(); | ||||||
| 
 | 
 | ||||||
|     // guess how much to sleep if queue is empty/full using last succesful op time as hint
 |     // guess how much to sleep if queue is empty/full using last succesful op time as hint
 | ||||||
|     static void _sleep_or_yield(const clock::time_point& last_op_time); |     static void sleep_or_yield(const clock::time_point& last_op_time); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     // clear all remaining messages(if any), stop the _worker_thread and join it
 |     // clear all remaining messages(if any), stop the _worker_thread and join it
 | ||||||
|     void _join(); |     void join_worker(); | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| }; | }; | ||||||
| } | } | ||||||
| @ -133,57 +156,60 @@ private: | |||||||
| ///////////////////////////////////////////////////////////////////////////////
 | ///////////////////////////////////////////////////////////////////////////////
 | ||||||
| // async_sink class implementation
 | // async_sink class implementation
 | ||||||
| ///////////////////////////////////////////////////////////////////////////////
 | ///////////////////////////////////////////////////////////////////////////////
 | ||||||
| inline spdlog::details::async_log_helper::async_log_helper(size_t max_queue_size) | inline spdlog::details::async_log_helper::async_log_helper(size_t queue_size) | ||||||
|     :_sinks(), |     :_sinks(), | ||||||
|      _active(true), |      _active(true), | ||||||
|      _q(max_queue_size), |      _q(queue_size), | ||||||
|      _worker_thread(&async_log_helper::_thread_loop, this) |      _worker_thread(&async_log_helper::thread_loop, this) | ||||||
| {} | {} | ||||||
| 
 | 
 | ||||||
| inline spdlog::details::async_log_helper::~async_log_helper() | inline spdlog::details::async_log_helper::~async_log_helper() | ||||||
| { | { | ||||||
|     _join(); |     join_worker(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| //Try to push and block until succeeded
 | //Try to push and block until succeeded
 | ||||||
| inline void spdlog::details::async_log_helper::log(const details::log_msg& msg) | inline void spdlog::details::async_log_helper::log(const details::log_msg& msg) | ||||||
| { | { | ||||||
|     _push_sentry(); |     throw_if_bad_worker(); | ||||||
| 
 | 
 | ||||||
|     //Only if queue is full, enter wait loop
 |     //Only if queue is full, enter wait loop
 | ||||||
|     if (!_q.push(std::unique_ptr < async_msg >(new async_msg(msg)))) |     //if (!_q.push(std::unique_ptr < async_msg >(new async_msg(msg))))
 | ||||||
|  |     //async_msg* as = new async_msg(msg);
 | ||||||
|  |     //if (!_q.enqueue(std::unique_ptr<async_msg>(new async_msg(msg))))
 | ||||||
|  | 	if (!_q.enqueue(std::move(async_msg(msg)))) | ||||||
|     {	 |     {	 | ||||||
|         auto last_op_time = clock::now(); |         auto last_op_time = clock::now(); | ||||||
|         do |         do | ||||||
|         { |         { | ||||||
|             _sleep_or_yield(last_op_time); |             sleep_or_yield(last_op_time); | ||||||
|         } |  | ||||||
|         while (!_q.push(std::unique_ptr < async_msg >(new async_msg(msg)))); |  | ||||||
|         } |         } | ||||||
|  |         while (!_q.enqueue(std::move(async_msg(msg)))); | ||||||
|     } |     } | ||||||
| 	 | 	 | ||||||
| inline void spdlog::details::async_log_helper::_thread_loop() | } | ||||||
|  | 
 | ||||||
|  | inline void spdlog::details::async_log_helper::thread_loop() | ||||||
| { | { | ||||||
| 
 | 
 | ||||||
|  | 	log_msg popped_log_msg; | ||||||
|     clock::time_point last_pop = clock::now(); |     clock::time_point last_pop = clock::now(); | ||||||
|  | 	size_t counter = 0; | ||||||
|     while (_active) |     while (_active) | ||||||
|     { |     { | ||||||
|         q_type::item_type popped_msg; |         q_type::item_type popped_msg; | ||||||
| 
 | 
 | ||||||
|         if (_q.pop(popped_msg)) |         if (_q.dequeue(popped_msg)) | ||||||
|         {			 |         {			 | ||||||
| 
 |  | ||||||
|             last_pop = clock::now(); |             last_pop = clock::now(); | ||||||
| 
 | 
 | ||||||
|             try |             try | ||||||
|             { |             { | ||||||
|                 details::log_msg log_msg = popped_msg->to_log_msg(); |                 popped_msg.fill_log_msg(popped_log_msg); | ||||||
| 
 |                 _formatter->format(popped_log_msg); | ||||||
|                 _formatter->format(log_msg); |  | ||||||
|                 for (auto &s : _sinks) |                 for (auto &s : _sinks) | ||||||
|                     s->log(log_msg); |                     s->log(popped_log_msg);								 | ||||||
| 
 |  | ||||||
|             } |             } | ||||||
|             catch (const std::exception& ex) |             catch (const std::exception& ex) | ||||||
|             { |             { | ||||||
| @ -197,7 +223,7 @@ inline void spdlog::details::async_log_helper::_thread_loop() | |||||||
|         // sleep or yield if queue is empty.
 |         // sleep or yield if queue is empty.
 | ||||||
|         else |         else | ||||||
|         {			  |         {			  | ||||||
|             _sleep_or_yield(last_pop); |             sleep_or_yield(last_pop); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -226,6 +252,7 @@ inline void spdlog::details::async_log_helper::set_formatter(formatter_ptr msg_f | |||||||
| 
 | 
 | ||||||
| inline void spdlog::details::async_log_helper::shutdown(const log_clock::duration& timeout) | inline void spdlog::details::async_log_helper::shutdown(const log_clock::duration& timeout) | ||||||
| { | { | ||||||
|  |     /*
 | ||||||
|     if (timeout > std::chrono::milliseconds::zero()) |     if (timeout > std::chrono::milliseconds::zero()) | ||||||
|     { |     { | ||||||
|         auto until = log_clock::now() + timeout; |         auto until = log_clock::now() + timeout; | ||||||
| @ -234,12 +261,13 @@ inline void spdlog::details::async_log_helper::shutdown(const log_clock::duratio | |||||||
|             std::this_thread::sleep_for(std::chrono::milliseconds(5)); |             std::this_thread::sleep_for(std::chrono::milliseconds(5)); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     _join(); |     join_worker(); | ||||||
|  |      */ | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| // Sleep or yield using the time passed since last message as a hint
 | // Sleep or yield using the time passed since last message as a hint
 | ||||||
| inline void spdlog::details::async_log_helper::_sleep_or_yield(const clock::time_point& last_op_time) | inline void spdlog::details::async_log_helper::sleep_or_yield(const clock::time_point& last_op_time) | ||||||
| { | { | ||||||
|     using std::chrono::milliseconds; |     using std::chrono::milliseconds; | ||||||
|     using std::this_thread::sleep_for; |     using std::this_thread::sleep_for; | ||||||
| @ -255,7 +283,8 @@ inline void spdlog::details::async_log_helper::_sleep_or_yield(const clock::time | |||||||
|         yield(); |         yield(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| inline void spdlog::details::async_log_helper::_push_sentry() | //throw if the worker thread threw an exception or not active
 | ||||||
|  | inline void spdlog::details::async_log_helper::throw_if_bad_worker() | ||||||
| { | { | ||||||
|     if (_last_workerthread_ex) |     if (_last_workerthread_ex) | ||||||
|     { |     { | ||||||
| @ -264,11 +293,11 @@ inline void spdlog::details::async_log_helper::_push_sentry() | |||||||
|         throw *ex; |         throw *ex; | ||||||
|     } |     } | ||||||
|     if (!_active) |     if (!_active) | ||||||
|         throw(spdlog_ex("async_sink not active")); |         throw(spdlog_ex("async logger is not active")); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| inline void spdlog::details::async_log_helper::_join() | inline void spdlog::details::async_log_helper::join_worker() | ||||||
| { | { | ||||||
|     _active = false; |     _active = false; | ||||||
|     if (_worker_thread.joinable()) |     if (_worker_thread.joinable()) | ||||||
|  | |||||||
| @ -65,7 +65,6 @@ public: | |||||||
|         { |         { | ||||||
|             _log_msg.logger_name = _callback_logger->name(); |             _log_msg.logger_name = _callback_logger->name(); | ||||||
|             _log_msg.time = log_clock::now(); |             _log_msg.time = log_clock::now(); | ||||||
|             //_log_msg.tm_time = details::os::localtime(log_clock::to_time_t(_log_msg.time));
 |  | ||||||
|             _callback_logger->_log_msg(_log_msg); |             _callback_logger->_log_msg(_log_msg); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -78,8 +78,6 @@ struct log_msg | |||||||
|         return *this; |         return *this; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     void clear() |     void clear() | ||||||
|     { |     { | ||||||
|         level = level::OFF; |         level = level::OFF; | ||||||
| @ -90,7 +88,6 @@ struct log_msg | |||||||
|     std::string logger_name; |     std::string logger_name; | ||||||
|     level::level_enum level; |     level::level_enum level; | ||||||
|     log_clock::time_point time; |     log_clock::time_point time; | ||||||
|     //std::tm tm_time;
 |  | ||||||
|     fmt::MemoryWriter raw; |     fmt::MemoryWriter raw; | ||||||
|     fmt::MemoryWriter formatted; |     fmt::MemoryWriter formatted; | ||||||
| }; | }; | ||||||
|  | |||||||
							
								
								
									
										175
									
								
								include/spdlog/details/mpmc_bounded_q.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										175
									
								
								include/spdlog/details/mpmc_bounded_q.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,175 @@ | |||||||
|  | /*
 | ||||||
|  | A modified version of Bounded MPMC queue by Dmitry Vyukov. | ||||||
|  | 
 | ||||||
|  | Original code from: | ||||||
|  | http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue
 | ||||||
|  | 
 | ||||||
|  | licensed by Dmitry Vyukov under the terms below: | ||||||
|  | 
 | ||||||
|  | Simplified BSD license | ||||||
|  | 
 | ||||||
|  | Copyright (c) 2010-2011 Dmitry Vyukov. All rights reserved. | ||||||
|  | Redistribution and use in source and binary forms, with or without modification, | ||||||
|  | are permitted provided that the following conditions are met: | ||||||
|  | 1. Redistributions of source code must retain the above copyright notice, this list of | ||||||
|  | conditions and the following disclaimer. | ||||||
|  | 
 | ||||||
|  | 2. Redistributions in binary form must reproduce the above copyright notice, this list | ||||||
|  | of conditions and the following disclaimer in the documentation and/or other materials | ||||||
|  | provided with the distribution. | ||||||
|  | 
 | ||||||
|  | THIS SOFTWARE IS PROVIDED BY DMITRY VYUKOV "AS IS" AND ANY EXPRESS OR IMPLIED | ||||||
|  | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||||||
|  | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT | ||||||
|  | SHALL DMITRY VYUKOV OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | ||||||
|  | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||||
|  | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, | ||||||
|  | OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||||||
|  | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE | ||||||
|  | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | ||||||
|  | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  | 
 | ||||||
|  | The views and conclusions contained in the software and documentation are those of the authors and | ||||||
|  | should not be interpreted as representing official policies, either expressed or implied, of Dmitry Vyukov. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  | The code in its current form adds the license below: | ||||||
|  | 
 | ||||||
|  | spdlog - an extremely fast and easy to use c++11 logging library. | ||||||
|  | Copyright (c) 2014 Gabi Melman. | ||||||
|  | 
 | ||||||
|  | Permission is hereby granted, free of charge, to any person obtaining | ||||||
|  | a copy of this software and associated documentation files (the | ||||||
|  | "Software"), to deal in the Software without restriction, including | ||||||
|  | without limitation the rights to use, copy, modify, merge, publish, | ||||||
|  | distribute, sublicense, and/or sell copies of the Software, and to | ||||||
|  | permit persons to whom the Software is furnished to do so, subject to | ||||||
|  | the following conditions: | ||||||
|  | 
 | ||||||
|  | The above copyright notice and this permission notice shall be | ||||||
|  | included in all copies or substantial portions of the Software. | ||||||
|  | 
 | ||||||
|  | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||||||
|  | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||||||
|  | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||||
|  | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||||||
|  | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | ||||||
|  | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | ||||||
|  | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <atomic> | ||||||
|  | #include "../common.h" | ||||||
|  | 
 | ||||||
|  | namespace spdlog | ||||||
|  | { | ||||||
|  | namespace details | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | template<typename T> | ||||||
|  | class mpmc_bounded_queue | ||||||
|  | { | ||||||
|  | public: | ||||||
|  | 
 | ||||||
|  | 	using item_type = T; | ||||||
|  | 	mpmc_bounded_queue(size_t buffer_size) | ||||||
|  | 		: buffer_(new cell_t [buffer_size]), | ||||||
|  | 		  buffer_mask_(buffer_size - 1) | ||||||
|  | 	{ | ||||||
|  | 		//queue size must be power of two
 | ||||||
|  | 		if(!((buffer_size >= 2) && ((buffer_size & (buffer_size - 1)) == 0))) | ||||||
|  | 			throw spdlog_ex("async logger queue size must be power of two"); | ||||||
|  | 
 | ||||||
|  | 		for (size_t i = 0; i != buffer_size; i += 1) | ||||||
|  | 			buffer_[i].sequence_.store(i, std::memory_order_relaxed); | ||||||
|  | 		enqueue_pos_.store(0, std::memory_order_relaxed); | ||||||
|  | 		dequeue_pos_.store(0, std::memory_order_relaxed); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	~mpmc_bounded_queue() | ||||||
|  | 	{		 | ||||||
|  | 		delete [] buffer_; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	bool enqueue(T&& data) | ||||||
|  | 	{ | ||||||
|  | 		cell_t* cell; | ||||||
|  | 		size_t pos = enqueue_pos_.load(std::memory_order_relaxed); | ||||||
|  | 		for (;;) | ||||||
|  | 		{		 | ||||||
|  | 			cell = &buffer_[pos & buffer_mask_]; | ||||||
|  | 			size_t seq = cell->sequence_.load(std::memory_order_acquire); | ||||||
|  | 			intptr_t dif = (intptr_t)seq - (intptr_t)pos; | ||||||
|  | 			if (dif == 0) | ||||||
|  | 			{			 | ||||||
|  | 				if (enqueue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed)) | ||||||
|  | 					break; | ||||||
|  | 			} | ||||||
|  | 			else if (dif < 0) | ||||||
|  | 			{				 | ||||||
|  | 				return false; | ||||||
|  | 			}			 | ||||||
|  | 			else | ||||||
|  | 			{		 | ||||||
|  | 				pos = enqueue_pos_.load(std::memory_order_relaxed); | ||||||
|  | 			} | ||||||
|  | 		}		 | ||||||
|  | 		cell->data_ = std::move(data); | ||||||
|  | 		cell->sequence_.store(pos + 1, std::memory_order_release); | ||||||
|  | 		return true; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	bool dequeue(T& data) | ||||||
|  | 	{ | ||||||
|  | 		cell_t* cell; | ||||||
|  | 		size_t pos = dequeue_pos_.load(std::memory_order_relaxed); | ||||||
|  | 		for (;;) | ||||||
|  | 		{ | ||||||
|  | 			cell = &buffer_[pos & buffer_mask_]; | ||||||
|  | 			size_t seq = | ||||||
|  | 				cell->sequence_.load(std::memory_order_acquire); | ||||||
|  | 			intptr_t dif = (intptr_t)seq - (intptr_t)(pos + 1); | ||||||
|  | 			if (dif == 0) | ||||||
|  | 			{ | ||||||
|  | 				if (dequeue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed)) | ||||||
|  | 					break; | ||||||
|  | 			} | ||||||
|  | 			else if (dif < 0) | ||||||
|  | 				return false; | ||||||
|  | 			else | ||||||
|  | 				pos = dequeue_pos_.load(std::memory_order_relaxed); | ||||||
|  | 		} | ||||||
|  | 		data = std::move(cell->data_); | ||||||
|  | 		cell->sequence_.store(pos + buffer_mask_ + 1, std::memory_order_release); | ||||||
|  | 		return true; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 	struct cell_t | ||||||
|  | 	{ | ||||||
|  | 		std::atomic<size_t>   sequence_; | ||||||
|  | 		T                     data_; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	static size_t const     cacheline_size = 64; | ||||||
|  | 	typedef char            cacheline_pad_t [cacheline_size]; | ||||||
|  | 
 | ||||||
|  | 	cacheline_pad_t         pad0_; | ||||||
|  | 	cell_t* const           buffer_; | ||||||
|  | 	size_t const            buffer_mask_; | ||||||
|  | 	cacheline_pad_t         pad1_; | ||||||
|  | 	std::atomic<size_t>     enqueue_pos_; | ||||||
|  | 	cacheline_pad_t         pad2_; | ||||||
|  | 	std::atomic<size_t>     dequeue_pos_; | ||||||
|  | 	cacheline_pad_t         pad3_; | ||||||
|  | 
 | ||||||
|  | 	mpmc_bounded_queue(mpmc_bounded_queue const&); | ||||||
|  | 	void operator = (mpmc_bounded_queue const&); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // ns details
 | ||||||
|  | } // ns spdlog
 | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user