无锁队列-使用hazard指针解决ABA问题
实现一个无锁队列, 原子操作使用了tbb::atomic, ABA问题使用hazard指针解决
无锁队列实现:
- /*
- * msque.hpp
- * Created on: 2012-8-30
- * Author: qianqians
- * msque:
- */
- #ifndef _MSQUE_H
- #define _MSQUE_H
- #include <boost/bind.hpp>
- #include <boost/atomic.hpp>
- #include <boost/pool/pool_alloc.hpp>
- #include <angelica/container/detail/_hazard_ptr.h>
- namespace angelica {
- namespace container{
- template <typename T, typename _Allocator = boost::pool_allocator<T> >
- class msque{
- private:
- struct _list_node{
- _list_node () {_next = 0;}
- _list_node (const T & val) : data(val) {_next = 0;}
- ~_list_node () {}
- T data;
- boost::atomic<_list_node *> _next;
- };
- struct _list{
- boost::atomic<_list_node *> _begin;
- boost::atomic<_list_node *> _end;
- boost::atomic_uint32_t _size;
- };
- typedef angelica::container::detail::_hazard_ptr<_list_node> _hazard_ptr;
- typedef angelica::container::detail::_hazard_system<_list_node> _hazard_system;
- typedef typename _Allocator::template rebind<_list_node>::other _node_alloc;
- typedef typename _Allocator::template rebind<_list>::other _list_alloc;
- public:
- msque(void){
- __list.store(get_list());
- }
- ~msque(void){
- put_list(__list.load());
- }
- bool empty(){
- return (__list.load()->_size.load() == 0);
- }
- std::size_t size(){
- return __list.load()->_size.load();
- }
- void clear(){
- _list * _new_list = get_list();
- _list * _old_list = __list.exchange(_new_list);
- put_list(_old_list);
- }
- void push(const T & data){
- _list_node * _null = 0;
- _list_node * _node = 0;
- while(_node == 0){
- _node = get_node(data);
- }
- _hazard_ptr * _hp = _hazard_sys.acquire();
- _hp->_hazard = __list.load()->_end.load();
- while(1){
- if(_hp->_hazard != __list.load()->_end.load()){
- _hp->_hazard = __list.load()->_end.load();
- continue;
- }
- _list_node * _begin_node = __list.load()->_begin.load();
- _list_node * next = _hp->_hazard->_next.load();
- if(_hp->_hazard != __list.load()->_end.load()){
- _hp->_hazard = __list.load()->_end.load();
- continue;
- }
- if(next != 0){
- __list.load()->_end.compare_exchange_weak(_hp->_hazard, next);
- _hp->_hazard = __list.load()->_end.load();
- continue;
- }
- if (_hp->_hazard->_next.compare_exchange_weak(_null, _node)){
- break;
- }else{
- _null = 0;
- _hp->_hazard = __list.load()->_end.load();
- }
- }
- __list.load()->_end.compare_exchange_weak(_hp->_hazard, _node);
- _hazard_sys.release(_hp);
- __list.load()->_size++;
- }
- bool pop(T & data){
- bool ret = true;
- _hazard_ptr * _hp_begin = _hazard_sys.acquire();
- _hazard_ptr * _hp_next = _hazard_sys.acquire();
- _hp_begin->_hazard = __list.load()->_begin.load();
- while(1){
- if(_hp_begin->_hazard != __list.load()->_begin.load()){
- _hp_begin->_hazard = __list.load()->_begin.load();
- continue;
- }
- _list_node * end = __list.load()->_end.load();
- _hp_next->_hazard = _hp_begin->_hazard->_next.load();
- if(_hp_next->_hazard == 0){
- ret = false;
- goto end;
- }
- if(_hp_begin->_hazard != __list.load()->_begin.load()){
- _hp_begin->_hazard = __list.load()->_begin.load();
- continue;
- }
- if(end == _hp_begin->_hazard){
- __list.load()->_end.compare_exchange_weak(end, _hp_next->_hazard);
- _hp_begin->_hazard = __list.load()->_begin.load();
- continue;
- }
- if(__list.load()->_begin.compare_exchange_strong(_hp_begin->_hazard, _hp_next->_hazard)){
- break;
- }
- }
- data = _hp_next->_hazard->data;
- _hazard_sys.retire(_hp_begin->_hazard, boost::bind(&angelica::container::msque<T>::put_node, this, _1));
- __list.load()->_size--;
- end:
- _hazard_sys.release(_hp_begin);
- _hazard_sys.release(_hp_next);
- return ret;
- }
- private:
- _list * get_list(){
- _list * __list = __list_alloc.allocate(1);
- __list->_size = 0;
- _list_node * _node = __node_alloc.allocate(1);
- _node->_next.store(0);
- __list->_begin.store(_node);
- __list->_end.store(_node);
- return __list;
- }
- void put_list(_list * _p){
- _list_node * _node = _p->_begin;
- do{
- _list_node * _tmp = _node;
- _node = _node->_next;
- _hazard_sys.retire(_tmp, boost::bind(&angelica::container::msque<T>::put_node, this, _1));
- }while(_node != 0);
- __list_alloc.deallocate(_p, 1);
- }
- _list_node * get_node(const T & data){
- _list_node * _node = __node_alloc.allocate(1);
- _node->data = data;
- _node->_next = 0;
- return _node;
- }
- void put_node(_list_node * _p){
- __node_alloc.deallocate(_p, 1);
- }
- private:
- boost::atomic<_list *> __list;
- _list_alloc __list_alloc;
- _node_alloc __node_alloc;
- _hazard_system _hazard_sys;
- _Allocator __T_alloc;
- };
- } /* angelica */
- } /* container */
- #endif //_MSQUE_H
hazard指针实现:
- /*
- * _hazard_ptr.hpp
- * Created on: 2012-8-26
- * Author: qianqians
- * _hazard_ptr: Used to solve the ABA problem
- */
- #ifndef _HAZARD_PTR_H
- #define _HAZARD_PTR_H
- #include <vector>
- #include <boost/thread.hpp>
- #include <boost/function.hpp>
- #include <boost/foreach.hpp>
- #include <boost/atomic.hpp>
- #include <boost/pool/pool_alloc.hpp>
- namespace angelica{
- namespace container{
- namespace detail{
- // _hazard_ptr
- template <typename X>
- struct _hazard_ptr{
- X * _hazard; //
- boost::atomic_int32_t _active; // 0 使用中/1 未使用
- };
- // hazard system
- template <typename T>
- class _hazard_system{
- public:
- _hazard_system(){
- llen.store(0);
- re_list_set.resize(8);
- for(int i = 0; i < 8; i++){
- re_list_set[i] = new recover_list;
- re_list_set[i]->active.store(1);
- }
- _head = _get_node();
- }
- ~_hazard_system(){
- BOOST_FOREACH(recover_list * _re_list, re_list_set){
- if(!_re_list->re_vector.empty()){
- BOOST_FOREACH(_deallocate_data var, _re_list->re_vector){
- var.second(var.first);
- }
- _re_list->re_vector.clear();
- }
- }
- re_list_set.clear();
- }
- private:
- // lock-free list(push only) storage _hazard_ptr
- typedef _hazard_ptr<typename T> _hazard_ptr_;
- typedef struct _list_node{
- _hazard_ptr_ _hazard;
- boost::atomic<_list_node *> next;
- } _list_head;
- // allocator
- boost::pool_allocator<_list_node> _alloc_list_node;
- // hazard ptr list
- boost::atomic<_list_head *> _head;
- // list lenght
- boost::atomic_uint32_t llen;
- _list_node * _get_node(){
- _list_node * _node = _alloc_list_node.allocate(1);
- _node->_hazard._hazard = 0;
- _node->_hazard._active = 1;
- _node->next = 0;
- return _node;
- }
- void _put_node(_list_node * _node){
- _alloc_list_node.deallocate(_node, 1);
- }
- public:
- _hazard_ptr_ * acquire(){
- // try to reuse a retired hazard ptr
- for(_list_node * _node = _head; _node; _node = _node->next){
- if (_node->_hazard._active.exchange(0) == 0)
- continue;
- return &_node->_hazard;
- }
- // alloc a new node
- _list_node * _new_node = _get_node();
- _new_node->_hazard._active.store(0);
- // increment the list length
- llen++;
- // push into list
- _new_node->next = _head.exchange(_new_node);
- return &(_new_node->_hazard);
- }
- void release(_hazard_ptr_ * ptr){
- ptr->_hazard = 0;
- ptr->_active.store(1);
- }
- private:
- // deallocate function
- typedef boost::function<void(typename T * )> fn_dealloc;
- // deallocate struct data
- typedef std::pair<typename T *, fn_dealloc> _deallocate_data;
- // recover list
- struct recover_list {
- std::vector<_deallocate_data> re_vector;
- boost::atomic_int32_t active; // 0 使用中 / 1 未使用
- };
- // 回收队列集合
- std::vector<recover_list * > re_list_set;
- public:
- void retire(T * p, fn_dealloc fn){
- // get tss rvector
- recover_list * _rvector_ptr = 0;
- while(1){
- for(int i = 0; i < 8; i++) {
- if (re_list_set[i]->active.exchange(0) == 0){
- continue;
- }else{
- _rvector_ptr = re_list_set[i];
- break;
- }
- }
- if (_rvector_ptr != 0)
- break;
- }
- // push into rvector
- _rvector_ptr->re_vector.push_back(std::make_pair(p, fn));
- // scan
- if(_rvector_ptr->re_vector.size() > 32 && _rvector_ptr->re_vector.size() > llen.load()){
- // scan hazard pointers list collecting all non-null ptrs
- std::vector<void *> hvector;
- for(_list_node * _node = _head; _node; _node = _node->next){
- void * p = _node->_hazard._hazard;
- if (p != 0)
- hvector.push_back(p);
- }
- // sort hazard list
- std::sort(hvector.begin(), hvector.end(), std::less<void*>());
- // deallocator
- std::vector<_deallocate_data>::iterator iter = _rvector_ptr->re_vector.begin();
- while(iter != _rvector_ptr->re_vector.end()){
- if(!std::binary_search(hvector.begin(), hvector.end(), iter->first)){
- iter->second(iter->first);
- if(iter->first != _rvector_ptr->re_vector.back().first){
- *iter = _rvector_ptr->re_vector.back();
- _rvector_ptr->re_vector.pop_back();
- }
- else{
- iter = _rvector_ptr->re_vector.erase(iter);
- }
- }
- else{
- ++iter;
- }
- }
- }
- _rvector_ptr->active.store(1);
- }
- };
- } /* angelica */
- } /* container */
- } /* detail */
- #endif // _HAZARD_PTR_H
改用boost::atomic实现
tbb::atomic:http://threadingbuildingblocks.org/files/documentation/a00118.html
关于hazard指针:http://blog.youkuaiyun.com/pongba/article/details/589864
另一种lock-free queue:http://www.drdobbs.com/cpp/writing-lock-free-code-a-corrected-queue/210604448?pgno=2