Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow multiple named shared memory regions #4982

Merged
merged 7 commits into from
Apr 5, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@
- ADDED: Maneuver relation now supports `straight` as a direction [#4995](https://github.com/Project-OSRM/osrm-backend/pull/4995)
- Tools:
- ADDED: `osrm-routed` accepts a new property `--memory_file` to store memory in a file on disk. [#4881](https://github.com/Project-OSRM/osrm-backend/pull/4881)
- ADDED: `osrm-datastore` accepts a new parameter `--dataset-name` to select the name of the dataset. [#4982](https://github.com/Project-OSRM/osrm-backend/pull/4982)
- ADDED: `osrm-datastore` accepts a new parameter `--list` to list all datasets loaded into memory. [#4982](https://github.com/Project-OSRM/osrm-backend/pull/4982)
- ADDED: `osrm-routed` accepts a new parameter `--dataset-name` to select the shared-memory dataset to use. [#4982](https://github.com/Project-OSRM/osrm-backend/pull/4982)
- NodeJS:
- ADDED: `OSRM` object accepts a new option `memory_file` that stores the memory in a file on disk. [#4881](https://github.com/Project-OSRM/osrm-backend/pull/4881)
- ADDED: `OSRM` object accepts a new option `dataset_name` to select the shared-memory dataset. [#4982](https://github.com/Project-OSRM/osrm-backend/pull/4982)
- Internals
- CHANGED: Updated segregated intersection identification [#4845](https://github.com/Project-OSRM/osrm-backend/pull/4845) [#4968](https://github.com/Project-OSRM/osrm-backend/pull/4968)
- REMOVED: Remove `.timestamp` file since it was unused [#4960](https://github.com/Project-OSRM/osrm-backend/pull/4960)
Expand Down
5 changes: 3 additions & 2 deletions features/lib/osrm_loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ class OSRMDatastoreLoader extends OSRMBaseLoader {
}

loadData (callback) {
this.scope.runBin('osrm-datastore', this.inputFile, this.scope.environment, (err) => {
const command_arguments = util.format('--dataset-name=%s %s', this.scope.DATASET_NAME, this.inputFile);
this.scope.runBin('osrm-datastore', command_arguments, this.scope.environment, (err) => {
if (err) return callback(new Error('*** osrm-datastore exited with ' + err.code + ': ' + err));
callback();
});
Expand All @@ -116,7 +117,7 @@ class OSRMDatastoreLoader extends OSRMBaseLoader {
osrmUp (callback) {
if (this.osrmIsRunning()) return callback();

const command_arguments = util.format('--shared-memory=1 -i %s -p %d -a %s', this.scope.OSRM_IP, this.scope.OSRM_PORT, this.scope.ROUTING_ALGORITHM);
const command_arguments = util.format('--dataset-name=%s -s -i %s -p %d -a %s', this.scope.DATASET_NAME, this.scope.OSRM_IP, this.scope.OSRM_PORT, this.scope.ROUTING_ALGORITHM);
this.child = this.scope.runBin('osrm-routed', command_arguments, this.scope.environment, (err) => {
if (err && err.signal !== 'SIGINT') {
this.child = null;
Expand Down
20 changes: 20 additions & 0 deletions features/options/datastore/datastore.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
@datastore @options @help
Feature: osrm-datastore command line options: list

Background:
Given the profile "testbot"
And the node map
"""
a b
"""
And the ways
| nodes |
| ab |
And the data has been contracted

Scenario: osrm-datastore - Help should be shown when no options are passed
When I try to run "osrm-datastore --dataset-name test_dataset_42 {processed_file}"
Then it should exit successfully
When I try to run "osrm-datastore --list"
Then it should exit successfully
And stdout should contain "test_dataset_42/data"
1 change: 1 addition & 0 deletions features/support/env.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ module.exports = function () {
return callback(new Error('*** '+stxxl_config+ 'does not exist'));
}

this.DATASET_NAME = 'cucumber';
this.PLATFORM_WINDOWS = process.platform.match(/^win.*/);
this.DEFAULT_ENVIRONMENT = Object.assign({STXXLCFG: stxxl_config}, process.env);
this.DEFAULT_PROFILE = 'bicycle';
Expand Down
36 changes: 23 additions & 13 deletions include/engine/data_watchdog.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,29 @@ template <typename AlgorithmT, typename FacadeT> class DataWatchdogImpl;
template <typename AlgorithmT>
class DataWatchdogImpl<AlgorithmT, datafacade::ContiguousInternalMemoryDataFacade<AlgorithmT>> final
{
using mutex_type = typename storage::SharedMonitor<storage::SharedDataTimestamp>::mutex_type;
using mutex_type = typename storage::SharedMonitor<storage::SharedRegionRegister>::mutex_type;
using Facade = datafacade::ContiguousInternalMemoryDataFacade<AlgorithmT>;

public:
DataWatchdogImpl() : active(true), timestamp(0)
DataWatchdogImpl(const std::string &dataset_name) : dataset_name(dataset_name), active(true)
{
// create the initial facade before launching the watchdog thread
{
boost::interprocess::scoped_lock<mutex_type> current_region_lock(barrier.get_mutex());

auto &shared_register = barrier.data();
auto region_id = shared_register.Find(dataset_name + "/data");
if (region_id == storage::SharedRegionRegister::INVALID_REGION_ID)
{
throw util::exception("Could not find shared memory region for \"" + dataset_name +
"/data\". Did you run osrm-datastore?");
}
shared_region = &shared_register.GetRegion(region_id);
region = *shared_region;

facade_factory =
DataFacadeFactory<datafacade::ContiguousInternalMemoryDataFacade, AlgorithmT>(
std::make_shared<datafacade::SharedMemoryAllocator>(barrier.data().region));
timestamp = barrier.data().timestamp;
std::make_shared<datafacade::SharedMemoryAllocator>(region.shm_key));
}

watcher = std::thread(&DataWatchdogImpl::Run, this);
Expand Down Expand Up @@ -74,30 +83,31 @@ class DataWatchdogImpl<AlgorithmT, datafacade::ContiguousInternalMemoryDataFacad
{
boost::interprocess::scoped_lock<mutex_type> current_region_lock(barrier.get_mutex());

while (active && timestamp == barrier.data().timestamp)
while (active && region.timestamp == shared_region->timestamp)
{
barrier.wait(current_region_lock);
}

if (timestamp != barrier.data().timestamp)
if (region.timestamp != shared_region->timestamp)
{
auto region = barrier.data().region;
region = *shared_region;
facade_factory =
DataFacadeFactory<datafacade::ContiguousInternalMemoryDataFacade, AlgorithmT>(
std::make_shared<datafacade::SharedMemoryAllocator>(region));
timestamp = barrier.data().timestamp;
util::Log() << "updated facade to region " << region << " with timestamp "
<< timestamp;
std::make_shared<datafacade::SharedMemoryAllocator>(region.shm_key));
util::Log() << "updated facade to region " << (int)region.shm_key
<< " with timestamp " << region.timestamp;
}
}

util::Log() << "DataWatchdog thread stopped";
}

storage::SharedMonitor<storage::SharedDataTimestamp> barrier;
const std::string dataset_name;
storage::SharedMonitor<storage::SharedRegionRegister> barrier;
std::thread watcher;
bool active;
unsigned timestamp;
storage::SharedRegion region;
storage::SharedRegion *shared_region;
DataFacadeFactory<datafacade::ContiguousInternalMemoryDataFacade, AlgorithmT> facade_factory;
};
}
Expand Down
2 changes: 1 addition & 1 deletion include/engine/datafacade/shared_memory_allocator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ namespace datafacade
class SharedMemoryAllocator : public ContiguousBlockAllocator
{
public:
explicit SharedMemoryAllocator(storage::SharedDataType data_region);
explicit SharedMemoryAllocator(storage::SharedRegionRegister::ShmKey data_shm_key);
~SharedMemoryAllocator() override final;

// interface to give access to the datafacades
Expand Down
2 changes: 2 additions & 0 deletions include/engine/datafacade_provider.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ class WatchingProvider : public DataFacadeProvider<AlgorithmT, FacadeT>
public:
using Facade = typename DataFacadeProvider<AlgorithmT, FacadeT>::Facade;

WatchingProvider(const std::string &dataset_name) : watchdog(dataset_name) {}

std::shared_ptr<const Facade> Get(const api::TileParameters &params) const override final
{
return watchdog.Get(params);
Expand Down
6 changes: 3 additions & 3 deletions include/engine/engine.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@ template <typename Algorithm> class Engine final : public EngineInterface
{
if (config.use_shared_memory)
{
util::Log(logDEBUG) << "Using shared memory with algorithm "
<< routing_algorithms::name<Algorithm>();
facade_provider = std::make_unique<WatchingProvider<Algorithm>>();
util::Log(logDEBUG) << "Using shared memory with name \"" << config.dataset_name
<< "\" with algorithm " << routing_algorithms::name<Algorithm>();
facade_provider = std::make_unique<WatchingProvider<Algorithm>>(config.dataset_name);
}
else if (!config.memory_file.empty())
{
Expand Down
1 change: 1 addition & 0 deletions include/engine/engine_config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ struct EngineConfig final
boost::filesystem::path memory_file;
Algorithm algorithm = Algorithm::CH;
std::string verbosity;
std::string dataset_name;
};
}
}
Expand Down
17 changes: 17 additions & 0 deletions include/nodejs/node_osrm_support.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,23 @@ inline engine_config_ptr argumentsToEngineConfig(const Nan::FunctionCallbackInfo
*v8::String::Utf8Value(Nan::To<v8::String>(memory_file).ToLocalChecked());
}

auto dataset_name = params->Get(Nan::New("dataset_name").ToLocalChecked());
if (dataset_name.IsEmpty())
return engine_config_ptr();
if (!dataset_name->IsUndefined())
{
if (dataset_name->IsString())
{
engine_config->dataset_name =
*v8::String::Utf8Value(Nan::To<v8::String>(dataset_name).ToLocalChecked());
}
else
{
Nan::ThrowError("dataset_name needs to be a string");
return engine_config_ptr();
}
}

if (!path->IsUndefined())
{
engine_config->storage_config =
Expand Down
117 changes: 93 additions & 24 deletions include/storage/shared_datatype.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@

#include "util/exception.hpp"
#include "util/exception_utils.hpp"
#include "util/log.hpp"

#include <boost/assert.hpp>

#include <array>
#include <cstdint>
#include <map>
#include <numeric>
#include <unordered_set>

namespace osrm
Expand Down Expand Up @@ -160,40 +160,109 @@ class DataLayout
std::map<std::string, Block> blocks;
};

enum SharedDataType
struct SharedRegion
{
REGION_NONE,
REGION_1,
REGION_2
};
static constexpr const int MAX_NAME_LENGTH = 254;

struct SharedDataTimestamp
{
explicit SharedDataTimestamp(SharedDataType region, unsigned timestamp)
: region(region), timestamp(timestamp)
SharedRegion() : name{0}, timestamp{0} {}
SharedRegion(const std::string &name_, std::uint64_t timestamp, std::uint8_t shm_key)
: name{0}, timestamp{timestamp}, shm_key{shm_key}
{
std::copy_n(name_.begin(), std::min<std::size_t>(MAX_NAME_LENGTH, name_.size()), name);
}

SharedDataType region;
unsigned timestamp;
bool IsEmpty() const { return timestamp == 0; }

static constexpr const char *name = "osrm-region";
char name[MAX_NAME_LENGTH + 1];
std::uint64_t timestamp;
std::uint8_t shm_key;
};

inline std::string regionToString(const SharedDataType region)
// Keeps a list of all shared regions in a fixed-sized struct
// for fast access and deserialization.
struct SharedRegionRegister
{
switch (region)
using RegionID = std::uint8_t;
static constexpr const RegionID INVALID_REGION_ID = std::numeric_limits<RegionID>::max();
using ShmKey = decltype(SharedRegion::shm_key);

// Returns the key of the region with the given name
RegionID Find(const std::string &name) const
{
case REGION_1:
return "REGION_1";
case REGION_2:
return "REGION_2";
case REGION_NONE:
return "REGION_NONE";
default:
return "INVALID_REGION";
auto iter = std::find_if(regions.begin(), regions.end(), [&](const auto &region) {
return std::strncmp(region.name, name.c_str(), SharedRegion::MAX_NAME_LENGTH) == 0;
});

if (iter == regions.end())
{
return INVALID_REGION_ID;
}
else
{
return std::distance(regions.begin(), iter);
}
}
}

RegionID Register(const std::string &name, ShmKey key)
{
auto iter = std::find_if(
regions.begin(), regions.end(), [&](const auto &region) { return region.IsEmpty(); });
if (iter == regions.end())
{
throw util::exception("No shared memory regions left. Could not register " + name +
".");
}
else
{
constexpr std::uint32_t INITIAL_TIMESTAMP = 1;
*iter = SharedRegion{name, INITIAL_TIMESTAMP, key};
RegionID key = std::distance(regions.begin(), iter);
return key;
}
}

template <typename OutIter> void List(OutIter out) const
{
for (const auto &region : regions)
{
if (!region.IsEmpty())
{
*out++ = region.name;
}
}
}

const auto &GetRegion(const RegionID key) const { return regions[key]; }

auto &GetRegion(const RegionID key) { return regions[key]; }

ShmKey ReserveKey()
{
auto free_key_iter = std::find(shm_key_in_use.begin(), shm_key_in_use.end(), false);
if (free_key_iter == shm_key_in_use.end())
{
throw util::exception("Could not reserve a new SHM key. All keys are in use");
}

*free_key_iter = true;
return std::distance(shm_key_in_use.begin(), free_key_iter);
}

void ReleaseKey(ShmKey key) { shm_key_in_use[key] = false; }

static constexpr const std::uint8_t MAX_SHARED_REGIONS =
std::numeric_limits<RegionID>::max() - 1;
static_assert(MAX_SHARED_REGIONS < std::numeric_limits<RegionID>::max(),
"Number of shared memory regions needs to be less than the region id size.");

static constexpr const std::uint8_t MAX_SHM_KEYS = std::numeric_limits<std::uint8_t>::max() - 1;

static constexpr const char *name = "osrm-region";

private:
std::array<SharedRegion, MAX_SHARED_REGIONS> regions;
std::array<bool, MAX_SHM_KEYS> shm_key_in_use;
};
}
}

Expand Down
2 changes: 1 addition & 1 deletion include/storage/shared_memory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class SharedMemory
{
shm = boost::interprocess::xsi_shared_memory(boost::interprocess::open_only, key);

util::Log(logDEBUG) << "opening " << shm.get_shmid() << " from id " << id;
util::Log(logDEBUG) << "opening " << (int)shm.get_shmid() << " from id " << (int)id;

region = boost::interprocess::mapped_region(shm, boost::interprocess::read_only);
}
Expand Down
Loading