Shm: configurable allocation strategy

This commit is contained in:
Alexey Rybalchenko 2020-08-21 12:34:13 +02:00
parent 3b2d2a0ac8
commit 73109fe6d3
11 changed files with 307 additions and 159 deletions

View File

@ -38,6 +38,7 @@ void Sampler::InitTask()
fChannels.at("data").at(0).Transport()->SubscribeToRegionEvents([](FairMQRegionInfo info) { fChannels.at("data").at(0).Transport()->SubscribeToRegionEvents([](FairMQRegionInfo info) {
LOG(info) << "Region event: " << info.event LOG(info) << "Region event: " << info.event
<< ", managed: " << info.managed
<< ", id: " << info.id << ", id: " << info.id
<< ", ptr: " << info.ptr << ", ptr: " << info.ptr
<< ", size: " << info.size << ", size: " << info.size

View File

@ -31,6 +31,7 @@ void Sink::InitTask()
fMaxIterations = fConfig->GetProperty<uint64_t>("max-iterations"); fMaxIterations = fConfig->GetProperty<uint64_t>("max-iterations");
fChannels.at("data").at(0).Transport()->SubscribeToRegionEvents([](FairMQRegionInfo info) { fChannels.at("data").at(0).Transport()->SubscribeToRegionEvents([](FairMQRegionInfo info) {
LOG(info) << "Region event: " << info.event LOG(info) << "Region event: " << info.event
<< ", managed: " << info.managed
<< ", id: " << info.id << ", id: " << info.id
<< ", ptr: " << info.ptr << ", ptr: " << info.ptr
<< ", size: " << info.size << ", size: " << info.size

View File

@ -28,21 +28,24 @@ enum class FairMQRegionEvent : int
struct FairMQRegionInfo struct FairMQRegionInfo
{ {
FairMQRegionInfo() FairMQRegionInfo()
: id(0) : managed(true)
, id(0)
, ptr(nullptr) , ptr(nullptr)
, size(0) , size(0)
, flags(0) , flags(0)
, event(FairMQRegionEvent::created) , event(FairMQRegionEvent::created)
{} {}
FairMQRegionInfo(uint64_t _id, void* _ptr, size_t _size, int64_t _flags, FairMQRegionEvent _event) FairMQRegionInfo(bool _managed, uint64_t _id, void* _ptr, size_t _size, int64_t _flags, FairMQRegionEvent _event)
: id(_id) : managed(_managed)
, id(_id)
, ptr(_ptr) , ptr(_ptr)
, size(_size) , size(_size)
, flags(_flags) , flags(_flags)
, event(_event) , event(_event)
{} {}
bool managed; // managed/unmanaged
uint64_t id; // id of the region uint64_t id; // id of the region
void* ptr; // pointer to the start of the region void* ptr; // pointer to the start of the region
size_t size; // region size size_t size; // region size

View File

@ -61,24 +61,25 @@ Plugin::ProgOptions ConfigPluginProgramOptions()
namespace po = boost::program_options; namespace po = boost::program_options;
auto pluginOptions = po::options_description{"FairMQ device options"}; auto pluginOptions = po::options_description{"FairMQ device options"};
pluginOptions.add_options() pluginOptions.add_options()
("id", po::value<string >()->default_value(""), "Device ID.") ("id", po::value<string >()->default_value(""), "Device ID.")
("io-threads", po::value<int >()->default_value(1), "Number of I/O threads.") ("io-threads", po::value<int >()->default_value(1), "Number of I/O threads.")
("transport", po::value<string >()->default_value("zeromq"), "Transport ('zeromq'/'shmem').") ("transport", po::value<string >()->default_value("zeromq"), "Transport ('zeromq'/'shmem').")
("network-interface", po::value<string >()->default_value("default"), "Network interface to bind on (e.g. eth0, ib0..., default will try to detect the interface of the default route).") ("network-interface", po::value<string >()->default_value("default"), "Network interface to bind on (e.g. eth0, ib0..., default will try to detect the interface of the default route).")
("init-timeout", po::value<int >()->default_value(120), "Timeout for the initialization in seconds (when expecting dynamic initialization).") ("init-timeout", po::value<int >()->default_value(120), "Timeout for the initialization in seconds (when expecting dynamic initialization).")
("max-run-time", po::value<uint64_t >()->default_value(0), "Maximum runtime for the Running state handler, after which state will change to Ready (in seconds, 0 for no limit).") ("max-run-time", po::value<uint64_t >()->default_value(0), "Maximum runtime for the Running state handler, after which state will change to Ready (in seconds, 0 for no limit).")
("print-channels", po::value<bool >()->implicit_value(true), "Print registered channel endpoints in a machine-readable format (<channel name>:<min num subchannels>:<max num subchannels>)") ("print-channels", po::value<bool >()->implicit_value(true), "Print registered channel endpoints in a machine-readable format (<channel name>:<min num subchannels>:<max num subchannels>)")
("shm-segment-size", po::value<size_t >()->default_value(2ULL << 30), "Shared memory: size of the shared memory segment (in bytes).") ("shm-segment-size", po::value<size_t >()->default_value(2ULL << 30), "Shared memory: size of the shared memory segment (in bytes).")
("shm-mlock-segment", po::value<bool >()->default_value(false), "Shared memory: mlock the shared memory segment after initialization.") ("shm-allocation", po::value<string >()->default_value("rbtree_best_fit"), "Shared memory allocation algorithm: rbtree_best_fit/simple_seq_fit.")
("shm-zero-segment", po::value<bool >()->default_value(false), "Shared memory: zero the shared memory segment memory after initialization.") ("shm-mlock-segment", po::value<bool >()->default_value(false), "Shared memory: mlock the shared memory segment after initialization.")
("shm-throw-bad-alloc", po::value<bool >()->default_value(true), "Throw a fair::mq::MessageBadAlloc if cannot allocate a message (retry if false).") ("shm-zero-segment", po::value<bool >()->default_value(false), "Shared memory: zero the shared memory segment memory after initialization.")
("shm-monitor", po::value<bool >()->default_value(true), "Shared memory: run monitor daemon.") ("shm-throw-bad-alloc", po::value<bool >()->default_value(true), "Throw a fair::mq::MessageBadAlloc if cannot allocate a message (retry if false).")
("ofi-size-hint", po::value<size_t >()->default_value(0), "EXPERIMENTAL: OFI size hint for the allocator.") ("shm-monitor", po::value<bool >()->default_value(true), "Shared memory: run monitor daemon.")
("rate", po::value<float >()->default_value(0.), "Rate for conditional run loop (Hz).") ("ofi-size-hint", po::value<size_t >()->default_value(0), "EXPERIMENTAL: OFI size hint for the allocator.")
("session", po::value<string >()->default_value("default"), "Session name.") ("rate", po::value<float >()->default_value(0.), "Rate for conditional run loop (Hz).")
("config-key", po::value<string >(), "Use provided value instead of device id for fetching the configuration from JSON file.") ("session", po::value<string >()->default_value("default"), "Session name.")
("mq-config", po::value<string >(), "JSON input as file.") ("config-key", po::value<string >(), "Use provided value instead of device id for fetching the configuration from JSON file.")
("channel-config", po::value<vector<string>>()->multitoken()->composing(), "Configuration of single or multiple channel(s) by comma separated key=value list"); ("mq-config", po::value<string >(), "JSON input as file.")
("channel-config", po::value<vector<string>>()->multitoken()->composing(), "Configuration of single or multiple channel(s) by comma separated key=value list");
return pluginOptions; return pluginOptions;
} }

View File

@ -19,8 +19,11 @@
#include <boost/interprocess/containers/map.hpp> #include <boost/interprocess/containers/map.hpp>
#include <boost/interprocess/containers/string.hpp> #include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/containers/vector.hpp> #include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/indexes/null_index.hpp>
#include <boost/interprocess/managed_shared_memory.hpp> #include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/mem_algo/simple_seq_fit.hpp>
#include <boost/unordered_map.hpp> #include <boost/unordered_map.hpp>
#include <boost/variant.hpp>
#include <unistd.h> #include <unistd.h>
#include <sys/types.h> #include <sys/types.h>
@ -32,6 +35,17 @@ namespace mq
namespace shmem namespace shmem
{ {
struct SharedMemoryError : std::runtime_error { using std::runtime_error::runtime_error; };
using SimpleSeqFitSegment = boost::interprocess::basic_managed_shared_memory<char,
boost::interprocess::simple_seq_fit<boost::interprocess::mutex_family>,
boost::interprocess::null_index>;
// boost::interprocess::iset_index>;
using RBTreeBestFitSegment = boost::interprocess::basic_managed_shared_memory<char,
boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>,
boost::interprocess::null_index>;
// boost::interprocess::iset_index>;
using SegmentManager = boost::interprocess::managed_shared_memory::segment_manager; using SegmentManager = boost::interprocess::managed_shared_memory::segment_manager;
using VoidAlloc = boost::interprocess::allocator<void, SegmentManager>; using VoidAlloc = boost::interprocess::allocator<void, SegmentManager>;
using CharAlloc = boost::interprocess::allocator<char, SegmentManager>; using CharAlloc = boost::interprocess::allocator<char, SegmentManager>;
@ -39,6 +53,12 @@ using Str = boost::interprocess::basic_string<char, std::char_traits<
using StrAlloc = boost::interprocess::allocator<Str, SegmentManager>; using StrAlloc = boost::interprocess::allocator<Str, SegmentManager>;
using StrVector = boost::interprocess::vector<Str, StrAlloc>; using StrVector = boost::interprocess::vector<Str, StrAlloc>;
enum class AllocationAlgorithm : int
{
rbtree_best_fit,
simple_seq_fit
};
struct RegionInfo struct RegionInfo
{ {
RegionInfo(const VoidAlloc& alloc) RegionInfo(const VoidAlloc& alloc)
@ -65,6 +85,19 @@ using Uint64RegionInfoPairAlloc = boost::interprocess::allocator<std::pair<const
using Uint64RegionInfoMap = boost::interprocess::map<uint64_t, RegionInfo, std::less<uint64_t>, Uint64RegionInfoPairAlloc>; using Uint64RegionInfoMap = boost::interprocess::map<uint64_t, RegionInfo, std::less<uint64_t>, Uint64RegionInfoPairAlloc>;
using Uint64RegionInfoHashMap = boost::unordered_map<uint64_t, RegionInfo, boost::hash<uint64_t>, std::equal_to<uint64_t>, Uint64RegionInfoPairAlloc>; using Uint64RegionInfoHashMap = boost::unordered_map<uint64_t, RegionInfo, boost::hash<uint64_t>, std::equal_to<uint64_t>, Uint64RegionInfoPairAlloc>;
struct SegmentInfo
{
SegmentInfo(AllocationAlgorithm aa)
: fAllocationAlgorithm(aa)
{}
AllocationAlgorithm fAllocationAlgorithm;
};
using Uint64SegmentInfoPairAlloc = boost::interprocess::allocator<std::pair<const uint64_t, SegmentInfo>, SegmentManager>;
using Uint64SegmentInfoMap = boost::interprocess::map<uint64_t, SegmentInfo, std::less<uint64_t>, Uint64SegmentInfoPairAlloc>;
using Uint64SegmentInfoHashMap = boost::unordered_map<uint64_t, SegmentInfo, boost::hash<uint64_t>, std::equal_to<uint64_t>, Uint64SegmentInfoPairAlloc>;
struct DeviceCounter struct DeviceCounter
{ {
DeviceCounter(unsigned int c) DeviceCounter(unsigned int c)
@ -153,6 +186,101 @@ inline std::string buildShmIdFromSessionIdAndUserId(const std::string& sessionId
return shmId; return shmId;
} }
struct SegmentSize : public boost::static_visitor<size_t>
{
template<typename S>
size_t operator()(S& s) const { return s.get_size(); }
};
struct SegmentAddress : public boost::static_visitor<void*>
{
template<typename S>
void* operator()(S& s) const { return s.get_address(); }
};
struct SegmentMemoryZeroer : public boost::static_visitor<>
{
template<typename S>
void operator()(S& s) const { s.zero_free_memory(); }
};
struct SegmentFreeMemory : public boost::static_visitor<size_t>
{
template<typename S>
size_t operator()(S& s) const { return s.get_free_memory(); }
};
struct SegmentHandleFromAddress : public boost::static_visitor<boost::interprocess::managed_shared_memory::handle_t>
{
SegmentHandleFromAddress(const void* _ptr) : ptr(_ptr) {}
template<typename S>
boost::interprocess::managed_shared_memory::handle_t operator()(S& s) const { return s.get_handle_from_address(ptr); }
const void* ptr;
};
struct SegmentAddressFromHandle : public boost::static_visitor<void*>
{
SegmentAddressFromHandle(const boost::interprocess::managed_shared_memory::handle_t _handle) : handle(_handle) {}
template<typename S>
void* operator()(S& s) const { return s.get_address_from_handle(handle); }
const boost::interprocess::managed_shared_memory::handle_t handle;
};
struct SegmentAllocate : public boost::static_visitor<void*>
{
SegmentAllocate(const size_t _size) : size(_size) {}
template<typename S>
void* operator()(S& s) const { return s.allocate(size); }
const size_t size;
};
struct SegmentAllocateAligned : public boost::static_visitor<void*>
{
SegmentAllocateAligned(const size_t _size, const size_t _alignment) : size(_size), alignment(_alignment) {}
template<typename S>
void* operator()(S& s) const { return s.allocate_aligned(size, alignment); }
const size_t size;
const size_t alignment;
};
struct SegmentBufferShrink : public boost::static_visitor<char*>
{
SegmentBufferShrink(const size_t _old_size, const size_t _new_size, char* _local_ptr)
: old_size(_old_size)
, new_size(_new_size)
, local_ptr(_local_ptr)
{}
template<typename S>
char* operator()(S& s) const
{
boost::interprocess::managed_shared_memory::size_type shrunk_size = new_size;
return s.template allocation_command<char>(boost::interprocess::shrink_in_place, old_size + 128, shrunk_size, local_ptr);
}
const size_t old_size;
const size_t new_size;
mutable char* local_ptr;
};
struct SegmentDeallocate : public boost::static_visitor<>
{
SegmentDeallocate(void* _ptr) : ptr(_ptr) {}
template<typename S>
void operator()(S& s) const { return s.deallocate(ptr); }
void* ptr;
};
} // namespace shmem } // namespace shmem
} // namespace mq } // namespace mq
} // namespace fair } // namespace fair

View File

@ -27,12 +27,10 @@
#include <boost/date_time/posix_time/posix_time.hpp> #include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/interprocess/indexes/null_index.hpp>
#include <boost/interprocess/ipc/message_queue.hpp> #include <boost/interprocess/ipc/message_queue.hpp>
#include <boost/interprocess/managed_shared_memory.hpp> #include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/sync/named_condition.hpp> #include <boost/interprocess/sync/named_condition.hpp>
#include <boost/interprocess/sync/named_mutex.hpp> #include <boost/interprocess/sync/named_mutex.hpp>
#include <boost/interprocess/mem_algo/simple_seq_fit.hpp>
#include <boost/process.hpp> #include <boost/process.hpp>
#include <boost/variant.hpp> #include <boost/variant.hpp>
@ -40,6 +38,7 @@
#include <condition_variable> #include <condition_variable>
#include <mutex> #include <mutex>
#include <set> #include <set>
#include <sstream>
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#include <thread> #include <thread>
@ -56,22 +55,12 @@ namespace mq
namespace shmem namespace shmem
{ {
struct SharedMemoryError : std::runtime_error { using std::runtime_error::runtime_error; };
using SimpleSeqFitSegment = boost::interprocess::basic_managed_shared_memory<char,
boost::interprocess::simple_seq_fit<boost::interprocess::mutex_family>,
boost::interprocess::iset_index>;
using RBTreeBestFitSegment = boost::interprocess::basic_managed_shared_memory<char,
boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family>,
boost::interprocess::iset_index>;
class Manager class Manager
{ {
public: public:
Manager(std::string shmId, std::string deviceId, size_t size, const ProgOptions* config) Manager(std::string shmId, std::string deviceId, size_t size, const ProgOptions* config)
: fShmId(std::move(shmId)) : fShmId(std::move(shmId))
, fDeviceId(std::move(deviceId)) , fDeviceId(std::move(deviceId))
// , fSegment(boost::interprocess::open_or_create, std::string("fmq_" + fShmId + "_main").c_str(), size)
, fSegments() , fSegments()
, fManagementSegment(boost::interprocess::open_or_create, std::string("fmq_" + fShmId + "_mng").c_str(), 6553600) , fManagementSegment(boost::interprocess::open_or_create, std::string("fmq_" + fShmId + "_mng").c_str(), 6553600)
, fShmVoidAlloc(fManagementSegment.get_segment_manager()) , fShmVoidAlloc(fManagementSegment.get_segment_manager())
@ -79,6 +68,7 @@ class Manager
, fRegionEventsCV(boost::interprocess::open_or_create, std::string("fmq_" + fShmId + "_cv").c_str()) , fRegionEventsCV(boost::interprocess::open_or_create, std::string("fmq_" + fShmId + "_cv").c_str())
, fRegionEventsSubscriptionActive(false) , fRegionEventsSubscriptionActive(false)
, fDeviceCounter(nullptr) , fDeviceCounter(nullptr)
, fShmSegments(nullptr)
, fShmRegions(nullptr) , fShmRegions(nullptr)
, fInterrupted(false) , fInterrupted(false)
, fMsgCounter(0) , fMsgCounter(0)
@ -95,77 +85,120 @@ class Manager
bool mlockSegment = false; bool mlockSegment = false;
bool zeroSegment = false; bool zeroSegment = false;
bool autolaunchMonitor = false; bool autolaunchMonitor = false;
std::string allocationAlgorithm("rbtree_best_fit");
if (config) { if (config) {
mlockSegment = config->GetProperty<bool>("shm-mlock-segment", mlockSegment); mlockSegment = config->GetProperty<bool>("shm-mlock-segment", mlockSegment);
zeroSegment = config->GetProperty<bool>("shm-zero-segment", zeroSegment); zeroSegment = config->GetProperty<bool>("shm-zero-segment", zeroSegment);
autolaunchMonitor = config->GetProperty<bool>("shm-monitor", autolaunchMonitor); autolaunchMonitor = config->GetProperty<bool>("shm-monitor", autolaunchMonitor);
fThrowOnBadAlloc = config->GetProperty<bool>("shm-throw-bad-alloc", fThrowOnBadAlloc); fThrowOnBadAlloc = config->GetProperty<bool>("shm-throw-bad-alloc", fThrowOnBadAlloc);
allocationAlgorithm = config->GetProperty<std::string>("shm-allocation", allocationAlgorithm);
} else { } else {
LOG(debug) << "ProgOptions not available! Using defaults."; LOG(debug) << "ProgOptions not available! Using defaults.";
} }
if (allocationAlgorithm != "rbtree_best_fit" && allocationAlgorithm != "simple_seq_fit") {
LOG(error) << "Provided shared memory allocation algorithm '" << allocationAlgorithm << "' is not supported. Supported are 'rbtree_best_fit'/'simple_seq_fit'";
throw SharedMemoryError(tools::ToString("Provided shared memory allocation algorithm '", allocationAlgorithm, "' is not supported. Supported are 'rbtree_best_fit'/'simple_seq_fit'"));
}
if (autolaunchMonitor) { if (autolaunchMonitor) {
StartMonitor(fShmId); StartMonitor(fShmId);
} }
{ {
std::stringstream ss;
boost::interprocess::scoped_lock<boost::interprocess::named_mutex> lock(fShmMtx); boost::interprocess::scoped_lock<boost::interprocess::named_mutex> lock(fShmMtx);
fShmSegments = fManagementSegment.find_or_construct<Uint64SegmentInfoHashMap>(unique_instance)(fShmVoidAlloc);
const uint64_t id = 0;
try { try {
fSegments.emplace(0, RBTreeBestFitSegment(open_only, std::string("fmq_" + fShmId + "_main").c_str())); auto it = fShmSegments->find(id);
LOG(debug) << "opened shared memory segment '" << "fmq_" << fShmId << "_main" << "' of " << fSegments.at(0).get_size() << " bytes. Available are " << fSegments.at(0).get_free_memory() << " bytes."; if (it == fShmSegments->end()) {
// no segment with given id exists, creating
if (allocationAlgorithm == "rbtree_best_fit") {
fSegments.emplace(id, RBTreeBestFitSegment(create_only, std::string("fmq_" + fShmId + "_main").c_str(), size));
fShmSegments->emplace(id, AllocationAlgorithm::rbtree_best_fit);
} else if (allocationAlgorithm == "simple_seq_fit") {
fSegments.emplace(id, SimpleSeqFitSegment(create_only, std::string("fmq_" + fShmId + "_main").c_str(), size));
fShmSegments->emplace(id, AllocationAlgorithm::simple_seq_fit);
}
ss << "Created ";
} else {
// found segment with the given id, opening
if (it->second.fAllocationAlgorithm == AllocationAlgorithm::rbtree_best_fit) {
fSegments.emplace(id, RBTreeBestFitSegment(open_only, std::string("fmq_" + fShmId + "_main").c_str()));
if (allocationAlgorithm != "rbtree_best_fit") {
LOG(warn) << "Allocation algorithm of the opened segment is rbtree_best_fit, but requested is " << allocationAlgorithm << ". Ignoring requested setting.";
allocationAlgorithm = "rbtree_best_fit";
}
} else {
fSegments.emplace(id, SimpleSeqFitSegment(open_only, std::string("fmq_" + fShmId + "_main").c_str()));
if (allocationAlgorithm != "simple_seq_fit") {
LOG(warn) << "Allocation algorithm of the opened segment is simple_seq_fit, but requested is " << allocationAlgorithm << ". Ignoring requested setting.";
allocationAlgorithm = "simple_seq_fit";
}
}
ss << "Opened ";
}
ss << "shared memory segment '" << "fmq_" << fShmId << "_main_" << id << "'."
<< " Size: " << boost::apply_visitor(SegmentSize{}, fSegments.at(id)) << " bytes."
<< " Available: " << boost::apply_visitor(SegmentFreeMemory{}, fSegments.at(id)) << " bytes."
<< " Allocation algorithm: " << allocationAlgorithm;
LOG(debug) << ss.str();
} catch(interprocess_exception&) { } catch(interprocess_exception&) {
fSegments.emplace(0, RBTreeBestFitSegment(create_only, std::string("fmq_" + fShmId + "_main").c_str(), size)); LOG(error) << "something went wrong";
LOG(debug) << "created shared memory segment '" << "fmq_" << fShmId << "_main" << "' of " << fSegments.at(0).get_size() << " bytes. Available are " << fSegments.at(0).get_free_memory() << " bytes.";
} }
} }
if (mlockSegment) { if (mlockSegment) {
LOG(debug) << "Locking the managed segment memory pages..."; LOG(debug) << "Locking the managed segment memory pages...";
if (mlock(fSegments.at(0).get_address(), fSegments.at(0).get_size()) == -1) { if (mlock(boost::apply_visitor(SegmentAddress{}, fSegments.at(0)), boost::apply_visitor(SegmentSize{}, fSegments.at(0))) == -1) {
LOG(error) << "Could not lock the managed segment memory. Code: " << errno << ", reason: " << strerror(errno); LOG(error) << "Could not lock the managed segment memory. Code: " << errno << ", reason: " << strerror(errno);
} }
LOG(debug) << "Successfully locked the managed segment memory pages."; LOG(debug) << "Successfully locked the managed segment memory pages.";
} }
if (zeroSegment) { if (zeroSegment) {
LOG(debug) << "Zeroing the managed segment free memory..."; LOG(debug) << "Zeroing the managed segment free memory...";
fSegments.at(0).zero_free_memory(); boost::apply_visitor(SegmentMemoryZeroer{}, fSegments.at(0));
LOG(debug) << "Successfully zeroed the managed segment free memory."; LOG(debug) << "Successfully zeroed the managed segment free memory.";
} }
boost::interprocess::scoped_lock<boost::interprocess::named_mutex> lock(fShmMtx); {
boost::interprocess::scoped_lock<boost::interprocess::named_mutex> lock(fShmMtx);
fShmRegions = fManagementSegment.find_or_construct<Uint64RegionInfoHashMap>(unique_instance)(fShmVoidAlloc); fShmRegions = fManagementSegment.find_or_construct<Uint64RegionInfoHashMap>(unique_instance)(fShmVoidAlloc);
#ifdef FAIRMQ_DEBUG_MODE
fMsgDebug = fManagementSegment.find_or_construct<SizetMsgDebugMap>(unique_instance)(fShmVoidAlloc);
#endif
// store info about the managed segment as region with id 0
fShmRegions->emplace(0, RegionInfo("", 0, 0, fShmVoidAlloc));
fDeviceCounter = fManagementSegment.find<DeviceCounter>(unique_instance).first;
if (fDeviceCounter) {
LOG(debug) << "device counter found, with value of " << fDeviceCounter->fCount << ". incrementing.";
(fDeviceCounter->fCount)++;
LOG(debug) << "incremented device counter, now: " << fDeviceCounter->fCount;
} else {
LOG(debug) << "no device counter found, creating one and initializing with 1";
fDeviceCounter = fManagementSegment.construct<DeviceCounter>(unique_instance)(1);
LOG(debug) << "initialized device counter with: " << fDeviceCounter->fCount;
}
#ifdef FAIRMQ_DEBUG_MODE #ifdef FAIRMQ_DEBUG_MODE
fShmMsgCounter = fManagementSegment.find<MsgCounter>(unique_instance).first; fMsgDebug = fManagementSegment.find_or_construct<SizetMsgDebugMap>(unique_instance)(fShmVoidAlloc);
if (fShmMsgCounter) {
LOG(debug) << "message counter found, with value of " << fShmMsgCounter->fCount << ".";
} else {
LOG(debug) << "no message counter found, creating one and initializing with 0";
fShmMsgCounter = fManagementSegment.construct<MsgCounter>(unique_instance)(0);
LOG(debug) << "initialized message counter with: " << fShmMsgCounter->fCount;
}
#endif #endif
fDeviceCounter = fManagementSegment.find<DeviceCounter>(unique_instance).first;
if (fDeviceCounter) {
LOG(debug) << "device counter found, with value of " << fDeviceCounter->fCount << ". incrementing.";
(fDeviceCounter->fCount)++;
LOG(debug) << "incremented device counter, now: " << fDeviceCounter->fCount;
} else {
LOG(debug) << "no device counter found, creating one and initializing with 1";
fDeviceCounter = fManagementSegment.construct<DeviceCounter>(unique_instance)(1);
LOG(debug) << "initialized device counter with: " << fDeviceCounter->fCount;
}
#ifdef FAIRMQ_DEBUG_MODE
fShmMsgCounter = fManagementSegment.find<MsgCounter>(unique_instance).first;
if (fShmMsgCounter) {
LOG(debug) << "message counter found, with value of " << fShmMsgCounter->fCount << ".";
} else {
LOG(debug) << "no message counter found, creating one and initializing with 0";
fShmMsgCounter = fManagementSegment.construct<MsgCounter>(unique_instance)(0);
LOG(debug) << "initialized message counter with: " << fShmMsgCounter->fCount;
}
#endif
}
fHeartbeatThread = std::thread(&Manager::SendHeartbeats, this); fHeartbeatThread = std::thread(&Manager::SendHeartbeats, this);
} }
@ -336,29 +369,29 @@ class Manager
for (const auto& e : *fShmRegions) { for (const auto& e : *fShmRegions) {
fair::mq::RegionInfo info; fair::mq::RegionInfo info;
info.managed = false;
info.id = e.first; info.id = e.first;
info.flags = e.second.fUserFlags; info.flags = e.second.fUserFlags;
info.event = e.second.fDestroyed ? RegionEvent::destroyed : RegionEvent::created; info.event = e.second.fDestroyed ? RegionEvent::destroyed : RegionEvent::created;
if (info.id != 0) { if (!e.second.fDestroyed) {
if (!e.second.fDestroyed) { auto region = GetRegionUnsafe(info.id);
auto region = GetRegionUnsafe(info.id); info.ptr = region->fRegion.get_address();
info.ptr = region->fRegion.get_address(); info.size = region->fRegion.get_size();
info.size = region->fRegion.get_size();
} else {
info.ptr = nullptr;
info.size = 0;
}
result.push_back(info);
} else { } else {
if (!e.second.fDestroyed) { info.ptr = nullptr;
info.ptr = fSegments.at(0).get_address(); info.size = 0;
info.size = fSegments.at(0).get_size();
} else {
info.ptr = nullptr;
info.size = 0;
}
result.push_back(info);
} }
result.push_back(info);
}
for (const auto& e : *fShmSegments) {
fair::mq::RegionInfo info;
info.managed = true;
info.id = e.first;
info.event = RegionEvent::created;
info.ptr = boost::apply_visitor(SegmentAddress{}, fSegments.at(e.first));
info.size = boost::apply_visitor(SegmentSize{}, fSegments.at(e.first));
result.push_back(info);
} }
return result; return result;
@ -462,8 +495,14 @@ class Manager
bool ThrowingOnBadAlloc() const { return fThrowOnBadAlloc; } bool ThrowingOnBadAlloc() const { return fThrowOnBadAlloc; }
boost::interprocess::managed_shared_memory::handle_t GetHandleFromAddress(const void* ptr) const { return fSegments.at(0).get_handle_from_address(ptr); } boost::interprocess::managed_shared_memory::handle_t GetHandleFromAddress(const void* ptr) const
void* GetAddressFromHandle(const boost::interprocess::managed_shared_memory::handle_t handle) const { return fSegments.at(0).get_address_from_handle(handle); } {
return boost::apply_visitor(SegmentHandleFromAddress{ptr}, fSegments.at(0));
}
void* GetAddressFromHandle(const boost::interprocess::managed_shared_memory::handle_t handle) const
{
return boost::apply_visitor(SegmentAddressFromHandle{handle}, fSegments.at(0));
}
char* Allocate(const size_t size, size_t alignment = 0) char* Allocate(const size_t size, size_t alignment = 0)
{ {
@ -475,19 +514,19 @@ class Manager
// boost::interprocess::managed_shared_memory::size_type actualSize = size; // boost::interprocess::managed_shared_memory::size_type actualSize = size;
// char* hint = 0; // unused for boost::interprocess::allocate_new // char* hint = 0; // unused for boost::interprocess::allocate_new
// ptr = fSegments.at(0).allocation_command<char>(boost::interprocess::allocate_new, size, actualSize, hint); // ptr = fSegments.at(0).allocation_command<char>(boost::interprocess::allocate_new, size, actualSize, hint);
size_t segmentSize = fSegments.at(0).get_size(); size_t segmentSize = boost::apply_visitor(SegmentSize{}, fSegments.at(0));
if (size > segmentSize) { if (size > segmentSize) {
throw MessageBadAlloc(tools::ToString("Requested message size (", size, ") exceeds segment size (", segmentSize, ")")); throw MessageBadAlloc(tools::ToString("Requested message size (", size, ") exceeds segment size (", segmentSize, ")"));
} }
if (alignment == 0) { if (alignment == 0) {
ptr = reinterpret_cast<char*>(fSegments.at(0).allocate(size)); ptr = reinterpret_cast<char*>(boost::apply_visitor(SegmentAllocate{size}, fSegments.at(0)));
} else { } else {
ptr = reinterpret_cast<char*>(fSegments.at(0).allocate_aligned(size, alignment)); ptr = reinterpret_cast<char*>(boost::apply_visitor(SegmentAllocateAligned{size, alignment}, fSegments.at(0)));
} }
} catch (boost::interprocess::bad_alloc& ba) { } catch (boost::interprocess::bad_alloc& ba) {
// LOG(warn) << "Shared memory full..."; // LOG(warn) << "Shared memory full...";
if (ThrowingOnBadAlloc()) { if (ThrowingOnBadAlloc()) {
throw MessageBadAlloc(tools::ToString("shmem: could not create a message of size ", size, ", alignment: ", (alignment != 0) ? std::to_string(alignment) : "default", ", free memory: ", fSegments.at(0).get_free_memory())); throw MessageBadAlloc(tools::ToString("shmem: could not create a message of size ", size, ", alignment: ", (alignment != 0) ? std::to_string(alignment) : "default", ", free memory: ", boost::apply_visitor(SegmentFreeMemory{}, fSegments.at(0))));
} }
// rateLimiter.maybe_sleep(); // rateLimiter.maybe_sleep();
std::this_thread::sleep_for(std::chrono::milliseconds(50)); std::this_thread::sleep_for(std::chrono::milliseconds(50));
@ -509,7 +548,7 @@ class Manager
void Deallocate(boost::interprocess::managed_shared_memory::handle_t handle) void Deallocate(boost::interprocess::managed_shared_memory::handle_t handle)
{ {
fSegments.at(0).deallocate(GetAddressFromHandle(handle)); boost::apply_visitor(SegmentDeallocate{GetAddressFromHandle(handle)}, fSegments.at(0));
#ifdef FAIRMQ_DEBUG_MODE #ifdef FAIRMQ_DEBUG_MODE
boost::interprocess::scoped_lock<boost::interprocess::named_mutex> lock(fShmMtx); boost::interprocess::scoped_lock<boost::interprocess::named_mutex> lock(fShmMtx);
DecrementShmMsgCounter(); DecrementShmMsgCounter();
@ -519,9 +558,7 @@ class Manager
char* ShrinkInPlace(size_t oldSize, size_t newSize, char* localPtr) char* ShrinkInPlace(size_t oldSize, size_t newSize, char* localPtr)
{ {
using namespace boost::interprocess; return boost::apply_visitor(SegmentBufferShrink{oldSize, newSize, localPtr}, fSegments.at(0));
managed_shared_memory::size_type shrunkSize = newSize;
return fSegments.at(0).allocation_command<char>(shrink_in_place, oldSize + 128, shrunkSize, localPtr);
} }
~Manager() ~Manager()
@ -563,8 +600,7 @@ class Manager
private: private:
std::string fShmId; std::string fShmId;
std::string fDeviceId; std::string fDeviceId;
// boost::interprocess::managed_shared_memory fSegment; std::unordered_map<uint64_t, boost::variant<RBTreeBestFitSegment, SimpleSeqFitSegment>> fSegments;
std::unordered_map<uint64_t, RBTreeBestFitSegment> fSegments;
boost::interprocess::managed_shared_memory fManagementSegment; boost::interprocess::managed_shared_memory fManagementSegment;
VoidAlloc fShmVoidAlloc; VoidAlloc fShmVoidAlloc;
boost::interprocess::named_mutex fShmMtx; boost::interprocess::named_mutex fShmMtx;
@ -576,6 +612,7 @@ class Manager
std::unordered_map<uint64_t, RegionEvent> fObservedRegionEvents; std::unordered_map<uint64_t, RegionEvent> fObservedRegionEvents;
DeviceCounter* fDeviceCounter; DeviceCounter* fDeviceCounter;
Uint64SegmentInfoHashMap* fShmSegments;
Uint64RegionInfoHashMap* fShmRegions; Uint64RegionInfoHashMap* fShmRegions;
std::unordered_map<uint64_t, std::unique_ptr<Region>> fRegions; std::unordered_map<uint64_t, std::unique_ptr<Region>> fRegions;

View File

@ -205,10 +205,6 @@ void Monitor::Interactive()
cout << "\n[q] --> quitting." << endl; cout << "\n[q] --> quitting." << endl;
fTerminating = true; fTerminating = true;
break; break;
case 'p':
cout << "\n[p] --> active queues:" << endl;
PrintQueues();
break;
case 'x': case 'x':
cout << "\n[x] --> closing shared memory:" << endl; cout << "\n[x] --> closing shared memory:" << endl;
if (!fViewOnly) { if (!fViewOnly) {
@ -254,6 +250,7 @@ void Monitor::Interactive()
void Monitor::CheckSegment() void Monitor::CheckSegment()
{ {
using namespace boost::interprocess;
char c = '#'; char c = '#';
if (fInteractive) { if (fInteractive) {
@ -281,8 +278,27 @@ void Monitor::CheckSegment()
} }
try { try {
bipc::managed_shared_memory segment(bipc::open_only, fSegmentName.c_str()); managed_shared_memory managementSegment(open_only, fManagementSegmentName.c_str());
bipc::managed_shared_memory managementSegment(bipc::open_only, fManagementSegmentName.c_str());
Uint64SegmentInfoHashMap* segmentInfos = managementSegment.find<Uint64SegmentInfoHashMap>(unique_instance).first;
std::unordered_map<uint64_t, boost::variant<RBTreeBestFitSegment, SimpleSeqFitSegment>> segments;
if (!segmentInfos) {
cout << "Found management segment, but cannot locate segment info, something went wrong..." << endl;
return;
}
const uint64_t id = 0;
auto it = segmentInfos->find(id);
if (it != segmentInfos->end()) {
// found segment with the given id, opening
if (it->second.fAllocationAlgorithm == AllocationAlgorithm::rbtree_best_fit) {
segments.emplace(id, RBTreeBestFitSegment(open_only, fSegmentName.c_str()));
} else {
segments.emplace(id, SimpleSeqFitSegment(open_only, fSegmentName.c_str()));
}
}
fSeenOnce = true; fSeenOnce = true;
@ -292,12 +308,12 @@ void Monitor::CheckSegment()
#endif #endif
if (fInteractive || fViewOnly) { if (fInteractive || fViewOnly) {
DeviceCounter* dc = managementSegment.find<DeviceCounter>(bipc::unique_instance).first; DeviceCounter* dc = managementSegment.find<DeviceCounter>(unique_instance).first;
if (dc) { if (dc) {
numDevices = dc->fCount; numDevices = dc->fCount;
} }
#ifdef FAIRMQ_DEBUG_MODE #ifdef FAIRMQ_DEBUG_MODE
MsgCounter* mc = managementSegment.find<MsgCounter>(bipc::unique_instance).first; MsgCounter* mc = managementSegment.find<MsgCounter>(unique_instance).first;
if (mc) { if (mc) {
numMessages = mc->fCount; numMessages = mc->fCount;
} }
@ -319,20 +335,20 @@ void Monitor::CheckSegment()
if (fInteractive) { if (fInteractive) {
cout << "| " cout << "| "
<< setw(18) << fSegmentName << " | " << setw(18) << fSegmentName << " | "
<< setw(10) << segment.get_size() << " | " << setw(10) << boost::apply_visitor(SegmentSize{}, segments.at(id)) << " | "
<< setw(10) << segment.get_free_memory() << " | " << setw(10) << boost::apply_visitor(SegmentFreeMemory{}, segments.at(id)) << " | "
<< setw(8) << numDevices << " | " << setw(8) << numDevices << " | "
#ifdef FAIRMQ_DEBUG_MODE #ifdef FAIRMQ_DEBUG_MODE
<< setw(8) << numMessages << " | " << setw(8) << numMessages << " | "
#else #else
<< setw(8) << "nodebug" << " | " << setw(8) << "nodebug" << " | "
#endif #endif
<< setw(10) << (fViewOnly ? "view only" : to_string(duration)) << " |" << setw(10) << (fViewOnly ? "view only" : to_string(duration)) << " |"
<< c << flush; << c << flush;
} else if (fViewOnly) { } else if (fViewOnly) {
size_t free = segment.get_free_memory(); size_t free = boost::apply_visitor(SegmentFreeMemory{}, segments.at(id));
size_t total = segment.get_size(); size_t total = boost::apply_visitor(SegmentSize{}, segments.at(id));
size_t used = total - free; size_t used = total - free;
// size_t mfree = managementSegment.get_free_memory(); // size_t mfree = managementSegment.get_free_memory();
// size_t mtotal = managementSegment.get_size(); // size_t mtotal = managementSegment.get_size();
@ -459,44 +475,6 @@ vector<BufferDebugInfo> Monitor::GetDebugInfo(const SessionId& sessionId)
return GetDebugInfo(shmId); return GetDebugInfo(shmId);
} }
void Monitor::PrintQueues()
{
cout << '\n';
try {
bipc::managed_shared_memory segment(bipc::open_only, fSegmentName.c_str());
StrVector* queues = segment.find<StrVector>(string("fmq_" + fShmId + "_qs").c_str()).first;
if (queues) {
cout << "found " << queues->size() << " queue(s):" << endl;
for (const auto& queue : *queues) {
string name(queue.c_str());
cout << '\t' << name << " : ";
atomic<int>* queueSize = segment.find<atomic<int>>(name.c_str()).first;
if (queueSize) {
cout << *queueSize << " messages" << endl;
} else {
cout << "\tqueue does not have a queue size entry." << endl;
}
}
} else {
cout << "\tno queues found" << endl;
}
} catch (bie&) {
cout << "\tno queues found" << endl;
} catch (out_of_range&) {
cout << "\tno queues found" << endl;
}
cout << "\n --> last heartbeats: " << endl << endl;
auto now = chrono::high_resolution_clock::now();
for (const auto& h : fDeviceHeartbeats) {
cout << "\t" << h.first << " : " << chrono::duration<double, milli>(now - h.second).count() << "ms ago." << endl;
}
cout << endl;
}
void Monitor::PrintHeader() void Monitor::PrintHeader()
{ {
cout << "| " cout << "| "
@ -512,7 +490,6 @@ void Monitor::PrintHeader()
void Monitor::PrintHelp() void Monitor::PrintHelp()
{ {
cout << "controls: [x] close memory, " cout << "controls: [x] close memory, "
<< "[p] print queues, "
<< "[b] print a list of allocated messages (only available when compiled with FAIMQ_DEBUG_MODE=ON), " << "[b] print a list of allocated messages (only available when compiled with FAIMQ_DEBUG_MODE=ON), "
<< "[h] help, " << "[h] help, "
<< "[q] quit." << endl; << "[q] quit." << endl;

View File

@ -92,7 +92,6 @@ class Monitor
private: private:
void PrintHeader(); void PrintHeader();
void PrintHelp(); void PrintHelp();
void PrintQueues();
void MonitorHeartbeats(); void MonitorHeartbeats();
void CheckSegment(); void CheckSegment();
void Interactive(); void Interactive();

View File

@ -56,7 +56,7 @@ class Context
throw ContextError(tools::ToString("failed configuring context, reason: ", zmq_strerror(errno))); throw ContextError(tools::ToString("failed configuring context, reason: ", zmq_strerror(errno)));
} }
fRegionEvents.emplace(0, nullptr, 0, 0, RegionEvent::local_only); fRegionEvents.emplace(true, 0, nullptr, 0, 0, RegionEvent::local_only);
} }
Context(const Context&) = delete; Context(const Context&) = delete;
@ -120,13 +120,13 @@ class Context
return fRegionCounter; return fRegionCounter;
} }
void AddRegion(uint64_t id, void* ptr, size_t size, int64_t userFlags, RegionEvent event) void AddRegion(bool managed, uint64_t id, void* ptr, size_t size, int64_t userFlags, RegionEvent event)
{ {
{ {
std::lock_guard<std::mutex> lock(fMtx); std::lock_guard<std::mutex> lock(fMtx);
++fRegionCounter; ++fRegionCounter;
fRegionInfos.emplace_back(id, ptr, size, userFlags, event); fRegionInfos.emplace_back(managed, id, ptr, size, userFlags, event);
fRegionEvents.emplace(id, ptr, size, userFlags, event); fRegionEvents.emplace(managed, id, ptr, size, userFlags, event);
} }
fRegionEventsCV.notify_one(); fRegionEventsCV.notify_one();
} }

View File

@ -125,7 +125,7 @@ class TransportFactory final : public FairMQTransportFactory
{ {
UnmanagedRegionPtr ptr = tools::make_unique<UnmanagedRegion>(*fCtx, size, userFlags, callback, bulkCallback, this); UnmanagedRegionPtr ptr = tools::make_unique<UnmanagedRegion>(*fCtx, size, userFlags, callback, bulkCallback, this);
auto zPtr = static_cast<UnmanagedRegion*>(ptr.get()); auto zPtr = static_cast<UnmanagedRegion*>(ptr.get());
fCtx->AddRegion(zPtr->GetId(), zPtr->GetData(), zPtr->GetSize(), zPtr->GetUserFlags(), RegionEvent::created); fCtx->AddRegion(false, zPtr->GetId(), zPtr->GetData(), zPtr->GetSize(), zPtr->GetUserFlags(), RegionEvent::created);
return ptr; return ptr;
} }

View File

@ -48,6 +48,7 @@ void RegionEventSubscriptions(const string& transport)
ASSERT_EQ(factory->SubscribedToRegionEvents(), false); ASSERT_EQ(factory->SubscribedToRegionEvents(), false);
factory->SubscribeToRegionEvents([&](FairMQRegionInfo info) { factory->SubscribeToRegionEvents([&](FairMQRegionInfo info) {
LOG(warn) << ">>>" << info.event; LOG(warn) << ">>>" << info.event;
LOG(warn) << "managed: " << info.managed;
LOG(warn) << "id: " << info.id; LOG(warn) << "id: " << info.id;
LOG(warn) << "ptr: " << info.ptr; LOG(warn) << "ptr: " << info.ptr;
LOG(warn) << "size: " << info.size; LOG(warn) << "size: " << info.size;