1
0
Fork 0
forked from lthn/blockchain
blockchain/contrib/epee/include/cache_helper.h
2025-04-10 22:42:56 +04:00

270 lines
8.3 KiB
C++

// Copyright (c) 2006-2016, Andrey N. Sabelnikov, www.sabelnikov.net
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of the Andrey N. Sabelnikov nor the
// names of its contributors may be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
#pragma once
#include <map>
#include <unordered_map>
#include <list>
#include <thread>
#include "boost/optional.hpp"
#include "syncobj.h"
#include "include_base_utils.h"
namespace epee
{
namespace misc_utils
{
/************************************************************************/
/* */
/************************************************************************/
template<bool is_ordered_type, class t_key, class t_value>
struct container_selector;
template<class t_key, class t_value>
struct container_selector<true, t_key, t_value>
{
typedef std::map<t_key, t_value > container;
};
template<class t_key, class t_value>
struct container_selector<false, t_key, t_value>
{
typedef std::unordered_map<t_key, t_value > container;
};
template<bool is_ordered_container, typename t_key, typename t_value, uint64_t max_elements>
class cache_base
{
uint64_t max_allowed_elements;
std::list<t_key> most_recet_acessed;
typename container_selector<is_ordered_container, t_key, std::pair<t_value, typename std::list<t_key>::iterator> >::container data;
protected:
critical_section m_lock;
public:
cache_base() : max_allowed_elements(max_elements)
{}
size_t size() const
{
return data.size();
}
size_t most_recent_accessed_container_size() const
{
return most_recet_acessed.size();
}
uint64_t get_max_elements() const
{
return max_allowed_elements;
}
void set_max_elements(uint64_t e)
{
max_allowed_elements = e;
}
bool get(const t_key& k, t_value& v)
{
CRITICAL_REGION_LOCAL(m_lock);
auto it = data.find(k);
if (it == data.end())
return false;
most_recet_acessed.splice(most_recet_acessed.begin(), most_recet_acessed, it->second.second);
v = it->second.first;
return true;
}
bool set(const t_key& k, const t_value& v)
{
CRITICAL_REGION_LOCAL(m_lock);
most_recet_acessed.push_front(k);
data[k] = std::pair<t_value, typename std::list<t_key>::iterator>(v, most_recet_acessed.begin());
trim();
return true;
}
void clear()
{
CRITICAL_REGION_LOCAL(m_lock);
data.clear();
most_recet_acessed.clear();
}
bool erase(const t_key& k)
{
CRITICAL_REGION_LOCAL(m_lock);
auto data_it = data.find(k);
if (data_it == data.end())
return false;
most_recet_acessed.erase(data_it->second.second);
data.erase(data_it);
return true;
}
protected:
void trim()
{
CRITICAL_REGION_LOCAL(m_lock);
while (most_recet_acessed.size() > max_allowed_elements)
{
auto data_it = data.find(most_recet_acessed.back());
if (data_it != data.end())
data.erase(data_it);
most_recet_acessed.erase(--most_recet_acessed.end());
}
}
};
class isolation_lock
{
private:
critical_section m_my_lock;
critical_section& m_lock;
boost::optional<std::thread::id> m_current_writer_thread;
public:
isolation_lock() :m_lock(m_my_lock)
{}
isolation_lock(critical_section& lock) :m_lock(lock)
{}
template<typename res_type, typename callback_t>
res_type isolated_access(callback_t cb) const
{
CRITICAL_REGION_LOCAL(m_lock);
if (m_current_writer_thread.is_initialized())
{
//has writer
if (std::this_thread::get_id() != m_current_writer_thread.get())
{
// cache isolated (not allowed)
return cb(false);
}
}
//cache shared(allowed)
return cb(true);
}
template<typename res_type, typename callback_t>
res_type isolated_write_access(callback_t cb) const
{
return isolated_access<res_type>([&](bool is_allowed_cache)
{
if (!is_allowed_cache)
{
ASSERT_MES_AND_THROW("internal error: writer not allowed while it's not in writer transaction");
}
return cb();
});
}
void set_isolation_mode()
{
CRITICAL_REGION_LOCAL(m_lock);
CHECK_AND_ASSERT_THROW_MES(!m_current_writer_thread.is_initialized(), "Isolation mode already enabled for cache");
m_current_writer_thread = std::this_thread::get_id();
}
void reset_isolation_mode()
{
CRITICAL_REGION_LOCAL(m_lock);
CHECK_AND_ASSERT_THROW_MES(m_current_writer_thread.is_initialized(), "Isolation mode already disable for cache");
m_current_writer_thread = boost::optional<std::thread::id>();
}
};
template<bool is_ordered_container, typename t_key, typename t_value, uint64_t max_elements>
class cache_with_write_isolation : public cache_base<is_ordered_container, t_key, t_value, max_elements>
{
typedef cache_base<is_ordered_container, t_key, t_value, max_elements> base_class;
isolation_lock& m_isolation;
public:
cache_with_write_isolation(isolation_lock& isolation) : m_isolation(isolation)
{}
bool get(const t_key& k, t_value& v)
{
return m_isolation.isolated_access<bool>([&](bool allowed_cache)
{
if (allowed_cache)
return base_class::get(k, v);
else
return false;
});
}
bool set(const t_key& k, const t_value& v)
{
return m_isolation.isolated_access<bool>([&] (bool cache_allowed)
{
if (cache_allowed)
return base_class::set(k, v);
return true;
});
}
void clear()
{
m_isolation.isolated_access<bool>([&](bool cache_allowed)
{
if (cache_allowed)
base_class::clear();
return true;
});
}
bool erase(const t_key& k)
{
return m_isolation.isolated_write_access<bool>([&](){return base_class::erase(k); });
}
};
template<bool is_ordered_container, typename t_key, typename t_value, uint64_t max_elements>
class cache_dummy : public cache_base<is_ordered_container, t_key, t_value, max_elements>
{
typedef cache_base<is_ordered_container, t_key, t_value, max_elements> base_class;
isolation_lock& m_isolation;
public:
cache_dummy(isolation_lock& isolation) : m_isolation(isolation){}
bool get(const t_key& k, t_value& v){return false;}
bool set(const t_key& k, const t_value& v){return true;}
void clear(){}
bool erase(const t_key& k){return true;}
};
}
}