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) 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) 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) 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 # install

View File

@@ -30,7 +30,7 @@ struct Sampler : fair::mq::Device
// create message object with a pointer to the data buffer, its size, // create message object with a pointer to the data buffer, its size,
// custom deletion function (called when transfer is done), // custom deletion function (called when transfer is done),
// and pointer to the object managing the data buffer // and pointer to the object managing the data buffer
FairMQMessagePtr msg(NewMessage( fair::mq::MessagePtr msg(NewMessage(
const_cast<char*>(text->c_str()), const_cast<char*>(text->c_str()),
text->length(), text->length(),
[](void* /*data*/, void* object) { delete static_cast<std::string*>(object); }, [](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"); 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()) << "\""; 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 transport=$1
fi 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 # 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="fairmq-ex-1-1-sampler"
SAMPLER+=" --id sampler1" SAMPLER+=" --id sampler1"
SAMPLER+=" --rate 1" SAMPLER+=" --rate 1"
SAMPLER+=" --transport $transport" SAMPLER+=" --transport $transport"
SAMPLER+=" --verbosity veryhigh" SAMPLER+=" --verbosity veryhigh"
SAMPLER+=" --session $SESSION" SAMPLER+=" --session $session"
SAMPLER+=" --shm-segment-size 100000000"
SAMPLER+=" --control static --color false" SAMPLER+=" --control static --color false"
SAMPLER+=" --max-iterations 1" 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 & @CMAKE_CURRENT_BINARY_DIR@/$SAMPLER &
SAMPLER_PID=$! SAMPLER_PID=$!
@@ -29,13 +32,16 @@ SINK="fairmq-ex-1-1-sink"
SINK+=" --id sink1" SINK+=" --id sink1"
SINK+=" --transport $transport" SINK+=" --transport $transport"
SINK+=" --verbosity veryhigh" SINK+=" --verbosity veryhigh"
SINK+=" --session $SESSION" SINK+=" --session $session"
SINK+=" --shm-segment-size 100000000"
SINK+=" --control static --color false" SINK+=" --control static --color false"
SINK+=" --max-iterations 1" 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 & @CMAKE_CURRENT_BINARY_DIR@/$SINK &
SINK_PID=$! SINK_PID=$!
# wait for sampler and sink to finish # wait for sampler and sink to finish
wait $SAMPLER_PID wait $SAMPLER_PID
wait $SINK_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) 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) 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) 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 # install

View File

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

View File

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

View File

@@ -27,7 +27,7 @@ struct Sink : fair::mq::Device
fMaxIterations = fConfig->GetProperty<uint64_t>("max-iterations"); 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()) << "\""; 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 transport=$1
fi 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 # 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="fairmq-ex-1-n-1-sampler"
SAMPLER+=" --id sampler1" SAMPLER+=" --id sampler1"
SAMPLER+=" --transport $transport" SAMPLER+=" --transport $transport"
SAMPLER+=" --verbosity veryhigh" SAMPLER+=" --verbosity veryhigh"
SAMPLER+=" --session $SESSION" SAMPLER+=" --session $session"
SAMPLER+=" --severity debug"
SAMPLER+=" --shm-segment-size 100000000"
SAMPLER+=" --control static --color false" SAMPLER+=" --control static --color false"
SAMPLER+=" --max-iterations 2" 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 & @CMAKE_CURRENT_BINARY_DIR@/$SAMPLER &
SAMPLER_PID=$! SAMPLER_PID=$!
@@ -29,10 +34,12 @@ PROCESSOR1="fairmq-ex-1-n-1-processor"
PROCESSOR1+=" --id processor1" PROCESSOR1+=" --id processor1"
PROCESSOR1+=" --transport $transport" PROCESSOR1+=" --transport $transport"
PROCESSOR1+=" --verbosity veryhigh" PROCESSOR1+=" --verbosity veryhigh"
PROCESSOR1+=" --session $SESSION" PROCESSOR1+=" --session $session"
PROCESSOR1+=" --severity debug"
PROCESSOR1+=" --shm-segment-size 100000000"
PROCESSOR1+=" --control static --color false" PROCESSOR1+=" --control static --color false"
PROCESSOR1+=" --mq-config $ex2config" PROCESSOR1+=" --channel-config name=$chan1,type=pull,method=connect,address=ipc://$chan1Addr,rateLogging=0"
PROCESSOR1+=" --config-key processor" PROCESSOR1+=" name=$chan2,type=push,method=connect,address=ipc://$chan2Addr,rateLogging=0"
@CMAKE_CURRENT_BINARY_DIR@/$PROCESSOR1 & @CMAKE_CURRENT_BINARY_DIR@/$PROCESSOR1 &
PROCESSOR1_PID=$! PROCESSOR1_PID=$!
@@ -40,10 +47,12 @@ PROCESSOR2="fairmq-ex-1-n-1-processor"
PROCESSOR2+=" --id processor2" PROCESSOR2+=" --id processor2"
PROCESSOR2+=" --transport $transport" PROCESSOR2+=" --transport $transport"
PROCESSOR2+=" --verbosity veryhigh" PROCESSOR2+=" --verbosity veryhigh"
PROCESSOR2+=" --session $SESSION" PROCESSOR2+=" --session $session"
PROCESSOR2+=" --severity debug"
PROCESSOR2+=" --shm-segment-size 100000000"
PROCESSOR2+=" --control static --color false" PROCESSOR2+=" --control static --color false"
PROCESSOR2+=" --mq-config $ex2config" PROCESSOR2+=" --channel-config name=$chan1,type=pull,method=connect,address=ipc://$chan1Addr,rateLogging=0"
PROCESSOR2+=" --config-key processor" PROCESSOR2+=" name=$chan2,type=push,method=connect,address=ipc://$chan2Addr,rateLogging=0"
@CMAKE_CURRENT_BINARY_DIR@/$PROCESSOR2 & @CMAKE_CURRENT_BINARY_DIR@/$PROCESSOR2 &
PROCESSOR2_PID=$! PROCESSOR2_PID=$!
@@ -51,10 +60,12 @@ SINK="fairmq-ex-1-n-1-sink"
SINK+=" --id sink1" SINK+=" --id sink1"
SINK+=" --transport $transport" SINK+=" --transport $transport"
SINK+=" --verbosity veryhigh" SINK+=" --verbosity veryhigh"
SINK+=" --session $SESSION" SINK+=" --session $session"
SINK+=" --severity debug"
SINK+=" --shm-segment-size 100000000"
SINK+=" --control static --color false" SINK+=" --control static --color false"
SINK+=" --max-iterations 2" 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 & @CMAKE_CURRENT_BINARY_DIR@/$SINK &
SINK_PID=$! SINK_PID=$!
@@ -69,3 +80,5 @@ kill -SIGINT $PROCESSOR2_PID
# wait for everything to finish # wait for everything to finish
wait $PROCESSOR1_PID wait $PROCESSOR1_PID
wait $PROCESSOR2_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) 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) 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) 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) 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) 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 # install

View File

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

View File

@@ -19,7 +19,7 @@ struct Sampler : fair::mq::Device
{ {
void InitTask() override void InitTask() override
{ {
fNumDataChannels = fChannels.at("data").size(); fNumDataChannels = GetNumSubChannels("data");
fMaxIterations = fConfig->GetProperty<uint64_t>("max-iterations"); 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). // 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 // 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) { for (int i = 0; i < fNumDataChannels - 1; ++i) {
FairMQMessagePtr msgCopy(NewMessage()); fair::mq::MessagePtr msgCopy(NewMessage());
msgCopy->Copy(*msg); msgCopy->Copy(*msg);
Send(msgCopy, "data", i); Send(msgCopy, "data", i);
} }

View File

@@ -27,7 +27,7 @@ struct Sink : fair::mq::Device
fMaxIterations = fConfig->GetProperty<uint64_t>("max-iterations"); 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())) << "\""; LOG(info) << "Received message: \"" << *(static_cast<uint64_t*>(msg->GetData())) << "\"";

View File

@@ -8,19 +8,24 @@ if [[ $1 =~ ^[a-z]+$ ]]; then
transport=$1 transport=$1
fi 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 # 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="fairmq-ex-copypush-sampler"
SAMPLER+=" --id sampler1" SAMPLER+=" --id sampler1"
SAMPLER+=" --transport $transport" SAMPLER+=" --transport $transport"
SAMPLER+=" --verbosity veryhigh" SAMPLER+=" --verbosity veryhigh"
SAMPLER+=" --session $SESSION" SAMPLER+=" --severity debug"
SAMPLER+=" --shm-segment-size 100000000"
SAMPLER+=" --session $session"
SAMPLER+=" --control static --color false" SAMPLER+=" --control static --color false"
SAMPLER+=" --max-iterations 1" 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 & @CMAKE_CURRENT_BINARY_DIR@/$SAMPLER &
SAMPLER_PID=$! SAMPLER_PID=$!
@@ -28,10 +33,12 @@ SINK1="fairmq-ex-copypush-sink"
SINK1+=" --id sink1" SINK1+=" --id sink1"
SINK1+=" --transport $transport" SINK1+=" --transport $transport"
SINK1+=" --verbosity veryhigh" SINK1+=" --verbosity veryhigh"
SINK1+=" --session $SESSION" SINK1+=" --severity debug"
SINK1+=" --shm-segment-size 100000000"
SINK1+=" --session $session"
SINK1+=" --control static --color false" SINK1+=" --control static --color false"
SINK1+=" --max-iterations 1" 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 & @CMAKE_CURRENT_BINARY_DIR@/$SINK1 &
SINK1_PID=$! SINK1_PID=$!
@@ -39,10 +46,12 @@ SINK2="fairmq-ex-copypush-sink"
SINK2+=" --id sink2" SINK2+=" --id sink2"
SINK2+=" --transport $transport" SINK2+=" --transport $transport"
SINK2+=" --verbosity veryhigh" SINK2+=" --verbosity veryhigh"
SINK2+=" --session $SESSION" SINK2+=" --severity debug"
SINK2+=" --shm-segment-size 100000000"
SINK2+=" --session $session"
SINK2+=" --control static --color false" SINK2+=" --control static --color false"
SINK2+=" --max-iterations 1" 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 & @CMAKE_CURRENT_BINARY_DIR@/$SINK2 &
SINK2_PID=$! SINK2_PID=$!
@@ -50,3 +59,5 @@ SINK2_PID=$!
wait $SAMPLER_PID wait $SAMPLER_PID
wait $SINK1_PID wait $SINK1_PID
wait $SINK2_PID wait $SINK2_PID
rm $chanAddr1; rm $chanAddr2

View File

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

View File

@@ -25,7 +25,7 @@ struct Sink : fair::mq::Device
fIterations = fConfig->GetValue<uint64_t>("iterations"); 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()) << "\""; 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) 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) 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) 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) if(BUILD_OFI_TRANSPORT)
add_test(NAME Example.Multipart.ofi COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-multipart.sh ofi) 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() endif()
# install # 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. 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. 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; 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). // 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 // Should only be used for small data because of the cost of an additional copy
parts.AddPart(NewSimpleMessage(header)); parts.AddPart(NewSimpleMessage(header));
parts.AddPart(NewMessage(1000)); parts.AddPart(NewMessage(1000));
// create more data parts, testing the FairMQParts in-place constructor // create more data parts, testing the fair::mq::Parts in-place constructor
FairMQParts auxData{ NewMessage(500), NewMessage(600), NewMessage(700) }; fair::mq::Parts auxData{ NewMessage(500), NewMessage(600), NewMessage(700) };
assert(auxData.Size() == 3); assert(auxData.Size() == 3);
parts.AddPart(std::move(auxData)); parts.AddPart(std::move(auxData));
assert(auxData.Size() == 0); assert(auxData.Size() == 0);

View File

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

View File

@@ -8,19 +8,28 @@ if [[ $1 =~ ^[a-z]+$ ]]; then
transport=$1 transport=$1
fi 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 # 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="fairmq-ex-multipart-sampler"
SAMPLER+=" --id sampler1" SAMPLER+=" --id sampler1"
SAMPLER+=" --transport $transport" SAMPLER+=" --transport $transport"
SAMPLER+=" --verbosity veryhigh" SAMPLER+=" --verbosity veryhigh"
SAMPLER+=" --session $SESSION" SAMPLER+=" --session $session"
SAMPLER+=" --shm-segment-size 100000000"
SAMPLER+=" --max-iterations 1" SAMPLER+=" --max-iterations 1"
SAMPLER+=" --control static --color false" 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 & @CMAKE_CURRENT_BINARY_DIR@/$SAMPLER &
SAMPLER_PID=$! SAMPLER_PID=$!
@@ -28,11 +37,14 @@ SINK="fairmq-ex-multipart-sink"
SINK+=" --id sink1" SINK+=" --id sink1"
SINK+=" --transport $transport" SINK+=" --transport $transport"
SINK+=" --verbosity veryhigh" SINK+=" --verbosity veryhigh"
SINK+=" --session $SESSION" SINK+=" --session $session"
SINK+=" --shm-segment-size 100000000"
SINK+=" --control static --color false" 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 & @CMAKE_CURRENT_BINARY_DIR@/$SINK &
SINK_PID=$! SINK_PID=$!
wait $SAMPLER_PID wait $SAMPLER_PID
wait $SINK_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) 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) 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 # 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. 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). // 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 // 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"; LOG(info) << "Sending OK";

View File

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

View File

@@ -27,7 +27,7 @@ struct Sink : fair::mq::Device
fMaxIterations = fConfig->GetProperty<uint64_t>("max-iterations"); 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()) << "\""; LOG(info) << "Received broadcast: \"" << std::string(static_cast<char*>(msg->GetData()), msg->GetSize()) << "\"";
fReceivedBroadcast = true; fReceivedBroadcast = true;
@@ -35,7 +35,7 @@ struct Sink : fair::mq::Device
return CheckIterations(); 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()) << "\""; LOG(info) << "Received message: \"" << std::string(static_cast<char*>(msg->GetData()), msg->GetSize()) << "\"";
fReceivedData = true; fReceivedData = true;

View File

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

View File

@@ -28,7 +28,7 @@ struct Sampler1 : fair::mq::Device
bool ConditionalRun() override bool ConditionalRun() override
{ {
// Creates a message using the transport of channel data1 // 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 // 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). // 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; uint64_t numAcks = 0;
while (!NewStatePending()) { while (!NewStatePending()) {
FairMQMessagePtr ack(NewMessageFor("ack", 0)); fair::mq::MessagePtr ack(NewMessageFor("ack", 0));
if (Receive(ack, "ack") < 0) { if (Receive(ack, "ack") < 0) {
break; break;
} }

View File

@@ -22,7 +22,7 @@ struct Sampler2 : fair::mq::Device
bool ConditionalRun() override 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 // 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). // 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) // 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++; fNumIterations1++;
// Creates a message using the transport of channel ack // 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) { if (Send(ack, "ack") < 0) {
return false; 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) // 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++; fNumIterations2++;
// return true if want to be called again (otherwise go to IDLE state) // 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@ 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="fairmq-ex-multiple-transports-sink"
SINK+=" --id sink1" SINK+=" --id sink1"
SINK+=" --verbosity veryhigh" SINK+=" --verbosity veryhigh --severity debug"
SINK+=" --session $SESSION" SINK+=" --shm-segment-size 100000000"
SINK+=" --session $session"
SINK+=" --max-iterations 1" SINK+=" --max-iterations 1"
SINK+=" --control static --color false" SINK+=" --control static --color false"
SINK+=" --transport shmem" SINK+=" --transport shmem"
SINK+=" --channel-config name=data1,type=pull,method=connect,address=tcp://127.0.0.1:5555" SINK+=" --channel-config name=$chan1,type=pull,method=connect,address=ipc://$chan1Addr"
SINK+=" name=data2,type=pull,method=connect,address=tcp://127.0.0.1:5556,transport=zeromq" SINK+=" name=$chan2,type=pull,method=connect,address=ipc://$chan2Addr,transport=zeromq"
SINK+=" name=ack,type=pub,method=connect,address=tcp://127.0.0.1:5557,transport=zeromq" SINK+=" name=$ackChan,type=pub,method=connect,address=ipc://$ackChanAddr,transport=zeromq"
@CMAKE_CURRENT_BINARY_DIR@/$SINK & @CMAKE_CURRENT_BINARY_DIR@/$SINK &
SINK_PID=$! SINK_PID=$!
SAMPLER1="fairmq-ex-multiple-transports-sampler1" SAMPLER1="fairmq-ex-multiple-transports-sampler1"
SAMPLER1+=" --id sampler1" SAMPLER1+=" --id sampler1"
SAMPLER1+=" --session $SESSION" SAMPLER1+=" --session $session"
SAMPLER1+=" --verbosity veryhigh" SAMPLER1+=" --verbosity veryhigh --severity debug"
SAMPLER1+=" --shm-segment-size 100000000"
SAMPLER1+=" --max-iterations 1" SAMPLER1+=" --max-iterations 1"
SAMPLER1+=" --control static --color false" SAMPLER1+=" --control static --color false"
SAMPLER1+=" --transport shmem" SAMPLER1+=" --transport shmem"
SAMPLER1+=" --channel-config name=data1,type=push,method=bind,address=tcp://127.0.0.1:5555" SAMPLER1+=" --channel-config name=$chan1,type=push,method=bind,address=ipc://$chan1Addr"
SAMPLER1+=" name=ack,type=sub,method=bind,address=tcp://127.0.0.1:5557,transport=zeromq" SAMPLER1+=" name=$ackChan,type=sub,method=bind,address=ipc://$ackChanAddr,transport=zeromq"
@CMAKE_CURRENT_BINARY_DIR@/$SAMPLER1 & @CMAKE_CURRENT_BINARY_DIR@/$SAMPLER1 &
SAMPLER1_PID=$! SAMPLER1_PID=$!
SAMPLER2="fairmq-ex-multiple-transports-sampler2" SAMPLER2="fairmq-ex-multiple-transports-sampler2"
SAMPLER2+=" --id sampler2" SAMPLER2+=" --id sampler2"
SAMPLER2+=" --session $SESSION" SAMPLER2+=" --session $session"
SAMPLER2+=" --verbosity veryhigh" SAMPLER2+=" --verbosity veryhigh --severity debug"
SAMPLER2+=" --shm-segment-size 100000000"
SAMPLER2+=" --max-iterations 1" SAMPLER2+=" --max-iterations 1"
SAMPLER2+=" --control static --color false" SAMPLER2+=" --control static --color false"
SAMPLER2+=" --transport zeromq" 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 & @CMAKE_CURRENT_BINARY_DIR@/$SAMPLER2 &
SAMPLER2_PID=$! SAMPLER2_PID=$!
wait $SAMPLER1_PID wait $SAMPLER1_PID
wait $SAMPLER2_PID wait $SAMPLER2_PID
wait $SINK_PID wait $SINK_PID
rm $chan1Addr; rm $chan2Addr; rm $ackChanAddr

View File

@@ -24,7 +24,7 @@ namespace bpo = boost::program_options;
struct TFBuffer struct TFBuffer
{ {
FairMQParts parts; fair::mq::Parts parts;
chrono::steady_clock::time_point start; chrono::steady_clock::time_point start;
chrono::steady_clock::time_point end; chrono::steady_clock::time_point end;
}; };
@@ -43,7 +43,7 @@ struct Receiver : fair::mq::Device
fMaxTimeframes = GetConfig()->GetValue<int>("max-timeframes"); 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())); Header& h = *(static_cast<Header*>(parts.At(0)->GetData()));
// LOG(info) << "Received sub-time frame #" << h.id << " from Sender" << h.senderIndex; // 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)"); ("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>(); return std::make_unique<Receiver>();
} }

View File

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

View File

@@ -19,7 +19,7 @@ struct Synchronizer : fair::mq::Device
{ {
bool ConditionalRun() override bool ConditionalRun() override
{ {
FairMQMessagePtr msg(NewSimpleMessage(fTimeframeId)); fair::mq::MessagePtr msg(NewSimpleMessage(fTimeframeId));
if (Send(msg, "sync") > 0) { if (Send(msg, "sync") > 0) {
if (++fTimeframeId == UINT16_MAX - 1) { if (++fTimeframeId == UINT16_MAX - 1) {
@@ -37,7 +37,7 @@ struct Synchronizer : fair::mq::Device
}; };
void addCustomOptions(bpo::options_description& /* options */) {} 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>(); 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) { if (fDoQC.load() == true) {
FairMQMessagePtr msgCopy(NewMessage()); fair::mq::MessagePtr msgCopy(NewMessage());
msgCopy->Copy(*msg); msgCopy->Copy(*msg);
if (Send(msg, "qc") < 0) { if (Send(msg, "qc") < 0) {
return false; return false;

View File

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

View File

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

View File

@@ -14,7 +14,7 @@
struct Sink : fair::mq::Device struct Sink : fair::mq::Device
{ {
Sink() { OnData("data2", &Sink::HandleData); } 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; namespace bpo = boost::program_options;

View File

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

View File

@@ -18,9 +18,9 @@ struct Processor : fair::mq::Device
OnData("bp", &Processor::HandleData); 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) { if (Send(msg2, "ps") < 0) {
return false; return false;
} }

View File

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

View File

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

View File

@@ -21,7 +21,7 @@ struct Sender : fair::mq::Device
OnData(fInputChannelName, &Sender::HandleData); OnData(fInputChannelName, &Sender::HandleData);
} }
bool HandleData(FairMQMessagePtr& msg, int /*index*/) bool HandleData(fair::mq::MessagePtr& msg, int /*index*/)
{ {
if (Send(msg, "sr") < 0) { if (Send(msg, "sr") < 0) {
return false; 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) add_executable(fairmq-ex-region-sink sink.cxx)
target_link_libraries(fairmq-ex-region-sink PRIVATE FairMQ) 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(EX_BIN_DIR ${CMAKE_CURRENT_BINARY_DIR})
set(FAIRMQ_BIN_DIR ${CMAKE_BINARY_DIR}/fairmq) 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) 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) 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) 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 # 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"); fLinger = fConfig->GetProperty<uint32_t>("region-linger");
fMaxIterations = fConfig->GetProperty<uint64_t>("max-iterations"); 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 << ": " LOG(info) << "Region event: " << info.event << ": "
<< (info.managed ? "managed" : "unmanaged") << (info.managed ? "managed" : "unmanaged")
<< ", id: " << info.id << ", id: " << info.id
@@ -32,27 +32,25 @@ struct Sampler : fair::mq::Device
<< ", flags: " << info.flags; << ", 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 0, // ... and this sub-channel
10000000, // region size 10000000, // region size
[this](const std::vector<fair::mq::RegionBlock>& blocks) { // callback to be called when message buffers no longer needed by transport [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); std::lock_guard<std::mutex> lock(fMtx);
fNumUnackedMsgs -= blocks.size(); fNumUnackedMsgs -= blocks.size();
if (fMaxIterations > 0) { if (fMaxIterations > 0) {
LOG(info) << "Received " << blocks.size() << " acks"; LOG(info) << "Received " << blocks.size() << " acks";
} }
}, }, regionCfg));
"", // 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);
} }
bool ConditionalRun() override bool ConditionalRun() override
{ {
FairMQMessagePtr msg(NewMessageFor("data", // channel fair::mq::MessagePtr msg(NewMessageFor("data", // channel
0, // sub-channel 0, // sub-channel
fRegion, // region fRegion, // region
fRegion->GetData(), // ptr within region fRegion->GetData(), // ptr within region
@@ -87,7 +85,7 @@ struct Sampler : fair::mq::Device
LOG(info) << "All acknowledgements received."; LOG(info) << "All acknowledgements received.";
} }
} }
fChannels.at("data").at(0).Transport()->UnsubscribeFromRegionEvents(); GetChannel("data", 0).Transport()->UnsubscribeFromRegionEvents();
} }
private: private:
@@ -95,7 +93,7 @@ struct Sampler : fair::mq::Device
uint32_t fLinger = 100; uint32_t fLinger = 100;
uint64_t fMaxIterations = 0; uint64_t fMaxIterations = 0;
uint64_t fNumIterations = 0; uint64_t fNumIterations = 0;
FairMQUnmanagedRegionPtr fRegion = nullptr; fair::mq::UnmanagedRegionPtr fRegion = nullptr;
std::mutex fMtx; std::mutex fMtx;
uint64_t fNumUnackedMsgs = 0; uint64_t fNumUnackedMsgs = 0;
}; };

View File

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

View File

@@ -9,22 +9,25 @@ if [[ $1 =~ ^[a-z]+$ ]]; then
fi fi
msgSize="1000000" 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 # 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="fairmq-ex-region-sampler"
SAMPLER+=" --id sampler1" SAMPLER+=" --id sampler1"
SAMPLER+=" --transport $transport" SAMPLER+=" --transport $transport"
SAMPLER+=" --severity debug" SAMPLER+=" --severity debug"
SAMPLER+=" --session $SESSION" SAMPLER+=" --session $session"
SAMPLER+=" --shm-segment-size 100000000"
SAMPLER+=" --verbosity veryhigh" SAMPLER+=" --verbosity veryhigh"
SAMPLER+=" --control static --color false" SAMPLER+=" --control static --color false"
SAMPLER+=" --max-iterations 1" SAMPLER+=" --max-iterations 1"
SAMPLER+=" --msg-size $msgSize" SAMPLER+=" --msg-size $msgSize"
SAMPLER+=" --region-linger 500" 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 & @CMAKE_CURRENT_BINARY_DIR@/$SAMPLER &
SAMPLER_PID=$! SAMPLER_PID=$!
@@ -32,14 +35,17 @@ SINK="fairmq-ex-region-sink"
SINK+=" --id sink1" SINK+=" --id sink1"
SINK+=" --transport $transport" SINK+=" --transport $transport"
SINK+=" --severity debug" SINK+=" --severity debug"
SINK+=" --session $SESSION" SINK+=" --session $session"
SINK+=" --shm-segment-size 100000000"
SINK+=" --verbosity veryhigh" SINK+=" --verbosity veryhigh"
SINK+=" --control static --color false" SINK+=" --control static --color false"
SINK+=" --max-iterations 1" 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 & @CMAKE_CURRENT_BINARY_DIR@/$SINK &
SINK_PID=$! SINK_PID=$!
# wait for sampler and sink to finish # wait for sampler and sink to finish
wait $SAMPLER_PID wait $SAMPLER_PID
wait $SINK_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) 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) 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) 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 # install

View File

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

View File

@@ -26,7 +26,7 @@ struct Server : fair::mq::Device
fMaxIterations = fConfig->GetProperty<uint64_t>("max-iterations"); 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()) << "\""; 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."; 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 text->length(), // size
[](void* /*data*/, void* object) { delete static_cast<std::string*>(object); }, // deletion callback [](void* /*data*/, void* object) { delete static_cast<std::string*>(object); }, // deletion callback
text)); // object that manages the data text)); // object that manages the data

View File

@@ -8,19 +8,22 @@ if [[ $1 =~ ^[a-z]+$ ]]; then
transport=$1 transport=$1
fi 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 # 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="fairmq-ex-req-rep-client"
CLIENT+=" --id client" CLIENT+=" --id client"
CLIENT+=" --transport $transport" CLIENT+=" --transport $transport"
CLIENT+=" --verbosity veryhigh" CLIENT+=" --verbosity veryhigh"
CLIENT+=" --session $SESSION" CLIENT+=" --session $session"
CLIENT+=" --shm-segment-size 100000000"
CLIENT+=" --control static --color false" CLIENT+=" --control static --color false"
CLIENT+=" --max-iterations 1" 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 & @CMAKE_CURRENT_BINARY_DIR@/$CLIENT &
CLIENT_PID=$! CLIENT_PID=$!
@@ -28,13 +31,16 @@ SERVER="fairmq-ex-req-rep-server"
SERVER+=" --id server" SERVER+=" --id server"
SERVER+=" --transport $transport" SERVER+=" --transport $transport"
SERVER+=" --verbosity veryhigh" SERVER+=" --verbosity veryhigh"
SERVER+=" --session $SESSION" SERVER+=" --session $session"
SERVER+=" --shm-segment-size 100000000"
SERVER+=" --control static --color false" SERVER+=" --control static --color false"
SERVER+=" --max-iterations 1" 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 & @CMAKE_CURRENT_BINARY_DIR@/$SERVER &
SERVER_PID=$! SERVER_PID=$!
# wait for everything to finish # wait for everything to finish
wait $CLIENT_PID wait $CLIENT_PID
wait $SERVER_PID wait $SERVER_PID
rm $chanAddr

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -351,7 +351,7 @@ void ProgOptions::DeleteProperty(const string& key)
vm.erase(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); lock_guard<mutex> lock(fMtx);
unordered_map<string, int> existingChannels = GetChannelInfoImpl(); unordered_map<string, int> existingChannels = GetChannelInfoImpl();

View File

@@ -10,9 +10,12 @@
#define FAIR_MQ_SOCKET_H #define FAIR_MQ_SOCKET_H
#include <fairmq/Message.h> #include <fairmq/Message.h>
#include <fairmq/Parts.h>
#include <memory> #include <memory>
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#include <type_traits>
#include <vector> #include <vector>
namespace fair::mq { namespace fair::mq {
@@ -27,6 +30,12 @@ enum class TransferCode : int
interrupted = -3 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 struct Socket
{ {
Socket() = default; Socket() = default;
@@ -45,6 +54,8 @@ struct Socket
virtual int64_t Receive(MessagePtr& msg, int timeout = -1) = 0; 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 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 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.")]] [[deprecated("Use Socket::~Socket() instead.")]]
virtual void Close() = 0; virtual void Close() = 0;

View File

@@ -7,6 +7,7 @@
********************************************************************************/ ********************************************************************************/
#include <fairmq/StateMachine.h> #include <fairmq/StateMachine.h>
#include <fairmq/tools/Exceptions.h>
#include <fairlogger/Logger.h> #include <fairlogger/Logger.h>
@@ -204,6 +205,7 @@ struct Machine_ : public state_machine_def<Machine_>
} }
if (fState == State::Error) { if (fState == State::Error) {
LOG(trace) << "Device transitioned to error state";
throw StateMachine::ErrorStateException("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); auto fsm = static_pointer_cast<FairMQFSM>(fFsm);
try { fair::mq::tools::CallOnDestruction cod([&](){
fsm->CallStateChangeCallbacks(State::Idle); LOG(debug) << "Exception caught in ProcessWork(), going to Error state";
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";
{ {
lock_guard<mutex> lock(fsm->fStateMtx); lock_guard<mutex> lock(fsm->fStateMtx);
fsm->fState = State::Error; fsm->fState = State::Error;
fsm->CallStateChangeCallbacks(State::Error); fsm->CallStateChangeCallbacks(State::Error);
} }
ChangeState(Transition::ErrorFound); 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 RCVBUFSIZE, // size of the receive queue
SNDKERNELSIZE, SNDKERNELSIZE,
RCVKERNELSIZE, RCVKERNELSIZE,
SNDTIMEOUTMS,
RCVTIMEOUTMS,
LINGER, LINGER,
RATELOGGING, // logging rate RATELOGGING, // logging rate
PORTRANGEMIN, PORTRANGEMIN,
@@ -57,6 +59,8 @@ constexpr static const char* channelOptionKeys[] = {
/*[RCVBUFSIZE] = */ "rcvBufSize", /*[RCVBUFSIZE] = */ "rcvBufSize",
/*[SNDKERNELSIZE] = */ "sndKernelSize", /*[SNDKERNELSIZE] = */ "sndKernelSize",
/*[RCVKERNELSIZE] = */ "rcvKernelSize", /*[RCVKERNELSIZE] = */ "rcvKernelSize",
/*[SNDTIMEOUTMS] = */ "sndTimeoutMs",
/*[RCVTIMEOUTMS] = */ "rcvTimeoutMs",
/*[LINGER] = */ "linger", /*[LINGER] = */ "linger",
/*[RATELOGGING] = */ "rateLogging", /*[RATELOGGING] = */ "rateLogging",
/*[PORTRANGEMIN] = */ "portRangeMin", /*[PORTRANGEMIN] = */ "portRangeMin",

View File

@@ -11,6 +11,7 @@
// IWYU pragma: begin_exports // IWYU pragma: begin_exports
#include <fairmq/tools/CppSTL.h> #include <fairmq/tools/CppSTL.h>
#include <fairmq/tools/Exceptions.h>
#include <fairmq/tools/InstanceLimit.h> #include <fairmq/tools/InstanceLimit.h>
#include <fairmq/tools/Network.h> #include <fairmq/tools/Network.h>
#include <fairmq/tools/Process.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 path optional parameter to pass to the underlying transport
/// @param flags optional parameter to pass to the underlying transport /// @param flags optional parameter to pass to the underlying transport
/// @return pointer to UnmanagedRegion /// @return pointer to UnmanagedRegion
// [[deprecated("Use CreateUnmanagedRegion(size_t size, RegionCallback callback, RegionConfig cfg)")]]
virtual UnmanagedRegionPtr CreateUnmanagedRegion(size_t size, virtual UnmanagedRegionPtr CreateUnmanagedRegion(size_t size,
RegionCallback callback = nullptr, RegionCallback callback = nullptr,
const std::string& path = "", const std::string& path = "",
int flags = 0, int flags = 0,
RegionConfig cfg = RegionConfig()) = 0; RegionConfig cfg = RegionConfig()) = 0;
// [[deprecated("Use CreateUnmanagedRegion(size_t size, RegionCallback callback, RegionConfig cfg)")]]
virtual UnmanagedRegionPtr CreateUnmanagedRegion(size_t size, virtual UnmanagedRegionPtr CreateUnmanagedRegion(size_t size,
RegionBulkCallback callback = nullptr, RegionBulkCallback bulkCallback = nullptr,
const std::string& path = "", const std::string& path = "",
int flags = 0, int flags = 0,
RegionConfig cfg = RegionConfig()) = 0; RegionConfig cfg = RegionConfig()) = 0;
@@ -128,19 +130,35 @@ class TransportFactory
/// @param path optional parameter to pass to the underlying transport /// @param path optional parameter to pass to the underlying transport
/// @param flags optional parameter to pass to the underlying transport /// @param flags optional parameter to pass to the underlying transport
/// @return pointer to UnmanagedRegion /// @return pointer to UnmanagedRegion
// [[deprecated("Use CreateUnmanagedRegion(size_t size, RegionCallback callback, RegionConfig cfg)")]]
virtual UnmanagedRegionPtr CreateUnmanagedRegion(size_t size, virtual UnmanagedRegionPtr CreateUnmanagedRegion(size_t size,
int64_t userFlags, int64_t userFlags,
RegionCallback callback = nullptr, RegionCallback callback = nullptr,
const std::string& path = "", const std::string& path = "",
int flags = 0, int flags = 0,
RegionConfig cfg = RegionConfig()) = 0; RegionConfig cfg = RegionConfig()) = 0;
// [[deprecated("Use CreateUnmanagedRegion(size_t size, RegionCallback callback, RegionConfig cfg)")]]
virtual UnmanagedRegionPtr CreateUnmanagedRegion(size_t size, virtual UnmanagedRegionPtr CreateUnmanagedRegion(size_t size,
int64_t userFlags, int64_t userFlags,
RegionBulkCallback callback = nullptr, RegionBulkCallback bulkCallback = nullptr,
const std::string& path = "", const std::string& path = "",
int flags = 0, int flags = 0,
RegionConfig cfg = RegionConfig()) = 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, ...) /// @brief Subscribe to region events (creation, destruction, ...)
/// @param callback the callback that is called when a region event occurs /// @param callback the callback that is called when a region event occurs
virtual void SubscribeToRegionEvents(RegionEventCallback callback) = 0; virtual void SubscribeToRegionEvents(RegionEventCallback callback) = 0;

View File

@@ -9,12 +9,16 @@
#ifndef FAIR_MQ_UNMANAGEDREGION_H #ifndef FAIR_MQ_UNMANAGEDREGION_H
#define FAIR_MQ_UNMANAGEDREGION_H #define FAIR_MQ_UNMANAGEDREGION_H
#include <fairmq/Transports.h>
#include <cstddef> // size_t #include <cstddef> // size_t
#include <cstdint> // uint32_t #include <cstdint> // uint32_t
#include <fairmq/Transports.h>
#include <functional> // std::function #include <functional> // std::function
#include <memory> // std::unique_ptr #include <memory> // std::unique_ptr
#include <ostream> // std::ostream #include <optional> // std::optional
#include <ostream>
#include <string>
#include <vector> #include <vector>
namespace fair::mq { namespace fair::mq {
@@ -119,13 +123,20 @@ struct RegionConfig
{ {
RegionConfig() = default; RegionConfig() = default;
RegionConfig(bool l, bool z) RegionConfig(bool _lock, bool _zero)
: lock(l) : lock(_lock)
, zero(z) , zero(_zero)
{} {}
bool lock = false; bool lock = false; /// mlock region after creation
bool zero = false; 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 } // namespace fair::mq

View File

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

View File

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

View File

@@ -30,7 +30,7 @@ class Multiplier : public Device
fMultipart = fConfig->GetProperty<bool>("multipart"); fMultipart = fConfig->GetProperty<bool>("multipart");
fInChannelName = fConfig->GetProperty<std::string>("in-channel"); fInChannelName = fConfig->GetProperty<std::string>("in-channel");
fOutChannelNames = fConfig->GetProperty<std::vector<std::string>>("out-channel"); fOutChannelNames = fConfig->GetProperty<std::vector<std::string>>("out-channel");
fNumOutputs = fChannels.at(fOutChannelNames.at(0)).size(); fNumOutputs = GetNumSubChannels(fOutChannelNames.at(0));
if (fMultipart) { if (fMultipart) {
OnData(fInChannelName, &Multiplier::HandleMultipartData); 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 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 for (unsigned int j = 0; j < GetNumSubChannels(fOutChannelNames.at(i)); ++j) { // all subChannels in a channel
FairMQMessagePtr msgCopy(fTransportFactory->CreateMessage()); MessagePtr msgCopy(fTransportFactory->CreateMessage());
msgCopy->Copy(*payload); msgCopy->Copy(*payload);
Send(msgCopy, fOutChannelNames.at(i), j); 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 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); msgCopy->Copy(*payload);
Send(msgCopy, fOutChannelNames.back(), i); Send(msgCopy, fOutChannelNames.back(), i);
@@ -65,14 +65,14 @@ class Multiplier : public Device
return true; 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 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 for (unsigned int j = 0; j < GetNumSubChannels(fOutChannelNames.at(i)); ++j) { // all subChannels in a channel
FairMQParts parts; Parts parts;
for (int k = 0; k < payload.Size(); ++k) { for (int k = 0; k < payload.Size(); ++k) {
FairMQMessagePtr msgCopy(fTransportFactory->CreateMessage()); MessagePtr msgCopy(fTransportFactory->CreateMessage());
msgCopy->Copy(payload.AtRef(k)); msgCopy->Copy(payload.AtRef(k));
parts.AddPart(std::move(msgCopy)); 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 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) { for (int k = 0; k < payload.Size(); ++k) {
FairMQMessagePtr msgCopy(fTransportFactory->CreateMessage()); MessagePtr msgCopy(fTransportFactory->CreateMessage());
msgCopy->Copy(payload.AtRef(k)); msgCopy->Copy(payload.AtRef(k));
parts.AddPart(std::move(msgCopy)); parts.AddPart(std::move(msgCopy));
} }

View File

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

View File

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

View File

@@ -30,13 +30,13 @@ class Splitter : public Device
fMultipart = fConfig->GetProperty<bool>("multipart"); fMultipart = fConfig->GetProperty<bool>("multipart");
fInChannelName = fConfig->GetProperty<std::string>("in-channel"); fInChannelName = fConfig->GetProperty<std::string>("in-channel");
fOutChannelName = fConfig->GetProperty<std::string>("out-channel"); fOutChannelName = fConfig->GetProperty<std::string>("out-channel");
fNumOutputs = fChannels.at(fOutChannelName).size(); fNumOutputs = GetNumSubChannels(fOutChannelName);
fDirection = 0; fDirection = 0;
if (fMultipart) { if (fMultipart) {
OnData(fInChannelName, &Splitter::HandleData<FairMQParts>); OnData(fInChannelName, &Splitter::HandleData<Parts>);
} else { } 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/Socket.h>
#include <fairmq/ofi/TransportFactory.h> #include <fairmq/ofi/TransportFactory.h>
#include <fairmq/tools/Strings.h> #include <fairmq/tools/Strings.h>
#include <FairMQLogger.h> #include <fairlogger/Logger.h>
#include <asiofi.hpp> #include <asiofi.hpp>
#include <asio/buffer.hpp> #include <asio/buffer.hpp>

View File

@@ -166,6 +166,22 @@ struct TransportFactory final : mq::TransportFactory
throw std::runtime_error("Not yet implemented UMR."); 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 auto SubscribeToRegionEvents(RegionEventCallback /*callback*/) -> void override
{ {
throw std::runtime_error("Not yet implemented."); throw std::runtime_error("Not yet implemented.");

View File

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

View File

@@ -14,7 +14,7 @@
#include <fairmq/Plugin.h> #include <fairmq/Plugin.h>
#include <fairmq/Version.h> #include <fairmq/Version.h>
#include <FairMQLogger.h> #include <fairlogger/Logger.h>
#include <string> #include <string>
#include <sstream> #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-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-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.") ("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", 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-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).") ("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") { if (control == "static") {
LOG(debug) << "Running builtin controller: static"; LOG(debug) << "Running builtin controller: static";
fControllerThread = thread(&Control::StaticMode, this); 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") { } else if (control == "dynamic" || control == "external" || control == "interactive") {
LOG(debug) << "Running builtin controller: interactive"; LOG(debug) << "Running builtin controller: interactive";
fControllerThread = thread(&Control::InteractiveMode, this); fControllerThread = thread(&Control::InteractiveMode, this);
@@ -380,6 +383,28 @@ try {
ReleaseDeviceControl(); 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 auto Control::SignalHandler() -> void
{ {
while (gSignalCount == 0 && !fPluginShutdownRequested) { while (gSignalCount == 0 && !fPluginShutdownRequested) {

View File

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

View File

@@ -11,7 +11,7 @@
#include <memory> #include <memory>
#include <string> #include <string>
using FairMQDevicePtr = FairMQDevice*; using FairMQDevicePtr = fair::mq::Device*;
// to be implemented by the user to return a child class of FairMQDevice // to be implemented by the user to return a child class of FairMQDevice
FairMQDevicePtr getDevice(const fair::mq::ProgOptions& config); FairMQDevicePtr getDevice(const fair::mq::ProgOptions& config);
@@ -45,7 +45,7 @@ int main(int argc, char* argv[])
// }); // });
runner.AddHook<InstantiateDevice>([](DeviceRunner& r){ 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(); 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_ #ifndef FAIR_MQ_SHMEM_COMMON_H_
#define FAIR_MQ_SHMEM_COMMON_H_ #define FAIR_MQ_SHMEM_COMMON_H_
#include <picosha2.h>
#include <atomic> #include <atomic>
#include <sstream>
#include <string> #include <string>
#include <functional> // std::equal_to #include <functional> // std::equal_to
@@ -26,20 +23,21 @@
#include <boost/unordered_map.hpp> #include <boost/unordered_map.hpp>
#include <boost/variant.hpp> #include <boost/variant.hpp>
#include <unistd.h>
#include <sys/types.h> #include <sys/types.h>
namespace fair::mq::shmem namespace fair::mq::shmem
{ {
static constexpr uint64_t kManagementSegmentSize = 6553600;
struct SharedMemoryError : std::runtime_error { using std::runtime_error::runtime_error; }; struct SharedMemoryError : std::runtime_error { using std::runtime_error::runtime_error; };
using SimpleSeqFitSegment = boost::interprocess::basic_managed_shared_memory<char, 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::null_index>;
// boost::interprocess::iset_index>; // boost::interprocess::iset_index>;
using RBTreeBestFitSegment = boost::interprocess::basic_managed_shared_memory<char, 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::null_index>;
// boost::interprocess::iset_index>; // boost::interprocess::iset_index>;
@@ -60,21 +58,24 @@ struct RegionInfo
{ {
RegionInfo(const VoidAlloc& alloc) RegionInfo(const VoidAlloc& alloc)
: fPath("", alloc) : fPath("", alloc)
, fFlags(0) , fCreationFlags(0)
, fUserFlags(0) , fUserFlags(0)
, fSize(0)
, fDestroyed(false) , 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) : fPath(path, alloc)
, fFlags(flags) , fCreationFlags(flags)
, fUserFlags(userFlags) , fUserFlags(userFlags)
, fSize(size)
, fDestroyed(false) , fDestroyed(false)
{} {}
Str fPath; Str fPath;
int fFlags; int fCreationFlags;
uint64_t fUserFlags; uint64_t fUserFlags;
uint64_t fSize;
bool fDestroyed; bool fDestroyed;
}; };
@@ -150,6 +151,7 @@ struct MetaHeader
mutable boost::interprocess::managed_shared_memory::handle_t fShared; mutable boost::interprocess::managed_shared_memory::handle_t fShared;
uint16_t fRegionId; uint16_t fRegionId;
mutable uint16_t fSegmentId; mutable uint16_t fSegmentId;
bool fManaged;
}; };
#ifdef FAIRMQ_DEBUG_MODE #ifdef FAIRMQ_DEBUG_MODE
@@ -212,31 +214,11 @@ struct RegionBlock
// find id for unique shmem name: // 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)). // 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 makeShmIdStr(const std::string& sessionId, const std::string& userId);
{ std::string makeShmIdStr(const std::string& sessionId);
std::string seed(userId + sessionId); std::string makeShmIdStr(uint64_t val);
// generate a 8-digit hex value out of sha256 hash uint64_t makeShmIdUint64(const std::string& sessionId);
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());
}
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> struct SegmentSize : public boost::static_visitor<size_t>
{ {

View File

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

View File

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

View File

@@ -6,8 +6,10 @@
* copied verbatim in the file "LICENSE" * * copied verbatim in the file "LICENSE" *
********************************************************************************/ ********************************************************************************/
#include "Monitor.h"
#include "Common.h" #include "Common.h"
#include "Monitor.h"
#include "Segment.h"
#include <fairmq/shmem/UnmanagedRegion.h>
#include <fairmq/tools/IO.h> #include <fairmq/tools/IO.h>
#include <fairmq/tools/Strings.h> #include <fairmq/tools/Strings.h>
@@ -16,6 +18,7 @@
#include <boost/interprocess/file_mapping.hpp> #include <boost/interprocess/file_mapping.hpp>
#include <boost/interprocess/sync/named_mutex.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/sync/named_condition.hpp>
#include <boost/interprocess/ipc/message_queue.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()); managed_shared_memory managementSegment(open_read_only, std::string("fmq_" + shmId.shmId + "_mng").c_str());
VoidAlloc allocInstance(managementSegment.get_segment_manager()); 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; 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..."; LOG(error) << "Found management segment, but cannot locate segment info, something went wrong...";
return false; 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) { 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())); segments.emplace(s.first, RBTreeBestFitSegment(open_read_only, std::string("fmq_" + shmId.shmId + "_m_" + to_string(s.first)).c_str()));
} else { } else {
@@ -221,19 +230,30 @@ bool Monitor::PrintShm(const ShmId& shmId)
<< ", session: " << sessionName << ", session: " << sessionName
<< ", creator id: " << creatorId << ", creator id: " << creatorId
<< ", devices: " << numDevices << ", devices: " << numDevices
<< ", segments:\n"; << ", managed segments:\n";
for (const auto& s : segments) { for (const auto& s : segments) {
size_t free = boost::apply_visitor(SegmentFreeMemory(), s.second); size_t free = boost::apply_visitor(SegmentFreeMemory(), s.second);
size_t total = boost::apply_visitor(SegmentSize(), s.second); size_t total = boost::apply_visitor(SegmentSize(), s.second);
size_t used = total - free; size_t used = total - free;
ss << " [" << s.first
<< "]: total: " << total std::string msgCount;
#ifdef FAIRMQ_DEBUG_MODE #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 #else
<< ", msgs: NODEBUG" msgCount = "NODEBUG";
#endif #endif
ss << " [" << s.first << "]"
<< ": total: " << total
<< ", msgs: " << msgCount
<< ", free: " << free << ", free: " << free
<< ", used: " << used << ", used: " << used
<< "\n"; << "\n";
@@ -243,6 +263,21 @@ bool Monitor::PrintShm(const ShmId& shmId)
<< "total: " << mtotal << "total: " << mtotal
<< ", free: " << mfree << ", free: " << mfree
<< ", used: " << mused; << ", 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(); LOGV(info, user1) << ss.str();
} catch (bie&) { } catch (bie&) {
return false; return false;
@@ -374,31 +409,35 @@ void Monitor::PrintDebugInfo(const ShmId& shmId __attribute__((unused)))
string managementSegmentName("fmq_" + shmId.shmId + "_mng"); string managementSegmentName("fmq_" + shmId.shmId + "_mng");
try { try {
bipc::managed_shared_memory managementSegment(bipc::open_only, managementSegmentName.c_str()); 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()); bipc::interprocess_mutex* mtx(managementSegment.find_or_construct<bipc::interprocess_mutex>(bipc::unique_instance)());
boost::interprocess::scoped_lock<bipc::named_mutex> lock(mtx); bipc::scoped_lock<bipc::interprocess_mutex> lock(*mtx);
Uint16MsgDebugMapHashMap* debug = managementSegment.find<Uint16MsgDebugMapHashMap>(bipc::unique_instance).first; Uint16MsgDebugMapHashMap* debug = managementSegment.find<Uint16MsgDebugMapHashMap>(bipc::unique_instance).first;
size_t numMessages = 0; size_t numMessages = 0;
for (const auto& e : *debug) { if (debug) {
numMessages += e.second.size(); 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;
} }
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&) { } catch (bie&) {
LOG(info) << "no segments found"; 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"); string managementSegmentName("fmq_" + shmId.shmId + "_mng");
try { try {
bipc::managed_shared_memory managementSegment(bipc::open_only, managementSegmentName.c_str()); 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()); bipc::interprocess_mutex* mtx(managementSegment.find_or_construct<bipc::interprocess_mutex>(bipc::unique_instance)());
boost::interprocess::scoped_lock<bipc::named_mutex> lock(mtx); bipc::scoped_lock<bipc::interprocess_mutex> lock(*mtx);
Uint16MsgDebugMapHashMap* debug = managementSegment.find<Uint16MsgDebugMapHashMap>(bipc::unique_instance).first; Uint16MsgDebugMapHashMap* debug = managementSegment.find<Uint16MsgDebugMapHashMap>(bipc::unique_instance).first;
result.reserve(debug->size()); result.reserve(debug->size());
for (const auto& s : *debug) {
result[s.first].reserve(s.second.size()); if (debug) {
for (const auto& e : s.second) { for (const auto& s : *debug) {
result[s.first][e.first] = BufferDebugInfo(e.first, e.second.fPid, e.second.fSize, e.second.fCreationTime); 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&) { } catch (bie&) {
LOG(info) << "no segments found"; LOG(info) << "no segments found";
@@ -455,18 +499,18 @@ unsigned long Monitor::GetFreeMemory(const ShmId& shmId, uint16_t segmentId)
using namespace boost::interprocess; using namespace boost::interprocess;
try { try {
bipc::managed_shared_memory managementSegment(bipc::open_only, std::string("fmq_" + shmId.shmId + "_mng").c_str()); 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::interprocess_mutex* mtx(managementSegment.find_or_construct<bipc::interprocess_mutex>(bipc::unique_instance)());
boost::interprocess::scoped_lock<bipc::named_mutex> lock(mtx); 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"; LOG(error) << "Found management segment, but could not locate segment info";
throw MonitorError("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); auto it = shmSegments->find(segmentId);
if (it != segmentInfos->end()) { if (it != shmSegments->end()) {
if (it->second.fAllocationAlgorithm == AllocationAlgorithm::rbtree_best_fit) { 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()); RBTreeBestFitSegment segment(open_read_only, std::string("fmq_" + shmId.shmId + "_m_" + std::to_string(segmentId)).c_str());
return segment.get_free_memory(); return segment.get_free_memory();
@@ -497,10 +541,16 @@ void Monitor::PrintHelp()
<< "[q] quit."; << "[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) { if (verbose) {
LOG(info) << "Successfully removed '" << name << "'."; 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()); } std::vector<std::pair<std::string, bool>> Monitor::Cleanup(const ShmId& shmIdT, bool verbose /* = true */)
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::string shmId = shmIdT.shmId;
std::vector<std::pair<std::string, bool>> result; std::vector<std::pair<std::string, bool>> result;
if (verbose) { 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 { try {
bipc::managed_shared_memory managementSegment(bipc::open_only, managementSegmentName.c_str()); bipc::managed_shared_memory managementSegment(bipc::open_only, managementSegmentName.c_str());
try { Uint16RegionInfoHashMap* shmRegions = managementSegment.find<Uint16RegionInfoHashMap>(bipc::unique_instance).first;
RegionCounter* rc = managementSegment.find<RegionCounter>(bipc::unique_instance).first; if (shmRegions) {
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) {
if (verbose) { 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; Uint16SegmentInfoHashMap* shmSegments = managementSegment.find<Uint16SegmentInfoHashMap>(bipc::unique_instance).first;
if (shmSegments) {
if (segmentInfos) { if (verbose) {
for (const auto& s : *segmentInfos) { LOG(info) << "Found " << shmSegments->size() << " managed segments...";
result.emplace_back(RunRemoval(Monitor::RemoveObject, "fmq_" + shmId.shmId + "_m_" + to_string(s.first), verbose)); }
for (const auto& segment : *shmSegments) {
result.emplace_back(Remove<bipc::shared_memory_object>("fmq_" + shmId + "_m_" + to_string(segment.first), verbose));
} }
} else { } else {
if (verbose) { 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&) { } catch (bie&) {
if (verbose) { 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; 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 */) std::vector<std::pair<std::string, bool>> Monitor::CleanupFull(const ShmId& shmId, bool verbose /* = true */)
{ {
auto result = Cleanup(shmId, verbose); 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; return result;
} }
@@ -621,6 +647,107 @@ std::vector<std::pair<std::string, bool>> Monitor::CleanupFull(const SessionId&
return CleanupFull(shmId, verbose); 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() Monitor::~Monitor()
{ {
if (fSignalThread.joinable()) { if (fSignalThread.joinable()) {

View File

@@ -8,6 +8,8 @@
#ifndef FAIR_MQ_SHMEM_MONITOR_H_ #ifndef FAIR_MQ_SHMEM_MONITOR_H_
#define FAIR_MQ_SHMEM_MONITOR_H_ #define FAIR_MQ_SHMEM_MONITOR_H_
#include <fairmq/UnmanagedRegion.h>
#include <fairlogger/Logger.h> #include <fairlogger/Logger.h>
#include <thread> #include <thread>
@@ -49,6 +51,13 @@ struct BufferDebugInfo
uint64_t fCreationTime; uint64_t fCreationTime;
}; };
struct SegmentConfig
{
uint16_t id;
uint64_t size;
std::string allocationAlgorithm;
};
class Monitor class Monitor
{ {
public: public:
@@ -80,6 +89,23 @@ class Monitor
/// @param verbose output cleanup results to stdout /// @param verbose output cleanup results to stdout
static std::vector<std::pair<std::string, bool>> CleanupFull(const SessionId& sessionId, bool verbose = true); 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) /// @brief Outputs list of messages in shmem (if compiled with FAIRMQ_DEBUG_MODE=ON)
/// @param shmId shmem id /// @param shmId shmem id
static void PrintDebugInfo(const ShmId& shmId); 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>_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>_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>_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>_rgq_<index>` | unmanaged region queue(s) | one of the devices | devices with unmanaged regions |
| `fmq_<shmId>_ms` | shmmonitor status | shmmonitor | devices, shmmonitor | | `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 "Common.h"
#include "Manager.h" #include "Manager.h"
#include "Message.h" #include "Message.h"
#include <fairmq/Socket.h>
#include <FairMQSocket.h> #include <fairmq/Message.h>
#include <FairMQMessage.h>
#include <FairMQLogger.h>
#include <fairmq/tools/Strings.h> #include <fairmq/tools/Strings.h>
#include <fairmq/zeromq/Common.h> #include <fairmq/zeromq/Common.h>
#include <fairlogger/Logger.h>
#include <zmq.h> #include <zmq.h>
#include <atomic> #include <atomic>
@@ -51,7 +51,7 @@ struct ZMsg
class Socket final : public fair::mq::Socket class Socket final : public fair::mq::Socket
{ {
public: 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) : fair::mq::Socket(fac)
, fManager(manager) , fManager(manager)
, fId(id + "." + name + "." + type) , fId(id + "." + name + "." + type)
@@ -117,52 +117,12 @@ class Socket final : public fair::mq::Socket
bool Bind(const std::string& address) override bool Bind(const std::string& address) override
{ {
// LOG(info) << "binding socket " << fId << " on " << address; return zmq::Bind(fSocket, address, fId);
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;
} }
bool Connect(const std::string& address) override bool Connect(const std::string& address) override
{ {
// LOG(info) << "connecting socket " << fId << " on " << address; return zmq::Connect(fSocket, address, fId);
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);
}
} }
int64_t Send(MessagePtr& msg, int timeout = -1) override 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) { } else if (zmq_errno() == EAGAIN || zmq_errno() == EINTR) {
if (fManager.Interrupted()) { if (fManager.Interrupted()) {
return static_cast<int>(TransferCode::interrupted); return static_cast<int>(TransferCode::interrupted);
} else if (ShouldRetry(flags, timeout, elapsed)) { } else if (zmq::ShouldRetry(flags, fTimeout, timeout, elapsed)) {
continue; continue;
} else { } else {
return static_cast<int>(TransferCode::timeout); return static_cast<int>(TransferCode::timeout);
} }
} else { } 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) { } else if (zmq_errno() == EAGAIN || zmq_errno() == EINTR) {
if (fManager.Interrupted()) { if (fManager.Interrupted()) {
return static_cast<int>(TransferCode::interrupted); return static_cast<int>(TransferCode::interrupted);
} else if (ShouldRetry(flags, timeout, elapsed)) { } else if (zmq::ShouldRetry(flags, fTimeout, timeout, elapsed)) {
continue; continue;
} else { } else {
return static_cast<int>(TransferCode::timeout); return static_cast<int>(TransferCode::timeout);
} }
} else { } 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) { } else if (zmq_errno() == EAGAIN || zmq_errno() == EINTR) {
if (fManager.Interrupted()) { if (fManager.Interrupted()) {
return static_cast<int>(TransferCode::interrupted); return static_cast<int>(TransferCode::interrupted);
} else if (ShouldRetry(flags, timeout, elapsed)) { } else if (zmq::ShouldRetry(flags, fTimeout, timeout, elapsed)) {
continue; continue;
} else { } else {
return static_cast<int>(TransferCode::timeout); return static_cast<int>(TransferCode::timeout);
} }
} else { } 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) { } else if (zmq_errno() == EAGAIN || zmq_errno() == EINTR) {
if (fManager.Interrupted()) { if (fManager.Interrupted()) {
return static_cast<int>(TransferCode::interrupted); return static_cast<int>(TransferCode::interrupted);
} else if (ShouldRetry(flags, timeout, elapsed)) { } else if (zmq::ShouldRetry(flags, fTimeout, timeout, elapsed)) {
continue; continue;
} else { } else {
return static_cast<int>(TransferCode::timeout); return static_cast<int>(TransferCode::timeout);
} }
} else { } else {
return HandleErrors(); return zmq::HandleErrors(fId);
} }
} }

View File

@@ -9,17 +9,17 @@
#ifndef FAIR_MQ_SHMEM_TRANSPORTFACTORY_H_ #ifndef FAIR_MQ_SHMEM_TRANSPORTFACTORY_H_
#define FAIR_MQ_SHMEM_TRANSPORTFACTORY_H_ #define FAIR_MQ_SHMEM_TRANSPORTFACTORY_H_
#include "Manager.h"
#include "Common.h" #include "Common.h"
#include "Manager.h"
#include "Message.h" #include "Message.h"
#include "Socket.h"
#include "Poller.h" #include "Poller.h"
#include "UnmanagedRegion.h" #include "Socket.h"
#include "UnmanagedRegionImpl.h"
#include <FairMQTransportFactory.h>
#include <fairmq/ProgOptions.h> #include <fairmq/ProgOptions.h>
#include <FairMQLogger.h>
#include <fairmq/tools/Strings.h> #include <fairmq/tools/Strings.h>
#include <fairmq/TransportFactory.h>
#include <fairlogger/Logger.h>
#include <boost/version.hpp> #include <boost/version.hpp>
@@ -78,7 +78,7 @@ class TransportFactory final : public fair::mq::TransportFactory
LOG(error) << "failed configuring context, reason: " << zmq_strerror(errno); 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) { } catch (boost::interprocess::interprocess_exception& e) {
LOG(error) << "Could not initialize shared memory transport: " << e.what(); LOG(error) << "Could not initialize shared memory transport: " << e.what();
throw std::runtime_error(tools::ToString("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); 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); 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); 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); 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); 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); 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 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 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 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 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); } 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 * * This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, * * GNU Lesser General Public Licence (LGPL) version 3, *
@@ -9,67 +9,375 @@
#ifndef FAIR_MQ_SHMEM_UNMANAGEDREGION_H_ #ifndef FAIR_MQ_SHMEM_UNMANAGEDREGION_H_
#define 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 <fairlogger/Logger.h>
#include <FairMQLogger.h>
#include <boost/interprocess/shared_memory_object.hpp> #include <boost/filesystem.hpp>
#include <boost/interprocess/mapped_region.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 <algorithm> // min
#include <string> #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 namespace fair::mq::shmem
{ {
class Message; struct UnmanagedRegion
class Socket;
class UnmanagedRegion final : public fair::mq::UnmanagedRegion
{ {
friend class Message; friend class Message;
friend class Socket; friend class Manager;
friend class Monitor;
public: UnmanagedRegion(const std::string& shmId, uint16_t id, uint64_t size)
UnmanagedRegion(Manager& manager, : UnmanagedRegion(shmId, size, false, makeRegionConfig(id))
const size_t size, {}
const int64_t userFlags,
RegionCallback callback, UnmanagedRegion(const std::string& shmId, uint64_t size, RegionConfig cfg)
RegionBulkCallback bulkCallback, : UnmanagedRegion(shmId, size, false, std::move(cfg))
const std::string& path, {}
int flags,
FairMQTransportFactory* factory, UnmanagedRegion(const std::string& shmId, RegionConfig cfg)
fair::mq::RegionConfig cfg) : UnmanagedRegion(shmId, cfg.size, false, std::move(cfg))
: FairMQUnmanagedRegion(factory) {}
, fManager(manager)
, fRegion(nullptr) UnmanagedRegion(const std::string& shmId, uint64_t size, bool remote, RegionConfig cfg)
, fRegionId(0) : 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); using namespace boost::interprocess;
fRegion = result.first;
fRegionId = result.second; // 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(const UnmanagedRegion&) = delete;
UnmanagedRegion(UnmanagedRegion&&) = delete; UnmanagedRegion(UnmanagedRegion&&) = delete;
UnmanagedRegion& operator=(const UnmanagedRegion&) = delete; UnmanagedRegion& operator=(const UnmanagedRegion&) = delete;
UnmanagedRegion& operator=(UnmanagedRegion&&) = delete; UnmanagedRegion& operator=(UnmanagedRegion&&) = delete;
void* GetData() const override { return fRegion->get_address(); } void Zero()
size_t GetSize() const override { return fRegion->get_size(); } {
uint16_t GetId() const override { return fRegionId; } memset(fRegion.get_address(), 0x00, fRegion.get_size());
void SetLinger(uint32_t linger) override { fManager.GetRegion(fRegionId)->SetLinger(linger); } }
uint32_t GetLinger() const override { return fManager.GetRegion(fRegionId)->GetLinger(); } 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: private:
Manager& fManager; bool fRemote;
boost::interprocess::mapped_region* fRegion; bool fRemoveOnDestruction;
uint16_t fRegionId; 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 } // 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 sessionName;
string shmId; string shmId;
bool cleanup = false; bool cleanup = false;
bool resetContent = false;
bool selfDestruct = false; bool selfDestruct = false;
bool interactive = false; bool interactive = false;
bool viewOnly = false; bool viewOnly = false;
@@ -97,6 +98,7 @@ int main(int argc, char** argv)
("session,s" , value<string>(&sessionName)->default_value("default"), "Session id") ("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)") ("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") ("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") ("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") ("interactive,i" , value<bool>(&interactive)->implicit_value(true), "Interactive run")
("view,v" , value<bool>(&viewOnly)->implicit_value(true), "Run in view only mode") ("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; return 0;
} }
if (resetContent) {
Monitor::ResetContent(ShmId{shmId});
return 0;
}
if (debug) { if (debug) {
Monitor::PrintDebugInfo(ShmId{shmId}); Monitor::PrintDebugInfo(ShmId{shmId});
return 0; 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