/*
Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University
This file is part of the Open Porous Media project (OPM).
OPM 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, either version 3 of the License, or
(at your option) any later version.
OPM 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 OPM. If not, see .
*/
/*
Copyright 2013--2018 James E. McClure, Virginia Polytechnic & State University
This file is part of the Open Porous Media project (OPM).
OPM 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, either version 3 of the License, or
(at your option) any later version.
OPM 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 OPM. If not, see .
*/
#ifndef included_AtomicList_hpp
#define included_AtomicList_hpp
#include
#include
#include
/******************************************************************
* Constructor *
******************************************************************/
template
AtomicList::AtomicList(
size_t capacity, const TYPE &default_value, const COMPARE &comp )
: d_compare( comp ),
d_capacity( capacity ),
d_default( default_value ),
d_objects( new TYPE[capacity] ),
d_N( 0 ),
d_next( new AtomicOperations::int32_atomic[capacity + 1] ),
d_unused( 1 ),
d_N_insert( 0 ),
d_N_remove( 0 )
{
d_next[0] = -1;
for ( size_t i = 0; i < d_capacity; i++ ) {
d_next[i + 1] = -5 - i;
d_objects[i] = d_default;
}
}
template
AtomicList::~AtomicList()
{
delete[] d_objects;
delete[] d_next;
}
/******************************************************************
* Remove an item *
******************************************************************/
template
template
inline TYPE AtomicList::remove( Compare compare, const Args &... args )
{
// Acquiring temporary ownership
int pos = 0;
auto next = lock( 0 );
while ( true ) {
if ( next == -1 ) {
// We have no more entires to search
unlock( pos, -1 );
pos = -1;
break;
}
if ( next < 0 )
throw std::logic_error( "Internal error" );
// Acquire ownership of the next item
int next2 = lock( next );
// Test to see if the object passes compare
bool test = compare( const_cast( d_objects[next - 1] ), args... );
if ( test ) {
// We want to return this object, update next to point to another entry and remove
unlock( next, -3 );
unlock( pos, next2 );
pos = next;
break;
}
// Release the ownership and move on
unlock( pos, next );
pos = next;
next = next2;
}
TYPE rtn( d_default );
if ( pos != -1 ) {
std::swap( rtn, const_cast( d_objects[pos - 1] ) );
put_unused( pos );
AtomicOperations::atomic_decrement( &d_N );
AtomicOperations::atomic_increment( &d_N_remove );
}
return rtn;
}
template
inline TYPE AtomicList::remove_first()
{
TYPE rtn( d_default );
auto next = lock( 0 );
if ( next != -1 ) {
int next2 = lock( next );
unlock( next, -3 );
unlock( 0, next2 );
std::swap( rtn, const_cast( d_objects[next - 1] ) );
put_unused( next );
AtomicOperations::atomic_decrement( &d_N );
AtomicOperations::atomic_increment( &d_N_remove );
} else {
unlock( 0, next );
}
return rtn;
}
/******************************************************************
* Insert an item *
******************************************************************/
template
inline void AtomicList::insert( const TYPE &x )
{
size_t N_used = AtomicOperations::atomic_increment( &d_N );
if ( N_used > d_capacity ) {
AtomicOperations::atomic_decrement( &d_N );
throw std::logic_error( "No room in list" );
}
// Get an index to store the entry
auto index = get_unused();
if ( index < 1 )
throw std::logic_error( "Internal error" );
// Store the object in d_objects
AtomicOperations::atomic_increment( &d_N_insert );
d_objects[index - 1] = x;
d_next[index] = -1;
// Find the position to store and update the next entires
int pos = 0;
auto next = lock( pos );
while ( true ) {
// Get the next item in the list (acquiring temporary ownership)
if ( next == -1 ) {
// We have no more entires to search, store here
unlock( pos, index );
break;
}
// Test to see if the object is < the value being compared
bool test = d_compare.operator()( x, const_cast( d_objects[next - 1] ) );
if ( test ) {
// We want to store this object before next
d_next[index] = next;
unlock( pos, index );
break;
}
// Release the ownership and move on
int last = pos;
pos = next;
next = lock( next );
unlock( last, pos );
}
}
/******************************************************************
* Check the internal structures of the list *
* This is mostly thread-safe, but blocks all threads *
******************************************************************/
template
inline bool AtomicList::check()
{
// Get the lock and check for any other threads modifying the list
auto start = lock( 0 );
std::this_thread::sleep_for( std::chrono::microseconds( 100 ) );
// Perform the checks on the list
bool pass = true;
int N1 = 0;
int N2 = 0;
int N_unused = 0;
int N_tail = 0;
for ( size_t i = 0; i < d_capacity; i++ ) {
if ( d_objects[i] != d_default )
N1++;
}
for ( size_t i = 0; i <= d_capacity; i++ ) {
int next = i == 0 ? start : d_next[i];
if ( next > 0 ) {
N2++;
} else if ( next < -3 ) {
N_unused++;
} else if ( next == -1 ) {
N_tail++;
} else {
pass = false;
}
}
pass = pass && N_tail == 1 && N1 == d_N && N2 == d_N && N_unused + d_N == (int) d_capacity;
int it = 0;
int pos = 0;
while ( true ) {
int next = pos == 0 ? start : d_next[pos];
if ( next == -1 )
break;
pos = next;
it++;
}
pass = pass && it == d_N;
// Unlock the list and return the results
unlock( 0, start );
return pass;
}
/******************************************************************
* MemoryPool *
******************************************************************/
template
MemoryPool::MemoryPool( size_t size )
{
static_assert( sizeof( TYPE ) >= sizeof( int ),
"sizeof(TYPE) must be >= sizeof(int) to ensure proper operation" );
static_assert( sizeof( TYPE ) >= sizeof( INT_TYPE ),
"sizeof(TYPE) must be >= sizeof(INT_TYPE) to ensure proper operation" );
d_objects = reinterpret_cast( malloc( sizeof( TYPE ) * size ) );
d_next = 1;
for ( size_t i = 0; i < size; i++ )
reinterpret_cast( d_objects[i] ) = i + 1;
reinterpret_cast( d_objects[size - 1] ) = -1;
}
template
MemoryPool::~MemoryPool()
{
free( const_cast( d_objects ) );
d_objects = nullptr;
}
template
inline TYPE *MemoryPool::allocate()
{
AtomicOperations::int32_atomic i = 0;
while ( i == 0 )
AtomicOperations::atomic_swap( &d_next, &i );
TYPE *ptr = nullptr;
if ( i != -1 ) {
INT_TYPE j = reinterpret_cast( d_objects[i - 1] );
ptr = const_cast( &d_objects[i - 1] );
new ( ptr ) TYPE();
i = j + 1;
}
AtomicOperations::atomic_fetch_and_or( &d_next, i );
return ptr;
}
template
inline void MemoryPool::free( TYPE *ptr )
{
ptr->~TYPE();
AtomicOperations::int32_atomic i = 0;
while ( i == 0 )
AtomicOperations::atomic_swap( &d_next, &i );
reinterpret_cast( *ptr ) = i - 1;
i = ptr - d_objects + 1;
AtomicOperations::atomic_fetch_and_or( &d_next, i );
}
#endif