From e82101b9c6df47422a069452491a38fdc49c300c Mon Sep 17 00:00:00 2001 From: Marty Kraimer Date: Fri, 3 Dec 2010 15:40:02 -0500 Subject: [PATCH] Implemented Queue. --- documentation/pvDataCpp.html | 73 ++++++++----- pvDataApp/misc/Makefile | 3 + pvDataApp/misc/queue.h | 53 ++++++++++ pvDataApp/misc/queueVoid.cpp | 175 +++++++++++++++++++++++++++++++ pvDataApp/misc/queueVoid.h | 62 +++++++++++ pvDataApp/miscTest/Makefile | 4 + pvDataApp/miscTest/testQueue.cpp | 162 ++++++++++++++++++++++++++++ pvDataApp/pv/pvData.h | 2 +- 8 files changed, 504 insertions(+), 30 deletions(-) create mode 100644 pvDataApp/misc/queue.h create mode 100644 pvDataApp/misc/queueVoid.cpp create mode 100644 pvDataApp/misc/queueVoid.h create mode 100644 pvDataApp/miscTest/testQueue.cpp diff --git a/documentation/pvDataCpp.html b/documentation/pvDataCpp.html index 1bc27ea..002ccc8 100644 --- a/documentation/pvDataCpp.html +++ b/documentation/pvDataCpp.html @@ -15,8 +15,8 @@ Overview

TODO

CONTENTS @@ -3315,33 +3315,48 @@ private:

Queue

-

This provides a queue which has an immutable capacity, which is specified -when the queue is created. When the queue is full the user code is expected -to keep using the current el;ement until a new free element becomes avalable. -This is used by pvData.monitor.

-
NOT IMPLEMENTED
+

This provides a queue which has an immutable capacit. When the queue is +full the user code is expected to keep using the current element until a new +free element becomes avalable.

+
template <typename T>
+class QueueElement : private QueueElementVoid {
+public:
+    QueueElement(T *object) : QueueElementVoid(static_cast<void *>(object)){}
+    ~QueueElement() {}
+    T *getObject() { return static_cast<T *>(QueueElementVoid::getObject());}
+    friend  class Queue<T>;
+};
 
-

A queueCreate instance is created via a call like the following:

-
 QueueCreate<MyObject> queueCreate = new QueueCreate<MyObject>();
+template <typename T> +class Queue : private QueueVoid { +public: + Queue(QueueElement<T> **array,int number) + : QueueVoid((QueueElementVoid**)array,number) + //: QueueVoid(static_cast<QueueElementVoid**>(array),number) + {} + ~Queue() {} + void clear() {QueueVoid::clear();} + int getNumberFree() {return QueueVoid::getNumberFree();} + int capacity() {return QueueVoid::capacity();} + QueueElement<T> *getFree() { + return static_cast<QueueElement<T> *>(QueueVoid::getFree());} + void setUsed(QueueElement<T> *queueElement) { + QueueVoid::setUsed(static_cast<QueueElementVoid *>(queueElement));} + QueueElement<T> *getUsed() { + return static_cast<QueueElement<T> *>(QueueVoid::getUsed());} + void releaseUsed(QueueElement<T> *queueElement) { + QueueVoid::releaseUsed(static_cast<QueueElementVoid *>(queueElement));} +};
-

Once a queueCreate is available a queue instance is created via code like -the following:

-
Queue<MyObject> queue create(MyObject[] myObjects) {
-    QueueElement<MyObject>[] queueElements = new QueueElement[length];
-    for(int i=0; i<length; i++) {
-        QueueElement<MonitorElement> queueElement =
-                 queueCreate.createQueueElement(myObjects[i);
-        queueElements[i] = queueElement;
-    }
-    return queueCreate.create(queueElements);
-}
+

miscTest/queueTest.cpp provides an example of how to define queueElements +and a queue.

The queue methods are:

clear
Make the queue empty.
getNumberFree
-
Get the number of fee elements in the queue.
+
Get the number of free elements in the queue.
capacity
Get the capacity, i.e. the maximun number of elements the queue can hold.
@@ -3362,19 +3377,19 @@ the following:

A producer calls getFree and setUsed via code like the following:

-
   MyObject getFree() {
-       QueueElement<MyObject> queueElement = queue.getFree();
-       if(queueElement==null) return null;
-       return queueElement.getObject();
+
   MyObject *getFree() {
+       QueueElement<MyObject> *queueElement = queue->getFree();
+       if(queueElement==0) return 0;
+       return queueElement->getObject();
   }

A consumer calls getUsed and releaseUsed via code like the following:

     while(true) {
-         QueueElement<MyObject> queueElement = queue.getUsed();
-         if(queueElement==null) break;
-         MyObject myObject = queueElement.getObject();
+         QueueElement<MyObject> *queueElement = queue->getUsed();
+         if(queueElement==0) break;
+         MyObject *myObject = queueElement->getObject();
          // do something with myObject
-         queue.releaseUsed(queueElement);
+         queue->releaseUsed(queueElement);
      }

diff --git a/pvDataApp/misc/Makefile b/pvDataApp/misc/Makefile index 1d44f01..91fc38a 100644 --- a/pvDataApp/misc/Makefile +++ b/pvDataApp/misc/Makefile @@ -20,6 +20,8 @@ INC += showConstructDestruct.h INC += timeStamp.h INC += timeFunction.h INC += timer.h +INC += queueVoid.h +INC += queue.h LIBSRCS += byteBuffer.cpp LIBSRCS += bitSet.cpp @@ -33,6 +35,7 @@ LIBSRCS += executor.cpp LIBSRCS += timeStamp.cpp LIBSRCS += timeFunction.cpp LIBSRCS += timer.cpp +LIBSRCS += queueVoid.cpp LIBRARY=pvMisc diff --git a/pvDataApp/misc/queue.h b/pvDataApp/misc/queue.h new file mode 100644 index 0000000..d7ac146 --- /dev/null +++ b/pvDataApp/misc/queue.h @@ -0,0 +1,53 @@ +/* queue.h */ +/** + * Copyright - See the COPYRIGHT that is included with this distribution. + * EPICS pvDataCPP is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + */ +#ifndef QUEUE_H +#define QUEUE_H +#include "queueVoid.h" +namespace epics { namespace pvData { + +template +class Queue; + +template +class QueueElement; + +template +class QueueElement : private QueueElementVoid { +public: + QueueElement(T *object) : QueueElementVoid(static_cast(object)){} + ~QueueElement() {} + T *getObject() { return static_cast(QueueElementVoid::getObject());} + friend class Queue; +}; + +template +class Queue : private QueueVoid { +public: + Queue(QueueElement **array,int number) + : QueueVoid((QueueElementVoid**)array,number) + //: QueueVoid(static_cast(array),number) + {} + ~Queue() {} + void clear() {QueueVoid::clear();} + int getNumberFree() {return QueueVoid::getNumberFree();} + int capacity() {return QueueVoid::capacity();} + QueueElement *getFree() { + return static_cast *>(QueueVoid::getFree());} + void setUsed(QueueElement *queueElement) { + QueueVoid::setUsed(static_cast(queueElement));} + QueueElement *getUsed() { + return static_cast *>(QueueVoid::getUsed());} + void releaseUsed(QueueElement *queueElement) { + QueueVoid::releaseUsed(static_cast(queueElement));} +}; + + +}} +#endif /* QUEUE_H */ + + + diff --git a/pvDataApp/misc/queueVoid.cpp b/pvDataApp/misc/queueVoid.cpp new file mode 100644 index 0000000..6a5af79 --- /dev/null +++ b/pvDataApp/misc/queueVoid.cpp @@ -0,0 +1,175 @@ +/* queueVoid.cpp */ +/** + * Copyright - See the COPYRIGHT that is included with this distribution. + * EPICS pvDataCPP is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + */ +#include +#include +#include +#include +#include +#include + +#include "lock.h" +#include "pvType.h" +#include "queueVoid.h" + +namespace epics { namespace pvData { + +static volatile int64 totalElementConstruct = 0; +static volatile int64 totalElementDestruct = 0; +static volatile int64 totalQueueConstruct = 0; +static volatile int64 totalQueueDestruct = 0; +static Mutex *globalMutex = 0; + +static int64 getTotalNodeConstruct() +{ + Lock xx(globalMutex); + return totalElementConstruct; +} + +static int64 getTotalNodeDestruct() +{ + Lock xx(globalMutex); + return totalElementDestruct; +} + +static int64 getTotalListConstruct() +{ + Lock xx(globalMutex); + return totalQueueConstruct; +} + +static int64 getTotalListDestruct() +{ + Lock xx(globalMutex); + return totalQueueDestruct; +} + +static ConstructDestructCallback *pCDCallbackQueueNode; +static ConstructDestructCallback *pCDCallbackQueue; + +static void initPvt() +{ + static Mutex mutex = Mutex(); + Lock xx(&mutex); + if(globalMutex==0) { + globalMutex = new Mutex(); + pCDCallbackQueueNode = new ConstructDestructCallback( + "queueElement", + getTotalNodeConstruct,getTotalNodeDestruct,0); + + pCDCallbackQueue = new ConstructDestructCallback( + "queue", + getTotalListConstruct,getTotalListDestruct,0); + } +} + + +QueueElementVoid::QueueElementVoid(void *object) +: object(object) +{ + initPvt(); + Lock xx(globalMutex); + totalElementConstruct++; +} + + +QueueElementVoid::~QueueElementVoid() +{ + Lock xx(globalMutex); + totalElementDestruct++; +} + +ConstructDestructCallback *QueueElementVoid::getConstructDestructCallback() +{ + initPvt(); + return pCDCallbackQueueNode; +} + +void *QueueElementVoid::getObject() { + return object; +} + +QueueVoid::QueueVoid(QueueElementVoidPtrArray array,int number) +: array(array),number(number), + numberFree(number),numberUsed(0), + nextGetFree(0),nextSetUsed(), + nextGetUsed(0),nextReleaseUsed(0) +{ + initPvt(); + Lock xx(globalMutex); + totalQueueConstruct++; +} + +QueueVoid::~QueueVoid() +{ + Lock xx(globalMutex); + totalQueueDestruct++; +} + +ConstructDestructCallback *QueueVoid::getConstructDestructCallback() +{ + initPvt(); + return pCDCallbackQueue; +} + +void QueueVoid::clear() +{ + numberFree = number; + numberUsed = 0; + nextGetFree = 0; + nextSetUsed = 0; + nextGetUsed = 0; + nextReleaseUsed = 0; +} + +int QueueVoid::getNumberFree() +{ + return numberFree; +} + +int QueueVoid::capacity() +{ + return number; +} + +QueueElementVoid * QueueVoid::getFree() +{ + if(numberFree==0) return 0; + numberFree--; + QueueElementVoid *queueElement = array[nextGetFree++]; + if(nextGetFree>=number) nextGetFree = 0; + return queueElement; +} + +void QueueVoid::setUsed(QueueElementVoid *queueElement) +{ + if(queueElement!=array[nextSetUsed++]) { + throw std::logic_error(String("not correcect queueElement")); + } + numberUsed++; + if(nextSetUsed>=number) nextSetUsed = 0; +} + +QueueElementVoid * QueueVoid::getUsed() +{ + if(numberUsed==0) return 0; + QueueElementVoid *queueElement = array[nextGetUsed++]; + if(nextGetUsed>=number) nextGetUsed = 0; + return queueElement; +} + +void QueueVoid::releaseUsed(QueueElementVoid *queueElement) +{ + if(queueElement!=array[nextReleaseUsed++]) { + throw std::logic_error(String( + "not queueElement returned by last call to getUsed")); + } + if(nextReleaseUsed>=number) nextReleaseUsed = 0; + numberUsed--; + numberFree++; +} + +}} diff --git a/pvDataApp/misc/queueVoid.h b/pvDataApp/misc/queueVoid.h new file mode 100644 index 0000000..859e57d --- /dev/null +++ b/pvDataApp/misc/queueVoid.h @@ -0,0 +1,62 @@ +/* queueVoid.h */ +/** + * Copyright - See the COPYRIGHT that is included with this distribution. + * EPICS pvDataCPP is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + */ +#include "showConstructDestruct.h" +#ifndef QUEUEVOID_H +#define QUEUEVOID_H +namespace epics { namespace pvData { + +class QueueVoid; +class QueueElementVoid; + +typedef QueueElementVoid * QueueElementVoidPtr; +typedef QueueElementVoidPtr * QueueElementVoidPtrArray; + +class QueueElementVoid { +public: + ~QueueElementVoid(); + static ConstructDestructCallback *getConstructDestructCallback(); + void *getObject(); +protected: + QueueElementVoid(void *object); +private: + void *object; + friend class QueueVoid; +}; + +typedef class QueueElementVoid * QueueElementVoidArray; + +class QueueVoid { +public: + ~QueueVoid(); + static ConstructDestructCallback *getConstructDestructCallback(); + void clear(); + int getNumberFree(); + int capacity(); + QueueElementVoid *getFree(); + void setUsed(QueueElementVoid *queueElement); + QueueElementVoid *getUsed(); + void releaseUsed(QueueElementVoid *queueElement); +protected: + QueueVoid(QueueElementVoidPtrArray array,int number); +private: + friend class QueueElementVoid; + QueueElementVoidPtrArray array; + int number; + int numberFree; + int numberUsed; + int nextGetFree; + int nextSetUsed; + int nextGetUsed; + int nextReleaseUsed; +}; + + +}} +#endif /* QUEUEVOID_H */ + + + diff --git a/pvDataApp/miscTest/Makefile b/pvDataApp/miscTest/Makefile index e41f91c..3192a46 100644 --- a/pvDataApp/miscTest/Makefile +++ b/pvDataApp/miscTest/Makefile @@ -10,6 +10,10 @@ PROD_HOST += testLinkedList testLinkedList_SRCS += testLinkedList.cpp testLinkedList_LIBS += pvMisc Com +PROD_HOST += testQueue +testQueue_SRCS += testQueue.cpp +testQueue_LIBS += pvMisc Com + PROD_HOST += testThread testThread_SRCS += testThread.cpp testThread_LIBS += pvMisc Com diff --git a/pvDataApp/miscTest/testQueue.cpp b/pvDataApp/miscTest/testQueue.cpp new file mode 100644 index 0000000..c0ab94f --- /dev/null +++ b/pvDataApp/miscTest/testQueue.cpp @@ -0,0 +1,162 @@ +/** + * Copyright - See the COPYRIGHT that is included with this distribution. + * EPICS pvDataCPP is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + */ +/* + * testQueue.cpp + * + * Created on: 2010.12 + * Author: Marty Kraimer + */ + +#include +#include +#include +#include +#include + +#include + +#include "lock.h" +#include "timeStamp.h" +#include "queue.h" +#include "showConstructDestruct.h" +#include "event.h" +#include "thread.h" +#include "executor.h" + + +using namespace epics::pvData; + +struct Data { + int a; + int b; +}; + + +static int numElements = 5; +typedef QueueElement DataElement; +typedef Queue DataQueue; + +class Sink : public Runnable { +public: + Sink(DataQueue *queue,FILE *auxfd); + ~Sink(); + void stop(); + void look(); + virtual void run(); +private: + DataQueue *queue; + FILE *auxfd; + bool isStopped; + Event *wait; + Event *stopped; + Event *waitReturn; + Event *waitEmpty; + Thread *thread; +}; + +Sink::Sink(DataQueue *queue,FILE *auxfd) +: queue(queue), + auxfd(auxfd), + isStopped(false), + wait(new Event()), + stopped(new Event()), + waitReturn(new Event()), + waitEmpty(new Event()), + thread(new Thread(String("sink"),middlePriority,this)) +{ +} + +Sink::~Sink() { + delete thread; + delete waitEmpty; + delete waitReturn; + delete stopped; + delete wait; +} + +void Sink::stop() +{ + isStopped = true; + wait->signal(); + stopped->wait(); +} + +void Sink::look() +{ + wait->signal(); + waitEmpty->wait(); +} + +void Sink::run() +{ + while(!isStopped) { + wait->wait(); + if(isStopped) break; + while(true) { + DataElement *element = queue->getUsed(); + if(element==0) { + waitEmpty->signal(); + break; + } + Data *data = element->getObject(); + fprintf(auxfd," sink a %d b %d\n",data->a,data->b); + queue->releaseUsed(element); + } + } + stopped->signal(); +} + +static void testBasic(FILE * fd,FILE *auxfd ) { + Data dataArray[numElements]; + DataElement *array[numElements]; + for(int i=0; igetFree(); + if(element==0) break; + Data *data = element->getObject(); + fprintf(auxfd,"source a %d b %d\n",data->a,data->b); + queue->setUsed(element); + } + sink->look(); + // now alternate + for(int i=0; igetFree(); + assert(element!=0); + Data *data = element->getObject(); + fprintf(auxfd,"source a %d b %d\n",data->a,data->b); + queue->setUsed(element); + sink->look(); + } + sink->stop(); + delete sink; + delete queue; + for(int i=0; i1) fileName = argv[1]; + FILE * fd = stdout; + if(fileName!=0 && fileName[0]!=0) { + fd = fopen(fileName,"w+"); + } + char *auxFileName = 0; + if(argc>2) auxFileName = argv[2]; + FILE *auxfd = stdout; + if(auxFileName!=0 && auxFileName[0]!=0) { + auxfd = fopen(auxFileName,"w+"); + } + testBasic(fd,auxfd); + getShowConstructDestruct()->constuctDestructTotals(fd); + return (0); +} + diff --git a/pvDataApp/pv/pvData.h b/pvDataApp/pv/pvData.h index d190038..7b5bd5c 100644 --- a/pvDataApp/pv/pvData.h +++ b/pvDataApp/pv/pvData.h @@ -57,7 +57,7 @@ typedef int32 * IntArray; typedef int64 * LongArray; typedef float * FloatArray; typedef double * DoubleArray; -//typedef String * StringArray; alreadt defined in pvType.h +//typedef String * StringArray; already defined in pvType.h class PVAuxInfo : private NoDefaultMethods { public: