// Multi-threaded version of a test program to send a single ECHO primitive #include using namespace std; #include #include "RodModule.h" #include "primParams.h" #include "RCCVmeInterface.h" #include "parameters.h" #include "PPBuffer.h" #include "PPThread.h" #include "PPThreadAttr.h" #include "PPMutex.h" #include "PPCondition.h" /*! @class ParamList * * @brief This is a class for passing parameter addresses to threads * * This class is used by threads to pass addresses of data to other threads. * * The mutex m_coutMutex is used to avoid interleaving when printing output from several * threads at the same time. *. * The boolean data member m_finished is used to signal that the program is ending. Threads * that are blocked waiting for a buffer are sent an empty buffer and should test m_finished. * If it is "true" they terminate themselves. * * Note that another way I could have done this would have been to use a separate ParamList object * for each thread, but it seems easier and more versatile to have all the links in one object. * * @author Tom Meyer (meyer@iastate.edu) */ using namespace SctPixelRod; class ParamList { public: ParamList(){m_finished = false;}; ParamList(RodModule* rod, PPBuffer* replyBuffer, PPBuffer* textBuffer, PPBuffer* errBuffer, PPMutex* coutMutex){ m_rod = rod; m_replyBuffer = replyBuffer; m_textBuffer = textBuffer; m_errBuffer = errBuffer; m_coutMutex = coutMutex; m_finished = false; }; ParamList( const ParamList&){}; ParamList& operator=(const ParamList&){}; ~ParamList(){}; void setRod(RodModule* rod) {m_rod = rod; return;}; void setReplyBuffer(PPBuffer* replyBuffer) {m_replyBuffer=replyBuffer; return;}; void setTextBuffer(PPBuffer* textBuffer) {m_textBuffer=textBuffer; return;}; void setErrBuffer(PPBuffer* errBuffer) {m_errBuffer=errBuffer; return;}; void setCoutMutex(PPMutex* coutMutex) {m_coutMutex = coutMutex; return;}; void setFinished(bool finished) {m_finished = finished; return;}; RodModule* getRod() {return m_rod;}; PPBuffer* getReplyBuffer() {return m_replyBuffer;} PPBuffer* getTextBuffer() {return m_textBuffer;} PPBuffer* getErrBuffer() {return m_errBuffer;} PPMutex* getCoutMutex() {return m_coutMutex;} bool getFinished() {return m_finished;}; private: RodModule* m_rod; PPBuffer* m_replyBuffer; PPBuffer* m_textBuffer; PPBuffer* m_errBuffer; PPMutex* m_coutMutex; bool m_finished; }; void *bkgStart(void *arg) { /*! This is the startup routine for the background thread. * It loops waiting for ROD activity and calls the approriate ROD handler methods. * It also checks for text buffers and reads them out if they exist. */ long myTextLength; // Actual length of text message TEXT_BUFFER_TYPE myTextType; // Buffer type for latest message unsigned long baseAddress, txtBuffSize; MdspMemoryMap* mdspMap; // Create a buffer for temporary storage of text messages mdspMap = rod0->getMdspMap(); txtBuffSize = mdspMap->txtBuffSize(0); char * myTextBuffer = new char[txtBuffSize]; PrimState returnPState; TextBuffState returnTState; // Get buffer addresses ParamList* myParamList; myParamList = static_cast(arg); RodModule* rod0 = myParamList->getRod(); PPBuffer* userBuffer = myParamList->getReplyBuffer(); PPBuffer* textBuffer = myParamList->getTextBuffer(); // Start of idle loop while (1) { do { if (myParamList->getFinished()) break; returnPState = rod0->primHandler(); returnTState = rod0->textHandler(); if (returnTState == TEXT_RQ_SET) { do { returnTState = rod0->textHandler(); } while (returnTState != TEXT_READOUT); rod0->getTextBuffer(myTextBuffer, myTextLength, myTextType); rod0->clearTextBuffer(); rod0->sleep(50); textBuffer->fillBuffer(myTextBuffer, myTextLength); } } while (returnPState != PRIM_EXECUTING); do { try { returnPState = rod0->primHandler(); } catch (RodException &r) { cerr << r.getDescriptor() <<", " << r.getData1() << ", " << r.getData2() << '\n'; } } while (returnPState != PRIM_WAITING); // Retrieve output buffer RodOutList* outList = rod0->getOutList(); UINT32 outLength = UINT32(outList->getLength()); unsigned long* outBody = outList->getBody(); // Copy to user buffer userBuffer->fillBuffer(outBody, outLength); // end of idle loop if (!myParamList->getFinished()) break; }; delete [] myTextBuffer; return 0; } void* errStart(void *arg) { /*! This is the startup routine for the error-handling thread. It waits for * the error buffer to be filled by another thread and then prints it to cerr. * A more sophisicated version might report it to a centralized messageing * system such as MRS. */ using namespace SctPixelRod; // Get buffer address ParamList* myParamList; myParamList = static_cast(arg); PPBuffer* errBuffer = myParamList->getErrBuffer(); // loop waiting for buffer to be filled, then print it out while (1) { errBuffer->waitForFilled(); long totItems = errBuffer->getTotItems(); if (totItems > 0) { char* errArray = new char[totItems]; errBuffer->dumpBuffer(errArray, totItems); for (int i=0; igetFinished()) break; } return 0; } void* textStart(void *arg) { /*! This is the startup routine for the thread to handle text buffers. It waits for * a buffer to be filled by another thread and then prints it to cout, using a * mutex to avoid interleaving output with possible printout from other threads. * A more sophisicated version might send the buffer contents to a centralized messageing * system such as MRS. */ using namespace SctPixelRod; // Get buffer address ParamList* myParamList; myParamList = static_cast(arg); PPBuffer* textBuffer = myParamList->getTextBuffer(); PPMutex* coutMutex = myParamList->getCoutMutex(); // loop waiting for buffer to be filled, then print it out while (1) { textBuffer->waitForFilled(); long totItems = textBuffer->getTotItems(); if (totItems > 0) { char* textArray = new char[totItems]; textBuffer->dumpBuffer(textArray, totItems); coutMutex->lock(); for (int i=0; iunlock(); delete [] textArray; } if (!myParamList->getFinished()) break; } return 0; } void* userStart(void *arg) { /*! This is the startup routine for the user thread. It waits for * the user (event) buffer to be filled by another thread and then print it to cout. * A more sophisicated version might do analysis and histogramming of the data. */ using namespace SctPixelRod; // Get buffer addresses ParamList* myParamList; myParamList = static_cast(arg); PPBuffer* userBuffer = myParamList->getReplyBuffer(); PPMutex* coutMutex = myParamList->getCoutMutex(); // loop waiting for buffer to be filled, then fetch it while (1) { userBuffer->waitForFilled(); UINT32 outLength = userBuffer->getTotItems(); if (outLength > 0) { unsigned long* outBody = new unsigned long[outLength]; userBuffer->dumpBuffer(outBody, outLength); // Print results (User processing of outList) UINT32 outIndex = UINT32(outBody[1]); UINT32 outNumPrims = outBody[2]; UINT32 outPrimVersion = outBody[3]; coutMutex->lock(); cout << "outLength = " << outLength << ", outIndex = " << outIndex << ", outNumPrims = " << outNumPrims << ", outPrimVersion = " << outPrimVersion <<'\n'; int outPtr = 4; for (unsigned int j=0; junlock(); } if (!myParamList->getFinished()) break; } return 0; } int main(int argc, char *argv[]) { /*! This is the inital routine for the main thread. It creates the following * four threads: * - a background thread that loops waiting for activity on the ROD * - a user thread to process events passed to it by the background thread * - a text thread to print text buffers encountered by the background thread * - an error thread to print error messages to cerr. At present this is not used. * * This program does the same thing as EchoTest, but does it using multiple threads. * It is intended as an example for future threaded programs, but is not necessarily * the way we will structure the threads in the future. */ using namespace SctPixelRod; const unsigned long mapSize=0xc00040; // VME map size RodPrimList primList(1); // Primitive List long numSlaves = 4; std::string fileName(""), option; int slot = -1; unsigned long baseAddress, txtBuffSize; MdspMemoryMap* mdspMap; if (argc > 1) { for (int i=1; i> slot; while ((slot < 1) || (slot > 21)) { cout << "Slot number out or range [1:21], re-enter: "; cin >> slot; } } baseAddress = slot << 24; // Create VME interface RCCVmeInterface *vme1 = new RCCVmeInterface(); // Create RodModule and initialize it RodModule* rod0 = new RodModule(baseAddress, mapSize, *vme1, numSlaves); try{ rod0->initialize(); } catch (HpiException &h) { hex(cout); cerr << h.getDescriptor() << '\n'; cerr << "calcAddr: " << h.getCalcAddr() << ", readAddr: " << h.getReadAddr() << '\n'; dec(cout); }; // Create shared buffers and buffer list PPBuffer textBuffer(1024); PPBuffer userBuffer(1024); PPBuffer errBuffer(1025); PPMutex bkgMutex, errMutex, textMutex, userMutex; PPMutex coutMutex; ParamList myParamList; myParamList.setRod(rod0); myParamList.setTextBuffer(&textBuffer); myParamList.setReplyBuffer(&userBuffer); myParamList.setErrBuffer(&errBuffer); myParamList.setCoutMutex(&coutMutex); // Start background, error, text, and user threads, waiting for them to start before proceeding PPThreadAttr defaultAttr; PPThread bkgThread(&defaultAttr, bkgStart, &myParamList); PPThread errThread(&defaultAttr, errStart, &myParamList); PPThread textThread(&defaultAttr, textStart, &myParamList); PPThread userThread(&defaultAttr, userStart, &myParamList); // Create and Send a simple ECHO primitive long dataLength; coutMutex.lock(); cout << "Enter number of data words: "; cin >> dataLength; coutMutex.unlock(); long* echoData = new long[dataLength]; for (long i=0; isendPrimList(&primList); } catch (HpiException &h) { hex(cout); cerr << h.getDescriptor() << '\n'; cerr << "calcAddr: " << h.getCalcAddr() << ", readAddr: " << h.getReadAddr() << '\n'; dec(cout); }; delete [] echoData; char reply; coutMutex.lock(); cout << "Press any key followed by to finish: "; cin >> reply; coutMutex.unlock(); // Clean up: clear primList, delete primitive, and the outList primList.clear(); delete echo; rod0->deleteOutList(); // Flush buffers, which sets BUFFER_FILLED condition variable myParamList.setFinished(true); userBuffer.close(); textBuffer.close(); errBuffer.close(); // Delete the ROD and VME objects before exiting delete rod0; delete vme1; return 0; }