From 3c060afedb70cbda2da1fc707b1d63c4170e794a Mon Sep 17 00:00:00 2001 From: Marty Kraimer Date: Wed, 8 Dec 2010 07:10:01 -0500 Subject: [PATCH] continuation of last commit --- pvDataApp/misc/bitSet.cpp | 386 +++++++++++++++++++++++ pvDataApp/misc/bitSet.h | 310 ++++++++++++++++++ pvDataApp/misc/byteBuffer.cpp | 198 ++++++++++++ pvDataApp/misc/byteBuffer.h | 330 +++++++++++++++++++ pvDataApp/misc/epicsException.h | 231 ++++++++++++++ pvDataApp/misc/event.cpp | 107 +++++++ pvDataApp/misc/event.h | 33 ++ pvDataApp/misc/executor.cpp | 187 +++++++++++ pvDataApp/misc/executor.h | 37 +++ pvDataApp/misc/linkedList.h | 88 ++++++ pvDataApp/misc/linkedListVoid.cpp | 300 ++++++++++++++++++ pvDataApp/misc/linkedListVoid.h | 73 +++++ pvDataApp/misc/lock.h | 38 +++ pvDataApp/misc/messageQueue.cpp | 166 ++++++++++ pvDataApp/misc/messageQueue.h | 49 +++ pvDataApp/misc/noDefaultMethods.h | 27 ++ pvDataApp/misc/pvType.h | 28 ++ pvDataApp/misc/queue.h | 54 ++++ pvDataApp/misc/queueVoid.cpp | 184 +++++++++++ pvDataApp/misc/queueVoid.h | 61 ++++ pvDataApp/misc/requester.cpp | 20 ++ pvDataApp/misc/requester.h | 28 ++ pvDataApp/misc/serialize.h | 54 ++++ pvDataApp/misc/serializeHelper.cpp | 125 ++++++++ pvDataApp/misc/serializeHelper.h | 103 ++++++ pvDataApp/misc/showConstructDestruct.cpp | 127 ++++++++ pvDataApp/misc/showConstructDestruct.h | 53 ++++ pvDataApp/misc/thread.cpp | 210 ++++++++++++ pvDataApp/misc/thread.h | 53 ++++ pvDataApp/misc/timeFunction.cpp | 54 ++++ pvDataApp/misc/timeFunction.h | 30 ++ pvDataApp/misc/timeStamp.cpp | 183 +++++++++++ pvDataApp/misc/timeStamp.h | 63 ++++ pvDataApp/misc/timer.cpp | 310 ++++++++++++++++++ pvDataApp/misc/timer.h | 53 ++++ 35 files changed, 4353 insertions(+) create mode 100644 pvDataApp/misc/bitSet.cpp create mode 100644 pvDataApp/misc/bitSet.h create mode 100644 pvDataApp/misc/byteBuffer.cpp create mode 100644 pvDataApp/misc/byteBuffer.h create mode 100644 pvDataApp/misc/epicsException.h create mode 100644 pvDataApp/misc/event.cpp create mode 100644 pvDataApp/misc/event.h create mode 100644 pvDataApp/misc/executor.cpp create mode 100644 pvDataApp/misc/executor.h create mode 100644 pvDataApp/misc/linkedList.h create mode 100644 pvDataApp/misc/linkedListVoid.cpp create mode 100644 pvDataApp/misc/linkedListVoid.h create mode 100644 pvDataApp/misc/lock.h create mode 100644 pvDataApp/misc/messageQueue.cpp create mode 100644 pvDataApp/misc/messageQueue.h create mode 100644 pvDataApp/misc/noDefaultMethods.h create mode 100644 pvDataApp/misc/pvType.h 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/misc/requester.cpp create mode 100644 pvDataApp/misc/requester.h create mode 100644 pvDataApp/misc/serialize.h create mode 100644 pvDataApp/misc/serializeHelper.cpp create mode 100644 pvDataApp/misc/serializeHelper.h create mode 100644 pvDataApp/misc/showConstructDestruct.cpp create mode 100644 pvDataApp/misc/showConstructDestruct.h create mode 100644 pvDataApp/misc/thread.cpp create mode 100644 pvDataApp/misc/thread.h create mode 100644 pvDataApp/misc/timeFunction.cpp create mode 100644 pvDataApp/misc/timeFunction.h create mode 100644 pvDataApp/misc/timeStamp.cpp create mode 100644 pvDataApp/misc/timeStamp.h create mode 100644 pvDataApp/misc/timer.cpp create mode 100644 pvDataApp/misc/timer.h diff --git a/pvDataApp/misc/bitSet.cpp b/pvDataApp/misc/bitSet.cpp new file mode 100644 index 0000000..0948c55 --- /dev/null +++ b/pvDataApp/misc/bitSet.cpp @@ -0,0 +1,386 @@ +/* bitSet.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 "string.h" +#include "stdio.h" +#include "bitSet.h" + +namespace epics { namespace pvData { + + BitSet::BitSet() : words(0), wordsLength(0), wordsInUse(0) { + initWords(BITS_PER_WORD); + } + + BitSet::BitSet(uint32 nbits) : words(0), wordsLength(0), wordsInUse(0) { + initWords(nbits); + } + + BitSet::~BitSet() { + delete words; + } + + void BitSet::initWords(uint32 nbits) { + uint32 length = (nbits <= 0) ? 1 : wordIndex(nbits-1) + 1; + if (words) delete words; + words = new uint64[length]; + bzero(words, sizeof(uint64)*length); + wordsLength = length; + } + + void BitSet::recalculateWordsInUse() { + // wordsInUse is unsigned + if (wordsInUse == 0) + return; + + // Traverse the bitset until a used word is found + uint32 i; + for (i = wordsInUse-1; i >= 0; i--) + if (words[i] != 0) + break; + + wordsInUse = i+1; // The new logical size + } + + void BitSet::ensureCapacity(uint32 wordsRequired) { + if (wordsLength < wordsRequired) { + + // create and copy + uint64* newwords = new uint64[wordsRequired]; + bzero(newwords, sizeof(uint64)*wordsRequired); + memcpy(newwords, words, sizeof(uint64)*wordsLength); + if (words) delete words; + words = newwords; + wordsLength = wordsRequired; + } + } + + void BitSet::expandTo(uint32 wordIndex) { + uint32 wordsRequired = wordIndex+1; + if (wordsInUse < wordsRequired) { + ensureCapacity(wordsRequired); + wordsInUse = wordsRequired; + } + } + + void BitSet::flip(uint32 bitIndex) { + + uint32 wordIdx = wordIndex(bitIndex); + expandTo(wordIdx); + + words[wordIdx] ^= (((uint64)1) << (bitIndex % BITS_PER_WORD)); + + recalculateWordsInUse(); + } + + void BitSet::set(uint32 bitIndex) { + + uint32 wordIdx = wordIndex(bitIndex); + expandTo(wordIdx); + + words[wordIdx] |= (((uint64)1) << (bitIndex % BITS_PER_WORD)); + } + + void BitSet::clear(uint32 bitIndex) { + + uint32 wordIdx = wordIndex(bitIndex); + if (wordIdx >= wordsInUse) + return; + + words[wordIdx] &= ~(((uint64)1) << (bitIndex % BITS_PER_WORD)); + + recalculateWordsInUse(); + } + + void BitSet::set(uint32 bitIndex, bool value) { + if (value) + set(bitIndex); + else + clear(bitIndex); + } + + bool BitSet::get(uint32 bitIndex) const { + uint32 wordIdx = wordIndex(bitIndex); + return ((wordIdx < wordsInUse) + && ((words[wordIdx] & (((uint64)1) << (bitIndex % BITS_PER_WORD))) != 0)); + } + + void BitSet::clear() { + while (wordsInUse > 0) + words[--wordsInUse] = 0; + } + + uint32 BitSet::numberOfTrailingZeros(uint64 i) { + // HD, Figure 5-14 + uint32 x, y; + if (i == 0) return 64; + uint32 n = 63; + y = (uint32)i; if (y != 0) { n = n -32; x = y; } else x = (uint32)(i>>32); + y = x <<16; if (y != 0) { n = n -16; x = y; } + y = x << 8; if (y != 0) { n = n - 8; x = y; } + y = x << 4; if (y != 0) { n = n - 4; x = y; } + y = x << 2; if (y != 0) { n = n - 2; x = y; } + return n - ((x << 1) >> 31); + } + + uint32 BitSet::bitCount(uint64 i) { + // HD, Figure 5-14 + i = i - ((i >> 1) & 0x5555555555555555LL); + i = (i & 0x3333333333333333LL) + ((i >> 2) & 0x3333333333333333LL); + i = (i + (i >> 4)) & 0x0f0f0f0f0f0f0f0fLL; + i = i + (i >> 8); + i = i + (i >> 16); + i = i + (i >> 32); + return (uint32)(i & 0x7f); + } + + int32 BitSet::nextSetBit(uint32 fromIndex) const { + + uint32 u = wordIndex(fromIndex); + if (u >= wordsInUse) + return -1; + + uint64 word = words[u] & (WORD_MASK << (fromIndex % BITS_PER_WORD)); + + while (true) { + if (word != 0) + return (u * BITS_PER_WORD) + numberOfTrailingZeros(word); + if (++u == wordsInUse) + return -1; + word = words[u]; + } + } + + int32 BitSet::nextClearBit(uint32 fromIndex) const { + // Neither spec nor implementation handle bitsets of maximal length. + + uint32 u = wordIndex(fromIndex); + if (u >= wordsInUse) + return fromIndex; + + uint64 word = ~words[u] & (WORD_MASK << (fromIndex % BITS_PER_WORD)); + + while (true) { + if (word != 0) + return (u * BITS_PER_WORD) + numberOfTrailingZeros(word); + if (++u == wordsInUse) + return wordsInUse * BITS_PER_WORD; + word = ~words[u]; + } + } + + bool BitSet::isEmpty() const { + return (wordsInUse == 0); + } + + uint32 BitSet::cardinality() const { + uint32 sum = 0; + for (uint32 i = 0; i < wordsInUse; i++) + sum += bitCount(words[i]); + return sum; + } + + BitSet& BitSet::operator&=(const BitSet& set) { + + while (wordsInUse > set.wordsInUse) + words[--wordsInUse] = 0; + + // Perform logical AND on words in common + for (uint32 i = 0; i < wordsInUse; i++) + words[i] &= set.words[i]; + + recalculateWordsInUse(); + + return *this; + } + + BitSet& BitSet::operator|=(const BitSet& set) { + + uint32 wordsInCommon; + if (wordsInUse < set.wordsInUse) { + wordsInCommon = wordsInUse; + //ensureCapacity(set.wordsInUse); + //wordsInUse = set.wordsInUse; + } + else + wordsInCommon = set.wordsInUse; + + // Perform logical OR on words in common + uint32 i = 0; + for (; i < wordsInCommon; i++) + words[i] |= set.words[i]; + + // TODO what to do if BitSets are not the same size !!! + + // recalculateWordsInUse() is not needed + + return *this; + } + + BitSet& BitSet::operator^=(const BitSet& set) { + + uint32 wordsInCommon; + if (wordsInUse < set.wordsInUse) { + wordsInCommon = wordsInUse; + //ensureCapacity(set.wordsInUse); + //wordsInUse = set.wordsInUse; + } + else + wordsInCommon = set.wordsInUse; + + // Perform logical XOR on words in common + uint32 i = 0; + for (; i < wordsInCommon; i++) + words[i] ^= set.words[i]; + + // TODO what to do if BitSets are not the same size !!! + + recalculateWordsInUse(); + + return *this; + } + + BitSet& BitSet::operator-=(const BitSet& set) { + + uint32 wordsInCommon; + if (wordsInUse < set.wordsInUse) { + wordsInCommon = wordsInUse; + //ensureCapacity(set.wordsInUse); + //wordsInUse = set.wordsInUse; + } + else + wordsInCommon = set.wordsInUse; + + // Perform logical (a & !b) on words in common + uint32 i = 0; + for (; i < wordsInCommon; i++) + words[i] &= ~set.words[i]; + + recalculateWordsInUse(); + + return *this; + } + + BitSet& BitSet::operator=(const BitSet &set) { + // Check for self-assignment! + if (this == &set) + return *this; + + // we ensure that words array size is adequate (and not wordsInUse to ensure capacity to the future) + if (wordsLength < set.wordsLength) + { + if (words) delete words; + words = new uint64[set.wordsLength]; + wordsLength = set.wordsLength; + } + memcpy(words, set.words, sizeof(uint64)*set.wordsInUse); + wordsInUse = set.wordsInUse; + + return *this; + } + + void BitSet::or_and(const BitSet& set1, const BitSet& set2) { + uint32 inUse = (set1.wordsInUse < set2.wordsInUse) ? set1.wordsInUse : set2.wordsInUse; + + ensureCapacity(inUse); + wordsInUse = inUse; + + // Perform logical AND on words in common + for (uint32 i = 0; i < inUse; i++) + words[i] |= (set1.words[i] & set2.words[i]); + + // recalculateWordsInUse()... + } + + bool BitSet::operator==(const BitSet &set) const + { + if (this == &set) + return true; + + if (wordsInUse != set.wordsInUse) + return false; + + // Check words in use by both BitSets + for (uint32 i = 0; i < wordsInUse; i++) + if (words[i] != set.words[i]) + return false; + + return true; + } + + bool BitSet::operator!=(const BitSet &set) const + { + return !(*this == set); + } + + void BitSet::toString(StringBuilder buffer) { toString(buffer, 0); } + + void BitSet::toString(StringBuilder buffer, int indentLevel) const + { + *buffer += '{'; + int32 i = nextSetBit(0); + char tmp[30]; + if (i != -1) { + sprintf(tmp,"%d",i); *buffer += tmp; + for (i = nextSetBit(i+1); i >= 0; i = nextSetBit(i+1)) { + int32 endOfRun = nextClearBit(i); + do { *buffer += ", "; sprintf(tmp,"%d",i); *buffer += tmp; } while (++i < endOfRun); + } + } + *buffer += '}'; + } + + + /* + +void serialize(ByteBuffer buffer, SerializableControl flusher) { + + final int n = wordsInUse; + if (n == 0) { + SerializeHelper.writeSize(0, buffer, flusher); + return; + } + int len = 8 * (n-1); + for (long x = words[n - 1]; x != 0; x >>>= 8) + len++; + + SerializeHelper.writeSize(len, buffer, flusher); + flusher.ensureBuffer(len); + + for (int i = 0; i < n - 1; i++) + buffer.putLong(words[i]); + + for (long x = words[n - 1]; x != 0; x >>>= 8) + buffer.put((byte) (x & 0xff)); +} + +public void deserialize(ByteBuffer buffer, DeserializableControl control) { + + final int bytes = SerializeHelper.readSize(buffer, control); // in bytes + + wordsInUse = (bytes + 7) / 8; + if (wordsInUse > words.length) + words = new long[wordsInUse]; + + if (wordsInUse == 0) + return; + + control.ensureData(bytes); + + int i = 0; + final int longs = bytes / 8; + while (i < longs) + words[i++] = buffer.getLong(); + + for (int j = i; j < wordsInUse; j++) + words[j] = 0; + + for (int remaining = (bytes - longs * 8), j = 0; j < remaining; j++) + words[i] |= (buffer.get() & 0xffL) << (8 * j); + +} + */ + +}}; diff --git a/pvDataApp/misc/bitSet.h b/pvDataApp/misc/bitSet.h new file mode 100644 index 0000000..f477e1c --- /dev/null +++ b/pvDataApp/misc/bitSet.h @@ -0,0 +1,310 @@ +/* bitSet.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 BITSET_H +#define BITSET_H +#include +#include +//#include "byteBuffer.h" +//#include "serialize.h" +namespace epics { namespace pvData { + + /** + * This class implements a vector of bits that grows as needed. Each + * component of the bit set has a {@code bool} value. The + * bits of a {@code BitSet} are indexed by nonnegative integers. + * Individual indexed bits can be examined, set, or cleared. One + * {@code BitSet} may be used to modify the contents of another + * {@code BitSet} through logical AND, logical inclusive OR, and + * logical exclusive OR operations. + * + *

By default, all bits in the set initially have the value + * {@code false}. + * + *

Every bit set has a current size, which is the number of bits + * of space currently in use by the bit set. Note that the size is + * related to the implementation of a bit set, so it may change with + * implementation. The length of a bit set relates to logical length + * of a bit set and is defined independently of implementation. + * + *

A {@code BitSet} is not safe for multithreaded use without + * external synchronization. + * + * Based on Java implementation. + */ + class BitSet /*: public Serializable*/ { + public: + + /** + * Creates a new bit set. All bits are initially {@code false}. + */ + BitSet(); + + /** + * Creates a bit set whose initial size is large enough to explicitly + * represent bits with indices in the range {@code 0} through + * {@code nbits-1}. All bits are initially {@code false}. + * + * @param nbits the initial size of the bit set + */ + BitSet(uint32 nbits); + + /** + * Destructor. + */ + virtual ~BitSet(); + + /** + * Sets the bit at the specified index to the complement of its + * current value. + * + * @param bitIndex the index of the bit to flip + */ + void flip(uint32 bitIndex); + + /** + * Sets the bit at the specified index to {@code true}. + * + * @param bitIndex a bit index + */ + void set(uint32 bitIndex); + + /** + * Sets the bit specified by the index to {@code false}. + * + * @param bitIndex the index of the bit to be cleared + */ + void clear(uint32 bitIndex); + + /** + * Sets the bit at the specified index to the specified value. + * + * @param bitIndex a bit index + * @param value a boolean value to set + */ + void set(uint32 bitIndex, bool value); + + /** + * Returns the value of the bit with the specified index. The value + * is {@code true} if the bit with the index {@code bitIndex} + * is currently set in this {@code BitSet}; otherwise, the result + * is {@code false}. + * + * @param bitIndex the bit index + * @return the value of the bit with the specified index + */ + bool get(uint32 bitIndex) const; + + /** + * Sets all of the bits in this BitSet to {@code false}. + */ + void clear(); + + /** + * Returns the index of the first bit that is set to {@code true} + * that occurs on or after the specified starting index. If no such + * bit exists then {@code -1} is returned. + * + *

To iterate over the {@code true} bits in a {@code BitSet}, + * use the following loop: + * + *

 {@code
+         * for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i+1)) {
+         *     // operate on index i here
+         * }}
+ * + * @param fromIndex the index to start checking from (inclusive) + * @return the index of the next set bit, or {@code -1} if there + * is no such bit + */ + int32 nextSetBit(uint32 fromIndex) const; + + /** + * Returns the index of the first bit that is set to {@code false} + * that occurs on or after the specified starting index. + * + * @param fromIndex the index to start checking from (inclusive) + * @return the index of the next clear bit + */ + int32 nextClearBit(uint32 fromIndex) const; + + /** + * Returns true if this {@code BitSet} contains no bits that are set + * to {@code true}. + * + * @return indicating whether this {@code BitSet} is empty + */ + bool isEmpty() const; + + /** + * Returns the number of bits set to {@code true} in this {@code BitSet}. + * + * @return the number of bits set to {@code true} in this {@code BitSet} + */ + uint32 cardinality() const; + + /** + * Performs a logical AND of this target bit set with the + * argument bit set. This bit set is modified so that each bit in it + * has the value {@code true} if and only if it both initially + * had the value {@code true} and the corresponding bit in the + * bit set argument also had the value {@code true}. + * + * @param set a bit set + */ + BitSet& operator&=(const BitSet& set); + + /** + * Performs a logical OR of this bit set with the bit set + * argument. This bit set is modified so that a bit in it has the + * value {@code true} if and only if it either already had the + * value {@code true} or the corresponding bit in the bit set + * argument has the value {@code true}. + * + * @param set a bit set + */ + BitSet& operator|=(const BitSet& set); + + /** + * Performs a logical XOR of this bit set with the bit set + * argument. This bit set is modified so that a bit in it has the + * value {@code true} if and only if one of the following + * statements holds: + *
    + *
  • The bit initially has the value {@code true}, and the + * corresponding bit in the argument has the value {@code false}. + *
  • The bit initially has the value {@code false}, and the + * corresponding bit in the argument has the value {@code true}. + *
+ * + * @param set a bit set + */ + BitSet& operator^=(const BitSet& set); + + /** + * Clears all of the bits in this {@code BitSet} whose corresponding + * bit is set in the specified {@code BitSet}. + * + * @param set the {@code BitSet} with which to mask this + * {@code BitSet} + */ + BitSet& operator-=(const BitSet& set); + + /** + * Assigment operator. + */ + BitSet& operator=(const BitSet &set); + + /** + * Perform AND operation on set1 and set2, + * and OR on result and this instance. + * @param set1 + * @param set2 + */ + void or_and(const BitSet& set1, const BitSet& set2); + + /** + * Comparison operator. + */ + bool operator==(const BitSet &set) const; + + bool operator!=(const BitSet &set) const; + + void toString(StringBuilder buffer); + + void toString(StringBuilder buffer, int indentLevel) const; + + private: + + /* + * BitSets are packed into arrays of "words." Currently a word is + * a long, which consists of 64 bits, requiring 6 address bits. + * The choice of word size is determined purely by performance concerns. + */ + static const uint32 ADDRESS_BITS_PER_WORD = 6; + static const uint32 BITS_PER_WORD = 1 << ADDRESS_BITS_PER_WORD; + static const uint32 BIT_INDEX_MASK = BITS_PER_WORD - 1; + + /** Used to shift left or right for a partial word mask */ + static const uint64 WORD_MASK = ~((uint64)0); + + /** The internal field corresponding to the serialField "bits". */ + uint64* words; + + /** The internal field corresponding to the size of words[] array. */ + uint32 wordsLength; + + /** The number of words in the logical size of this BitSet. */ + uint32 wordsInUse; + + + private: + + /** + * Given a bit index, return word index containing it. + */ + static inline uint32 wordIndex(uint32 bitIndex) { + return bitIndex >> ADDRESS_BITS_PER_WORD; + } + + /** + * Creates a new word array. + */ + void initWords(uint32 nbits); + + /** + * Sets the field wordsInUse to the logical size in words of the bit set. + * WARNING: This method assumes that the number of words actually in use is + * less than or equal to the current value of wordsInUse! + */ + void recalculateWordsInUse(); + + /** + * Ensures that the BitSet can hold enough words. + * @param wordsRequired the minimum acceptable number of words. + */ + void ensureCapacity(uint32 wordsRequired); + + /** + * Ensures that the BitSet can accommodate a given wordIndex, + * temporarily violating the invariants. The caller must + * restore the invariants before returning to the user, + * possibly using recalculateWordsInUse(). + * @param wordIndex the index to be accommodated. + */ + void expandTo(uint32 wordIndex); + + /** + * Returns the number of zero bits following the lowest-order ("rightmost") + * one-bit in the two's complement binary representation of the specified + * long value. Returns 64 if the specified value has no + * one-bits in its two's complement representation, in other words if it is + * equal to zero. + * + * @return the number of zero bits following the lowest-order ("rightmost") + * one-bit in the two's complement binary representation of the + * specified long value, or 64 if the value is equal + * to zero. + */ + static uint32 numberOfTrailingZeros(uint64 i); + + /** + * Returns the number of one-bits in the two's complement binary + * representation of the specified long value. This function is + * sometimes referred to as the population count. + * + * @return the number of one-bits in the two's complement binary + * representation of the specified long value. + */ + static uint32 bitCount(uint64 i); + + }; + +}} +#endif /* BITSET_H */ + + + diff --git a/pvDataApp/misc/byteBuffer.cpp b/pvDataApp/misc/byteBuffer.cpp new file mode 100644 index 0000000..7ee6730 --- /dev/null +++ b/pvDataApp/misc/byteBuffer.cpp @@ -0,0 +1,198 @@ +/** + * 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. + */ +/* + * byteBuffer.cpp + * + * Created on: Oct 18, 2010 + * Author: Miha Vitorovic + */ +#include +#include "byteBuffer.h" +#include "epicsException.h" + +namespace epics { + namespace pvData { + + using std::stringstream; + + ByteBuffer::ByteBuffer(int size, int byteOrder) : + _bufferByteOrder(byteOrder), _size(size), _position(0), + _limit(size), _buffer(0) { + + if (size < 0) + throw EpicsException("negative size"); + + if (byteOrder!=EPICS_ENDIAN_BIG && byteOrder!=EPICS_ENDIAN_LITTLE) + throw EpicsException("invalid endian"); + + _buffer = new char[_size]; + } + + ByteBuffer::~ByteBuffer() { + if (_buffer) delete _buffer; + } + + ByteBuffer* ByteBuffer::clear() { + _position = 0; + _limit = _size; + return this; + } + + ByteBuffer* ByteBuffer::flip() { + _limit = _position; + _position = 0; + return this; + } + + ByteBuffer* ByteBuffer::rewind() { + _position = 0; + return this; + } + + bool ByteBuffer::getBoolean() { + if(_position<_limit) + return _buffer[_position++]==0 ? false : true; + else + throw EpicsException("buffer underflow"); + } + + int8 ByteBuffer::getByte() { + if(_position<_limit) + return (int8)_buffer[_position++]; + else + throw EpicsException("buffer underflow"); + } + + int16 ByteBuffer::getShort() { + if(_limit-_position<(int)sizeof(int16)) + throw EpicsException("buffer underflow"); + int16 val; + getWithEndianness((char*)&val, sizeof(int16)); // store short into val + return val; + } + + int32 ByteBuffer::getInt() { + if(_limit-_position<(int)sizeof(int32)) + throw EpicsException("buffer underflow"); + int32 val; + getWithEndianness((char*)&val, sizeof(int32)); // store int into val + return val; + } + + int64 ByteBuffer::getLong() { + if(_limit-_position<(int)sizeof(int64)) + throw EpicsException("buffer underflow"); + int64 val; + getWithEndianness((char*)&val, sizeof(int64)); // store long into val + return val; + } + + float ByteBuffer::getFloat() { + if(_limit-_position<(int)sizeof(float)) + throw EpicsException("buffer underflow"); + float val; + getWithEndianness((char*)&val, sizeof(float)); // store float into val + return val; + } + + double ByteBuffer::getDouble() { + if(_limit-_position<(int)sizeof(double)) + throw EpicsException("buffer underflow"); + double val; + getWithEndianness((char*)&val, sizeof(double)); // store double into val + return val; + } + + void ByteBuffer::get(char* dst, int offset, int count) { + if(count>getRemaining()) throw EpicsException("buffer underflow"); + for(int i = 0; igetRemaining()) throw EpicsException("buffer overflow"); + for(int i = 0; i=0; i--) + dest[i] = _buffer[_position++]; + } + + /** + * Buffer underflow or overflow checks are performed in the caller. + */ + void ByteBuffer::putWithEndianness(char* src, size_t size) { + if(_bufferByteOrder==EPICS_BYTE_ORDER) + for(size_t i = 0; i=0; i--) + _buffer[_position++] = src[i]; + } + + } +} diff --git a/pvDataApp/misc/byteBuffer.h b/pvDataApp/misc/byteBuffer.h new file mode 100644 index 0000000..61e488c --- /dev/null +++ b/pvDataApp/misc/byteBuffer.h @@ -0,0 +1,330 @@ +/* byteBuffer.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 BYTEBUFFER_H +#define BYTEBUFFER_H + +#include +#include +#include + +namespace epics { + namespace pvData { + + + /** @brief A buffer of bytes. + * + * A buffer of bytes, which has + * - capacity (size) + * - limit + * - position + * The capacity is the maximum capacity of the buffer, the buffer's + * limit is the index of the first element that should not be read or + * written, and its position is the index of the next element to be + * written. + * + * The buffer also ha a byte order specified which can be little or + * big endian. + */ + class ByteBuffer { + public: + /** @brief ByteBuffer constructor. + * + * Creates a buffer of a given size, and with a specified byteOrder. + * + * @param size Specify the capacity of the buffer. Default is 32. + * @param byteOrder the byte order of the buffer. Can be either + * EPICS_ENDIAN_LITTLE or EPICS_ENDIAN_BIG. + */ + ByteBuffer(int size = 32, int byteOrder = EPICS_BYTE_ORDER); + + /** + * @brief ByteBuffer destructor. + */ + ~ByteBuffer(); + + /** + * Clears the buffer. Sets the {@code position} to 0 and {@code limit} + * to {@code capacity}. + */ + ByteBuffer* clear(); + + /** + * Flips the buffer. The {@code limit} is set to the current {@code position} + * and the {@code position} is set to {@code 0}. This prepares the + * buffer for a series of relative @em get operations after a series + * of relative @em put operations. + */ + ByteBuffer* flip(); + + /** + * Rewinds the buffer. This sets {@code position} to {@code 0} + * without affecting the {@code limit}. + */ + ByteBuffer* rewind(); + + /** + * Relative boolean read, {@code position} is incremented by + * {@code 1}. + * + * @returns The next byte in the buffer as a boolean. + * @throws EpicsException - Buffer underflow if there are no bytes + * remaining in the buffer. + */ + bool getBoolean(); + + /** + * Relative Int8 read, {@code position} is incremented by + * {@code 1}. + * + * @returns The next byte in the buffer as a signed Int8. + * @throws EpicsException - Buffer underflow if there are no bytes + * remaining in the buffer. + */ + int8 getByte(); + + /** + * Relative Int16 read, {@code position} is incremented by + * {@code 2}. + * + * @returns The next Int16 value in the buffer. + * @throws EpicsException - Buffer underflow if there are less than + * 2 bytes remaining in the buffer. + */ + int16 getShort(); + + /** + * Relative Int32 read, {@code position} is incremented by + * {@code 4}. + * + * @returns The next Int32 value in the buffer. + * @throws EpicsException - Buffer underflow if there are less than + * 4 bytes remaining in the buffer. + */ + int32 getInt(); + + /** + * Relative Int64 read, {@code position} is incremented by + * {@code 8}. + * + * @returns The next Int64 value in the buffer. + * @throws EpicsException - Buffer underflow if there are less than + * 8 bytes remaining in the buffer. + */ + int64 getLong(); + + /** + * Relative float read, {@code position} is incremented by + * {@code 4}. + * + * @returns The next float value in the buffer. + * @throws EpicsException - Buffer underflow if there are less than + * 4 bytes remaining in the buffer. + */ + float getFloat(); + + /** + * Relative double read, {@code position} is incremented by + * {@code 8}. + * + * @returns The next double value in the buffer. + * @throws EpicsException - Buffer underflow if there are less than + * 8 bytes remaining in the buffer. + */ + double getDouble(); + + /** + * Relative bulk @em get method. It transfers {@code count} bytes + * from the buffer into the {@code dst}. + * + * @param[out] dst Destination buffer + * @param[in] offset Offset in the destination buffer + * @param[in] count The number of bytes to copy + * @throws EpicsException - Buffer underflow if there are fewer + * than count bytes remaining in the buffer. + */ + void get(char* dst, int offset, int count); + + //virtual String getString() = 0; // TODO + + + /** + * Relative bulk @em put method. It transfers {@code count} bytes + * from the {@code src} into the the buffer. + * + * @param[in] src Source buffer + * @param[in] offset Offset in the source buffer + * @param[in] count The number of bytes to copy + * @returns Pointer to this ByteBuffer instance. + * @throws EpicsException - Buffer overflow if there are not + * enough bytes remaining in the buffer. + */ + ByteBuffer* put(const char* src, int offset, int count); + + /** + * Relative boolean write, {@code position} is incremented by + * {@code 1}. + * + * @param[in] value The boolean value to write. + * @throws EpicsException - Buffer overflow if there are no + * bytes remaining in the buffer. + */ + ByteBuffer* putBoolean(bool value); + + + /** + * Relative Int8 write, {@code position} is incremented by + * {@code 1}. + * + * @param[in] value The Int8 value to write. + * @throws EpicsException - Buffer overflow if there are no + * bytes remaining in the buffer. + */ + ByteBuffer* putByte(int8 value); + + /** + * Relative Int16 write, {@code position} is incremented by + * {@code 2}. + * + * @param[in] value The Int16 value to write. + * @throws EpicsException - Buffer overflow if there are less than + * 2 bytes remaining in the buffer. + */ + ByteBuffer* putShort(int16 value); + + /** + * Relative Int32 write, {@code position} is incremented by + * {@code 4}. + * + * @param[in] value The Int32 value to write. + * @throws EpicsException - Buffer overflow if there are less than + * 4 bytes remaining in the buffer. + */ + ByteBuffer* putInt(int32 value); + + /** + * Relative Int64 write, {@code position} is incremented by + * {@code 8}. + * + * @param[in] value The Int64 value to write. + * @throws EpicsException - Buffer overflow if there are less than + * 8 bytes remaining in the buffer. + */ + ByteBuffer* putLong(int64 value); + + /** + * Relative float write, {@code position} is incremented by + * {@code 4}. + * + * @param[in] value The float value to write. + * @throws EpicsException - Buffer overflow if there are less than + * 4 bytes remaining in the buffer. + */ + ByteBuffer* putFloat(float value); + + /** + * Relative float write, {@code position} is incremented by + * {@code 8}. + * + * @param[in] value The double value to write. + * @throws EpicsException - Buffer overflow if there are less than + * 8 bytes remaining in the buffer. + */ + ByteBuffer* putDouble(double value); + + //virtual ByteBuffer *putString(String value) = 0; // TODO + + /** + * The capacity (size) of the buffer in bytes. + * + * @returns The capacity of the buffer in bytes. + */ + inline int getSize() const { + return _size; + } + + /** + * The offset from the start of the buffer in bytes. Currently this + * method always returns {@code 0}. + * + * @returns The offset from the start of the buffer in bytes + */ + inline int getArrayOffset() const { + return 0; + } + + /** + * The byte index of the next element to @em get or @em put. + * + * @returns The byte index of the next element. + */ + inline int getPosition() const { + return _position; + } + + /** + * The byte index of the {@code limit}. {@code limit} is always less + * or equal to the buffer capacity. No @em put or @em get operation + * can be performed pass the {@code limit}. + * + * @returns The byte index of the {@code limit}. + */ + inline int getLimit() const { + return _limit; + } + + /** + * The number of the bytes that can be successfully read or written. + * + * @returns The number of the bytes that can be successfully read or + * written. + */ + inline int getRemaining() const { + return _limit-_position; + } + + /** + * The byte order of the buffer. + * + * @returns Either {@code EPICS_ENDIAN_LITTLE} or + * {@code EPICS_ENDIAN_BIG}. + */ + inline int getByteOrder() const { + return _bufferByteOrder; + } + + /** + * Gives a read-only access to the buffer. + * + * @returns A const (read-only) pointer to the actual buffer. + */ + inline const char* getArray() const { + return _buffer; + } + + // TODO must define arrays + + private: + int _bufferByteOrder, _size, _position, _limit; + char* _buffer; + + /** + * Reads the next
size
bytes from the buffer and stores them + * into the destination taking into account endianness of the buffer + * and the current hardware. + */ + void getWithEndianness(char* dest, size_t size); + /** + * Puts the next
size
bytes into the buffer reading them + * from source taking into account endianness of the buffer + * and the current hardware. + */ + void putWithEndianness(char* src, size_t size); + }; + + } +} +#endif /* BYTEBUFFER_H */ diff --git a/pvDataApp/misc/epicsException.h b/pvDataApp/misc/epicsException.h new file mode 100644 index 0000000..8c6755d --- /dev/null +++ b/pvDataApp/misc/epicsException.h @@ -0,0 +1,231 @@ +/** + * 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. + */ +/* + * epicsException.hpp + * + * Created on: Oct 20, 2010 + * Author: Matej Sekoranja + */ + +#include +#ifndef EPICSEXCEPTION_H_ +#define EPICSEXCEPTION_H_ + +// TODO to be redefined!!!!!! + +#include +#include + +#include +#include +#include +#include + +namespace epics { namespace pvData { + + +class BaseException : + public std::exception { +public: + BaseException(const char* message, const char* file, int line, std::exception* cause = 0) + : m_msg(message), m_file(file), m_line(line), m_cause(cause) + { + getStackTrace(&m_stackTrace); + } + + virtual ~BaseException() throw() + { + if (m_cause) delete m_cause; + } + + virtual const char* what() const throw() { return m_msg.c_str(); } + + const char* getFile() const { return m_file.c_str(); } + int getLine() const { return m_line; } + + void toString(std::string& str, unsigned int depth = 0) { + str.append("BaseException: "); + str.append(m_msg); + str.append("\n\tat "); + str.append(m_file); + str.append(":"); + char sline[10]; + snprintf(sline, 10, "%d", m_line); + str.append(sline); + str.append("\n"); + str.append(m_stackTrace); + if (m_cause) + { + str.append("caused by: "); + BaseException *be = dynamic_cast(m_cause); + if (be) + be->toString(str, depth+1); + else + str.append(m_cause->what()); + } + } + + /** Get stack trace, i.e. demangled backtrace of the caller. */ + static inline void getStackTrace(std::string* trace, unsigned int skip_frames = 0, unsigned int max_frames = 63) + { +#ifdef DISABLE_STACK_TRACE + trace += "(stack trace disabled)"; +#else + // storage array for stack trace address data + void* addrlist[max_frames+1]; + + // retrieve current stack addresses + int addrlen = backtrace(addrlist, sizeof(addrlist) / sizeof(void*)); + + if (addrlen == 0) { + trace->append("(stack trace not available)"); + return; + } + + // resolve addresses into strings containing "filename(function+address)", + // this array must be free()-ed + char** symbollist = backtrace_symbols(addrlist, addrlen); + + // allocate string which will be filled with the demangled function name + size_t funcnamesize = 256; + char* funcname = (char*)malloc(funcnamesize); + + // iterate over the returned symbol lines. skip the first, it is the + // address of this function. + for (int i = (1 + skip_frames); i < addrlen; i++) + { + char *module = 0, *fname = 0, *offset = 0; +#ifdef __APPLE__ + int stage = 0; + for (char *p = symbollist[i]; *p; ++p) + { + // find spaces and separate + // 0 a.out 0x0000000100000bbc _Z11print_tracev + 22 + switch (stage) + { + case 0: // skip frame index + if (*p == ' ') stage++; + break; + case 1: // skip spaces + if (*p != ' ') { module = p; stage++; } + break; + case 2: // module name + if (*p == ' ') { *p = '\0'; stage++; } + break; + case 3: // skip spaces + if (*p != ' ') stage++; + break; + case 4: // address + if (*p == ' ') { fname = p+1; stage++; } + break; + case 5: // function + if (*p == ' ') { *p = '\0'; stage++; } + break; + case 6: // "+ " + if (*p == '+') { p++; offset = p+1; }; + break; + } + } +#else + // find parentheses and +address offset surrounding the mangled name: + // ./module(function+0x15c) [0x8048a6d] + module = symbollist[i]; + for (char *p = symbollist[i]; *p; ++p) + { + if (*p == '(') { + // terminate module + *p = '\0'; + fname = p+1; + } + else if (*p == '+') { + // terminate fname + *p = '\0'; + offset = p+1; + } + else if (*p == ')' && offset) { + // terminate offset + *p = '\0'; + break; + } + } +#endif + + if (fname && offset && offset && fname < offset) + { + + // mangled name is now in [begin_name, begin_offset) and caller + // offset in [begin_offset, end_offset). now apply + // __cxa_demangle(): + + int status; + char* ret = abi::__cxa_demangle(fname, + funcname, &funcnamesize, &status); + if (status == 0) { + trace->append("\t "); + *trace += module; + trace->append(": "); + *trace += ret; // use possibly realloc()-ed string + trace->append("+"); + *trace += offset; + trace->append("\n"); + } + else { + // demangling failed. Output function name as a C function with + // no arguments. + trace->append("\t "); + *trace += module; + trace->append(": "); + *trace += fname; + *trace += "()+"; + *trace += offset; + trace->append("\n"); + } + } + else + { + // couldn't parse the line? print the whole line. + trace->append("\t "); + *trace += symbollist[i]; + trace->append("\n"); + } + } + + free(funcname); + free(symbollist); + +#endif + } + +private: + std::string m_msg; + std::string m_file; + int m_line; + std::exception* m_cause; + std::string m_stackTrace; +}; + + +#define THROW_BASE_EXCEPTION(msg) throw new BaseException(msg, __FILE__, __LINE__) +#define THROW_BASE_EXCEPTION_CAUSE(msg, cause) throw new BaseException(msg, __FILE__, __LINE__, cause) + +/* + /// Construct with file, line info and printf-type arguments. + GenericException(const char *sourcefile, size_t line, const char *format, ...) + __attribute__ ((format (printf, 4, 5))); +*/ + + /** Base Epics Exception */ + class EpicsException : public std::logic_error { + public: + explicit EpicsException(const std::string& arg) : + std::logic_error(arg) { + } + }; + } +} + + +#endif /* EPICSEXCEPTION_H_ */ diff --git a/pvDataApp/misc/event.cpp b/pvDataApp/misc/event.cpp new file mode 100644 index 0000000..ea0b4f1 --- /dev/null +++ b/pvDataApp/misc/event.cpp @@ -0,0 +1,107 @@ +/* event.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 +#include +#include +#include +#include +#include "noDefaultMethods.h" +#include "pvType.h" +#include "lock.h" +#include "event.h" + +namespace epics { namespace pvData { + +static volatile int64 totalConstruct = 0; +static volatile int64 totalDestruct = 0; +static Mutex *globalMutex = 0; +static String alreadyOn("already on list"); + + +static int64 getTotalConstruct() +{ + Lock xx(globalMutex); + return totalConstruct; +} + +static int64 getTotalDestruct() +{ + Lock xx(globalMutex); + return totalDestruct; +} + +static ConstructDestructCallback *pConstructDestructCallback; + +static void init() +{ + static Mutex mutex = Mutex(); + Lock xx(&mutex); + if(globalMutex==0) { + globalMutex = new Mutex(); + pConstructDestructCallback = new ConstructDestructCallback( + String("event"), + getTotalConstruct,getTotalDestruct,0); + } +} + +Event::~Event() { + epicsEventDestroy(id); + id = 0; + Lock xx(globalMutex); + totalDestruct++; +} + + +Event::Event(bool full) +: id(epicsEventCreate(full?epicsEventFull : epicsEventEmpty)) +{ + init(); + Lock xx(globalMutex); + totalConstruct++; +} + +ConstructDestructCallback *Event::getConstructDestructCallback() +{ + init(); + return pConstructDestructCallback; +} + +void Event::signal() +{ + if(id==0) throw std::logic_error(String("event was deleted")); + epicsEventSignal(id); +} + +bool Event::wait () +{ + if(id==0) throw std::logic_error(String("event was deleted")); + epicsEventWaitStatus status = epicsEventWait(id); + return status==epicsEventWaitOK ? true : false; +} + +bool Event::wait ( double timeOut ) +{ + if(id==0) throw std::logic_error(String("event was deleted")); + epicsEventWaitStatus status = epicsEventWaitWithTimeout(id,timeOut); + return status==epicsEventWaitOK ? true : false; +} + +bool Event::tryWait () +{ + if(id==0) throw std::logic_error(String("event was deleted")); + epicsEventWaitStatus status = epicsEventTryWait(id); + return status==epicsEventWaitOK ? true : false; +} + +}} diff --git a/pvDataApp/misc/event.h b/pvDataApp/misc/event.h new file mode 100644 index 0000000..00dcdb0 --- /dev/null +++ b/pvDataApp/misc/event.h @@ -0,0 +1,33 @@ +/* event.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 EVENT_H +#define EVENT_H +#include +#include +#include +#include +#include "noDefaultMethods.h" +#include "pvType.h" +#include "showConstructDestruct.h" + +namespace epics { namespace pvData { + +class Event : private NoDefaultMethods { +public: + explicit Event(bool = false); + ~Event(); + static ConstructDestructCallback *getConstructDestructCallback(); + void signal(); + bool wait (); /* blocks until full */ + bool wait ( double timeOut ); /* false if empty at time out */ + bool tryWait (); /* false if empty */ +private: + epicsEventId id; +}; + +}} +#endif /* EVENT_H */ diff --git a/pvDataApp/misc/executor.cpp b/pvDataApp/misc/executor.cpp new file mode 100644 index 0000000..bb96898 --- /dev/null +++ b/pvDataApp/misc/executor.cpp @@ -0,0 +1,187 @@ +/* executor.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 +#include "linkedList.h" +#include "lock.h" +#include "thread.h" +#include "event.h" +#include "executor.h" + +namespace epics { namespace pvData { + +static volatile int64 totalConstruct = 0; +static volatile int64 totalDestruct = 0; +static Mutex *globalMutex = 0; + +static int64 getTotalConstruct() +{ + Lock xx(globalMutex); + return totalConstruct; +} + +static int64 getTotalDestruct() +{ + Lock xx(globalMutex); + return totalDestruct; +} + +static ConstructDestructCallback *pConstructDestructCallback; + +static void init() { + static Mutex mutex = Mutex(); + Lock xx(&mutex); + if(globalMutex==0) { + globalMutex = new Mutex(); + pConstructDestructCallback = new ConstructDestructCallback( + String("executor"), + getTotalConstruct,getTotalDestruct,0); + } +} + + +typedef LinkedListNode ExecutorListNode; +typedef LinkedList ExecutorList; + +class ExecutorNode { +public: + ExecutorNode(Command *command); + ~ExecutorNode(); + + Command *command; + ExecutorListNode *node; + ExecutorListNode *runNode; +}; + +ExecutorNode::ExecutorNode(Command *command) +: command(command), + node(new ExecutorListNode(this)), + runNode(new ExecutorListNode(this)) +{} + +ExecutorNode::~ExecutorNode() +{ + delete node; + delete runNode; +} + +ConstructDestructCallback *Executor::getConstructDestructCallback() +{ + init(); + return pConstructDestructCallback; +} + +class ExecutorPvt : public Runnable{ +public: + ExecutorPvt(String threadName,ThreadPriority priority); + ~ExecutorPvt(); + ExecutorNode * createNode(Command *command); + void execute(ExecutorNode *node); + virtual void run(); +private: + ExecutorList *executorList; + ExecutorList *runList; + Event *moreWork; + Event *stopped; + Mutex mutex; + volatile bool alive; + Thread *thread; +}; + +ExecutorPvt::ExecutorPvt(String threadName,ThreadPriority priority) +: executorList(new ExecutorList()), + runList(new ExecutorList()), + moreWork(new Event(false)), + stopped(new Event(false)), + mutex(Mutex()), + alive(true), + thread(new Thread(threadName,priority,this)) +{} + +ExecutorPvt::~ExecutorPvt() +{ + { + Lock xx(&mutex); + alive = false; + } + moreWork->signal(); + { + Lock xx(&mutex); + stopped->wait(); + } + ExecutorListNode *node; + while((node=executorList->removeHead())!=0) { + delete node->getObject(); + } + delete thread; + delete stopped; + delete moreWork; + delete runList; + delete executorList; +} + +void ExecutorPvt::run() +{ + while(alive) { + ExecutorListNode * executorListNode = 0; + while(alive && runList->isEmpty()) { + moreWork->wait(); + } + if(alive) { + Lock xx(&mutex); + executorListNode = runList->removeHead(); + } + if(alive && executorListNode!=0) { + executorListNode->getObject()->command->command(); + } + } + stopped->signal(); +} + +ExecutorNode * ExecutorPvt::createNode(Command *command) +{ + Lock xx(&mutex); + ExecutorNode *executorNode = new ExecutorNode(command); + executorList->addTail(executorNode->node); + return executorNode; +} + +void ExecutorPvt::execute(ExecutorNode *node) +{ + Lock xx(&mutex); + if(!alive || node->runNode->isOnList()) return; + bool isEmpty = runList->isEmpty(); + runList->addTail(node->runNode); + if(isEmpty) moreWork->signal(); +} + +Executor::Executor(String threadName,ThreadPriority priority) +: pImpl(new ExecutorPvt(threadName,priority)) +{ + init(); + Lock xx(globalMutex); + totalConstruct++; +} + +Executor::~Executor() { + delete pImpl; + Lock xx(globalMutex); + totalDestruct++; +} + +ExecutorNode * Executor::createNode(Command*command) +{return pImpl->createNode(command);} + +void Executor::execute(ExecutorNode *node) {pImpl->execute(node);} + +}} diff --git a/pvDataApp/misc/executor.h b/pvDataApp/misc/executor.h new file mode 100644 index 0000000..1c70439 --- /dev/null +++ b/pvDataApp/misc/executor.h @@ -0,0 +1,37 @@ +/* executor.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 EXECUTOR_H +#define EXECUTOR_H +#include +#include +#include "noDefaultMethods.h" +#include "pvType.h" +#include "thread.h" + +namespace epics { namespace pvData { + +// This is created by Executor.createNode and passed to Executor.execute +class ExecutorNode; + +class Command { +public: + virtual void command() = 0; +}; + +class Executor : private NoDefaultMethods { +public: + Executor(String threadName,ThreadPriority priority); + ~Executor(); + static ConstructDestructCallback *getConstructDestructCallback(); + ExecutorNode * createNode(Command *command); + void execute(ExecutorNode *node); +private: + class ExecutorPvt *pImpl; +}; + +}} +#endif /* EXECUTOR_H */ diff --git a/pvDataApp/misc/linkedList.h b/pvDataApp/misc/linkedList.h new file mode 100644 index 0000000..abf41c5 --- /dev/null +++ b/pvDataApp/misc/linkedList.h @@ -0,0 +1,88 @@ +/* linkedList.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 LINKEDLIST_H +#define LINKEDLIST_H +#include "linkedListVoid.h" +namespace epics { namespace pvData { + +template +class LinkedList; + +template +class LinkedListNode : private LinkedListVoidNode { +public: + LinkedListNode(T *object) : LinkedListVoidNode(object){} + ~LinkedListNode() {} + T *getObject() { return static_cast(LinkedListVoidNode::getObject());} + bool isOnList() {return LinkedListVoidNode::isOnList();} + friend class LinkedList; +}; + +template +class LinkedList : private LinkedListVoid { +public: + LinkedList() : LinkedListVoid() {} + ~LinkedList() {} + int getLength() {return LinkedListVoid::getLength();} + void addTail(LinkedListNode *listNode) + { + LinkedListVoid::addTail(static_cast(listNode)); + } + void addHead(LinkedListNode *listNode) + { + LinkedListVoid::addHead(static_cast(listNode)); + } + void insertAfter(LinkedListNode *listNode, + LinkedListNode *addNode) + { + LinkedListVoid::insertAfter( + static_cast(listNode), + static_cast(addNode)); + } + void insertBefore(LinkedListNode *listNode, + LinkedListNode *addNode) + { + LinkedListVoid::insertBefore( + static_cast(listNode), + static_cast(addNode)); + } + LinkedListNode *removeTail(){ + return static_cast *>(LinkedListVoid::removeTail()); + } + LinkedListNode *removeHead(){ + return static_cast *>(LinkedListVoid::removeHead()); + } + void remove(LinkedListNode *listNode){ + LinkedListVoid::remove(static_cast(listNode)); + } + void remove(T *object){ + LinkedListVoid::remove(object); + } + LinkedListNode *getHead(){ + return static_cast *>(LinkedListVoid::getHead()); + } + LinkedListNode *getTail(){ + return static_cast *>(LinkedListVoid::getTail()); + } + LinkedListNode *getNext(LinkedListNode *listNode){ + return static_cast *>(LinkedListVoid::getNext( + static_cast(listNode))); + } + LinkedListNode *getPrev(LinkedListNode *listNode){ + return static_cast *>(LinkedListVoid::getPrev( + static_cast(listNode))); + } + bool isEmpty() { return LinkedListVoid::isEmpty();} + bool contains(T *object) { return LinkedListVoid::contains(object);} +}; + + +}} +#endif /* LINKEDLIST_H */ + + + diff --git a/pvDataApp/misc/linkedListVoid.cpp b/pvDataApp/misc/linkedListVoid.cpp new file mode 100644 index 0000000..097b3a4 --- /dev/null +++ b/pvDataApp/misc/linkedListVoid.cpp @@ -0,0 +1,300 @@ +/* linkedListVoid.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 "linkedListVoid.h" + +namespace epics { namespace pvData { + +static volatile int64 totalNodeConstruct = 0; +static volatile int64 totalNodeDestruct = 0; +static volatile int64 totalListConstruct = 0; +static volatile int64 totalListDestruct = 0; +static Mutex *globalMutex = 0; +static String alreadyOnList("already on list"); + +static int64 getTotalNodeConstruct() +{ + Lock xx(globalMutex); + return totalNodeConstruct; +} + +static int64 getTotalNodeDestruct() +{ + Lock xx(globalMutex); + return totalNodeDestruct; +} + +static int64 getTotalListConstruct() +{ + Lock xx(globalMutex); + return totalListConstruct; +} + +static int64 getTotalListDestruct() +{ + Lock xx(globalMutex); + return totalListDestruct; +} + +static ConstructDestructCallback *pCDCallbackLinkedListNode; +static ConstructDestructCallback *pCDCallbackLinkedList; + +static void initPvt() +{ + static Mutex mutex = Mutex(); + Lock xx(&mutex); + if(globalMutex==0) { + globalMutex = new Mutex(); + pCDCallbackLinkedListNode = new ConstructDestructCallback( + "linkedListNode", + getTotalNodeConstruct,getTotalNodeDestruct,0); + + pCDCallbackLinkedList = new ConstructDestructCallback( + "linkedList", + getTotalListConstruct,getTotalListDestruct,0); + } +} + + +LinkedListVoidNode::LinkedListVoidNode(void *object) +: object(object),before(0),after(0),linkedListVoid(0) +{ + initPvt(); + Lock xx(globalMutex); + totalNodeConstruct++; +} + +LinkedListVoidNode::LinkedListVoidNode(bool isHead) +: object(this),before(this),after(this) +{ + initPvt(); + Lock xx(globalMutex); + totalNodeConstruct++; +} + + +LinkedListVoidNode::~LinkedListVoidNode() +{ + Lock xx(globalMutex); + totalNodeDestruct++; +} + +ConstructDestructCallback *LinkedListVoidNode::getConstructDestructCallback() +{ + initPvt(); + return pCDCallbackLinkedListNode; +} + +void *LinkedListVoidNode::getObject() { + return object; +} + +bool LinkedListVoidNode::isOnList() +{ + if(before==0 && after==0) return false; + return true; +} + +LinkedListVoid::LinkedListVoid() +: head(new LinkedListVoidNode(true)),length(0) +{ + initPvt(); + Lock xx(globalMutex); + totalListConstruct++; +} + +LinkedListVoid::~LinkedListVoid() +{ + Lock xx(globalMutex); + delete head; + totalListDestruct++; +} + +ConstructDestructCallback *LinkedListVoid::getConstructDestructCallback() +{ + initPvt(); + return pCDCallbackLinkedList; +} + +int LinkedListVoid::getLength() +{ + return length; +} + +void LinkedListVoid::addTail(LinkedListVoidNode *node) +{ + if(node->before!=0 || node->after!=0) { + throw std::logic_error(alreadyOnList); + } + node->linkedListVoid = this; + node->before = head->before; + node->after = head; + head->before->after = node; + head->before = node; + ++length; +} + +void LinkedListVoid::addHead(LinkedListVoidNode *node) +{ + if(node->before!=0 || node->after!=0) { + throw std::logic_error(alreadyOnList); + } + node->linkedListVoid = this; + node->after = head->after; + node->before = head; + head->after->before = node; + head->after = node; + ++length; +} + +void LinkedListVoid::insertAfter(LinkedListVoidNode *node, + LinkedListVoidNode *addNode) +{ + LinkedListVoidNode *existingNode = node; + LinkedListVoidNode *newNode = addNode; + if(existingNode->after==0 || existingNode->before==0) { + throw std::logic_error(String("listNode not on list")); + } + if(newNode->before!=0 || newNode->after!=0) { + throw std::logic_error(alreadyOnList); + } + if(node->linkedListVoid!=this) { + throw std::logic_error(String("node not on this list")); + } + newNode->linkedListVoid = this; + newNode->after = existingNode->after; + newNode->before = existingNode; + existingNode->after->before = newNode; + existingNode->after = newNode; + ++length; +} + +void LinkedListVoid::insertBefore(LinkedListVoidNode *node, + LinkedListVoidNode *addNode) +{ + LinkedListVoidNode *existingNode = node; + LinkedListVoidNode *newNode = addNode; + if(existingNode->after==0 || existingNode->before==0) { + throw std::logic_error(String("listNode not on list")); + } + if(newNode->before!=0 || newNode->after!=0) { + throw std::logic_error(alreadyOnList); + } + if(node->linkedListVoid!=this) { + throw std::logic_error(String("node not on this list")); + } + newNode->linkedListVoid = this; + newNode->after = existingNode; + newNode->before = existingNode->before; + existingNode->before->after = newNode; + existingNode->before = newNode; + ++length; +} + +LinkedListVoidNode *LinkedListVoid::removeTail() +{ + if(head->after==head) return 0; + LinkedListVoidNode *node = head->before; + remove(head->before); + return node; +} + +LinkedListVoidNode *LinkedListVoid::removeHead() +{ + if(head->after==head) return 0; + LinkedListVoidNode *node = head->after; + remove(head->after); + return node; +} + +void LinkedListVoid::remove(LinkedListVoidNode *node) +{ + if(node->before==0 || node->after==0) { + throw std::logic_error(String("node not on list")); + } + if(node->linkedListVoid!=this) { + throw std::logic_error(String("node not on this list")); + } + node->linkedListVoid = 0; + LinkedListVoidNode *prev = node->before; + LinkedListVoidNode *next = node->after; + node->after = node->before = 0; + prev->after = next; + next->before = prev; + length--; +} + +void LinkedListVoid::remove(void * object) +{ + LinkedListVoidNode *node = getHead(); + while(node!=0) { + if(node->getObject()==object) { + remove(node); + return; + } + node = getNext(node); + } + throw std::logic_error(String("object not on this list")); +} + +LinkedListVoidNode *LinkedListVoid::getHead() +{ + if(head->after==head) return 0; + return head->after; +} + +LinkedListVoidNode *LinkedListVoid::getTail() +{ + if(head->after==head) return 0; + return head->before; +} + +LinkedListVoidNode *LinkedListVoid::getNext(LinkedListVoidNode *listNode) +{ + if(listNode->linkedListVoid!=this) { + throw std::logic_error(String("node not on this list")); + } + if(listNode->after==head) return 0; + return listNode->after; +} + +LinkedListVoidNode *LinkedListVoid::getPrev(LinkedListVoidNode *listNode) +{ + if(listNode->linkedListVoid!=this) { + throw std::logic_error(String("node not on this list")); + } + if(listNode->before==head) return 0; + return listNode->before; +} + +bool LinkedListVoid::isEmpty() +{ + if(head->after==head) return true; + return false; +} + +bool LinkedListVoid::contains(void * object) +{ + LinkedListVoidNode *node = getHead(); + while(node!=0) { + if(node->getObject()==object) { + return true; + } + node = getNext(node); + } + return false; +} + +}} diff --git a/pvDataApp/misc/linkedListVoid.h b/pvDataApp/misc/linkedListVoid.h new file mode 100644 index 0000000..d19c648 --- /dev/null +++ b/pvDataApp/misc/linkedListVoid.h @@ -0,0 +1,73 @@ +/* linkedListVoid.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 "pvType.h" +#include "showConstructDestruct.h" +#ifndef LINKEDLISTVOID_H +#define LINKEDLISTVOID_H +namespace epics { namespace pvData { + +class LinkedListVoid; +class LinkedListVoidNode; + +class LinkedListVoidNode { +public: + ~LinkedListVoidNode(); + static ConstructDestructCallback *getConstructDestructCallback(); + void *getObject(); + bool isOnList(); +protected: + LinkedListVoidNode(void *object); +private: + LinkedListVoidNode(bool isHead); + friend class LinkedListVoid; + void *object; + LinkedListVoidNode *before; + LinkedListVoidNode *after; + LinkedListVoid *linkedListVoid; + // do not implement the following + LinkedListVoidNode(const LinkedListVoidNode&); + LinkedListVoidNode & operator=(const LinkedListVoidNode&); +}; + +class LinkedListVoid { +public: + ~LinkedListVoid(); + static ConstructDestructCallback *getConstructDestructCallback(); + int getLength(); + void addTail(LinkedListVoidNode *listNode); + void addHead(LinkedListVoidNode *listNode); + void insertAfter(LinkedListVoidNode *listNode, + LinkedListVoidNode *addNode); + void insertBefore(LinkedListVoidNode *listNode, + LinkedListVoidNode *addNode); + LinkedListVoidNode *removeTail(); + LinkedListVoidNode *removeHead(); + void remove(LinkedListVoidNode *listNode); + void remove(void * object); + LinkedListVoidNode *getHead(); + LinkedListVoidNode *getTail(); + LinkedListVoidNode *getNext(LinkedListVoidNode *listNode); + LinkedListVoidNode *getPrev(LinkedListVoidNode *listNode); + bool isEmpty(); + bool contains(void * object); +protected: + LinkedListVoid(); +private: + friend class LinkedListVoidNode; + LinkedListVoidNode *head; + int length; + // do not implement the following + LinkedListVoid(const LinkedListVoid&); + LinkedListVoid & operator=(const LinkedListVoid&); +}; + +}} +#endif /* LINKEDLISTVOID_H */ + + + diff --git a/pvDataApp/misc/lock.h b/pvDataApp/misc/lock.h new file mode 100644 index 0000000..70e7a2d --- /dev/null +++ b/pvDataApp/misc/lock.h @@ -0,0 +1,38 @@ +/* lock.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 LOCK_H +#define LOCK_H +#include +#include "noDefaultMethods.h" +/* This is based on item 14 of + * Effective C++, Third Edition, Scott Meyers + */ + +namespace epics { namespace pvData { + +class Mutex { +public: + Mutex() : id(epicsMutexMustCreate()){} + ~Mutex() { epicsMutexDestroy(id) ;} + void lock(){epicsMutexMustLock(id);} + void unlock(){epicsMutexUnlock(id);} +private: + epicsMutexId id; +}; + + +class Lock : private NoDefaultMethods { +public: + explicit Lock(Mutex *pm) + : mutexPtr(pm) + {mutexPtr->lock();} + ~Lock(){mutexPtr->unlock();} +private: + Mutex *mutexPtr; +}; +}} +#endif /* LOCK_H */ diff --git a/pvDataApp/misc/messageQueue.cpp b/pvDataApp/misc/messageQueue.cpp new file mode 100644 index 0000000..5743914 --- /dev/null +++ b/pvDataApp/misc/messageQueue.cpp @@ -0,0 +1,166 @@ +/* messageQueue.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 "pvType.h" +#include "lock.h" +#include "requester.h" +#include "noDefaultMethods.h" +#include "showConstructDestruct.h" +#include "queue.h" +#include "messageQueue.h" + +namespace epics { namespace pvData { + +static volatile int64 totalQueueConstruct = 0; +static volatile int64 totalQueueDestruct = 0; +static Mutex *globalMutex = 0; + +static int64 getTotalQueueConstruct() +{ + Lock xx(globalMutex); + return totalQueueConstruct; +} + +static int64 getTotalQueueDestruct() +{ + Lock xx(globalMutex); + return totalQueueDestruct; +} + +static ConstructDestructCallback *pCDCallbackQueue; + +static void initPvt() +{ + static Mutex mutex = Mutex(); + Lock xx(&mutex); + if(globalMutex==0) { + globalMutex = new Mutex(); + pCDCallbackQueue = new ConstructDestructCallback( + "messageQueue", + getTotalQueueConstruct,getTotalQueueDestruct,0); + } +} + +typedef MessageNode * MessageNodePtr; +typedef QueueElement MessageElement; +typedef MessageElement *MessageElementPtr; +typedef Queue MessageNodeQueue; + + +MessageNode::MessageNode() +: message(String("")),messageType(infoMessage){} + +MessageNode::~MessageNode() {} + +String MessageNode::getMessage() const { return message;}; + +MessageType MessageNode::getMessageType() const { return messageType;} + + +class MessageQueuePvt { +public: + MessageNodePtr *messageNodeArray; + MessageNodeQueue *queue; + MessageNodePtr lastPut; + MessageElementPtr lastGet; + int size; + int overrun; +}; + +MessageQueue::MessageQueue(int size) +: pImpl(new MessageQueuePvt) +{ + initPvt(); + Lock xx(globalMutex); + totalQueueConstruct++; + pImpl->size = size; + pImpl->overrun = 0; + pImpl->lastPut = 0; + pImpl->lastGet = 0; + pImpl->messageNodeArray = new MessageNodePtr[size]; + for(int i=0; imessageNodeArray[i] = new MessageNode(); + } + pImpl->queue = new MessageNodeQueue(pImpl->messageNodeArray,size); +} + +MessageQueue::~MessageQueue() +{ + delete pImpl->queue; + for(int i=0; i< pImpl->size; i++) { + delete pImpl->messageNodeArray[i]; + } + delete[] pImpl->messageNodeArray; + initPvt(); + Lock xx(globalMutex); + totalQueueDestruct++; +} + +MessageNode *MessageQueue::get() { + if(pImpl->lastGet!=0) { + throw std::logic_error( + String("MessageQueue::get() but did not release last")); + } + MessageElementPtr element = pImpl->queue->getUsed(); + if(element==0) return 0; + pImpl->lastGet = element; + return element->getObject(); +} + +void MessageQueue::release() { + if(pImpl->lastGet==0) return; + pImpl->queue->releaseUsed(pImpl->lastGet); + pImpl->lastGet = 0; +} + +bool MessageQueue::put(String message,MessageType messageType,bool replaceLast) +{ + MessageElementPtr element = pImpl->queue->getFree(); + if(element!=0) { + MessageNodePtr node = element->getObject(); + node->message = message; + node->messageType = messageType; + pImpl->lastPut = node; + pImpl->queue->setUsed(element); + return true; + } + pImpl->overrun++; + if(replaceLast) { + MessageNodePtr node = pImpl->lastPut; + node->message = message; + node->messageType = messageType; + } + return false; +} + +bool MessageQueue::isEmpty() const +{ + int free = pImpl->queue->getNumberFree(); + if(free==pImpl->size) return true; + return false; +} + +bool MessageQueue::isFull() const +{ + if(pImpl->queue->getNumberFree()==0) return true; + return false; +} + +int MessageQueue::getClearOverrun() +{ + int num = pImpl->overrun; + pImpl->overrun = 0; + return num; +} + +}} diff --git a/pvDataApp/misc/messageQueue.h b/pvDataApp/misc/messageQueue.h new file mode 100644 index 0000000..b029afe --- /dev/null +++ b/pvDataApp/misc/messageQueue.h @@ -0,0 +1,49 @@ +/* messageQueue.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 MESSAGEQUEUE_H +#define MESSAGEQUEUE_H +#include "pvType.h" +#include "requester.h" +#include "noDefaultMethods.h" +#include "showConstructDestruct.h" + +namespace epics { namespace pvData { + +class MessageNode { +public: + String getMessage() const; + MessageType getMessageType() const; +private: + MessageNode(); + ~MessageNode(); + friend class MessageQueue; + String message; + MessageType messageType; +}; + +class MessageQueue : private NoDefaultMethods { +public: + MessageQueue(int size); + ~MessageQueue(); + static ConstructDestructCallback *getConstructDestructCallback(); + MessageNode *get(); + // must call release before next get + void release(); + // return (false,true) if message (was not, was) put into queue + bool put(String message,MessageType messageType,bool replaceLast); + bool isEmpty() const; + bool isFull() const; + int getClearOverrun(); +private: + class MessageQueuePvt *pImpl; +}; + +}} +#endif /* MESSAGEQUEUE_H */ + + + diff --git a/pvDataApp/misc/noDefaultMethods.h b/pvDataApp/misc/noDefaultMethods.h new file mode 100644 index 0000000..89eb52d --- /dev/null +++ b/pvDataApp/misc/noDefaultMethods.h @@ -0,0 +1,27 @@ +/* noDefaultMethods.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 NO_DEFAULT_METHODS_H +#define NO_DEFAULT_METHODS_H +namespace epics { namespace pvData { +/* This is based on Item 6 of + * Effective C++, Third Edition, Scott Meyers + */ + + + class NoDefaultMethods { + protected: + // allow by derived objects + NoDefaultMethods(){}; + ~NoDefaultMethods(){} + private: + // do not implment + NoDefaultMethods(const NoDefaultMethods&); + NoDefaultMethods & operator=(const NoDefaultMethods &); + }; + +}} +#endif /* NO_DEFAULT_METHODS_H */ diff --git a/pvDataApp/misc/pvType.h b/pvDataApp/misc/pvType.h new file mode 100644 index 0000000..5389366 --- /dev/null +++ b/pvDataApp/misc/pvType.h @@ -0,0 +1,28 @@ +/* pvType.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 +#ifndef PVTYPE_H +#define PVTYPE_H + +namespace epics { namespace pvData { + +typedef signed char int8; +typedef short int16; +typedef int int32; +typedef long long int64; +typedef unsigned int uint32; +typedef unsigned long long uint64; + +typedef std::string String; +typedef std::string * StringBuilder; +typedef String* StringArray; + +}} +#endif /* PVTYPE_H */ + + + diff --git a/pvDataApp/misc/queue.h b/pvDataApp/misc/queue.h new file mode 100644 index 0000000..2b808d6 --- /dev/null +++ b/pvDataApp/misc/queue.h @@ -0,0 +1,54 @@ +/* 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: + T *getObject() { return static_cast(QueueElementVoid::getObject());} +protected: + QueueElement(T *object) : QueueElementVoid(static_cast(object)){} + ~QueueElement() {} + friend class Queue; +}; + +template +class Queue : private QueueVoid { +public: + Queue(T *array[],int number) + : QueueVoid((ObjectPtr*)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..a33257e --- /dev/null +++ b/pvDataApp/misc/queueVoid.cpp @@ -0,0 +1,184 @@ +/* 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(ObjectPtr object) +: object(object) +{ + initPvt(); + Lock xx(globalMutex); + totalElementConstruct++; +} + + +QueueElementVoid::~QueueElementVoid() +{ + Lock xx(globalMutex); + totalElementDestruct++; +} + +ConstructDestructCallback *QueueElementVoid::getConstructDestructCallback() +{ + initPvt(); + return pCDCallbackQueueNode; +} + +ObjectPtr QueueElementVoid::getObject() { + return object; +} + + + +QueueVoid::QueueVoid(ObjectPtr object[],int number) +: array(new QueueElementVoidPtr[number]),number(number), + numberFree(number),numberUsed(0), + nextGetFree(0),nextSetUsed(), + nextGetUsed(0),nextReleaseUsed(0) +{ + for(int i=0; i=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..559f932 --- /dev/null +++ b/pvDataApp/misc/queueVoid.h @@ -0,0 +1,61 @@ +/* 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 void * ObjectPtr; +typedef QueueElementVoid * QueueElementVoidPtr; +typedef QueueElementVoidPtr * QueueElementVoidPtrArray; + +class QueueElementVoid { +public: + static ConstructDestructCallback *getConstructDestructCallback(); +protected: + ObjectPtr getObject(); + QueueElementVoid(ObjectPtr object); + ~QueueElementVoid(); + ObjectPtr object; + friend class QueueVoid; +}; + + +class QueueVoid { +public: + static ConstructDestructCallback *getConstructDestructCallback(); +protected: + QueueVoid(ObjectPtr array[],int number); + ~QueueVoid(); + void clear(); + int getNumberFree(); + int capacity(); + QueueElementVoidPtr getFree(); + void setUsed(QueueElementVoid *queueElement); + QueueElementVoid *getUsed(); + void releaseUsed(QueueElementVoid *queueElement); +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/misc/requester.cpp b/pvDataApp/misc/requester.cpp new file mode 100644 index 0000000..dd36d01 --- /dev/null +++ b/pvDataApp/misc/requester.cpp @@ -0,0 +1,20 @@ +/* requester.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 "requester.h" +namespace epics { namespace pvData { + +static std::string typeName[] = { + String("info"), + String("warning"), + String("error"), + String("fatalError") +}; + +StringArray messageTypeName = typeName; + +}} diff --git a/pvDataApp/misc/requester.h b/pvDataApp/misc/requester.h new file mode 100644 index 0000000..9f826c5 --- /dev/null +++ b/pvDataApp/misc/requester.h @@ -0,0 +1,28 @@ +/* requester.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 +#ifndef REQUESTER_H +#define REQUESTER_H +#include "pvType.h" +namespace epics { namespace pvData { + +class Requester; + +enum MessageType { + infoMessage,warningMessage,errorMessage,fatalErrorMessage +}; + +extern StringArray messageTypeName; + +class Requester { +public: + virtual String getRequesterName() = 0; + virtual void message(String message,MessageType messageType) = 0; +}; + +}} +#endif /* REQUESTER_H */ diff --git a/pvDataApp/misc/serialize.h b/pvDataApp/misc/serialize.h new file mode 100644 index 0000000..45e65f8 --- /dev/null +++ b/pvDataApp/misc/serialize.h @@ -0,0 +1,54 @@ +/* serialize.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 SERIALIZE_H +#define SERIALIZE_H +#include "bitSet.h" +#include "byteBuffer.h" +namespace epics { namespace pvData { + + class SerializableControl; + class DeserializableControl; + class Serializable; + class BitSetSerializable; + class SerializableArray; + + class SerializableControl { + public: + virtual void flushSerializeBuffer() =0; + virtual void ensureBuffer(int size) =0; + }; + + class DeserializableControl { + public: + virtual void ensureData(int size) =0; + }; + + class Serializable { + public: + virtual void serialize(ByteBuffer *buffer, + SerializableControl *flusher) = 0; + virtual void deserialize(ByteBuffer *buffer, + DeserializableControl *flusher) = 0; + }; + + class BitSetSerializable { + public: + virtual void serialize(ByteBuffer *buffer, + SerializableControl *flusher,BitSet *bitSet) = 0; + virtual void deserialize(ByteBuffer *buffer, + DeserializableControl *flusher,BitSet *bitSet) = 0; + }; + + + class SerializableArray : public Serializable { + public: + virtual void serialize(ByteBuffer *buffer, + SerializableControl *flusher, int offset, int count) = 0; + }; + +}} +#endif /* SERIALIZE_H */ diff --git a/pvDataApp/misc/serializeHelper.cpp b/pvDataApp/misc/serializeHelper.cpp new file mode 100644 index 0000000..549432d --- /dev/null +++ b/pvDataApp/misc/serializeHelper.cpp @@ -0,0 +1,125 @@ +/** + * 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. + */ +/* + * serializeHelper.cpp + * + * Created on: Oct 22, 2010 + * Author: Miha vitorovic + */ + +#include +#include + +#include "epicsException.h" +#include "byteBuffer.h" +#include "serializeHelper.h" + + +using namespace std; + +namespace epics { + namespace pvData { + + void SerializeHelper::writeSize(int s, ByteBuffer* buffer, + SerializableControl* flusher) { + flusher->ensureBuffer(sizeof(int64)+1); + SerializeHelper::writeSize(s, buffer); + } + + void SerializeHelper::writeSize(int s, ByteBuffer* buffer) { + if(s==-1) // null + buffer->putByte(-1); + else if(s<254) + buffer->putByte(s); + else + buffer->putByte(-2)->putInt(s); // (byte)-2 + size + } + + int SerializeHelper::readSize(ByteBuffer* buffer, + DeserializableControl* control) { + control->ensureData(1); + int8 b = buffer->getByte(); + if(b==-1) + return -1; + else if(b==-2) { + control->ensureData(sizeof(int32)); + int32 s = buffer->getInt(); + if(s<0) throw EpicsException("negative array size"); + return s; + } + else + return (int)(b<0 ? b+256 : b); + } + + void SerializeHelper::serializeString(const String& value, + ByteBuffer* buffer, SerializableControl* flusher) { + int len = value.length(); + SerializeHelper::writeSize(len, buffer, flusher); + int i = 0; + while(true) { + int maxToWrite = min(len-i, buffer->getRemaining()); + buffer->put(value.data(), i, maxToWrite); // UTF-8 + i += maxToWrite; + if(iflushSerializeBuffer(); + else + break; + } + } + + void SerializeHelper::serializeSubstring(const String& value, + int offset, int count, ByteBuffer* buffer, + SerializableControl* flusher) { + if(offset<0) + offset = 0; + else if(offset>(int)value.length()) offset = value.length(); + + if(offset+count>(int)value.length()) count = value.length()-offset; + + SerializeHelper::writeSize(count, buffer, flusher); + int i = 0; + while(true) { + int maxToWrite = min(count-i, buffer->getRemaining()); + buffer->put(value.data(), offset+i, maxToWrite); // UTF-8 + i += maxToWrite; + if(iflushSerializeBuffer(); + else + break; + } + } + + String SerializeHelper::deserializeString(ByteBuffer* buffer, + DeserializableControl* control) { + + int size = SerializeHelper::readSize(buffer, control); + if(size>=0) { + char* retBuffer = new char[size]; // get the return buffer + try { + int i = 0; + while(true) { + int toRead = min(size-i, buffer->getRemaining()); + buffer->get(retBuffer, i, toRead); + i += toRead; + if(iensureData(1); + else + break; + } + String s = String(retBuffer, size); + delete retBuffer; + return s; + } catch(...) { + delete retBuffer; // remove the buffer + throw; + } + } + else + return String(""); + } + + } +} diff --git a/pvDataApp/misc/serializeHelper.h b/pvDataApp/misc/serializeHelper.h new file mode 100644 index 0000000..8ce9605 --- /dev/null +++ b/pvDataApp/misc/serializeHelper.h @@ -0,0 +1,103 @@ +/** + * 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. + */ +/* + * serializeHelper.h + * + * Created on: Oct 21, 2010 + * Author: Miha Vitorovic + */ + +#ifndef SERIALIZEHELPER_H_ +#define SERIALIZEHELPER_H_ + +#include "serialize.h" +#include "byteBuffer.h" +#include "noDefaultMethods.h" +#include "../pv/pvIntrospect.h" + +namespace epics { + namespace pvData { + + class SerializeHelper : public NoDefaultMethods { + public: + + /** + * Serialize array size. + * + * @param[in] s size to encode + * @param[in] buffer serialization buffer + * @param[in] flusher flusher + */ + static void writeSize(int s, ByteBuffer* buffer, + SerializableControl* flusher); + + /** + * Deserialize array size. + * + * @param[in] buffer deserialization buffer. + * @returns array size. + */ + static int readSize(ByteBuffer* buffer, + DeserializableControl* control); + + /** + * String serialization helper method. + * + * @param[in] value String to serialize + * @param[in] buffer serialization buffer + * @param[in] flusher flusher + */ + static void serializeString(const String& value, ByteBuffer* buffer, + SerializableControl* flusher); + + /** + * String serialization helper method. + * + * @param[in] value String to serialize + * @param[in] offset start of the substring in {@code value} + * @param[in] count the number of characters to write + * @param[in] buffer serialization buffer + * @param[in] flusher flusher + */ + static void serializeSubstring(const String& value, int offset, + int count, ByteBuffer* buffer, + SerializableControl* flusher); + + /** + * String deserialization helper method. + * TODO This method cannot return "null", but Java implementation + * could have serialized "null" value as well. We need to decide + * how to deserialize "null". + * + * @param[in] buffer deserialization buffer + * @param[in] control control + * @returns deserialized string + * + * @todo This method cannot return "null", but Java implementation + * could have serialized "null" value as well. We need to decide + * how to deserialize "null". + */ + static String deserializeString(ByteBuffer* buffer, + DeserializableControl* control); + + private: + SerializeHelper() {}; + ~SerializeHelper() {}; + + /** + * Serialize array size. + * + * @param[in] s size to encode + * @param[in] buffer serialization buffer + */ + static void writeSize(int s, ByteBuffer* buffer); + + }; + + } +} + +#endif /* SERIALIZEHELPER_H_ */ diff --git a/pvDataApp/misc/showConstructDestruct.cpp b/pvDataApp/misc/showConstructDestruct.cpp new file mode 100644 index 0000000..c6f6c61 --- /dev/null +++ b/pvDataApp/misc/showConstructDestruct.cpp @@ -0,0 +1,127 @@ +/* showConstructDestruct.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 "noDefaultMethods.h" +#include "lock.h" +#include "pvType.h" +#include "linkedList.h" +#include "showConstructDestruct.h" + +namespace epics { namespace pvData { + +static ShowConstructDestruct *pShowConstructDestruct = 0; +static Mutex *globalMutex = 0; +typedef LinkedListNode ListNode; +typedef LinkedList List; +static List *list; + +ConstructDestructCallback::ConstructDestructCallback( + String name, + getTotal construct, + getTotal destruct, + getTotal reference) +: name(name), construct(construct), destruct(destruct) ,reference(reference) +{ + getShowConstructDestruct()->registerCallback(this); +} + +ConstructDestructCallback::~ConstructDestructCallback() {} + +String ConstructDestructCallback::getConstructName() +{ + return name; +} + +int64 ConstructDestructCallback::getTotalConstruct() +{ + return construct(); +} + +int64 ConstructDestructCallback:: getTotalDestruct() +{ + return destruct(); +} + +int64 ConstructDestructCallback::getTotalReferenceCount() +{ + if(reference==0) return 0; + return reference(); +} + + +ShowConstructDestruct::ShowConstructDestruct() {} + +void ShowConstructDestruct::constuctDestructTotals(FILE *fd) +{ + getShowConstructDestruct(); // make it initialize + Lock xx(globalMutex); + ListNode *node = list->getHead(); + while(node!=0) { + ConstructDestructCallback *callback = node->getObject(); + String name = callback->getConstructName(); + int64 reference = callback->getTotalReferenceCount(); + int64 construct = callback->getTotalConstruct(); + int64 destruct = callback->getTotalDestruct(); + fprintf(fd,"%s: totalConstruct %lli totalDestruct %lli", + name.c_str(),construct,destruct); + if(reference>0) fprintf(fd," totalReference %lli",reference); + fprintf(fd,"\n"); + node = list->getNext(node); + } +} + +void ShowConstructDestruct::registerCallback(ConstructDestructCallback *callback) +{ + static ConstructDestructCallback *listCallback = 0; + static ConstructDestructCallback *listNodeCallback = 0; + Lock xx(globalMutex); + ListNode *listNode = 0; + if(list==0) { + if(callback->getConstructName().compare("linkedListNode")==0) { + listNodeCallback = callback; + } else if(callback->getConstructName().compare("linkedList")==0) { + listCallback = callback; + } else { + throw std::logic_error(String("ShowConstructDestruct::registerCallback")); + } + return; + } + if(listCallback!=0) { + if(listNodeCallback==0) { + throw std::logic_error(String( + "ShowConstructDestruct::registerCallback expected listNodeCallback!=0")); + } + listNode = new ListNode(listNodeCallback); + list->addTail(listNode); + listNode = new ListNode(listCallback); + list->addTail(listNode); + listCallback = 0; + listNodeCallback = 0; + } + listNode = new ListNode(callback); + list->addTail(listNode); +} + +ShowConstructDestruct * getShowConstructDestruct() +{ + static Mutex mutex = Mutex(); + Lock xx(&mutex); + if(pShowConstructDestruct==0) { + globalMutex = new Mutex(); + pShowConstructDestruct = new ShowConstructDestruct(); + list = new List(); + } + return pShowConstructDestruct; +} + +}} diff --git a/pvDataApp/misc/showConstructDestruct.h b/pvDataApp/misc/showConstructDestruct.h new file mode 100644 index 0000000..f8fe20c --- /dev/null +++ b/pvDataApp/misc/showConstructDestruct.h @@ -0,0 +1,53 @@ +/* showConstructDestruct.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 SHOWCONSTRUCTDESTRUCT_H +#define SHOWCONSTRUCTDESTRUCT_H +#include +#include +#include +#include +#include + +#include "noDefaultMethods.h" +#include "pvType.h" + +namespace epics { namespace pvData { + +typedef int64 (*getTotal)(); + +class ConstructDestructCallback : private NoDefaultMethods { +public: + ConstructDestructCallback( + String name, + getTotal construct, + getTotal destruct, + getTotal reference); + String getConstructName(); + int64 getTotalConstruct(); + int64 getTotalDestruct(); + int64 getTotalReferenceCount(); +private: + ~ConstructDestructCallback(); + String name; + getTotal construct; + getTotal destruct; + getTotal reference; +}; + +class ShowConstructDestruct : private NoDefaultMethods { +public: + void constuctDestructTotals(FILE *fd); + void registerCallback(ConstructDestructCallback *callback); +private: + ShowConstructDestruct(); + friend ShowConstructDestruct* getShowConstructDestruct(); +}; + +extern ShowConstructDestruct* getShowConstructDestruct(); + +}} +#endif /* SHOWCONSTRUCTDESTRUCT_H */ diff --git a/pvDataApp/misc/thread.cpp b/pvDataApp/misc/thread.cpp new file mode 100644 index 0000000..5c0dbff --- /dev/null +++ b/pvDataApp/misc/thread.cpp @@ -0,0 +1,210 @@ +/* thread.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 +#include +#include "lock.h" +#include "event.h" +#include "thread.h" +#include "linkedList.h" + +namespace epics { namespace pvData { + +static unsigned int epicsPriority[] = { + epicsThreadPriorityLow, + epicsThreadPriorityLow + 15, + epicsThreadPriorityMedium - 15, + epicsThreadPriorityMedium, + epicsThreadPriorityMedium + 15, + epicsThreadPriorityHigh - 15, + epicsThreadPriorityHigh +}; + +unsigned int const * const ThreadPriorityFunc::getEpicsPriorities() +{ + return epicsPriority; +} + + +static String threadPriorityNames[] = { + String("lowest"),String("lower"),String("low"), + String("middle"), + String("high"),String("higher"),String("highest") +}; + +class ThreadListElement; +typedef LinkedListNode ThreadListNode; +typedef LinkedList ThreadList; + +static volatile int64 totalConstruct = 0; +static volatile int64 totalDestruct = 0; +static Mutex *globalMutex = 0; +static ThreadList *threadList; + +static int64 getTotalConstruct() +{ + Lock xx(globalMutex); + return totalConstruct; +} + +static int64 getTotalDestruct() +{ + Lock xx(globalMutex); + return totalDestruct; +} + +static ConstructDestructCallback *pConstructDestructCallback; + +static void init() +{ + static Mutex mutex = Mutex(); + Lock xx(&mutex); + if(globalMutex==0) { + globalMutex = new Mutex(); + threadList = new ThreadList(); + pConstructDestructCallback = new ConstructDestructCallback( + String("thread"), + getTotalConstruct,getTotalDestruct,0); + } +} + + +class ThreadListElement { +public: + ThreadListElement(Thread *thread) : thread(thread),node(new ThreadListNode(this)){} + ~ThreadListElement(){delete node;} + Thread *thread; + ThreadListNode *node; +}; + + +int ThreadPriorityFunc::getEpicsPriority(ThreadPriority threadPriority) { + return epicsPriority[threadPriority]; +} + +extern "C" void myFunc ( void * pPvt ); + + +class ThreadPvt { +public: + ThreadPvt(Thread *thread,String name, + ThreadPriority priority, Runnable*runnable); + virtual ~ThreadPvt(); + void ready(); +public: // only used within this source module + Thread *thread; + String name; + ThreadPriority priority; + Runnable *runnable; + bool isReady; + ThreadListElement *threadListElement; + Event *waitDone; + epicsThreadId id; +}; + +extern "C" void myFunc ( void * pPvt ) +{ + ThreadPvt *threadPvt = (ThreadPvt *)pPvt; + threadPvt->runnable->run(); + threadPvt->waitDone->signal(); +} + +ThreadPvt::ThreadPvt(Thread *thread,String name, + ThreadPriority priority, Runnable *runnable) +: thread(thread),name(name),priority(priority), + runnable(runnable), + isReady(false), + threadListElement(new ThreadListElement(thread)), + waitDone(new Event()), + id(epicsThreadCreate( + name.c_str(), + epicsPriority[priority], + epicsThreadGetStackSize(epicsThreadStackSmall), + myFunc,this)) +{ + init(); + Lock xx(globalMutex); + threadList->addTail(threadListElement->node); + totalConstruct++; +} + +ThreadPvt::~ThreadPvt() +{ + bool result = waitDone->wait(2.0); + if(!result) { + throw std::logic_error(String("delete thread but run did not return")); + String message("destroy thread "); + message += thread->getName(); + message += " but run did not return"; + throw std::logic_error(message); + } + if(!threadListElement->node->isOnList()) { + String message("destroy thread "); + message += thread->getName(); + message += " is not on threadlist"; + throw std::logic_error(message); + } + threadList->remove(threadListElement->node); + delete waitDone; + delete threadListElement; + Lock xx(globalMutex); + totalDestruct++; +} + +Thread::Thread(String name,ThreadPriority priority,Runnable *runnable) +: pImpl(new ThreadPvt(this,name,priority,runnable)) +{ +} + +Thread::~Thread() +{ + delete pImpl; +} + +ConstructDestructCallback *Thread::getConstructDestructCallback() +{ + init(); + return pConstructDestructCallback; +} + +void Thread::sleep(double seconds) +{ + epicsThreadSleep(seconds);; +} + +String Thread::getName() +{ + return pImpl->name; +} + +ThreadPriority Thread::getPriority() +{ + return pImpl->priority; +} + +void Thread::showThreads(StringBuilder buf) +{ + init(); + Lock xx(globalMutex); + ThreadListNode *node = threadList->getHead(); + while(node!=0) { + Thread *thread = node->getObject()->thread; + *buf += thread->getName(); + *buf += " "; + *buf += threadPriorityNames[thread->getPriority()]; + *buf += "\n"; + node = threadList->getNext(node); + } +} + +}} diff --git a/pvDataApp/misc/thread.h b/pvDataApp/misc/thread.h new file mode 100644 index 0000000..664a20d --- /dev/null +++ b/pvDataApp/misc/thread.h @@ -0,0 +1,53 @@ +/* thread.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 THREAD_H +#define THREAD_H +#include "noDefaultMethods.h" +#include "pvType.h" +#include "showConstructDestruct.h" + +namespace epics { namespace pvData { + +enum ThreadPriority { + lowestPriority, + lowerPriority, + lowPriority, + middlePriority, + highPriority, + higherPriority, + highestPriority +}; + +class ThreadPriorityFunc { +public: + static unsigned int const * const getEpicsPriorities(); + static int getEpicsPriority(ThreadPriority threadPriority); +}; + +class Runnable{ +public: + virtual void run() = 0; +}; + +class Thread; + +class Thread : private NoDefaultMethods { +public: + Thread(String name,ThreadPriority priority,Runnable *runnable); + ~Thread(); + static ConstructDestructCallback *getConstructDestructCallback(); + String getName(); + ThreadPriority getPriority(); + static void showThreads(StringBuilder buf); + static void sleep(double seconds); +private: + class ThreadPvt *pImpl; + friend class ThreadPvt; +}; + +}} +#endif /* THREAD_H */ diff --git a/pvDataApp/misc/timeFunction.cpp b/pvDataApp/misc/timeFunction.cpp new file mode 100644 index 0000000..3435f1d --- /dev/null +++ b/pvDataApp/misc/timeFunction.cpp @@ -0,0 +1,54 @@ +/* timeFunction.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 "noDefaultMethods.h" +#include "pvType.h" +#include "timeStamp.h" + +namespace epics { namespace pvData { + +class TimeFunctionRequester { +public: + virtual void function() = 0; +}; + +class TimeFunction : private NoDefaultMethods { +public: + TimeFunction(TimeFunctionRequester *requester); + ~TimeFunction(); + double timeCall(); +private: + TimeFunctionRequester *requester; +}; + +TimeFunction::TimeFunction(TimeFunctionRequester *requester) +: requester(requester) {} + + +TimeFunction::~TimeFunction() {} + +double TimeFunction::timeCall() +{ + TimeStamp startTime; + TimeStamp endTime; + double perCall = 0.0; + long ntimes = 1; + while(true) { + startTime.getCurrent(); + for(long i=0; ifunction(); + endTime.getCurrent(); + double diff = TimeStamp::diff(endTime,startTime); + if(diff>=1.0) { + perCall = diff/(double)ntimes; + break; + } + ntimes *= 2; + } + return perCall; + +} + +}} diff --git a/pvDataApp/misc/timeFunction.h b/pvDataApp/misc/timeFunction.h new file mode 100644 index 0000000..2bd6c2b --- /dev/null +++ b/pvDataApp/misc/timeFunction.h @@ -0,0 +1,30 @@ +/* timeFunction.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 TIMEFUNCTION_H +#define TIMEFUNCTION_H +#include "noDefaultMethods.h" +#include "pvType.h" + +namespace epics { namespace pvData { + +class TimeFunctionRequester { +public: + virtual void function() = 0; +}; + +class TimeFunction : private NoDefaultMethods { +public: + TimeFunction(TimeFunctionRequester *requester); + ~TimeFunction(); + double timeCall(); +private: + TimeFunctionRequester *requester; +}; + + +}} +#endif /* TIMEFUNCTION_H */ diff --git a/pvDataApp/misc/timeStamp.cpp b/pvDataApp/misc/timeStamp.cpp new file mode 100644 index 0000000..064001e --- /dev/null +++ b/pvDataApp/misc/timeStamp.cpp @@ -0,0 +1,183 @@ +/* timeStamp.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 "noDefaultMethods.h" +#include "pvType.h" +#include "timeStamp.h" + +namespace epics { namespace pvData { + +int32 milliSecPerSec = 1000; +int32 microSecPerSec = milliSecPerSec*milliSecPerSec; +int32 nanoSecPerSec = milliSecPerSec*microSecPerSec; +int64 posixEpochAtEpicsEpoch = POSIX_TIME_AT_EPICS_EPOCH; + +TimeStamp::TimeStamp(int64 secondsPastEpoch,int32 nanoSeconds) +: secondsPastEpoch(secondsPastEpoch),nanoSeconds(nanoSeconds) +{ + normalize(); +} + +void TimeStamp::normalize() +{ + if(nanoSeconds>=0 && nanoSeconds=nanoSecPerSec) { + nanoSeconds -= nanoSecPerSec; + secondsPastEpoch++; + } + while(nanoSeconds<0) { + nanoSeconds += nanoSecPerSec; + secondsPastEpoch--; + } +} + +void TimeStamp::fromTime_t(const time_t & tt) +{ + epicsTimeStamp epicsTime; + epicsTimeFromTime_t(&epicsTime,tt); + secondsPastEpoch = epicsTime.secPastEpoch + posixEpochAtEpicsEpoch; + nanoSeconds = epicsTime.nsec; +} + +void TimeStamp::toTime_t(time_t &tt) const +{ + epicsTimeStamp epicsTime; + epicsTime.secPastEpoch = secondsPastEpoch-posixEpochAtEpicsEpoch; + epicsTime.nsec = nanoSeconds; + epicsTimeToTime_t(&tt,&epicsTime); +} + +void TimeStamp::put(int64 milliseconds) +{ + secondsPastEpoch = milliseconds/1000; + nanoSeconds = (milliseconds%1000)*1000000; +} + +void TimeStamp::getCurrent() +{ + epicsTimeStamp epicsTime; + epicsTimeGetCurrent(&epicsTime); + secondsPastEpoch = epicsTime.secPastEpoch; + secondsPastEpoch += posixEpochAtEpicsEpoch; + nanoSeconds = epicsTime.nsec; +} + +double TimeStamp::toSeconds() const +{ + double value = secondsPastEpoch; + double nano = nanoSeconds; + value += nano/1e9; + return value; +} + +int64 TimeStamp::diffInt(TimeStamp const & left,TimeStamp const&right ) +{ + int64 sl = left.secondsPastEpoch; + int32 nl = left.nanoSeconds; + int64 sr = right.secondsPastEpoch; + int32 nr = right.nanoSeconds; + int64 sdiff = sl - sr; + sdiff *= nanoSecPerSec; + sdiff += nl - nr; + return sdiff; +} + +bool TimeStamp::operator==(TimeStamp const &right) const +{ + int64 sdiff = diffInt(*this,right); + if(sdiff==0) return true; + return false; +} + +bool TimeStamp::operator!=(TimeStamp const &right) const +{ + int64 sdiff = diffInt(*this,right); + if(sdiff!=0) return true; + return false; +} + +bool TimeStamp::operator<=(TimeStamp const &right) const +{ + int64 sdiff = diffInt(*this,right); + if(sdiff<=0) return true; + return false; +} + +bool TimeStamp::operator< (TimeStamp const &right) const +{ + int64 sdiff = diffInt(*this,right); + if(sdiff<0) return true; + return false; +} + +bool TimeStamp::operator>=(TimeStamp const &right) const +{ + int64 sdiff = diffInt(*this,right); + if(sdiff>=0) return true; + return false; +} + +bool TimeStamp::operator>(TimeStamp const &right) const +{ + int64 sdiff = diffInt(*this,right); + if(sdiff>0) return true; + return false; +} + +double TimeStamp::diff(TimeStamp const & a,TimeStamp const & b) +{ + double result = a.secondsPastEpoch - b.secondsPastEpoch; + result += (a.nanoSeconds - b.nanoSeconds)/1e9; + return result; +} + + +TimeStamp & TimeStamp::operator+=(int64 seconds) +{ + secondsPastEpoch += seconds; + return *this; +} + +TimeStamp & TimeStamp::operator-=(int64 seconds) +{ + secondsPastEpoch -= seconds; + return *this; +} + +TimeStamp & TimeStamp::operator+=(double seconds) +{ + int64 secs = seconds; + int64 nano = (seconds - secs)*1e9; + nanoSeconds += nano; + if(nanoSeconds>nanoSecPerSec) { + nanoSeconds -= nanoSecPerSec; + secondsPastEpoch += 1; + } else if(nanoSeconds<-nanoSecPerSec) { + nanoSeconds += -nanoSecPerSec; + secondsPastEpoch -= 1; + } + secondsPastEpoch += secs; + return *this; +} + +TimeStamp & TimeStamp::operator-=(double seconds) +{ + return operator+=(-seconds); +} + +int64 TimeStamp::getMilliseconds() +{ + return secondsPastEpoch*1000 + nanoSeconds/1000000; +} + +}} diff --git a/pvDataApp/misc/timeStamp.h b/pvDataApp/misc/timeStamp.h new file mode 100644 index 0000000..4398acc --- /dev/null +++ b/pvDataApp/misc/timeStamp.h @@ -0,0 +1,63 @@ +/* timeStamp.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 TIMESTAMP_H +#define TIMESTAMP_H +#include +#include "epicsTime.h" +#include "pvType.h" + +namespace epics { namespace pvData { + +extern int32 milliSecPerSec; +extern int32 microSecPerSec; +extern int32 nanoSecPerSec; +extern int64 posixEpochAtEpicsEpoch; + +class TimeStamp { +public: + TimeStamp() + :secondsPastEpoch(0),nanoSeconds(0) {} + TimeStamp(int64 secondsPastEpoch,int32 nanoSeconds = 0); + //default constructors and destructor are OK + //This class should not be extended + void normalize(); + void fromTime_t(const time_t &); + void toTime_t(time_t &) const; + int64 getSecondsPastEpoch() const {return secondsPastEpoch;} + int64 getEpicsSecondsPastEpoch() const { + return secondsPastEpoch - posixEpochAtEpicsEpoch; + } + int32 getNanoSeconds() const {return nanoSeconds;} + void put(int64 secondsPastEpoch,int32 nanoSeconds = 0) { + this->secondsPastEpoch = secondsPastEpoch; + this->nanoSeconds = nanoSeconds; + normalize(); + } + void put(int64 milliseconds); + void getCurrent(); + double toSeconds() const ; + bool operator==(TimeStamp const &) const; + bool operator!=(TimeStamp const &) const; + bool operator<=(TimeStamp const &) const; + bool operator< (TimeStamp const &) const; + bool operator>=(TimeStamp const &) const; + bool operator> (TimeStamp const &) const; + static double diff(TimeStamp const & a,TimeStamp const & b); + TimeStamp & operator+=(int64 seconds); + TimeStamp & operator-=(int64 seconds); + TimeStamp & operator+=(double seconds); + TimeStamp & operator-=(double seconds); + int64 getMilliseconds(); // milliseconds since epoch +private: + static int64 diffInt(TimeStamp const &left,TimeStamp const &right ); + int64 secondsPastEpoch; + int32 nanoSeconds; +}; + + +}} +#endif /* TIMESTAMP_H */ diff --git a/pvDataApp/misc/timer.cpp b/pvDataApp/misc/timer.cpp new file mode 100644 index 0000000..48231ec --- /dev/null +++ b/pvDataApp/misc/timer.cpp @@ -0,0 +1,310 @@ +/* timer.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 "pvType.h" +#include "lock.h" +#include "noDefaultMethods.h" +#include "showConstructDestruct.h" +#include "linkedList.h" +#include "thread.h" +#include "timeStamp.h" +#include "timer.h" +#include "event.h" + +namespace epics { namespace pvData { + + +static volatile int64 totalNodeConstruct = 0; +static volatile int64 totalNodeDestruct = 0; +static volatile int64 totalTimerConstruct = 0; +static volatile int64 totalTimerDestruct = 0; +static Mutex *globalMutex = 0; + +static int64 getTotalTimerNodeConstruct() +{ + Lock xx(globalMutex); + return totalNodeConstruct; +} + +static int64 getTotalTimerNodeDestruct() +{ + Lock xx(globalMutex); + return totalNodeDestruct; +} + +static int64 getTotalTimerConstruct() +{ + Lock xx(globalMutex); + return totalTimerConstruct; +} + +static int64 getTotalTimerDestruct() +{ + Lock xx(globalMutex); + return totalTimerDestruct; +} + +static ConstructDestructCallback *pCDCallbackTimerNode; +static ConstructDestructCallback *pCDCallbackTimer; + +static void init() +{ + static Mutex mutex = Mutex(); + Lock xx(&mutex); + if(globalMutex==0) { + globalMutex = new Mutex(); + pCDCallbackTimerNode = new ConstructDestructCallback( + "timerNode", + getTotalTimerNodeConstruct,getTotalTimerNodeDestruct,0); + + pCDCallbackTimer = new ConstructDestructCallback( + "timer", + getTotalTimerConstruct,getTotalTimerDestruct,0); + } +} + +ConstructDestructCallback * TimerNode::getConstructDestructCallback() +{ + init(); + return pCDCallbackTimerNode; +} + +ConstructDestructCallback * Timer::getConstructDestructCallback() +{ + init(); + return pCDCallbackTimer; +} + +class TimerNodePvt; + +typedef LinkedListNode TimerListNode; +typedef LinkedList TimerList; + +class TimerNodePvt { +public: + TimerNode *timerNode; + TimerCallback *callback; + TimerListNode *timerListNode; + TimeStamp timeToRun; + TimerPvt *timerPvt; + double period; + TimerNodePvt(TimerNode *timerNode,TimerCallback *callback); + ~TimerNodePvt(); +}; + +TimerNodePvt::TimerNodePvt(TimerNode *timerNode,TimerCallback *callback) +: timerNode(timerNode),callback(callback), + timerListNode(new TimerListNode(this)),timeToRun(TimeStamp()), + timerPvt(0), period(0.0) +{} + +TimerNodePvt::~TimerNodePvt() +{ + delete timerListNode; +} + +struct TimerPvt : public Runnable{ +public: + TimerPvt(String threadName,ThreadPriority priority); + ~TimerPvt(); + virtual void run(); +public: // only used by this source module + TimerList *timerList; + Mutex mutex; + Event *waitForWork; + Event *waitForDone; + volatile bool alive; + Thread *thread; +}; + +TimerPvt::TimerPvt(String threadName,ThreadPriority priority) +: timerList(new TimerList()), + mutex(Mutex()), + waitForWork(new Event(false)), + waitForDone(new Event(false)), + alive(true), + thread(new Thread(threadName,priority,this)) +{} + +TimerPvt::~TimerPvt() +{ + delete thread; + delete waitForDone; + delete waitForWork; + delete timerList; +} + +static void addElement(TimerPvt *timer,TimerNodePvt *node) +{ + TimerList *timerList = timer->timerList; + TimerListNode *nextNode = timerList->getHead(); + if(nextNode==0) { + timerList->addTail(node->timerListNode); + return; + } + while(true) { + TimerNodePvt *timerListNode = nextNode->getObject(); + if((node->timeToRun)<(timerListNode->timeToRun)) { + timerList->insertBefore(timerListNode->timerListNode,node->timerListNode); + return; + } + nextNode = timerList->getNext(timerListNode->timerListNode); + if(nextNode==0) { + timerList->addTail(node->timerListNode); + return; + } + } +} + + +TimerNode::TimerNode(TimerCallback *callback) +: pImpl(new TimerNodePvt(this,callback)) +{ + init(); + Lock xx(globalMutex); + totalNodeConstruct++; +} + + +TimerNode::~TimerNode() +{ + cancel(); + delete pImpl; + Lock xx(globalMutex); + totalNodeDestruct++; +} + +void TimerNode::cancel() +{ + TimerPvt *timerPvt = pImpl->timerPvt; + if(timerPvt==0) return; + Lock xx(&timerPvt->mutex); + if(pImpl->timerPvt==0) return; + pImpl->timerPvt->timerList->remove(pImpl); + pImpl->timerPvt = 0; +} + +bool TimerNode::isScheduled() +{ + TimerPvt *pvt = pImpl->timerPvt; + if(pvt==0) return false; + Lock xx(&pvt->mutex); + return pImpl->timerListNode->isOnList(); +} + + +void TimerPvt::run() +{ + TimeStamp currentTime; + while(alive) { + currentTime.getCurrent(); + TimeStamp *timeToRun = 0; + double period = 0.0; + TimerNodePvt *nodeToCall = 0; + { + Lock xx(&mutex); + TimerListNode *timerListNode = timerList->getHead(); + if(timerListNode!=0) { + TimerNodePvt *timerNodePvt = timerListNode->getObject(); + timeToRun = &timerNodePvt->timeToRun; + double diff = TimeStamp::diff( + *timeToRun,currentTime); + if(diff<=0.0) { + nodeToCall = timerNodePvt; + timerList->removeHead(); + period = timerNodePvt->period; + if(period>0.0) { + timerNodePvt->timeToRun += period; + addElement(this,timerNodePvt); + } else { + timerNodePvt->timerPvt = 0; + } + timerListNode = timerList->getHead(); + if(timerListNode!=0) { + timerNodePvt = timerListNode->getObject(); + timeToRun = &timerNodePvt->timeToRun; + } else { + timeToRun = 0; + } + } + } + } + if(nodeToCall!=0) { + nodeToCall->callback->callback(); + } + if(!alive) break; + if(timeToRun==0) { + waitForWork->wait(); + } else { + double delay = TimeStamp::diff(*timeToRun,currentTime); + waitForWork->wait(delay); + } + } + waitForDone->signal(); +} + +Timer::Timer(String threadName, ThreadPriority priority) +: pImpl(new TimerPvt(threadName,priority)) +{ + init(); + Lock xx(globalMutex); + totalTimerConstruct++; +} + +Timer::~Timer() { + { + Lock xx(&pImpl->mutex); + pImpl->alive = false; + } + pImpl->waitForWork->signal(); + pImpl->waitForDone->wait(); + TimerList *timerList = pImpl->timerList; + TimerListNode *node = 0; + while((node = timerList->removeHead())!=0) { + node->getObject()->callback->timerStopped(); + } + delete pImpl; + Lock xx(globalMutex); + totalTimerDestruct++; +} + +void Timer::scheduleAfterDelay(TimerNode *timerNode,double delay) +{ + schedulePeriodic(timerNode,delay,0.0); +} +void Timer::schedulePeriodic(TimerNode *timerNode,double delay,double period) +{ + TimerNodePvt *timerNodePvt = timerNode->pImpl; + if(timerNodePvt->timerListNode->isOnList()) { + throw std::logic_error(String("already queued")); + } + if(!pImpl->alive) { + timerNodePvt->callback->timerStopped(); + return; + } + TimeStamp *timeStamp = &timerNodePvt->timeToRun; + timeStamp->getCurrent(); + *timeStamp += delay; + timerNodePvt->period = period; + bool isFirst = false; + { + Lock xx(&pImpl->mutex); + timerNodePvt->timerPvt = pImpl; + addElement(pImpl,timerNodePvt); + TimerNodePvt *first = pImpl->timerList->getHead()->getObject(); + if(first==timerNodePvt) isFirst = true; + } + if(isFirst) pImpl->waitForWork->signal(); +} + +}} diff --git a/pvDataApp/misc/timer.h b/pvDataApp/misc/timer.h new file mode 100644 index 0000000..00f3e88 --- /dev/null +++ b/pvDataApp/misc/timer.h @@ -0,0 +1,53 @@ +/* timer.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 TIMER_H +#define TIMER_H +#include +#include +#include +#include +#include + +#include "pvType.h" +#include "thread.h" +#include "noDefaultMethods.h" +#include "showConstructDestruct.h" + +namespace epics { namespace pvData { + +class TimerCallback { +public: + virtual void callback() = 0; + virtual void timerStopped() = 0; +}; + +class TimerNode : private NoDefaultMethods { +public: + TimerNode(TimerCallback *timerCallback); + ~TimerNode(); + static ConstructDestructCallback *getConstructDestructCallback(); + void cancel(); + bool isScheduled(); +private: + class TimerNodePvt *pImpl; + friend class Timer; +}; + +class Timer : private NoDefaultMethods { +public: + Timer(String threadName, ThreadPriority priority); + ~Timer(); + static ConstructDestructCallback *getConstructDestructCallback(); + void scheduleAfterDelay(TimerNode *timerNode,double delay); + void schedulePeriodic(TimerNode *timerNode,double delay,double period); +private: + class TimerPvt *pImpl; + friend class TimerNode; +}; + +}} +#endif /* TIMER_H */