Writing multithreading programs is not trivial. Problems like data races (i.e. concurrently writing and reading of data), minimize explicit sharing of writable data (i.e. if you share data, it should be constant), and synchronization between tasks are only several of the things to keep in mind. For this reason, I provide two basic skeletons for analysis pipeline (one for VMUSB events and one for DDAS built events) to guide the use to a correct way to program for Parallel SpecTcl.
To start with, obtain the Parallel SpecTcl skeleton. This consists of the files in the VMUSBSkel (or DDASSkel) directory of the Skeleton installation tree. Put these files in an empty directory. For example: suppose SpecTcl is installed in /usr/opt/spectcl/6.0-000,
Example 3-1. Obtaining the Parallel SpecTcl skeleton
mkdir parallel cd parallel cp /usr/opt/spectcl/6.0-000/VMUSBSkel/* .
The skeleton consists of several files; Makefile and MySpecTclApp.cpp, CMyProcessor.cpp (which is what you want to modify for your needs) and other files that you won't need to edit.
In editing MySpecTclApp.cpp, you need to consider three things:
The actual event processing pipeline you are going to create. Note that unlike SpecTcl, all pipeline elements (event processors) must have names.
The name of the shared object the Makefile will create.
To setup the event processing pipeline you'll need to provide #include directives to pull in the headers for your event processors. Don't specify absolute paths, take care of that in the Makefile.
Here's an example
Example 3-2. Including event processor headers
... // Here you should include your headers for your event processors. #include <CMyProcessor.h> ...
The command shown is in the skeleton to indicate where to put these #include directives.
Next, locate the class MySpecTclApp
, create and register the event processor(s) you need
in the order you want them called. For example,
Example 3-3. Registering your event processing pipeline
... void CMySpecTclApp::CreateAnalysisPipeline(CAnalyzer& rAnalyzer) { RegisterEventProcessor(*(new CStackUnpacker), "adc-data"); RegisterEventProcessor(*(new CMyProcessor), "example"); } ...
FYI, CStackUnpacker is the basic unpacking class for VMUSBSpecTcl that has to be defined before any user-defined processor to work correctly. For more information on VMUSBSpecTcl please refer to the corresponding section in the SpecTcl manual.
Now let's see what the CMyProcessor class looks like.
Example 3-4. Example of CMyProcessor.h
#ifndef CMYPROCESSOR_H #define CMYPROCESSOR_H #include "EventProcessor.h" #include "ThreadAnalyzer.h" #include <Event.h> #include <TreeParameter.h> class CParameterMapper; class CMyProcessor : public CEventProcessor { public: long var1; CTreeParameter pars; CTreeParameter tmp; CTreeVariable vars; CMyProcessor(); CMyProcessor(const CMyProcessor& rhs); virtual ~CMyProcessor(); virtual CMyProcessor* clone() { return new CMyProcessor(*this); } void setParameterMapper(DAQ::DDAS::CParameterMapper& rParameterMapper){}; virtual Bool_t operator()(const Address_t pEvent, CEvent& rEvent, CAnalyzer& rAnalyzer, CBufferDecoder& rDecoder, BufferTranslator& trans, long thread); virtual Bool_t OnInitialize(); }; #endif
Example 3-5. Example of CMyProcessor.cpp
#include "CMyProcessor.h" #include <CRingBufferDecoder.h> #include <SpecTcl.h> #include <sstream> #include <iomanip> #include <cmath> CMyProcessor::CMyProcessor(): var1(0) {} CMyProcessor::CMyProcessor(const CMyProcessor& rhs): var1(rhs.var1) {} CMyProcessor::~CMyProcessor() {} Bool_t CMyProcessor::operator()(const Address_t pEvent, CEvent& rEvent, CAnalyzer& rAnalyzer, CBufferDecoder& rDecoder, BufferTranslator& trans, long thread) { if (tmp.isValid()){ pars = tmp + vars*var1; } return kfTRUE; } Bool_t CMyProcessor::OnInitialize() { tmp.Initialize("adc1.06"); pars.Initialize("test_var", 16384, 0.0, 16383.0, ""); vars.Initialize("const", 10, ""); if (!tmp.isBound()) tmp.Bind(); if (!pars.isBound()) pars.Bind(); return kfTRUE; }
Once you've edited everything, use make to build the shared object and don't forget to add it to the Makefile.
NB: If you have a more complex structure for your calibration parameters please look at the DDASSkel example for more details.