Only keep reader lock on shared memory during queries.

This commit is contained in:
Patrick Niklaus
2016-10-12 01:09:20 +02:00
committed by Patrick Niklaus
parent c69545c47a
commit 847f530c8e
7 changed files with 174 additions and 301 deletions
+47 -34
View File
@@ -5,10 +5,11 @@
#include "storage/shared_datatype.hpp"
#include "storage/shared_memory.hpp"
#include "storage/shared_barriers.hpp"
#include <boost/interprocess/sync/named_upgradable_mutex.hpp>
#include <boost/thread/locks.hpp>
#include <boost/thread/lock_types.hpp>
#include <boost/thread/locks.hpp>
#include <boost/thread/shared_mutex.hpp>
#include <memory>
@@ -31,7 +32,7 @@ class DataWatchdog
public:
DataWatchdog()
: shared_barriers{std::make_shared<storage::SharedBarriers>()},
shared_regions(storage::makeSharedMemoryView(storage::CURRENT_REGIONS)),
shared_regions(storage::makeSharedMemory(storage::CURRENT_REGIONS)),
current_timestamp{storage::LAYOUT_NONE, storage::DATA_NONE, 0}
{
}
@@ -42,26 +43,13 @@ class DataWatchdog
return storage::SharedMemory::RegionExists(storage::CURRENT_REGIONS);
}
// Check if it might be worth to try to aquire a exclusive lock
bool HasNewRegion() const
{
const boost::interprocess::sharable_lock<boost::interprocess::named_upgradable_mutex> lock(
shared_barriers->current_regions_mutex);
const auto shared_timestamp =
static_cast<const storage::SharedDataTimestamp *>(shared_regions->Ptr());
// sanity check: if the timestamp is the same all other data needs to be the same as well
BOOST_ASSERT(shared_timestamp->timestamp != current_timestamp.timestamp ||
(shared_timestamp->layout == current_timestamp.layout &&
shared_timestamp->data == current_timestamp.data));
return shared_timestamp->timestamp != current_timestamp.timestamp;
}
using RegionsLock =
boost::interprocess::sharable_lock<boost::interprocess::named_sharable_mutex>;
using LockAndFacade = std::pair<RegionsLock, std::shared_ptr<datafacade::BaseDataFacade>>;
// This will either update the contens of facade or just leave it as is
// if the update was already done by another thread
void MaybeLoadNewRegion(std::shared_ptr<datafacade::BaseDataFacade> &facade)
LockAndFacade GetDataFacade()
{
const boost::interprocess::sharable_lock<boost::interprocess::named_upgradable_mutex> lock(
shared_barriers->current_regions_mutex);
@@ -69,31 +57,55 @@ class DataWatchdog
const auto shared_timestamp =
static_cast<const storage::SharedDataTimestamp *>(shared_regions->Ptr());
const auto get_locked_facade = [this, shared_timestamp]() {
if (current_timestamp.data == storage::DATA_1)
{
BOOST_ASSERT(current_timestamp.layout == storage::LAYOUT_1);
return std::make_pair(RegionsLock(shared_barriers->regions_1_mutex), facade);
}
else
{
BOOST_ASSERT(current_timestamp.layout == storage::LAYOUT_2);
BOOST_ASSERT(current_timestamp.data == storage::DATA_2);
return std::make_pair(RegionsLock(shared_barriers->regions_2_mutex), facade);
}
};
// this blocks handle the common case when there is no data update -> we will only need a
// shared lock
{
boost::shared_lock<boost::shared_mutex> facade_lock(facade_mutex);
if (shared_timestamp->timestamp == current_timestamp.timestamp)
{
BOOST_ASSERT(shared_timestamp->layout == current_timestamp.layout);
BOOST_ASSERT(shared_timestamp->data == current_timestamp.data);
return get_locked_facade();
}
}
// if we reach this code there is a data update to be made. multiple
// requests can reach this, but only ever one goes through at a time.
boost::upgrade_lock<boost::shared_mutex> facade_lock(facade_mutex);
// if more then one request tried to aquire the write lock
// we might get overtaken before we actually do the writing
// in that case we don't modify anthing
if (shared_timestamp->timestamp == current_timestamp.timestamp)
{
BOOST_ASSERT(shared_timestamp->layout == current_timestamp.layout);
BOOST_ASSERT(shared_timestamp->data == current_timestamp.data);
}
// this thread has won and can update the data
else
{
boost::upgrade_to_unique_lock<boost::upgrade_mutex> unique_facade_lock(facade_lock);
current_timestamp = *shared_timestamp;
// TODO remove once we allow for more then one SharedMemoryFacade at the same time
// at this point no other query is allowed to reference this facade!
// the old facade will die exactly here
BOOST_ASSERT(!facade || facade.use_count() == 1);
facade = std::make_shared<datafacade::SharedDataFacade>(shared_barriers,
current_timestamp.layout,
current_timestamp.data,
current_timestamp.timestamp);
return get_locked_facade();
}
// this thread has won and can update the data
boost::upgrade_to_unique_lock<boost::upgrade_mutex> unique_facade_lock(facade_lock);
current_timestamp = *shared_timestamp;
facade = std::make_shared<datafacade::SharedDataFacade>(
current_timestamp.layout, current_timestamp.data, current_timestamp.timestamp);
return get_locked_facade();
}
private:
@@ -105,6 +117,7 @@ class DataWatchdog
std::unique_ptr<storage::SharedMemory> shared_regions;
mutable boost::shared_mutex facade_mutex;
std::shared_ptr<datafacade::SharedDataFacade> facade;
storage::SharedDataTimestamp current_timestamp;
};
}
@@ -3,7 +3,6 @@
// implements all data storage when shared memory _IS_ used
#include "storage/shared_barriers.hpp"
#include "storage/shared_datatype.hpp"
#include "storage/shared_memory.hpp"
#include "engine/datafacade/datafacade_base.hpp"
@@ -66,11 +65,9 @@ class SharedDataFacade final : public BaseDataFacade
storage::SharedDataLayout *data_layout;
char *shared_memory;
std::shared_ptr<storage::SharedBarriers> shared_barriers;
storage::SharedDataType layout_region;
storage::SharedDataType data_region;
unsigned shared_timestamp;
boost::interprocess::sharable_lock<boost::interprocess::named_sharable_mutex> regions_lock;
unsigned m_check_sum;
std::unique_ptr<QueryGraph> m_query_graph;
@@ -384,26 +381,22 @@ class SharedDataFacade final : public BaseDataFacade
public:
virtual ~SharedDataFacade() {}
SharedDataFacade(const std::shared_ptr<storage::SharedBarriers> &shared_barriers_,
storage::SharedDataType layout_region_,
SharedDataFacade(storage::SharedDataType layout_region_,
storage::SharedDataType data_region_,
unsigned shared_timestamp_)
: shared_barriers(shared_barriers_),
layout_region(layout_region_), data_region(data_region_),
shared_timestamp(shared_timestamp_),
regions_lock(layout_region == storage::LAYOUT_1 ? shared_barriers->regions_1_mutex
: shared_barriers->regions_2_mutex)
: layout_region(layout_region_), data_region(data_region_),
shared_timestamp(shared_timestamp_)
{
util::SimpleLogger().Write(logDEBUG) << "Loading new data with shared timestamp "
<< shared_timestamp;
BOOST_ASSERT(storage::SharedMemory::RegionExists(layout_region));
m_layout_memory = storage::makeSharedMemoryView(layout_region);
m_layout_memory = storage::makeSharedMemory(layout_region);
data_layout = static_cast<storage::SharedDataLayout *>(m_layout_memory->Ptr());
BOOST_ASSERT(storage::SharedMemory::RegionExists(data_region));
m_large_memory = storage::makeSharedMemoryView(data_region);
m_large_memory = storage::makeSharedMemory(data_region);
shared_memory = (char *)(m_large_memory->Ptr());
LoadGraph();
+3 -2
View File
@@ -85,8 +85,9 @@ class Engine final
std::unique_ptr<plugins::MatchPlugin> match_plugin;
std::unique_ptr<plugins::TilePlugin> tile_plugin;
// reading and setting this is protected by locking in the watchdog
mutable std::shared_ptr<datafacade::BaseDataFacade> query_data_facade;
// note in case of shared memory this will be empty, since the watchdog
// will provide us with the up-to-date facade
std::shared_ptr<datafacade::BaseDataFacade> immutable_data_facade;
};
}
}
+23
View File
@@ -183,6 +183,29 @@ struct SharedDataTimestamp
unsigned timestamp;
};
inline std::string regionToString(const SharedDataType region)
{
switch (region)
{
case CURRENT_REGIONS:
return "CURRENT_REGIONS";
case LAYOUT_1:
return "LAYOUT_1";
case DATA_1:
return "DATA_1";
case LAYOUT_2:
return "LAYOUT_2";
case DATA_2:
return "DATA_2";
case LAYOUT_NONE:
return "LAYOUT_NONE";
case DATA_NONE:
return "DATA_NONE";
default:
return "INVALID_REGION";
}
}
static_assert(sizeof(block_id_to_name) / sizeof(*block_id_to_name) == SharedDataLayout::NUM_BLOCKS,
"Number of blocks needs to match the number of Block names.");
}
+29 -144
View File
@@ -42,40 +42,6 @@ struct OSRMLockFile
#ifndef _WIN32
class SharedMemory
{
// Remove shared memory on destruction
class shm_remove
{
private:
int m_shmid;
public:
shm_remove() : m_shmid(INT_MAX) {}
shm_remove(int shmid) : m_shmid(shmid) {}
shm_remove(shm_remove &&other) : m_shmid(std::move(other.m_shmid)) {}
shm_remove(const shm_remove &) = delete;
shm_remove &operator=(const shm_remove &) = delete;
shm_remove &operator=(const shm_remove &&other)
{
m_shmid = other.m_shmid;
return *this;
}
~shm_remove()
{
if (m_shmid != INT_MAX)
{
util::SimpleLogger().Write(logDEBUG) << "automatic memory deallocation of "
<< m_shmid;
if (!boost::interprocess::xsi_shared_memory::remove(m_shmid))
{
util::SimpleLogger().Write(logDEBUG) << "could not deallocate id " << m_shmid;
}
}
}
};
public:
void *Ptr() const { return region.get_address(); }
@@ -86,28 +52,24 @@ class SharedMemory
SharedMemory(const boost::filesystem::path &lock_file,
const IdentifierT id,
const uint64_t size = 0,
bool read_write = false,
bool remove_prev = true,
bool owner = true)
bool read_write = false)
: key(lock_file.string().c_str(), id)
{
const auto access =
read_write ? boost::interprocess::read_write : boost::interprocess::read_only;
// open only
if (0 == size)
{ // read_only
{
shm = boost::interprocess::xsi_shared_memory(boost::interprocess::open_only, key);
util::SimpleLogger().Write(logDEBUG) << "opening " << shm.get_shmid() << " from id "
<< id;
region = boost::interprocess::mapped_region(
shm,
(read_write ? boost::interprocess::read_write : boost::interprocess::read_only));
region = boost::interprocess::mapped_region(shm, access);
}
// open or create
else
{ // writeable pointer
// remove previously allocated mem
if (remove_prev)
{
Remove(key);
}
{
shm = boost::interprocess::xsi_shared_memory(
boost::interprocess::open_or_create, key, size);
util::SimpleLogger().Write(logDEBUG) << "opening/creating " << shm.get_shmid()
@@ -121,12 +83,7 @@ class SharedMemory
}
}
#endif
region = boost::interprocess::mapped_region(shm, boost::interprocess::read_write);
if (owner)
{
remover = shm_remove{shm.get_shmid()};
}
region = boost::interprocess::mapped_region(shm, access);
}
}
@@ -161,36 +118,28 @@ class SharedMemory
{
boost::interprocess::xsi_shared_memory shm(boost::interprocess::open_only, key);
}
catch (...)
{
result = false;
}
return result;
}
static bool Remove(const boost::interprocess::xsi_key &key)
{
bool ret = false;
try
{
boost::interprocess::xsi_shared_memory xsi(boost::interprocess::open_only, key);
util::SimpleLogger().Write(logDEBUG) << "deallocating prev memory " << xsi.get_shmid();
ret = boost::interprocess::xsi_shared_memory::remove(xsi.get_shmid());
}
catch (const boost::interprocess::interprocess_exception &e)
{
if (e.get_error_code() != boost::interprocess::not_found_error)
{
throw;
}
result = false;
}
return ret;
return result;
}
static bool Remove(const boost::interprocess::xsi_key &key)
{
boost::interprocess::xsi_shared_memory xsi(boost::interprocess::open_only, key);
util::SimpleLogger().Write(logDEBUG) << "deallocating prev memory " << xsi.get_shmid();
return boost::interprocess::xsi_shared_memory::remove(xsi.get_shmid());
}
boost::interprocess::xsi_key key;
boost::interprocess::xsi_shared_memory shm;
boost::interprocess::mapped_region region;
shm_remove remover;
};
#else
// Windows - specific code
@@ -198,31 +147,6 @@ class SharedMemory
{
SharedMemory(const SharedMemory &) = delete;
SharedMemory &operator=(const SharedMemory &) = delete;
// Remove shared memory on destruction
class shm_remove
{
private:
char *m_shmid;
public:
shm_remove() : m_shmid(nullptr) {}
shm_remove(char *id) : m_shmid(id) {}
shm_remove(const shm_remove &) = delete;
shm_remove &operator=(const shm_remove &) = delete;
~shm_remove()
{
if (m_shmid != nullptr)
{
util::SimpleLogger().Write(logDEBUG) << "automatic memory deallocation";
if (!boost::interprocess::shared_memory_object::remove(m_shmid))
{
util::SimpleLogger().Write(logDEBUG) << "could not deallocate id " << m_shmid;
}
}
}
};
public:
void *Ptr() const { return region.get_address(); }
@@ -230,36 +154,25 @@ class SharedMemory
SharedMemory(const boost::filesystem::path &lock_file,
const int id,
const uint64_t size = 0,
bool read_write = false,
bool remove_prev = true,
bool owner = false)
bool read_write = false)
{
sprintf(key, "%s.%d", "osrm.lock", id);
auto access = read_write ? boost::interprocess::read_write : boost::interprocess::read_only;
if (0 == size)
{ // read_only
shm = boost::interprocess::shared_memory_object(
boost::interprocess::open_only,
key,
read_write ? boost::interprocess::read_write : boost::interprocess::read_only);
region = boost::interprocess::mapped_region(
shm, read_write ? boost::interprocess::read_write : boost::interprocess::read_only);
region = boost::interprocess::mapped_region(shm, access);
}
else
{ // writeable pointer
// remove previously allocated mem
if (remove_prev)
{
Remove(key);
}
shm = boost::interprocess::shared_memory_object(
boost::interprocess::open_or_create, key, boost::interprocess::read_write);
shm.truncate(size);
region = boost::interprocess::mapped_region(shm, boost::interprocess::read_write);
region = boost::interprocess::mapped_region(shm, access);
if (owner)
{
remover = shm_remover{key};
}
util::SimpleLogger().Write(logDEBUG) << "writeable memory allocated " << size
<< " bytes";
}
@@ -308,35 +221,19 @@ class SharedMemory
static bool Remove(char *key)
{
bool ret = false;
try
{
util::SimpleLogger().Write(logDEBUG) << "deallocating prev memory for key " << key;
ret = boost::interprocess::shared_memory_object::remove(key);
}
catch (const boost::interprocess::interprocess_exception &e)
{
if (e.get_error_code() != boost::interprocess::not_found_error)
{
throw;
}
}
return ret;
util::SimpleLogger().Write(logDEBUG) << "deallocating prev memory for key " << key;
return boost::interprocess::shared_memory_object::remove(key);
}
char key[500];
boost::interprocess::shared_memory_object shm;
boost::interprocess::mapped_region region;
shm_remove remover;
};
#endif
template <typename IdentifierT, typename LockFileT = OSRMLockFile>
std::unique_ptr<SharedMemory> makeSharedMemory(const IdentifierT &id,
const uint64_t size = 0,
bool read_write = false,
bool remove_prev = true,
bool owner = false)
std::unique_ptr<SharedMemory>
makeSharedMemory(const IdentifierT &id, const uint64_t size = 0, bool read_write = false)
{
try
{
@@ -352,8 +249,7 @@ std::unique_ptr<SharedMemory> makeSharedMemory(const IdentifierT &id,
boost::filesystem::ofstream ofs(lock_file());
}
}
return std::make_unique<SharedMemory>(
lock_file(), id, size, read_write, remove_prev, owner);
return std::make_unique<SharedMemory>(lock_file(), id, size, read_write);
}
catch (const boost::interprocess::interprocess_exception &e)
{
@@ -362,17 +258,6 @@ std::unique_ptr<SharedMemory> makeSharedMemory(const IdentifierT &id,
throw util::exception(e.what());
}
}
template <typename IdentifierT>
std::unique_ptr<SharedMemory> makeSharedMemoryView(const IdentifierT &id)
{
return makeSharedMemory(id, 0, false, false, false);
}
template <typename IdentifierT>
std::unique_ptr<SharedMemory> makeOwnedSharedMemoryView(const IdentifierT &id)
{
return makeSharedMemory(id, 0, false, false, true);
}
}
}