//------------------------------------------------------------------------------
// Author: Pavel Karneliuk
// Description: Special Queue for fixed size elements without copying them
// Copyright (c) 2013 EPAM Systems
//------------------------------------------------------------------------------
/*
This file is part of Nfstrace.
Nfstrace is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, version 2 of the License.
Nfstrace is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Nfstrace. If not, see .
*/
//------------------------------------------------------------------------------
#ifndef QUEUE_H
#define QUEUE_H
//------------------------------------------------------------------------------
#include
#include
#include
#include "utils/block_allocator.h"
#include "utils/spinlock.h"
//------------------------------------------------------------------------------
namespace NST
{
namespace utils
{
template
class Queue
{
struct Element // an element of the queue
{
Element* prev;
T data;
};
struct ElementDeleter
{
explicit ElementDeleter(Queue* q = nullptr) noexcept
: queue{q}
{
}
void operator()(T* const pointer) const
{
if(pointer /*&& queue - dont check - optimization*/)
{
queue->deallocate(pointer);
}
}
Queue* queue;
};
public:
using Ptr = std::unique_ptr;
class List // List of elements for client code
{
public:
inline explicit List(Queue& q)
: queue{&q}
{
ptr = queue->pop_list();
}
List(const List&) = delete;
List& operator=(const List&) = delete;
~List()
{
while(ptr)
{
free_current();
}
}
operator bool() const { return ptr; } // is empty?
const T& data() const { return ptr->data; } // get data
Ptr get_current() // return element and switch to next
{
Element* tmp{ptr};
ptr = ptr->prev;
return Ptr{&tmp->data, ElementDeleter{queue}};
}
void free_current() // deallocate element and switch to next
{
Element* tmp{ptr->prev};
queue->deallocate(ptr);
ptr = tmp;
}
private:
Element* ptr;
Queue* queue;
};
Queue(uint32_t size, uint32_t limit)
: last{nullptr}
, first{nullptr}
{
allocator.init_allocation(sizeof(Element), size, limit);
}
~Queue()
{
List list{*this}; // deallocate items by destructor of List
}
T* allocate()
{
static_assert(std::is_nothrow_constructible::value,
"The construction of T must not to throw any exception");
Spinlock::Lock lock{a_spinlock};
Element* e{(Element*)allocator.allocate()}; // may throw std::bad_alloc
return ::new(&(e->data)) T; // placement construction T
}
void deallocate(T* ptr)
{
ptr->~T(); // placement construction was used
Element* e{(Element*)(((char*)ptr) - offsetof(Element, data))};
deallocate(e);
}
void push(T* ptr)
{
Element* e{(Element*)(((char*)ptr) - offsetof(Element, data))};
Spinlock::Lock lock{q_spinlock};
if(last)
{
last->prev = e;
last = e;
}
else // queue is empty
{
last = first = e;
}
}
Element* pop_list() // take out list of all queued elements
{
Element* list{nullptr};
if(last)
{
Spinlock::Lock lock{q_spinlock};
if(last)
{
list = first;
last->prev = nullptr; // set end of list
last = first = nullptr;
}
}
return list;
}
private:
// accessible from Queue::List and Queue::Ptr
void deallocate(Element* e)
{
Spinlock::Lock lock{a_spinlock};
allocator.deallocate(e);
}
BlockAllocator allocator;
Spinlock a_spinlock; // for allocate/deallocate
Spinlock q_spinlock; // for queue push/pop
// queue empty: last->nullptr<-first
// queue filled: last->e<-e<-e<-e<-first
// queue push(i): last->i<-e<-e<-e<-e<-first
Element* last;
Element* first;
};
} // namespace utils
} // namespace NST
//------------------------------------------------------------------------------
#endif // QUEUE_H
//------------------------------------------------------------------------------