Compare commits

..

28 Commits

Author SHA1 Message Date
Alexey Rybalchenko
1a75141fc4 shm: allow monitor::ResetContent to cleanup after a crash 2022-02-02 10:49:00 +01:00
Alexey Rybalchenko
2f82eb4f09 shm: monitor: disable number of msgs in the ack queue output 2022-02-02 10:49:00 +01:00
Alexey Rybalchenko
92a56c26bc shm: remove UR queues on ResetContent 2022-02-02 10:49:00 +01:00
Alexey Rybalchenko
4f9aeda8ec shm: Add size to UnmanagedRegion debug output 2022-02-02 10:49:00 +01:00
Giulio Eulisse
ad894c79cf GUI Controller
provide a controller which can be used to control state
transitions from an external GUI.
2022-01-25 18:02:25 +01:00
Alexey Rybalchenko
5f33401d41 Parallelize more tests 2022-01-25 11:55:38 +01:00
Alexey Rybalchenko
f4d39d224b Avoid fixed ports in the test suites 2022-01-25 11:55:38 +01:00
Alexey Rybalchenko
bfd08bb33f Don't use to-be-deprecated names 2022-01-24 06:40:24 +01:00
Alexey Rybalchenko
f15f669853 use [[maybe_unused]] for values used in assertions 2022-01-24 06:40:24 +01:00
Alexey Rybalchenko
f6bade32bb modify keep-alive example executable a bit, make it configurable 2022-01-12 19:54:49 +01:00
Alexey Rybalchenko
ddf9bc7272 shm: keep mng segment around when skipping cleanup 2022-01-12 19:54:49 +01:00
Alexey Rybalchenko
f79a0714b4 shm: fix double unlock() 2022-01-12 19:54:49 +01:00
Alexey Rybalchenko
c04958e2a4 shm: reduce contention on region events 2022-01-10 19:42:08 +01:00
Alexey Rybalchenko
692576a5b1 shm: add APIs for implementing keep-alive process 2021-12-16 16:27:07 +01:00
Alexey Rybalchenko
eb4620b1ec shm: always open_or_create segment 2021-12-16 16:27:07 +01:00
Alexey Rybalchenko
9f9583eb55 shm: hide picosha2 from header 2021-12-16 16:27:07 +01:00
Alexey Rybalchenko
08ba068791 shm: remove unused member 2021-12-16 16:27:07 +01:00
Alexey Rybalchenko
1839f7e8c0 shm: integrate mtx and cv into management segment 2021-12-16 16:27:07 +01:00
Alexey Rybalchenko
80ed45df63 extend region config 2021-12-16 16:27:07 +01:00
Alexey Rybalchenko
eef42d2dea simplify region cleanup 2021-12-16 16:27:07 +01:00
Alexey Rybalchenko
d630fbb1e4 consolidate UnmanagedRegion options 2021-12-16 16:27:07 +01:00
Giulio Eulisse
acfb495411 Do not print logo, if not requested 2021-12-14 11:26:10 +01:00
Alexey Rybalchenko
953c4a75c8 refactor: deduplicate more zmq/shmem code 2021-12-06 09:45:39 +01:00
Alexey Rybalchenko
f24dee33c2 Add configurable default snd/rcv timeout 2021-12-06 09:45:39 +01:00
Alexey Rybalchenko
856780f88a fix: install tools/Exceptions.h 2021-11-12 13:20:48 +01:00
Alexey Rybalchenko
dbdf17c661 Avoid accessing Device.fChannels directly, use getters 2021-11-03 20:23:40 +01:00
Alexey Rybalchenko
a3bb5fb4b0 feat: Add Device::GetNumSubChannels(channel) 2021-11-03 20:23:40 +01:00
Alexey Rybalchenko
0eaea3c66f Do not catch and rethrow exception from state handlers 2021-11-03 20:23:40 +01:00
149 changed files with 2508 additions and 1914 deletions

View File

@@ -23,10 +23,10 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fairmq-start-ex-1-1.sh.in ${CMAKE_CUR
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test-ex-1-1.sh.in ${CMAKE_CURRENT_BINARY_DIR}/test-ex-1-1.sh)
add_test(NAME Example.1-1.zeromq COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-1-1.sh zeromq)
set_tests_properties(Example.1-1.zeromq PROPERTIES TIMEOUT "30" RUN_SERIAL true PASS_REGULAR_EXPRESSION "Received: ")
set_tests_properties(Example.1-1.zeromq PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Received: ")
add_test(NAME Example.1-1.shmem COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-1-1.sh shmem)
set_tests_properties(Example.1-1.shmem PROPERTIES TIMEOUT "30" RUN_SERIAL true PASS_REGULAR_EXPRESSION "Received: ")
set_tests_properties(Example.1-1.shmem PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Received: ")
# install

View File

@@ -30,7 +30,7 @@ struct Sampler : fair::mq::Device
// create message object with a pointer to the data buffer, its size,
// custom deletion function (called when transfer is done),
// and pointer to the object managing the data buffer
FairMQMessagePtr msg(NewMessage(
fair::mq::MessagePtr msg(NewMessage(
const_cast<char*>(text->c_str()),
text->length(),
[](void* /*data*/, void* object) { delete static_cast<std::string*>(object); },

View File

@@ -27,7 +27,7 @@ struct Sink : fair::mq::Device
fMaxIterations = fConfig->GetProperty<uint64_t>("max-iterations");
}
bool HandleData(FairMQMessagePtr& msg, int)
bool HandleData(fair::mq::MessagePtr& msg, int)
{
LOG(info) << "Received: \"" << std::string(static_cast<char*>(msg->GetData()), msg->GetSize()) << "\"";

View File

@@ -8,20 +8,23 @@ if [[ $1 =~ ^[a-z]+$ ]]; then
transport=$1
fi
SESSION="$(@CMAKE_BINARY_DIR@/fairmq/fairmq-uuid-gen -h)"
session="$(@CMAKE_BINARY_DIR@/fairmq/fairmq-uuid-gen -h)"
chan="data"
chanAddr="/tmp/fmq_$session""_""$chan""_""$transport"
# setup a trap to kill everything if the test fails/timeouts
trap 'kill -TERM $SAMPLER_PID; kill -TERM $SINK_PID; wait $SAMPLER_PID; wait $SINK_PID;' TERM
trap 'kill -TERM $SAMPLER_PID; kill -TERM $SINK_PID; wait $SAMPLER_PID; wait $SINK_PID; rm $chanAddr' TERM
SAMPLER="fairmq-ex-1-1-sampler"
SAMPLER+=" --id sampler1"
SAMPLER+=" --rate 1"
SAMPLER+=" --transport $transport"
SAMPLER+=" --verbosity veryhigh"
SAMPLER+=" --session $SESSION"
SAMPLER+=" --session $session"
SAMPLER+=" --shm-segment-size 100000000"
SAMPLER+=" --control static --color false"
SAMPLER+=" --max-iterations 1"
SAMPLER+=" --channel-config name=data,type=push,method=bind,address=tcp://*:5555,rateLogging=0"
SAMPLER+=" --channel-config name=$chan,type=push,method=bind,address=ipc://$chanAddr,rateLogging=0"
@CMAKE_CURRENT_BINARY_DIR@/$SAMPLER &
SAMPLER_PID=$!
@@ -29,13 +32,16 @@ SINK="fairmq-ex-1-1-sink"
SINK+=" --id sink1"
SINK+=" --transport $transport"
SINK+=" --verbosity veryhigh"
SINK+=" --session $SESSION"
SINK+=" --session $session"
SINK+=" --shm-segment-size 100000000"
SINK+=" --control static --color false"
SINK+=" --max-iterations 1"
SINK+=" --channel-config name=data,type=pull,method=connect,address=tcp://localhost:5555,rateLogging=0"
SINK+=" --channel-config name=$chan,type=pull,method=connect,address=ipc://$chanAddr,rateLogging=0"
@CMAKE_CURRENT_BINARY_DIR@/$SINK &
SINK_PID=$!
# wait for sampler and sink to finish
wait $SAMPLER_PID
wait $SINK_PID
rm $chanAddr

View File

@@ -29,10 +29,10 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/ex-1-n-1.json ${CMAKE_CURRENT_BINARY_
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test-ex-1-n-1.sh.in ${CMAKE_CURRENT_BINARY_DIR}/test-ex-1-n-1.sh)
add_test(NAME Example.1-n-1.zeromq COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-1-n-1.sh zeromq)
set_tests_properties(Example.1-n-1.zeromq PROPERTIES TIMEOUT "30" RUN_SERIAL true PASS_REGULAR_EXPRESSION "Received: ")
set_tests_properties(Example.1-n-1.zeromq PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Received: ")
add_test(NAME Example.1-n-1.shmem COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-1-n-1.sh shmem)
set_tests_properties(Example.1-n-1.shmem PROPERTIES TIMEOUT "30" RUN_SERIAL true PASS_REGULAR_EXPRESSION "Received: ")
set_tests_properties(Example.1-n-1.shmem PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Received: ")
# install

View File

@@ -20,7 +20,7 @@ struct Processor : fair::mq::Device
OnData("data1", &Processor::HandleData);
}
bool HandleData(FairMQMessagePtr& msg, int)
bool HandleData(fair::mq::MessagePtr& msg, int)
{
LOG(info) << "Received data, processing...";
@@ -32,7 +32,7 @@ struct Processor : fair::mq::Device
// its size,
// custom deletion function (called when transfer is done),
// and pointer to the object managing the data buffer
FairMQMessagePtr msg2(NewMessage(const_cast<char*>(text->c_str()),
fair::mq::MessagePtr msg2(NewMessage(const_cast<char*>(text->c_str()),
text->length(),
[](void* /*data*/, void* object) { delete static_cast<std::string*>(object); },
text));

View File

@@ -28,7 +28,7 @@ struct Sampler : fair::mq::Device
{
// Initializing message with NewStaticMessage will avoid copy
// but won't delete the data after the sending is completed.
FairMQMessagePtr msg(NewStaticMessage(fText));
fair::mq::MessagePtr msg(NewStaticMessage(fText));
LOG(info) << "Sending \"" << fText << "\"";

View File

@@ -27,7 +27,7 @@ struct Sink : fair::mq::Device
fMaxIterations = fConfig->GetProperty<uint64_t>("max-iterations");
}
bool HandleData(FairMQMessagePtr& msg, int)
bool HandleData(fair::mq::MessagePtr& msg, int)
{
LOG(info) << "Received: \"" << std::string(static_cast<char*>(msg->GetData()), msg->GetSize()) << "\"";

View File

@@ -8,20 +8,25 @@ if [[ $1 =~ ^[a-z]+$ ]]; then
transport=$1
fi
ex2config="@CMAKE_CURRENT_BINARY_DIR@/ex-1-n-1.json"
SESSION="$(@CMAKE_BINARY_DIR@/fairmq/fairmq-uuid-gen -h)"
session="$(@CMAKE_BINARY_DIR@/fairmq/fairmq-uuid-gen -h)"
chan1="data1"
chan2="data2"
chan1Addr="/tmp/fmq_$session""_""$chan1""_""$transport"
chan2Addr="/tmp/fmq_$session""_""$chan2""_""$transport"
# setup a trap to kill everything if the test fails/timeouts
trap 'kill -TERM $SAMPLER_PID; kill -TERM $SINK_PID; kill -TERM $PROCESSOR1_PID; kill -TERM $PROCESSOR2_PID; wait $SAMPLER_PID; wait $SINK_PID; wait $PROCESSOR1_PID; wait $PROCESSOR2_PID;' TERM
trap 'kill -TERM $SAMPLER_PID; kill -TERM $SINK_PID; kill -TERM $PROCESSOR1_PID; kill -TERM $PROCESSOR2_PID; wait $SAMPLER_PID; wait $SINK_PID; wait $PROCESSOR1_PID; wait $PROCESSOR2_PID; rm $chan1Addr; rm $chan2Addr' TERM
SAMPLER="fairmq-ex-1-n-1-sampler"
SAMPLER+=" --id sampler1"
SAMPLER+=" --transport $transport"
SAMPLER+=" --verbosity veryhigh"
SAMPLER+=" --session $SESSION"
SAMPLER+=" --session $session"
SAMPLER+=" --severity debug"
SAMPLER+=" --shm-segment-size 100000000"
SAMPLER+=" --control static --color false"
SAMPLER+=" --max-iterations 2"
SAMPLER+=" --mq-config $ex2config"
SAMPLER+=" --channel-config name=$chan1,type=push,method=bind,address=ipc://$chan1Addr,rateLogging=0"
@CMAKE_CURRENT_BINARY_DIR@/$SAMPLER &
SAMPLER_PID=$!
@@ -29,10 +34,12 @@ PROCESSOR1="fairmq-ex-1-n-1-processor"
PROCESSOR1+=" --id processor1"
PROCESSOR1+=" --transport $transport"
PROCESSOR1+=" --verbosity veryhigh"
PROCESSOR1+=" --session $SESSION"
PROCESSOR1+=" --session $session"
PROCESSOR1+=" --severity debug"
PROCESSOR1+=" --shm-segment-size 100000000"
PROCESSOR1+=" --control static --color false"
PROCESSOR1+=" --mq-config $ex2config"
PROCESSOR1+=" --config-key processor"
PROCESSOR1+=" --channel-config name=$chan1,type=pull,method=connect,address=ipc://$chan1Addr,rateLogging=0"
PROCESSOR1+=" name=$chan2,type=push,method=connect,address=ipc://$chan2Addr,rateLogging=0"
@CMAKE_CURRENT_BINARY_DIR@/$PROCESSOR1 &
PROCESSOR1_PID=$!
@@ -40,10 +47,12 @@ PROCESSOR2="fairmq-ex-1-n-1-processor"
PROCESSOR2+=" --id processor2"
PROCESSOR2+=" --transport $transport"
PROCESSOR2+=" --verbosity veryhigh"
PROCESSOR2+=" --session $SESSION"
PROCESSOR2+=" --session $session"
PROCESSOR2+=" --severity debug"
PROCESSOR2+=" --shm-segment-size 100000000"
PROCESSOR2+=" --control static --color false"
PROCESSOR2+=" --mq-config $ex2config"
PROCESSOR2+=" --config-key processor"
PROCESSOR2+=" --channel-config name=$chan1,type=pull,method=connect,address=ipc://$chan1Addr,rateLogging=0"
PROCESSOR2+=" name=$chan2,type=push,method=connect,address=ipc://$chan2Addr,rateLogging=0"
@CMAKE_CURRENT_BINARY_DIR@/$PROCESSOR2 &
PROCESSOR2_PID=$!
@@ -51,10 +60,12 @@ SINK="fairmq-ex-1-n-1-sink"
SINK+=" --id sink1"
SINK+=" --transport $transport"
SINK+=" --verbosity veryhigh"
SINK+=" --session $SESSION"
SINK+=" --session $session"
SINK+=" --severity debug"
SINK+=" --shm-segment-size 100000000"
SINK+=" --control static --color false"
SINK+=" --max-iterations 2"
SINK+=" --mq-config $ex2config"
SINK+=" --channel-config name=$chan2,type=pull,method=bind,address=ipc://$chan2Addr,rateLogging=0"
@CMAKE_CURRENT_BINARY_DIR@/$SINK &
SINK_PID=$!
@@ -69,3 +80,5 @@ kill -SIGINT $PROCESSOR2_PID
# wait for everything to finish
wait $PROCESSOR1_PID
wait $PROCESSOR2_PID
rm $chan1Addr; rm $chan2Addr

View File

@@ -15,16 +15,16 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fairmq-start-ex-builtin-devices.sh.in
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test-ex-builtin-devices.sh.in ${CMAKE_CURRENT_BINARY_DIR}/test-ex-builtin-devices.sh)
add_test(NAME Example.BuiltinDevices.zeromq COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-builtin-devices.sh zeromq)
set_tests_properties(Example.BuiltinDevices.zeromq PROPERTIES TIMEOUT "30" RUN_SERIAL true PASS_REGULAR_EXPRESSION "Configured maximum number of iterations reached")
set_tests_properties(Example.BuiltinDevices.zeromq PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Configured maximum number of iterations reached")
add_test(NAME Example.BuiltinDevices.shmem COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-builtin-devices.sh shmem)
set_tests_properties(Example.BuiltinDevices.shmem PROPERTIES TIMEOUT "30" RUN_SERIAL true PASS_REGULAR_EXPRESSION "Configured maximum number of iterations reached")
set_tests_properties(Example.BuiltinDevices.shmem PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Configured maximum number of iterations reached")
add_test(NAME Example.BuiltinDevices.multipart.zeromq COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-builtin-devices.sh zeromq true 2)
set_tests_properties(Example.BuiltinDevices.multipart.zeromq PROPERTIES TIMEOUT "30" RUN_SERIAL true PASS_REGULAR_EXPRESSION "Configured maximum number of iterations reached")
set_tests_properties(Example.BuiltinDevices.multipart.zeromq PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Configured maximum number of iterations reached")
add_test(NAME Example.BuiltinDevices.multipart.shmem COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-builtin-devices.sh shmem true 2)
set_tests_properties(Example.BuiltinDevices.multipart.shmem PROPERTIES TIMEOUT "30" RUN_SERIAL true PASS_REGULAR_EXPRESSION "Configured maximum number of iterations reached")
set_tests_properties(Example.BuiltinDevices.multipart.shmem PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Configured maximum number of iterations reached")
# install

View File

@@ -2,8 +2,6 @@
export FAIRMQ_PATH=@FAIRMQ_BIN_DIR@
SESSION="$(@CMAKE_BINARY_DIR@/fairmq/fairmq-uuid-gen -h)"
transport="zeromq"
multipart="false"
numParts="1"
@@ -20,8 +18,22 @@ if [[ $3 =~ ^[0-9]+$ ]]; then
numParts=$3
fi
session="$(@CMAKE_BINARY_DIR@/fairmq/fairmq-uuid-gen -h)"
chan1="data1"
chan2="data2"
chan3="data3"
chan4="data4"
chan5="data5"
chan1Addr="/tmp/fmq_$session""_""$chan1""_""$transport"
chan2Addr1="/tmp/fmq_$session""_""$chan2""_1""_""$transport"
chan2Addr2="/tmp/fmq_$session""_""$chan2""_2""_""$transport"
chan3Addr1="/tmp/fmq_$session""_""$chan3""_1""_""$transport"
chan3Addr2="/tmp/fmq_$session""_""$chan3""_2""_""$transport"
chan4Addr="/tmp/fmq_$session""_""$chan4""_""$transport"
chan5Addr="/tmp/fmq_$session""_""$chan5""_""$transport"
# setup a trap to kill everything if the test fails/timeouts
trap 'kill -TERM $SAMPLER_PID; kill -TERM $SPLITTER_PID; kill -TERM $PROXY1_PID; kill -TERM $PROXY2_PID; kill -TERM $MERGER_PID; kill -TERM $MULTIPLIER_PID; kill -TERM $SINK_PID;' TERM
trap 'kill -TERM $SAMPLER_PID; kill -TERM $SPLITTER_PID; kill -TERM $PROXY1_PID; kill -TERM $PROXY2_PID; kill -TERM $MERGER_PID; kill -TERM $MULTIPLIER_PID; kill -TERM $SINK_PID; rm $chan1Addr; rm $chan2Addr1; rm $chan2Addr2; rm $chan3Addr1; rm $chan3Addr2; rm $chan4Addr; rm $chan5Addr' TERM
SAMPLER="fairmq-bsampler"
SAMPLER+=" --id bsampler1"
@@ -30,14 +42,15 @@ SAMPLER+=" --transport $transport"
SAMPLER+=" --color false"
SAMPLER+=" --control static"
SAMPLER+=" --verbosity veryhigh"
SAMPLER+=" --shm-segment-size 100000000"
SAMPLER+=" --severity debug"
SAMPLER+=" --msg-size 100000"
SAMPLER+=" --multipart $multipart"
SAMPLER+=" --num-parts $numParts"
SAMPLER+=" --msg-rate 1"
SAMPLER+=" --max-iterations 0"
SAMPLER+=" --out-channel data1"
SAMPLER+=" --channel-config name=data1,type=push,method=bind,sndBufSize=50,rcvBufSize=50,address=tcp://localhost:5555"
SAMPLER+=" --out-channel $chan1"
SAMPLER+=" --channel-config name=$chan1,type=push,method=bind,sndBufSize=50,rcvBufSize=50,address=ipc://$chan1Addr"
@FAIRMQ_BIN_DIR@/$SAMPLER &
SAMPLER_PID=$!
@@ -48,11 +61,12 @@ SPLITTER+=" --transport $transport"
SPLITTER+=" --color false"
SPLITTER+=" --control static"
SPLITTER+=" --verbosity veryhigh"
SPLITTER+=" --shm-segment-size 100000000"
SPLITTER+=" --multipart $multipart"
SPLITTER+=" --in-channel data1"
SPLITTER+=" --out-channel data2"
SPLITTER+=" --channel-config name=data1,type=pull,method=connect,sndBufSize=50,rcvBufSize=50,address=tcp://localhost:5555"
SPLITTER+=" name=data2,type=push,method=bind,sndBufSize=50,rcvBufSize=50,address=tcp://localhost:5556,address=tcp://localhost:5557"
SPLITTER+=" --in-channel $chan1"
SPLITTER+=" --out-channel $chan2"
SPLITTER+=" --channel-config name=$chan1,type=pull,method=connect,sndBufSize=50,rcvBufSize=50,address=ipc://$chan1Addr"
SPLITTER+=" name=$chan2,type=push,method=bind,sndBufSize=50,rcvBufSize=50,address=ipc://$chan2Addr1,address=ipc://$chan2Addr2"
@FAIRMQ_BIN_DIR@/$SPLITTER &
SPLITTER_PID=$!
@@ -63,11 +77,12 @@ PROXY1+=" --transport $transport"
PROXY1+=" --color false"
PROXY1+=" --control static"
PROXY1+=" --verbosity veryhigh"
PROXY1+=" --shm-segment-size 100000000"
PROXY1+=" --multipart $multipart"
PROXY1+=" --in-channel data2"
PROXY1+=" --out-channel data3"
PROXY1+=" --channel-config name=data2,type=pull,method=connect,sndBufSize=50,rcvBufSize=50,address=tcp://localhost:5556"
PROXY1+=" name=data3,type=push,method=connect,sndBufSize=50,rcvBufSize=50,address=tcp://localhost:5558"
PROXY1+=" --in-channel $chan2"
PROXY1+=" --out-channel $chan3"
PROXY1+=" --channel-config name=$chan2,type=pull,method=connect,sndBufSize=50,rcvBufSize=50,address=ipc://$chan2Addr1"
PROXY1+=" name=$chan3,type=push,method=connect,sndBufSize=50,rcvBufSize=50,address=ipc://$chan3Addr1"
@FAIRMQ_BIN_DIR@/$PROXY1 &
PROXY1_PID=$!
@@ -78,11 +93,12 @@ PROXY2+=" --transport $transport"
PROXY2+=" --color false"
PROXY2+=" --control static"
PROXY2+=" --verbosity veryhigh"
PROXY2+=" --shm-segment-size 100000000"
PROXY2+=" --multipart $multipart"
PROXY2+=" --in-channel data2"
PROXY2+=" --out-channel data3"
PROXY2+=" --channel-config name=data2,type=pull,method=connect,sndBufSize=50,rcvBufSize=50,address=tcp://localhost:5557"
PROXY2+=" name=data3,type=push,method=connect,sndBufSize=50,rcvBufSize=50,address=tcp://localhost:5559"
PROXY2+=" --in-channel $chan2"
PROXY2+=" --out-channel $chan3"
PROXY2+=" --channel-config name=$chan2,type=pull,method=connect,sndBufSize=50,rcvBufSize=50,address=ipc://$chan2Addr2"
PROXY2+=" name=$chan3,type=push,method=connect,sndBufSize=50,rcvBufSize=50,address=ipc://$chan3Addr2"
@FAIRMQ_BIN_DIR@/$PROXY2 &
PROXY2_PID=$!
@@ -93,11 +109,12 @@ MERGER+=" --transport $transport"
MERGER+=" --color false"
MERGER+=" --control static"
MERGER+=" --verbosity veryhigh"
MERGER+=" --shm-segment-size 100000000"
MERGER+=" --multipart $multipart"
MERGER+=" --in-channel data3"
MERGER+=" --out-channel data4"
MERGER+=" --channel-config name=data3,type=pull,method=bind,sndBufSize=50,rcvBufSize=50,address=tcp://localhost:5558,address=tcp://localhost:5559"
MERGER+=" name=data4,type=push,method=connect,sndBufSize=50,rcvBufSize=50,address=tcp://localhost:5560"
MERGER+=" --in-channel $chan3"
MERGER+=" --out-channel $chan4"
MERGER+=" --channel-config name=$chan3,type=pull,method=bind,sndBufSize=50,rcvBufSize=50,address=ipc://$chan3Addr1,address=ipc://$chan3Addr2"
MERGER+=" name=$chan4,type=push,method=connect,sndBufSize=50,rcvBufSize=50,address=ipc://$chan4Addr"
@FAIRMQ_BIN_DIR@/$MERGER &
MERGER_PID=$!
@@ -108,11 +125,12 @@ MULTIPLIER+=" --transport $transport"
MULTIPLIER+=" --color false"
MULTIPLIER+=" --control static"
MULTIPLIER+=" --verbosity veryhigh"
MULTIPLIER+=" --shm-segment-size 100000000"
MULTIPLIER+=" --multipart $multipart"
MULTIPLIER+=" --in-channel data4"
MULTIPLIER+=" --out-channel data5"
MULTIPLIER+=" --channel-config name=data4,type=pull,method=bind,sndBufSize=50,rcvBufSize=50,address=tcp://localhost:5560"
MULTIPLIER+=" name=data5,type=push,method=connect,sndBufSize=50,rcvBufSize=50,address=tcp://localhost:5561,address=tcp://localhost:5561"
MULTIPLIER+=" --in-channel $chan4"
MULTIPLIER+=" --out-channel $chan5"
MULTIPLIER+=" --channel-config name=$chan4,type=pull,method=bind,sndBufSize=50,rcvBufSize=50,address=ipc://$chan4Addr"
MULTIPLIER+=" name=$chan5,type=push,method=connect,sndBufSize=50,rcvBufSize=50,address=ipc://$chan5Addr,address=ipc://$chan5Addr"
@FAIRMQ_BIN_DIR@/$MULTIPLIER &
MULTIPLIER_PID=$!
@@ -126,8 +144,8 @@ SINK+=" --verbosity veryhigh"
SINK+=" --severity debug"
SINK+=" --multipart $multipart"
SINK+=" --max-iterations 2"
SINK+=" --in-channel data5"
SINK+=" --channel-config name=data5,type=pull,method=bind,sndBufSize=50,rcvBufSize=50,address=tcp://localhost:5561"
SINK+=" --in-channel $chan5"
SINK+=" --channel-config name=$chan5,type=pull,method=bind,sndBufSize=50,rcvBufSize=50,address=ipc://$chan5Addr"
@FAIRMQ_BIN_DIR@/$SINK &
SINK_PID=$!
@@ -146,3 +164,5 @@ wait $PROXY1_PID
wait $PROXY2_PID
wait $MERGER_PID
wait $MULTIPLIER_PID
rm $chan1Addr; rm $chan2Addr1; rm $chan2Addr2; rm $chan3Addr1; rm $chan3Addr2; rm $chan4Addr; rm $chan5Addr

View File

@@ -24,10 +24,10 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fairmq-start-ex-copypush.sh.in ${CMAK
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test-ex-copypush.sh.in ${CMAKE_CURRENT_BINARY_DIR}/test-ex-copypush.sh)
add_test(NAME Example.CopyPush.zeromq COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-copypush.sh zeromq)
set_tests_properties(Example.CopyPush.zeromq PROPERTIES TIMEOUT "30" RUN_SERIAL true PASS_REGULAR_EXPRESSION "Received message: ")
set_tests_properties(Example.CopyPush.zeromq PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Received message: ")
add_test(NAME Example.CopyPush.shmem COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-copypush.sh shmem)
set_tests_properties(Example.CopyPush.shmem PROPERTIES TIMEOUT "30" RUN_SERIAL true PASS_REGULAR_EXPRESSION "Received message: ")
set_tests_properties(Example.CopyPush.shmem PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Received message: ")
# install

View File

@@ -19,7 +19,7 @@ struct Sampler : fair::mq::Device
{
void InitTask() override
{
fNumDataChannels = fChannels.at("data").size();
fNumDataChannels = GetNumSubChannels("data");
fMaxIterations = fConfig->GetProperty<uint64_t>("max-iterations");
}
@@ -27,10 +27,10 @@ struct Sampler : fair::mq::Device
{
// NewSimpleMessage creates a copy of the data and takes care of its destruction (after the transfer takes place).
// Should only be used for small data because of the cost of an additional copy
FairMQMessagePtr msg(NewSimpleMessage(fCounter++));
fair::mq::MessagePtr msg(NewSimpleMessage(fCounter++));
for (int i = 0; i < fNumDataChannels - 1; ++i) {
FairMQMessagePtr msgCopy(NewMessage());
fair::mq::MessagePtr msgCopy(NewMessage());
msgCopy->Copy(*msg);
Send(msgCopy, "data", i);
}

View File

@@ -27,7 +27,7 @@ struct Sink : fair::mq::Device
fMaxIterations = fConfig->GetProperty<uint64_t>("max-iterations");
}
bool HandleData(FairMQMessagePtr& msg, int)
bool HandleData(fair::mq::MessagePtr& msg, int)
{
LOG(info) << "Received message: \"" << *(static_cast<uint64_t*>(msg->GetData())) << "\"";

View File

@@ -8,19 +8,24 @@ if [[ $1 =~ ^[a-z]+$ ]]; then
transport=$1
fi
SESSION="$(@CMAKE_BINARY_DIR@/fairmq/fairmq-uuid-gen -h)"
session="$(@CMAKE_BINARY_DIR@/fairmq/fairmq-uuid-gen -h)"
chan="data"
chanAddr1="/tmp/fmq_$session""_""$chan""_1""_""$transport"
chanAddr2="/tmp/fmq_$session""_""$chan""_2""_""$transport"
# setup a trap to kill everything if the test fails/timeouts
trap 'kill -TERM $SAMPLER_PID; kill -TERM $SINK1_PID; kill -TERM $SINK2_PID; wait $SAMPLER_PID; wait $SINK1_PID; wait $SINK2_PID;' TERM
trap 'kill -TERM $SAMPLER_PID; kill -TERM $SINK1_PID; kill -TERM $SINK2_PID; wait $SAMPLER_PID; wait $SINK1_PID; wait $SINK2_PID; rm $chanAddr1; rm $chanAddr2' TERM
SAMPLER="fairmq-ex-copypush-sampler"
SAMPLER+=" --id sampler1"
SAMPLER+=" --transport $transport"
SAMPLER+=" --verbosity veryhigh"
SAMPLER+=" --session $SESSION"
SAMPLER+=" --severity debug"
SAMPLER+=" --shm-segment-size 100000000"
SAMPLER+=" --session $session"
SAMPLER+=" --control static --color false"
SAMPLER+=" --max-iterations 1"
SAMPLER+=" --channel-config name=data,type=push,method=bind,rateLogging=0,address=tcp://*:5555,address=tcp://*:5556"
SAMPLER+=" --channel-config name=$chan,type=push,method=bind,rateLogging=0,address=ipc://$chanAddr1,address=ipc://$chanAddr2"
@CMAKE_CURRENT_BINARY_DIR@/$SAMPLER &
SAMPLER_PID=$!
@@ -28,10 +33,12 @@ SINK1="fairmq-ex-copypush-sink"
SINK1+=" --id sink1"
SINK1+=" --transport $transport"
SINK1+=" --verbosity veryhigh"
SINK1+=" --session $SESSION"
SINK1+=" --severity debug"
SINK1+=" --shm-segment-size 100000000"
SINK1+=" --session $session"
SINK1+=" --control static --color false"
SINK1+=" --max-iterations 1"
SINK1+=" --channel-config name=data,type=pull,method=connect,rateLogging=0,address=tcp://localhost:5555"
SINK1+=" --channel-config name=$chan,type=pull,method=connect,rateLogging=0,address=ipc://$chanAddr1"
@CMAKE_CURRENT_BINARY_DIR@/$SINK1 &
SINK1_PID=$!
@@ -39,10 +46,12 @@ SINK2="fairmq-ex-copypush-sink"
SINK2+=" --id sink2"
SINK2+=" --transport $transport"
SINK2+=" --verbosity veryhigh"
SINK2+=" --session $SESSION"
SINK2+=" --severity debug"
SINK2+=" --shm-segment-size 100000000"
SINK2+=" --session $session"
SINK2+=" --control static --color false"
SINK2+=" --max-iterations 1"
SINK2+=" --channel-config name=data,type=pull,method=connect,rateLogging=0,address=tcp://localhost:5556"
SINK2+=" --channel-config name=$chan,type=pull,method=connect,rateLogging=0,address=ipc://$chanAddr2"
@CMAKE_CURRENT_BINARY_DIR@/$SINK2 &
SINK2_PID=$!
@@ -50,3 +59,5 @@ SINK2_PID=$!
wait $SAMPLER_PID
wait $SINK1_PID
wait $SINK2_PID
rm $chanAddr1; rm $chanAddr2

View File

@@ -20,7 +20,7 @@ struct Processor : fair::mq::Device
OnData("data1", &Processor::HandleData);
}
bool HandleData(FairMQMessagePtr& msg, int)
bool HandleData(fair::mq::MessagePtr& msg, int)
{
LOG(info) << "Received data, processing...";
@@ -32,7 +32,7 @@ struct Processor : fair::mq::Device
// its size,
// custom deletion function (called when transfer is done),
// and pointer to the object managing the data buffer
FairMQMessagePtr msg2(NewMessage(const_cast<char*>(text->c_str()),
fair::mq::MessagePtr msg2(NewMessage(const_cast<char*>(text->c_str()),
text->length(),
[](void* /*data*/, void* object) { delete static_cast<std::string*>(object); },
text));

View File

@@ -24,7 +24,7 @@ struct Sampler : fair::mq::Device
{
// NewSimpleMessage creates a copy of the data and takes care of its destruction (after the transfer takes place).
// Should only be used for small data because of the cost of an additional copy
FairMQMessagePtr msg(NewSimpleMessage("Data"));
fair::mq::MessagePtr msg(NewSimpleMessage("Data"));
LOG(info) << "Sending \"Data\"";

View File

@@ -25,7 +25,7 @@ struct Sink : fair::mq::Device
fIterations = fConfig->GetValue<uint64_t>("iterations");
}
bool HandleData(FairMQMessagePtr& msg, int)
bool HandleData(fair::mq::MessagePtr& msg, int)
{
LOG(info) << "Received: \"" << std::string(static_cast<char*>(msg->GetData()), msg->GetSize()) << "\"";

View File

@@ -23,14 +23,14 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fairmq-start-ex-multipart.sh.in ${CMA
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test-ex-multipart.sh.in ${CMAKE_CURRENT_BINARY_DIR}/test-ex-multipart.sh)
add_test(NAME Example.Multipart.zeromq COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-multipart.sh zeromq)
set_tests_properties(Example.Multipart.zeromq PROPERTIES TIMEOUT "30" RUN_SERIAL true PASS_REGULAR_EXPRESSION "Received message with 7 parts")
set_tests_properties(Example.Multipart.zeromq PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Received message with 7 parts")
add_test(NAME Example.Multipart.shmem COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-multipart.sh shmem)
set_tests_properties(Example.Multipart.shmem PROPERTIES TIMEOUT "30" RUN_SERIAL true PASS_REGULAR_EXPRESSION "Received message with 7 parts")
set_tests_properties(Example.Multipart.shmem PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Received message with 7 parts")
if(BUILD_OFI_TRANSPORT)
add_test(NAME Example.Multipart.ofi COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-multipart.sh ofi)
set_tests_properties(Example.Multipart.ofi PROPERTIES TIMEOUT "30" RUN_SERIAL true PASS_REGULAR_EXPRESSION "Received message with 7 parts")
set_tests_properties(Example.Multipart.ofi PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Received message with 7 parts")
endif()
# install

View File

@@ -5,7 +5,7 @@ A topology of two devices - Sampler and Sink, communicating with PUSH-PULL patte
The Sampler sends a multipart message to the Sink, consisting of two message parts - header and body.
Each message part is a regular FairMQMessage. To combine them into a multi-part message use `FairMQParts`. Add messages to `FairMQParts` with `AddPart` method.
Each message part is a regular fair::mq::Message. To combine them into a multi-part message use `fair::mq::Parts`. Add messages to `fair::mq::Parts` with `AddPart` method.
All parts are guaranteed to be delivered together. The Receive call in the sink will recive the entire parts structure.

View File

@@ -35,15 +35,15 @@ struct Sampler : fair::mq::Device
}
LOG(info) << "Sending header with stopFlag: " << header.stopFlag;
FairMQParts parts;
fair::mq::Parts parts;
// NewSimpleMessage creates a copy of the data and takes care of its destruction (after the transfer takes place).
// Should only be used for small data because of the cost of an additional copy
parts.AddPart(NewSimpleMessage(header));
parts.AddPart(NewMessage(1000));
// create more data parts, testing the FairMQParts in-place constructor
FairMQParts auxData{ NewMessage(500), NewMessage(600), NewMessage(700) };
// create more data parts, testing the fair::mq::Parts in-place constructor
fair::mq::Parts auxData{ NewMessage(500), NewMessage(600), NewMessage(700) };
assert(auxData.Size() == 3);
parts.AddPart(std::move(auxData));
assert(auxData.Size() == 0);

View File

@@ -20,7 +20,7 @@ struct Sink : fair::mq::Device
OnData("data", &Sink::HandleData);
}
bool HandleData(FairMQParts& parts, int)
bool HandleData(fair::mq::Parts& parts, int)
{
LOG(info) << "Received message with " << parts.Size() << " parts";

View File

@@ -8,19 +8,28 @@ if [[ $1 =~ ^[a-z]+$ ]]; then
transport=$1
fi
SESSION="$(@CMAKE_BINARY_DIR@/fairmq/fairmq-uuid-gen -h)"
session="$(@CMAKE_BINARY_DIR@/fairmq/fairmq-uuid-gen -h)"
chan="data"
chanAddr=""
chanIpcFile="/tmp/fmq_$session""_""$chan""_""$transport"
if [ $transport = "ofi" ]; then
chanAddr="tcp://127.0.0.1:5656"
else
chanAddr="ipc://""$chanIpcFile"
fi
# setup a trap to kill everything if the test fails/timeouts
trap 'kill -TERM $SAMPLER_PID; kill -TERM $SINK_PID; wait $SAMPLER_PID; wait $SINK_PID;' TERM
trap 'kill -TERM $SAMPLER_PID; kill -TERM $SINK_PID; wait $SAMPLER_PID; wait $SINK_PID; rm $chanIpcFile' TERM
SAMPLER="fairmq-ex-multipart-sampler"
SAMPLER+=" --id sampler1"
SAMPLER+=" --transport $transport"
SAMPLER+=" --verbosity veryhigh"
SAMPLER+=" --session $SESSION"
SAMPLER+=" --session $session"
SAMPLER+=" --shm-segment-size 100000000"
SAMPLER+=" --max-iterations 1"
SAMPLER+=" --control static --color false"
SAMPLER+=" --channel-config name=data,type=pair,method=connect,rateLogging=0,address=tcp://127.0.0.1:5555,linger=1000"
SAMPLER+=" --channel-config name=$chan,type=pair,method=connect,rateLogging=0,address=$chanAddr,linger=1000"
@CMAKE_CURRENT_BINARY_DIR@/$SAMPLER &
SAMPLER_PID=$!
@@ -28,11 +37,14 @@ SINK="fairmq-ex-multipart-sink"
SINK+=" --id sink1"
SINK+=" --transport $transport"
SINK+=" --verbosity veryhigh"
SINK+=" --session $SESSION"
SINK+=" --session $session"
SINK+=" --shm-segment-size 100000000"
SINK+=" --control static --color false"
SINK+=" --channel-config name=data,type=pair,method=bind,rateLogging=0,address=tcp://127.0.0.1:5555"
SINK+=" --channel-config name=$chan,type=pair,method=bind,rateLogging=0,address=$chanAddr"
@CMAKE_CURRENT_BINARY_DIR@/$SINK &
SINK_PID=$!
wait $SAMPLER_PID
wait $SINK_PID
rm $chanIpcFile

View File

@@ -26,7 +26,7 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fairmq-start-ex-multiple-channels.sh.
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test-ex-multiple-channels.sh.in ${CMAKE_CURRENT_BINARY_DIR}/test-ex-multiple-channels.sh)
add_test(NAME Example.MultipleChannels.zeromq COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-multiple-channels.sh zeromq)
set_tests_properties(Example.MultipleChannels.zeromq PROPERTIES TIMEOUT "30" RUN_SERIAL true PASS_REGULAR_EXPRESSION "Received messages from both sources.")
set_tests_properties(Example.MultipleChannels.zeromq PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Received messages from both sources.")
# install

View File

@@ -5,4 +5,4 @@ This example demonstrates how to work with multiple channels and multiplex betwe
A topology of three devices - **Sampler**, **Sink** and **Broadcaster**. The Sampler sends data to the Sink via the PUSH-PULL pattern. The Broadcaster device sends a message to both Sampler and Sink containing a string "OK" every second. The Broadcaster sends the message via PUB pattern. Both Sampler and Sink, besides doing their PUSH-PULL job, listen via SUB to the Broadcaster.
The multiplexing between their data channels and the broadcast channels happens with `FairMQPoller`.
The multiplexing between their data channels and the broadcast channels happens with `fair::mq::Poller`.

View File

@@ -22,7 +22,7 @@ struct Broadcaster : fair::mq::Device
// NewSimpleMessage creates a copy of the data and takes care of its destruction (after the transfer takes place).
// Should only be used for small data because of the cost of an additional copy
FairMQMessagePtr msg(NewSimpleMessage("OK"));
fair::mq::MessagePtr msg(NewSimpleMessage("OK"));
LOG(info) << "Sending OK";

View File

@@ -7,7 +7,7 @@
********************************************************************************/
#include <fairmq/Device.h>
#include <FairMQPoller.h>
#include <fairmq/Poller.h>
#include <fairmq/runDevice.h>
#include <chrono>
@@ -26,13 +26,13 @@ struct Sampler : fair::mq::Device
void Run() override
{
FairMQPollerPtr poller(NewPoller("data", "broadcast"));
fair::mq::PollerPtr poller(NewPoller("data", "broadcast"));
while (!NewStatePending()) {
poller->Poll(100);
if (poller->CheckInput("broadcast", 0)) {
FairMQMessagePtr msg(NewMessage());
fair::mq::MessagePtr msg(NewMessage());
if (Receive(msg, "broadcast") > 0) {
LOG(info) << "Received broadcast: \"" << std::string(static_cast<char*>(msg->GetData()), msg->GetSize()) << "\"";
@@ -42,7 +42,7 @@ struct Sampler : fair::mq::Device
if (poller->CheckOutput("data", 0)) {
std::this_thread::sleep_for(std::chrono::seconds(1));
FairMQMessagePtr msg(NewSimpleMessage(fText));
fair::mq::MessagePtr msg(NewSimpleMessage(fText));
if (Send(msg, "data") > 0) {
LOG(info) << "Sent \"" << fText << "\"";

View File

@@ -27,7 +27,7 @@ struct Sink : fair::mq::Device
fMaxIterations = fConfig->GetProperty<uint64_t>("max-iterations");
}
bool HandleBroadcast(FairMQMessagePtr& msg, int /*index*/)
bool HandleBroadcast(fair::mq::MessagePtr& msg, int /*index*/)
{
LOG(info) << "Received broadcast: \"" << std::string(static_cast<char*>(msg->GetData()), msg->GetSize()) << "\"";
fReceivedBroadcast = true;
@@ -35,7 +35,7 @@ struct Sink : fair::mq::Device
return CheckIterations();
}
bool HandleData(FairMQMessagePtr& msg, int /*index*/)
bool HandleData(fair::mq::MessagePtr& msg, int /*index*/)
{
LOG(info) << "Received message: \"" << std::string(static_cast<char*>(msg->GetData()), msg->GetSize()) << "\"";
fReceivedData = true;

View File

@@ -8,18 +8,25 @@ if [[ $1 =~ ^[a-z]+$ ]]; then
transport=$1
fi
# setup a trap to kill everything if the test fails/timeouts
trap 'kill -TERM $SAMPLER_PID; kill -TERM $SINK_PID; kill -TERM $BROADCASTER_PID; wait $SAMPLER_PID; wait $SINK_PID; wait $BROADCASTER_PID;' TERM
session="$(@CMAKE_BINARY_DIR@/fairmq/fairmq-uuid-gen -h)"
dataChan="data"
broadcastChan="broadcast"
dataChanAddr="/tmp/fmq_$session""_""$dataChan""_""$transport"
broadcastChanAddr="/tmp/fmq_$session""_""$broadcastChan""_""$transport"
# setup a trap to kill everything if the test fails/timeouts
trap 'kill -TERM $SAMPLER_PID; kill -TERM $SINK_PID; kill -TERM $BROADCASTER_PID; wait $SAMPLER_PID; wait $SINK_PID; wait $BROADCASTER_PID; rm $dataChanAddr; rm $broadcastChanAddr' TERM
SINK="fairmq-ex-multiple-channels-sink"
SINK+=" --id sink1"
SINK+=" --session $session"
SINK+=" --transport $transport"
SINK+=" --verbosity veryhigh"
SINK+=" --verbosity veryhigh --severity debug"
SINK+=" --shm-segment-size 100000000"
SINK+=" --max-iterations 1"
SINK+=" --control static --color false"
SINK+=" --channel-config name=data,type=pull,method=connect,rateLogging=0,address=tcp://localhost:5555"
SINK+=" name=broadcast,type=sub,method=connect,rateLogging=0,address=tcp://localhost:5005"
SINK+=" --channel-config name=$dataChan,type=pull,method=connect,rateLogging=0,address=ipc://$dataChanAddr"
SINK+=" name=$broadcastChan,type=sub,method=connect,rateLogging=0,address=ipc://$broadcastChanAddr"
@CMAKE_CURRENT_BINARY_DIR@/$SINK &
SINK_PID=$!
@@ -27,21 +34,25 @@ sleep 1
SAMPLER="fairmq-ex-multiple-channels-sampler"
SAMPLER+=" --id sampler1"
SAMPLER+=" --session $session"
SAMPLER+=" --transport $transport"
SAMPLER+=" --verbosity veryhigh"
SAMPLER+=" --verbosity veryhigh --severity debug"
SAMPLER+=" --shm-segment-size 100000000"
SAMPLER+=" --max-iterations 1"
SAMPLER+=" --control static --color false"
SAMPLER+=" --channel-config name=data,type=push,method=bind,rateLogging=0,address=tcp://*:5555"
SAMPLER+=" name=broadcast,type=sub,method=connect,rateLogging=0,address=tcp://localhost:5005"
SAMPLER+=" --channel-config name=$dataChan,type=push,method=bind,rateLogging=0,address=ipc://$dataChanAddr"
SAMPLER+=" name=$broadcastChan,type=sub,method=connect,rateLogging=0,address=ipc://$broadcastChanAddr"
@CMAKE_CURRENT_BINARY_DIR@/$SAMPLER &
SAMPLER_PID=$!
BROADCASTER="fairmq-ex-multiple-channels-broadcaster"
BROADCASTER+=" --id broadcaster1"
BROADCASTER+=" --session $session"
BROADCASTER+=" --transport $transport"
BROADCASTER+=" --verbosity veryhigh"
BROADCASTER+=" --verbosity veryhigh --severity debug"
BROADCASTER+=" --shm-segment-size 100000000"
BROADCASTER+=" --control static --color false"
BROADCASTER+=" --channel-config name=broadcast,type=pub,method=bind,rateLogging=0,address=tcp://*:5005"
BROADCASTER+=" --channel-config name=$broadcastChan,type=pub,method=bind,rateLogging=0,address=ipc://$broadcastChanAddr"
@CMAKE_CURRENT_BINARY_DIR@/$BROADCASTER &
BROADCASTER_PID=$!
@@ -53,3 +64,5 @@ kill -SIGINT $BROADCASTER_PID
# wait for broadcaster to finish
wait $BROADCASTER_PID
rm $dataChanAddr; rm $broadcastChanAddr

View File

@@ -25,7 +25,7 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fairmq-start-ex-multiple-transports.s
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test-ex-multiple-transports.sh.in ${CMAKE_CURRENT_BINARY_DIR}/test-ex-multiple-transports.sh)
add_test(NAME Example.MultipleTransports COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-multiple-transports.sh)
set_tests_properties(Example.MultipleTransports PROPERTIES TIMEOUT "30" RUN_SERIAL true PASS_REGULAR_EXPRESSION "Received messages from both sources.")
set_tests_properties(Example.MultipleTransports PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Received messages from both sources.")
# install

View File

@@ -28,7 +28,7 @@ struct Sampler1 : fair::mq::Device
bool ConditionalRun() override
{
// Creates a message using the transport of channel data1
FairMQMessagePtr msg(NewMessageFor("data1", 0, 1000000));
fair::mq::MessagePtr msg(NewMessageFor("data1", 0, 1000000));
// in case of error or transfer interruption, return false to go to IDLE state
// successfull transfer will return number of bytes transfered (can be 0 if sending an empty message).
@@ -54,7 +54,7 @@ struct Sampler1 : fair::mq::Device
uint64_t numAcks = 0;
while (!NewStatePending()) {
FairMQMessagePtr ack(NewMessageFor("ack", 0));
fair::mq::MessagePtr ack(NewMessageFor("ack", 0));
if (Receive(ack, "ack") < 0) {
break;
}

View File

@@ -22,7 +22,7 @@ struct Sampler2 : fair::mq::Device
bool ConditionalRun() override
{
FairMQMessagePtr msg(NewMessage(1000));
fair::mq::MessagePtr msg(NewMessage(1000));
// in case of error or transfer interruption, return false to go to IDLE state
// successfull transfer will return number of bytes transfered (can be 0 if sending an empty message).

View File

@@ -26,11 +26,11 @@ struct Sink : fair::mq::Device
}
// handler is called whenever a message arrives on "data", with a reference to the message and a sub-channel index (here 0)
bool HandleData1(FairMQMessagePtr& /*msg*/, int /*index*/)
bool HandleData1(fair::mq::MessagePtr& /*msg*/, int /*index*/)
{
fNumIterations1++;
// Creates a message using the transport of channel ack
FairMQMessagePtr ack(NewMessageFor("ack", 0));
fair::mq::MessagePtr ack(NewMessageFor("ack", 0));
if (Send(ack, "ack") < 0) {
return false;
}
@@ -40,7 +40,7 @@ struct Sink : fair::mq::Device
}
// handler is called whenever a message arrives on "data", with a reference to the message and a sub-channel index (here 0)
bool HandleData2(FairMQMessagePtr& /*msg*/, int /*index*/)
bool HandleData2(fair::mq::MessagePtr& /*msg*/, int /*index*/)
{
fNumIterations2++;
// return true if want to be called again (otherwise go to IDLE state)

View File

@@ -2,46 +2,57 @@
export FAIRMQ_PATH=@FAIRMQ_BIN_DIR@
SESSION="$(@CMAKE_BINARY_DIR@/fairmq/fairmq-uuid-gen -h)"
session="$(@CMAKE_BINARY_DIR@/fairmq/fairmq-uuid-gen -h)"
chan1="data1"
chan2="data2"
ackChan="ack"
chan1Addr="/tmp/fmq_$session""_""$chan1"
chan2Addr="/tmp/fmq_$session""_""$chan2"
ackChanAddr="/tmp/fmq_$session""_""$ackChan"
trap 'kill -TERM $SAMPLER1_PID; kill -TERM $SAMPLER2_PID; kill -TERM $SINK_PID; wait $SAMPLER1_PID; wait $SAMPLER2_PID; wait $SINK_PID; @CMAKE_BINARY_DIR@/fairmq/fairmq-shmmonitor --cleanup --session $SESSION;' TERM
trap 'kill -TERM $SAMPLER1_PID; kill -TERM $SAMPLER2_PID; kill -TERM $SINK_PID; wait $SAMPLER1_PID; wait $SAMPLER2_PID; wait $SINK_PID; @CMAKE_BINARY_DIR@/fairmq/fairmq-shmmonitor --cleanup --session $SESSION; rm $chan1Addr; rm $chan2Addr; rm $ackChanAddr' TERM
SINK="fairmq-ex-multiple-transports-sink"
SINK+=" --id sink1"
SINK+=" --verbosity veryhigh"
SINK+=" --session $SESSION"
SINK+=" --verbosity veryhigh --severity debug"
SINK+=" --shm-segment-size 100000000"
SINK+=" --session $session"
SINK+=" --max-iterations 1"
SINK+=" --control static --color false"
SINK+=" --transport shmem"
SINK+=" --channel-config name=data1,type=pull,method=connect,address=tcp://127.0.0.1:5555"
SINK+=" name=data2,type=pull,method=connect,address=tcp://127.0.0.1:5556,transport=zeromq"
SINK+=" name=ack,type=pub,method=connect,address=tcp://127.0.0.1:5557,transport=zeromq"
SINK+=" --channel-config name=$chan1,type=pull,method=connect,address=ipc://$chan1Addr"
SINK+=" name=$chan2,type=pull,method=connect,address=ipc://$chan2Addr,transport=zeromq"
SINK+=" name=$ackChan,type=pub,method=connect,address=ipc://$ackChanAddr,transport=zeromq"
@CMAKE_CURRENT_BINARY_DIR@/$SINK &
SINK_PID=$!
SAMPLER1="fairmq-ex-multiple-transports-sampler1"
SAMPLER1+=" --id sampler1"
SAMPLER1+=" --session $SESSION"
SAMPLER1+=" --verbosity veryhigh"
SAMPLER1+=" --session $session"
SAMPLER1+=" --verbosity veryhigh --severity debug"
SAMPLER1+=" --shm-segment-size 100000000"
SAMPLER1+=" --max-iterations 1"
SAMPLER1+=" --control static --color false"
SAMPLER1+=" --transport shmem"
SAMPLER1+=" --channel-config name=data1,type=push,method=bind,address=tcp://127.0.0.1:5555"
SAMPLER1+=" name=ack,type=sub,method=bind,address=tcp://127.0.0.1:5557,transport=zeromq"
SAMPLER1+=" --channel-config name=$chan1,type=push,method=bind,address=ipc://$chan1Addr"
SAMPLER1+=" name=$ackChan,type=sub,method=bind,address=ipc://$ackChanAddr,transport=zeromq"
@CMAKE_CURRENT_BINARY_DIR@/$SAMPLER1 &
SAMPLER1_PID=$!
SAMPLER2="fairmq-ex-multiple-transports-sampler2"
SAMPLER2+=" --id sampler2"
SAMPLER2+=" --session $SESSION"
SAMPLER2+=" --verbosity veryhigh"
SAMPLER2+=" --session $session"
SAMPLER2+=" --verbosity veryhigh --severity debug"
SAMPLER2+=" --shm-segment-size 100000000"
SAMPLER2+=" --max-iterations 1"
SAMPLER2+=" --control static --color false"
SAMPLER2+=" --transport zeromq"
SAMPLER2+=" --channel-config name=data2,type=push,method=bind,address=tcp://127.0.0.1:5556"
SAMPLER2+=" --channel-config name=$chan2,type=push,method=bind,address=ipc://$chan2Addr"
@CMAKE_CURRENT_BINARY_DIR@/$SAMPLER2 &
SAMPLER2_PID=$!
wait $SAMPLER1_PID
wait $SAMPLER2_PID
wait $SINK_PID
rm $chan1Addr; rm $chan2Addr; rm $ackChanAddr

View File

@@ -24,7 +24,7 @@ namespace bpo = boost::program_options;
struct TFBuffer
{
FairMQParts parts;
fair::mq::Parts parts;
chrono::steady_clock::time_point start;
chrono::steady_clock::time_point end;
};
@@ -43,7 +43,7 @@ struct Receiver : fair::mq::Device
fMaxTimeframes = GetConfig()->GetValue<int>("max-timeframes");
}
bool HandleData(FairMQParts& parts, int /* index */)
bool HandleData(fair::mq::Parts& parts, int /* index */)
{
Header& h = *(static_cast<Header*>(parts.At(0)->GetData()));
// LOG(info) << "Received sub-time frame #" << h.id << " from Sender" << h.senderIndex;
@@ -107,7 +107,7 @@ void addCustomOptions(bpo::options_description& options)
("max-timeframes", bpo::value<int>()->default_value(0), "Maximum number of timeframes to receive (0 - unlimited)");
}
std::unique_ptr<fair::mq::Device> getDevice(FairMQProgOptions& /* config */)
std::unique_ptr<fair::mq::Device> getDevice(fair::mq::ProgOptions& /* config */)
{
return std::make_unique<Receiver>();
}

View File

@@ -28,11 +28,11 @@ struct Sender : fair::mq::Device
void Run() override
{
FairMQChannel& dataInChannel = fChannels.at("sync").at(0);
fair::mq::Channel& dataInChannel = GetChannel("sync", 0);
while (!NewStatePending()) {
Header h;
FairMQMessagePtr id(NewMessage());
fair::mq::MessagePtr id(NewMessage());
if (dataInChannel.Receive(id) > 0) {
h.id = *(static_cast<uint16_t*>(id->GetData()));
h.senderIndex = fIndex;
@@ -40,7 +40,7 @@ struct Sender : fair::mq::Device
continue;
}
FairMQParts parts;
fair::mq::Parts parts;
parts.AddPart(NewSimpleMessage(h));
parts.AddPart(NewMessage(fSubtimeframeSize));
@@ -66,7 +66,7 @@ void addCustomOptions(bpo::options_description& options)
("subtimeframe-size", bpo::value<int>()->default_value(1000), "Subtimeframe size in bytes")
("num-receivers", bpo::value<int>()->required(), "Number of EPNs");
}
std::unique_ptr<fair::mq::Device> getDevice(FairMQProgOptions& /* config */)
std::unique_ptr<fair::mq::Device> getDevice(fair::mq::ProgOptions& /* config */)
{
return std::make_unique<Sender>();
}

View File

@@ -19,7 +19,7 @@ struct Synchronizer : fair::mq::Device
{
bool ConditionalRun() override
{
FairMQMessagePtr msg(NewSimpleMessage(fTimeframeId));
fair::mq::MessagePtr msg(NewSimpleMessage(fTimeframeId));
if (Send(msg, "sync") > 0) {
if (++fTimeframeId == UINT16_MAX - 1) {
@@ -37,7 +37,7 @@ struct Synchronizer : fair::mq::Device
};
void addCustomOptions(bpo::options_description& /* options */) {}
std::unique_ptr<fair::mq::Device> getDevice(FairMQProgOptions& /* config */)
std::unique_ptr<fair::mq::Device> getDevice(fair::mq::ProgOptions& /* config */)
{
return std::make_unique<Synchronizer>();
}

View File

@@ -30,10 +30,10 @@ struct QCDispatcher : fair::mq::Device
});
}
bool HandleData(FairMQMessagePtr& msg, int)
bool HandleData(fair::mq::MessagePtr& msg, int)
{
if (fDoQC.load() == true) {
FairMQMessagePtr msgCopy(NewMessage());
fair::mq::MessagePtr msgCopy(NewMessage());
msgCopy->Copy(*msg);
if (Send(msg, "qc") < 0) {
return false;

View File

@@ -9,12 +9,12 @@
#include <fairmq/Device.h>
#include <fairmq/runDevice.h>
class QCTask : public FairMQDevice
class QCTask : public fair::mq::Device
{
public:
QCTask()
{
OnData("qc", [](FairMQMessagePtr& /*msg*/, int) {
OnData("qc", [](fair::mq::MessagePtr& /*msg*/, int) {
LOG(info) << "received data";
return false;
});

View File

@@ -16,7 +16,7 @@ struct Sampler : fair::mq::Device
{
bool ConditionalRun() override
{
FairMQMessagePtr msg(NewMessage(1000));
fair::mq::MessagePtr msg(NewMessage(1000));
if (Send(msg, "data1") < 0) {
return false;

View File

@@ -14,7 +14,7 @@
struct Sink : fair::mq::Device
{
Sink() { OnData("data2", &Sink::HandleData); }
bool HandleData(FairMQMessagePtr& /*msg*/, int /*index*/) { return true; }
bool HandleData(fair::mq::MessagePtr& /*msg*/, int /*index*/) { return true; }
};
namespace bpo = boost::program_options;

View File

@@ -13,7 +13,7 @@
namespace bpo = boost::program_options;
class Builder : public FairMQDevice
class Builder : public fair::mq::Device
{
public:
Builder() = default;
@@ -24,7 +24,7 @@ class Builder : public FairMQDevice
OnData("rb", &Builder::HandleData);
}
bool HandleData(FairMQMessagePtr& msg, int /*index*/)
bool HandleData(fair::mq::MessagePtr& msg, int /*index*/)
{
if (Send(msg, fOutputChannelName) < 0) {
return false;

View File

@@ -18,9 +18,9 @@ struct Processor : fair::mq::Device
OnData("bp", &Processor::HandleData);
}
bool HandleData(FairMQMessagePtr& msg, int /*index*/)
bool HandleData(fair::mq::MessagePtr& msg, int /*index*/)
{
FairMQMessagePtr msg2(NewMessageFor("ps", 0, msg->GetSize()));
fair::mq::MessagePtr msg2(NewMessageFor("ps", 0, msg->GetSize()));
if (Send(msg2, "ps") < 0) {
return false;
}

View File

@@ -22,7 +22,7 @@ struct Readout : fair::mq::Device
fMsgSize = fConfig->GetProperty<int>("msg-size");
fMaxIterations = fConfig->GetProperty<uint64_t>("max-iterations");
fRegion = FairMQUnmanagedRegionPtr(NewUnmanagedRegionFor("rb",
fRegion = fair::mq::UnmanagedRegionPtr(NewUnmanagedRegionFor("rb",
0,
10000000,
[this](const std::vector<fair::mq::RegionBlock>& blocks) { // callback to be called when message buffers no longer needed by transport
@@ -36,7 +36,7 @@ struct Readout : fair::mq::Device
bool ConditionalRun() override
{
FairMQMessagePtr msg(NewMessageFor("rb", // channel
fair::mq::MessagePtr msg(NewMessageFor("rb", // channel
0, // sub-channel
fRegion, // region
fRegion->GetData(), // ptr within region
@@ -71,7 +71,7 @@ struct Readout : fair::mq::Device
int fMsgSize = 10000;
uint64_t fMaxIterations = 0;
uint64_t fNumIterations = 0;
FairMQUnmanagedRegionPtr fRegion = nullptr;
fair::mq::UnmanagedRegionPtr fRegion = nullptr;
std::atomic<uint64_t> fNumUnackedMsgs = 0;
};

View File

@@ -26,7 +26,7 @@ struct Receiver : Device
void Run() override
{
Channel& dataInChannel = fChannels.at("sr").at(0);
Channel& dataInChannel = GetChannel("sr", 0);
while (!NewStatePending()) {
auto msg(dataInChannel.NewMessage());

View File

@@ -21,7 +21,7 @@ struct Sender : fair::mq::Device
OnData(fInputChannelName, &Sender::HandleData);
}
bool HandleData(FairMQMessagePtr& msg, int /*index*/)
bool HandleData(fair::mq::MessagePtr& msg, int /*index*/)
{
if (Send(msg, "sr") < 0) {
return false;

View File

@@ -12,7 +12,10 @@ target_link_libraries(fairmq-ex-region-sampler PRIVATE FairMQ)
add_executable(fairmq-ex-region-sink sink.cxx)
target_link_libraries(fairmq-ex-region-sink PRIVATE FairMQ)
add_custom_target(ExampleRegion DEPENDS fairmq-ex-region-sampler fairmq-ex-region-sink)
add_executable(fairmq-ex-region-keep-alive keep-alive.cxx)
target_link_libraries(fairmq-ex-region-keep-alive PRIVATE FairMQ)
add_custom_target(ExampleRegion DEPENDS fairmq-ex-region-sampler fairmq-ex-region-sink fairmq-ex-region-keep-alive)
set(EX_BIN_DIR ${CMAKE_CURRENT_BINARY_DIR})
set(FAIRMQ_BIN_DIR ${CMAKE_BINARY_DIR}/fairmq)
@@ -23,10 +26,10 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fairmq-start-ex-region.sh.in ${CMAKE_
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test-ex-region.sh.in ${CMAKE_CURRENT_BINARY_DIR}/test-ex-region.sh)
add_test(NAME Example.Region.zeromq COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-region.sh zeromq)
set_tests_properties(Example.Region.zeromq PROPERTIES TIMEOUT "30" RUN_SERIAL true PASS_REGULAR_EXPRESSION "Received [0-9*] acks")
set_tests_properties(Example.Region.zeromq PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Received [0-9*] acks")
add_test(NAME Example.Region.shmem COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-region.sh shmem)
set_tests_properties(Example.Region.shmem PROPERTIES TIMEOUT "30" RUN_SERIAL true PASS_REGULAR_EXPRESSION "Received [0-9*] acks")
set_tests_properties(Example.Region.shmem PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Received [0-9*] acks")
# install

View File

@@ -0,0 +1,144 @@
/********************************************************************************
* Copyright (C) 2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
* copied verbatim in the file "LICENSE" *
********************************************************************************/
#include <fairmq/shmem/Common.h>
#include <fairmq/shmem/UnmanagedRegion.h>
#include <fairmq/shmem/Segment.h>
#include <fairmq/shmem/Monitor.h>
#include <fairmq/tools/Unique.h>
#include <fairlogger/Logger.h>
#include <boost/algorithm/string.hpp>
#include <boost/program_options.hpp>
#include <csignal>
#include <chrono>
#include <map>
#include <string>
#include <thread>
using namespace std;
using namespace boost::program_options;
namespace
{
volatile sig_atomic_t gStopping = 0;
}
void signalHandler(int /* signal */)
{
gStopping = 1;
}
struct ShmManager
{
ShmManager(uint64_t _shmId, const vector<string>& _segments, const vector<string>& _regions)
: shmId(fair::mq::shmem::makeShmIdStr(_shmId))
{
for (const auto& s : _segments) {
vector<string> segmentConf;
boost::algorithm::split(segmentConf, s, boost::algorithm::is_any_of(","));
if (segmentConf.size() != 2) {
LOG(error) << "incorrect format for --segments. Expecting pairs of <id>,<size>.";
fair::mq::shmem::Monitor::Cleanup(fair::mq::shmem::ShmId{shmId});
throw runtime_error("incorrect format for --segments. Expecting pairs of <id>,<size>.");
}
uint16_t id = stoi(segmentConf.at(0));
uint64_t size = stoull(segmentConf.at(1));
auto ret = segments.emplace(id, fair::mq::shmem::Segment(shmId, id, size, fair::mq::shmem::rbTreeBestFit));
fair::mq::shmem::Segment& segment = ret.first->second;
LOG(info) << "Created segment " << id << " of size " << segment.GetSize() << ", starting at " << segment.GetData() << ". Locking...";
segment.Lock();
LOG(info) << "Done.";
LOG(info) << "Zeroing...";
segment.Zero();
LOG(info) << "Done.";
}
for (const auto& r : _regions) {
vector<string> regionConf;
boost::algorithm::split(regionConf, r, boost::algorithm::is_any_of(","));
if (regionConf.size() != 2) {
LOG(error) << "incorrect format for --regions. Expecting pairs of <id>,<size>.";
fair::mq::shmem::Monitor::Cleanup(fair::mq::shmem::ShmId{shmId});
throw runtime_error("incorrect format for --regions. Expecting pairs of <id>,<size>.");
}
uint16_t id = stoi(regionConf.at(0));
uint64_t size = stoull(regionConf.at(1));
auto ret = regions.emplace(id, make_unique<fair::mq::shmem::UnmanagedRegion>(shmId, id, size));
fair::mq::shmem::UnmanagedRegion& region = *(ret.first->second);
LOG(info) << "Created unamanged region " << id << " of size " << region.GetSize() << ", starting at " << region.GetData() << ". Locking...";
region.Lock();
LOG(info) << "Done.";
LOG(info) << "Zeroing...";
region.Zero();
LOG(info) << "Done.";
}
}
void ResetContent()
{
fair::mq::shmem::Monitor::ResetContent(fair::mq::shmem::ShmId{shmId});
}
~ShmManager()
{
// clean all segments, regions and any other shmem objects belonging to this shmId
fair::mq::shmem::Monitor::Cleanup(fair::mq::shmem::ShmId{shmId});
}
std::string shmId;
map<uint16_t, fair::mq::shmem::Segment> segments;
map<uint16_t, unique_ptr<fair::mq::shmem::UnmanagedRegion>> regions;
};
int main(int argc, char** argv)
{
fair::Logger::SetConsoleColor(true);
signal(SIGINT, signalHandler);
signal(SIGTERM, signalHandler);
try {
uint64_t shmId = 0;
vector<string> segments;
vector<string> regions;
options_description desc("Options");
desc.add_options()
("shmid", value<uint64_t>(&shmId)->required(), "Shm id")
("segments", value<vector<string>>(&segments)->multitoken()->composing(), "Segments, as <id>,<size> <id>,<size> <id>,<size> ...")
("regions", value<vector<string>>(&regions)->multitoken()->composing(), "Regions, as <id>,<size> <id>,<size> <id>,<size> ...")
("help,h", "Print help");
variables_map vm;
store(parse_command_line(argc, argv, desc), vm);
if (vm.count("help")) {
LOG(info) << "ShmManager" << "\n" << desc;
return 0;
}
notify(vm);
ShmManager shmManager(shmId, segments, regions);
while (!gStopping) {
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
LOG(info) << "stopping.";
} catch (exception& e) {
LOG(error) << "Unhandled Exception reached the top of main: " << e.what() << ", application will now exit";
return 2;
}
return 0;
}

View File

@@ -23,7 +23,7 @@ struct Sampler : fair::mq::Device
fLinger = fConfig->GetProperty<uint32_t>("region-linger");
fMaxIterations = fConfig->GetProperty<uint64_t>("max-iterations");
fChannels.at("data").at(0).Transport()->SubscribeToRegionEvents([](FairMQRegionInfo info) {
GetChannel("data", 0).Transport()->SubscribeToRegionEvents([](fair::mq::RegionInfo info) {
LOG(info) << "Region event: " << info.event << ": "
<< (info.managed ? "managed" : "unmanaged")
<< ", id: " << info.id
@@ -32,27 +32,25 @@ struct Sampler : fair::mq::Device
<< ", flags: " << info.flags;
});
fRegion = FairMQUnmanagedRegionPtr(NewUnmanagedRegionFor("data", // region is created using the transport of this channel...
fair::mq::RegionConfig regionCfg;
regionCfg.linger = fLinger; // delay in ms before region destruction to collect outstanding events
regionCfg.lock = true; // mlock region after creation
regionCfg.zero = true; // zero region content after creation
fRegion = fair::mq::UnmanagedRegionPtr(NewUnmanagedRegionFor("data", // region is created using the transport of this channel...
0, // ... and this sub-channel
10000000, // region size
[this](const std::vector<fair::mq::RegionBlock>& blocks) { // callback to be called when message buffers no longer needed by transport
std::lock_guard<std::mutex> lock(fMtx);
fNumUnackedMsgs -= blocks.size();
if (fMaxIterations > 0) {
LOG(info) << "Received " << blocks.size() << " acks";
}
},
"", // path, if a region is backed by a file
0, // flags that are passed for region creation
fair::mq::RegionConfig{true, true} // additional config: { call mlock on the region, zero the region memory }
));
fRegion->SetLinger(fLinger);
}, regionCfg));
}
bool ConditionalRun() override
{
FairMQMessagePtr msg(NewMessageFor("data", // channel
fair::mq::MessagePtr msg(NewMessageFor("data", // channel
0, // sub-channel
fRegion, // region
fRegion->GetData(), // ptr within region
@@ -87,7 +85,7 @@ struct Sampler : fair::mq::Device
LOG(info) << "All acknowledgements received.";
}
}
fChannels.at("data").at(0).Transport()->UnsubscribeFromRegionEvents();
GetChannel("data", 0).Transport()->UnsubscribeFromRegionEvents();
}
private:
@@ -95,7 +93,7 @@ struct Sampler : fair::mq::Device
uint32_t fLinger = 100;
uint64_t fMaxIterations = 0;
uint64_t fNumIterations = 0;
FairMQUnmanagedRegionPtr fRegion = nullptr;
fair::mq::UnmanagedRegionPtr fRegion = nullptr;
std::mutex fMtx;
uint64_t fNumUnackedMsgs = 0;
};

View File

@@ -22,7 +22,7 @@ struct Sink : Device
{
// Get the fMaxIterations value from the command line options (via fConfig)
fMaxIterations = fConfig->GetProperty<uint64_t>("max-iterations");
fChannels.at("data").at(0).Transport()->SubscribeToRegionEvents([](RegionInfo info) {
GetChannel("data", 0).Transport()->SubscribeToRegionEvents([](RegionInfo info) {
LOG(info) << "Region event: " << info.event << ": "
<< (info.managed ? "managed" : "unmanaged") << ", id: " << info.id
<< ", ptr: " << info.ptr << ", size: " << info.size
@@ -32,7 +32,7 @@ struct Sink : Device
void Run() override
{
Channel& dataInChannel = fChannels.at("data").at(0);
Channel& dataInChannel = GetChannel("data", 0);
while (!NewStatePending()) {
auto msg(dataInChannel.Transport()->CreateMessage());
@@ -51,7 +51,7 @@ struct Sink : Device
void ResetTask() override
{
fChannels.at("data").at(0).Transport()->UnsubscribeFromRegionEvents();
GetChannel("data", 0).Transport()->UnsubscribeFromRegionEvents();
}
private:

View File

@@ -9,22 +9,25 @@ if [[ $1 =~ ^[a-z]+$ ]]; then
fi
msgSize="1000000"
SESSION="$(@CMAKE_BINARY_DIR@/fairmq/fairmq-uuid-gen -h)"
session="$(@CMAKE_BINARY_DIR@/fairmq/fairmq-uuid-gen -h)"
chan="data"
chanAddr="/tmp/fmq_$session""_""$chan""_""$transport"
# setup a trap to kill everything if the test fails/timeouts
trap 'kill -TERM $SAMPLER_PID; kill -TERM $SINK_PID; wait $SAMPLER_PID; wait $SINK_PID; @CMAKE_BINARY_DIR@/fairmq/fairmq-shmmonitor --cleanup --session $SESSION' TERM
trap 'kill -TERM $SAMPLER_PID; kill -TERM $SINK_PID; wait $SAMPLER_PID; wait $SINK_PID; @CMAKE_BINARY_DIR@/fairmq/fairmq-shmmonitor --cleanup --session $SESSION; rm $chanAddr' TERM
SAMPLER="fairmq-ex-region-sampler"
SAMPLER+=" --id sampler1"
SAMPLER+=" --transport $transport"
SAMPLER+=" --severity debug"
SAMPLER+=" --session $SESSION"
SAMPLER+=" --session $session"
SAMPLER+=" --shm-segment-size 100000000"
SAMPLER+=" --verbosity veryhigh"
SAMPLER+=" --control static --color false"
SAMPLER+=" --max-iterations 1"
SAMPLER+=" --msg-size $msgSize"
SAMPLER+=" --region-linger 500"
SAMPLER+=" --channel-config name=data,type=push,method=bind,address=tcp://127.0.0.1:7777"
SAMPLER+=" --channel-config name=$chan,type=push,method=bind,address=ipc://$chanAddr"
@CMAKE_CURRENT_BINARY_DIR@/$SAMPLER &
SAMPLER_PID=$!
@@ -32,14 +35,17 @@ SINK="fairmq-ex-region-sink"
SINK+=" --id sink1"
SINK+=" --transport $transport"
SINK+=" --severity debug"
SINK+=" --session $SESSION"
SINK+=" --session $session"
SINK+=" --shm-segment-size 100000000"
SINK+=" --verbosity veryhigh"
SINK+=" --control static --color false"
SINK+=" --max-iterations 1"
SINK+=" --channel-config name=data,type=pull,method=connect,address=tcp://127.0.0.1:7777"
SINK+=" --channel-config name=$chan,type=pull,method=connect,address=ipc://$chanAddr"
@CMAKE_CURRENT_BINARY_DIR@/$SINK &
SINK_PID=$!
# wait for sampler and sink to finish
wait $SAMPLER_PID
wait $SINK_PID
rm $chanAddr

View File

@@ -24,10 +24,10 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fairmq-start-ex-req-rep.sh.in ${CMAKE
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test-ex-req-rep.sh.in ${CMAKE_CURRENT_BINARY_DIR}/test-ex-req-rep.sh)
add_test(NAME Example.ReqRep.zeromq COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-req-rep.sh zeromq)
set_tests_properties(Example.ReqRep.zeromq PROPERTIES TIMEOUT "30" RUN_SERIAL true PASS_REGULAR_EXPRESSION "Received reply from server: ")
set_tests_properties(Example.ReqRep.zeromq PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Received reply from server: ")
add_test(NAME Example.ReqRep.shmem COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-req-rep.sh shmem)
set_tests_properties(Example.ReqRep.shmem PROPERTIES TIMEOUT "30" RUN_SERIAL true PASS_REGULAR_EXPRESSION "Received reply from server: ")
set_tests_properties(Example.ReqRep.shmem PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Received reply from server: ")
# install

View File

@@ -32,11 +32,11 @@ struct Client : fair::mq::Device
// its size,
// custom deletion function (called when transfer is done),
// and pointer to the object managing the data buffer
FairMQMessagePtr req(NewMessage(const_cast<char*>(text->c_str()), // data
fair::mq::MessagePtr req(NewMessage(const_cast<char*>(text->c_str()), // data
text->length(), // size
[](void* /*data*/, void* object) { delete static_cast<std::string*>(object); }, // deletion callback
text)); // object that manages the data
FairMQMessagePtr rep(NewMessage());
fair::mq::MessagePtr rep(NewMessage());
LOG(info) << "Sending \"" << fText << "\" to server.";

View File

@@ -26,7 +26,7 @@ struct Server : fair::mq::Device
fMaxIterations = fConfig->GetProperty<uint64_t>("max-iterations");
}
bool HandleData(FairMQMessagePtr& req, int)
bool HandleData(fair::mq::MessagePtr& req, int)
{
LOG(info) << "Received request from client: \"" << std::string(static_cast<char*>(req->GetData()), req->GetSize()) << "\"";
@@ -34,7 +34,7 @@ struct Server : fair::mq::Device
LOG(info) << "Sending reply to client.";
FairMQMessagePtr rep(NewMessage(const_cast<char*>(text->c_str()), // data
fair::mq::MessagePtr rep(NewMessage(const_cast<char*>(text->c_str()), // data
text->length(), // size
[](void* /*data*/, void* object) { delete static_cast<std::string*>(object); }, // deletion callback
text)); // object that manages the data

View File

@@ -8,19 +8,22 @@ if [[ $1 =~ ^[a-z]+$ ]]; then
transport=$1
fi
SESSION="$(@CMAKE_BINARY_DIR@/fairmq/fairmq-uuid-gen -h)"
session="$(@CMAKE_BINARY_DIR@/fairmq/fairmq-uuid-gen -h)"
chan="data"
chanAddr="/tmp/fmq_$session""_""$chan""_""$transport"
# setup a trap to kill everything if the test fails/timeouts
trap 'kill -TERM $CLIENT_PID; kill -TERM $SERVER_PID; wait $CLIENT_PID; wait $SERVER_PID;' TERM
trap 'kill -TERM $CLIENT_PID; kill -TERM $SERVER_PID; wait $CLIENT_PID; wait $SERVER_PID; rm $chanAddr' TERM
CLIENT="fairmq-ex-req-rep-client"
CLIENT+=" --id client"
CLIENT+=" --transport $transport"
CLIENT+=" --verbosity veryhigh"
CLIENT+=" --session $SESSION"
CLIENT+=" --session $session"
CLIENT+=" --shm-segment-size 100000000"
CLIENT+=" --control static --color false"
CLIENT+=" --max-iterations 1"
CLIENT+=" --channel-config name=data,type=req,method=connect,rateLogging=0,address=tcp://127.0.0.1:5005"
CLIENT+=" --channel-config name=$chan,type=req,method=connect,rateLogging=0,address=ipc://$chanAddr"
@CMAKE_CURRENT_BINARY_DIR@/$CLIENT &
CLIENT_PID=$!
@@ -28,13 +31,16 @@ SERVER="fairmq-ex-req-rep-server"
SERVER+=" --id server"
SERVER+=" --transport $transport"
SERVER+=" --verbosity veryhigh"
SERVER+=" --session $SESSION"
SERVER+=" --session $session"
SERVER+=" --shm-segment-size 100000000"
SERVER+=" --control static --color false"
SERVER+=" --max-iterations 1"
SERVER+=" --channel-config name=data,type=rep,method=bind,rateLogging=0,address=tcp://127.0.0.1:5005"
SERVER+=" --channel-config name=$chan,type=rep,method=bind,rateLogging=0,address=ipc://$chanAddr"
@CMAKE_CURRENT_BINARY_DIR@/$SERVER &
SERVER_PID=$!
# wait for everything to finish
wait $CLIENT_PID
wait $SERVER_PID
rm $chanAddr

View File

@@ -30,6 +30,7 @@ if(BUILD_FAIRMQ OR BUILD_SDK)
set(TOOLS_PUBLIC_HEADER_FILES
tools/CppSTL.h
tools/Exceptions.h
tools/InstanceLimit.h
tools/IO.h
tools/Network.h
@@ -191,6 +192,9 @@ if(BUILD_FAIRMQ)
runDevice.h
runFairMQDevice.h
shmem/Monitor.h
shmem/Common.h
shmem/UnmanagedRegion.h
shmem/Segment.h
)
set(FAIRMQ_PRIVATE_HEADER_FILES
@@ -205,12 +209,10 @@ if(BUILD_FAIRMQ)
plugins/control/Control.h
shmem/Message.h
shmem/Poller.h
shmem/UnmanagedRegion.h
shmem/UnmanagedRegionImpl.h
shmem/Socket.h
shmem/TransportFactory.h
shmem/Common.h
shmem/Manager.h
shmem/Region.h
zeromq/Common.h
zeromq/Context.h
zeromq/Message.h
@@ -248,6 +250,7 @@ if(BUILD_FAIRMQ)
TransportFactory.cxx
plugins/config/Config.cxx
plugins/control/Control.cxx
shmem/Common.cxx
shmem/Manager.cxx
shmem/Monitor.cxx
)
@@ -384,7 +387,7 @@ if(BUILD_FAIRMQ)
fairmq_target_tidy(TARGET fairmq-splitter)
endif()
add_executable(fairmq-shmmonitor shmem/Monitor.cxx shmem/Monitor.h shmem/runMonitor.cxx)
add_executable(fairmq-shmmonitor shmem/Common.cxx shmem/Monitor.cxx shmem/Monitor.h shmem/runMonitor.cxx)
target_compile_features(fairmq-shmmonitor PUBLIC cxx_std_17)
target_compile_definitions(fairmq-shmmonitor PUBLIC BOOST_ERROR_CODE_HEADER_ONLY)
if(FAIRMQ_DEBUG_MODE)

View File

@@ -39,6 +39,8 @@ constexpr int Channel::DefaultSndBufSize;
constexpr int Channel::DefaultRcvBufSize;
constexpr int Channel::DefaultSndKernelSize;
constexpr int Channel::DefaultRcvKernelSize;
constexpr int Channel::DefaultSndTimeoutMs;
constexpr int Channel::DefaultRcvTimeoutMs;
constexpr int Channel::DefaultLinger;
constexpr int Channel::DefaultRateLogging;
constexpr int Channel::DefaultPortRangeMin;
@@ -73,6 +75,8 @@ Channel::Channel(string name, string type, string method, string address, shared
, fRcvBufSize(DefaultRcvBufSize)
, fSndKernelSize(DefaultSndKernelSize)
, fRcvKernelSize(DefaultRcvKernelSize)
, fSndTimeoutMs(DefaultSndTimeoutMs)
, fRcvTimeoutMs(DefaultRcvTimeoutMs)
, fLinger(DefaultLinger)
, fRateLogging(DefaultRateLogging)
, fPortRangeMin(DefaultPortRangeMin)
@@ -97,6 +101,8 @@ Channel::Channel(const string& name, int index, const Properties& properties)
fRcvBufSize = GetPropertyOrDefault(properties, string(prefix + "rcvBufSize"), DefaultRcvBufSize);
fSndKernelSize = GetPropertyOrDefault(properties, string(prefix + "sndKernelSize"), DefaultSndKernelSize);
fRcvKernelSize = GetPropertyOrDefault(properties, string(prefix + "rcvKernelSize"), DefaultRcvKernelSize);
fSndTimeoutMs = GetPropertyOrDefault(properties, string(prefix + "sndTimeoutMs"), DefaultSndTimeoutMs);
fRcvTimeoutMs = GetPropertyOrDefault(properties, string(prefix + "rcvTimeoutMs"), DefaultRcvTimeoutMs);
fLinger = GetPropertyOrDefault(properties, string(prefix + "linger"), DefaultLinger);
fRateLogging = GetPropertyOrDefault(properties, string(prefix + "rateLogging"), DefaultRateLogging);
fPortRangeMin = GetPropertyOrDefault(properties, string(prefix + "portRangeMin"), DefaultPortRangeMin);
@@ -120,6 +126,8 @@ Channel::Channel(const Channel& chan, string newName)
, fRcvBufSize(chan.fRcvBufSize)
, fSndKernelSize(chan.fSndKernelSize)
, fRcvKernelSize(chan.fRcvKernelSize)
, fSndTimeoutMs(chan.fSndTimeoutMs)
, fRcvTimeoutMs(chan.fRcvTimeoutMs)
, fLinger(chan.fLinger)
, fRateLogging(chan.fRateLogging)
, fPortRangeMin(chan.fPortRangeMin)
@@ -146,6 +154,8 @@ Channel& Channel::operator=(const Channel& chan)
fRcvBufSize = chan.fRcvBufSize;
fSndKernelSize = chan.fSndKernelSize;
fRcvKernelSize = chan.fRcvKernelSize;
fSndTimeoutMs = chan.fSndTimeoutMs;
fRcvTimeoutMs = chan.fRcvTimeoutMs;
fLinger = chan.fLinger;
fRateLogging = chan.fRateLogging;
fPortRangeMin = chan.fPortRangeMin;

View File

@@ -166,6 +166,14 @@ class Channel
/// @return Returns socket kernel transmit receive buffer size (in bytes)
int GetRcvKernelSize() const { return fRcvKernelSize; }
/// Get socket default send timeout (in ms)
/// @return Returns socket default send timeout (in ms)
int GetSndTimeout() const { return fSndTimeoutMs; }
/// Get socket default receive timeout (in ms)
/// @return Returns socket default receive timeout (in ms)
int GetRcvTimeout() const { return fRcvTimeoutMs; }
/// Get linger duration (in milliseconds)
/// @return Returns linger duration (in milliseconds)
int GetLinger() const { return fLinger; }
@@ -230,6 +238,14 @@ class Channel
/// @param rcvKernelSize Socket receive buffer size (in bytes)
void UpdateRcvKernelSize(int rcvKernelSize) { fRcvKernelSize = rcvKernelSize; Invalidate(); }
/// Set socket default send timeout (in ms)
/// @param sndTimeoutMs Socket default send timeout (in ms)
void UpdateSndTimeout(int sndTimeoutMs) { fSndTimeoutMs = sndTimeoutMs; Invalidate(); }
/// Set socket default receive timeout (in ms)
/// @param rcvTimeoutMs Socket default receive timeout (in ms)
void UpdateRcvTimeout(int rcvTimeoutMs) { fRcvTimeoutMs = rcvTimeoutMs; Invalidate(); }
/// Set linger duration (in milliseconds)
/// @param duration linger duration (in milliseconds)
void UpdateLinger(int duration) { fLinger = duration; Invalidate(); }
@@ -267,62 +283,52 @@ class Channel
/// invalidates the channel (requires validation to be used again).
void Invalidate() { fValid = false; }
/// Sends a message to the socket queue.
/// @param msg Constant reference of unique_ptr to a Message
/// @param sndTimeoutInMs send timeout in ms. -1 will wait forever (or until interrupt (e.g. via state change)), 0 will not wait (return immediately if cannot send)
/// @return Number of bytes that have been queued, TransferCode::timeout if timed out, TransferCode::error if there was an error, TransferCode::interrupted if interrupted (e.g. by requested state change)
int64_t Send(MessagePtr& msg, int sndTimeoutInMs = -1)
/// Send message(s) to the socket queue.
/// @param m reference to MessagePtr/Parts/vector<MessagePtr>
/// @param sndTimeoutMs send timeout in ms.
/// -1 will wait forever (or until interrupt (e.g. via state change)),
/// 0 will not wait (return immediately if cannot send).
/// If not provided, default timeout will be taken.
/// @return Number of bytes that have been queued,
/// TransferCode::timeout if timed out,
/// TransferCode::error if there was an error,
/// TransferCode::interrupted if interrupted (e.g. by requested state change)
template<typename M, typename... Timeout>
std::enable_if_t<is_transferrable<M>::value, int64_t>
Send(M& m, Timeout&&... sndTimeoutMs)
{
CheckSendCompatibility(msg);
return fSocket->Send(msg, sndTimeoutInMs);
static_assert(sizeof...(sndTimeoutMs) <= 1, "Send called with too many arguments");
CheckSendCompatibility(m);
int t = fSndTimeoutMs;
if constexpr (sizeof...(sndTimeoutMs) == 1) {
t = {sndTimeoutMs...};
}
return fSocket->Send(m, t);
}
/// Receives a message from the socket queue.
/// @param msg Constant reference of unique_ptr to a Message
/// @param rcvTimeoutInMs receive timeout in ms. -1 will wait forever (or until interrupt (e.g. via state change)), 0 will not wait (return immediately if cannot receive)
/// @return Number of bytes that have been received, TransferCode::timeout if timed out, TransferCode::error if there was an error, TransferCode::interrupted if interrupted (e.g. by requested state change)
int64_t Receive(MessagePtr& msg, int rcvTimeoutInMs = -1)
/// Receive message(s) from the socket queue.
/// @param m reference to MessagePtr/Parts/vector<MessagePtr>
/// @param rcvTimeoutMs receive timeout in ms.
/// -1 will wait forever (or until interrupt (e.g. via state change)),
/// 0 will not wait (return immediately if cannot receive).
/// If not provided, default timeout will be taken.
/// @return Number of bytes that have been received,
/// TransferCode::timeout if timed out,
/// TransferCode::error if there was an error,
/// TransferCode::interrupted if interrupted (e.g. by requested state change)
template<typename M, typename... Timeout>
std::enable_if_t<is_transferrable<M>::value, int64_t>
Receive(M& m, Timeout&&... rcvTimeoutMs)
{
CheckReceiveCompatibility(msg);
return fSocket->Receive(msg, rcvTimeoutInMs);
}
static_assert(sizeof...(rcvTimeoutMs) <= 1, "Receive called with too many arguments");
/// Send a vector of messages
/// @param msgVec message vector reference
/// @param sndTimeoutInMs send timeout in ms. -1 will wait forever (or until interrupt (e.g. via state change)), 0 will not wait (return immediately if cannot send)
/// @return Number of bytes that have been queued, TransferCode::timeout if timed out, TransferCode::error if there was an error, TransferCode::interrupted if interrupted (e.g. by requested state change)
int64_t Send(std::vector<MessagePtr>& msgVec, int sndTimeoutInMs = -1)
{
CheckSendCompatibility(msgVec);
return fSocket->Send(msgVec, sndTimeoutInMs);
}
/// Receive a vector of messages
/// @param msgVec message vector reference
/// @param rcvTimeoutInMs receive timeout in ms. -1 will wait forever (or until interrupt (e.g. via state change)), 0 will not wait (return immediately if cannot receive)
/// @return Number of bytes that have been received, TransferCode::timeout if timed out, TransferCode::error if there was an error, TransferCode::interrupted if interrupted (e.g. by requested state change)
int64_t Receive(std::vector<MessagePtr>& msgVec, int rcvTimeoutInMs = -1)
{
CheckReceiveCompatibility(msgVec);
return fSocket->Receive(msgVec, rcvTimeoutInMs);
}
/// Send Parts
/// @param parts Parts reference
/// @param sndTimeoutInMs send timeout in ms. -1 will wait forever (or until interrupt (e.g. via state change)), 0 will not wait (return immediately if cannot send)
/// @return Number of bytes that have been queued, TransferCode::timeout if timed out, TransferCode::error if there was an error, TransferCode::interrupted if interrupted (e.g. by requested state change)
int64_t Send(Parts& parts, int sndTimeoutInMs = -1)
{
return Send(parts.fParts, sndTimeoutInMs);
}
/// Receive Parts
/// @param parts Parts reference
/// @param rcvTimeoutInMs receive timeout in ms. -1 will wait forever (or until interrupt (e.g. via state change)), 0 will not wait (return immediately if cannot receive)
/// @return Number of bytes that have been received, TransferCode::timeout if timed out, TransferCode::error if there was an error, TransferCode::interrupted if interrupted (e.g. by requested state change)
int64_t Receive(Parts& parts, int rcvTimeoutInMs = -1)
{
return Receive(parts.fParts, rcvTimeoutInMs);
CheckReceiveCompatibility(m);
int t = fRcvTimeoutMs;
if constexpr (sizeof...(rcvTimeoutMs) == 1) {
t = {rcvTimeoutMs...};
}
return fSocket->Receive(m, t);
}
unsigned long GetBytesTx() const { return fSocket->GetBytesTx(); }
@@ -366,6 +372,8 @@ class Channel
static constexpr int DefaultRcvBufSize = 1000;
static constexpr int DefaultSndKernelSize = 0;
static constexpr int DefaultRcvKernelSize = 0;
static constexpr int DefaultSndTimeoutMs = -1;
static constexpr int DefaultRcvTimeoutMs = -1;
static constexpr int DefaultLinger = 500;
static constexpr int DefaultRateLogging = 1;
static constexpr int DefaultPortRangeMin = 22000;
@@ -385,6 +393,8 @@ class Channel
int fRcvBufSize;
int fSndKernelSize;
int fRcvKernelSize;
int fSndTimeoutMs;
int fRcvTimeoutMs;
int fLinger;
int fRateLogging;
int fPortRangeMin;
@@ -414,6 +424,7 @@ class Channel
}
}
void CheckSendCompatibility(Parts& parts) { CheckSendCompatibility(parts.fParts); }
void CheckSendCompatibility(std::vector<MessagePtr>& msgVec)
{
for (auto& msg : msgVec) {
@@ -443,6 +454,7 @@ class Channel
}
}
void CheckReceiveCompatibility(Parts& parts) { CheckReceiveCompatibility(parts.fParts); }
void CheckReceiveCompatibility(std::vector<MessagePtr>& msgVec)
{
for (auto& msg : msgVec) {

View File

@@ -435,7 +435,7 @@ void Device::InitTaskWrapper()
void Device::RunWrapper()
{
LOG(info) << "DEVICE: Running...";
LOG(info) << "fair::mq::Device running...";
// start the rate logger thread
future<void> rateLogger = async(launch::async, &Device::LogSocketRates, this);
@@ -445,46 +445,43 @@ void Device::RunWrapper()
t.second->Resume();
}
try {
PreRun();
// change to Error state in case of an exception, to release LogSocketRates
tools::CallOnDestruction cod([&](){
ChangeState(Transition::ErrorFound);
});
// process either data callbacks or ConditionalRun/Run
if (fDataCallbacks) {
// if only one input channel, do lightweight handling without additional polling.
if (fInputChannelKeys.size() == 1 && fChannels.at(fInputChannelKeys.at(0)).size() == 1) {
HandleSingleChannelInput();
} else {// otherwise do full handling with polling
HandleMultipleChannelInput();
PreRun();
// process either data callbacks or ConditionalRun/Run
if (fDataCallbacks) {
// if only one input channel, do lightweight handling without additional polling.
if (fInputChannelKeys.size() == 1 && fChannels.at(fInputChannelKeys.at(0)).size() == 1) {
HandleSingleChannelInput();
} else {// otherwise do full handling with polling
HandleMultipleChannelInput();
}
} else {
tools::RateLimiter rateLimiter(fRate);
while (!NewStatePending() && ConditionalRun()) {
if (fRate > 0.001) {
rateLimiter.maybe_sleep();
}
} else {
tools::RateLimiter rateLimiter(fRate);
while (!NewStatePending() && ConditionalRun()) {
if (fRate > 0.001) {
rateLimiter.maybe_sleep();
}
}
Run();
}
// if Run() exited and the state is still RUNNING, transition to READY.
if (!NewStatePending()) {
UnblockTransports();
ChangeState(Transition::Stop);
}
PostRun();
} catch (const out_of_range& oor) {
LOG(error) << "out of range: " << oor.what();
LOG(error) << "incorrect/incomplete channel configuration?";
ChangeState(Transition::ErrorFound);
throw;
} catch (...) {
ChangeState(Transition::ErrorFound);
throw;
Run();
}
// if Run() exited and the state is still RUNNING, transition to READY.
if (!NewStatePending()) {
UnblockTransports();
ChangeState(Transition::Stop);
}
PostRun();
cod.disable();
rateLogger.get();
}

View File

@@ -81,72 +81,70 @@ class Device
Deserializer().Deserialize(msg, std::forward<DataType>(data), std::forward<Args>(args)...);
}
/// Shorthand method to send `msg` on `chan` at index `i`
/// @param msg message reference
/// Send `m` on `chan` at index `i`
/// @param m reference to MessagePtr/Parts/vector<MessagePtr>
/// @param chan channel name
/// @param i channel index
/// @param sndTimeoutInMs send timeout in ms, -1 will wait forever (or until interrupt (e.g. via
/// state change)), 0 will not wait (return immediately if cannot send)
/// @return Number of bytes that have been queued, TransferCode::timeout if timed out,
/// TransferCode::error if there was an error, TransferCode::interrupted if interrupted (e.g. by
/// requested state change)
int64_t Send(MessagePtr& msg,
const std::string& channel,
const int index = 0,
int sndTimeoutInMs = -1)
/// @return Number of queued bytes,
/// TransferCode::timeout if timed out,
/// TransferCode::error if there was an error,
/// TransferCode::interrupted if interrupted (e.g. by requested state change)
template<typename M>
std::enable_if_t<is_transferrable<M>::value, int64_t>
Send(M& m, const std::string& channel, const int index = 0)
{
return GetChannel(channel, index).Send(msg, sndTimeoutInMs);
return GetChannel(channel, index).Send(m);
}
/// Shorthand method to receive `msg` on `chan` at index `i`
/// @param msg message reference
/// Receive `m` on `chan` at index `i`
/// @param m reference to MessagePtr/Parts/vector<MessagePtr>
/// @param chan channel name
/// @param i channel index
/// @param rcvTimeoutInMs receive timeout in ms, -1 will wait forever (or until interrupt (e.g.
/// via state change)), 0 will not wait (return immediately if cannot receive)
/// @return Number of bytes that have been received, TransferCode::timeout if timed out,
/// TransferCode::error if there was an error, TransferCode::interrupted if interrupted (e.g. by
/// requested state change)
int64_t Receive(MessagePtr& msg,
const std::string& channel,
const int index = 0,
int rcvTimeoutInMs = -1)
/// @return Number of received bytes,
/// TransferCode::timeout if timed out,
/// TransferCode::error if there was an error,
/// TransferCode::interrupted if interrupted (e.g. by requested state change)
template<typename M>
std::enable_if_t<is_transferrable<M>::value, int64_t>
Receive(M& m, const std::string& channel, const int index = 0)
{
return GetChannel(channel, index).Receive(msg, rcvTimeoutInMs);
return GetChannel(channel, index).Receive(m);
}
/// Shorthand method to send Parts on `chan` at index `i`
/// @param parts parts reference
/// Send `m` on `chan` at index `i`
/// @param m reference to MessagePtr/Parts/vector<MessagePtr>
/// @param chan channel name
/// @param i channel index
/// @param sndTimeoutInMs send timeout in ms, -1 will wait forever (or until interrupt (e.g. via
/// state change)), 0 will not wait (return immediately if cannot send)
/// @return Number of bytes that have been queued, TransferCode::timeout if timed out,
/// TransferCode::error if there was an error, TransferCode::interrupted if interrupted (e.g. by
/// requested state change)
int64_t Send(Parts& parts,
const std::string& channel,
const int index = 0,
int sndTimeoutInMs = -1)
/// @param sndTimeoutMs send timeout in ms,
/// -1 will wait forever (or until interrupt (e.g. via state change)),
/// 0 will not wait (return immediately if cannot send)
/// @return Number of queued bytes,
/// TransferCode::timeout if timed out,
/// TransferCode::error if there was an error,
/// TransferCode::interrupted if interrupted (e.g. by requested state change)
template<typename M>
std::enable_if_t<is_transferrable<M>::value, int64_t>
Send(M& m, const std::string& channel, const int index, int sndTimeoutMs)
{
return GetChannel(channel, index).Send(parts.fParts, sndTimeoutInMs);
return GetChannel(channel, index).Send(m, sndTimeoutMs);
}
/// Shorthand method to receive Parts on `chan` at index `i`
/// @param parts parts reference
/// Receive `m` on `chan` at index `i`
/// @param m reference to MessagePtr/Parts/vector<MessagePtr>
/// @param chan channel name
/// @param i channel index
/// @param rcvTimeoutInMs receive timeout in ms, -1 will wait forever (or until interrupt (e.g.
/// via state change)), 0 will not wait (return immediately if cannot receive)
/// @return Number of bytes that have been received, TransferCode::timeout if timed out,
/// TransferCode::error if there was an error, TransferCode::interrupted if interrupted (e.g. by
/// requested state change)
int64_t Receive(Parts& parts,
const std::string& channel,
const int index = 0,
int rcvTimeoutInMs = -1)
/// @param rcvTimeoutMs receive timeout in ms,
/// -1 will wait forever (or until interrupt (e.g. via state change),
/// 0 will not wait (return immediately if cannot receive)
/// @return Number of received bytes,
/// TransferCode::timeout if timed out,
/// TransferCode::error if there was an error,
/// TransferCode::interrupted if interrupted (e.g. by requested state change)
template<typename M>
std::enable_if_t<is_transferrable<M>::value, int64_t>
Receive(M& m, const std::string& channel, const int index, int rcvTimeoutMs)
{
return GetChannel(channel, index).Receive(parts.fParts, rcvTimeoutInMs);
return GetChannel(channel, index).Receive(m, rcvTimeoutMs);
}
/// @brief Getter for default transport factory
@@ -320,10 +318,15 @@ class Device
try {
return fChannels.at(channelName).at(index);
} catch (const std::out_of_range& oor) {
LOG(error)
<< "requested channel has not been configured? check channel names/configuration.";
LOG(error) << "channel: " << channelName << ", index: " << index;
LOG(error) << "out of range: " << oor.what();
LOG(error) << "GetChannel(): '" << channelName << "[" << index << "]' does not exist.";
throw;
}
size_t GetNumSubChannels(const std::string& channelName)
try {
return fChannels.at(channelName).size();
} catch (const std::out_of_range& oor) {
LOG(error) << "GetNumSubChannels(): '" << channelName << "' does not exist.";
throw;
}

View File

@@ -128,7 +128,7 @@ auto DeviceRunner::Run() -> int
fConfig.ParseAll(fRawCmdLineArgs, true);
if (!HandleGeneralOptions(fConfig)) {
if (!HandleGeneralOptions(fConfig, fPrintLogo)) {
return 0;
}

View File

@@ -30,13 +30,13 @@
using namespace std;
using namespace fair::mq;
using namespace fair::mq::tools;
using namespace tools;
using namespace boost::property_tree;
namespace fair::mq
{
fair::mq::Properties PtreeParser(const ptree& pt, const string& id)
Properties PtreeParser(const ptree& pt, const string& id)
{
if (id.empty()) {
throw ParserError("no device ID provided. Provide with `--id` cmd option");
@@ -47,7 +47,7 @@ fair::mq::Properties PtreeParser(const ptree& pt, const string& id)
return helper::DeviceParser(pt.get_child("fairMQOptions"), id);
}
fair::mq::Properties JSONParser(const string& filename, const string& deviceId)
Properties JSONParser(const string& filename, const string& deviceId)
{
ptree pt;
LOG(debug) << "Parsing JSON from " << filename << " ...";
@@ -58,9 +58,9 @@ fair::mq::Properties JSONParser(const string& filename, const string& deviceId)
namespace helper
{
fair::mq::Properties DeviceParser(const ptree& fairMQOptions, const string& deviceId)
Properties DeviceParser(const ptree& fairMQOptions, const string& deviceId)
{
fair::mq::Properties properties;
Properties properties;
for (const auto& node : fairMQOptions) {
if (node.first == "devices") {
@@ -82,25 +82,27 @@ fair::mq::Properties DeviceParser(const ptree& fairMQOptions, const string& devi
return properties;
}
void ChannelParser(const ptree& tree, fair::mq::Properties& properties)
void ChannelParser(const ptree& tree, Properties& properties)
{
for (const auto& node : tree) {
if (node.first == "channels") {
for (const auto& cn : node.second) {
fair::mq::Properties commonProperties;
commonProperties.emplace("type", cn.second.get<string>("type", FairMQChannel::DefaultType));
commonProperties.emplace("method", cn.second.get<string>("method", FairMQChannel::DefaultMethod));
commonProperties.emplace("address", cn.second.get<string>("address", FairMQChannel::DefaultAddress));
commonProperties.emplace("transport", cn.second.get<string>("transport", FairMQChannel::DefaultTransportName));
commonProperties.emplace("sndBufSize", cn.second.get<int>("sndBufSize", FairMQChannel::DefaultSndBufSize));
commonProperties.emplace("rcvBufSize", cn.second.get<int>("rcvBufSize", FairMQChannel::DefaultRcvBufSize));
commonProperties.emplace("sndKernelSize", cn.second.get<int>("sndKernelSize", FairMQChannel::DefaultSndKernelSize));
commonProperties.emplace("rcvKernelSize", cn.second.get<int>("rcvKernelSize", FairMQChannel::DefaultRcvKernelSize));
commonProperties.emplace("linger", cn.second.get<int>("linger", FairMQChannel::DefaultLinger));
commonProperties.emplace("rateLogging", cn.second.get<int>("rateLogging", FairMQChannel::DefaultRateLogging));
commonProperties.emplace("portRangeMin", cn.second.get<int>("portRangeMin", FairMQChannel::DefaultPortRangeMin));
commonProperties.emplace("portRangeMax", cn.second.get<int>("portRangeMax", FairMQChannel::DefaultPortRangeMax));
commonProperties.emplace("autoBind", cn.second.get<bool>("autoBind", FairMQChannel::DefaultAutoBind));
Properties commonProperties;
commonProperties.emplace("type", cn.second.get<string>("type", Channel::DefaultType));
commonProperties.emplace("method", cn.second.get<string>("method", Channel::DefaultMethod));
commonProperties.emplace("address", cn.second.get<string>("address", Channel::DefaultAddress));
commonProperties.emplace("transport", cn.second.get<string>("transport", Channel::DefaultTransportName));
commonProperties.emplace("sndBufSize", cn.second.get<int>("sndBufSize", Channel::DefaultSndBufSize));
commonProperties.emplace("rcvBufSize", cn.second.get<int>("rcvBufSize", Channel::DefaultRcvBufSize));
commonProperties.emplace("sndKernelSize", cn.second.get<int>("sndKernelSize", Channel::DefaultSndKernelSize));
commonProperties.emplace("rcvKernelSize", cn.second.get<int>("rcvKernelSize", Channel::DefaultRcvKernelSize));
commonProperties.emplace("sndTimeoutMs", cn.second.get<int>("sndTimeoutMs", Channel::DefaultSndTimeoutMs));
commonProperties.emplace("rcvTimeoutMs", cn.second.get<int>("rcvTimeoutMs", Channel::DefaultRcvTimeoutMs));
commonProperties.emplace("linger", cn.second.get<int>("linger", Channel::DefaultLinger));
commonProperties.emplace("rateLogging", cn.second.get<int>("rateLogging", Channel::DefaultRateLogging));
commonProperties.emplace("portRangeMin", cn.second.get<int>("portRangeMin", Channel::DefaultPortRangeMin));
commonProperties.emplace("portRangeMax", cn.second.get<int>("portRangeMax", Channel::DefaultPortRangeMax));
commonProperties.emplace("autoBind", cn.second.get<bool>("autoBind", Channel::DefaultAutoBind));
string name = cn.second.get<string>("name");
int numSockets = cn.second.get<int>("numSockets", 0);
@@ -126,7 +128,7 @@ void ChannelParser(const ptree& tree, fair::mq::Properties& properties)
}
}
void SubChannelParser(const ptree& channelTree, fair::mq::Properties& properties, const string& channelName, const fair::mq::Properties& commonProperties)
void SubChannelParser(const ptree& channelTree, Properties& properties, const string& channelName, const Properties& commonProperties)
{
// for each socket in channel
int i = 0;
@@ -135,7 +137,7 @@ void SubChannelParser(const ptree& channelTree, fair::mq::Properties& properties
if (node.first == "sockets") {
for (const auto& sn : node.second) {
// a sub-channel inherits relevant properties from the common channel ...
fair::mq::Properties newProperties(commonProperties);
Properties newProperties(commonProperties);
// ... and adds/overwrites its own properties
newProperties["type"] = sn.second.get<string>("type", boost::any_cast<string>(commonProperties.at("type")));
@@ -146,6 +148,8 @@ void SubChannelParser(const ptree& channelTree, fair::mq::Properties& properties
newProperties["rcvBufSize"] = sn.second.get<int>("rcvBufSize", boost::any_cast<int>(commonProperties.at("rcvBufSize")));
newProperties["sndKernelSize"] = sn.second.get<int>("sndKernelSize", boost::any_cast<int>(commonProperties.at("sndKernelSize")));
newProperties["rcvKernelSize"] = sn.second.get<int>("rcvKernelSize", boost::any_cast<int>(commonProperties.at("rcvKernelSize")));
newProperties["sndTimeoutMs"] = sn.second.get<int>("sndTimeoutMs", boost::any_cast<int>(commonProperties.at("sndTimeoutMs")));
newProperties["rcvTimeoutMs"] = sn.second.get<int>("rcvTimeoutMs", boost::any_cast<int>(commonProperties.at("rcvTimeoutMs")));
newProperties["linger"] = sn.second.get<int>("linger", boost::any_cast<int>(commonProperties.at("linger")));
newProperties["rateLogging"] = sn.second.get<int>("rateLogging", boost::any_cast<int>(commonProperties.at("rateLogging")));
newProperties["portRangeMin"] = sn.second.get<int>("portRangeMin", boost::any_cast<int>(commonProperties.at("portRangeMin")));
@@ -173,7 +177,7 @@ void SubChannelParser(const ptree& channelTree, fair::mq::Properties& properties
LOG(trace) << "\tNo sockets specified,";
LOG(trace) << "\tapplying common settings to the channel:";
fair::mq::Properties newProperties(commonProperties);
Properties newProperties(commonProperties);
for (auto& p : newProperties) {
LOG(trace) << "\t" << setw(13) << left << p.first << " : " << p.second;

View File

@@ -12,7 +12,7 @@
///
/// @author Mikolaj Krzewicki, mkrzewic@cern.ch
#include <fairmq/FairMQTransportFactory.h>
#include <fairmq/TransportFactory.h>
#include <fairmq/MemoryResources.h>
namespace fair::mq
@@ -28,13 +28,13 @@ template<typename ContainerT>
// pmr::polymorphic_allocator<typename
// ContainerT::value_type>,
// typename ContainerT::allocator_type>::value == true,
// FairMQMessagePtr>::type
FairMQMessagePtr getMessage(ContainerT &&container_, FairMQMemoryResource *targetResource = nullptr)
// MessagePtr>::type
MessagePtr getMessage(ContainerT &&container_, MemoryResource *targetResource = nullptr)
{
auto container = std::move(container_);
auto alloc = container.get_allocator();
auto resource = dynamic_cast<FairMQMemoryResource *>(alloc.resource());
auto resource = dynamic_cast<MemoryResource *>(alloc.resource());
if (!resource && !targetResource) {
throw std::runtime_error("Neither the container or target resource specified");
}

View File

@@ -7,7 +7,7 @@
********************************************************************************/
#include <fairmq/Plugin.h>
#include <FairMQLogger.h>
#include <fairlogger/Logger.h>
#include <utility>
using namespace std;

View File

@@ -351,7 +351,7 @@ void ProgOptions::DeleteProperty(const string& key)
vm.erase(key);
}
void ProgOptions::AddChannel(const string& name, const FairMQChannel& channel)
void ProgOptions::AddChannel(const string& name, const Channel& channel)
{
lock_guard<mutex> lock(fMtx);
unordered_map<string, int> existingChannels = GetChannelInfoImpl();

View File

@@ -10,9 +10,12 @@
#define FAIR_MQ_SOCKET_H
#include <fairmq/Message.h>
#include <fairmq/Parts.h>
#include <memory>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <vector>
namespace fair::mq {
@@ -27,6 +30,12 @@ enum class TransferCode : int
interrupted = -3
};
template <typename T>
struct is_transferrable : std::disjunction<std::is_same<T, MessagePtr>,
std::is_same<T, std::vector<MessagePtr>>,
std::is_same<T, fair::mq::Parts>>
{};
struct Socket
{
Socket() = default;
@@ -45,6 +54,8 @@ struct Socket
virtual int64_t Receive(MessagePtr& msg, int timeout = -1) = 0;
virtual int64_t Send(std::vector<std::unique_ptr<Message>>& msgVec, int timeout = -1) = 0;
virtual int64_t Receive(std::vector<std::unique_ptr<Message>>& msgVec, int timeout = -1) = 0;
virtual int64_t Send(Parts& parts, int timeout = -1) { return Send(parts.fParts, timeout); }
virtual int64_t Receive(Parts& parts, int timeout = -1) { return Receive(parts.fParts, timeout); }
[[deprecated("Use Socket::~Socket() instead.")]]
virtual void Close() = 0;

View File

@@ -7,6 +7,7 @@
********************************************************************************/
#include <fairmq/StateMachine.h>
#include <fairmq/tools/Exceptions.h>
#include <fairlogger/Logger.h>
@@ -204,6 +205,7 @@ struct Machine_ : public state_machine_def<Machine_>
}
if (fState == State::Error) {
LOG(trace) << "Device transitioned to error state";
throw StateMachine::ErrorStateException("Device transitioned to error state");
}
}
@@ -366,20 +368,18 @@ void StateMachine::ProcessWork()
{
auto fsm = static_pointer_cast<FairMQFSM>(fFsm);
try {
fsm->CallStateChangeCallbacks(State::Idle);
fsm->ProcessWork();
} catch(ErrorStateException& ese) {
LOG(trace) << "ErrorStateException caught in ProcessWork(), rethrowing";
throw;
} catch(...) {
LOG(debug) << "Exception caught in ProcessWork(), going to Error state and rethrowing";
fair::mq::tools::CallOnDestruction cod([&](){
LOG(debug) << "Exception caught in ProcessWork(), going to Error state";
{
lock_guard<mutex> lock(fsm->fStateMtx);
fsm->fState = State::Error;
fsm->CallStateChangeCallbacks(State::Error);
}
ChangeState(Transition::ErrorFound);
throw;
}
});
fsm->CallStateChangeCallbacks(State::Idle);
fsm->ProcessWork();
cod.disable();
}

View File

@@ -38,6 +38,8 @@ enum channelOptionKeyIds
RCVBUFSIZE, // size of the receive queue
SNDKERNELSIZE,
RCVKERNELSIZE,
SNDTIMEOUTMS,
RCVTIMEOUTMS,
LINGER,
RATELOGGING, // logging rate
PORTRANGEMIN,
@@ -57,6 +59,8 @@ constexpr static const char* channelOptionKeys[] = {
/*[RCVBUFSIZE] = */ "rcvBufSize",
/*[SNDKERNELSIZE] = */ "sndKernelSize",
/*[RCVKERNELSIZE] = */ "rcvKernelSize",
/*[SNDTIMEOUTMS] = */ "sndTimeoutMs",
/*[RCVTIMEOUTMS] = */ "rcvTimeoutMs",
/*[LINGER] = */ "linger",
/*[RATELOGGING] = */ "rateLogging",
/*[PORTRANGEMIN] = */ "portRangeMin",

View File

@@ -11,6 +11,7 @@
// IWYU pragma: begin_exports
#include <fairmq/tools/CppSTL.h>
#include <fairmq/tools/Exceptions.h>
#include <fairmq/tools/InstanceLimit.h>
#include <fairmq/tools/Network.h>
#include <fairmq/tools/Process.h>

View File

@@ -109,13 +109,15 @@ class TransportFactory
/// @param path optional parameter to pass to the underlying transport
/// @param flags optional parameter to pass to the underlying transport
/// @return pointer to UnmanagedRegion
// [[deprecated("Use CreateUnmanagedRegion(size_t size, RegionCallback callback, RegionConfig cfg)")]]
virtual UnmanagedRegionPtr CreateUnmanagedRegion(size_t size,
RegionCallback callback = nullptr,
const std::string& path = "",
int flags = 0,
RegionConfig cfg = RegionConfig()) = 0;
// [[deprecated("Use CreateUnmanagedRegion(size_t size, RegionCallback callback, RegionConfig cfg)")]]
virtual UnmanagedRegionPtr CreateUnmanagedRegion(size_t size,
RegionBulkCallback callback = nullptr,
RegionBulkCallback bulkCallback = nullptr,
const std::string& path = "",
int flags = 0,
RegionConfig cfg = RegionConfig()) = 0;
@@ -128,19 +130,35 @@ class TransportFactory
/// @param path optional parameter to pass to the underlying transport
/// @param flags optional parameter to pass to the underlying transport
/// @return pointer to UnmanagedRegion
// [[deprecated("Use CreateUnmanagedRegion(size_t size, RegionCallback callback, RegionConfig cfg)")]]
virtual UnmanagedRegionPtr CreateUnmanagedRegion(size_t size,
int64_t userFlags,
RegionCallback callback = nullptr,
const std::string& path = "",
int flags = 0,
RegionConfig cfg = RegionConfig()) = 0;
// [[deprecated("Use CreateUnmanagedRegion(size_t size, RegionCallback callback, RegionConfig cfg)")]]
virtual UnmanagedRegionPtr CreateUnmanagedRegion(size_t size,
int64_t userFlags,
RegionBulkCallback callback = nullptr,
RegionBulkCallback bulkCallback = nullptr,
const std::string& path = "",
int flags = 0,
RegionConfig cfg = RegionConfig()) = 0;
/// @brief Create new UnmanagedRegion
/// @param size size of the region
/// @param callback callback to be called when a message belonging to this region is no longer needed by the transport
/// @param cfg region configuration
/// @return pointer to UnmanagedRegion
virtual UnmanagedRegionPtr CreateUnmanagedRegion(size_t size, RegionCallback callback, RegionConfig cfg) = 0;
/// @brief Create new UnmanagedRegion
/// @param size size of the region
/// @param bulkCallback callback to be called when message(s) belonging to this region is no longer needed by the transport
/// @param cfg region configuration
/// @return pointer to UnmanagedRegion
virtual UnmanagedRegionPtr CreateUnmanagedRegion(size_t size, RegionBulkCallback bulkCallback, RegionConfig cfg) = 0;
/// @brief Subscribe to region events (creation, destruction, ...)
/// @param callback the callback that is called when a region event occurs
virtual void SubscribeToRegionEvents(RegionEventCallback callback) = 0;

View File

@@ -9,12 +9,16 @@
#ifndef FAIR_MQ_UNMANAGEDREGION_H
#define FAIR_MQ_UNMANAGEDREGION_H
#include <fairmq/Transports.h>
#include <cstddef> // size_t
#include <cstdint> // uint32_t
#include <fairmq/Transports.h>
#include <functional> // std::function
#include <memory> // std::unique_ptr
#include <ostream> // std::ostream
#include <optional> // std::optional
#include <ostream>
#include <string>
#include <vector>
namespace fair::mq {
@@ -119,13 +123,20 @@ struct RegionConfig
{
RegionConfig() = default;
RegionConfig(bool l, bool z)
: lock(l)
, zero(z)
RegionConfig(bool _lock, bool _zero)
: lock(_lock)
, zero(_zero)
{}
bool lock = false;
bool zero = false;
bool lock = false; /// mlock region after creation
bool zero = false; /// zero region content after creation
bool removeOnDestruction = true; /// remove the region on object destruction
int creationFlags = 0; /// flags passed to the underlying transport on region creation
int64_t userFlags = 0; /// custom flags that have no effect on the transport, but can be retrieved from the region by the user
uint64_t size = 0; /// region size
std::string path = ""; /// file path, if the region is backed by a file
std::optional<uint16_t> id = std::nullopt; /// region id
uint32_t linger = 100; /// delay in ms before region destruction to collect outstanding events
};
} // namespace fair::mq

View File

@@ -44,7 +44,7 @@ class BenchmarkSampler : public Device
void Run() override
{
// store the channel reference to avoid traversing the map on every loop iteration
FairMQChannel& dataOutChannel = fChannels.at(fOutChannelName).at(0);
Channel& dataOutChannel = GetChannel(fOutChannelName, 0);
LOG(info) << "Starting the benchmark with message size of " << fMsgSize << " and " << fMaxIterations << " iterations.";
auto tStart = std::chrono::high_resolution_clock::now();
@@ -53,7 +53,7 @@ class BenchmarkSampler : public Device
while (!NewStatePending()) {
if (fMultipart) {
FairMQParts parts;
Parts parts;
for (size_t i = 0; i < fNumParts; ++i) {
parts.AddPart(dataOutChannel.NewMessage(fMsgSize, fair::mq::Alignment{fMsgAlignment}));
@@ -71,7 +71,7 @@ class BenchmarkSampler : public Device
++fNumIterations;
}
} else {
FairMQMessagePtr msg(dataOutChannel.NewMessage(fMsgSize, fair::mq::Alignment{fMsgAlignment}));
MessagePtr msg(dataOutChannel.NewMessage(fMsgSize, fair::mq::Alignment{fMsgAlignment}));
if (fMemSet) {
std::memset(msg->GetData(), 0, msg->GetSize());
}

View File

@@ -9,7 +9,7 @@
#ifndef FAIR_MQ_MERGER_H
#define FAIR_MQ_MERGER_H
#include <FairMQPoller.h>
#include <fairmq/Poller.h>
#include <fairmq/Device.h>
#include <fairlogger/Logger.h>
@@ -43,15 +43,15 @@ class Merger : public Device
void Run() override
{
int numInputs = fChannels.at(fInChannelName).size();
int numInputs = GetNumSubChannels(fInChannelName);
std::vector<FairMQChannel*> chans;
std::vector<Channel*> chans;
for (auto& chan : fChannels.at(fInChannelName)) {
chans.push_back(&chan);
}
FairMQPollerPtr poller(NewPoller(chans));
PollerPtr poller(NewPoller(chans));
if (fMultipart) {
while (!NewStatePending()) {
@@ -61,7 +61,7 @@ class Merger : public Device
for (int i = 0; i < numInputs; ++i) {
// Check if the channel has data ready to be received.
if (poller->CheckInput(i)) {
FairMQParts payload;
Parts payload;
if (Receive(payload, fInChannelName, i) >= 0) {
if (Send(payload, fOutChannelName) < 0) {
@@ -83,7 +83,7 @@ class Merger : public Device
for (int i = 0; i < numInputs; ++i) {
// Check if the channel has data ready to be received.
if (poller->CheckInput(i)) {
FairMQMessagePtr payload(fTransportFactory->CreateMessage());
MessagePtr payload(fTransportFactory->CreateMessage());
if (Receive(payload, fInChannelName, i) >= 0) {
if (Send(payload, fOutChannelName) < 0) {

View File

@@ -30,7 +30,7 @@ class Multiplier : public Device
fMultipart = fConfig->GetProperty<bool>("multipart");
fInChannelName = fConfig->GetProperty<std::string>("in-channel");
fOutChannelNames = fConfig->GetProperty<std::vector<std::string>>("out-channel");
fNumOutputs = fChannels.at(fOutChannelNames.at(0)).size();
fNumOutputs = GetNumSubChannels(fOutChannelNames.at(0));
if (fMultipart) {
OnData(fInChannelName, &Multiplier::HandleMultipartData);
@@ -40,21 +40,21 @@ class Multiplier : public Device
}
bool HandleSingleData(std::unique_ptr<FairMQMessage>& payload, int)
bool HandleSingleData(std::unique_ptr<Message>& payload, int)
{
for (unsigned int i = 0; i < fOutChannelNames.size() - 1; ++i) { // all except last channel
for (unsigned int j = 0; j < fChannels.at(fOutChannelNames.at(i)).size(); ++j) { // all subChannels in a channel
FairMQMessagePtr msgCopy(fTransportFactory->CreateMessage());
for (unsigned int j = 0; j < GetNumSubChannels(fOutChannelNames.at(i)); ++j) { // all subChannels in a channel
MessagePtr msgCopy(fTransportFactory->CreateMessage());
msgCopy->Copy(*payload);
Send(msgCopy, fOutChannelNames.at(i), j);
}
}
unsigned int lastChannelSize = fChannels.at(fOutChannelNames.back()).size();
unsigned int lastChannelSize = GetNumSubChannels(fOutChannelNames.back());
for (unsigned int i = 0; i < lastChannelSize - 1; ++i) { // iterate over all except last subChannels of the last channel
FairMQMessagePtr msgCopy(fTransportFactory->CreateMessage());
MessagePtr msgCopy(fTransportFactory->CreateMessage());
msgCopy->Copy(*payload);
Send(msgCopy, fOutChannelNames.back(), i);
@@ -65,14 +65,14 @@ class Multiplier : public Device
return true;
}
bool HandleMultipartData(FairMQParts& payload, int)
bool HandleMultipartData(Parts& payload, int)
{
for (unsigned int i = 0; i < fOutChannelNames.size() - 1; ++i) { // all except last channel
for (unsigned int j = 0; j < fChannels.at(fOutChannelNames.at(i)).size(); ++j) { // all subChannels in a channel
FairMQParts parts;
for (unsigned int j = 0; j < GetNumSubChannels(fOutChannelNames.at(i)); ++j) { // all subChannels in a channel
Parts parts;
for (int k = 0; k < payload.Size(); ++k) {
FairMQMessagePtr msgCopy(fTransportFactory->CreateMessage());
MessagePtr msgCopy(fTransportFactory->CreateMessage());
msgCopy->Copy(payload.AtRef(k));
parts.AddPart(std::move(msgCopy));
}
@@ -81,13 +81,13 @@ class Multiplier : public Device
}
}
unsigned int lastChannelSize = fChannels.at(fOutChannelNames.back()).size();
unsigned int lastChannelSize = GetNumSubChannels(fOutChannelNames.back());
for (unsigned int i = 0; i < lastChannelSize - 1; ++i) { // iterate over all except last subChannels of the last channel
FairMQParts parts;
Parts parts;
for (int k = 0; k < payload.Size(); ++k) {
FairMQMessagePtr msgCopy(fTransportFactory->CreateMessage());
MessagePtr msgCopy(fTransportFactory->CreateMessage());
msgCopy->Copy(payload.AtRef(k));
parts.AddPart(std::move(msgCopy));
}

View File

@@ -34,7 +34,7 @@ class Proxy : public Device
{
if (fMultipart) {
while (!NewStatePending()) {
FairMQParts payload;
Parts payload;
if (Receive(payload, fInChannelName) >= 0) {
if (Send(payload, fOutChannelName) < 0) {
LOG(debug) << "Transfer interrupted";
@@ -47,7 +47,7 @@ class Proxy : public Device
}
} else {
while (!NewStatePending()) {
FairMQMessagePtr payload(fTransportFactory->CreateMessage());
MessagePtr payload(fTransportFactory->CreateMessage());
if (Receive(payload, fInChannelName) >= 0) {
if (Send(payload, fOutChannelName) < 0) {
LOG(debug) << "Transfer interrupted";

View File

@@ -9,7 +9,6 @@
#ifndef FAIR_MQ_SINK_H
#define FAIR_MQ_SINK_H
#include <FairMQPoller.h>
#include <fairmq/Device.h>
#include <fairmq/tools/Strings.h>
@@ -48,7 +47,7 @@ class Sink : public Device
void Run() override
{
// store the channel reference to avoid traversing the map on every loop iteration
FairMQChannel& dataInChannel = fChannels.at(fInChannelName).at(0);
Channel& dataInChannel = GetChannel(fInChannelName, 0);
LOG(info) << "Starting sink and expecting to receive " << fMaxIterations << " messages.";
auto tStart = std::chrono::high_resolution_clock::now();
@@ -70,7 +69,7 @@ class Sink : public Device
while (!NewStatePending()) {
if (fMultipart) {
FairMQParts parts;
Parts parts;
if (dataInChannel.Receive(parts) < 0) {
continue;
}
@@ -80,7 +79,7 @@ class Sink : public Device
}
}
} else {
FairMQMessagePtr msg(dataInChannel.NewMessage());
MessagePtr msg(dataInChannel.NewMessage());
if (dataInChannel.Receive(msg) < 0) {
continue;
}

View File

@@ -30,13 +30,13 @@ class Splitter : public Device
fMultipart = fConfig->GetProperty<bool>("multipart");
fInChannelName = fConfig->GetProperty<std::string>("in-channel");
fOutChannelName = fConfig->GetProperty<std::string>("out-channel");
fNumOutputs = fChannels.at(fOutChannelName).size();
fNumOutputs = GetNumSubChannels(fOutChannelName);
fDirection = 0;
if (fMultipart) {
OnData(fInChannelName, &Splitter::HandleData<FairMQParts>);
OnData(fInChannelName, &Splitter::HandleData<Parts>);
} else {
OnData(fInChannelName, &Splitter::HandleData<FairMQMessagePtr>);
OnData(fInChannelName, &Splitter::HandleData<MessagePtr>);
}
}

View File

@@ -10,7 +10,7 @@
#include <fairmq/ofi/Socket.h>
#include <fairmq/ofi/TransportFactory.h>
#include <fairmq/tools/Strings.h>
#include <FairMQLogger.h>
#include <fairlogger/Logger.h>
#include <asiofi.hpp>
#include <asio/buffer.hpp>

View File

@@ -166,6 +166,22 @@ struct TransportFactory final : mq::TransportFactory
throw std::runtime_error("Not yet implemented UMR.");
}
auto CreateUnmanagedRegion(std::size_t /*size*/,
RegionCallback /*callback*/,
RegionConfig /*cfg*/)
-> std::unique_ptr<mq::UnmanagedRegion> override
{
throw std::runtime_error("Not yet implemented UMR.");
}
auto CreateUnmanagedRegion(std::size_t /*size*/,
RegionBulkCallback /*callback*/,
RegionConfig /*cfg*/)
-> std::unique_ptr<mq::UnmanagedRegion> override
{
throw std::runtime_error("Not yet implemented UMR.");
}
auto SubscribeToRegionEvents(RegionEventCallback /*callback*/) -> void override
{
throw std::runtime_error("Not yet implemented.");

View File

@@ -11,7 +11,7 @@
#include "PMIx.hpp"
#include <FairMQLogger.h>
#include <fairlogger/Logger.h>
#include <fairmq/tools/Semaphore.h>
#include <memory> // make_unique
#include <string>

View File

@@ -14,7 +14,7 @@
#include <fairmq/Plugin.h>
#include <fairmq/Version.h>
#include <FairMQLogger.h>
#include <fairlogger/Logger.h>
#include <string>
#include <sstream>

View File

@@ -67,6 +67,7 @@ Plugin::ProgOptions ConfigPluginProgramOptions()
("shm-segment-size", po::value<size_t >()->default_value(2ULL << 30), "Shared memory: size of the shared memory segment (in bytes).")
("shm-allocation", po::value<string >()->default_value("rbtree_best_fit"), "Shared memory allocation algorithm: rbtree_best_fit/simple_seq_fit.")
("shm-segment-id", po::value<uint16_t >()->default_value(0), "EXPERIMENTAL: Shared memory segment id for message creation.")
("shmid", po::value<uint64_t >(), "EXPERIMENTAL: Fixed shmid to use instead of deriving it from the session name.")
("shm-mlock-segment", po::value<bool >()->default_value(false), "Shared memory: mlock the shared memory segment after initialization (opened or created).")
("shm-mlock-segment-on-creation", po::value<bool >()->default_value(false), "Shared memory: mlock the shared memory segment only once when created.")
("shm-zero-segment", po::value<bool >()->default_value(false), "Shared memory: zero the shared memory segment memory after initialization (opened or created).")

View File

@@ -72,6 +72,9 @@ Control::Control(const string& name, Plugin::Version version, const string& main
if (control == "static") {
LOG(debug) << "Running builtin controller: static";
fControllerThread = thread(&Control::StaticMode, this);
} else if (control == "gui") {
LOG(debug) << "Running builtin controller: gui";
fControllerThread = thread(&Control::GUIMode, this);
} else if (control == "dynamic" || control == "external" || control == "interactive") {
LOG(debug) << "Running builtin controller: interactive";
fControllerThread = thread(&Control::InteractiveMode, this);
@@ -380,6 +383,28 @@ try {
ReleaseDeviceControl();
}
auto Control::GUIMode() -> void
try {
RunStartupSequence();
{
// Wait for next state, which is DeviceState::Ready,
// or for device shutdown request (Ctrl-C)
pair<bool, fair::mq::State> result;
do {
result = fStateQueue.WaitForNext(chrono::milliseconds(50));
} while (!fDeviceShutdownRequested);
}
RunShutdownSequence();
} catch (PluginServices::DeviceControlError& e) {
// If we are here, it means another plugin has taken control. That's fine, just print the
// exception message and do nothing else.
LOG(debug) << e.what();
} catch (DeviceErrorState&) {
ReleaseDeviceControl();
}
auto Control::SignalHandler() -> void
{
while (gSignalCount == 0 && !fPluginShutdownRequested) {

View File

@@ -43,6 +43,7 @@ class Control : public Plugin
static auto PrintStateMachine() -> void;
auto PrintNumberOfConnectedPeers() -> void;
auto StaticMode() -> void;
auto GUIMode() -> void;
auto SignalHandler() -> void;
auto RunShutdownSequence() -> void;
auto RunStartupSequence() -> void;

View File

@@ -11,7 +11,7 @@
#include <memory>
#include <string>
using FairMQDevicePtr = FairMQDevice*;
using FairMQDevicePtr = fair::mq::Device*;
// to be implemented by the user to return a child class of FairMQDevice
FairMQDevicePtr getDevice(const fair::mq::ProgOptions& config);
@@ -45,7 +45,7 @@ int main(int argc, char* argv[])
// });
runner.AddHook<InstantiateDevice>([](DeviceRunner& r){
r.fDevice = std::unique_ptr<FairMQDevice>{getDevice(r.fConfig)};
r.fDevice = std::unique_ptr<fair::mq::Device>{getDevice(r.fConfig)};
});
return runner.Run();

56
fairmq/shmem/Common.cxx Normal file
View File

@@ -0,0 +1,56 @@
/********************************************************************************
* Copyright (C) 2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
* copied verbatim in the file "LICENSE" *
********************************************************************************/
#include "Common.h"
#include <picosha2.h>
#include <unistd.h>
#include <iomanip>
#include <sstream>
#include <string>
namespace fair::mq::shmem
{
std::string makeShmIdStr(const std::string& sessionId, const std::string& userId)
{
std::string seed(userId + sessionId);
// generate a 8-digit hex value out of sha256 hash
std::vector<unsigned char> hash(4);
picosha2::hash256(seed.begin(), seed.end(), hash.begin(), hash.end());
return picosha2::bytes_to_hex_string(hash.begin(), hash.end());
}
std::string makeShmIdStr(const std::string& sessionId)
{
return makeShmIdStr(sessionId, std::to_string(geteuid()));
}
uint64_t makeShmIdUint64(const std::string& sessionId)
{
std::string shmId = makeShmIdStr(sessionId);
uint64_t id = 0;
std::stringstream ss;
ss << std::hex << shmId;
ss >> id;
return id;
}
std::string makeShmIdStr(uint64_t val)
{
std::stringstream ss;
ss << std::setfill('0') << std::setw(8) << std::hex << val;
return ss.str();
}
} // namespace fair::mq::shmem

View File

@@ -8,10 +8,7 @@
#ifndef FAIR_MQ_SHMEM_COMMON_H_
#define FAIR_MQ_SHMEM_COMMON_H_
#include <picosha2.h>
#include <atomic>
#include <sstream>
#include <string>
#include <functional> // std::equal_to
@@ -26,20 +23,21 @@
#include <boost/unordered_map.hpp>
#include <boost/variant.hpp>
#include <unistd.h>
#include <sys/types.h>
namespace fair::mq::shmem
{
static constexpr uint64_t kManagementSegmentSize = 6553600;
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::simple_seq_fit<boost::interprocess::mutex_family, boost::interprocess::offset_ptr<void>>,
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::rbtree_best_fit<boost::interprocess::mutex_family, boost::interprocess::offset_ptr<void>>,
boost::interprocess::null_index>;
// boost::interprocess::iset_index>;
@@ -60,21 +58,24 @@ struct RegionInfo
{
RegionInfo(const VoidAlloc& alloc)
: fPath("", alloc)
, fFlags(0)
, fCreationFlags(0)
, fUserFlags(0)
, fSize(0)
, fDestroyed(false)
{}
RegionInfo(const char* path, const int flags, const uint64_t userFlags, const VoidAlloc& alloc)
RegionInfo(const char* path, int flags, uint64_t userFlags, uint64_t size, const VoidAlloc& alloc)
: fPath(path, alloc)
, fFlags(flags)
, fCreationFlags(flags)
, fUserFlags(userFlags)
, fSize(size)
, fDestroyed(false)
{}
Str fPath;
int fFlags;
int fCreationFlags;
uint64_t fUserFlags;
uint64_t fSize;
bool fDestroyed;
};
@@ -150,6 +151,7 @@ struct MetaHeader
mutable boost::interprocess::managed_shared_memory::handle_t fShared;
uint16_t fRegionId;
mutable uint16_t fSegmentId;
bool fManaged;
};
#ifdef FAIRMQ_DEBUG_MODE
@@ -212,31 +214,11 @@ struct RegionBlock
// find id for unique shmem name:
// a hash of user id + session id, truncated to 8 characters (to accommodate for name size limit on some systems (MacOS)).
inline std::string makeShmIdStr(const std::string& sessionId, const std::string& userId)
{
std::string seed(userId + sessionId);
// generate a 8-digit hex value out of sha256 hash
std::vector<unsigned char> hash(4);
picosha2::hash256(seed.begin(), seed.end(), hash.begin(), hash.end());
std::string makeShmIdStr(const std::string& sessionId, const std::string& userId);
std::string makeShmIdStr(const std::string& sessionId);
std::string makeShmIdStr(uint64_t val);
uint64_t makeShmIdUint64(const std::string& sessionId);
return picosha2::bytes_to_hex_string(hash.begin(), hash.end());
}
inline std::string makeShmIdStr(const std::string& sessionId)
{
return makeShmIdStr(sessionId, std::to_string(geteuid()));
}
inline uint64_t makeShmIdUint64(const std::string& sessionId)
{
std::string shmId = makeShmIdStr(sessionId);
uint64_t id = 0;
std::stringstream ss;
ss << std::hex << shmId;
ss >> id;
return id;
}
struct SegmentSize : public boost::static_visitor<size_t>
{

View File

@@ -11,19 +11,20 @@
#include "Common.h"
#include "Monitor.h"
#include "Region.h"
#include "UnmanagedRegion.h"
#include <fairmq/Message.h>
#include <fairmq/ProgOptions.h>
#include <fairmq/tools/Strings.h>
#include <fairmq/Transports.h>
#include <FairMQLogger.h>
#include <FairMQMessage.h>
#include <fairlogger/Logger.h>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/filesystem.hpp>
#include <boost/interprocess/ipc/message_queue.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/sync/named_condition.hpp>
#include <boost/interprocess/sync/interprocess_condition.hpp>
#include <boost/interprocess/sync/interprocess_mutex.hpp>
#include <boost/interprocess/sync/named_mutex.hpp>
#include <boost/variant.hpp>
@@ -127,15 +128,13 @@ struct ShmHeader
class Manager
{
public:
Manager(const std::string& sessionName, std::string deviceId, size_t size, const ProgOptions* config)
: fShmId64(makeShmIdUint64(sessionName))
, fShmId(makeShmIdStr(sessionName))
Manager(const std::string& sessionName, size_t size, const ProgOptions* config)
: fShmId64(config ? config->GetProperty<uint64_t>("shmid", makeShmIdUint64(sessionName)) : makeShmIdUint64(sessionName))
, fShmId(makeShmIdStr(fShmId64))
, fSegmentId(config ? config->GetProperty<uint16_t>("shm-segment-id", 0) : 0)
, fDeviceId(std::move(deviceId))
, 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(), kManagementSegmentSize)
, fShmVoidAlloc(fManagementSegment.get_segment_manager())
, fShmMtx(boost::interprocess::open_or_create, std::string("fmq_" + fShmId + "_mtx").c_str())
, fRegionEventsShmCV(boost::interprocess::open_or_create, std::string("fmq_" + fShmId + "_cv").c_str())
, fShmMtx(fManagementSegment.find_or_construct<boost::interprocess::interprocess_mutex>(boost::interprocess::unique_instance)())
, fNumObservedEvents(0)
, fDeviceCounter(nullptr)
, fEventCounter(nullptr)
@@ -188,17 +187,14 @@ class Manager
}
if (autolaunchMonitor) {
boost::interprocess::scoped_lock<boost::interprocess::named_mutex> lock(fShmMtx);
boost::interprocess::scoped_lock<boost::interprocess::interprocess_mutex> lock(*fShmMtx);
StartMonitor(fShmId);
}
fHeartbeatThread = std::thread(&Manager::Heartbeats, this);
try {
std::stringstream ss;
boost::interprocess::scoped_lock<boost::interprocess::named_mutex> lock(fShmMtx);
fShmSegments = fManagementSegment.find_or_construct<Uint16SegmentInfoHashMap>(unique_instance)(fShmVoidAlloc);
boost::interprocess::scoped_lock<boost::interprocess::interprocess_mutex> lock(*fShmMtx);
SessionInfo* sessionInfo = fManagementSegment.find<SessionInfo>(unique_instance).first;
if (sessionInfo) {
@@ -218,8 +214,6 @@ class Manager
LOG(debug) << "initialized event counter with: " << fEventCounter->fCount;
}
fShmRegions = fManagementSegment.find_or_construct<Uint16RegionInfoHashMap>(unique_instance)(fShmVoidAlloc);
fDeviceCounter = fManagementSegment.find<DeviceCounter>(unique_instance).first;
if (fDeviceCounter) {
LOG(debug) << "device counter found, with value of " << fDeviceCounter->fCount << ". incrementing.";
@@ -231,22 +225,21 @@ class Manager
LOG(debug) << "initialized device counter with: " << fDeviceCounter->fCount;
}
std::string op("create/open");
fShmSegments = fManagementSegment.find_or_construct<Uint16SegmentInfoHashMap>(unique_instance)(fShmVoidAlloc);
fShmRegions = fManagementSegment.find_or_construct<Uint16RegionInfoHashMap>(unique_instance)(fShmVoidAlloc);
try {
std::string segmentName("fmq_" + fShmId + "_m_" + std::to_string(fSegmentId));
auto it = fShmSegments->find(fSegmentId);
if (it == fShmSegments->end()) {
op = "create";
// no segment with given id exists, creating
if (allocationAlgorithm == "rbtree_best_fit") {
fSegments.emplace(fSegmentId, RBTreeBestFitSegment(create_only, std::string("fmq_" + fShmId + "_m_" + std::to_string(fSegmentId)).c_str(), size));
fSegments.emplace(fSegmentId, RBTreeBestFitSegment(open_or_create, segmentName.c_str(), size));
fShmSegments->emplace(fSegmentId, AllocationAlgorithm::rbtree_best_fit);
} else if (allocationAlgorithm == "simple_seq_fit") {
fSegments.emplace(fSegmentId, SimpleSeqFitSegment(create_only, std::string("fmq_" + fShmId + "_m_" + std::to_string(fSegmentId)).c_str(), size));
fSegments.emplace(fSegmentId, SimpleSeqFitSegment(open_or_create, segmentName.c_str(), size));
fShmSegments->emplace(fSegmentId, AllocationAlgorithm::simple_seq_fit);
}
ss << "Created ";
(fEventCounter->fCount)++;
if (mlockSegmentOnCreation) {
MlockSegment(fSegmentId);
}
@@ -254,31 +247,28 @@ class Manager
ZeroSegment(fSegmentId);
}
} else {
op = "open";
// found segment with the given id, opening
if (it->second.fAllocationAlgorithm == AllocationAlgorithm::rbtree_best_fit) {
fSegments.emplace(fSegmentId, RBTreeBestFitSegment(open_only, std::string("fmq_" + fShmId + "_m_" + std::to_string(fSegmentId)).c_str()));
fSegments.emplace(fSegmentId, RBTreeBestFitSegment(open_or_create, segmentName.c_str(), size));
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(fSegmentId, SimpleSeqFitSegment(open_only, std::string("fmq_" + fShmId + "_m_" + std::to_string(fSegmentId)).c_str()));
fSegments.emplace(fSegmentId, SimpleSeqFitSegment(open_or_create, segmentName.c_str(), size));
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 << "_m_" << fSegmentId << "'."
LOG(debug) << "Created/opened shared memory segment '" << "fmq_" << fShmId << "_m_" << fSegmentId << "'."
<< " Size: " << boost::apply_visitor(SegmentSize(), fSegments.at(fSegmentId)) << " bytes."
<< " Available: " << boost::apply_visitor(SegmentFreeMemory(), fSegments.at(fSegmentId)) << " bytes."
<< " Allocation algorithm: " << allocationAlgorithm;
LOG(debug) << ss.str();
} catch(interprocess_exception& bie) {
LOG(error) << "Failed to " << op << " shared memory segment (" << "fmq_" << fShmId << "_m_" << fSegmentId << "): " << bie.what();
throw TransportError(tools::ToString("Failed to ", op, " shared memory segment (", "fmq_", fShmId, "_m_", fSegmentId, "): ", bie.what()));
} catch (interprocess_exception& bie) {
LOG(error) << "Failed to create/open shared memory segment '" << "fmq_" << fShmId << "_m_" << fSegmentId << "': " << bie.what();
throw TransportError(tools::ToString("Failed to create/open shared memory segment '", "fmq_", fShmId, "_m_", fSegmentId, "': ", bie.what()));
}
if (mlockSegment) {
@@ -288,6 +278,8 @@ class Manager
ZeroSegment(fSegmentId);
}
(fEventCounter->fCount)++;
#ifdef FAIRMQ_DEBUG_MODE
fMsgDebug = fManagementSegment.find_or_construct<Uint16MsgDebugMapHashMap>(unique_instance)(fShmVoidAlloc);
fShmMsgCounters = fManagementSegment.find_or_construct<Uint16MsgCounterHashMap>(unique_instance)(fShmVoidAlloc);
@@ -368,69 +360,56 @@ class Manager
}
bool Interrupted() { return fInterrupted.load(); }
std::pair<boost::interprocess::mapped_region*, uint16_t> CreateRegion(const size_t size,
const int64_t userFlags,
RegionCallback callback,
RegionBulkCallback bulkCallback,
const std::string& path,
int flags,
fair::mq::RegionConfig cfg)
std::pair<UnmanagedRegion*, uint16_t> CreateRegion(const size_t size,
RegionCallback callback,
RegionBulkCallback bulkCallback,
RegionConfig cfg)
{
using namespace boost::interprocess;
try {
std::pair<mapped_region*, uint16_t> result;
std::pair<UnmanagedRegion*, uint16_t> result;
{
uint16_t id = 0;
boost::interprocess::scoped_lock<boost::interprocess::named_mutex> lock(fShmMtx);
boost::interprocess::scoped_lock<boost::interprocess::interprocess_mutex> lock(*fShmMtx);
RegionCounter* rc = fManagementSegment.find<RegionCounter>(unique_instance).first;
if (!cfg.id.has_value()) {
RegionCounter* rc = fManagementSegment.find<RegionCounter>(unique_instance).first;
if (rc) {
LOG(debug) << "region counter found, with value of " << rc->fCount << ". incrementing.";
(rc->fCount)++;
LOG(debug) << "incremented region counter, now: " << rc->fCount;
} else {
LOG(debug) << "no region counter found, creating one and initializing with 1";
rc = fManagementSegment.construct<RegionCounter>(unique_instance)(1);
LOG(debug) << "initialized region counter with: " << rc->fCount;
}
id = rc->fCount;
auto it = fRegions.find(id);
if (it != fRegions.end()) {
LOG(error) << "Trying to create a region that already exists";
return {nullptr, id};
}
auto r = fRegions.emplace(id, std::make_unique<Region>(fShmId, id, size, false, callback, bulkCallback, path, flags));
// LOG(debug) << "Created region with id '" << id << "', path: '" << path << "', flags: '" << flags << "'";
if (cfg.lock) {
LOG(debug) << "Locking region " << id << "...";
if (mlock(r.first->second->fRegion.get_address(), r.first->second->fRegion.get_size()) == -1) {
LOG(error) << "Could not lock region " << id << ". Code: " << errno << ", reason: " << strerror(errno);
throw TransportError(tools::ToString("Could not lock region ", id, ": ", strerror(errno)));
if (rc) {
LOG(trace) << "region counter found, with value of " << rc->fCount << ". incrementing.";
(rc->fCount)++;
LOG(trace) << "incremented region counter, now: " << rc->fCount;
} else {
LOG(trace) << "no region counter found, creating one and initializing with 1024";
rc = fManagementSegment.construct<RegionCounter>(unique_instance)(1024);
LOG(trace) << "initialized region counter with: " << rc->fCount;
}
LOG(debug) << "Successfully locked region " << id << ".";
}
if (cfg.zero) {
LOG(debug) << "Zeroing free memory of region " << id << "...";
memset(r.first->second->fRegion.get_address(), 0x00, r.first->second->fRegion.get_size());
LOG(debug) << "Successfully zeroed free memory of region " << id << ".";
cfg.id = rc->fCount;
}
fShmRegions->emplace(id, RegionInfo(path.c_str(), flags, userFlags, fShmVoidAlloc));
const uint16_t id = cfg.id.value();
r.first->second->StartReceivingAcks();
result.first = &(r.first->second->fRegion);
auto res = fRegions.emplace(id, std::make_unique<UnmanagedRegion>(fShmId, size, false, cfg));
bool newRegionCreated = res.second;
UnmanagedRegion& region = *(res.first->second);
// LOG(debug) << "Created region with id '" << id << "', path: '" << cfg.path << "', flags: '" << cfg.creationFlags << "'";
if (!newRegionCreated) {
region.fRemote = false; // TODO: this should be more clear, refactor it.
}
// start ack receiver only if a callback has been provided.
if (callback || bulkCallback) {
region.SetCallbacks(callback, bulkCallback);
region.InitializeQueues();
region.StartAckSender();
region.StartAckReceiver();
}
result.first = &(region);
result.second = id;
(fEventCounter->fCount)++;
}
fRegionsGen += 1; // signal TL cache invalidation
fRegionEventsShmCV.notify_all();
return result;
} catch (interprocess_exception& e) {
@@ -440,7 +419,7 @@ class Manager
}
}
Region* GetRegion(const uint16_t id)
UnmanagedRegion* GetRegion(const uint16_t id)
{
// NOTE: gcc optimizations. Prevent loading tls addresses many times in the fast path
const auto &lTlCache = fTlRegionCache;
@@ -454,19 +433,19 @@ class Manager
}
}
boost::interprocess::scoped_lock<boost::interprocess::named_mutex> lock(fShmMtx);
boost::interprocess::scoped_lock<boost::interprocess::interprocess_mutex> shmLock(*fShmMtx);
// slow path: check invalidation
if (lTlCacheGen != fRegionsGen) {
fTlRegionCache.fRegionsTLCache.clear();
}
auto *lRegion = GetRegionUnsafe(id);
auto* lRegion = GetRegionUnsafe(id, shmLock);
fTlRegionCache.fRegionsTLCache.emplace_back(std::make_tuple(lRegion, id, fShmId64));
fTlRegionCache.fRegionsTLCacheGen = fRegionsGen;
return lRegion;
}
Region* GetRegionUnsafe(const uint16_t id)
UnmanagedRegion* GetRegionUnsafe(const uint16_t id, boost::interprocess::scoped_lock<boost::interprocess::interprocess_mutex>& lockedShmLock)
{
// remote region could actually be a local one if a message originates from this device (has been sent out and returned)
auto it = fRegions.find(id);
@@ -476,11 +455,18 @@ class Manager
try {
// get region info
RegionInfo regionInfo = fShmRegions->at(id);
std::string path = regionInfo.fPath.c_str();
int flags = regionInfo.fFlags;
// LOG(debug) << "Located remote region with id '" << id << "', path: '" << path << "', flags: '" << flags << "'";
// safe to unlock now - no shm container accessed after this
lockedShmLock.unlock();
RegionConfig cfg;
cfg.id = id;
cfg.creationFlags = regionInfo.fCreationFlags;
cfg.path = regionInfo.fPath.c_str();
// LOG(debug) << "Located remote region with id '" << id << "', path: '" << cfg.path << "', flags: '" << cfg.creationFlags << "'";
auto r = fRegions.emplace(id, std::make_unique<Region>(fShmId, id, 0, true, nullptr, nullptr, path, flags));
auto r = fRegions.emplace(id, std::make_unique<UnmanagedRegion>(fShmId, 0, true, std::move(cfg)));
r.first->second->InitializeQueues();
r.first->second->StartAckSender();
lockedShmLock.lock();
return r.first->second.get();
} catch (std::out_of_range& oor) {
LOG(error) << "Could not get remote region with id '" << id << "'. Does the region creator run with the same session id?";
@@ -498,48 +484,23 @@ class Manager
try {
fRegions.at(id)->StopAcks();
{
boost::interprocess::scoped_lock<boost::interprocess::named_mutex> lock(fShmMtx);
fShmRegions->at(id).fDestroyed = true;
boost::interprocess::scoped_lock<boost::interprocess::interprocess_mutex> lock(*fShmMtx);
if (fRegions.at(id)->RemoveOnDestruction()) {
fShmRegions->at(id).fDestroyed = true;
(fEventCounter->fCount)++;
}
fRegions.erase(id);
(fEventCounter->fCount)++;
}
fRegionEventsShmCV.notify_all();
} catch(std::out_of_range& oor) {
} catch (std::out_of_range& oor) {
LOG(debug) << "RemoveRegion() could not locate region with id '" << id << "'";
}
fRegionsGen += 1; // signal TL cache invalidation
}
std::vector<fair::mq::RegionInfo> GetRegionInfo()
{
boost::interprocess::scoped_lock<boost::interprocess::named_mutex> lock(fShmMtx);
return GetRegionInfoUnsafe();
}
std::vector<fair::mq::RegionInfo> GetRegionInfoUnsafe()
{
std::vector<fair::mq::RegionInfo> result;
for (const auto& e : *fShmRegions) {
fair::mq::RegionInfo info;
info.managed = false;
info.id = e.first;
info.flags = e.second.fUserFlags;
info.event = e.second.fDestroyed ? RegionEvent::destroyed : RegionEvent::created;
if (!e.second.fDestroyed) {
auto region = GetRegionUnsafe(info.id);
if (region) {
info.ptr = region->fRegion.get_address();
info.size = region->fRegion.get_size();
} else {
throw std::runtime_error(tools::ToString("GetRegionInfoUnsafe() could not get region with id '", info.id, "'"));
}
} else {
info.ptr = nullptr;
info.size = 0;
}
result.push_back(info);
}
boost::interprocess::scoped_lock<boost::interprocess::interprocess_mutex> shmLock(*fShmMtx);
for (const auto& e : *fShmSegments) {
// make sure any segments in the session are found
@@ -558,6 +519,27 @@ class Manager
}
}
for (const auto& e : *fShmRegions) {
fair::mq::RegionInfo info;
info.managed = false;
info.id = e.first;
info.flags = e.second.fUserFlags;
info.event = e.second.fDestroyed ? RegionEvent::destroyed : RegionEvent::created;
if (info.event == RegionEvent::created) {
auto region = GetRegionUnsafe(info.id, shmLock);
if (region) {
info.ptr = region->GetData();
info.size = region->GetSize();
} else {
throw std::runtime_error(tools::ToString("GetRegionInfo() could not get region with id '", info.id, "'"));
}
} else {
info.ptr = nullptr;
info.size = 0;
}
result.push_back(info);
}
return result;
}
@@ -565,13 +547,13 @@ class Manager
{
if (fRegionEventThread.joinable()) {
LOG(debug) << "Already subscribed. Overwriting previous subscription.";
boost::interprocess::scoped_lock<boost::interprocess::named_mutex> lock(fShmMtx);
std::unique_lock<std::mutex> lock(fRegionEventsMtx);
fRegionEventsSubscriptionActive = false;
lock.unlock();
fRegionEventsShmCV.notify_all();
fRegionEventsCV.notify_one();
fRegionEventThread.join();
}
boost::interprocess::scoped_lock<boost::interprocess::named_mutex> lock(fShmMtx);
std::lock_guard<std::mutex> lock(fRegionEventsMtx);
fRegionEventCallback = callback;
fRegionEventsSubscriptionActive = true;
fRegionEventThread = std::thread(&Manager::RegionEventsSubscription, this);
@@ -582,10 +564,10 @@ class Manager
void UnsubscribeFromRegionEvents()
{
if (fRegionEventThread.joinable()) {
boost::interprocess::scoped_lock<boost::interprocess::named_mutex> lock(fShmMtx);
std::unique_lock<std::mutex> lock(fRegionEventsMtx);
fRegionEventsSubscriptionActive = false;
lock.unlock();
fRegionEventsShmCV.notify_all();
fRegionEventsCV.notify_one();
fRegionEventThread.join();
lock.lock();
fRegionEventCallback = nullptr;
@@ -594,33 +576,38 @@ class Manager
void RegionEventsSubscription()
{
boost::interprocess::scoped_lock<boost::interprocess::named_mutex> lock(fShmMtx);
std::unique_lock<std::mutex> lock(fRegionEventsMtx);
while (fRegionEventsSubscriptionActive) {
auto infos = GetRegionInfoUnsafe();
for (const auto& i : infos) {
auto el = fObservedRegionEvents.find({i.id, i.managed});
if (el == fObservedRegionEvents.end()) { // if event id has not been observed
fObservedRegionEvents.emplace(std::make_pair(i.id, i.managed), i.event);
// if a region has been created and destroyed rapidly, we could see 'destroyed' without ever seeing 'created'
// TODO: do we care to show 'created' events if we know region is already destroyed?
if (i.event == RegionEvent::created) {
fRegionEventCallback(i);
++fNumObservedEvents;
} else {
fNumObservedEvents += 2;
}
} else { // if event id has been observed (expected - there are two events per id - created & destroyed)
// fire a callback if we have observed 'created' event and incoming is 'destroyed'
if (el->second == RegionEvent::created && i.event == RegionEvent::destroyed) {
fRegionEventCallback(i);
el->second = i.event;
++fNumObservedEvents;
} else {
// LOG(debug) << "ignoring event " << i.id << ": incoming: " << i.event << ", stored: " << el->second;
if (fNumObservedEvents != fEventCounter->fCount) {
auto infos = GetRegionInfo();
for (const auto& i : infos) {
auto el = fObservedRegionEvents.find({i.id, i.managed});
if (el == fObservedRegionEvents.end()) { // if event id has not been observed
fObservedRegionEvents.emplace(std::make_pair(i.id, i.managed), i.event);
// if a region has been created and destroyed rapidly, we could see 'destroyed' without ever seeing 'created'
// TODO: do we care to show 'created' events if we know region is already destroyed?
if (i.event == RegionEvent::created) {
fRegionEventCallback(i);
++fNumObservedEvents;
} else {
fNumObservedEvents += 2;
}
} else { // if event id has been observed (expected - there are two events per id - created & destroyed)
// fire a callback if we have observed 'created' event and incoming is 'destroyed'
if (el->second == RegionEvent::created && i.event == RegionEvent::destroyed) {
fRegionEventCallback(i);
el->second = i.event;
++fNumObservedEvents;
} else {
// LOG(debug) << "ignoring event " << i.id << ": incoming: " << i.event << ", stored: " << el->second;
}
}
}
}
fRegionEventsShmCV.wait(lock, [&] { return !fRegionEventsSubscriptionActive || fNumObservedEvents != fEventCounter->fCount; });
// TODO: do better than polling here, without adding too much shmem contention
fRegionEventsCV.wait_for(lock, std::chrono::milliseconds(50), [&] { return !fRegionEventsSubscriptionActive; });
}
}
@@ -729,7 +716,7 @@ class Manager
}
}
#ifdef FAIRMQ_DEBUG_MODE
boost::interprocess::scoped_lock<boost::interprocess::named_mutex> lock(fShmMtx);
boost::interprocess::scoped_lock<boost::interprocess::interprocess_mutex> lock(*fShmMtx);
IncrementShmMsgCounter(fSegmentId);
if (fMsgDebug->count(fSegmentId) == 0) {
fMsgDebug->emplace(fSegmentId, fShmVoidAlloc);
@@ -748,11 +735,11 @@ class Manager
{
char* ptr = GetAddressFromHandle(handle, segmentId);
#ifdef FAIRMQ_DEBUG_MODE
boost::interprocess::scoped_lock<boost::interprocess::named_mutex> lock(fShmMtx);
boost::interprocess::scoped_lock<boost::interprocess::interprocess_mutex> lock(*fShmMtx);
DecrementShmMsgCounter(segmentId);
try {
fMsgDebug->at(segmentId).erase(GetHandleFromAddress(ShmHeader::UserPtr(ptr), fSegmentId));
} catch(const std::out_of_range& oor) {
} catch (const std::out_of_range& oor) {
LOG(debug) << "could not locate debug container for " << segmentId << ": " << oor.what();
}
#endif
@@ -773,7 +760,7 @@ class Manager
bool lastRemoved = false;
try {
boost::interprocess::scoped_lock<named_mutex> lock(fShmMtx);
boost::interprocess::scoped_lock<interprocess_mutex> lock(*fShmMtx);
(fDeviceCounter->fCount)--;
@@ -787,8 +774,10 @@ class Manager
LOG(error) << "Manager could not acquire lock: " << e.what();
}
if (lastRemoved && !fNoCleanup) {
Monitor::Cleanup(ShmId{fShmId});
if (lastRemoved) {
if (!fNoCleanup) {
Monitor::Cleanup(ShmId{fShmId});
}
}
}
@@ -806,13 +795,13 @@ class Manager
uint64_t fShmId64;
std::string fShmId;
uint16_t fSegmentId;
std::string fDeviceId;
std::unordered_map<uint16_t, boost::variant<RBTreeBestFitSegment, SimpleSeqFitSegment>> fSegments;
boost::interprocess::managed_shared_memory fManagementSegment;
std::unordered_map<uint16_t, boost::variant<RBTreeBestFitSegment, SimpleSeqFitSegment>> fSegments; // TODO: refactor to use Segment class
boost::interprocess::managed_shared_memory fManagementSegment; // TODO: refactor to use ManagementSegment class
VoidAlloc fShmVoidAlloc;
boost::interprocess::named_mutex fShmMtx;
boost::interprocess::interprocess_mutex* fShmMtx;
boost::interprocess::named_condition fRegionEventsShmCV;
std::mutex fRegionEventsMtx;
std::condition_variable fRegionEventsCV;
std::thread fRegionEventThread;
std::function<void(fair::mq::RegionInfo)> fRegionEventCallback;
std::map<std::pair<uint16_t, bool>, RegionEvent> fObservedRegionEvents; // pair: <region id, managed>
@@ -822,11 +811,11 @@ class Manager
EventCounter* fEventCounter;
Uint16SegmentInfoHashMap* fShmSegments;
Uint16RegionInfoHashMap* fShmRegions;
std::unordered_map<uint16_t, std::unique_ptr<Region>> fRegions;
std::unordered_map<uint16_t, std::unique_ptr<UnmanagedRegion>> fRegions;
inline static std::atomic<unsigned long> fRegionsGen = 0ul;
inline static thread_local struct ManagerTLCache {
unsigned long fRegionsTLCacheGen;
std::vector<std::tuple<Region*, uint16_t, uint64_t>> fRegionsTLCache;
std::vector<std::tuple<UnmanagedRegion*, uint16_t, uint64_t>> fRegionsTLCache;
} fTlRegionCache;
#ifdef FAIRMQ_DEBUG_MODE

View File

@@ -10,11 +10,12 @@
#include "Common.h"
#include "Manager.h"
#include "Region.h"
#include "UnmanagedRegion.h"
#include <FairMQLogger.h>
#include <FairMQMessage.h>
#include <FairMQUnmanagedRegion.h>
#include "UnmanagedRegionImpl.h"
#include <fairmq/Message.h>
#include <fairmq/UnmanagedRegion.h>
#include <fairlogger/Logger.h>
#include <boost/interprocess/mapped_region.hpp>
@@ -34,22 +35,22 @@ class Message final : public fair::mq::Message
friend class Socket;
public:
Message(Manager& manager, FairMQTransportFactory* factory = nullptr)
Message(Manager& manager, fair::mq::TransportFactory* factory = nullptr)
: fair::mq::Message(factory)
, fManager(manager)
, fQueued(false)
, fMeta{0, 0, -1, -1, 0, fManager.GetSegmentId()}
, fMeta{0, 0, -1, -1, 0, fManager.GetSegmentId(), true}
, fRegionPtr(nullptr)
, fLocalPtr(nullptr)
{
fManager.IncrementMsgCounter();
}
Message(Manager& manager, Alignment alignment, FairMQTransportFactory* factory = nullptr)
Message(Manager& manager, Alignment alignment, fair::mq::TransportFactory* factory = nullptr)
: fair::mq::Message(factory)
, fManager(manager)
, fQueued(false)
, fMeta{0, 0, -1, -1, 0, fManager.GetSegmentId()}
, fMeta{0, 0, -1, -1, 0, fManager.GetSegmentId(), true}
, fAlignment(alignment.alignment)
, fRegionPtr(nullptr)
, fLocalPtr(nullptr)
@@ -57,11 +58,11 @@ class Message final : public fair::mq::Message
fManager.IncrementMsgCounter();
}
Message(Manager& manager, const size_t size, FairMQTransportFactory* factory = nullptr)
Message(Manager& manager, const size_t size, fair::mq::TransportFactory* factory = nullptr)
: fair::mq::Message(factory)
, fManager(manager)
, fQueued(false)
, fMeta{0, 0, -1, -1, 0, fManager.GetSegmentId()}
, fMeta{0, 0, -1, -1, 0, fManager.GetSegmentId(), true}
, fRegionPtr(nullptr)
, fLocalPtr(nullptr)
{
@@ -69,11 +70,11 @@ class Message final : public fair::mq::Message
fManager.IncrementMsgCounter();
}
Message(Manager& manager, const size_t size, Alignment alignment, FairMQTransportFactory* factory = nullptr)
Message(Manager& manager, const size_t size, Alignment alignment, fair::mq::TransportFactory* factory = nullptr)
: fair::mq::Message(factory)
, fManager(manager)
, fQueued(false)
, fMeta{0, 0, -1, -1, 0, fManager.GetSegmentId()}
, fMeta{0, 0, -1, -1, 0, fManager.GetSegmentId(), true}
, fAlignment(alignment.alignment)
, fRegionPtr(nullptr)
, fLocalPtr(nullptr)
@@ -82,11 +83,11 @@ class Message final : public fair::mq::Message
fManager.IncrementMsgCounter();
}
Message(Manager& manager, void* data, const size_t size, fairmq_free_fn* ffn, void* hint = nullptr, FairMQTransportFactory* factory = nullptr)
Message(Manager& manager, void* data, const size_t size, fair::mq::FreeFn* ffn, void* hint = nullptr, fair::mq::TransportFactory* factory = nullptr)
: fair::mq::Message(factory)
, fManager(manager)
, fQueued(false)
, fMeta{0, 0, -1, -1, 0, fManager.GetSegmentId()}
, fMeta{0, 0, -1, -1, 0, fManager.GetSegmentId(), true}
, fRegionPtr(nullptr)
, fLocalPtr(nullptr)
{
@@ -101,11 +102,11 @@ class Message final : public fair::mq::Message
fManager.IncrementMsgCounter();
}
Message(Manager& manager, UnmanagedRegionPtr& region, void* data, const size_t size, void* hint = 0, FairMQTransportFactory* factory = nullptr)
Message(Manager& manager, UnmanagedRegionPtr& region, void* data, const size_t size, void* hint = 0, fair::mq::TransportFactory* factory = nullptr)
: fair::mq::Message(factory)
, fManager(manager)
, fQueued(false)
, fMeta{size, reinterpret_cast<size_t>(hint), -1, -1, static_cast<UnmanagedRegion*>(region.get())->fRegionId, fManager.GetSegmentId()}
, fMeta{size, reinterpret_cast<size_t>(hint), -1, -1, static_cast<UnmanagedRegionImpl*>(region.get())->fRegionId, fManager.GetSegmentId(), false}
, fRegionPtr(nullptr)
, fLocalPtr(static_cast<char*>(data))
{
@@ -124,7 +125,7 @@ class Message final : public fair::mq::Message
fManager.IncrementMsgCounter();
}
Message(Manager& manager, MetaHeader& hdr, FairMQTransportFactory* factory = nullptr)
Message(Manager& manager, MetaHeader& hdr, fair::mq::TransportFactory* factory = nullptr)
: fair::mq::Message(factory)
, fManager(manager)
, fQueued(false)
@@ -168,7 +169,7 @@ class Message final : public fair::mq::Message
InitializeChunk(size, fAlignment);
}
void Rebuild(void* data, size_t size, fairmq_free_fn* ffn, void* hint = nullptr) override
void Rebuild(void* data, size_t size, fair::mq::FreeFn* ffn, void* hint = nullptr) override
{
CloseMessage();
fQueued = false;
@@ -186,7 +187,7 @@ class Message final : public fair::mq::Message
void* GetData() const override
{
if (!fLocalPtr) {
if (fMeta.fRegionId == 0) {
if (fMeta.fManaged) {
if (fMeta.fSize > 0) {
fManager.GetSegment(fMeta.fSegmentId);
fLocalPtr = ShmHeader::UserPtr(fManager.GetAddressFromHandle(fMeta.fHandle, fMeta.fSegmentId));
@@ -196,7 +197,7 @@ class Message final : public fair::mq::Message
} else {
fRegionPtr = fManager.GetRegion(fMeta.fRegionId);
if (fRegionPtr) {
fLocalPtr = reinterpret_cast<char*>(fRegionPtr->fRegion.get_address()) + fMeta.fHandle;
fLocalPtr = reinterpret_cast<char*>(fRegionPtr->GetData()) + fMeta.fHandle;
} else {
// LOG(warn) << "could not get pointer from a region message";
fLocalPtr = nullptr;
@@ -258,7 +259,7 @@ class Message final : public fair::mq::Message
return 1;
}
if (fMeta.fRegionId == 0) { // managed segment
if (fMeta.fManaged) { // managed segment
fManager.GetSegment(fMeta.fSegmentId);
return ShmHeader::RefCount(fManager.GetAddressFromHandle(fMeta.fHandle, fMeta.fSegmentId));
} else { // unmanaged region
@@ -285,7 +286,7 @@ class Message final : public fair::mq::Message
CloseMessage();
}
if (otherMsg.fMeta.fRegionId == 0) { // managed segment
if (otherMsg.fMeta.fManaged) { // managed segment
fMeta = otherMsg.fMeta;
fManager.GetSegment(fMeta.fSegmentId);
ShmHeader::IncrementRefCount(fManager.GetAddressFromHandle(fMeta.fHandle, fMeta.fSegmentId));
@@ -316,7 +317,7 @@ class Message final : public fair::mq::Message
bool fQueued;
MetaHeader fMeta;
size_t fAlignment;
mutable Region* fRegionPtr;
mutable UnmanagedRegion* fRegionPtr;
mutable char* fLocalPtr;
char* InitializeChunk(const size_t size, size_t alignment = 0)
@@ -335,7 +336,7 @@ class Message final : public fair::mq::Message
void Deallocate()
{
if (fMeta.fHandle >= 0 && !fQueued) {
if (fMeta.fRegionId == 0) { // managed segment
if (fMeta.fManaged) { // managed segment
fManager.GetSegment(fMeta.fSegmentId);
uint16_t refCount = ShmHeader::DecrementRefCount(fManager.GetAddressFromHandle(fMeta.fHandle, fMeta.fSegmentId));
if (refCount == 1) {
@@ -380,9 +381,9 @@ class Message final : public fair::mq::Message
Deallocate();
fAlignment = 0;
fManager.DecrementMsgCounter();
} catch(SharedMemoryError& sme) {
} catch (SharedMemoryError& sme) {
LOG(error) << "error closing message: " << sme.what();
} catch(boost::interprocess::lock_exception& le) {
} catch (boost::interprocess::lock_exception& le) {
LOG(error) << "error closing message: " << le.what();
}
}

View File

@@ -6,8 +6,10 @@
* copied verbatim in the file "LICENSE" *
********************************************************************************/
#include "Monitor.h"
#include "Common.h"
#include "Monitor.h"
#include "Segment.h"
#include <fairmq/shmem/UnmanagedRegion.h>
#include <fairmq/tools/IO.h>
#include <fairmq/tools/Strings.h>
@@ -16,6 +18,7 @@
#include <boost/interprocess/file_mapping.hpp>
#include <boost/interprocess/sync/named_mutex.hpp>
#include <boost/interprocess/sync/interprocess_mutex.hpp>
#include <boost/interprocess/sync/named_condition.hpp>
#include <boost/interprocess/ipc/message_queue.hpp>
@@ -179,15 +182,21 @@ bool Monitor::PrintShm(const ShmId& shmId)
managed_shared_memory managementSegment(open_read_only, std::string("fmq_" + shmId.shmId + "_mng").c_str());
VoidAlloc allocInstance(managementSegment.get_segment_manager());
Uint16SegmentInfoHashMap* segmentInfos = managementSegment.find<Uint16SegmentInfoHashMap>(unique_instance).first;
Uint16SegmentInfoHashMap* shmSegments = managementSegment.find<Uint16SegmentInfoHashMap>(unique_instance).first;
std::unordered_map<uint16_t, boost::variant<RBTreeBestFitSegment, SimpleSeqFitSegment>> segments;
if (!segmentInfos) {
Uint16RegionInfoHashMap* shmRegions = managementSegment.find<Uint16RegionInfoHashMap>(unique_instance).first;
if (!shmSegments) {
LOG(error) << "Found management segment, but cannot locate segment info, something went wrong...";
return false;
}
for (const auto& s : *segmentInfos) {
if (!shmRegions) {
LOG(error) << "Found management segment, but cannot locate region info...";
}
for (const auto& s : *shmSegments) {
if (s.second.fAllocationAlgorithm == AllocationAlgorithm::rbtree_best_fit) {
segments.emplace(s.first, RBTreeBestFitSegment(open_read_only, std::string("fmq_" + shmId.shmId + "_m_" + to_string(s.first)).c_str()));
} else {
@@ -221,19 +230,30 @@ bool Monitor::PrintShm(const ShmId& shmId)
<< ", session: " << sessionName
<< ", creator id: " << creatorId
<< ", devices: " << numDevices
<< ", segments:\n";
<< ", managed segments:\n";
for (const auto& s : segments) {
size_t free = boost::apply_visitor(SegmentFreeMemory(), s.second);
size_t total = boost::apply_visitor(SegmentSize(), s.second);
size_t used = total - free;
ss << " [" << s.first
<< "]: total: " << total
std::string msgCount;
#ifdef FAIRMQ_DEBUG_MODE
<< ", msgs: " << ( (msgCounters != nullptr) ? to_string((*msgCounters)[s.first].fCount) : "unknown")
if (msgCounters) {
auto it = msgCounters->find(s.first);
if (it != msgCounters->end()) {
msgCount = to_string(it->second.fCount.load());
} else {
msgCount = "0";
}
}
#else
<< ", msgs: NODEBUG"
msgCount = "NODEBUG";
#endif
ss << " [" << s.first << "]"
<< ": total: " << total
<< ", msgs: " << msgCount
<< ", free: " << free
<< ", used: " << used
<< "\n";
@@ -243,6 +263,21 @@ bool Monitor::PrintShm(const ShmId& shmId)
<< "total: " << mtotal
<< ", free: " << mfree
<< ", used: " << mused;
if (shmRegions && !shmRegions->empty()) {
ss << "\n unmanaged regions:";
for (const auto& r : *shmRegions) {
ss << "\n [" << r.first << "]: " << (r.second.fDestroyed ? "destroyed" : "alive");
ss << ", size: " << r.second.fSize;
// try {
// boost::interprocess::message_queue q(open_only, std::string("fmq_" + std::string(shmId) + "_rgq_" + to_string(r.first)).c_str());
// ss << ", ack queue: " << q.get_num_msg() << " messages";
// } catch (bie&) {
// ss << ", ack queue: not found";
// }
}
}
LOGV(info, user1) << ss.str();
} catch (bie&) {
return false;
@@ -374,31 +409,35 @@ void Monitor::PrintDebugInfo(const ShmId& shmId __attribute__((unused)))
string managementSegmentName("fmq_" + shmId.shmId + "_mng");
try {
bipc::managed_shared_memory managementSegment(bipc::open_only, managementSegmentName.c_str());
boost::interprocess::named_mutex mtx(boost::interprocess::open_only, string("fmq_" + shmId.shmId + "_mtx").c_str());
boost::interprocess::scoped_lock<bipc::named_mutex> lock(mtx);
bipc::interprocess_mutex* mtx(managementSegment.find_or_construct<bipc::interprocess_mutex>(bipc::unique_instance)());
bipc::scoped_lock<bipc::interprocess_mutex> lock(*mtx);
Uint16MsgDebugMapHashMap* debug = managementSegment.find<Uint16MsgDebugMapHashMap>(bipc::unique_instance).first;
size_t numMessages = 0;
for (const auto& e : *debug) {
numMessages += e.second.size();
}
LOG(info) << endl << "found " << numMessages << " messages.";
for (const auto& s : *debug) {
for (const auto& e : s.second) {
using time_point = chrono::system_clock::time_point;
time_point tmpt{chrono::duration_cast<time_point::duration>(chrono::nanoseconds(e.second.fCreationTime))};
time_t t = chrono::system_clock::to_time_t(tmpt);
uint64_t ms = e.second.fCreationTime % 1000000;
auto tm = localtime(&t);
LOG(info) << "segment: " << setw(3) << setfill(' ') << s.first
<< ", offset: " << setw(12) << setfill(' ') << e.first
<< ", size: " << setw(10) << setfill(' ') << e.second.fSize
<< ", creator PID: " << e.second.fPid << setfill('0')
<< ", at: " << setw(2) << tm->tm_hour << ":" << setw(2) << tm->tm_min << ":" << setw(2) << tm->tm_sec << "." << setw(6) << ms;
if (debug) {
for (const auto& e : *debug) {
numMessages += e.second.size();
}
LOG(info) << endl << "found " << numMessages << " messages.";
for (const auto& s : *debug) {
for (const auto& e : s.second) {
using time_point = chrono::system_clock::time_point;
time_point tmpt{chrono::duration_cast<time_point::duration>(chrono::nanoseconds(e.second.fCreationTime))};
time_t t = chrono::system_clock::to_time_t(tmpt);
uint64_t ms = e.second.fCreationTime % 1000000;
auto tm = localtime(&t);
LOG(info) << "segment: " << setw(3) << setfill(' ') << s.first
<< ", offset: " << setw(12) << setfill(' ') << e.first
<< ", size: " << setw(10) << setfill(' ') << e.second.fSize
<< ", creator PID: " << e.second.fPid << setfill('0')
<< ", at: " << setw(2) << tm->tm_hour << ":" << setw(2) << tm->tm_min << ":" << setw(2) << tm->tm_sec << "." << setw(6) << ms;
}
}
} else {
LOG(info) << "no debug data found";
}
} catch (bie&) {
LOG(info) << "no segments found";
@@ -422,18 +461,23 @@ unordered_map<uint16_t, std::vector<BufferDebugInfo>> Monitor::GetDebugInfo(cons
string managementSegmentName("fmq_" + shmId.shmId + "_mng");
try {
bipc::managed_shared_memory managementSegment(bipc::open_only, managementSegmentName.c_str());
boost::interprocess::named_mutex mtx(boost::interprocess::open_only, string("fmq_" + shmId.shmId + "_mtx").c_str());
boost::interprocess::scoped_lock<bipc::named_mutex> lock(mtx);
bipc::interprocess_mutex* mtx(managementSegment.find_or_construct<bipc::interprocess_mutex>(bipc::unique_instance)());
bipc::scoped_lock<bipc::interprocess_mutex> lock(*mtx);
Uint16MsgDebugMapHashMap* debug = managementSegment.find<Uint16MsgDebugMapHashMap>(bipc::unique_instance).first;
result.reserve(debug->size());
for (const auto& s : *debug) {
result[s.first].reserve(s.second.size());
for (const auto& e : s.second) {
result[s.first][e.first] = BufferDebugInfo(e.first, e.second.fPid, e.second.fSize, e.second.fCreationTime);
if (debug) {
for (const auto& s : *debug) {
result[s.first].reserve(s.second.size());
for (const auto& e : s.second) {
result[s.first][e.first] = BufferDebugInfo(e.first, e.second.fPid, e.second.fSize, e.second.fCreationTime);
}
}
} else {
LOG(info) << "no debug data found";
}
} catch (bie&) {
LOG(info) << "no segments found";
@@ -455,18 +499,18 @@ unsigned long Monitor::GetFreeMemory(const ShmId& shmId, uint16_t segmentId)
using namespace boost::interprocess;
try {
bipc::managed_shared_memory managementSegment(bipc::open_only, std::string("fmq_" + shmId.shmId + "_mng").c_str());
boost::interprocess::named_mutex mtx(boost::interprocess::open_only, std::string("fmq_" + shmId.shmId + "_mtx").c_str());
boost::interprocess::scoped_lock<bipc::named_mutex> lock(mtx);
boost::interprocess::interprocess_mutex* mtx(managementSegment.find_or_construct<bipc::interprocess_mutex>(bipc::unique_instance)());
boost::interprocess::scoped_lock<bipc::interprocess_mutex> lock(*mtx);
Uint16SegmentInfoHashMap* segmentInfos = managementSegment.find<Uint16SegmentInfoHashMap>(unique_instance).first;
Uint16SegmentInfoHashMap* shmSegments = managementSegment.find<Uint16SegmentInfoHashMap>(unique_instance).first;
if (!segmentInfos) {
if (!shmSegments) {
LOG(error) << "Found management segment, but could not locate segment info";
throw MonitorError("Found management segment, but could not locate segment info");
}
auto it = segmentInfos->find(segmentId);
if (it != segmentInfos->end()) {
auto it = shmSegments->find(segmentId);
if (it != shmSegments->end()) {
if (it->second.fAllocationAlgorithm == AllocationAlgorithm::rbtree_best_fit) {
RBTreeBestFitSegment segment(open_read_only, std::string("fmq_" + shmId.shmId + "_m_" + std::to_string(segmentId)).c_str());
return segment.get_free_memory();
@@ -497,10 +541,16 @@ void Monitor::PrintHelp()
<< "[q] quit.";
}
bool Monitor::RemoveObject(const string& name) { return bipc::shared_memory_object::remove(name.c_str()); }
bool Monitor::RemoveFileMapping(const string& name) { return bipc::file_mapping::remove(name.c_str()); }
bool Monitor::RemoveQueue(const string& name) { return bipc::message_queue::remove(name.c_str()); }
bool Monitor::RemoveMutex(const string& name) { return bipc::named_mutex::remove(name.c_str()); }
bool Monitor::RemoveCondition(const string& name) { return bipc::named_condition::remove(name.c_str()); }
std::pair<std::string, bool> RunRemoval(std::function<bool(const std::string&)> f, std::string name, bool verbose)
template<typename T>
std::pair<std::string, bool> Remove(const std::string& name, bool verbose)
{
if (f(name)) {
if (T::remove(name.c_str())) {
if (verbose) {
LOG(info) << "Successfully removed '" << name << "'.";
}
@@ -513,69 +563,48 @@ std::pair<std::string, bool> RunRemoval(std::function<bool(const std::string&)>
}
}
bool Monitor::RemoveObject(const string& name) { return bipc::shared_memory_object::remove(name.c_str()); }
bool Monitor::RemoveFileMapping(const string& name) { return bipc::file_mapping::remove(name.c_str()); }
bool Monitor::RemoveQueue(const string& name) { return bipc::message_queue::remove(name.c_str()); }
bool Monitor::RemoveMutex(const string& name) { return bipc::named_mutex::remove(name.c_str()); }
bool Monitor::RemoveCondition(const string& name) { return bipc::named_condition::remove(name.c_str()); }
std::vector<std::pair<std::string, bool>> Monitor::Cleanup(const ShmId& shmId, bool verbose /* = true */)
std::vector<std::pair<std::string, bool>> Monitor::Cleanup(const ShmId& shmIdT, bool verbose /* = true */)
{
std::string shmId = shmIdT.shmId;
std::vector<std::pair<std::string, bool>> result;
if (verbose) {
LOG(info) << "Cleaning up for shared memory id '" << shmId.shmId << "'...";
LOG(info) << "Cleaning up for shared memory id '" << shmId << "'...";
}
string managementSegmentName("fmq_" + shmId.shmId + "_mng");
string managementSegmentName("fmq_" + shmId + "_mng");
try {
bipc::managed_shared_memory managementSegment(bipc::open_only, managementSegmentName.c_str());
try {
RegionCounter* rc = managementSegment.find<RegionCounter>(bipc::unique_instance).first;
if (rc) {
if (verbose) {
LOG(debug) << "Region counter found: " << rc->fCount;
}
uint16_t regionCount = rc->fCount;
Uint16RegionInfoMap* m = managementSegment.find<Uint16RegionInfoMap>(bipc::unique_instance).first;
for (uint16_t i = 1; i <= regionCount; ++i) {
if (m != nullptr) {
RegionInfo ri = m->at(i);
string path = ri.fPath.c_str();
int flags = ri.fFlags;
if (verbose) {
LOG(info) << "Found RegionInfo with path: '" << path << "', flags: " << flags << ", fDestroyed: " << ri.fDestroyed << ".";
}
if (!path.empty()) {
result.emplace_back(RunRemoval(Monitor::RemoveFileMapping, path + "fmq_" + shmId.shmId + "_rg_" + to_string(i), verbose));
} else {
result.emplace_back(RunRemoval(Monitor::RemoveObject, "fmq_" + shmId.shmId + "_rg_" + to_string(i), verbose));
}
} else {
result.emplace_back(RunRemoval(Monitor::RemoveObject, "fmq_" + shmId.shmId + "_rg_" + to_string(i), verbose));
}
result.emplace_back(RunRemoval(Monitor::RemoveQueue, string("fmq_" + shmId.shmId + "_rgq_" + to_string(i)), verbose));
}
} else {
if (verbose) {
LOG(info) << "No region counter found. No regions to cleanup.";
}
}
} catch(out_of_range& oor) {
Uint16RegionInfoHashMap* shmRegions = managementSegment.find<Uint16RegionInfoHashMap>(bipc::unique_instance).first;
if (shmRegions) {
if (verbose) {
LOG(info) << "Could not locate element in the region map, out of range: " << oor.what();
LOG(info) << "Found " << shmRegions->size() << " unmanaged regions...";
}
for (const auto& region : *shmRegions) {
uint16_t id = region.first;
RegionInfo info = region.second;
string path = info.fPath.c_str();
int flags = info.fCreationFlags;
if (verbose) {
LOG(info) << "Found RegionInfo with path: '" << path << "', flags: " << flags << ", fDestroyed: " << info.fDestroyed << ".";
}
if (!path.empty()) {
result.emplace_back(Remove<bipc::file_mapping>(path + "fmq_" + shmId + "_rg_" + to_string(id), verbose));
} else {
result.emplace_back(Remove<bipc::shared_memory_object>("fmq_" + shmId + "_rg_" + to_string(id), verbose));
}
result.emplace_back(Remove<bipc::message_queue>("fmq_" + shmId + "_rgq_" + to_string(id), verbose));
}
}
Uint16SegmentInfoHashMap* segmentInfos = managementSegment.find<Uint16SegmentInfoHashMap>(bipc::unique_instance).first;
if (segmentInfos) {
for (const auto& s : *segmentInfos) {
result.emplace_back(RunRemoval(Monitor::RemoveObject, "fmq_" + shmId.shmId + "_m_" + to_string(s.first), verbose));
Uint16SegmentInfoHashMap* shmSegments = managementSegment.find<Uint16SegmentInfoHashMap>(bipc::unique_instance).first;
if (shmSegments) {
if (verbose) {
LOG(info) << "Found " << shmSegments->size() << " managed segments...";
}
for (const auto& segment : *shmSegments) {
result.emplace_back(Remove<bipc::shared_memory_object>("fmq_" + shmId + "_m_" + to_string(segment.first), verbose));
}
} else {
if (verbose) {
@@ -583,16 +612,13 @@ std::vector<std::pair<std::string, bool>> Monitor::Cleanup(const ShmId& shmId, b
}
}
result.emplace_back(RunRemoval(Monitor::RemoveObject, managementSegmentName.c_str(), verbose));
result.emplace_back(Remove<bipc::shared_memory_object>(managementSegmentName, verbose));
} catch (bie&) {
if (verbose) {
LOG(info) << "Did not find '" << managementSegmentName << "' shared memory segment. No regions to cleanup.";
LOG(info) << "Did not find '" << managementSegmentName << "' management segment. No regions to cleanup.";
}
}
result.emplace_back(RunRemoval(Monitor::RemoveMutex, "fmq_" + shmId.shmId + "_mtx", verbose));
result.emplace_back(RunRemoval(Monitor::RemoveCondition, "fmq_" + shmId.shmId + "_cv", verbose));
return result;
}
@@ -608,7 +634,7 @@ std::vector<std::pair<std::string, bool>> Monitor::Cleanup(const SessionId& sess
std::vector<std::pair<std::string, bool>> Monitor::CleanupFull(const ShmId& shmId, bool verbose /* = true */)
{
auto result = Cleanup(shmId, verbose);
result.emplace_back(RunRemoval(Monitor::RemoveMutex, "fmq_" + shmId.shmId + "_ms", verbose));
result.emplace_back(Remove<bipc::named_mutex>("fmq_" + shmId.shmId + "_ms", verbose));
return result;
}
@@ -621,6 +647,107 @@ std::vector<std::pair<std::string, bool>> Monitor::CleanupFull(const SessionId&
return CleanupFull(shmId, verbose);
}
void Monitor::ResetContent(const ShmId& shmIdT, bool verbose /* = true */)
{
std::string shmId = shmIdT.shmId;
if (verbose) {
cout << "Resetting segments content for shared memory id '" << shmId << "'..." << endl;
}
string managementSegmentName("fmq_" + shmId + "_mng");
try {
using namespace boost::interprocess;
managed_shared_memory managementSegment(open_only, managementSegmentName.c_str());
Uint16SegmentInfoHashMap* segmentInfos = managementSegment.find<Uint16SegmentInfoHashMap>(unique_instance).first;
for (const auto& s : *segmentInfos) {
if (verbose) {
cout << "Resetting content of segment '" << "fmq_" << shmId << "_m_" << s.first << "'..." << endl;
}
try {
if (s.second.fAllocationAlgorithm == AllocationAlgorithm::rbtree_best_fit) {
RBTreeBestFitSegment segment(open_only, std::string("fmq_" + shmId + "_m_" + to_string(s.first)).c_str());
void* ptr = segment.get_segment_manager();
size_t size = segment.get_segment_manager()->get_size();
new(ptr) segment_manager<char, rbtree_best_fit<mutex_family, offset_ptr<void>>, null_index>(size);
} else {
SimpleSeqFitSegment segment(open_only, std::string("fmq_" + shmId + "_m_" + to_string(s.first)).c_str());
void* ptr = segment.get_segment_manager();
size_t size = segment.get_segment_manager()->get_size();
new(ptr) segment_manager<char, simple_seq_fit<mutex_family, offset_ptr<void>>, null_index>(size);
}
} catch (bie& e) {
if (verbose) {
cout << "Error resetting content of segment '" << std::string("fmq_" + shmId + "_m_" + to_string(s.first)) << "': " << e.what() << endl;
}
}
}
Uint16RegionInfoHashMap* shmRegions = managementSegment.find<Uint16RegionInfoHashMap>(bipc::unique_instance).first;
if (shmRegions) {
for (const auto& region : *shmRegions) {
uint16_t id = region.first;
Remove<bipc::message_queue>("fmq_" + shmId + "_rgq_" + to_string(id), verbose);
}
}
} catch (bie& e) {
if (verbose) {
cout << "Could not find '" << managementSegmentName << "' segment. Nothing to cleanup." << endl;
cout << e.what() << endl;
}
}
if (verbose) {
cout << "Done resetting segment content for shared memory id '" << shmId << "'." << endl;
}
}
void Monitor::ResetContent(const SessionId& sessionId, bool verbose /* = true */)
{
ShmId shmId{makeShmIdStr(sessionId.sessionId)};
if (verbose) {
cout << "ResetContent called with session id '" << sessionId.sessionId << "', translating to shared memory id '" << shmId.shmId << "'" << endl;
}
ResetContent(shmId, verbose);
}
void Monitor::ResetContent(const ShmId& shmIdT, const std::vector<SegmentConfig>& segmentCfgs, const std::vector<RegionConfig>& regionCfgs, bool verbose /* = true */)
{
using namespace boost::interprocess;
std::string shmId = shmIdT.shmId;
std::string managementSegmentName("fmq_" + shmId + "_mng");
// reset managed segments
ResetContent(shmIdT, verbose);
// delete management segment
Remove<bipc::shared_memory_object>(managementSegmentName, verbose);
// recreate management segment
managed_shared_memory mngSegment(create_only, managementSegmentName.c_str(), kManagementSegmentSize);
// fill management segment with segment & region infos
for (const auto& s : segmentCfgs) {
if (s.allocationAlgorithm == "rbtree_best_fit") {
Segment::Register(shmId, s.id, AllocationAlgorithm::rbtree_best_fit);
} else if (s.allocationAlgorithm == "simple_seq_fit") {
Segment::Register(shmId, s.id, AllocationAlgorithm::simple_seq_fit);
} else {
LOG(error) << "Unknown allocation algorithm provided: " << s.allocationAlgorithm;
throw MonitorError("Unknown allocation algorithm provided: " + s.allocationAlgorithm);
}
}
for (const auto& r : regionCfgs) {
fair::mq::shmem::UnmanagedRegion::Register(shmId, r);
}
}
void Monitor::ResetContent(const SessionId& sessionId, const std::vector<SegmentConfig>& segmentCfgs, const std::vector<RegionConfig>& regionCfgs, bool verbose /* = true */)
{
ShmId shmId{makeShmIdStr(sessionId.sessionId)};
if (verbose) {
cout << "ResetContent called with session id '" << sessionId.sessionId << "', translating to shared memory id '" << shmId.shmId << "'" << endl;
}
ResetContent(shmId, segmentCfgs, regionCfgs, verbose);
}
Monitor::~Monitor()
{
if (fSignalThread.joinable()) {

View File

@@ -8,6 +8,8 @@
#ifndef FAIR_MQ_SHMEM_MONITOR_H_
#define FAIR_MQ_SHMEM_MONITOR_H_
#include <fairmq/UnmanagedRegion.h>
#include <fairlogger/Logger.h>
#include <thread>
@@ -49,6 +51,13 @@ struct BufferDebugInfo
uint64_t fCreationTime;
};
struct SegmentConfig
{
uint16_t id;
uint64_t size;
std::string allocationAlgorithm;
};
class Monitor
{
public:
@@ -80,6 +89,23 @@ class Monitor
/// @param verbose output cleanup results to stdout
static std::vector<std::pair<std::string, bool>> CleanupFull(const SessionId& sessionId, bool verbose = true);
/// @brief [EXPERIMENTAL] cleanup the content of the shem segment, without recreating it
/// @param shmId shared memory id
/// Only call this when segment is not in use
static void ResetContent(const ShmId& shmId, bool verbose = true);
/// @brief [EXPERIMENTAL] cleanup the content of the shem segment, without recreating it
/// @param sessionId session id
/// Only call this when segment is not in use
static void ResetContent(const SessionId& sessionId, bool verbose = true);
/// @brief [EXPERIMENTAL] cleanup the content of the shem segment, without recreating it
/// @param shmId shared memory id
/// Only call this when segment is not in use
static void ResetContent(const ShmId& shmId, const std::vector<SegmentConfig>& segmentCfgs, const std::vector<RegionConfig>& regionCfgs, bool verbose = true);
/// @brief [EXPERIMENTAL] cleanup the content of the shem segment, without recreating it
/// @param sessionId session id
/// Only call this when segment is not in use
static void ResetContent(const SessionId& sessionId, const std::vector<SegmentConfig>& segmentCfgs, const std::vector<RegionConfig>& regionCfgs, bool verbose = true);
/// @brief Outputs list of messages in shmem (if compiled with FAIRMQ_DEBUG_MODE=ON)
/// @param shmId shmem id
static void PrintDebugInfo(const ShmId& shmId);

View File

@@ -14,8 +14,6 @@ FairMQ Shared Memory currently uses the following names to register shared memor
| --------------------------- | ---------------------------------------------- | ------------------ | ------------------------------ |
| `fmq_<shmId>_m_<segmentId>` | managed segment(s) (user data) | one of the devices | devices |
| `fmq_<shmId>_mng` | management segment (management data) | one of the devices | devices |
| `fmq_<shmId>_mtx` | mutex | one of the devices | devices |
| `fmq_<shmId>_cv` | condition variable | one of the devices | devices |
| `fmq_<shmId>_rg_<index>` | unmanaged region(s) | one of the devices | devices with unmanaged regions |
| `fmq_<shmId>_rgq_<index>` | unmanaged region queue(s) | one of the devices | devices with unmanaged regions |
| `fmq_<shmId>_ms` | shmmonitor status | shmmonitor | devices, shmmonitor |

View File

@@ -1,305 +0,0 @@
/********************************************************************************
* Copyright (C) 2014-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
* copied verbatim in the file "LICENSE" *
********************************************************************************/
#ifndef FAIR_MQ_SHMEM_REGION_H_
#define FAIR_MQ_SHMEM_REGION_H_
#include "Common.h"
#include <FairMQLogger.h>
#include <FairMQUnmanagedRegion.h>
#include <fairmq/tools/Strings.h>
#include <boost/filesystem.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/file_mapping.hpp>
#include <boost/interprocess/ipc/message_queue.hpp>
#include <algorithm> // min
#include <atomic>
#include <thread>
#include <memory> // make_unique
#include <mutex>
#include <condition_variable>
#include <unordered_map>
#include <cerrno>
#include <chrono>
#include <ios>
#include <utility> // move
namespace fair::mq::shmem
{
struct Region
{
Region(const std::string& shmId, uint16_t id, uint64_t size, bool remote, RegionCallback callback, RegionBulkCallback bulkCallback, const std::string& path, int flags)
: fRemote(remote)
, fLinger(100)
, fStopAcks(false)
, fName("fmq_" + shmId + "_rg_" + std::to_string(id))
, fQueueName("fmq_" + shmId + "_rgq_" + std::to_string(id))
, fShmemObject()
, fFile(nullptr)
, fFileMapping()
, fQueue(nullptr)
, fCallback(std::move(callback))
, fBulkCallback(std::move(bulkCallback))
{
using namespace boost::interprocess;
if (!path.empty()) {
fName = std::string(path + fName);
if (!fRemote) {
// create a file
std::filebuf fbuf;
if (fbuf.open(fName, std::ios_base::in | std::ios_base::out | std::ios_base::trunc | std::ios_base::binary)) {
// set the size
fbuf.pubseekoff(size - 1, std::ios_base::beg);
fbuf.sputc(0);
}
}
fFile = fopen(fName.c_str(), "r+");
if (!fFile) {
LOG(error) << "Failed to initialize file: " << fName;
LOG(error) << "errno: " << errno << ": " << strerror(errno);
throw std::runtime_error(tools::ToString("Failed to initialize file for shared memory region: ", strerror(errno)));
}
fFileMapping = file_mapping(fName.c_str(), read_write);
LOG(debug) << "shmem: initialized file: " << fName;
fRegion = mapped_region(fFileMapping, read_write, 0, size, 0, flags);
} else {
try {
if (fRemote) {
fShmemObject = shared_memory_object(open_only, fName.c_str(), read_write);
} else {
fShmemObject = shared_memory_object(create_only, fName.c_str(), read_write);
fShmemObject.truncate(size);
}
} catch(interprocess_exception& e) {
LOG(error) << "Failed " << (fRemote ? "opening" : "creating") << " shared_memory_object for region id '" << id << "': " << e.what();
throw;
}
try {
fRegion = mapped_region(fShmemObject, read_write, 0, 0, 0, flags);
} catch(interprocess_exception& e) {
LOG(error) << "Failed mapping shared_memory_object for region id '" << id << "': " << e.what();
throw;
}
}
InitializeQueues();
StartSendingAcks();
LOG(trace) << "shmem: initialized region: " << fName << " (" << (fRemote ? "remote" : "local") << ")";
}
Region() = delete;
Region(const Region&) = delete;
Region(Region&&) = delete;
Region& operator=(const Region&) = delete;
Region& operator=(Region&&) = delete;
void InitializeQueues()
{
using namespace boost::interprocess;
if (fRemote) {
fQueue = std::make_unique<message_queue>(open_only, fQueueName.c_str());
} else {
fQueue = std::make_unique<message_queue>(create_only, fQueueName.c_str(), 1024, fAckBunchSize * sizeof(RegionBlock));
}
LOG(trace) << "shmem: initialized region queue: " << fQueueName << " (" << (fRemote ? "remote" : "local") << ")";
}
void StartSendingAcks()
{
fAcksSender = std::thread(&Region::SendAcks, this);
}
void SendAcks()
{
std::unique_ptr<RegionBlock[]> blocks = std::make_unique<RegionBlock[]>(fAckBunchSize);
size_t blocksToSend = 0;
while (true) {
{
std::unique_lock<std::mutex> lock(fBlockMtx);
// try to get <fAckBunchSize> blocks
if (fBlocksToFree.size() < fAckBunchSize) {
fBlockSendCV.wait_for(lock, std::chrono::milliseconds(500));
}
// send whatever blocks we have
blocksToSend = std::min(fBlocksToFree.size(), fAckBunchSize);
copy_n(fBlocksToFree.end() - blocksToSend, blocksToSend, blocks.get());
fBlocksToFree.resize(fBlocksToFree.size() - blocksToSend);
}
if (blocksToSend > 0) {
while (!fQueue->try_send(blocks.get(), blocksToSend * sizeof(RegionBlock), 0) && !fStopAcks) {
// receiver slow? yield and try again...
std::this_thread::yield();
}
// LOG(debug) << "Sent " << blocksToSend << " blocks.";
} else { // blocksToSend == 0
if (fStopAcks) {
break;
}
}
}
LOG(trace) << "AcksSender for " << fName << " leaving " << "(blocks left to free: " << fBlocksToFree.size() << ", "
<< " blocks left to send: " << blocksToSend << ").";
}
void StartReceivingAcks()
{
if (!fAcksReceiver.joinable()) {
fAcksReceiver = std::thread(&Region::ReceiveAcks, this);
}
}
void ReceiveAcks()
{
unsigned int priority = 0;
boost::interprocess::message_queue::size_type recvdSize = 0;
std::unique_ptr<RegionBlock[]> blocks = std::make_unique<RegionBlock[]>(fAckBunchSize);
std::vector<fair::mq::RegionBlock> result;
result.reserve(fAckBunchSize);
while (true) {
uint32_t timeout = 100;
bool leave = false;
if (fStopAcks) {
timeout = fLinger;
leave = true;
}
auto rcvTill = boost::posix_time::microsec_clock::universal_time() + boost::posix_time::milliseconds(timeout);
while (fQueue->timed_receive(blocks.get(), fAckBunchSize * sizeof(RegionBlock), recvdSize, priority, rcvTill)) {
const auto numBlocks = recvdSize / sizeof(RegionBlock);
// LOG(debug) << "Received " << numBlocks << " blocks (recvdSize: " << recvdSize << "). (remaining queue size: " << fQueue->get_num_msg() << ").";
if (fBulkCallback) {
result.clear();
for (size_t i = 0; i < numBlocks; i++) {
result.emplace_back(reinterpret_cast<char*>(fRegion.get_address()) + blocks[i].fHandle, blocks[i].fSize, reinterpret_cast<void*>(blocks[i].fHint));
}
fBulkCallback(result);
} else if (fCallback) {
for (size_t i = 0; i < numBlocks; i++) {
fCallback(reinterpret_cast<char*>(fRegion.get_address()) + blocks[i].fHandle, blocks[i].fSize, reinterpret_cast<void*>(blocks[i].fHint));
}
}
}
if (leave) {
break;
}
}
LOG(trace) << "AcksReceiver for " << fName << " leaving (remaining queue size: " << fQueue->get_num_msg() << ").";
}
void ReleaseBlock(const RegionBlock& block)
{
std::unique_lock<std::mutex> lock(fBlockMtx);
fBlocksToFree.emplace_back(block);
if (fBlocksToFree.size() >= fAckBunchSize) {
lock.unlock();
fBlockSendCV.notify_one();
}
}
void SetLinger(uint32_t linger) { fLinger = linger; }
uint32_t GetLinger() const { return fLinger; }
void StopAcks()
{
fStopAcks = true;
if (fAcksSender.joinable()) {
fBlockSendCV.notify_one();
fAcksSender.join();
}
if (!fRemote) {
if (fAcksReceiver.joinable()) {
fAcksReceiver.join();
}
}
}
~Region()
{
fStopAcks = true;
if (fAcksSender.joinable()) {
fBlockSendCV.notify_one();
fAcksSender.join();
}
if (!fRemote) {
if (fAcksReceiver.joinable()) {
fAcksReceiver.join();
}
if (boost::interprocess::shared_memory_object::remove(fName.c_str())) {
LOG(trace) << "Region '" << fName << "' destroyed.";
}
if (boost::interprocess::file_mapping::remove(fName.c_str())) {
LOG(trace) << "File mapping '" << fName << "' destroyed.";
}
if (fFile) {
fclose(fFile);
}
if (boost::interprocess::message_queue::remove(fQueueName.c_str())) {
LOG(trace) << "Region queue '" << fQueueName << "' destroyed.";
}
} else {
// LOG(debug) << "Region queue '" << fQueueName << "' is remote, no cleanup necessary";
}
// LOG(debug) << "Region '" << fName << "' (" << (fRemote ? "remote" : "local") << ") destructed.";
}
bool fRemote;
uint32_t fLinger;
std::atomic<bool> fStopAcks;
std::string fName;
std::string fQueueName;
boost::interprocess::shared_memory_object fShmemObject;
FILE* fFile;
boost::interprocess::file_mapping fFileMapping;
boost::interprocess::mapped_region fRegion;
std::mutex fBlockMtx;
std::condition_variable fBlockSendCV;
std::vector<RegionBlock> fBlocksToFree;
const std::size_t fAckBunchSize = 256;
std::unique_ptr<boost::interprocess::message_queue> fQueue;
std::thread fAcksReceiver;
std::thread fAcksSender;
RegionCallback fCallback;
RegionBulkCallback fBulkCallback;
};
} // namespace fair::mq::shmem
#endif /* FAIR_MQ_SHMEM_REGION_H_ */

87
fairmq/shmem/Segment.h Normal file
View File

@@ -0,0 +1,87 @@
/********************************************************************************
* Copyright (C) 2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
* copied verbatim in the file "LICENSE" *
********************************************************************************/
#ifndef FAIR_MQ_SHMEM_SEGMENT_H_
#define FAIR_MQ_SHMEM_SEGMENT_H_
#include <fairmq/shmem/Common.h>
#include <fairmq/shmem/Monitor.h>
#include <boost/variant.hpp>
#include <cstdint>
#include <string>
namespace fair::mq::shmem
{
struct SimpleSeqFit {};
struct RBTreeBestFit {};
static const SimpleSeqFit simpleSeqFit = SimpleSeqFit();
static const RBTreeBestFit rbTreeBestFit = RBTreeBestFit();
struct Segment
{
friend class Monitor;
Segment(const std::string& shmId, uint16_t id, size_t size, SimpleSeqFit)
: fSegment(SimpleSeqFitSegment(boost::interprocess::open_or_create,
std::string("fmq_" + shmId + "_m_" + std::to_string(id)).c_str(),
size))
{
Register(shmId, id, AllocationAlgorithm::simple_seq_fit);
}
Segment(const std::string& shmId, uint16_t id, size_t size, RBTreeBestFit)
: fSegment(RBTreeBestFitSegment(boost::interprocess::open_or_create,
std::string("fmq_" + shmId + "_m_" + std::to_string(id)).c_str(),
size))
{
Register(shmId, id, AllocationAlgorithm::rbtree_best_fit);
}
size_t GetSize() const { return boost::apply_visitor(SegmentSize(), fSegment); }
void* GetData() { return boost::apply_visitor(SegmentAddress(), fSegment); }
size_t GetFreeMemory() const { return boost::apply_visitor(SegmentFreeMemory(), fSegment); }
void Zero() { boost::apply_visitor(SegmentMemoryZeroer(), fSegment); }
void Lock()
{
if (mlock(GetData(), GetSize()) == -1) {
throw TransportError(tools::ToString("Could not lock the managed segment memory: ", strerror(errno)));
}
}
static void Remove(const std::string& shmId, uint16_t id)
{
Monitor::RemoveObject("fmq_" + shmId + "_m_" + std::to_string(id));
}
private:
boost::variant<RBTreeBestFitSegment, SimpleSeqFitSegment> fSegment;
static void Register(const std::string& shmId, uint16_t id, AllocationAlgorithm allocAlgo)
{
using namespace boost::interprocess;
managed_shared_memory mngSegment(open_or_create, std::string("fmq_" + shmId + "_mng").c_str(), kManagementSegmentSize);
VoidAlloc alloc(mngSegment.get_segment_manager());
Uint16SegmentInfoHashMap* shmSegments = mngSegment.find_or_construct<Uint16SegmentInfoHashMap>(unique_instance)(alloc);
EventCounter* eventCounter = mngSegment.find_or_construct<EventCounter>(unique_instance)(0);
bool newSegmentRegistered = shmSegments->emplace(id, allocAlgo).second;
if (newSegmentRegistered) {
(eventCounter->fCount)++;
}
}
};
} // namespace fair::mq::shmem
#endif /* FAIR_MQ_SHMEM_SEGMENT_H_ */

View File

@@ -11,13 +11,13 @@
#include "Common.h"
#include "Manager.h"
#include "Message.h"
#include <FairMQSocket.h>
#include <FairMQMessage.h>
#include <FairMQLogger.h>
#include <fairmq/Socket.h>
#include <fairmq/Message.h>
#include <fairmq/tools/Strings.h>
#include <fairmq/zeromq/Common.h>
#include <fairlogger/Logger.h>
#include <zmq.h>
#include <atomic>
@@ -51,7 +51,7 @@ struct ZMsg
class Socket final : public fair::mq::Socket
{
public:
Socket(Manager& manager, const std::string& type, const std::string& name, const std::string& id, void* context, FairMQTransportFactory* fac = nullptr)
Socket(Manager& manager, const std::string& type, const std::string& name, const std::string& id, void* context, fair::mq::TransportFactory* fac = nullptr)
: fair::mq::Socket(fac)
, fManager(manager)
, fId(id + "." + name + "." + type)
@@ -117,52 +117,12 @@ class Socket final : public fair::mq::Socket
bool Bind(const std::string& address) override
{
// LOG(info) << "binding socket " << fId << " on " << address;
if (zmq_bind(fSocket, address.c_str()) != 0) {
if (errno == EADDRINUSE) {
// do not print error in this case, this is handled by FairMQDevice in case no connection could be established after trying a number of random ports from a range.
return false;
}
LOG(error) << "Failed binding socket " << fId << ", reason: " << zmq_strerror(errno);
return false;
}
return true;
return zmq::Bind(fSocket, address, fId);
}
bool Connect(const std::string& address) override
{
// LOG(info) << "connecting socket " << fId << " on " << address;
if (zmq_connect(fSocket, address.c_str()) != 0) {
LOG(error) << "Failed connecting socket " << fId << ", reason: " << zmq_strerror(errno);
return false;
}
return true;
}
bool ShouldRetry(int flags, int timeout, int& elapsed) const
{
if ((flags & ZMQ_DONTWAIT) == 0) {
if (timeout > 0) {
elapsed += fTimeout;
if (elapsed >= timeout) {
return false;
}
}
return true;
} else {
return false;
}
}
int HandleErrors() const
{
if (zmq_errno() == ETERM) {
LOG(debug) << "Terminating socket " << fId;
return static_cast<int>(TransferCode::error);
} else {
LOG(error) << "Failed transfer on socket " << fId << ", reason: " << zmq_strerror(errno);
return static_cast<int>(TransferCode::error);
}
return zmq::Connect(fSocket, address, fId);
}
int64_t Send(MessagePtr& msg, int timeout = -1) override
@@ -186,13 +146,13 @@ class Socket final : public fair::mq::Socket
} else if (zmq_errno() == EAGAIN || zmq_errno() == EINTR) {
if (fManager.Interrupted()) {
return static_cast<int>(TransferCode::interrupted);
} else if (ShouldRetry(flags, timeout, elapsed)) {
} else if (zmq::ShouldRetry(flags, fTimeout, timeout, elapsed)) {
continue;
} else {
return static_cast<int>(TransferCode::timeout);
}
} else {
return HandleErrors();
return zmq::HandleErrors(fId);
}
}
@@ -226,13 +186,13 @@ class Socket final : public fair::mq::Socket
} else if (zmq_errno() == EAGAIN || zmq_errno() == EINTR) {
if (fManager.Interrupted()) {
return static_cast<int>(TransferCode::interrupted);
} else if (ShouldRetry(flags, timeout, elapsed)) {
} else if (zmq::ShouldRetry(flags, fTimeout, timeout, elapsed)) {
continue;
} else {
return static_cast<int>(TransferCode::timeout);
}
} else {
return HandleErrors();
return zmq::HandleErrors(fId);
}
}
}
@@ -277,13 +237,13 @@ class Socket final : public fair::mq::Socket
} else if (zmq_errno() == EAGAIN || zmq_errno() == EINTR) {
if (fManager.Interrupted()) {
return static_cast<int>(TransferCode::interrupted);
} else if (ShouldRetry(flags, timeout, elapsed)) {
} else if (zmq::ShouldRetry(flags, fTimeout, timeout, elapsed)) {
continue;
} else {
return static_cast<int>(TransferCode::timeout);
}
} else {
return HandleErrors();
return zmq::HandleErrors(fId);
}
}
@@ -333,13 +293,13 @@ class Socket final : public fair::mq::Socket
} else if (zmq_errno() == EAGAIN || zmq_errno() == EINTR) {
if (fManager.Interrupted()) {
return static_cast<int>(TransferCode::interrupted);
} else if (ShouldRetry(flags, timeout, elapsed)) {
} else if (zmq::ShouldRetry(flags, fTimeout, timeout, elapsed)) {
continue;
} else {
return static_cast<int>(TransferCode::timeout);
}
} else {
return HandleErrors();
return zmq::HandleErrors(fId);
}
}

View File

@@ -9,17 +9,17 @@
#ifndef FAIR_MQ_SHMEM_TRANSPORTFACTORY_H_
#define FAIR_MQ_SHMEM_TRANSPORTFACTORY_H_
#include "Manager.h"
#include "Common.h"
#include "Manager.h"
#include "Message.h"
#include "Socket.h"
#include "Poller.h"
#include "UnmanagedRegion.h"
#include <FairMQTransportFactory.h>
#include "Socket.h"
#include "UnmanagedRegionImpl.h"
#include <fairmq/ProgOptions.h>
#include <FairMQLogger.h>
#include <fairmq/tools/Strings.h>
#include <fairmq/TransportFactory.h>
#include <fairlogger/Logger.h>
#include <boost/version.hpp>
@@ -78,7 +78,7 @@ class TransportFactory final : public fair::mq::TransportFactory
LOG(error) << "failed configuring context, reason: " << zmq_strerror(errno);
}
fManager = std::make_unique<Manager>(sessionName, deviceId, segmentSize, config);
fManager = std::make_unique<Manager>(sessionName, segmentSize, config);
} catch (boost::interprocess::interprocess_exception& e) {
LOG(error) << "Could not initialize shared memory transport: " << e.what();
throw std::runtime_error(tools::ToString("Could not initialize shared memory transport: ", e.what()));
@@ -113,7 +113,7 @@ class TransportFactory final : public fair::mq::TransportFactory
return std::make_unique<Message>(*fManager, size, alignment, this);
}
MessagePtr CreateMessage(void* data, size_t size, fairmq_free_fn* ffn, void* hint = nullptr) override
MessagePtr CreateMessage(void* data, size_t size, fair::mq::FreeFn* ffn, void* hint = nullptr) override
{
return std::make_unique<Message>(*fManager, data, size, ffn, hint, this);
}
@@ -128,44 +128,63 @@ class TransportFactory final : public fair::mq::TransportFactory
return std::make_unique<Socket>(*fManager, type, name, GetId(), fZmqCtx, this);
}
PollerPtr CreatePoller(const std::vector<FairMQChannel>& channels) const override
PollerPtr CreatePoller(const std::vector<Channel>& channels) const override
{
return std::make_unique<Poller>(channels);
}
PollerPtr CreatePoller(const std::vector<FairMQChannel*>& channels) const override
PollerPtr CreatePoller(const std::vector<Channel*>& channels) const override
{
return std::make_unique<Poller>(channels);
}
PollerPtr CreatePoller(const std::unordered_map<std::string, std::vector<FairMQChannel>>& channelsMap, const std::vector<std::string>& channelList) const override
PollerPtr CreatePoller(const std::unordered_map<std::string, std::vector<Channel>>& channelsMap, const std::vector<std::string>& channelList) const override
{
return std::make_unique<Poller>(channelsMap, channelList);
}
UnmanagedRegionPtr CreateUnmanagedRegion(size_t size, RegionCallback callback = nullptr, const std::string& path = "", int flags = 0, fair::mq::RegionConfig cfg = fair::mq::RegionConfig()) override
{
return CreateUnmanagedRegion(size, 0, callback, nullptr, path, flags, cfg);
cfg.path = path;
cfg.creationFlags = flags;
return CreateUnmanagedRegion(size, callback, nullptr, std::move(cfg));
}
UnmanagedRegionPtr CreateUnmanagedRegion(size_t size, RegionBulkCallback bulkCallback = nullptr, const std::string& path = "", int flags = 0, fair::mq::RegionConfig cfg = fair::mq::RegionConfig()) override
{
return CreateUnmanagedRegion(size, 0, nullptr, bulkCallback, path, flags, cfg);
cfg.path = path;
cfg.creationFlags = flags;
return CreateUnmanagedRegion(size, nullptr, bulkCallback, std::move(cfg));
}
UnmanagedRegionPtr CreateUnmanagedRegion(size_t size, int64_t userFlags, RegionCallback callback = nullptr, const std::string& path = "", int flags = 0, fair::mq::RegionConfig cfg = fair::mq::RegionConfig()) override
{
return CreateUnmanagedRegion(size, userFlags, callback, nullptr, path, flags, cfg);
cfg.path = path;
cfg.userFlags = userFlags;
cfg.creationFlags = flags;
return CreateUnmanagedRegion(size, callback, nullptr, std::move(cfg));
}
UnmanagedRegionPtr CreateUnmanagedRegion(size_t size, int64_t userFlags, RegionBulkCallback bulkCallback = nullptr, const std::string& path = "", int flags = 0, fair::mq::RegionConfig cfg = fair::mq::RegionConfig()) override
{
return CreateUnmanagedRegion(size, userFlags, nullptr, bulkCallback, path, flags, cfg);
cfg.path = path;
cfg.userFlags = userFlags;
cfg.creationFlags = flags;
return CreateUnmanagedRegion(size, nullptr, bulkCallback, std::move(cfg));
}
UnmanagedRegionPtr CreateUnmanagedRegion(size_t size, int64_t userFlags, RegionCallback callback, RegionBulkCallback bulkCallback, const std::string& path, int flags, fair::mq::RegionConfig cfg)
UnmanagedRegionPtr CreateUnmanagedRegion(size_t size, RegionCallback callback, RegionConfig cfg) override
{
return std::make_unique<UnmanagedRegion>(*fManager, size, userFlags, callback, bulkCallback, path, flags, this, cfg);
return CreateUnmanagedRegion(size, callback, nullptr, std::move(cfg));
}
UnmanagedRegionPtr CreateUnmanagedRegion(size_t size, RegionBulkCallback bulkCallback, RegionConfig cfg) override
{
return CreateUnmanagedRegion(size, nullptr, bulkCallback, std::move(cfg));
}
UnmanagedRegionPtr CreateUnmanagedRegion(size_t size, RegionCallback callback, RegionBulkCallback bulkCallback, fair::mq::RegionConfig cfg)
{
return std::make_unique<UnmanagedRegionImpl>(*fManager, size, callback, bulkCallback, std::move(cfg), this);
}
void SubscribeToRegionEvents(RegionEventCallback callback) override { fManager->SubscribeToRegionEvents(callback); }

View File

@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (C) 2014 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* Copyright (C) 2014-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
@@ -9,67 +9,375 @@
#ifndef FAIR_MQ_SHMEM_UNMANAGEDREGION_H_
#define FAIR_MQ_SHMEM_UNMANAGEDREGION_H_
#include "Manager.h"
#include <fairmq/shmem/Common.h>
#include <fairmq/shmem/Monitor.h>
#include <fairmq/tools/Strings.h>
#include <fairmq/UnmanagedRegion.h>
#include <FairMQUnmanagedRegion.h>
#include <FairMQLogger.h>
#include <fairlogger/Logger.h>
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <boost/filesystem.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/file_mapping.hpp>
#include <boost/interprocess/ipc/message_queue.hpp>
#include <cstddef> // size_t
#include <string>
#include <algorithm> // min
#include <atomic>
#include <thread>
#include <memory> // make_unique
#include <mutex>
#include <condition_variable>
#include <unordered_map>
#include <cerrno>
#include <chrono>
#include <ios>
#include <utility> // move
namespace fair::mq::shmem
{
class Message;
class Socket;
class UnmanagedRegion final : public fair::mq::UnmanagedRegion
struct UnmanagedRegion
{
friend class Message;
friend class Socket;
friend class Manager;
friend class Monitor;
public:
UnmanagedRegion(Manager& manager,
const size_t size,
const int64_t userFlags,
RegionCallback callback,
RegionBulkCallback bulkCallback,
const std::string& path,
int flags,
FairMQTransportFactory* factory,
fair::mq::RegionConfig cfg)
: FairMQUnmanagedRegion(factory)
, fManager(manager)
, fRegion(nullptr)
, fRegionId(0)
UnmanagedRegion(const std::string& shmId, uint16_t id, uint64_t size)
: UnmanagedRegion(shmId, size, false, makeRegionConfig(id))
{}
UnmanagedRegion(const std::string& shmId, uint64_t size, RegionConfig cfg)
: UnmanagedRegion(shmId, size, false, std::move(cfg))
{}
UnmanagedRegion(const std::string& shmId, RegionConfig cfg)
: UnmanagedRegion(shmId, cfg.size, false, std::move(cfg))
{}
UnmanagedRegion(const std::string& shmId, uint64_t size, bool remote, RegionConfig cfg)
: fRemote(remote)
, fRemoveOnDestruction(cfg.removeOnDestruction)
, fLinger(cfg.linger)
, fStopAcks(false)
, fName("fmq_" + shmId + "_rg_" + std::to_string(cfg.id.value()))
, fQueueName("fmq_" + shmId + "_rgq_" + std::to_string(cfg.id.value()))
, fShmemObject()
, fFile(nullptr)
, fFileMapping()
, fQueue(nullptr)
, fCallback(nullptr)
, fBulkCallback(nullptr)
{
auto result = fManager.CreateRegion(size, userFlags, callback, bulkCallback, path, flags, cfg);
fRegion = result.first;
fRegionId = result.second;
using namespace boost::interprocess;
// TODO: refactor this
cfg.size = size;
if (!cfg.path.empty()) {
fName = std::string(cfg.path + fName);
if (!fRemote) {
// create a file
std::filebuf fbuf;
if (fbuf.open(fName, std::ios_base::in | std::ios_base::out | std::ios_base::trunc | std::ios_base::binary)) {
// set the size
fbuf.pubseekoff(size - 1, std::ios_base::beg);
fbuf.sputc(0);
}
}
fFile = fopen(fName.c_str(), "r+");
if (!fFile) {
LOG(error) << "Failed to initialize file: " << fName;
LOG(error) << "errno: " << errno << ": " << strerror(errno);
throw std::runtime_error(tools::ToString("Failed to initialize file for shared memory region: ", strerror(errno)));
}
fFileMapping = file_mapping(fName.c_str(), read_write);
LOG(debug) << "shmem: initialized file: " << fName;
fRegion = mapped_region(fFileMapping, read_write, 0, size, 0, cfg.creationFlags);
} else {
try {
fShmemObject = shared_memory_object(open_or_create, fName.c_str(), read_write);
if (size != 0) {
fShmemObject.truncate(size);
}
} catch (interprocess_exception& e) {
LOG(error) << "Failed " << (remote ? "opening" : "creating") << " shared_memory_object for region id '" << cfg.id.value() << "': " << e.what();
throw;
}
try {
fRegion = mapped_region(fShmemObject, read_write, 0, 0, 0, cfg.creationFlags);
} catch (interprocess_exception& e) {
LOG(error) << "Failed mapping shared_memory_object for region id '" << cfg.id.value() << "': " << e.what();
throw;
}
}
if (cfg.lock) {
LOG(debug) << "Locking region " << cfg.id.value() << "...";
Lock();
LOG(debug) << "Successfully locked region " << cfg.id.value() << ".";
}
if (cfg.zero) {
LOG(debug) << "Zeroing free memory of region " << cfg.id.value() << "...";
Zero();
LOG(debug) << "Successfully zeroed free memory of region " << cfg.id.value() << ".";
}
if (!remote) {
Register(shmId, cfg);
}
LOG(trace) << "shmem: initialized region: " << fName << " (" << (remote ? "remote" : "local") << ")";
}
UnmanagedRegion() = delete;
UnmanagedRegion(const UnmanagedRegion&) = delete;
UnmanagedRegion(UnmanagedRegion&&) = delete;
UnmanagedRegion& operator=(const UnmanagedRegion&) = delete;
UnmanagedRegion& operator=(UnmanagedRegion&&) = delete;
void* GetData() const override { return fRegion->get_address(); }
size_t GetSize() const override { return fRegion->get_size(); }
uint16_t GetId() const override { return fRegionId; }
void SetLinger(uint32_t linger) override { fManager.GetRegion(fRegionId)->SetLinger(linger); }
uint32_t GetLinger() const override { return fManager.GetRegion(fRegionId)->GetLinger(); }
void Zero()
{
memset(fRegion.get_address(), 0x00, fRegion.get_size());
}
void Lock()
{
if (mlock(fRegion.get_address(), fRegion.get_size()) == -1) {
LOG(error) << "Could not lock region " << fName << ". Code: " << errno << ", reason: " << strerror(errno);
throw TransportError(tools::ToString("Could not lock region ", fName, ": ", strerror(errno)));
}
}
Transport GetType() const override { return fair::mq::Transport::SHM; }
void* GetData() const { return fRegion.get_address(); }
size_t GetSize() const { return fRegion.get_size(); }
~UnmanagedRegion() override { fManager.RemoveRegion(fRegionId); }
void SetLinger(uint32_t linger) { fLinger = linger; }
uint32_t GetLinger() const { return fLinger; }
bool RemoveOnDestruction() { return fRemoveOnDestruction; }
~UnmanagedRegion()
{
fStopAcks = true;
if (fAcksSender.joinable()) {
fBlockSendCV.notify_one();
fAcksSender.join();
}
if (!fRemote) {
if (fAcksReceiver.joinable()) {
fAcksReceiver.join();
}
if (fRemoveOnDestruction) {
if (Monitor::RemoveObject(fName.c_str())) {
LOG(trace) << "Region '" << fName << "' destroyed.";
}
if (Monitor::RemoveFileMapping(fName.c_str())) {
LOG(trace) << "File mapping '" << fName << "' destroyed.";
}
} else {
LOG(debug) << "Skipping removal of " << fName << " unmanaged region, because RegionConfig::removeOnDestruction is false";
}
if (boost::interprocess::message_queue::remove(fQueueName.c_str())) {
LOG(trace) << "Region queue '" << fQueueName << "' destroyed.";
} else {
LOG(debug) << "Region queue '" << fQueueName << "' not destroyed.";
}
if (fFile) {
fclose(fFile);
}
} else {
// LOG(debug) << "Region queue '" << fQueueName << "' is remote, no cleanup necessary";
}
// LOG(debug) << "Region '" << fName << "' (" << (fRemote ? "remote" : "local") << ") destructed.";
}
private:
Manager& fManager;
boost::interprocess::mapped_region* fRegion;
uint16_t fRegionId;
bool fRemote;
bool fRemoveOnDestruction;
uint32_t fLinger;
std::atomic<bool> fStopAcks;
std::string fName;
std::string fQueueName;
boost::interprocess::shared_memory_object fShmemObject;
FILE* fFile;
boost::interprocess::file_mapping fFileMapping;
boost::interprocess::mapped_region fRegion;
std::mutex fBlockMtx;
std::condition_variable fBlockSendCV;
std::vector<RegionBlock> fBlocksToFree;
const std::size_t fAckBunchSize = 256;
std::unique_ptr<boost::interprocess::message_queue> fQueue;
std::thread fAcksReceiver;
std::thread fAcksSender;
RegionCallback fCallback;
RegionBulkCallback fBulkCallback;
static RegionConfig makeRegionConfig(uint16_t id)
{
RegionConfig regionCfg;
regionCfg.id = id;
return regionCfg;
}
static void Register(const std::string& shmId, const RegionConfig& cfg)
{
using namespace boost::interprocess;
managed_shared_memory mngSegment(open_or_create, std::string("fmq_" + shmId + "_mng").c_str(), kManagementSegmentSize);
VoidAlloc alloc(mngSegment.get_segment_manager());
Uint16RegionInfoHashMap* shmRegions = mngSegment.find_or_construct<Uint16RegionInfoHashMap>(unique_instance)(alloc);
EventCounter* eventCounter = mngSegment.find_or_construct<EventCounter>(unique_instance)(0);
bool newShmRegionCreated = shmRegions->emplace(cfg.id.value(), RegionInfo(cfg.path.c_str(), cfg.creationFlags, cfg.userFlags, cfg.size, alloc)).second;
if (newShmRegionCreated) {
(eventCounter->fCount)++;
}
}
void SetCallbacks(RegionCallback callback, RegionBulkCallback bulkCallback)
{
fCallback = std::move(callback);
fBulkCallback = std::move(bulkCallback);
}
void InitializeQueues()
{
using namespace boost::interprocess;
if (!fQueue) {
fQueue = std::make_unique<message_queue>(open_or_create, fQueueName.c_str(), 1024, fAckBunchSize * sizeof(RegionBlock));
LOG(trace) << "shmem: initialized region queue: " << fQueueName;
}
}
void StartAckSender()
{
if (!fAcksSender.joinable()) {
fAcksSender = std::thread(&UnmanagedRegion::SendAcks, this);
}
}
void SendAcks()
{
std::unique_ptr<RegionBlock[]> blocks = std::make_unique<RegionBlock[]>(fAckBunchSize);
size_t blocksToSend = 0;
while (true) {
{
std::unique_lock<std::mutex> lock(fBlockMtx);
// try to get <fAckBunchSize> blocks
if (fBlocksToFree.size() < fAckBunchSize) {
fBlockSendCV.wait_for(lock, std::chrono::milliseconds(500));
}
// send whatever blocks we have
blocksToSend = std::min(fBlocksToFree.size(), fAckBunchSize);
copy_n(fBlocksToFree.end() - blocksToSend, blocksToSend, blocks.get());
fBlocksToFree.resize(fBlocksToFree.size() - blocksToSend);
}
if (blocksToSend > 0) {
while (!fQueue->try_send(blocks.get(), blocksToSend * sizeof(RegionBlock), 0) && !fStopAcks) {
// receiver slow? yield and try again...
std::this_thread::yield();
}
// LOG(debug) << "Sent " << blocksToSend << " blocks.";
} else { // blocksToSend == 0
if (fStopAcks) {
break;
}
}
}
LOG(trace) << "AcksSender for " << fName << " leaving " << "(blocks left to free: " << fBlocksToFree.size() << ", "
<< " blocks left to send: " << blocksToSend << ").";
}
void StartAckReceiver()
{
if (!fAcksReceiver.joinable()) {
fAcksReceiver = std::thread(&UnmanagedRegion::ReceiveAcks, this);
}
}
void ReceiveAcks()
{
unsigned int priority = 0;
boost::interprocess::message_queue::size_type recvdSize = 0;
std::unique_ptr<RegionBlock[]> blocks = std::make_unique<RegionBlock[]>(fAckBunchSize);
std::vector<fair::mq::RegionBlock> result;
result.reserve(fAckBunchSize);
while (true) {
uint32_t timeout = 100;
bool leave = false;
if (fStopAcks) {
timeout = fLinger;
leave = true;
}
auto rcvTill = boost::posix_time::microsec_clock::universal_time() + boost::posix_time::milliseconds(timeout);
while (fQueue->timed_receive(blocks.get(), fAckBunchSize * sizeof(RegionBlock), recvdSize, priority, rcvTill)) {
const auto numBlocks = recvdSize / sizeof(RegionBlock);
// LOG(debug) << "Received " << numBlocks << " blocks (recvdSize: " << recvdSize << "). (remaining queue size: " << fQueue->get_num_msg() << ").";
if (fBulkCallback) {
result.clear();
for (size_t i = 0; i < numBlocks; i++) {
result.emplace_back(reinterpret_cast<char*>(fRegion.get_address()) + blocks[i].fHandle, blocks[i].fSize, reinterpret_cast<void*>(blocks[i].fHint));
}
fBulkCallback(result);
} else if (fCallback) {
for (size_t i = 0; i < numBlocks; i++) {
fCallback(reinterpret_cast<char*>(fRegion.get_address()) + blocks[i].fHandle, blocks[i].fSize, reinterpret_cast<void*>(blocks[i].fHint));
}
}
}
if (leave) {
break;
}
}
LOG(trace) << "AcksReceiver for " << fName << " leaving (remaining queue size: " << fQueue->get_num_msg() << ").";
}
void ReleaseBlock(const RegionBlock& block)
{
std::unique_lock<std::mutex> lock(fBlockMtx);
fBlocksToFree.emplace_back(block);
if (fBlocksToFree.size() >= fAckBunchSize) {
lock.unlock();
fBlockSendCV.notify_one();
}
}
void StopAcks()
{
fStopAcks = true;
if (fAcksSender.joinable()) {
fBlockSendCV.notify_one();
fAcksSender.join();
}
if (fAcksReceiver.joinable()) {
fAcksReceiver.join();
}
}
};
} // namespace fair::mq::shmem

View File

@@ -0,0 +1,71 @@
/********************************************************************************
* Copyright (C) 2014 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
* copied verbatim in the file "LICENSE" *
********************************************************************************/
#ifndef FAIR_MQ_SHMEM_UNMANAGEDREGIONIMPL_H_
#define FAIR_MQ_SHMEM_UNMANAGEDREGIONIMPL_H_
#include "Manager.h"
#include "UnmanagedRegion.h"
#include <fairmq/UnmanagedRegion.h>
#include <fairlogger/Logger.h>
#include <cstddef> // size_t
namespace fair::mq::shmem
{
class Message;
class Socket;
class UnmanagedRegionImpl final : public fair::mq::UnmanagedRegion
{
friend class Message;
friend class Socket;
public:
UnmanagedRegionImpl(Manager& manager,
const size_t size,
RegionCallback callback,
RegionBulkCallback bulkCallback,
fair::mq::RegionConfig cfg,
fair::mq::TransportFactory* factory)
: fair::mq::UnmanagedRegion(factory)
, fManager(manager)
, fRegion(nullptr)
, fRegionId(0)
{
auto result = fManager.CreateRegion(size, callback, bulkCallback, std::move(cfg));
fRegion = result.first;
fRegionId = result.second;
}
UnmanagedRegionImpl(const UnmanagedRegionImpl&) = delete;
UnmanagedRegionImpl(UnmanagedRegionImpl&&) = delete;
UnmanagedRegionImpl& operator=(const UnmanagedRegionImpl&) = delete;
UnmanagedRegionImpl& operator=(UnmanagedRegionImpl&&) = delete;
void* GetData() const override { return fRegion->GetData(); }
size_t GetSize() const override { return fRegion->GetSize(); }
uint16_t GetId() const override { return fRegionId; }
void SetLinger(uint32_t linger) override { fRegion->SetLinger(linger); }
uint32_t GetLinger() const override { return fRegion->GetLinger(); }
Transport GetType() const override { return fair::mq::Transport::SHM; }
~UnmanagedRegionImpl() override { fManager.RemoveRegion(fRegionId); }
private:
Manager& fManager;
shmem::UnmanagedRegion* fRegion;
uint16_t fRegionId;
};
} // namespace fair::mq::shmem
#endif /* FAIR_MQ_SHMEM_UNMANAGEDREGIONIMPL_H_ */

View File

@@ -76,6 +76,7 @@ int main(int argc, char** argv)
string sessionName;
string shmId;
bool cleanup = false;
bool resetContent = false;
bool selfDestruct = false;
bool interactive = false;
bool viewOnly = false;
@@ -97,6 +98,7 @@ int main(int argc, char** argv)
("session,s" , value<string>(&sessionName)->default_value("default"), "Session id")
("shmid" , value<string>(&shmId)->default_value(""), "Shmem id (if not provided, it is generated out of session id and user id)")
("cleanup,c" , value<bool>(&cleanup)->implicit_value(true), "Perform cleanup and quit")
("reset-content,r", value<bool>(&resetContent)->implicit_value(true), "[EXPERIMENTAL] Reset content of the segments (only call this when segment is not in use)")
("self-destruct,x", value<bool>(&selfDestruct)->implicit_value(true), "Quit after first closing of the memory")
("interactive,i" , value<bool>(&interactive)->implicit_value(true), "Interactive run")
("view,v" , value<bool>(&viewOnly)->implicit_value(true), "Run in view only mode")
@@ -146,6 +148,11 @@ int main(int argc, char** argv)
return 0;
}
if (resetContent) {
Monitor::ResetContent(ShmId{shmId});
return 0;
}
if (debug) {
Monitor::PrintDebugInfo(ShmId{shmId});
return 0;

54
fairmq/tools/Exceptions.h Normal file
View File

@@ -0,0 +1,54 @@
/********************************************************************************
* Copyright (C) 2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
* copied verbatim in the file "LICENSE" *
********************************************************************************/
#ifndef FAIR_MQ_TOOLS_EXCEPTIONS_H
#define FAIR_MQ_TOOLS_EXCEPTIONS_H
#include <functional>
namespace fair::mq::tools
{
/**
* Executes the given callback in the destructor.
* Can be used to execute something in case of an exception when catch is undesirable, e.g.:
*
* {
* // callback will be executed only if f throws an exception
* CallOnDestruction cod([](){ cout << "exception was thrown"; }, true);
* f();
* cod.disable();
* }
*/
class CallOnDestruction
{
public:
CallOnDestruction(std::function<void()> c, bool enable = true)
: callback(c)
, enabled(enable)
{}
~CallOnDestruction()
{
if (enabled) {
callback();
}
}
void enable() { enabled = true; }
void disable() { enabled = false; }
private:
std::function<void()> callback;
bool enabled;
};
} // namespace fair::mq::tools
#endif /* FAIR_MQ_TOOLS_EXCEPTIONS_H */

Some files were not shown because too many files have changed in this diff Show More