dust3d/thirdparty/cgal/CGAL-4.13/include/CGAL/Spatial_lock_grid_3.h

629 lines
17 KiB
C++
Executable File

// Copyright (c) 2012 INRIA Sophia-Antipolis (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org); you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 3 of the License,
// or (at your option) any later version.
//
// Licensees holding a valid commercial license may use this file in
// accordance with the commercial license agreement provided with the software.
//
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// $URL$
// $Id$
// SPDX-License-Identifier: LGPL-3.0+
//
// Author(s) : Clement Jamin
#ifndef CGAL_STL_EXTENSION_SPATIAL_LOCK_GRID_3_H
#define CGAL_STL_EXTENSION_SPATIAL_LOCK_GRID_3_H
#ifdef CGAL_LINKED_WITH_TBB
#include <CGAL/Bbox_3.h>
#include <boost/bind.hpp>
#include <tbb/atomic.h>
#if TBB_IMPLEMENT_CPP0X
# include <tbb/compat/thread>
#else
# include <thread>
#endif
#include <tbb/enumerable_thread_specific.h>
#include <algorithm>
#include <vector>
namespace CGAL {
struct Tag_no_lock {};
struct Tag_non_blocking {};
struct Tag_priority_blocking {};
//*****************************************************************************
// class Spatial_lock_grid_base_3
// (Uses Curiously recurring template pattern)
//*****************************************************************************
template <typename Derived>
class Spatial_lock_grid_base_3
{
private:
static bool *init_TLS_grid(int num_cells_per_axis)
{
int num_cells = num_cells_per_axis*
num_cells_per_axis*num_cells_per_axis;
bool *local_grid = new bool[num_cells];
for (int i = 0 ; i < num_cells ; ++i)
local_grid[i] = false;
return local_grid;
}
public:
bool *get_thread_local_grid()
{
return m_tls_grids.local();
}
void set_bbox(const Bbox_3 &bbox)
{
// Compute resolutions
m_bbox = bbox;
double n = static_cast<double>(m_num_grid_cells_per_axis);
m_resolution_x = n / (bbox.xmax() - bbox.xmin());
m_resolution_y = n / (bbox.ymax() - bbox.ymin());
m_resolution_z = n / (bbox.zmax() - bbox.zmin());
#ifdef CGAL_CONCURRENT_MESH_3_VERBOSE
std::cerr << "Locking data structure Bounding Box = "
<< "[" << bbox.xmin() << ", " << bbox.xmax() << "], "
<< "[" << bbox.ymin() << ", " << bbox.ymax() << "], "
<< "[" << bbox.zmin() << ", " << bbox.zmax() << "]"
<< std::endl;
#endif
}
const Bbox_3 &get_bbox() const
{
return m_bbox;
}
bool is_locked_by_this_thread(int cell_index)
{
return get_thread_local_grid()[cell_index];
}
template <typename P3>
bool is_locked(const P3 &point)
{
return is_cell_locked(get_grid_index(point));
}
template <typename P3>
bool is_locked_by_this_thread(const P3 &point)
{
return get_thread_local_grid()[get_grid_index(point)];
}
bool try_lock(int cell_index)
{
return try_lock<false>(cell_index);
}
template <bool no_spin>
bool try_lock(int cell_index)
{
return get_thread_local_grid()[cell_index]
|| try_lock_cell<no_spin>(cell_index);
}
bool try_lock(int index_x, int index_y, int index_z, int lock_radius)
{
return try_lock<false>(index_x, index_y, index_z, lock_radius);
}
template <bool no_spin>
bool try_lock(int index_x, int index_y, int index_z, int lock_radius)
{
if (lock_radius == 0)
{
int index_to_lock =
index_z*m_num_grid_cells_per_axis*m_num_grid_cells_per_axis
+ index_y*m_num_grid_cells_per_axis
+ index_x;
return try_lock<no_spin>(index_to_lock);
}
else
{
// We have to lock the square
std::vector<int> locked_cells_tmp;
// For each cell inside the square
for (int i = (std::max)(0, index_x-lock_radius) ;
i <= (std::min)(m_num_grid_cells_per_axis - 1, index_x+lock_radius) ;
++i)
{
for (int j = (std::max)(0, index_y-lock_radius) ;
j <= (std::min)(m_num_grid_cells_per_axis - 1, index_y+lock_radius) ;
++j)
{
for (int k = (std::max)(0, index_z-lock_radius) ;
k <= (std::min)(m_num_grid_cells_per_axis - 1, index_z+lock_radius) ;
++k)
{
int index_to_lock =
k*m_num_grid_cells_per_axis*m_num_grid_cells_per_axis
+ j*m_num_grid_cells_per_axis
+ i;
// Try to lock it
if (try_lock<no_spin>(index_to_lock))
{
locked_cells_tmp.push_back(index_to_lock);
}
else
{
// failed => we unlock already locked cells and return false
std::vector<int>::const_iterator it = locked_cells_tmp.begin();
std::vector<int>::const_iterator it_end = locked_cells_tmp.end();
for( ; it != it_end ; ++it)
{
unlock(*it);
}
return false;
}
}
}
}
return true;
}
}
bool try_lock(int cell_index, int lock_radius)
{
return try_lock<false>(cell_index, lock_radius);
}
template <bool no_spin>
bool try_lock(int cell_index, int lock_radius)
{
if (lock_radius == 0)
{
return try_lock<no_spin>(cell_index);
}
else
{
int index_z = cell_index/(m_num_grid_cells_per_axis*m_num_grid_cells_per_axis);
cell_index -= index_z*m_num_grid_cells_per_axis*m_num_grid_cells_per_axis;
int index_y = cell_index/m_num_grid_cells_per_axis;
cell_index -= index_y*m_num_grid_cells_per_axis;
int index_x = cell_index;
return try_lock<no_spin>(index_x, index_y, index_z, lock_radius);
}
}
// P3 must provide .x(), .y(), .z()
template <typename P3>
bool try_lock(const P3 &point, int lock_radius = 0)
{
return try_lock<false, P3>(point, lock_radius);
}
// P3 must provide .x(), .y(), .z()
template <bool no_spin, typename P3>
bool try_lock(const P3 &point, int lock_radius = 0)
{
// Compute index on grid
int index_x = static_cast<int>( (CGAL::to_double(point.x()) - m_bbox.xmin()) * m_resolution_x);
//index_x = std::max( 0, std::min(index_x, m_num_grid_cells_per_axis - 1) );
index_x =
(index_x < 0 ?
0
: (index_x >= m_num_grid_cells_per_axis ?
m_num_grid_cells_per_axis - 1
: index_x
)
);
int index_y = static_cast<int>( (CGAL::to_double(point.y()) - m_bbox.ymin()) * m_resolution_y);
//index_y = std::max( 0, std::min(index_y, m_num_grid_cells_per_axis - 1) );
index_y =
(index_y < 0 ?
0
: (index_y >= m_num_grid_cells_per_axis ?
m_num_grid_cells_per_axis - 1
: index_y
)
);
int index_z = static_cast<int>( (CGAL::to_double(point.z()) - m_bbox.zmin()) * m_resolution_z);
//index_z = std::max( 0, std::min(index_z, m_num_grid_cells_per_axis - 1) );
index_z =
(index_z < 0 ?
0
: (index_z >= m_num_grid_cells_per_axis ?
m_num_grid_cells_per_axis - 1
: index_z
)
);
if (lock_radius == 0)
{
int index =
index_z*m_num_grid_cells_per_axis*m_num_grid_cells_per_axis
+ index_y*m_num_grid_cells_per_axis
+ index_x;
return try_lock<no_spin>(index);
}
else
{
return try_lock<no_spin>(index_x, index_y, index_z, lock_radius);
}
}
void unlock(int cell_index)
{
// Unlock lock and shared grid
unlock_cell(cell_index);
get_thread_local_grid()[cell_index] = false;
}
void unlock_all_points_locked_by_this_thread()
{
std::vector<int> &tls_locked_cells = m_tls_locked_cells.local();
std::vector<int>::const_iterator it = tls_locked_cells.begin();
std::vector<int>::const_iterator it_end = tls_locked_cells.end();
for( ; it != it_end ; ++it)
{
// If we still own the lock
int cell_index = *it;
if (get_thread_local_grid()[cell_index] == true)
unlock(cell_index);
}
tls_locked_cells.clear();
}
void unlock_all_tls_locked_cells_but_one(int cell_index_to_keep_locked)
{
std::vector<int> &tls_locked_cells = m_tls_locked_cells.local();
std::vector<int>::const_iterator it = tls_locked_cells.begin();
std::vector<int>::const_iterator it_end = tls_locked_cells.end();
bool cell_to_keep_found = false;
for( ; it != it_end ; ++it)
{
// If we still own the lock
int cell_index = *it;
if (get_thread_local_grid()[cell_index] == true)
{
if (cell_index == cell_index_to_keep_locked)
cell_to_keep_found = true;
else
unlock(cell_index);
}
}
tls_locked_cells.clear();
if (cell_to_keep_found)
tls_locked_cells.push_back(cell_index_to_keep_locked);
}
template <typename P3>
void unlock_all_tls_locked_locations_but_one_point(const P3 &point)
{
unlock_all_tls_locked_cells_but_one(get_grid_index(point));
}
bool check_if_all_cells_are_unlocked()
{
int num_cells = m_num_grid_cells_per_axis*
m_num_grid_cells_per_axis*m_num_grid_cells_per_axis;
bool unlocked = true;
for (int i = 0 ; unlocked && i < num_cells ; ++i)
unlocked = !is_cell_locked(i);
return unlocked;
}
bool check_if_all_tls_cells_are_unlocked()
{
int num_cells = m_num_grid_cells_per_axis*
m_num_grid_cells_per_axis*m_num_grid_cells_per_axis;
bool unlocked = true;
for (int i = 0 ; unlocked && i < num_cells ; ++i)
unlocked = (get_thread_local_grid()[i] == false);
return unlocked;
}
protected:
// Constructor
Spatial_lock_grid_base_3(const Bbox_3 &bbox,
int num_grid_cells_per_axis)
: m_num_grid_cells_per_axis(num_grid_cells_per_axis),
m_tls_grids(boost::bind(init_TLS_grid, num_grid_cells_per_axis))
{
set_bbox(bbox);
}
/// Destructor
~Spatial_lock_grid_base_3()
{
for( TLS_grid::iterator it_grid = m_tls_grids.begin() ;
it_grid != m_tls_grids.end() ;
++it_grid )
{
delete [] *it_grid;
}
}
template <typename P3>
int get_grid_index(const P3& point) const
{
// Compute indices on grid
int index_x = static_cast<int>( (CGAL::to_double(point.x()) - m_bbox.xmin()) * m_resolution_x);
//index_x = std::max( 0, std::min(index_x, m_num_grid_cells_per_axis - 1) );
index_x =
(index_x < 0 ?
0
: (index_x >= m_num_grid_cells_per_axis ?
m_num_grid_cells_per_axis - 1
: index_x
)
);
int index_y = static_cast<int>( (CGAL::to_double(point.y()) - m_bbox.ymin()) * m_resolution_y);
//index_y = std::max( 0, std::min(index_y, m_num_grid_cells_per_axis - 1) );
index_y =
(index_y < 0 ?
0
: (index_y >= m_num_grid_cells_per_axis ?
m_num_grid_cells_per_axis - 1
: index_y
)
);
int index_z = static_cast<int>( (CGAL::to_double(point.z()) - m_bbox.zmin()) * m_resolution_z);
//index_z = std::max( 0, std::min(index_z, m_num_grid_cells_per_axis - 1) );
index_z =
(index_z < 0 ?
0
: (index_z >= m_num_grid_cells_per_axis ?
m_num_grid_cells_per_axis - 1
: index_z
)
);
return
index_z*m_num_grid_cells_per_axis*m_num_grid_cells_per_axis
+ index_y*m_num_grid_cells_per_axis
+ index_x;
}
bool is_cell_locked(int cell_index)
{
return static_cast<Derived*>(this)->is_cell_locked_impl(cell_index);
}
bool try_lock_cell(int cell_index)
{
return try_lock_cell<false>(cell_index);
}
template <bool no_spin>
bool try_lock_cell(int cell_index)
{
return static_cast<Derived*>(this)
->template try_lock_cell_impl<no_spin>(cell_index);
}
void unlock_cell(int cell_index)
{
static_cast<Derived*>(this)->unlock_cell_impl(cell_index);
}
int m_num_grid_cells_per_axis;
Bbox_3 m_bbox;
double m_resolution_x;
double m_resolution_y;
double m_resolution_z;
// TLS
typedef tbb::enumerable_thread_specific<
bool*,
tbb::cache_aligned_allocator<bool*>,
tbb::ets_key_per_instance> TLS_grid;
typedef tbb::enumerable_thread_specific<std::vector<int> > TLS_locked_cells;
TLS_grid m_tls_grids;
TLS_locked_cells m_tls_locked_cells;
};
//*****************************************************************************
// class Spatial_lock_grid_3
//*****************************************************************************
template <typename Grid_lock_tag = Tag_priority_blocking>
class Spatial_lock_grid_3;
//*****************************************************************************
// class Spatial_lock_grid_3<Tag_non_blocking>
//*****************************************************************************
template <>
class Spatial_lock_grid_3<Tag_non_blocking>
: public Spatial_lock_grid_base_3<
Spatial_lock_grid_3<Tag_non_blocking> >
{
typedef Spatial_lock_grid_base_3<
Spatial_lock_grid_3<Tag_non_blocking> > Base;
public:
// Constructors
Spatial_lock_grid_3(const Bbox_3 &bbox, int num_grid_cells_per_axis)
: Base(bbox, num_grid_cells_per_axis)
{
int num_cells =
num_grid_cells_per_axis*num_grid_cells_per_axis*num_grid_cells_per_axis;
m_grid.resize(num_cells);
// Initialize grid (useless?)
for (int i = 0 ; i < num_cells ; ++i)
m_grid[i] = false;
}
~Spatial_lock_grid_3()
{
}
bool is_cell_locked_impl(int cell_index)
{
return (m_grid[cell_index] == true);
}
template <bool no_spin>
bool try_lock_cell_impl(int cell_index)
{
bool old_value = m_grid[cell_index].compare_and_swap(true, false);
if (old_value == false)
{
get_thread_local_grid()[cell_index] = true;
m_tls_locked_cells.local().push_back(cell_index);
return true;
}
return false;
}
void unlock_cell_impl(int cell_index)
{
m_grid[cell_index] = false;
}
protected:
std::vector<tbb::atomic<bool> > m_grid;
};
//*****************************************************************************
// class Spatial_lock_grid_3<Tag_priority_blocking>
//*****************************************************************************
template <>
class Spatial_lock_grid_3<Tag_priority_blocking>
: public Spatial_lock_grid_base_3<Spatial_lock_grid_3<Tag_priority_blocking> >
{
typedef Spatial_lock_grid_base_3<
Spatial_lock_grid_3<Tag_priority_blocking> > Base;
public:
// Constructors
Spatial_lock_grid_3(const Bbox_3 &bbox, int num_grid_cells_per_axis)
: Base(bbox, num_grid_cells_per_axis),
m_tls_thread_priorities(init_TLS_thread_priorities)
{
int num_cells =
num_grid_cells_per_axis*num_grid_cells_per_axis*num_grid_cells_per_axis;
m_grid.resize(num_cells);
// Explicitly initialize the atomics
std::vector<tbb::atomic<unsigned int> >::iterator it = m_grid.begin();
std::vector<tbb::atomic<unsigned int> >::iterator it_end = m_grid.end();
for ( ; it != it_end ; ++it)
*it = 0;
}
/// Destructor
~Spatial_lock_grid_3()
{
}
bool is_cell_locked_impl(int cell_index)
{
return (m_grid[cell_index] != 0);
}
template <bool no_spin>
bool try_lock_cell_impl(int cell_index)
{
unsigned int this_thread_priority = m_tls_thread_priorities.local();
// NO SPIN
if (no_spin)
{
unsigned int old_value =
m_grid[cell_index].compare_and_swap(this_thread_priority, 0);
if (old_value == 0)
{
get_thread_local_grid()[cell_index] = true;
m_tls_locked_cells.local().push_back(cell_index);
return true;
}
}
// SPIN
else
{
for(;;)
{
unsigned int old_value =
m_grid[cell_index].compare_and_swap(this_thread_priority, 0);
if (old_value == 0)
{
get_thread_local_grid()[cell_index] = true;
m_tls_locked_cells.local().push_back(cell_index);
return true;
}
else if (old_value > this_thread_priority)
{
// Another "more prioritary" thread owns the lock, we back off
return false;
}
else
{
std::this_thread::yield();
}
}
}
return false;
}
void unlock_cell_impl(int cell_index)
{
m_grid[cell_index] = 0;
}
private:
static unsigned int init_TLS_thread_priorities()
{
static tbb::atomic<unsigned int> last_id;
unsigned int id = ++last_id;
// Ensure it is > 0
return (1 + id%((std::numeric_limits<unsigned int>::max)()));
}
protected:
std::vector<tbb::atomic<unsigned int> > m_grid;
typedef tbb::enumerable_thread_specific<unsigned int> TLS_thread_uint_ids;
TLS_thread_uint_ids m_tls_thread_priorities;
};
} //namespace CGAL
#else // !CGAL_LINKED_WITH_TBB
namespace CGAL {
template <typename Grid_lock_tag = void>
class Spatial_lock_grid_3
{
};
}
#endif // CGAL_LINKED_WITH_TBB
#endif // CGAL_STL_EXTENSION_SPATIAL_LOCK_GRID_3_H