lockless_queue.h
Go to the documentation of this file.
00001 /*
00002  * Copyright 2018 The Cartographer Authors
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *      http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 #ifndef CARTOGRAPHER_COMMON_LOCKLESS_QUEUE_H_
00018 #define CARTOGRAPHER_COMMON_LOCKLESS_QUEUE_H_
00019 
00020 #include <atomic>
00021 #include <memory>
00022 
00023 #include "absl/memory/memory.h"
00024 #include "glog/logging.h"
00025 
00026 namespace cartographer {
00027 namespace common {
00028 
00029 // Lock-less queue which is thread safe for concurrent data producers and a
00030 // single data consumer.
00031 //
00032 // This lockless queue implementation is adapted from
00033 // https://github.com/resonance-audio/resonance-audio/blob/master/resonance_audio/utils/lockless_task_queue.h
00034 template <typename T>
00035 class LocklessQueue {
00036  public:
00037   LocklessQueue() {
00038     free_list_head_ = nullptr;
00039     incoming_data_list_head_ = nullptr;
00040     data_list_head_ = nullptr;
00041     data_list_tail_ = nullptr;
00042   }
00043 
00044   ~LocklessQueue() {
00045     FreeNodes(free_list_head_.exchange(nullptr));
00046     FreeNodes(incoming_data_list_head_.exchange(nullptr));
00047     FreeNodes(data_list_head_);
00048   }
00049 
00050   // Pushes the data item into the queue.
00051   void Push(std::unique_ptr<T> t) {
00052     Node* const free_node = PopNodeFromFreeList();
00053     CHECK(free_node);
00054     free_node->data = std::move(t);
00055     PushNodeToList(&incoming_data_list_head_, free_node);
00056   }
00057 
00058   // Pops the oldest data item from the queue. If the queue is empty returns a
00059   // nullptr.
00060   std::unique_ptr<T> Pop() {
00061     SwapLists();
00062     if (data_list_head_ != nullptr) {
00063       Node* node = data_list_head_;
00064       data_list_head_ = data_list_head_->next;
00065       std::unique_ptr<T> data = std::move(node->data);
00066       PushNodeToList(&free_list_head_, node);
00067       return std::move(data);
00068     }
00069     return nullptr;
00070   }
00071 
00072  private:
00073   // Node to model a single-linked list.
00074   struct Node {
00075     Node() = default;
00076 
00077     // Dummy copy constructor to enable vector::resize allocation.
00078     Node(const Node& node) : next() {}
00079 
00080     // User data.
00081     std::unique_ptr<T> data;
00082 
00083     // Pointer to next node.
00084     std::atomic<Node*> next;
00085   };
00086 
00087   // Deallocates all nodes of the list starting with 'head'.
00088   void FreeNodes(Node* node) {
00089     while (node != nullptr) {
00090       Node* next_node_ptr = node->next;
00091       delete node;
00092       node = next_node_ptr;
00093     }
00094   }
00095 
00096   // Pushes a node to the front of a list.
00097   void PushNodeToList(std::atomic<Node*>* list_head, Node* node) {
00098     DCHECK(list_head);
00099     DCHECK(node);
00100     Node* list_head_ptr;
00101     do {
00102       list_head_ptr = list_head->load();
00103       node->next = list_head_ptr;
00104     } while (!std::atomic_compare_exchange_strong_explicit(
00105         list_head, &list_head_ptr, node, std::memory_order_release,
00106         std::memory_order_relaxed));
00107   }
00108 
00109   // Pops a node from the front of the free node list. If the list is empty
00110   // constructs a new node instance.
00111   Node* PopNodeFromFreeList() {
00112     Node* list_head_ptr;
00113     Node* list_head_next_ptr;
00114     do {
00115       list_head_ptr = free_list_head_.load();
00116       if (list_head_ptr == nullptr) {
00117         return new Node;
00118       }
00119       list_head_next_ptr = list_head_ptr->next.load();
00120     } while (!std::atomic_compare_exchange_strong_explicit(
00121         &free_list_head_, &list_head_ptr, list_head_next_ptr,
00122         std::memory_order_relaxed, std::memory_order_relaxed));
00123     return list_head_ptr;
00124   }
00125 
00126   // Swaps the incoming data list for an empty list and appends all items
00127   // to 'data_list_tail_'.
00128   void SwapLists() {
00129     Node* node_itr = incoming_data_list_head_.exchange(nullptr);
00130     if (node_itr == nullptr) {
00131       // There is no data on the incoming list.
00132       return;
00133     }
00134     // The first node of the incoming data list will become the tail of the
00135     // data list.
00136     Node* const data_list_tail = node_itr;
00137 
00138     // Reverses the list order. After this operation 'prev_node_itr' points to
00139     // head of the new data list items.
00140     Node* prev_node_itr = nullptr;
00141     while (node_itr != nullptr) {
00142       Node* const next_node_ptr = node_itr->next;
00143       node_itr->next = prev_node_itr;
00144       prev_node_itr = node_itr;
00145       node_itr = next_node_ptr;
00146     }
00147 
00148     // If the previous data list was empty, replace head rather than appending
00149     // to the list.
00150     if (data_list_tail_ == nullptr) {
00151       data_list_head_ = prev_node_itr;
00152     } else {
00153       data_list_tail_->next = prev_node_itr;
00154     }
00155     data_list_tail_ = data_list_tail;
00156   }
00157 
00158   // Pointer to head node of free list.
00159   std::atomic<Node*> free_list_head_;
00160 
00161   // Pointer to head node of incoming data list, which is in FILO order.
00162   std::atomic<Node*> incoming_data_list_head_;
00163 
00164   // Pointer to head node of data list.
00165   Node* data_list_head_;
00166 
00167   // Pointer to tail node of data list.
00168   Node* data_list_tail_;
00169 };
00170 
00171 }  // namespace common
00172 }  // namespace cartographer
00173 
00174 #endif  // CARTOGRAPHER_COMMON_LOCKLESS_QUEUE_H_


cartographer
Author(s): The Cartographer Authors
autogenerated on Thu May 9 2019 02:27:35