Conversation
alessandrothea
left a comment
There was a problem hiding this comment.
- Move
Socket*classes outside wiec schema file. - Motivate the use of callbacks outside readout.
schema/appmodel/wiec.schema.xml
Outdated
| @@ -122,9 +122,9 @@ | |||
|
|
|||
| <class name="SocketDataSender"> | |||
There was a problem hiding this comment.
Why is the SocketDataSender in the wiec.schema file?
Am I wrong or the sender It has nothing to do with wiec and TPC cold electronics?
src/CRTFrameBuilderApplication.cpp
Outdated
| // Populate configuration and interfaces | ||
| writer_obj.set_obj("configuration", &writer_conf->config_object()); | ||
| writer_obj.set_objs("connections", {&d2d_conn->config_object()}); | ||
| writer_obj.set_objs("raw_data_callbacks", raw_data_callback_objs); |
There was a problem hiding this comment.
Callbacks, at this stage, are an internal Readout interface. Its use at the interface with detector code needs to be motivated, since performace is typically not an issue at the transmitter.
Switch back to queues if not soundly motivated.
There was a problem hiding this comment.
The model used when separating the callback configuration from the iomanager configuration was that DataReaderModules should use callbacks to communicate with DataHandlerModules. CRTReaderModule is a DataReaderModule, and the symmetry argument is that the SocketWriterModule is taking the place of the DataHandlerModule in the CRTReaderApplication. Using a callback mechanism here also makes sense for the many-to-one communication case, and follows the same pattern as the DRM->DHM communication in the ReadoutApplication.
There was a problem hiding this comment.
I'm not sure if all of you are aware, but it was discussed at many occasions in the past:
Callbacks don't work on fan-in mode! That's why we don't use them for the TPs. In the context of the callee every members should be locked and protected against concurrent access.
Was there a demonstrator or simple test app available to demonstrate that callbacks will work for the CRTReaderModule between the data producers and the SocketWriter?
Seems like this slipped under the radar...
There was a problem hiding this comment.
I'd also argue, that CRTReaderModule is not a DataReaderModule. The SocketReaderModule is, to push data towards the DLHs through sockets (fan-out).
We don't have an appropriate model or classes to represent the CRTReaderModule. Again, these are a new type. The CRTReaderModule is something similar that the Hermes firmware block which is a hardware implementation of a principle or concept, that we do not have in the current configuration model. But for sure, it's not a DataReaderModule. I'm not arguing here about the chosen -unfortunate- name of "CRTReaderModule".
There was a problem hiding this comment.
Summary of where we are at:
We'll follow up with a new issue for the next release to switch to IOMs for the communication between CRT readers and socket writers. For this release, we'll stick to callbacks. We'll switch to IOMs for the communication between CRT readers and socket writers. Keeping the text below for reference.
I still want to share the current flow (that I already shared with some of you in another chat) because it may be very similar to what we would also do with IOMs/consumer threads. (This is to be confirmed, maybe we'll follow a different approach instead, like using MPMC queues.)
Here's the example flow.
- SocketWriter creates 2 Writers, registers 4 Callbacks (Callback1007, Callback1008, Callback1009, Callback1010).
Callback1007 will call Writer1.enqueue()
Callback1008 will call Writer1.enqueue()
Callback1009 will call Writer2.enqueue()
Callback1010 will call Writer2.enqueue() - CRTReaderModule acquires Callback1007, Callback1008, Callback1009, Callback1010. It creates packets for all.
2.1. Callback1007 (calls Writer1.enqueue())
2.2. Callback1008 (calls Writer1.enqueue())
2.3. Callback1009 (calls Writer2.enqueue())
2.4. Callback1010 (calls Writer2.enqueue())
enqueue() pushes a packet to the internal std::queue of Writer. if the queue was empty, it spawns a coroutine (start()).
until the queue is empty, start() fires async_write with the front element; after the write is completed, it pops the element.
as there's only 1 thread, the only synchronization needed here is due to async_write. for a case like this:
- CRTReader calls Callback1007 (Writer1.enqueue()).
enqueue()pushes the packet to the internal queue. the queue was empty, so it spawns a coroutine (start()).- Coroutine: the queue is not empty, it fires
async_writewith the front element. - while
async_writeis ongoing, CRTReader calls Callback1008 (Writer1.enqueue()). enqueue()pushes the packet to the internal queue. the queue was not empty, so it doesn't spawn a new coroutine. it returns.- Coroutine: the write is completed, it pops the element.
- Coroutine: the queue is still not empty (a new element was pushed), so it wants to fire an
async_writewith the front element. - let's say, this time, CRTReader calls Callback1007 (Writer1.enqueue()) before the coroutine fires the async write.
- this
enqueue()will have to wait until the coroutine releases the strand (which happens either when it suspends/async_writeor completes). this is how strand works, it synchronizes the coroutine and the lambda function insideenqueue().
- strand acts like a mutex for the
std::queue. std::queueis needed to prevent firing simultaneousasync_writes on the same socket.
test/apps/generate_modules_test.cxx
Outdated
| if (socketwriter_module != nullptr) { | ||
| auto callback_conf = socketwriter_module->get_raw_data_callback(); | ||
| if (callback_conf != nullptr) { | ||
| auto callback_confs = socketwriter_module->get_raw_data_callbacks(); |
There was a problem hiding this comment.
See previous comment about callbacks.
…dte/crt_topology_change
|
Closing this PR since the branch has been merged to the |
20.03 Updates:
CRTReader is not a
DataReader. It's not an interface to read DAQ frames from a front-end (likeDPDKReader) and send them to data handlers. Instead, it builds DAQ frames from raw electronics output. It's similar to Hermes FW, a functional block that prepares data and passes it on.The new name,
CRTFrameBuilder, eliminates confusion with standardDataReadermodules and accurately reflects its role as a builder of DAQ frames rather than a reader.appmodel PR dte/crt_topology_change
daqsystemtest PR dte/crt_topology_change
crtmodules PR dte/crt_topology_change
asiolibs PR dte/crt_topology_change
daqconf PR dte/crt_cb_update
datahandlinglibs PR dte/cb_acquire
fdreadoutmodules PR dte/crt_rates
Prior to this development, socket connection info resided in reader and writer configurations. Now, there is SocketDetectorToDaqConnection which has SocketDataSenders and SocketReceiver; SocketDataSender has the info socket type (TCP/UDP), local port and remote port; plus we make use of their NetworkInterface.ip_address to interpret them as local IP and remote IP.
CRT readers communicate with socket writers via IOM.
Socket readers communicate with DHLs via callbacks.
To try out:
drunc-unified-shell ssh-standalone config/daqsystemtest/example-configs.data.xml local-socket-1x1-config ${USER}-local-testgenerate_modules_test local-socket-1x1-config crt-data-source-01 config/daqsystemtest/example-configs.data.xmlgenerate_modules_test local-socket-1x1-config socket-ru-01 config/daqsystemtest/example-configs.data.xmlType of change
Testing checklist
pytest -s minimal_system_quick_test.py)dunedaq_integtest_bundle.sh)Further checks
dbt-build --lint, and/or see https://dune-daq-sw.readthedocs.io/en/latest/packages/styleguide/)