From 253ef9903df24f1f3f73b86a4d7000b4962f34c6 Mon Sep 17 00:00:00 2001 From: Philip Top Date: Wed, 14 Nov 2018 05:35:13 -0800 Subject: [PATCH 1/8] fix warnings and errors from Travis # Conflicts: # src/helics/common/CircularBuffer.cpp # src/helics/common/StackQueue.cpp # tests/helics/common/StackQueueTests.cpp --- src/helics/common/CircularBuffer.cpp | 244 +++++++++++ src/helics/common/StackQueue.cpp | 200 +++++++++ tests/helics/common/StackQueueTests.cpp | 516 ++++++++++++++++++++++++ 3 files changed, 960 insertions(+) create mode 100644 src/helics/common/CircularBuffer.cpp create mode 100644 src/helics/common/StackQueue.cpp create mode 100644 tests/helics/common/StackQueueTests.cpp diff --git a/src/helics/common/CircularBuffer.cpp b/src/helics/common/CircularBuffer.cpp new file mode 100644 index 0000000000..ada93f9576 --- /dev/null +++ b/src/helics/common/CircularBuffer.cpp @@ -0,0 +1,244 @@ +/* +Copyright © 2017-2018, +Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable Energy, LLC +All rights reserved. See LICENSE file and DISCLAIMER for more details. +*/ + +#include "CircularBuffer.hpp" +#include +#include + +namespace helics +{ +namespace common +{ + +CircularBufferRaw::CircularBufferRaw (unsigned char *dataBlock, int blockSize) + : origin (dataBlock), next_write (origin), next_read (origin), capacity_ (blockSize) +{ +} + +bool CircularBufferRaw::isSpaceAvailable (int sz) const +{ + if (next_write >= next_read) + { + if ((capacity_ - (next_write - origin)) >= sz + 4) + { + return true; + } + else if ((next_read - origin) >= sz + 4) + { + return true; + } + return false; + } + else if ((next_read - next_write) >= sz + 4) + { + return true; + } + return false; +} + +// Return number of bytes written. +bool CircularBufferRaw::push (const unsigned char *data, int blockSize) +{ + if (blockSize <= 0) + { + return false; + } + if (next_write >= next_read) + { + if ((capacity_ - (next_write - origin)) >= blockSize + 4) + { + *(reinterpret_cast (next_write)) = blockSize; + memcpy (next_write + 4, data, blockSize); + next_write += blockSize + 4; + // loop around if there isn't really space for another block of at least 4 bytes and the + // next_read>origin + if (((capacity_ - (next_write - origin)) < 8) && (next_read > origin)) + { + next_write = origin; + } + return true; + } + else if ((next_read - origin) >= blockSize + 4) + { + *(reinterpret_cast (next_write)) = -1; + *(reinterpret_cast (origin)) = blockSize; + memcpy (origin + 4, data, blockSize); + next_write = origin + blockSize + 4; + return true; + } + } + else if ((next_read - next_write) >= blockSize + 4) + { + *(reinterpret_cast (next_write)) = blockSize; + memcpy (next_write + 4, data, blockSize); + next_write += blockSize + 4; + return true; + } + return false; +} + + +int CircularBufferRaw::nextDataSize() const +{ + if (next_write == next_read) + { + return 0; + } + int size = *(reinterpret_cast (next_read)); + if (size < 0) + { + size = *(reinterpret_cast (origin)); + } + return size; +} + +// Return number of bytes read. +int CircularBufferRaw::pop (unsigned char *data, int maxLen) +{ + if (next_write == next_read) + { + return 0; + } + int size = *(reinterpret_cast (next_read)); + if (size < 0) + { + next_read = origin; + size = *(reinterpret_cast (next_read)); + } + if (size <= maxLen) + { + memcpy (data, next_read + 4, size); + next_read += size + 4; + if ((capacity_ - (next_read - origin)) < 8) + { + next_read = origin; + } + return size; + } + return 0; +} +/** check if the block is Empty or not*/ +bool CircularBufferRaw::empty () const { return (next_write == next_read); } +void CircularBufferRaw::clear () { next_write = next_read = origin; } + + +CircularBuffer::CircularBuffer () noexcept : buffer (nullptr, 0){} +CircularBuffer::CircularBuffer (int size) : data (size), buffer (data.data (), size) {} + +CircularBuffer::CircularBuffer (CircularBuffer &&cb) noexcept + : data (std::move (cb.data)), buffer (std::move (cb.buffer)) +{ + cb.buffer.capacity_ = 0; + cb.buffer.origin = nullptr; + cb.buffer.next_read = nullptr; + cb.buffer.next_write = nullptr; +} + +CircularBuffer::CircularBuffer (const CircularBuffer &cb) : data (cb.data), buffer(cb.buffer) +{ + auto read_offset = buffer.next_read - buffer.origin; + auto write_offset = buffer.next_write - buffer.origin; + buffer.origin = data.data (); + buffer.next_read=buffer.origin+read_offset; + buffer.next_write = buffer.origin+write_offset; +} + +CircularBuffer &CircularBuffer::operator= (CircularBuffer &&cb) noexcept +{ + buffer = std::move (cb.buffer); + data = std::move (cb.data); + + cb.buffer.capacity_ = 0; + cb.buffer.origin = nullptr; + cb.buffer.next_read = nullptr; + cb.buffer.next_write = nullptr; + return *this; +} + +CircularBuffer &CircularBuffer::operator= (const CircularBuffer &cb) +{ + buffer = cb.buffer; + data = cb.data; + auto read_offset = buffer.next_read - buffer.origin; + auto write_offset = buffer.next_write - buffer.origin; + buffer.origin = data.data (); + buffer.next_read = buffer.origin + read_offset; + buffer.next_write = buffer.origin + write_offset; + return *this; +} + +void CircularBuffer::resize (int newsize) +{ + if (newsize == buffer.capacity_) + { + return; + } + if (buffer.empty()) + { + data.resize (newsize); + buffer = CircularBufferRaw (data.data (), newsize); + } + else if (newsize > static_cast(data.size ())) + { + data.resize (newsize); + int read_offset = static_cast (buffer.next_read - buffer.origin); + int write_offset = static_cast (buffer.next_write - buffer.origin); + if (buffer.next_read < buffer.next_write) + { + + buffer.capacity_ = newsize; + buffer.origin = data.data (); + buffer.next_read = buffer.origin + read_offset; + buffer.next_write = buffer.origin + write_offset; + } + else + { + int readDiff = buffer.capacity_ - read_offset; + memmove (data.data () + newsize - readDiff, data.data () + read_offset, + buffer.capacity_ - read_offset); + buffer.origin = data.data (); + buffer.next_write = buffer.origin + write_offset; + buffer.next_read = buffer.origin + newsize - readDiff; + buffer.capacity_ = newsize; + } + + } + else // smaller size + { + int read_offset = static_cast(buffer.next_read - buffer.origin); + if (buffer.next_read < buffer.next_write) + { + if (read_offset <= newsize) + { + data.resize (newsize); + buffer.capacity_ = newsize; + } + } + else + { + int write_offset = static_cast (buffer.next_write - buffer.origin); + int readDiff = buffer.capacity_ - read_offset; + if (readDiff + write_offset < newsize) + { + memmove (data.data () + newsize - readDiff, data.data () + read_offset, + buffer.capacity_ - read_offset); + buffer.origin = data.data (); + buffer.next_write = buffer.origin + write_offset; + buffer.next_read = buffer.origin + newsize - readDiff; + buffer.capacity_ = newsize; + } + else + { + throw (std::runtime_error ( + "unable to resize, current data exceeds new size, please empty buffer before resizing")); + } + } + + } +} + +} // namespace common +} // namespace helics diff --git a/src/helics/common/StackQueue.cpp b/src/helics/common/StackQueue.cpp new file mode 100644 index 0000000000..2b4747eac3 --- /dev/null +++ b/src/helics/common/StackQueue.cpp @@ -0,0 +1,200 @@ +/* +Copyright © 2017-2018, +Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable Energy, LLC +All rights reserved. See LICENSE file and DISCLAIMER for more details. +*/ +#pragma once + +#include "StackQueue.hpp" +#include +#include + +namespace helics +{ +namespace common +{ + +StackQueueRaw::StackQueueRaw (unsigned char *newBlock, int blockSize) + : origin (newBlock), next (newBlock), dataSize (blockSize) +{ + nextIndex = reinterpret_cast (origin + dataSize - sizeof (dataIndex)); +} + +void StackQueueRaw::swap (StackQueueRaw &other) noexcept +{ + std::swap (origin, other.origin); + std::swap (next, other.next); + std::swap (dataSize, other.dataSize); + std::swap (nextIndex, other.nextIndex); + std::swap (dataCount, other.dataCount); +} + +bool StackQueueRaw::isSpaceAvailable (int sz) const +{ + return (dataSize - (next - origin) - (dataCount + 1) * sizeof (dataIndex)) >= sz; +} + +bool StackQueueRaw::push (const unsigned char *block, int blockSize) +{ + if (blockSize <= 0) + { + return false; + } + if (!isSpaceAvailable (blockSize)) + { + return false; + } + memcpy (next, block, blockSize); + nextIndex->offset = static_cast (next - origin); + nextIndex->dataSize = blockSize; + next += blockSize; + --nextIndex; + ++dataCount; + return true; +} + +int StackQueueRaw::nextDataSize () const +{ + if (dataCount > 0) + { + return nextIndex[1].dataSize; + } + return 0; +} + +int StackQueueRaw::pop (unsigned char *block, int maxSize) +{ + if (dataCount > 0) + { + int blkSize = nextIndex[1].dataSize; + if (maxSize >= blkSize) + { + memcpy (block, origin + nextIndex[1].offset, blkSize); + if (nextIndex[1].offset + blkSize == static_cast (next - origin)) + { + next -= blkSize; + } + ++nextIndex; + --dataCount; + if (dataCount == 0) + { + next = origin; + nextIndex = reinterpret_cast (origin + dataSize - sizeof (dataIndex)); + } + return blkSize; + } + } + return 0; +} + +/** reverse the order in which the data will be extracted*/ +void StackQueueRaw::reverse () +{ + if (dataCount <= 1) + { + return; + } + std::reverse (nextIndex + 1, nextIndex + dataCount + 1); +} + +void StackQueueRaw::clear () +{ + next = origin; + dataCount = 0; + nextIndex = reinterpret_cast (origin + dataSize - sizeof (dataIndex)); +} + + + StackQueue::StackQueue () noexcept : stack (nullptr, 0){}; + StackQueue::StackQueue(int size) : data(size), stack(data.data(), size) + { + + } + + StackQueue::StackQueue (StackQueue &&sq) noexcept : data(std::move(sq.data)),stack (std::move(sq.stack)) + { + sq.stack.dataSize = 0; + sq.stack.origin = nullptr; + sq.stack.next = nullptr; + sq.stack.dataCount = 0; + sq.stack.nextIndex = nullptr; + } + + StackQueue::StackQueue (const StackQueue &sq) : data (sq.data), stack (sq.stack) + { + auto offset = stack.next - stack.origin; + stack.origin = data.data (); + stack.next = stack.origin + offset; + stack.nextIndex = reinterpret_cast (stack.origin + stack.dataSize - sizeof (dataIndex)); + stack.nextIndex -= stack.dataCount; + } + + StackQueue &StackQueue::operator= (StackQueue &&sq) noexcept { + stack = std::move (sq.stack); + data = std::move (sq.data); + + sq.stack.dataSize = 0; + sq.stack.origin = nullptr; + sq.stack.next = nullptr; + sq.stack.dataCount = 0; + sq.stack.nextIndex = nullptr; + return *this; + } + + StackQueue &StackQueue::operator= (const StackQueue &sq) + { + stack = sq.stack; + data = sq.data; + auto offset = stack.next - stack.origin; + stack.origin = data.data (); + stack.next = stack.origin + offset; + stack.nextIndex = reinterpret_cast (stack.origin + stack.dataSize - sizeof (dataIndex)); + stack.nextIndex -= stack.dataCount; + return *this; + } + + void StackQueue::resize(int newsize) + { + if (newsize == stack.dataSize) + { + return; + } + if (stack.dataCount == 0) + { + data.resize (newsize); + stack = StackQueueRaw (data.data (), newsize); + } + else if (newsize>data.size()) + { + data.resize (newsize); + int indexOffset = stack.dataSize - sizeof (dataIndex) * stack.dataCount; + int newOffset = newsize - sizeof (dataIndex) * stack.dataCount; + memmove(data.data () + newOffset, data.data () + indexOffset, sizeof (dataIndex) * stack.dataCount); + stack.dataSize = newsize; + stack.origin = data.data (); + stack.next = stack.origin + newsize; + stack.nextIndex = reinterpret_cast (stack.origin + stack.dataSize - sizeof (dataIndex)); + stack.nextIndex -= stack.dataCount; + } + else //smaller size + { + int indexOffset = stack.dataSize - sizeof (dataIndex) * stack.dataCount; + int newOffset = newsize - sizeof (dataIndex) * stack.dataCount; + int dataOffset = static_cast(stack.next - stack.origin); + if (newsize < dataOffset + sizeof(dataIndex) * stack.dataCount) + { + throw (std::runtime_error ( + "unable to resize, current data exceeds new size, please empty stack before resizing")); + } + memmove (data.data () + newOffset, data.data () + indexOffset, sizeof (dataIndex) * stack.dataCount); + stack.dataSize = newsize; + stack.origin = data.data (); + stack.next = stack.origin + newsize; + stack.nextIndex = reinterpret_cast (stack.origin + stack.dataSize - sizeof (dataIndex)); + stack.nextIndex -= stack.dataCount; + data.resize (newsize); + } + } + +} // namespace common +} // namespace helics diff --git a/tests/helics/common/StackQueueTests.cpp b/tests/helics/common/StackQueueTests.cpp new file mode 100644 index 0000000000..d0d87c4815 --- /dev/null +++ b/tests/helics/common/StackQueueTests.cpp @@ -0,0 +1,516 @@ +/* +Copyright © 2017-2018, +Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable Energy, LLC +All rights reserved. See LICENSE file and DISCLAIMER for more details. +*/ + +#include + +#include "helics/common/StackQueue.hpp" + + +namespace utf = boost::unit_test; +using namespace std::literals::chrono_literals; +using namespace helics::common; + +BOOST_AUTO_TEST_SUITE (StackQueue_tests, *utf::label ("ci")) + + +BOOST_AUTO_TEST_CASE (test_stackqueueraw_simple) +{ + unsigned char *block = new unsigned char[4096]; + StackQueueRaw stack (block, 4096); + + std::vector testData (1024, 'a'); + int res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 0); + + bool pushed = stack.push (testData.data (), 571); + BOOST_CHECK (pushed); + testData.assign (1024, '\0'); + + res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 571); + BOOST_CHECK_EQUAL (testData[0], 'a'); + BOOST_CHECK_EQUAL (testData[236], 'a'); + BOOST_CHECK_EQUAL (testData[570], 'a'); + BOOST_CHECK_EQUAL (testData[1023], 0); + delete[] block; +} + +BOOST_AUTO_TEST_CASE (test_stackqueueraw_3_push) +{ + unsigned char *block = new unsigned char[4096]; + StackQueueRaw stack (block, 4096); + + std::vector testData (1024, 'a'); + int res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 0); + + bool pushed = stack.push (testData.data (), 571); + BOOST_CHECK (pushed); + testData.assign (1024, 'b'); + pushed = stack.push (testData.data (), 249); + BOOST_CHECK (pushed); + testData.assign (1024, 'c'); + pushed = stack.push (testData.data (), 393); + BOOST_CHECK (pushed); + res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 393); + BOOST_CHECK_EQUAL (testData[0], 'c'); + BOOST_CHECK_EQUAL (testData[236], 'c'); + + res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 249); + BOOST_CHECK_EQUAL (testData[0], 'b'); + BOOST_CHECK_EQUAL (testData[236], 'b'); + BOOST_CHECK (!stack.empty ()); + + res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 571); + BOOST_CHECK_EQUAL (testData[0], 'a'); + BOOST_CHECK_EQUAL (testData[236], 'a'); + BOOST_CHECK_EQUAL (testData[570], 'a'); + + BOOST_CHECK (stack.empty ()); + delete[] block; +} + +BOOST_AUTO_TEST_CASE (test_stackqueueraw_push_full) +{ + unsigned char *block = new unsigned char[1024]; + StackQueueRaw stack (block, 1024); + + std::vector testData (1024, 'a'); + int res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 0); + + bool pushed = stack.push (testData.data (), 571); + BOOST_CHECK (pushed); + testData.assign (1024, 'b'); + pushed = stack.push (testData.data (), 249); + BOOST_CHECK (pushed); + testData.assign (1024, 'c'); + pushed = stack.push (testData.data (), 393); + BOOST_CHECK (!pushed); + + BOOST_CHECK (!stack.isSpaceAvailable (393)); + BOOST_CHECK (!stack.isSpaceAvailable (200)); + BOOST_CHECK (stack.isSpaceAvailable (180)); + BOOST_CHECK_EQUAL (stack.getCurrentCount (), 2); + + pushed = stack.push (testData.data (), 180); + BOOST_CHECK (pushed); + testData.assign (1024, 'd'); + BOOST_CHECK_EQUAL (stack.getCurrentCount (), 3); + + res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 180); + BOOST_CHECK_EQUAL (testData[0], 'c'); + BOOST_CHECK_EQUAL (testData[179], 'c'); + BOOST_CHECK_EQUAL (testData[180], 'd'); //this is one past the copy + + res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 249); + BOOST_CHECK_EQUAL (testData[0], 'b'); + BOOST_CHECK_EQUAL (testData[236], 'b'); + BOOST_CHECK (!stack.empty ()); + + res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 571); + BOOST_CHECK_EQUAL (testData[0], 'a'); + BOOST_CHECK_EQUAL (testData[236], 'a'); + BOOST_CHECK_EQUAL (testData[570], 'a'); + + res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 0); + BOOST_CHECK_EQUAL (testData[0], 'a'); + BOOST_CHECK_EQUAL (testData[236], 'a'); + BOOST_CHECK_EQUAL (testData[570], 'a'); + + BOOST_CHECK (stack.empty ()); + delete[] block; +} + + +BOOST_AUTO_TEST_CASE (test_stackqueueraw_reverse) +{ + unsigned char *block = new unsigned char[4096]; + StackQueueRaw stack (block, 4096); + + std::vector testData (1024, 'a'); + int res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 0); + + bool pushed = stack.push (testData.data (), 571); + BOOST_CHECK (pushed); + testData.assign (1024, 'b'); + pushed = stack.push (testData.data (), 249); + BOOST_CHECK (pushed); + testData.assign (1024, 'c'); + pushed = stack.push (testData.data (), 393); + BOOST_CHECK (pushed); + + stack.reverse (); + + res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 571); + BOOST_CHECK_EQUAL (testData[0], 'a'); + BOOST_CHECK_EQUAL (testData[236], 'a'); + BOOST_CHECK_EQUAL (testData[570], 'a'); + + res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 249); + BOOST_CHECK_EQUAL (testData[0], 'b'); + BOOST_CHECK_EQUAL (testData[236], 'b'); + BOOST_CHECK (!stack.empty ()); + + res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 393); + BOOST_CHECK_EQUAL (testData[0], 'c'); + BOOST_CHECK_EQUAL (testData[236], 'c'); + + + BOOST_CHECK (stack.empty ()); + delete[] block; +} + +BOOST_AUTO_TEST_CASE (test_stackqueue_simple) +{ + StackQueue stack (4096); + + std::vector testData (1024, 'a'); + int res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 0); + + bool pushed = stack.push (testData.data (), 571); + BOOST_CHECK (pushed); + testData.assign (1024, '\0'); + + res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 571); + BOOST_CHECK_EQUAL (testData[0], 'a'); + BOOST_CHECK_EQUAL (testData[236], 'a'); + BOOST_CHECK_EQUAL (testData[570], 'a'); + BOOST_CHECK_EQUAL (testData[1023], 0); +} + +BOOST_AUTO_TEST_CASE (test_stackqueue_3_push) +{ + StackQueue stack (4096); + + std::vector testData (1024, 'a'); + int res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 0); + + bool pushed = stack.push (testData.data (), 571); + BOOST_CHECK (pushed); + testData.assign (1024, 'b'); + pushed = stack.push (testData.data (), 249); + BOOST_CHECK (pushed); + testData.assign (1024, 'c'); + pushed = stack.push (testData.data (), 393); + BOOST_CHECK (pushed); + res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 393); + BOOST_CHECK_EQUAL (testData[0], 'c'); + BOOST_CHECK_EQUAL (testData[236], 'c'); + + res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 249); + BOOST_CHECK_EQUAL (testData[0], 'b'); + BOOST_CHECK_EQUAL (testData[236], 'b'); + BOOST_CHECK (!stack.empty ()); + + res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 571); + BOOST_CHECK_EQUAL (testData[0], 'a'); + BOOST_CHECK_EQUAL (testData[236], 'a'); + BOOST_CHECK_EQUAL (testData[570], 'a'); + + BOOST_CHECK (stack.empty ()); +} + +BOOST_AUTO_TEST_CASE (test_stackqueue_push_full) +{ + StackQueue stack (1024); + + std::vector testData (1024, 'a'); + int res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 0); + + bool pushed = stack.push (testData.data (), 571); + BOOST_CHECK (pushed); + testData.assign (1024, 'b'); + pushed = stack.push (testData.data (), 249); + BOOST_CHECK (pushed); + testData.assign (1024, 'c'); + pushed = stack.push (testData.data (), 393); + BOOST_CHECK (!pushed); + + BOOST_CHECK (!stack.isSpaceAvailable (393)); + BOOST_CHECK (!stack.isSpaceAvailable (200)); + BOOST_CHECK (stack.isSpaceAvailable (180)); + BOOST_CHECK_EQUAL (stack.getCurrentCount (), 2); + + pushed = stack.push (testData.data (), 180); + BOOST_CHECK (pushed); + testData.assign (1024, 'd'); + BOOST_CHECK_EQUAL (stack.getCurrentCount (), 3); + + res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 180); + BOOST_CHECK_EQUAL (testData[0], 'c'); + BOOST_CHECK_EQUAL (testData[179], 'c'); + BOOST_CHECK_EQUAL (testData[180], 'd'); // this is one past the copy + + res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 249); + BOOST_CHECK_EQUAL (testData[0], 'b'); + BOOST_CHECK_EQUAL (testData[236], 'b'); + BOOST_CHECK (!stack.empty ()); + + res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 571); + BOOST_CHECK_EQUAL (testData[0], 'a'); + BOOST_CHECK_EQUAL (testData[236], 'a'); + BOOST_CHECK_EQUAL (testData[570], 'a'); + + res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 0); + BOOST_CHECK_EQUAL (testData[0], 'a'); + BOOST_CHECK_EQUAL (testData[236], 'a'); + BOOST_CHECK_EQUAL (testData[570], 'a'); + + BOOST_CHECK (stack.empty ()); +} + +BOOST_AUTO_TEST_CASE (test_stackqueue_reverse) +{ + StackQueue stack ( 4096); + + std::vector testData (1024, 'a'); + int res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 0); + + bool pushed = stack.push (testData.data (), 571); + BOOST_CHECK (pushed); + testData.assign (1024, 'b'); + pushed = stack.push (testData.data (), 249); + BOOST_CHECK (pushed); + testData.assign (1024, 'c'); + pushed = stack.push (testData.data (), 393); + BOOST_CHECK (pushed); + + stack.reverse (); + + res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 571); + BOOST_CHECK_EQUAL (testData[0], 'a'); + BOOST_CHECK_EQUAL (testData[236], 'a'); + BOOST_CHECK_EQUAL (testData[570], 'a'); + + res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 249); + BOOST_CHECK_EQUAL (testData[0], 'b'); + BOOST_CHECK_EQUAL (testData[236], 'b'); + BOOST_CHECK (!stack.empty ()); + + res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 393); + BOOST_CHECK_EQUAL (testData[0], 'c'); + BOOST_CHECK_EQUAL (testData[236], 'c'); + + BOOST_CHECK (stack.empty ()); +} + + +BOOST_AUTO_TEST_CASE (test_stackqueue_move) +{ + + StackQueue stack (2048); + + std::vector testData (1024, 'a'); + int res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 0); + + stack.push (testData.data (), 571); + testData.assign (1024, '\0'); + + StackQueue mstack (std::move (stack)); + BOOST_CHECK_EQUAL (mstack.getCurrentCount (), 1); + res = mstack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 571); + BOOST_CHECK_EQUAL (testData[0], 'a'); + BOOST_CHECK_EQUAL (testData[236], 'a'); + BOOST_CHECK_EQUAL (testData[570], 'a'); + BOOST_CHECK_EQUAL (testData[1023], 0); +} + + +BOOST_AUTO_TEST_CASE (test_stackqueue_3_push_and_copy) +{ + StackQueue stack (4096); + + std::vector testData (1024, 'a'); + int res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 0); + + bool pushed = stack.push (testData.data (), 571); + BOOST_CHECK (pushed); + testData.assign (1024, 'b'); + pushed = stack.push (testData.data (), 249); + BOOST_CHECK (pushed); + testData.assign (1024, 'c'); + pushed = stack.push (testData.data (), 393); + BOOST_CHECK (pushed); + + StackQueue cstack (stack); + stack.reverse (); + + res = cstack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 393); + BOOST_CHECK_EQUAL (testData[0], 'c'); + BOOST_CHECK_EQUAL (testData[236], 'c'); + + res = cstack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 249); + BOOST_CHECK_EQUAL (testData[0], 'b'); + BOOST_CHECK_EQUAL (testData[236], 'b'); + BOOST_CHECK (!stack.empty ()); + + res = cstack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 571); + BOOST_CHECK_EQUAL (testData[0], 'a'); + BOOST_CHECK_EQUAL (testData[236], 'a'); + BOOST_CHECK_EQUAL (testData[570], 'a'); + + BOOST_CHECK (cstack.empty ()); + BOOST_CHECK (!stack.empty ()); + + //check the original still has data and was reversed + res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 571); + BOOST_CHECK_EQUAL (testData[0], 'a'); + BOOST_CHECK_EQUAL (testData[236], 'a'); + BOOST_CHECK_EQUAL (testData[570], 'a'); + + //now copy assign the stack + cstack = stack; + + res = cstack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 249); + BOOST_CHECK_EQUAL (testData[0], 'b'); + BOOST_CHECK_EQUAL (testData[236], 'b'); + BOOST_CHECK (!stack.empty ()); + +} + +BOOST_AUTO_TEST_CASE (test_stackqueue_move_assignement) +{ + StackQueue stack (2048); + StackQueue stack2 (1024); + std::vector testData (1024, 'a'); + int res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 0); + + stack.push (testData.data (), 571); + testData.assign (1024, 'b'); + stack2.push (testData.data (), 397); + + stack2 = std::move (stack); + testData.assign (1024, '\0'); + + BOOST_CHECK_EQUAL (stack2.getCurrentCount (), 1); + res = stack2.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 571); + BOOST_CHECK_EQUAL (testData[0], 'a'); + BOOST_CHECK_EQUAL (testData[236], 'a'); + BOOST_CHECK_EQUAL (testData[570], 'a'); + BOOST_CHECK_EQUAL (testData[1023], 0); +} + + +BOOST_AUTO_TEST_CASE (test_stackqueue_3_push_resize) +{ + StackQueue stack (2048); + + std::vector testData (1024, 'a'); + int res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 0); + + bool pushed = stack.push (testData.data (), 571); + BOOST_CHECK (pushed); + testData.assign (1024, 'b'); + pushed = stack.push (testData.data (), 249); + BOOST_CHECK (pushed); + testData.assign (1024, 'c'); + pushed = stack.push (testData.data (), 393); + BOOST_CHECK (pushed); + //make sure to trigger a reallocation in memory + stack.resize (100000); + res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 393); + BOOST_CHECK_EQUAL (testData[0], 'c'); + BOOST_CHECK_EQUAL (testData[236], 'c'); + + res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 249); + BOOST_CHECK_EQUAL (testData[0], 'b'); + BOOST_CHECK_EQUAL (testData[236], 'b'); + BOOST_CHECK (!stack.empty ()); + + res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 571); + BOOST_CHECK_EQUAL (testData[0], 'a'); + BOOST_CHECK_EQUAL (testData[236], 'a'); + BOOST_CHECK_EQUAL (testData[570], 'a'); + + BOOST_CHECK (stack.empty ()); +} + + + +BOOST_AUTO_TEST_CASE (test_stackqueue_3_push_resize_shrink) +{ + StackQueue stack (2048); + + std::vector testData (1024, 'a'); + int res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 0); + + bool pushed = stack.push (testData.data (), 571); + BOOST_CHECK (pushed); + testData.assign (1024, 'b'); + pushed = stack.push (testData.data (), 249); + BOOST_CHECK (pushed); + testData.assign (1024, 'c'); + pushed = stack.push (testData.data (), 393); + BOOST_CHECK (pushed); + + stack.resize (1400); + + BOOST_CHECK_EQUAL (stack.capacity (), 1400); + + BOOST_CHECK_THROW (stack.resize (95), std::runtime_error); + res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 393); + BOOST_CHECK_EQUAL (testData[0], 'c'); + BOOST_CHECK_EQUAL (testData[236], 'c'); + + res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 249); + BOOST_CHECK_EQUAL (testData[0], 'b'); + BOOST_CHECK_EQUAL (testData[236], 'b'); + BOOST_CHECK (!stack.empty ()); + + res = stack.pop (testData.data (), 1024); + BOOST_CHECK_EQUAL (res, 571); + BOOST_CHECK_EQUAL (testData[0], 'a'); + BOOST_CHECK_EQUAL (testData[236], 'a'); + BOOST_CHECK_EQUAL (testData[570], 'a'); + + BOOST_CHECK (stack.empty ()); +} + +BOOST_AUTO_TEST_SUITE_END () \ No newline at end of file From 2e75855064abd9e03bf9a14079f8d19379ece4a3 Mon Sep 17 00:00:00 2001 From: Philip Top Date: Wed, 14 Nov 2018 07:35:14 -0800 Subject: [PATCH 2/8] update to push and pushPriority to allow for a full buffer # Conflicts: # src/helics/core/ipc/IpcBlockingPriorityQueueImpl.cpp --- .../core/ipc/IpcBlockingPriorityQueueImpl.cpp | 359 ++++++++++++++++++ 1 file changed, 359 insertions(+) create mode 100644 src/helics/core/ipc/IpcBlockingPriorityQueueImpl.cpp diff --git a/src/helics/core/ipc/IpcBlockingPriorityQueueImpl.cpp b/src/helics/core/ipc/IpcBlockingPriorityQueueImpl.cpp new file mode 100644 index 0000000000..48fb138d75 --- /dev/null +++ b/src/helics/core/ipc/IpcBlockingPriorityQueueImpl.cpp @@ -0,0 +1,359 @@ +/* +Copyright © 2017-2018, +Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable Energy, LLC +All rights reserved. See LICENSE file and DISCLAIMER for more details. +*/ +#pragma once + +#include "IpcBlockingPriorityQueueImpl.hpp" +#include +#include "boost/date_time/posix_time/posix_time.hpp" +#include + +namespace helics +{ +namespace ipc +{ +namespace detail +{ + + +using namespace boost::interprocess; + +static constexpr int sizeAlign8(int fullSize, double fraction) +{ + return (static_cast (fullSize * fraction) >> 3) * 8; +} + + /** default constructor*/ +IpcBlockingPriorityQueueImpl::IpcBlockingPriorityQueueImpl (unsigned char *dataBlock, int blockSize) + : pushData (dataBlock, sizeAlign8(blockSize,0.4)), + pullData (dataBlock + sizeAlign8 (blockSize, 0.4), sizeAlign8 (blockSize, 0.4)), + priorityData (dataBlock + 2*sizeAlign8(blockSize,0.4), sizeAlign8(blockSize,0.2)), + dataBlock_ (dataBlock), dataSize (blockSize) +{ +} + +/** clear the queue*/ +void IpcBlockingPriorityQueueImpl::clear () +{ + scoped_lock pullLock (m_pullLock); // first pullLock + scoped_lock pushLock (m_pushLock); // second pushLock + pullData.clear (); + pushData.clear (); + priorityData.clear (); + queueEmptyFlag = true; +} + +bool IpcBlockingPriorityQueueImpl::try_push(const unsigned char *data, int size) +{ + scoped_lock pushLock (m_pushLock); // only one lock on this branch + if (!pushData.empty ()) + { + return pushData.push (data, size); + } + else + { + scoped_lock conditionLock (m_conditionLock); + if (queueEmptyFlag) + { + queueEmptyFlag = false; + conditionLock.unlock (); + // release the push lock so we don't get a potential deadlock condition + pushLock.unlock (); + // all locks released + // no lock the pullLock + scoped_lock pullLock (m_pullLock); + conditionLock.lock (); + queueEmptyFlag = false; // reset the queueEmptyflag + conditionLock.unlock (); + if (pullData.empty ()) + { + if (pullData.push(data, size)) + { + // pullLock.unlock (); + condition_empty.notify_all (); + return true; + } + return false; + } + else + { + pushLock.lock (); + return pushData.push (data, size); + } + } + else + { + return pushData.push (data, size); + } + } +} + +/** push an element onto the queue +val the value to push on the queue +*/ +bool IpcBlockingPriorityQueueImpl::try_pushPriority(const unsigned char *data, int size) +{ + scoped_lock conditionLock (m_conditionLock); + + if (queueEmptyFlag) + { + conditionLock.unlock (); + scoped_lock pullLock (m_pullLock); + conditionLock.lock (); + queueEmptyFlag = false; // need to set the flag again just in case after we get the lock + conditionLock.unlock (); + if (priorityData.push(data, size)) + { + // pullLock.unlock (); + condition_empty.notify_all (); + return true; + } + + } + else + { + conditionLock.unlock (); + scoped_lock pullLock (m_pullLock); + if (priorityData.push(data, size)) + { + conditionLock.lock (); + if (queueEmptyFlag) + { + queueEmptyFlag = false; + conditionLock.unlock (); + condition_empty.notify_all (); + } + return true; + } + + } + return false; +} + +void IpcBlockingPriorityQueueImpl::push (const unsigned char *data, int size) +{ + if (try_push(data,size)) + { + return; + } + + while (true) + { + scoped_lock pushLock(m_pushLock); // only one lock on this branch + if (size>pushData.capacity() - 12) + { + throw(std::invalid_argument("data size is greater than buffer capacity")); + } + if (pushData.isSpaceAvailable(size)) + { + pushData.push(data, size); + return; + } + scoped_lock conditionLock(m_conditionLock); + queueFullFlag = true; + conditionLock.unlock(); + condition_full.wait(pushLock); // now wait + if (pushData.isSpaceAvailable(size)) + { + pushData.push(data, size); + return; + } + } +} + +/** push an element onto the queue +val the value to push on the queue +*/ +void IpcBlockingPriorityQueueImpl::pushPriority (const unsigned char *data, int size) // forwarding reference +{ + + if (try_pushPriority(data, size)) + { + return; + } + + while (true) + { + scoped_lock pullLock(m_pullLock); + if (size>priorityData.capacity() - 8) + { + throw(std::invalid_argument("data size is greater than priority buffer capacity")); + } + if (priorityData.isSpaceAvailable(size)) + { + priorityData.push(data, size); + return; + } + scoped_lock conditionLock(m_conditionLock); + queueFullFlag = true; + conditionLock.unlock(); + condition_full.wait(pullLock); // now wait + if (priorityData.isSpaceAvailable(size)) + { + priorityData.push(data, size); + return; + } + + } +} + +int IpcBlockingPriorityQueueImpl::try_pop (unsigned char *data, int maxSize) +{ + scoped_lock pullLock (m_pullLock); + if (!priorityData.empty ()) + { + scoped_lock conditionLock(m_conditionLock); + if (queueFullFlag) + { + queueFullFlag = false; + conditionLock.unlock(); + condition_full.notify_all(); + } + return priorityData.pop (data, maxSize); + } + if (pullData.empty ()) + { + scoped_lock pushLock (m_pushLock); + if (!pushData.empty ()) + { // on the off chance the queue got out of sync + pushData.swap (pullData); + scoped_lock conditionLock(m_conditionLock); + if (queueFullFlag) + { + queueFullFlag = false; + conditionLock.unlock(); + condition_full.notify_all(); + } + pushLock.unlock (); // we can free the push function to accept more elements after the swap call; + pullData.reverse (); + int ret = pullData.pop (data, maxSize); + if (pullData.empty ()) + { + pushLock.lock (); // second pushLock + if (!pushData.empty ()) // more elements could have been added + { // this is the potential for slow operations + pushData.swap (pullData); + // we can free the push function to accept more elements after the swap call; + pushLock.unlock (); + pullData.reverse (); + } + else + { + conditionLock.lock(); + queueEmptyFlag = true; + } + } + return ret; + } + scoped_lock conditionLock (m_conditionLock); + queueEmptyFlag = true; + return 0; // return the empty optional + } + int ret = pullData.pop (data, maxSize); + if (pullData.empty ()) + { + scoped_lock pushLock (m_pushLock); // second PushLock + if (!pushData.empty ()) + { // this is the potential for slow operations + pushData.swap (pullData); + // we can free the push function to accept more elements after the swap call; + pushLock.unlock (); + pullData.reverse (); + } + else + { + scoped_lock conditionLock (m_conditionLock); + queueEmptyFlag = true; + } + } + return ret; +} + +/** blocking call to wait on an object from the stack*/ +int IpcBlockingPriorityQueueImpl::pop (unsigned char *data, int maxSize) +{ + auto val = try_pop (data, maxSize); + if (val < 0) + { + return val; + } + while (val == 0) + { + scoped_lock pullLock (m_pullLock); + if (!priorityData.empty ()) + { + return priorityData.pop (data, maxSize); + } + if (!pullData.empty ()) // make sure we are actually empty; + { + return pullData.pop (data, maxSize); + } + condition_empty.wait (pullLock); // now wait + if (!priorityData.empty ()) + { + return priorityData.pop (data, maxSize); + } + if (!pullData.empty ()) // make sure we are actually empty; + { + return pullData.pop (data, maxSize); + } + pullLock.unlock (); + val = try_pop (data, maxSize); + } + // move the value out of the optional + return val; +} + +/** blocking call to wait on an object from the stack with timeout*/ +int IpcBlockingPriorityQueueImpl::pop (std::chrono::milliseconds timeout, unsigned char *data, int maxSize) +{ + auto val = try_pop (data, maxSize); + if (val < 0) + { + return val; + } + while (val == 0) + { + scoped_lock pullLock (m_pullLock); + if (!priorityData.empty ()) + { + return priorityData.pop (data, maxSize); + } + if (!pullData.empty ()) // make sure we are actually empty; + { + return pullData.pop (data, maxSize); + } + bool timedOut = + condition_empty.timed_wait (pullLock, boost::posix_time::microsec_clock::universal_time () + + boost::posix_time::milliseconds (timeout.count ())); // now wait + if (!priorityData.empty ()) + { + return priorityData.pop (data, maxSize); + } + if (!pullData.empty ()) // make sure we are actually empty; + { + return pullData.pop (data, maxSize); + } + + pullLock.unlock (); + val = try_pop (data, maxSize); + if (!timedOut) + { + return val; + } + } + // move the value out of the optional + return val; +} + +bool IpcBlockingPriorityQueueImpl::empty () const +{ + scoped_lock conditionLock (m_conditionLock); + return queueEmptyFlag; +} + +} // namespace detail +} // namespace ipc +} // namespace helics From 17b09988a4a2f049086f79ce26266b9fb054af4b Mon Sep 17 00:00:00 2001 From: Philip Top Date: Sat, 24 Nov 2018 16:49:19 -0800 Subject: [PATCH 3/8] add more tests for placement new # Conflicts: # src/helics/common/CircularBuffer.hpp # src/helics/common/StackQueue.hpp # src/helics/core/ipc/IpcBlockingPriorityQueueImpl.hpp # tests/helics/application_api/CombinationFederateTests.cpp # tests/helics/common/BlockingQueueTests.cpp # tests/helics/common/PriorityBlockingQueueTests.cpp # tests/helics/core/CMakeLists.txt # tests/helics/core/IpcQueueTests.cpp --- src/helics/common/CircularBuffer.hpp | 74 +++ src/helics/common/StackQueue.cpp | 2 +- src/helics/common/StackQueue.hpp | 94 ++++ .../core/ipc/IpcBlockingPriorityQueueImpl.cpp | 106 +++- .../core/ipc/IpcBlockingPriorityQueueImpl.hpp | 115 ++++ .../CombinationFederateTests.cpp | 410 +++++++------- tests/helics/core/CMakeLists.txt | 102 ++-- tests/helics/core/IpcQueueImpTests.cpp | 506 ++++++++++++++++++ tests/helics/core/IpcQueueTests.cpp | 506 ++++++++++++++++++ 9 files changed, 1643 insertions(+), 272 deletions(-) create mode 100644 src/helics/common/CircularBuffer.hpp create mode 100644 src/helics/common/StackQueue.hpp create mode 100644 src/helics/core/ipc/IpcBlockingPriorityQueueImpl.hpp create mode 100644 tests/helics/core/IpcQueueImpTests.cpp create mode 100644 tests/helics/core/IpcQueueTests.cpp diff --git a/src/helics/common/CircularBuffer.hpp b/src/helics/common/CircularBuffer.hpp new file mode 100644 index 0000000000..cd7f6e9fc6 --- /dev/null +++ b/src/helics/common/CircularBuffer.hpp @@ -0,0 +1,74 @@ +/* +Copyright © 2017-2018, +Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable Energy, LLC +All rights reserved. See LICENSE file and DISCLAIMER for more details. +*/ +#pragma once + +#include +#include + +namespace helics +{ +namespace common +{ + +class CircularBufferRaw +{ + public: + CircularBufferRaw (unsigned char *dataBlock, int capacity); + + int capacity () const { return capacity_; } + bool isSpaceAvailable (int sz) const; + // Return true if push was successful + bool push (const unsigned char *data, int blockSize); + int nextDataSize () const; + // Return number of bytes read. + int pop (unsigned char *data, int maxSize); + /** check if the block is Empty or not*/ + bool empty () const; + /** clear the buffer*/ + void clear (); + + private: + unsigned char *origin; + unsigned char *next_write; + unsigned char *next_read; + int capacity_ = 0; + + private: + friend class CircularBuffer; +}; + +class CircularBuffer +{ + public: + CircularBuffer () noexcept; + explicit CircularBuffer (int size); + ~CircularBuffer () = default; + CircularBuffer (CircularBuffer &&cb) noexcept; + CircularBuffer (const CircularBuffer &cb); + + CircularBuffer &operator= (CircularBuffer &&cb) noexcept; + CircularBuffer &operator= (const CircularBuffer &cb); + + void resize (int newsize); + int capacity () const { return buffer.capacity (); } + bool isSpaceAvailable (int sz) const { return buffer.isSpaceAvailable (sz); } + bool empty () const { return buffer.empty (); } + + bool push (const unsigned char *block, int blockSize) { return buffer.push (block, blockSize); } + + int next_data_size () const { return buffer.nextDataSize (); } + + int pop (unsigned char *block, int maxSize) { return buffer.pop (block, maxSize); } + + void clear () { buffer.clear (); } + + private: + std::vector data; + CircularBufferRaw buffer; +}; + +} // namespace common +} // namespace helics diff --git a/src/helics/common/StackQueue.cpp b/src/helics/common/StackQueue.cpp index 2b4747eac3..c8dcac4ae0 100644 --- a/src/helics/common/StackQueue.cpp +++ b/src/helics/common/StackQueue.cpp @@ -31,7 +31,7 @@ void StackQueueRaw::swap (StackQueueRaw &other) noexcept bool StackQueueRaw::isSpaceAvailable (int sz) const { - return (dataSize - (next - origin) - (dataCount + 1) * sizeof (dataIndex)) >= sz; + return (dataSize - (next - origin) - (dataCount + 1) * static_cast(sizeof (dataIndex))) >= sz; } bool StackQueueRaw::push (const unsigned char *block, int blockSize) diff --git a/src/helics/common/StackQueue.hpp b/src/helics/common/StackQueue.hpp new file mode 100644 index 0000000000..898dcada88 --- /dev/null +++ b/src/helics/common/StackQueue.hpp @@ -0,0 +1,94 @@ +/* +Copyright © 2017-2018, +Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable Energy, LLC +All rights reserved. See LICENSE file and DISCLAIMER for more details. +*/ +#pragma once + +#include +#include +#include + +namespace helics +{ +namespace common +{ +struct dataIndex +{ + int32_t offset; + int32_t dataSize; +}; +/** class containing the raw stackQueue implementation +@details the stackQueueRaw class operates on raw memory +it is given a memory location and uses that for the life of the queue, it does not own the memory so care must be +taken for memory management It operates on blocks of raw data +*/ +class StackQueueRaw +{ + private: + unsigned char *origin = nullptr; + unsigned char *next = nullptr; + dataIndex *nextIndex = nullptr; + int dataSize = 0; + int dataCount = 0; + + public: + StackQueueRaw (unsigned char *newBlock, int blockSize); + + void swap (StackQueueRaw &other) noexcept; + + int capacity () const { return dataSize; }; + int getCurrentCount () const { return dataCount; } + bool isSpaceAvailable (int sz) const; + bool empty () const { return (dataCount == 0); } + + bool push (const unsigned char *block, int blockSize); + + int nextDataSize () const; + + int pop (unsigned char *block, int maxSize); + + /** reverse the order in which the data will be extracted*/ + void reverse (); + /** clear all data from the StackQueueRaw*/ + void clear (); + + private: + friend class StackQueue; +}; + +/** StackQueue manages memory for a StackQueueRaw and adds some convenience functions */ +class StackQueue +{ + public: + StackQueue () noexcept; + explicit StackQueue (int size); + ~StackQueue () = default; + StackQueue (StackQueue &&sq) noexcept; + StackQueue (const StackQueue &sq); + + StackQueue &operator= (StackQueue &&sq) noexcept; + StackQueue &operator= (const StackQueue &sq); + + void resize (int newsize); + int getCurrentCount () const { return stack.getCurrentCount (); } + int capacity () const { return stack.capacity (); } + bool isSpaceAvailable (int sz) const { return stack.isSpaceAvailable (sz); } + bool empty () const { return stack.empty (); } + + bool push (const unsigned char *block, int blockSize) { return stack.push (block, blockSize); } + + int nextDataSize () const { return stack.nextDataSize (); } + + int pop (unsigned char *block, int maxSize) { return stack.pop (block, maxSize); } + + void reverse () { stack.reverse (); } + void clear () { stack.clear (); } + + private: + std::vector data; + StackQueueRaw stack; +}; + +} // namespace common +} // namespace helics diff --git a/src/helics/core/ipc/IpcBlockingPriorityQueueImpl.cpp b/src/helics/core/ipc/IpcBlockingPriorityQueueImpl.cpp index 48fb138d75..622b4674c9 100644 --- a/src/helics/core/ipc/IpcBlockingPriorityQueueImpl.cpp +++ b/src/helics/core/ipc/IpcBlockingPriorityQueueImpl.cpp @@ -20,6 +20,7 @@ namespace detail using namespace boost::interprocess; +/** function to verify the blocks are aligned as 8 byte alignment*/ static constexpr int sizeAlign8(int fullSize, double fraction) { return (static_cast (fullSize * fraction) >> 3) * 8; @@ -27,9 +28,9 @@ static constexpr int sizeAlign8(int fullSize, double fraction) /** default constructor*/ IpcBlockingPriorityQueueImpl::IpcBlockingPriorityQueueImpl (unsigned char *dataBlock, int blockSize) - : pushData (dataBlock, sizeAlign8(blockSize,0.4)), - pullData (dataBlock + sizeAlign8 (blockSize, 0.4), sizeAlign8 (blockSize, 0.4)), - priorityData (dataBlock + 2*sizeAlign8(blockSize,0.4), sizeAlign8(blockSize,0.2)), + : queueSize(sizeAlign8(blockSize, 0.4)),prioritySize(sizeAlign8(blockSize, 0.2)),pushData (dataBlock, queueSize), + pullData (dataBlock + queueSize, queueSize), + priorityData (dataBlock + 2*queueSize, prioritySize), dataBlock_ (dataBlock), dataSize (blockSize) { } @@ -43,6 +44,7 @@ void IpcBlockingPriorityQueueImpl::clear () pushData.clear (); priorityData.clear (); queueEmptyFlag = true; + queueFullFlag = false; } bool IpcBlockingPriorityQueueImpl::try_push(const unsigned char *data, int size) @@ -138,7 +140,6 @@ void IpcBlockingPriorityQueueImpl::push (const unsigned char *data, int size) { return; } - while (true) { scoped_lock pushLock(m_pushLock); // only one lock on this branch @@ -163,6 +164,44 @@ void IpcBlockingPriorityQueueImpl::push (const unsigned char *data, int size) } } + +int IpcBlockingPriorityQueueImpl::push(std::chrono::milliseconds timeout, const unsigned char *data, int size) +{ + if (try_push(data, size)) + { + return size; + } + + while (true) + { + scoped_lock pushLock(m_pushLock); // only one lock on this branch + if (size>pushData.capacity() - 12) + { + throw(std::invalid_argument("data size is greater than buffer capacity")); + } + if (pushData.isSpaceAvailable(size)) + { + pushData.push(data, size); + return size; + } + scoped_lock conditionLock(m_conditionLock); + queueFullFlag = true; + conditionLock.unlock(); + bool conditionMet = + condition_empty.timed_wait(pushLock, boost::posix_time::microsec_clock::universal_time() + + boost::posix_time::milliseconds(timeout.count())); // now wait + if (pushData.isSpaceAvailable(size)) + { + pushData.push(data, size); + return size; + } + if (!conditionMet) + { + return 0; + } + } +} + /** push an element onto the queue val the value to push on the queue */ @@ -199,6 +238,47 @@ void IpcBlockingPriorityQueueImpl::pushPriority (const unsigned char *data, int } } +/** push an element onto the queue +val the value to push on the queue +*/ +int IpcBlockingPriorityQueueImpl::pushPriority(std::chrono::milliseconds timeout, const unsigned char *data, int size) // forwarding reference +{ + + if (try_pushPriority(data, size)) + { + return size; + } + + while (true) + { + scoped_lock pullLock(m_pullLock); + if (size>priorityData.capacity() - 8) + { + throw(std::invalid_argument("data size is greater than priority buffer capacity")); + } + if (priorityData.isSpaceAvailable(size)) + { + priorityData.push(data, size); + return size; + } + scoped_lock conditionLock(m_conditionLock); + queueFullFlag = true; + conditionLock.unlock(); + bool conditionMet = + condition_empty.timed_wait(pullLock, boost::posix_time::microsec_clock::universal_time() + + boost::posix_time::milliseconds(timeout.count())); // now wait + if (priorityData.isSpaceAvailable(size)) + { + priorityData.push(data, size); + return size; + } + if (!conditionMet) + { + return 0; + } + } +} + int IpcBlockingPriorityQueueImpl::try_pop (unsigned char *data, int maxSize) { scoped_lock pullLock (m_pullLock); @@ -256,8 +336,15 @@ int IpcBlockingPriorityQueueImpl::try_pop (unsigned char *data, int maxSize) { scoped_lock pushLock (m_pushLock); // second PushLock if (!pushData.empty ()) - { // this is the potential for slow operations + { // this has the potential for slow operations pushData.swap (pullData); + scoped_lock conditionLock(m_conditionLock); + if (queueFullFlag) + { + queueFullFlag = false; + conditionLock.unlock(); + condition_full.notify_all(); + } // we can free the push function to accept more elements after the swap call; pushLock.unlock (); pullData.reverse (); @@ -275,7 +362,7 @@ int IpcBlockingPriorityQueueImpl::try_pop (unsigned char *data, int maxSize) int IpcBlockingPriorityQueueImpl::pop (unsigned char *data, int maxSize) { auto val = try_pop (data, maxSize); - if (val < 0) + if (val > 0) { return val; } @@ -310,7 +397,7 @@ int IpcBlockingPriorityQueueImpl::pop (unsigned char *data, int maxSize) int IpcBlockingPriorityQueueImpl::pop (std::chrono::milliseconds timeout, unsigned char *data, int maxSize) { auto val = try_pop (data, maxSize); - if (val < 0) + if (val > 0) { return val; } @@ -325,7 +412,7 @@ int IpcBlockingPriorityQueueImpl::pop (std::chrono::milliseconds timeout, unsign { return pullData.pop (data, maxSize); } - bool timedOut = + bool conditionMet = condition_empty.timed_wait (pullLock, boost::posix_time::microsec_clock::universal_time () + boost::posix_time::milliseconds (timeout.count ())); // now wait if (!priorityData.empty ()) @@ -339,12 +426,11 @@ int IpcBlockingPriorityQueueImpl::pop (std::chrono::milliseconds timeout, unsign pullLock.unlock (); val = try_pop (data, maxSize); - if (!timedOut) + if (!conditionMet) { return val; } } - // move the value out of the optional return val; } diff --git a/src/helics/core/ipc/IpcBlockingPriorityQueueImpl.hpp b/src/helics/core/ipc/IpcBlockingPriorityQueueImpl.hpp new file mode 100644 index 0000000000..a3b85fdb5d --- /dev/null +++ b/src/helics/core/ipc/IpcBlockingPriorityQueueImpl.hpp @@ -0,0 +1,115 @@ +/* +Copyright © 2017-2018, +Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable Energy, LLC +All rights reserved. See LICENSE file and DISCLAIMER for more details. +*/ +#pragma once + +#include +#include +#include +#include "helics/common/CircularBuffer.hpp" +#include "helics/common/StackQueue.hpp" + +namespace helics +{ +namespace ipc +{ +namespace detail +{ + +/** class implementing a blocking queue with a priority channel +@details this class uses locks one for push and pull it can exhibit longer blocking times if the internal +operations require a swap, however in high usage the two locks will reduce contention in most cases. +*/ + class IpcBlockingPriorityQueueImpl + { + private: + boost::interprocess::interprocess_mutex m_pushLock; //!< lock for operations on the pushElements vector + const int queueSize; + const int prioritySize; + common::StackQueueRaw pushData; + boost::interprocess::interprocess_mutex + m_pullLock; //!< lock for elements on the pullData and priority structure + common::StackQueueRaw pullData; + mutable boost::interprocess::interprocess_mutex m_conditionLock; //!< lock for the empty and full Flag + bool queueEmptyFlag{true}; //!< flag indicating the queue is empty + bool queueFullFlag{false}; + + // the condition variable should be keyed of the conditionLock + boost::interprocess::interprocess_condition + condition_empty; //!< condition variable for notification of new data + boost::interprocess::interprocess_condition + condition_full; //!< condition variable for notification of available space + unsigned char *dataBlock_; + const size_t dataSize; + common::CircularBufferRaw priorityData; + + public: + /** default constructor*/ + IpcBlockingPriorityQueueImpl (unsigned char *dataBlock, int blockSize); + + /** clear the queue*/ + void clear (); + + /** DISABLE_COPY_AND_ASSIGN */ + IpcBlockingPriorityQueueImpl (const IpcBlockingPriorityQueueImpl &) = delete; + IpcBlockingPriorityQueueImpl &operator= (const IpcBlockingPriorityQueueImpl &) = delete; + + /** push a data block + val the value to push on the queue + */ + void push (const unsigned char *data, int size); + + /** push a data block + val the value to push on the queue + */ + int push(std::chrono::milliseconds timeout, const unsigned char *data, int size); + + /** push an element onto the queue + val the value to push on the queue + */ + void pushPriority (const unsigned char *data, int size); + /** push an element onto the queue + @param timeout the time to wait + @param data the data to put into the queue + @param size the number of bytes of data to store + @return size if successful, 0 if not + @throws invalid_argument if the size is greater than the capacity + */ + int pushPriority(std::chrono::milliseconds timeout, const unsigned char *data, int size); + /** push a data block + val the value to push on the queue + */ + bool try_push (const unsigned char *data, int size); + + /** push an element onto the queue + val the value to push on the queue + */ + bool try_pushPriority (const unsigned char *data, int size); + + /** try to pop an object from the queue + @details this function does not block, will return 0 if no data could be popped + @return an integer with the size of the popped value, + */ + int try_pop (unsigned char *data, int maxSize); + + /** pop an object from the queue + @details this function will block, will return 0 if the data does not fit into the max size + @return an integer with the size of the popped value, + */ + int pop (unsigned char *data, int maxSize); + + /** blocking call to wait on an object from the stack with timeout*/ + int pop (std::chrono::milliseconds timeout, unsigned char *data, int maxSize); + + /** check whether there are any elements in the queue + because this is meant for multi-process applications this may or may not have any meaning + depending on the number of consumers + */ + bool empty () const; + }; + +} // namespace detail +} // namespace ipc +} // namespace helics diff --git a/tests/helics/application_api/CombinationFederateTests.cpp b/tests/helics/application_api/CombinationFederateTests.cpp index aac1cb72c1..c27ff2efaa 100644 --- a/tests/helics/application_api/CombinationFederateTests.cpp +++ b/tests/helics/application_api/CombinationFederateTests.cpp @@ -1,345 +1,305 @@ /* -Copyright (c) 2017-2022, -Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable -Energy, LLC. See the top-level NOTICE for additional details. All rights reserved. -SPDX-License-Identifier: BSD-3-Clause +Copyright © 2017-2018, +Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable Energy, LLC +All rights reserved. See LICENSE file and DISCLAIMER for more details. */ -#include "helics/application_api/CombinationFederate.hpp" -#include "helics/application_api/CoreApp.hpp" -#include "helics/application_api/Endpoints.hpp" +#include +#include +#include #include "helics/application_api/Publications.hpp" +#include "helics/application_api/Endpoints.hpp" +#include "helics/application_api/CombinationFederate.hpp" #include "helics/core/BrokerFactory.hpp" #include "helics/core/Core.hpp" #include "helics/core/CoreFactory.hpp" #include "helics/core/core-exceptions.hpp" #include "testFixtures.hpp" - #include -#include -class combofed_single_type_tests: - public ::testing::TestWithParam, - public FederateTestFixture {}; +namespace bdata = boost::unit_test::data; +namespace utf = boost::unit_test; -class combofed_type_tests: - public ::testing::TestWithParam, - public FederateTestFixture {}; +BOOST_FIXTURE_TEST_SUITE (combo_federate_tests, FederateTestFixture) -// const std::string CoreTypes[] = {"udp" }; +// const std::string core_types[] = {"udp" }; /** test simple creation and destruction*/ -TEST_P(combofed_single_type_tests, initialize_tests) +BOOST_DATA_TEST_CASE (combo_federate_initialize_tests, bdata::make (core_types_single), core_type) { - SetupTest(GetParam(), 1); - auto vFed1 = GetFederateAs(0); + SetupTest (core_type, 1); + auto vFed1 = GetFederateAs (0); - vFed1->enterExecutingMode(); + vFed1->enterExecutingMode (); - EXPECT_TRUE(vFed1->getCurrentMode() == helics::Federate::Modes::EXECUTING); + BOOST_CHECK (vFed1->getCurrentState () == helics::Federate::op_states::execution); - vFed1->disconnect(); + vFed1->finalize (); - EXPECT_TRUE(vFed1->getCurrentMode() == helics::Federate::Modes::FINALIZE); + BOOST_CHECK (vFed1->getCurrentState () == helics::Federate::op_states::finalize); } -TEST_P(combofed_single_type_tests, publication_registration) +BOOST_DATA_TEST_CASE (combo_federate_publication_registration, bdata::make (core_types_single), core_type) { - SetupTest(GetParam(), 1); - auto vFed1 = GetFederateAs(0); + SetupTest (core_type, 1); + auto vFed1 = GetFederateAs (0); - auto& pubid = vFed1->registerPublication("pub1"); - auto& pubid2 = vFed1->registerGlobalPublication("pub2"); + auto &pubid = vFed1->registerPublication ("pub1"); + auto &pubid2 = vFed1->registerGlobalPublication ("pub2"); - auto& pubid3 = vFed1->registerPublication("pub3", "double", "V"); - vFed1->enterExecutingMode(); + auto &pubid3 = vFed1->registerPublication ("pub3", "double", "V"); + vFed1->enterExecutingMode (); - EXPECT_TRUE(vFed1->getCurrentMode() == helics::Federate::Modes::EXECUTING); + BOOST_CHECK (vFed1->getCurrentState () == helics::Federate::op_states::execution); - auto& sv = pubid.getName(); - auto& sv2 = pubid2.getName(); - EXPECT_EQ(sv, "fed0/pub1"); - EXPECT_EQ(sv2, "pub2"); - auto& pub3name = pubid3.getName(); - EXPECT_EQ(pub3name, "fed0/pub3"); + auto &sv = vFed1->getPublicationKey (pubid); + auto &sv2 = vFed1->getPublicationKey (pubid2); + BOOST_CHECK_EQUAL (sv, "fed0/pub1"); + BOOST_CHECK_EQUAL (sv2, "pub2"); + auto &pub3name = vFed1->getPublicationKey (pubid3); + BOOST_CHECK_EQUAL (pub3name, "fed0/pub3"); - EXPECT_EQ(pubid3.getExtractionType(), "double"); - EXPECT_EQ(pubid3.getUnits(), "V"); + BOOST_CHECK_EQUAL (vFed1->getPublicationType (pubid3), "double"); + BOOST_CHECK_EQUAL (vFed1->getPublicationUnits (pubid3), "V"); - EXPECT_TRUE(vFed1->getPublication("pub1").getHandle() == pubid.getHandle()); - EXPECT_TRUE(vFed1->getPublication("pub2").getHandle() == pubid2.getHandle()); - EXPECT_TRUE(vFed1->getPublication("fed0/pub1").getHandle() == pubid.getHandle()); - vFed1->disconnect(); + BOOST_CHECK (vFed1->getPublication ("pub1").getHandle () == pubid.getHandle ()); + BOOST_CHECK (vFed1->getPublication ("pub2").getHandle () == pubid2.getHandle ()); + BOOST_CHECK (vFed1->getPublication ("fed0/pub1").getHandle () == pubid.getHandle ()); + vFed1->finalize (); - EXPECT_TRUE(vFed1->getCurrentMode() == helics::Federate::Modes::FINALIZE); + BOOST_CHECK (vFed1->getCurrentState () == helics::Federate::op_states::finalize); } -TEST_P(combofed_single_type_tests, single_transfer) +BOOST_DATA_TEST_CASE (combo_federate_single_transfer, bdata::make (core_types_single), core_type) { - SetupTest(GetParam(), 1); - auto vFed1 = GetFederateAs(0); + SetupTest (core_type, 1); + auto vFed1 = GetFederateAs (0); // register the publications - auto& pubid = vFed1->registerGlobalPublication("pub1"); + auto &pubid = vFed1->registerGlobalPublication ("pub1"); - auto& subid = vFed1->registerSubscription("pub1"); - vFed1->setProperty(HELICS_PROPERTY_TIME_DELTA, 1.0); - vFed1->enterExecutingMode(); + auto &subid = vFed1->registerSubscription("pub1"); + vFed1->setTimeProperty (TIME_DELTA_PROPERTY, 1.0); + vFed1->enterExecutingMode (); // publish string1 at time=0.0; - pubid.publish("string1"); - auto gtime = vFed1->requestTime(1.0); + vFed1->publish (pubid, "string1"); + auto gtime = vFed1->requestTime (1.0); - EXPECT_EQ(gtime, 1.0); + BOOST_CHECK_EQUAL (gtime, 1.0); // get the value - std::string s = subid.getString(); + std::string s = vFed1->getString (subid); // make sure the string is what we expect - EXPECT_EQ(s, "string1"); + BOOST_CHECK_EQUAL (s, "string1"); // publish a second string - pubid.publish("string2"); + vFed1->publish (pubid, "string2"); // make sure the value is still what we expect - s = subid.getString(); - - EXPECT_EQ(s, "string1"); + s=vFed1->getString (subid); + + BOOST_CHECK_EQUAL (s, "string1"); // advance time - gtime = vFed1->requestTime(2.0); + gtime = vFed1->requestTime (2.0); // make sure the value was updated - EXPECT_EQ(gtime, 2.0); - s = subid.getString(); + BOOST_CHECK_EQUAL (gtime, 2.0); + s=vFed1->getString (subid); - EXPECT_EQ(s, "string2"); - vFed1->disconnect(); + BOOST_CHECK_EQUAL (s, "string2"); } -TEST_P(combofed_single_type_tests, endpoint_registration) +BOOST_DATA_TEST_CASE (combo_federate_endpoint_registration, bdata::make (core_types_single), core_type) { - SetupTest(GetParam(), 1); - auto mFed1 = GetFederateAs(0); + SetupTest (core_type, 1); + auto mFed1 = GetFederateAs (0); - auto& epid = mFed1->registerEndpoint("ep1"); - auto& epid2 = mFed1->registerGlobalEndpoint("ep2", "random"); + auto &epid = mFed1->registerEndpoint ("ep1"); + auto &epid2 = mFed1->registerGlobalEndpoint ("ep2", "random"); - mFed1->enterExecutingMode(); + mFed1->enterExecutingMode (); - EXPECT_TRUE(mFed1->getCurrentMode() == helics::Federate::Modes::EXECUTING); + BOOST_CHECK (mFed1->getCurrentState () == helics::Federate::op_states::execution); - auto& sv = epid.getName(); - auto& sv2 = epid2.getName(); - EXPECT_EQ(sv, "fed0/ep1"); - EXPECT_EQ(sv2, "ep2"); + auto &sv = mFed1->getEndpointName (epid); + auto &sv2 = mFed1->getEndpointName (epid2); + BOOST_CHECK_EQUAL (sv, "fed0/ep1"); + BOOST_CHECK_EQUAL (sv2, "ep2"); - EXPECT_EQ(epid.getExtractionType(), ""); - EXPECT_EQ(epid2.getInjectionType(), "random"); + BOOST_CHECK_EQUAL (mFed1->getEndpointType (epid), ""); + BOOST_CHECK_EQUAL (mFed1->getEndpointType (epid2), "random"); - EXPECT_TRUE(mFed1->getEndpoint("ep1").getHandle() == epid.getHandle()); - EXPECT_TRUE(mFed1->getEndpoint("fed0/ep1").getHandle() == epid.getHandle()); - EXPECT_TRUE(mFed1->getEndpoint("ep2").getHandle() == epid2.getHandle()); - mFed1->disconnect(); + BOOST_CHECK (mFed1->getEndpoint ("ep1").getHandle () == epid.getHandle ()); + BOOST_CHECK (mFed1->getEndpoint ("fed0/ep1").getHandle () == epid.getHandle ()); + BOOST_CHECK (mFed1->getEndpoint ("ep2").getHandle () == epid2.getHandle ()); + mFed1->finalize (); - EXPECT_TRUE(mFed1->getCurrentMode() == helics::Federate::Modes::FINALIZE); + BOOST_CHECK (mFed1->getCurrentState () == helics::Federate::op_states::finalize); } -TEST_P(combofed_type_tests, send_receive_2fed) +BOOST_DATA_TEST_CASE (combination_federate_send_receive_2fed, bdata::make (core_types), core_type) { - SetupTest(GetParam(), 2); - auto mFed1 = GetFederateAs(0); - auto mFed2 = GetFederateAs(1); + SetupTest (core_type, 2); + auto mFed1 = GetFederateAs (0); + auto mFed2 = GetFederateAs (1); - auto& epid = mFed1->registerEndpoint("ep1"); - auto& epid2 = mFed2->registerGlobalEndpoint("ep2", "random"); + auto &epid = mFed1->registerEndpoint ("ep1"); + auto &epid2 = mFed2->registerGlobalEndpoint ("ep2", "random"); - mFed1->setProperty(HELICS_PROPERTY_TIME_DELTA, 1.0); - mFed2->setProperty(HELICS_PROPERTY_TIME_DELTA, 1.0); + mFed1->setTimeProperty (TIME_DELTA_PROPERTY, 1.0); + mFed2->setTimeProperty (TIME_DELTA_PROPERTY, 1.0); - auto f1finish = std::async(std::launch::async, [&]() { mFed1->enterExecutingMode(); }); - mFed2->enterExecutingMode(); - f1finish.wait(); + auto f1finish = std::async (std::launch::async, [&]() { mFed1->enterExecutingMode (); }); + mFed2->enterExecutingMode (); + f1finish.wait (); - EXPECT_TRUE(mFed1->getCurrentMode() == helics::Federate::Modes::EXECUTING); - EXPECT_TRUE(mFed2->getCurrentMode() == helics::Federate::Modes::EXECUTING); + BOOST_CHECK (mFed1->getCurrentState () == helics::Federate::op_states::execution); + BOOST_CHECK (mFed2->getCurrentState () == helics::Federate::op_states::execution); - helics::SmallBuffer data(500, 'a'); - helics::SmallBuffer data2(400, 'b'); + helics::data_block data (500, 'a'); + helics::data_block data2 (400, 'b'); - epid.sendTo(data, "ep2"); - epid2.sendTo(data2, "fed0/ep1"); + mFed1->sendMessage (epid, "ep2", data); + mFed2->sendMessage (epid2, "fed0/ep1", data2); // move the time to 1.0 - auto f1time = std::async(std::launch::async, [&]() { return mFed1->requestTime(1.0); }); - auto gtime = mFed2->requestTime(1.0); + auto f1time = std::async (std::launch::async, [&]() { return mFed1->requestTime (1.0); }); + auto gtime = mFed2->requestTime (1.0); - EXPECT_EQ(gtime, 1.0); - EXPECT_EQ(f1time.get(), 1.0); + BOOST_CHECK_EQUAL (gtime, 1.0); + BOOST_CHECK_EQUAL (f1time.get (), 1.0); - auto res = mFed1->hasMessage(); - EXPECT_TRUE(res); - res = mFed1->hasMessage(epid); - EXPECT_TRUE(res); - res = mFed2->hasMessage(epid2); - EXPECT_TRUE(res); + auto res = mFed1->hasMessage (); + BOOST_CHECK (res); + res = mFed1->hasMessage (epid); + BOOST_CHECK (res); + res = mFed2->hasMessage (epid2); + BOOST_CHECK (res); - auto M1 = mFed1->getMessage(epid); - ASSERT_EQ(M1->data.size(), data2.size()); + auto M1 = mFed1->getMessage (epid); + BOOST_REQUIRE_EQUAL (M1->data.size (), data2.size ()); - EXPECT_EQ(M1->data[245], data2[245]); + BOOST_CHECK_EQUAL (M1->data[245], data2[245]); - auto M2 = mFed2->getMessage(epid2); - ASSERT_EQ(M2->data.size(), data.size()); + auto M2 = mFed2->getMessage (epid2); + BOOST_REQUIRE_EQUAL (M2->data.size (), data.size ()); - EXPECT_EQ(M2->data[245], data[245]); - mFed1->disconnect(); - mFed2->disconnect(); + BOOST_CHECK_EQUAL (M2->data[245], data[245]); + mFed1->finalize (); + mFed2->finalize (); - EXPECT_TRUE(mFed1->getCurrentMode() == helics::Federate::Modes::FINALIZE); - EXPECT_TRUE(mFed2->getCurrentMode() == helics::Federate::Modes::FINALIZE); + BOOST_CHECK (mFed1->getCurrentState () == helics::Federate::op_states::finalize); + BOOST_CHECK (mFed2->getCurrentState () == helics::Federate::op_states::finalize); } -TEST_P(combofed_type_tests, multimode_transfer) +BOOST_DATA_TEST_CASE (combination_federate_multimode_transfer, bdata::make (core_types), core_type) { - SetupTest(GetParam(), 2); - auto cFed1 = GetFederateAs(0); - auto cFed2 = GetFederateAs(1); + SetupTest (core_type, 2); + auto cFed1 = GetFederateAs (0); + auto cFed2 = GetFederateAs (1); - cFed1->setProperty(HELICS_PROPERTY_TIME_GRANT_TIMEOUT, 1.0); - cFed2->setProperty(HELICS_PROPERTY_TIME_GRANT_TIMEOUT, 1.0); - - auto& epid = cFed1->registerEndpoint("ep1"); - auto& epid2 = cFed2->registerGlobalEndpoint("ep2", "random"); + auto &epid = cFed1->registerEndpoint ("ep1"); + auto &epid2 = cFed2->registerGlobalEndpoint ("ep2", "random"); // register the publications - auto& pubid = cFed1->registerGlobalPublication("pub1"); + auto &pubid = cFed1->registerGlobalPublication ("pub1"); - auto& subid = cFed2->registerSubscription("pub1"); + auto &subid = cFed2->registerSubscription("pub1"); - cFed1->setProperty(HELICS_PROPERTY_TIME_DELTA, 1.0); - cFed2->setProperty(HELICS_PROPERTY_TIME_DELTA, 1.0); + cFed1->setTimeProperty (TIME_DELTA_PROPERTY, 1.0); + cFed2->setTimeProperty (TIME_DELTA_PROPERTY, 1.0); - auto f1finish = std::async(std::launch::async, [&]() { cFed1->enterExecutingMode(); }); - cFed2->enterExecutingMode(); - f1finish.wait(); + auto f1finish = std::async (std::launch::async, [&]() { cFed1->enterExecutingMode (); }); + cFed2->enterExecutingMode (); + f1finish.wait (); // publish string1 at time=0.0; - pubid.publish("string1"); + cFed1->publish (pubid, "string1"); - EXPECT_TRUE(cFed1->getCurrentMode() == helics::Federate::Modes::EXECUTING); - EXPECT_TRUE(cFed2->getCurrentMode() == helics::Federate::Modes::EXECUTING); + BOOST_CHECK (cFed1->getCurrentState () == helics::Federate::op_states::execution); + BOOST_CHECK (cFed2->getCurrentState () == helics::Federate::op_states::execution); - helics::SmallBuffer data(500, 'a'); - helics::SmallBuffer data2(400, 'b'); + helics::data_block data (500, 'a'); + helics::data_block data2 (400, 'b'); - epid.sendTo(data, "ep2"); - epid2.sendTo(data2, "fed0/ep1"); + cFed1->sendMessage (epid, "ep2", data); + cFed2->sendMessage (epid2, "fed0/ep1", data2); // move the time to 1.0 - auto f1time = std::async(std::launch::async, [&]() { return cFed1->requestTime(1.0); }); - auto gtime = cFed2->requestTime(1.0); + auto f1time = std::async (std::launch::async, [&]() { return cFed1->requestTime (1.0); }); + auto gtime = cFed2->requestTime (1.0); - EXPECT_EQ(gtime, 1.0); - EXPECT_EQ(f1time.get(), 1.0); + BOOST_CHECK_EQUAL (gtime, 1.0); + BOOST_CHECK_EQUAL (f1time.get (), 1.0); - std::string s = subid.getString(); + std::string s = cFed2->getString (subid); // get the value // make sure the string is what we expect - EXPECT_EQ(s, "string1"); + BOOST_CHECK_EQUAL (s, "string1"); // publish a second string - pubid.publish("string2"); + cFed1->publish (pubid, "string2"); // make sure the value is still what we expect - s = subid.getString(); + s=cFed2->getString(subid); - EXPECT_EQ(s, "string1"); + BOOST_CHECK_EQUAL (s, "string1"); - auto res = cFed1->hasMessage(); - EXPECT_TRUE(res); - res = cFed1->hasMessage(epid); - EXPECT_TRUE(res); - res = cFed2->hasMessage(epid2); - EXPECT_TRUE(res); + auto res = cFed1->hasMessage (); + BOOST_CHECK (res); + res = cFed1->hasMessage (epid); + BOOST_CHECK (res); + res = cFed2->hasMessage (epid2); + BOOST_CHECK (res); - auto M1 = cFed1->getMessage(epid); - ASSERT_EQ(M1->data.size(), data2.size()); + auto M1 = cFed1->getMessage (epid); + BOOST_REQUIRE_EQUAL (M1->data.size (), data2.size ()); - EXPECT_EQ(M1->data[245], data2[245]); + BOOST_CHECK_EQUAL (M1->data[245], data2[245]); - auto M2 = cFed2->getMessage(epid2); - ASSERT_EQ(M2->data.size(), data.size()); + auto M2 = cFed2->getMessage (epid2); + BOOST_REQUIRE_EQUAL (M2->data.size (), data.size ()); - EXPECT_EQ(M2->data[245], data[245]); + BOOST_CHECK_EQUAL (M2->data[245], data[245]); // advance time - f1time = std::async(std::launch::async, [&]() { return cFed1->requestTime(2.0); }); - gtime = cFed2->requestTime(2.0); + f1time = std::async (std::launch::async, [&]() { return cFed1->requestTime (2.0); }); + gtime = cFed2->requestTime (2.0); - EXPECT_EQ(gtime, 2.0); - EXPECT_EQ(f1time.get(), 2.0); + BOOST_CHECK_EQUAL (gtime, 2.0); + BOOST_CHECK_EQUAL (f1time.get (), 2.0); // make sure the value was updated - const auto& ns = subid.getString(); + auto &ns=cFed2->getString (subid); - EXPECT_EQ(ns, "string2"); + BOOST_CHECK_EQUAL (ns, "string2"); - cFed1->disconnect(); - cFed2->disconnect(); + cFed1->finalize (); + cFed2->finalize (); - EXPECT_TRUE(cFed1->getCurrentMode() == helics::Federate::Modes::FINALIZE); - EXPECT_TRUE(cFed2->getCurrentMode() == helics::Federate::Modes::FINALIZE); + BOOST_CHECK (cFed1->getCurrentState () == helics::Federate::op_states::finalize); + BOOST_CHECK (cFed2->getCurrentState () == helics::Federate::op_states::finalize); } -INSTANTIATE_TEST_SUITE_P(combofed_tests, - combofed_single_type_tests, - ::testing::ValuesIn(CoreTypes_simple)); -INSTANTIATE_TEST_SUITE_P(combofed_tests, combofed_type_tests, ::testing::ValuesIn(CoreTypes)); - -static constexpr const char* combo_config_files[] = {"example_combo_fed.json", - "example_combo_fed.toml"}; - -class combofed_file_load_tests: - public ::testing::TestWithParam, - public FederateTestFixture {}; - -TEST_P(combofed_file_load_tests, test_file_load) +BOOST_AUTO_TEST_CASE (test_file_load) { - helics::CombinationFederate cFed(std::string(TEST_DIR) + GetParam()); + helics::CombinationFederate cFed (std::string (TEST_DIR) + "/test_files/example_combo_fed.json"); - EXPECT_EQ(cFed.getName(), "comboFed"); + BOOST_CHECK_EQUAL (cFed.getName (), "comboFed"); - EXPECT_EQ(cFed.getEndpointCount(), 2); - auto& id = cFed.getEndpoint("ept1"); - EXPECT_EQ(id.getExtractionType(), "genmessage"); + BOOST_CHECK_EQUAL (cFed.getEndpointCount (), 2); + auto &id = cFed.getEndpoint ("ept1"); + BOOST_CHECK_EQUAL (cFed.getEndpointType (id), "genmessage"); - EXPECT_EQ(cFed.getInputCount(), 2); - EXPECT_EQ(cFed.getPublicationCount(), 2); + BOOST_CHECK_EQUAL (cFed.getInputCount (), 2); + BOOST_CHECK_EQUAL (cFed.getPublicationCount (), 2); - EXPECT_TRUE(!cFed.getPublication(1).getInfo().empty()); - cFed.getCorePointer()->disconnect(); - cFed.disconnect(); + cFed.disconnect (); } -INSTANTIATE_TEST_SUITE_P(combofed_tests, - combofed_file_load_tests, - ::testing::ValuesIn(combo_config_files)); - -TEST(comboFederate, constructor2) +BOOST_AUTO_TEST_CASE (test_file_load_toml) { - auto cr = helics::CoreFactory::create(helics::CoreType::TEST, "--name=mf --autobroker"); - helics::FederateInfo fi(helics::CoreType::TEST); - fi.setProperty(HELICS_PROPERTY_INT_LOG_LEVEL, HELICS_LOG_LEVEL_ERROR); - helics::CombinationFederate mf1("fed1", cr, fi); - - mf1.registerGlobalFilter("filt1"); - mf1.registerGlobalFilter("filt2"); + helics::CombinationFederate cFed (std::string (TEST_DIR) + "/test_files/example_combo_fed.toml"); - EXPECT_NO_THROW(mf1.enterExecutingMode()); - mf1.disconnect(); + BOOST_CHECK_EQUAL (cFed.getName (), "comboFed"); - cr.reset(); -} - -TEST(comboFederate, constructor3) -{ - helics::CoreApp cr(helics::CoreType::TEST, "--name=mf2 --autobroker"); - helics::FederateInfo fi(helics::CoreType::TEST); - fi.setProperty(HELICS_PROPERTY_INT_LOG_LEVEL, HELICS_LOG_LEVEL_ERROR); - helics::CombinationFederate mf1("fed1", cr, fi); + BOOST_CHECK_EQUAL (cFed.getEndpointCount (), 2); + auto &id = cFed.getEndpoint ("ept1"); + BOOST_CHECK_EQUAL (cFed.getEndpointType (id), "genmessage"); - mf1.registerGlobalFilter("filt1"); - mf1.registerGlobalFilter("filt2"); + BOOST_CHECK_EQUAL (cFed.getInputCount (), 2); + BOOST_CHECK_EQUAL (cFed.getPublicationCount (), 2); - EXPECT_NO_THROW(mf1.enterExecutingMode()); - mf1.disconnect(); - EXPECT_TRUE(cr.waitForDisconnect(std::chrono::milliseconds(500))); + cFed.disconnect (); } +BOOST_AUTO_TEST_SUITE_END () diff --git a/tests/helics/core/CMakeLists.txt b/tests/helics/core/CMakeLists.txt index 740424c0dc..26ce08fb7a 100644 --- a/tests/helics/core/CMakeLists.txt +++ b/tests/helics/core/CMakeLists.txt @@ -1,49 +1,79 @@ -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# Copyright (c) 2017-2022, Battelle Memorial Institute; Lawrence Livermore -# National Security, LLC; Alliance for Sustainable Energy, LLC. -# See the top-level NOTICE for additional details. -# All rights reserved. # -# SPDX-License-Identifier: BSD-3-Clause -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -set(core_test_headers) - -set(core_test_sources - core-tests.cpp - InfoClass-tests.cpp - FederateState-tests.cpp - ActionMessage-tests.cpp - BrokerClassTests.cpp - CoreFactory-tests.cpp - ForwardingTimeCoordinatorTests.cpp - TimeCoordinatorTests.cpp - CoreConfigureTests.cpp - FilterFederateTests.cpp - TimeDependenciesTests.cpp - CoreOperationsTests.cpp +# Copyright © 2017-2018, Battelle Memorial Institute; Lawrence Livermore National +# Security, LLC; Alliance for Sustainable Energy, LLC All rights reserved. See LICENSE +# file and DISCLAIMER for more details. +# + +# ----------------------------------------------------------------------------- +# Core tests using Boost +# ----------------------------------------------------------------------------- + +set(core_test_headers testFixtures.h) + +set( + core_test_sources + core-tests.cpp + InfoClass-tests.cpp + FederateState-tests.cpp + ActionMessage-tests.cpp + # CoreBroker-tests.cpp + CoreFactory-tests.cpp + MessageTimerTests.cpp + # CommonCore-tests.cpp + TestCore-tests.cpp + testFixtures.cpp + IPCcore_tests.cpp + IpcQueueTests.cpp + IpcQueueImplTests.cpp + data-block-tests.cpp + UdpCore-tests.cpp + ForwardingTimeCoordinatorTests.cpp + TimeCoordinatorTests.cpp + networkInfoTests.cpp ) -if(NOT HELICS_DISABLE_ASIO) - list(APPEND core_test_sources MessageTimerTests.cpp) +if(HELICS_HAVE_ZEROMQ) + list(APPEND core_test_sources ZeromqCore-tests.cpp) +endif(HELICS_HAVE_ZEROMQ) + +if(NOT DISABLE_TCP_CORE) + list(APPEND core_test_sources TcpCore-tests.cpp TcpSSCore-tests.cpp) +endif() + +if(MPI_ENABLE) + list(APPEND core_test_sources MpiCore-tests.cpp) endif() add_executable(core-tests ${core_test_sources} ${core_test_headers}) -target_link_libraries(core-tests helics_network helics_test_base) +target_link_libraries(core-tests helics-static helics_test_base) target_include_directories(core-tests PRIVATE ${PROJECT_SOURCE_DIR}/src) -target_compile_definitions(core-tests PRIVATE BOOST_DATE_TIME_NO_LIB) - -target_compile_definitions( - core-tests PRIVATE "-DTEST_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/../test_files/\"" -) - set_target_properties(core-tests PROPERTIES FOLDER tests) -add_test(NAME core-tests COMMAND core-tests) +add_test(NAME core-tests COMMAND core-tests --log_level=test_suite --report_level=short) set_property(TEST core-tests PROPERTY LABELS Core Coverage Daily) # Tests for Continuous Integration builds -add_test(NAME core-ci-tests COMMAND core-tests --gtest_filter=-*ci_skip*) -set_property(TEST core-ci-tests PROPERTY LABELS CoreCI Continuous) -# set_property(TEST core-ci-tests PROPERTY LABELS DebugTest) +add_test( + NAME core-ci-tests + COMMAND + core-tests + --run_test=@ci + --log_level=test_suite + --report_level=short +) +set_property(TEST core-ci-tests PROPERTY LABELS Continuous) +#set_property(TEST core-ci-tests PROPERTY LABELS DebugTest) +foreach(keyfile IN LISTS KEY_LIBRARY_FILES) + add_custom_command( + TARGET + core-tests + POST_BUILD # Adds a post-build event to core tests + COMMAND + ${CMAKE_COMMAND} + -E + copy_if_different # which executes "cmake - E copy_if_different..." + "${keyfile}" # <--this is in-file + "$/" + ) # <--this is out- file path +endforeach(keyfile) diff --git a/tests/helics/core/IpcQueueImpTests.cpp b/tests/helics/core/IpcQueueImpTests.cpp new file mode 100644 index 0000000000..1b35c26930 --- /dev/null +++ b/tests/helics/core/IpcQueueImpTests.cpp @@ -0,0 +1,506 @@ +/* +Copyright © 2017-2018, +Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable Energy, LLC +All rights reserved. See LICENSE file and DISCLAIMER for more details. +*/ + +#include + +#include "helics/core/ipc/IpcBlockingPriorityQueueImpl.hpp" +#include +#include +#include + +namespace utf = boost::unit_test; +using namespace std::literals::chrono_literals; + +BOOST_AUTO_TEST_SUITE (IpcQueue_tests, *utf::label ("ci")) + +BOOST_AUTO_TEST_CASE (creation_test) +{ + std::unique_ptr memblock (new unsigned char[10192]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 10192); + + std::vector data (500, 'a'); + BOOST_CHECK (queue.try_push (data.data (), 500)); + + data.assign (500, 'b'); + int res = queue.pop (data.data (), 500); + BOOST_CHECK_EQUAL (res, 500); + BOOST_CHECK_EQUAL (data[234], 'a'); + BOOST_CHECK_EQUAL (data[499], 'a'); +} + +BOOST_AUTO_TEST_CASE (push_pop_tests) +{ + std::unique_ptr memblock (new unsigned char[10192]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 10192); + + std::vector data (500, 'a'); + BOOST_CHECK (queue.try_push (data.data (), 400)); // this would go into the pull + queue.push (data.data (), 401); // this goes into push + queue.push (data.data (), 402); // this goes into push + queue.push (data.data (), 403); // this goes into push + + int sz = queue.pop (data.data (), 500); // pop from pull + BOOST_CHECK_EQUAL (sz, 400); + sz = queue.pop (data.data (), 500); // pull empty, so rotate + BOOST_CHECK_EQUAL (sz, 401); // pop from pull + queue.push (data.data (), 404); // this goes into push + queue.push (data.data (), 405); // this goes into push + queue.push (data.data (), 406); // this goes into push + + sz = queue.pop (data.data (), 500); // pull empty, so rotate + BOOST_CHECK_EQUAL (sz, 402); // pop from pull + + sz = queue.pop (data.data (), 500); + BOOST_CHECK_EQUAL (sz, 403); + + sz = queue.pop (data.data (), 500); + BOOST_CHECK_EQUAL (sz, 404); + sz = queue.pop (data.data (), 500); + BOOST_CHECK_EQUAL (sz, 405); + sz = queue.pop (data.data (), 500); + BOOST_CHECK_EQUAL (sz, 406); +} + +BOOST_AUTO_TEST_CASE (push_pop_priority_tests) +{ + std::unique_ptr memblock (new unsigned char[10192]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 10192); + + std::vector data (500, 'a'); + BOOST_CHECK (queue.try_push (data.data (), 400)); // this would go into the pull + queue.push (data.data (), 401); // this goes into push + queue.push (data.data (), 402); // this goes into push + queue.push (data.data (), 403); // this goes into push + + int sz = queue.pop (data.data (), 500); // pop from pull + BOOST_CHECK_EQUAL (sz, 400); + queue.pushPriority (data.data (), 417); + sz = queue.pop (data.data (), 500); // pull from priority + BOOST_CHECK_EQUAL (sz, 417); // pop from pull + sz = queue.pop (data.data (), 500); // pull from priority + BOOST_CHECK_EQUAL (sz, 401); // pop from pull + queue.push (data.data (), 404); // this goes into push + queue.pushPriority (data.data (), 420); // this goes into priority + queue.pushPriority (data.data (), 421); // this goes into priority + queue.push (data.data (), 405); // this goes into push + queue.push (data.data (), 406); // this goes into push + + sz = queue.pop (data.data (), 500); // pull empty, so rotate + BOOST_CHECK_EQUAL (sz, 420); // pop from priority + sz = queue.pop (data.data (), 500); // pull empty, so rotate + BOOST_CHECK_EQUAL (sz, 421); // pop from priority + + sz = queue.pop (data.data (), 500); // pull empty, so rotate + BOOST_CHECK_EQUAL (sz, 402); // pop from pull + + sz = queue.pop (data.data (), 500); + BOOST_CHECK_EQUAL (sz, 403); + + sz = queue.pop (data.data (), 500); + BOOST_CHECK_EQUAL (sz, 404); + sz = queue.pop (data.data (), 500); + BOOST_CHECK_EQUAL (sz, 405); + BOOST_CHECK (!queue.empty ()); + sz = queue.try_pop (data.data (), 500); + BOOST_CHECK_EQUAL (sz, 406); + + BOOST_CHECK (queue.empty ()); +} + +BOOST_AUTO_TEST_CASE (push_full) +{ + std::unique_ptr memblock (new unsigned char[4096]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 4096); + std::vector data (500, 'a'); + BOOST_CHECK (queue.try_push (data.data (), 420)); // this would go into the pull + BOOST_CHECK (queue.try_push (data.data (), 420)); // this would go into the push + BOOST_CHECK (queue.try_push (data.data (), 420)); // this would go into the push + BOOST_CHECK (queue.try_push (data.data (), 420)); // this would go into the push + + BOOST_CHECK (!queue.try_push (data.data (), 420)); // this should fail as it is full + BOOST_CHECK_EQUAL (queue.push (std::chrono::milliseconds (50), data.data (), 420), + 0); // this should return 0 as timeout +} + +BOOST_AUTO_TEST_CASE (push_priority_full) +{ + std::unique_ptr memblock (new unsigned char[4096]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 4096); + std::vector data (500, 'a'); + BOOST_CHECK (queue.try_pushPriority (data.data (), 390)); // this would go into the pull + BOOST_CHECK (queue.try_pushPriority (data.data (), 390)); // this would go into the push + + BOOST_CHECK (!queue.try_pushPriority (data.data (), 390)); // this should fail as it is full + BOOST_CHECK_EQUAL (queue.pushPriority (std::chrono::milliseconds (50), data.data (), 420), + 0); // this should return 0 as timeout +} + +BOOST_AUTO_TEST_CASE (pop_wait) +{ + std::unique_ptr memblock (new unsigned char[4096]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 4096); + std::vector data (500, 'a'); + BOOST_CHECK_EQUAL (queue.try_pop (data.data (), 390), 0); // this would go into the pull + + BOOST_CHECK_EQUAL (queue.pop (std::chrono::milliseconds (100), data.data (), 420), + 0); // this should return 0 as timeout +} + +BOOST_AUTO_TEST_CASE (pop_wait2) +{ + std::unique_ptr memblock (new unsigned char[4096]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 4096); + std::vector data (500, 'a'); + + auto def = std::async (std::launch::async, [&]() { + std::this_thread::sleep_for (std::chrono::milliseconds (50)); + queue.push (data.data (), 300); + }); + + int ret = queue.pop (data.data (), 500); + BOOST_CHECK_EQUAL (ret, 300); + def.wait(); +} + +BOOST_AUTO_TEST_CASE(pop_wait_priority) +{ + std::unique_ptr memblock(new unsigned char[4096]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); + std::vector data(500, 'a'); + + auto def = std::async(std::launch::async, [&]() { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + queue.pushPriority(data.data(), 300); + }); + + int ret = queue.pop(data.data(), 500); + BOOST_CHECK_EQUAL(ret, 300); + def.wait(); +} + + +BOOST_AUTO_TEST_CASE(push_wait) +{ + std::unique_ptr memblock(new unsigned char[4096]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); + std::vector data(500, 'a'); + std::vector data2(500, 'a'); + BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the pull + BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the push + BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the push + BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the push + + BOOST_CHECK(!queue.try_push(data.data(), 420)); // this should fail as it is full + auto def = std::async(std::launch::async, [&]() { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + queue.pop(data.data(), 500); + }); + queue.push(data.data(), 420); + def.wait(); +} + +BOOST_AUTO_TEST_CASE(priority_push_wait) +{ + std::unique_ptr memblock(new unsigned char[4096]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); + std::vector data(500, 'a'); + std::vector data2(500, 'a'); + + BOOST_CHECK(queue.try_pushPriority(data.data(), 390)); // this would go into the pull + BOOST_CHECK(queue.try_pushPriority(data.data(), 390)); // this would go into the push + + BOOST_CHECK(!queue.try_pushPriority(data.data(), 390)); // this should fail as it is full + + auto def = std::async(std::launch::async, [&]() { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + queue.pop(data.data(), 500); + }); + queue.pushPriority(data.data(), 390); + def.wait(); +} +/** test with single consumer/single producer*/ + +BOOST_AUTO_TEST_CASE(multithreaded_tests) +{ + std::unique_ptr memblock(new unsigned char[1048576]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 1048576); + + + for (int64_t ii = 0; ii < 10'000; ++ii) + { + queue.push(reinterpret_cast(&ii),8); + } + + auto prod1 = [&]() { + int64_t bdata; + for (int64_t jj = 10'000; jj < 1'010'000; ++jj) + { + bdata = jj; + queue.push(reinterpret_cast(&bdata), 8); + } + }; + + auto cons = [&]() { + int64_t data; + auto res = queue.try_pop(reinterpret_cast(&data),8); + int64_t cnt = 0; + while ((res)) + { + ++cnt; + res = queue.try_pop(reinterpret_cast(&data), 8); + if (res==0) + { // make an additional sleep period so the producer can catch up + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + res = queue.try_pop(reinterpret_cast(&data), 8); + } + } + return cnt; + }; + + auto ret = std::async(std::launch::async, prod1); + + auto res = std::async(std::launch::async, cons); + + ret.wait(); + auto V = res.get(); + BOOST_CHECK_EQUAL(V, 1'010'000); +} + +/** test with multiple consumer/single producer*/ +BOOST_AUTO_TEST_CASE(multithreaded_tests2) +{ + std::unique_ptr memblock(new unsigned char[1048576]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 1048576); + for (int64_t ii = 0; ii < 10'000; ++ii) + { + queue.push(reinterpret_cast(&ii), 8); + } + auto prod1 = [&]() { + int64_t bdata; + for (int64_t jj = 10'000; jj < 1'010'000; ++jj) + { + bdata = jj; + queue.push(reinterpret_cast(&bdata), 8); + } + }; + + auto cons = [&]() { + int64_t data; + auto res = queue.try_pop(reinterpret_cast(&data), 8); + int64_t cnt = 0; + while ((res)) + { + ++cnt; + res = queue.try_pop(reinterpret_cast(&data), 8); + if (res == 0) + { // make an additional sleep period so the producer can catch up + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + res = queue.try_pop(reinterpret_cast(&data), 8); + } + } + return cnt; + }; + + auto ret = std::async(std::launch::async, prod1); + + auto res1 = std::async(std::launch::async, cons); + auto res2 = std::async(std::launch::async, cons); + auto res3 = std::async(std::launch::async, cons); + ret.wait(); + auto V1 = res1.get(); + auto V2 = res2.get(); + auto V3 = res3.get(); + + BOOST_CHECK_EQUAL(V1 + V2 + V3, 1'010'000); +} + +/** test with multiple producer/multiple consumer*/ +BOOST_AUTO_TEST_CASE(multithreaded_tests3) +{ + std::unique_ptr memblock(new unsigned char[1048576]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 1048576); + for (int64_t ii = 0; ii < 10'000; ++ii) + { + queue.push(reinterpret_cast(&ii), 8); + } + auto prod1 = [&]() { + int64_t bdata; + for (int64_t jj = 10'000; jj < 1'010'000; ++jj) + { + bdata = jj; + queue.push(reinterpret_cast(&bdata), 8); + } + }; + + auto cons = [&]() { + int64_t data; + auto res = queue.try_pop(reinterpret_cast(&data), 8); + int64_t cnt = 0; + while ((res)) + { + ++cnt; + res = queue.try_pop(reinterpret_cast(&data), 8); + if (res == 0) + { // make an additional sleep period so the producer can catch up + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + res = queue.try_pop(reinterpret_cast(&data), 8); + } + } + return cnt; + }; + + + auto ret1 = std::async(std::launch::async, prod1); + auto ret2 = std::async(std::launch::async, prod1); + auto ret3 = std::async(std::launch::async, prod1); + + auto res1 = std::async(std::launch::async, cons); + auto res2 = std::async(std::launch::async, cons); + auto res3 = std::async(std::launch::async, cons); + ret1.wait(); + ret2.wait(); + ret3.wait(); + auto V1 = res1.get(); + auto V2 = res2.get(); + auto V3 = res3.get(); + + BOOST_CHECK_EQUAL(V1 + V2 + V3, 3'010'000); +} + +/** test with multiple producer/multiple consumer*/ +BOOST_AUTO_TEST_CASE(multithreaded_tests3_pop) +{ + std::unique_ptr memblock(new unsigned char[1048576]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 1048576); + + auto prod1 = [&]() { + int64_t bdata; + for (int64_t jj = 0; jj < 1'000'000; ++jj) + { + bdata = jj; + queue.push(reinterpret_cast(&bdata), 8); + } + bdata = (-1); + queue.push(reinterpret_cast(&bdata), 8); + }; + + auto cons = [&]() { + int64_t data=0; + int64_t cnt = 0; + while (data>=0) + { + ++cnt; + queue.pop(reinterpret_cast(&data), 8); + } + return cnt; + }; + + + auto ret1 = std::async(std::launch::async, prod1); + auto ret2 = std::async(std::launch::async, prod1); + auto ret3 = std::async(std::launch::async, prod1); + + auto res1 = std::async(std::launch::async, cons); + auto res2 = std::async(std::launch::async, cons); + auto res3 = std::async(std::launch::async, cons); + ret1.wait(); + ret2.wait(); + ret3.wait(); + auto V1 = res1.get(); + auto V2 = res2.get(); + auto V3 = res3.get(); + + BOOST_CHECK_EQUAL(V1 + V2 + V3, 3'000'003); +} + + +BOOST_AUTO_TEST_CASE(push_full_mem) +{ + std::unique_ptr memblock(new unsigned char[4096]); + + std::unique_ptr memblock2(new unsigned char[4096]); + + auto *queue =new(memblock2.get()) helics::ipc::detail::IpcBlockingPriorityQueueImpl(memblock.get(), 4096); + std::vector data(500, 'a'); + BOOST_CHECK(queue->try_push(data.data(), 420)); // this would go into the pull + BOOST_CHECK(queue->try_push(data.data(), 420)); // this would go into the push + BOOST_CHECK(queue->try_push(data.data(), 420)); // this would go into the push + BOOST_CHECK(queue->try_push(data.data(), 420)); // this would go into the push + + BOOST_CHECK(!queue->try_push(data.data(), 420)); // this should fail as it is full + BOOST_CHECK_EQUAL(queue->push(std::chrono::milliseconds(50), data.data(), 420), + 0); // this should return 0 as timeout +} + + + +/** test with multiple producer/multiple consumer*/ +BOOST_AUTO_TEST_CASE(multithreaded_tests3_pop_mem) +{ + std::unique_ptr memblock(new unsigned char[1048576]); + + std::unique_ptr memblock2(new unsigned char[4096]); + + auto *qn = new(memblock2.get()) helics::ipc::detail::IpcBlockingPriorityQueueImpl(memblock.get(), 1048576); + + auto prod1 = [&](void *data) { + auto *queue = reinterpret_cast(data); + int64_t bdata; + for (int64_t jj = 0; jj < 1'000'000; ++jj) + { + bdata = jj; + queue->push(reinterpret_cast(&bdata), 8); + } + bdata = (-1); + queue->push(reinterpret_cast(&bdata), 8); + }; + + auto cons = [&](void *mem) { + auto *queue = reinterpret_cast(mem); + int64_t data = 0; + int64_t cnt = 0; + while (data >= 0) + { + ++cnt; + queue->pop(reinterpret_cast(&data), 8); + } + return cnt; + }; + + + auto ret1 = std::async(std::launch::async, prod1, memblock2.get()); + auto ret2 = std::async(std::launch::async, prod1, memblock2.get()); + auto ret3 = std::async(std::launch::async, prod1, memblock2.get()); + + auto res1 = std::async(std::launch::async, cons, memblock2.get()); + auto res2 = std::async(std::launch::async, cons, memblock2.get()); + auto res3 = std::async(std::launch::async, cons, memblock2.get()); + ret1.wait(); + ret2.wait(); + ret3.wait(); + auto V1 = res1.get(); + auto V2 = res2.get(); + auto V3 = res3.get(); + + BOOST_CHECK_EQUAL(V1 + V2 + V3, 3'000'003); + +} + +BOOST_AUTO_TEST_SUITE_END () \ No newline at end of file diff --git a/tests/helics/core/IpcQueueTests.cpp b/tests/helics/core/IpcQueueTests.cpp new file mode 100644 index 0000000000..1b35c26930 --- /dev/null +++ b/tests/helics/core/IpcQueueTests.cpp @@ -0,0 +1,506 @@ +/* +Copyright © 2017-2018, +Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable Energy, LLC +All rights reserved. See LICENSE file and DISCLAIMER for more details. +*/ + +#include + +#include "helics/core/ipc/IpcBlockingPriorityQueueImpl.hpp" +#include +#include +#include + +namespace utf = boost::unit_test; +using namespace std::literals::chrono_literals; + +BOOST_AUTO_TEST_SUITE (IpcQueue_tests, *utf::label ("ci")) + +BOOST_AUTO_TEST_CASE (creation_test) +{ + std::unique_ptr memblock (new unsigned char[10192]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 10192); + + std::vector data (500, 'a'); + BOOST_CHECK (queue.try_push (data.data (), 500)); + + data.assign (500, 'b'); + int res = queue.pop (data.data (), 500); + BOOST_CHECK_EQUAL (res, 500); + BOOST_CHECK_EQUAL (data[234], 'a'); + BOOST_CHECK_EQUAL (data[499], 'a'); +} + +BOOST_AUTO_TEST_CASE (push_pop_tests) +{ + std::unique_ptr memblock (new unsigned char[10192]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 10192); + + std::vector data (500, 'a'); + BOOST_CHECK (queue.try_push (data.data (), 400)); // this would go into the pull + queue.push (data.data (), 401); // this goes into push + queue.push (data.data (), 402); // this goes into push + queue.push (data.data (), 403); // this goes into push + + int sz = queue.pop (data.data (), 500); // pop from pull + BOOST_CHECK_EQUAL (sz, 400); + sz = queue.pop (data.data (), 500); // pull empty, so rotate + BOOST_CHECK_EQUAL (sz, 401); // pop from pull + queue.push (data.data (), 404); // this goes into push + queue.push (data.data (), 405); // this goes into push + queue.push (data.data (), 406); // this goes into push + + sz = queue.pop (data.data (), 500); // pull empty, so rotate + BOOST_CHECK_EQUAL (sz, 402); // pop from pull + + sz = queue.pop (data.data (), 500); + BOOST_CHECK_EQUAL (sz, 403); + + sz = queue.pop (data.data (), 500); + BOOST_CHECK_EQUAL (sz, 404); + sz = queue.pop (data.data (), 500); + BOOST_CHECK_EQUAL (sz, 405); + sz = queue.pop (data.data (), 500); + BOOST_CHECK_EQUAL (sz, 406); +} + +BOOST_AUTO_TEST_CASE (push_pop_priority_tests) +{ + std::unique_ptr memblock (new unsigned char[10192]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 10192); + + std::vector data (500, 'a'); + BOOST_CHECK (queue.try_push (data.data (), 400)); // this would go into the pull + queue.push (data.data (), 401); // this goes into push + queue.push (data.data (), 402); // this goes into push + queue.push (data.data (), 403); // this goes into push + + int sz = queue.pop (data.data (), 500); // pop from pull + BOOST_CHECK_EQUAL (sz, 400); + queue.pushPriority (data.data (), 417); + sz = queue.pop (data.data (), 500); // pull from priority + BOOST_CHECK_EQUAL (sz, 417); // pop from pull + sz = queue.pop (data.data (), 500); // pull from priority + BOOST_CHECK_EQUAL (sz, 401); // pop from pull + queue.push (data.data (), 404); // this goes into push + queue.pushPriority (data.data (), 420); // this goes into priority + queue.pushPriority (data.data (), 421); // this goes into priority + queue.push (data.data (), 405); // this goes into push + queue.push (data.data (), 406); // this goes into push + + sz = queue.pop (data.data (), 500); // pull empty, so rotate + BOOST_CHECK_EQUAL (sz, 420); // pop from priority + sz = queue.pop (data.data (), 500); // pull empty, so rotate + BOOST_CHECK_EQUAL (sz, 421); // pop from priority + + sz = queue.pop (data.data (), 500); // pull empty, so rotate + BOOST_CHECK_EQUAL (sz, 402); // pop from pull + + sz = queue.pop (data.data (), 500); + BOOST_CHECK_EQUAL (sz, 403); + + sz = queue.pop (data.data (), 500); + BOOST_CHECK_EQUAL (sz, 404); + sz = queue.pop (data.data (), 500); + BOOST_CHECK_EQUAL (sz, 405); + BOOST_CHECK (!queue.empty ()); + sz = queue.try_pop (data.data (), 500); + BOOST_CHECK_EQUAL (sz, 406); + + BOOST_CHECK (queue.empty ()); +} + +BOOST_AUTO_TEST_CASE (push_full) +{ + std::unique_ptr memblock (new unsigned char[4096]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 4096); + std::vector data (500, 'a'); + BOOST_CHECK (queue.try_push (data.data (), 420)); // this would go into the pull + BOOST_CHECK (queue.try_push (data.data (), 420)); // this would go into the push + BOOST_CHECK (queue.try_push (data.data (), 420)); // this would go into the push + BOOST_CHECK (queue.try_push (data.data (), 420)); // this would go into the push + + BOOST_CHECK (!queue.try_push (data.data (), 420)); // this should fail as it is full + BOOST_CHECK_EQUAL (queue.push (std::chrono::milliseconds (50), data.data (), 420), + 0); // this should return 0 as timeout +} + +BOOST_AUTO_TEST_CASE (push_priority_full) +{ + std::unique_ptr memblock (new unsigned char[4096]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 4096); + std::vector data (500, 'a'); + BOOST_CHECK (queue.try_pushPriority (data.data (), 390)); // this would go into the pull + BOOST_CHECK (queue.try_pushPriority (data.data (), 390)); // this would go into the push + + BOOST_CHECK (!queue.try_pushPriority (data.data (), 390)); // this should fail as it is full + BOOST_CHECK_EQUAL (queue.pushPriority (std::chrono::milliseconds (50), data.data (), 420), + 0); // this should return 0 as timeout +} + +BOOST_AUTO_TEST_CASE (pop_wait) +{ + std::unique_ptr memblock (new unsigned char[4096]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 4096); + std::vector data (500, 'a'); + BOOST_CHECK_EQUAL (queue.try_pop (data.data (), 390), 0); // this would go into the pull + + BOOST_CHECK_EQUAL (queue.pop (std::chrono::milliseconds (100), data.data (), 420), + 0); // this should return 0 as timeout +} + +BOOST_AUTO_TEST_CASE (pop_wait2) +{ + std::unique_ptr memblock (new unsigned char[4096]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 4096); + std::vector data (500, 'a'); + + auto def = std::async (std::launch::async, [&]() { + std::this_thread::sleep_for (std::chrono::milliseconds (50)); + queue.push (data.data (), 300); + }); + + int ret = queue.pop (data.data (), 500); + BOOST_CHECK_EQUAL (ret, 300); + def.wait(); +} + +BOOST_AUTO_TEST_CASE(pop_wait_priority) +{ + std::unique_ptr memblock(new unsigned char[4096]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); + std::vector data(500, 'a'); + + auto def = std::async(std::launch::async, [&]() { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + queue.pushPriority(data.data(), 300); + }); + + int ret = queue.pop(data.data(), 500); + BOOST_CHECK_EQUAL(ret, 300); + def.wait(); +} + + +BOOST_AUTO_TEST_CASE(push_wait) +{ + std::unique_ptr memblock(new unsigned char[4096]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); + std::vector data(500, 'a'); + std::vector data2(500, 'a'); + BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the pull + BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the push + BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the push + BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the push + + BOOST_CHECK(!queue.try_push(data.data(), 420)); // this should fail as it is full + auto def = std::async(std::launch::async, [&]() { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + queue.pop(data.data(), 500); + }); + queue.push(data.data(), 420); + def.wait(); +} + +BOOST_AUTO_TEST_CASE(priority_push_wait) +{ + std::unique_ptr memblock(new unsigned char[4096]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); + std::vector data(500, 'a'); + std::vector data2(500, 'a'); + + BOOST_CHECK(queue.try_pushPriority(data.data(), 390)); // this would go into the pull + BOOST_CHECK(queue.try_pushPriority(data.data(), 390)); // this would go into the push + + BOOST_CHECK(!queue.try_pushPriority(data.data(), 390)); // this should fail as it is full + + auto def = std::async(std::launch::async, [&]() { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + queue.pop(data.data(), 500); + }); + queue.pushPriority(data.data(), 390); + def.wait(); +} +/** test with single consumer/single producer*/ + +BOOST_AUTO_TEST_CASE(multithreaded_tests) +{ + std::unique_ptr memblock(new unsigned char[1048576]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 1048576); + + + for (int64_t ii = 0; ii < 10'000; ++ii) + { + queue.push(reinterpret_cast(&ii),8); + } + + auto prod1 = [&]() { + int64_t bdata; + for (int64_t jj = 10'000; jj < 1'010'000; ++jj) + { + bdata = jj; + queue.push(reinterpret_cast(&bdata), 8); + } + }; + + auto cons = [&]() { + int64_t data; + auto res = queue.try_pop(reinterpret_cast(&data),8); + int64_t cnt = 0; + while ((res)) + { + ++cnt; + res = queue.try_pop(reinterpret_cast(&data), 8); + if (res==0) + { // make an additional sleep period so the producer can catch up + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + res = queue.try_pop(reinterpret_cast(&data), 8); + } + } + return cnt; + }; + + auto ret = std::async(std::launch::async, prod1); + + auto res = std::async(std::launch::async, cons); + + ret.wait(); + auto V = res.get(); + BOOST_CHECK_EQUAL(V, 1'010'000); +} + +/** test with multiple consumer/single producer*/ +BOOST_AUTO_TEST_CASE(multithreaded_tests2) +{ + std::unique_ptr memblock(new unsigned char[1048576]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 1048576); + for (int64_t ii = 0; ii < 10'000; ++ii) + { + queue.push(reinterpret_cast(&ii), 8); + } + auto prod1 = [&]() { + int64_t bdata; + for (int64_t jj = 10'000; jj < 1'010'000; ++jj) + { + bdata = jj; + queue.push(reinterpret_cast(&bdata), 8); + } + }; + + auto cons = [&]() { + int64_t data; + auto res = queue.try_pop(reinterpret_cast(&data), 8); + int64_t cnt = 0; + while ((res)) + { + ++cnt; + res = queue.try_pop(reinterpret_cast(&data), 8); + if (res == 0) + { // make an additional sleep period so the producer can catch up + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + res = queue.try_pop(reinterpret_cast(&data), 8); + } + } + return cnt; + }; + + auto ret = std::async(std::launch::async, prod1); + + auto res1 = std::async(std::launch::async, cons); + auto res2 = std::async(std::launch::async, cons); + auto res3 = std::async(std::launch::async, cons); + ret.wait(); + auto V1 = res1.get(); + auto V2 = res2.get(); + auto V3 = res3.get(); + + BOOST_CHECK_EQUAL(V1 + V2 + V3, 1'010'000); +} + +/** test with multiple producer/multiple consumer*/ +BOOST_AUTO_TEST_CASE(multithreaded_tests3) +{ + std::unique_ptr memblock(new unsigned char[1048576]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 1048576); + for (int64_t ii = 0; ii < 10'000; ++ii) + { + queue.push(reinterpret_cast(&ii), 8); + } + auto prod1 = [&]() { + int64_t bdata; + for (int64_t jj = 10'000; jj < 1'010'000; ++jj) + { + bdata = jj; + queue.push(reinterpret_cast(&bdata), 8); + } + }; + + auto cons = [&]() { + int64_t data; + auto res = queue.try_pop(reinterpret_cast(&data), 8); + int64_t cnt = 0; + while ((res)) + { + ++cnt; + res = queue.try_pop(reinterpret_cast(&data), 8); + if (res == 0) + { // make an additional sleep period so the producer can catch up + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + res = queue.try_pop(reinterpret_cast(&data), 8); + } + } + return cnt; + }; + + + auto ret1 = std::async(std::launch::async, prod1); + auto ret2 = std::async(std::launch::async, prod1); + auto ret3 = std::async(std::launch::async, prod1); + + auto res1 = std::async(std::launch::async, cons); + auto res2 = std::async(std::launch::async, cons); + auto res3 = std::async(std::launch::async, cons); + ret1.wait(); + ret2.wait(); + ret3.wait(); + auto V1 = res1.get(); + auto V2 = res2.get(); + auto V3 = res3.get(); + + BOOST_CHECK_EQUAL(V1 + V2 + V3, 3'010'000); +} + +/** test with multiple producer/multiple consumer*/ +BOOST_AUTO_TEST_CASE(multithreaded_tests3_pop) +{ + std::unique_ptr memblock(new unsigned char[1048576]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 1048576); + + auto prod1 = [&]() { + int64_t bdata; + for (int64_t jj = 0; jj < 1'000'000; ++jj) + { + bdata = jj; + queue.push(reinterpret_cast(&bdata), 8); + } + bdata = (-1); + queue.push(reinterpret_cast(&bdata), 8); + }; + + auto cons = [&]() { + int64_t data=0; + int64_t cnt = 0; + while (data>=0) + { + ++cnt; + queue.pop(reinterpret_cast(&data), 8); + } + return cnt; + }; + + + auto ret1 = std::async(std::launch::async, prod1); + auto ret2 = std::async(std::launch::async, prod1); + auto ret3 = std::async(std::launch::async, prod1); + + auto res1 = std::async(std::launch::async, cons); + auto res2 = std::async(std::launch::async, cons); + auto res3 = std::async(std::launch::async, cons); + ret1.wait(); + ret2.wait(); + ret3.wait(); + auto V1 = res1.get(); + auto V2 = res2.get(); + auto V3 = res3.get(); + + BOOST_CHECK_EQUAL(V1 + V2 + V3, 3'000'003); +} + + +BOOST_AUTO_TEST_CASE(push_full_mem) +{ + std::unique_ptr memblock(new unsigned char[4096]); + + std::unique_ptr memblock2(new unsigned char[4096]); + + auto *queue =new(memblock2.get()) helics::ipc::detail::IpcBlockingPriorityQueueImpl(memblock.get(), 4096); + std::vector data(500, 'a'); + BOOST_CHECK(queue->try_push(data.data(), 420)); // this would go into the pull + BOOST_CHECK(queue->try_push(data.data(), 420)); // this would go into the push + BOOST_CHECK(queue->try_push(data.data(), 420)); // this would go into the push + BOOST_CHECK(queue->try_push(data.data(), 420)); // this would go into the push + + BOOST_CHECK(!queue->try_push(data.data(), 420)); // this should fail as it is full + BOOST_CHECK_EQUAL(queue->push(std::chrono::milliseconds(50), data.data(), 420), + 0); // this should return 0 as timeout +} + + + +/** test with multiple producer/multiple consumer*/ +BOOST_AUTO_TEST_CASE(multithreaded_tests3_pop_mem) +{ + std::unique_ptr memblock(new unsigned char[1048576]); + + std::unique_ptr memblock2(new unsigned char[4096]); + + auto *qn = new(memblock2.get()) helics::ipc::detail::IpcBlockingPriorityQueueImpl(memblock.get(), 1048576); + + auto prod1 = [&](void *data) { + auto *queue = reinterpret_cast(data); + int64_t bdata; + for (int64_t jj = 0; jj < 1'000'000; ++jj) + { + bdata = jj; + queue->push(reinterpret_cast(&bdata), 8); + } + bdata = (-1); + queue->push(reinterpret_cast(&bdata), 8); + }; + + auto cons = [&](void *mem) { + auto *queue = reinterpret_cast(mem); + int64_t data = 0; + int64_t cnt = 0; + while (data >= 0) + { + ++cnt; + queue->pop(reinterpret_cast(&data), 8); + } + return cnt; + }; + + + auto ret1 = std::async(std::launch::async, prod1, memblock2.get()); + auto ret2 = std::async(std::launch::async, prod1, memblock2.get()); + auto ret3 = std::async(std::launch::async, prod1, memblock2.get()); + + auto res1 = std::async(std::launch::async, cons, memblock2.get()); + auto res2 = std::async(std::launch::async, cons, memblock2.get()); + auto res3 = std::async(std::launch::async, cons, memblock2.get()); + ret1.wait(); + ret2.wait(); + ret3.wait(); + auto V1 = res1.get(); + auto V2 = res2.get(); + auto V3 = res3.get(); + + BOOST_CHECK_EQUAL(V1 + V2 + V3, 3'000'003); + +} + +BOOST_AUTO_TEST_SUITE_END () \ No newline at end of file From 87644f6a49fa9fdce41917f270ff6ce8babdab1c Mon Sep 17 00:00:00 2001 From: Philip Top Date: Thu, 6 Dec 2018 12:20:50 -0800 Subject: [PATCH 4/8] update a few tests # Conflicts: # tests/helics/application_api/CombinationFederateTests.cpp --- .../CombinationFederateTests.cpp | 67 +-- tests/helics/core/IpcQueueImplTests.cpp | 506 ++++++++++++++++++ 2 files changed, 540 insertions(+), 33 deletions(-) create mode 100644 tests/helics/core/IpcQueueImplTests.cpp diff --git a/tests/helics/application_api/CombinationFederateTests.cpp b/tests/helics/application_api/CombinationFederateTests.cpp index c27ff2efaa..d6afbc969b 100644 --- a/tests/helics/application_api/CombinationFederateTests.cpp +++ b/tests/helics/application_api/CombinationFederateTests.cpp @@ -4,18 +4,18 @@ Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance All rights reserved. See LICENSE file and DISCLAIMER for more details. */ -#include -#include -#include -#include "helics/application_api/Publications.hpp" -#include "helics/application_api/Endpoints.hpp" #include "helics/application_api/CombinationFederate.hpp" +#include "helics/application_api/Endpoints.hpp" +#include "helics/application_api/Publications.hpp" #include "helics/core/BrokerFactory.hpp" #include "helics/core/Core.hpp" #include "helics/core/CoreFactory.hpp" #include "helics/core/core-exceptions.hpp" #include "testFixtures.hpp" #include +#include +#include +#include namespace bdata = boost::unit_test::data; namespace utf = boost::unit_test; @@ -31,11 +31,11 @@ BOOST_DATA_TEST_CASE (combo_federate_initialize_tests, bdata::make (core_types_s vFed1->enterExecutingMode (); - BOOST_CHECK (vFed1->getCurrentState () == helics::Federate::op_states::execution); + BOOST_CHECK (vFed1->getCurrentState () == helics::Federate::states::execution); vFed1->finalize (); - BOOST_CHECK (vFed1->getCurrentState () == helics::Federate::op_states::finalize); + BOOST_CHECK (vFed1->getCurrentState () == helics::Federate::states::finalize); } BOOST_DATA_TEST_CASE (combo_federate_publication_registration, bdata::make (core_types_single), core_type) @@ -49,7 +49,7 @@ BOOST_DATA_TEST_CASE (combo_federate_publication_registration, bdata::make (core auto &pubid3 = vFed1->registerPublication ("pub3", "double", "V"); vFed1->enterExecutingMode (); - BOOST_CHECK (vFed1->getCurrentState () == helics::Federate::op_states::execution); + BOOST_CHECK (vFed1->getCurrentState () == helics::Federate::states::execution); auto &sv = vFed1->getPublicationKey (pubid); auto &sv2 = vFed1->getPublicationKey (pubid2); @@ -66,7 +66,7 @@ BOOST_DATA_TEST_CASE (combo_federate_publication_registration, bdata::make (core BOOST_CHECK (vFed1->getPublication ("fed0/pub1").getHandle () == pubid.getHandle ()); vFed1->finalize (); - BOOST_CHECK (vFed1->getCurrentState () == helics::Federate::op_states::finalize); + BOOST_CHECK (vFed1->getCurrentState () == helics::Federate::states::finalize); } BOOST_DATA_TEST_CASE (combo_federate_single_transfer, bdata::make (core_types_single), core_type) @@ -77,8 +77,8 @@ BOOST_DATA_TEST_CASE (combo_federate_single_transfer, bdata::make (core_types_si // register the publications auto &pubid = vFed1->registerGlobalPublication ("pub1"); - auto &subid = vFed1->registerSubscription("pub1"); - vFed1->setTimeProperty (TIME_DELTA_PROPERTY, 1.0); + auto &subid = vFed1->registerSubscription ("pub1"); + vFed1->setTimeProperty (helics_property_time_delta, 1.0); vFed1->enterExecutingMode (); // publish string1 at time=0.0; vFed1->publish (pubid, "string1"); @@ -92,14 +92,14 @@ BOOST_DATA_TEST_CASE (combo_federate_single_transfer, bdata::make (core_types_si // publish a second string vFed1->publish (pubid, "string2"); // make sure the value is still what we expect - s=vFed1->getString (subid); - + s = vFed1->getString (subid); + BOOST_CHECK_EQUAL (s, "string1"); // advance time gtime = vFed1->requestTime (2.0); // make sure the value was updated BOOST_CHECK_EQUAL (gtime, 2.0); - s=vFed1->getString (subid); + s = vFed1->getString (subid); BOOST_CHECK_EQUAL (s, "string2"); } @@ -114,7 +114,7 @@ BOOST_DATA_TEST_CASE (combo_federate_endpoint_registration, bdata::make (core_ty mFed1->enterExecutingMode (); - BOOST_CHECK (mFed1->getCurrentState () == helics::Federate::op_states::execution); + BOOST_CHECK (mFed1->getCurrentState () == helics::Federate::states::execution); auto &sv = mFed1->getEndpointName (epid); auto &sv2 = mFed1->getEndpointName (epid2); @@ -129,7 +129,7 @@ BOOST_DATA_TEST_CASE (combo_federate_endpoint_registration, bdata::make (core_ty BOOST_CHECK (mFed1->getEndpoint ("ep2").getHandle () == epid2.getHandle ()); mFed1->finalize (); - BOOST_CHECK (mFed1->getCurrentState () == helics::Federate::op_states::finalize); + BOOST_CHECK (mFed1->getCurrentState () == helics::Federate::states::finalize); } BOOST_DATA_TEST_CASE (combination_federate_send_receive_2fed, bdata::make (core_types), core_type) @@ -141,15 +141,15 @@ BOOST_DATA_TEST_CASE (combination_federate_send_receive_2fed, bdata::make (core_ auto &epid = mFed1->registerEndpoint ("ep1"); auto &epid2 = mFed2->registerGlobalEndpoint ("ep2", "random"); - mFed1->setTimeProperty (TIME_DELTA_PROPERTY, 1.0); - mFed2->setTimeProperty (TIME_DELTA_PROPERTY, 1.0); + mFed1->setTimeProperty (helics_property_time_delta, 1.0); + mFed2->setTimeProperty (helics_property_time_delta, 1.0); auto f1finish = std::async (std::launch::async, [&]() { mFed1->enterExecutingMode (); }); mFed2->enterExecutingMode (); f1finish.wait (); - BOOST_CHECK (mFed1->getCurrentState () == helics::Federate::op_states::execution); - BOOST_CHECK (mFed2->getCurrentState () == helics::Federate::op_states::execution); + BOOST_CHECK (mFed1->getCurrentState () == helics::Federate::states::execution); + BOOST_CHECK (mFed2->getCurrentState () == helics::Federate::states::execution); helics::data_block data (500, 'a'); helics::data_block data2 (400, 'b'); @@ -182,8 +182,8 @@ BOOST_DATA_TEST_CASE (combination_federate_send_receive_2fed, bdata::make (core_ mFed1->finalize (); mFed2->finalize (); - BOOST_CHECK (mFed1->getCurrentState () == helics::Federate::op_states::finalize); - BOOST_CHECK (mFed2->getCurrentState () == helics::Federate::op_states::finalize); + BOOST_CHECK (mFed1->getCurrentState () == helics::Federate::states::finalize); + BOOST_CHECK (mFed2->getCurrentState () == helics::Federate::states::finalize); } BOOST_DATA_TEST_CASE (combination_federate_multimode_transfer, bdata::make (core_types), core_type) @@ -198,10 +198,10 @@ BOOST_DATA_TEST_CASE (combination_federate_multimode_transfer, bdata::make (core // register the publications auto &pubid = cFed1->registerGlobalPublication ("pub1"); - auto &subid = cFed2->registerSubscription("pub1"); + auto &subid = cFed2->registerSubscription ("pub1"); - cFed1->setTimeProperty (TIME_DELTA_PROPERTY, 1.0); - cFed2->setTimeProperty (TIME_DELTA_PROPERTY, 1.0); + cFed1->setTimeProperty (helics_property_time_delta, 1.0); + cFed2->setTimeProperty (helics_property_time_delta, 1.0); auto f1finish = std::async (std::launch::async, [&]() { cFed1->enterExecutingMode (); }); cFed2->enterExecutingMode (); @@ -209,8 +209,8 @@ BOOST_DATA_TEST_CASE (combination_federate_multimode_transfer, bdata::make (core // publish string1 at time=0.0; cFed1->publish (pubid, "string1"); - BOOST_CHECK (cFed1->getCurrentState () == helics::Federate::op_states::execution); - BOOST_CHECK (cFed2->getCurrentState () == helics::Federate::op_states::execution); + BOOST_CHECK (cFed1->getCurrentState () == helics::Federate::states::execution); + BOOST_CHECK (cFed2->getCurrentState () == helics::Federate::states::execution); helics::data_block data (500, 'a'); helics::data_block data2 (400, 'b'); @@ -231,7 +231,7 @@ BOOST_DATA_TEST_CASE (combination_federate_multimode_transfer, bdata::make (core // publish a second string cFed1->publish (pubid, "string2"); // make sure the value is still what we expect - s=cFed2->getString(subid); + s = cFed2->getString (subid); BOOST_CHECK_EQUAL (s, "string1"); @@ -260,20 +260,20 @@ BOOST_DATA_TEST_CASE (combination_federate_multimode_transfer, bdata::make (core BOOST_CHECK_EQUAL (f1time.get (), 2.0); // make sure the value was updated - auto &ns=cFed2->getString (subid); + auto &ns = cFed2->getString (subid); BOOST_CHECK_EQUAL (ns, "string2"); cFed1->finalize (); cFed2->finalize (); - BOOST_CHECK (cFed1->getCurrentState () == helics::Federate::op_states::finalize); - BOOST_CHECK (cFed2->getCurrentState () == helics::Federate::op_states::finalize); + BOOST_CHECK (cFed1->getCurrentState () == helics::Federate::states::finalize); + BOOST_CHECK (cFed2->getCurrentState () == helics::Federate::states::finalize); } BOOST_AUTO_TEST_CASE (test_file_load) { - helics::CombinationFederate cFed (std::string (TEST_DIR) + "/test_files/example_combo_fed.json"); + helics::CombinationFederate cFed (std::string (TEST_DIR) + "/example_combo_fed.json"); BOOST_CHECK_EQUAL (cFed.getName (), "comboFed"); @@ -284,12 +284,13 @@ BOOST_AUTO_TEST_CASE (test_file_load) BOOST_CHECK_EQUAL (cFed.getInputCount (), 2); BOOST_CHECK_EQUAL (cFed.getPublicationCount (), 2); + BOOST_CHECK (!cFed.getPublication (1).getInfo ().empty ()); cFed.disconnect (); } BOOST_AUTO_TEST_CASE (test_file_load_toml) { - helics::CombinationFederate cFed (std::string (TEST_DIR) + "/test_files/example_combo_fed.toml"); + helics::CombinationFederate cFed (std::string (TEST_DIR) + "/example_combo_fed.toml"); BOOST_CHECK_EQUAL (cFed.getName (), "comboFed"); diff --git a/tests/helics/core/IpcQueueImplTests.cpp b/tests/helics/core/IpcQueueImplTests.cpp new file mode 100644 index 0000000000..28c08ee819 --- /dev/null +++ b/tests/helics/core/IpcQueueImplTests.cpp @@ -0,0 +1,506 @@ +/* +Copyright © 2017-2018, +Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable Energy, LLC +All rights reserved. See LICENSE file and DISCLAIMER for more details. +*/ + +#include + +#include "helics/core/ipc/IpcBlockingPriorityQueueImpl.hpp" +#include +#include +#include + +namespace utf = boost::unit_test; +using namespace std::literals::chrono_literals; + +BOOST_AUTO_TEST_SUITE (IpcQueueImpl_tests, *utf::label ("ci")) + +BOOST_AUTO_TEST_CASE (creation_test) +{ + std::unique_ptr memblock (new unsigned char[10192]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 10192); + + std::vector data (500, 'a'); + BOOST_CHECK (queue.try_push (data.data (), 500)); + + data.assign (500, 'b'); + int res = queue.pop (data.data (), 500); + BOOST_CHECK_EQUAL (res, 500); + BOOST_CHECK_EQUAL (data[234], 'a'); + BOOST_CHECK_EQUAL (data[499], 'a'); +} + +BOOST_AUTO_TEST_CASE (push_pop_tests) +{ + std::unique_ptr memblock (new unsigned char[10192]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 10192); + + std::vector data (500, 'a'); + BOOST_CHECK (queue.try_push (data.data (), 400)); // this would go into the pull + queue.push (data.data (), 401); // this goes into push + queue.push (data.data (), 402); // this goes into push + queue.push (data.data (), 403); // this goes into push + + int sz = queue.pop (data.data (), 500); // pop from pull + BOOST_CHECK_EQUAL (sz, 400); + sz = queue.pop (data.data (), 500); // pull empty, so rotate + BOOST_CHECK_EQUAL (sz, 401); // pop from pull + queue.push (data.data (), 404); // this goes into push + queue.push (data.data (), 405); // this goes into push + queue.push (data.data (), 406); // this goes into push + + sz = queue.pop (data.data (), 500); // pull empty, so rotate + BOOST_CHECK_EQUAL (sz, 402); // pop from pull + + sz = queue.pop (data.data (), 500); + BOOST_CHECK_EQUAL (sz, 403); + + sz = queue.pop (data.data (), 500); + BOOST_CHECK_EQUAL (sz, 404); + sz = queue.pop (data.data (), 500); + BOOST_CHECK_EQUAL (sz, 405); + sz = queue.pop (data.data (), 500); + BOOST_CHECK_EQUAL (sz, 406); +} + +BOOST_AUTO_TEST_CASE (push_pop_priority_tests) +{ + std::unique_ptr memblock (new unsigned char[10192]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 10192); + + std::vector data (500, 'a'); + BOOST_CHECK (queue.try_push (data.data (), 400)); // this would go into the pull + queue.push (data.data (), 401); // this goes into push + queue.push (data.data (), 402); // this goes into push + queue.push (data.data (), 403); // this goes into push + + int sz = queue.pop (data.data (), 500); // pop from pull + BOOST_CHECK_EQUAL (sz, 400); + queue.pushPriority (data.data (), 417); + sz = queue.pop (data.data (), 500); // pull from priority + BOOST_CHECK_EQUAL (sz, 417); // pop from pull + sz = queue.pop (data.data (), 500); // pull from priority + BOOST_CHECK_EQUAL (sz, 401); // pop from pull + queue.push (data.data (), 404); // this goes into push + queue.pushPriority (data.data (), 420); // this goes into priority + queue.pushPriority (data.data (), 421); // this goes into priority + queue.push (data.data (), 405); // this goes into push + queue.push (data.data (), 406); // this goes into push + + sz = queue.pop (data.data (), 500); // pull empty, so rotate + BOOST_CHECK_EQUAL (sz, 420); // pop from priority + sz = queue.pop (data.data (), 500); // pull empty, so rotate + BOOST_CHECK_EQUAL (sz, 421); // pop from priority + + sz = queue.pop (data.data (), 500); // pull empty, so rotate + BOOST_CHECK_EQUAL (sz, 402); // pop from pull + + sz = queue.pop (data.data (), 500); + BOOST_CHECK_EQUAL (sz, 403); + + sz = queue.pop (data.data (), 500); + BOOST_CHECK_EQUAL (sz, 404); + sz = queue.pop (data.data (), 500); + BOOST_CHECK_EQUAL (sz, 405); + BOOST_CHECK (!queue.empty ()); + sz = queue.try_pop (data.data (), 500); + BOOST_CHECK_EQUAL (sz, 406); + + BOOST_CHECK (queue.empty ()); +} + +BOOST_AUTO_TEST_CASE (push_full) +{ + std::unique_ptr memblock (new unsigned char[4096]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 4096); + std::vector data (500, 'a'); + BOOST_CHECK (queue.try_push (data.data (), 420)); // this would go into the pull + BOOST_CHECK (queue.try_push (data.data (), 420)); // this would go into the push + BOOST_CHECK (queue.try_push (data.data (), 420)); // this would go into the push + BOOST_CHECK (queue.try_push (data.data (), 420)); // this would go into the push + + BOOST_CHECK (!queue.try_push (data.data (), 420)); // this should fail as it is full + BOOST_CHECK_EQUAL (queue.push (std::chrono::milliseconds (50), data.data (), 420), + 0); // this should return 0 as timeout +} + +BOOST_AUTO_TEST_CASE (push_priority_full) +{ + std::unique_ptr memblock (new unsigned char[4096]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 4096); + std::vector data (500, 'a'); + BOOST_CHECK (queue.try_pushPriority (data.data (), 390)); // this would go into the pull + BOOST_CHECK (queue.try_pushPriority (data.data (), 390)); // this would go into the push + + BOOST_CHECK (!queue.try_pushPriority (data.data (), 390)); // this should fail as it is full + BOOST_CHECK_EQUAL (queue.pushPriority (std::chrono::milliseconds (50), data.data (), 420), + 0); // this should return 0 as timeout +} + +BOOST_AUTO_TEST_CASE (pop_wait) +{ + std::unique_ptr memblock (new unsigned char[4096]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 4096); + std::vector data (500, 'a'); + BOOST_CHECK_EQUAL (queue.try_pop (data.data (), 390), 0); // this would go into the pull + + BOOST_CHECK_EQUAL (queue.pop (std::chrono::milliseconds (100), data.data (), 420), + 0); // this should return 0 as timeout +} + +BOOST_AUTO_TEST_CASE (pop_wait2) +{ + std::unique_ptr memblock (new unsigned char[4096]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 4096); + std::vector data (500, 'a'); + + auto def = std::async (std::launch::async, [&]() { + std::this_thread::sleep_for (std::chrono::milliseconds (50)); + queue.push (data.data (), 300); + }); + + int ret = queue.pop (data.data (), 500); + BOOST_CHECK_EQUAL (ret, 300); + def.wait(); +} + +BOOST_AUTO_TEST_CASE(pop_wait_priority) +{ + std::unique_ptr memblock(new unsigned char[4096]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); + std::vector data(500, 'a'); + + auto def = std::async(std::launch::async, [&]() { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + queue.pushPriority(data.data(), 300); + }); + + int ret = queue.pop(data.data(), 500); + BOOST_CHECK_EQUAL(ret, 300); + def.wait(); +} + + +BOOST_AUTO_TEST_CASE(push_wait) +{ + std::unique_ptr memblock(new unsigned char[4096]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); + std::vector data(500, 'a'); + std::vector data2(500, 'a'); + BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the pull + BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the push + BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the push + BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the push + + BOOST_CHECK(!queue.try_push(data.data(), 420)); // this should fail as it is full + auto def = std::async(std::launch::async, [&]() { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + queue.pop(data.data(), 500); + }); + queue.push(data.data(), 420); + def.wait(); +} + +BOOST_AUTO_TEST_CASE(priority_push_wait) +{ + std::unique_ptr memblock(new unsigned char[4096]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); + std::vector data(500, 'a'); + std::vector data2(500, 'a'); + + BOOST_CHECK(queue.try_pushPriority(data.data(), 390)); // this would go into the pull + BOOST_CHECK(queue.try_pushPriority(data.data(), 390)); // this would go into the push + + BOOST_CHECK(!queue.try_pushPriority(data.data(), 390)); // this should fail as it is full + + auto def = std::async(std::launch::async, [&]() { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + queue.pop(data.data(), 500); + }); + queue.pushPriority(data.data(), 390); + def.wait(); +} +/** test with single consumer/single producer*/ + +BOOST_AUTO_TEST_CASE(multithreaded_tests) +{ + std::unique_ptr memblock(new unsigned char[1048576]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 1048576); + + + for (int64_t ii = 0; ii < 10'000; ++ii) + { + queue.push(reinterpret_cast(&ii),8); + } + + auto prod1 = [&]() { + int64_t bdata; + for (int64_t jj = 10'000; jj < 1'010'000; ++jj) + { + bdata = jj; + queue.push(reinterpret_cast(&bdata), 8); + } + }; + + auto cons = [&]() { + int64_t data; + auto res = queue.try_pop(reinterpret_cast(&data),8); + int64_t cnt = 0; + while ((res)) + { + ++cnt; + res = queue.try_pop(reinterpret_cast(&data), 8); + if (res==0) + { // make an additional sleep period so the producer can catch up + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + res = queue.try_pop(reinterpret_cast(&data), 8); + } + } + return cnt; + }; + + auto ret = std::async(std::launch::async, prod1); + + auto res = std::async(std::launch::async, cons); + + ret.wait(); + auto V = res.get(); + BOOST_CHECK_EQUAL(V, 1'010'000); +} + +/** test with multiple consumer/single producer*/ +BOOST_AUTO_TEST_CASE(multithreaded_tests2) +{ + std::unique_ptr memblock(new unsigned char[1048576]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 1048576); + for (int64_t ii = 0; ii < 10'000; ++ii) + { + queue.push(reinterpret_cast(&ii), 8); + } + auto prod1 = [&]() { + int64_t bdata; + for (int64_t jj = 10'000; jj < 1'010'000; ++jj) + { + bdata = jj; + queue.push(reinterpret_cast(&bdata), 8); + } + }; + + auto cons = [&]() { + int64_t data; + auto res = queue.try_pop(reinterpret_cast(&data), 8); + int64_t cnt = 0; + while ((res)) + { + ++cnt; + res = queue.try_pop(reinterpret_cast(&data), 8); + if (res == 0) + { // make an additional sleep period so the producer can catch up + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + res = queue.try_pop(reinterpret_cast(&data), 8); + } + } + return cnt; + }; + + auto ret = std::async(std::launch::async, prod1); + + auto res1 = std::async(std::launch::async, cons); + auto res2 = std::async(std::launch::async, cons); + auto res3 = std::async(std::launch::async, cons); + ret.wait(); + auto V1 = res1.get(); + auto V2 = res2.get(); + auto V3 = res3.get(); + + BOOST_CHECK_EQUAL(V1 + V2 + V3, 1'010'000); +} + +/** test with multiple producer/multiple consumer*/ +BOOST_AUTO_TEST_CASE(multithreaded_tests3) +{ + std::unique_ptr memblock(new unsigned char[1048576]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 1048576); + for (int64_t ii = 0; ii < 10'000; ++ii) + { + queue.push(reinterpret_cast(&ii), 8); + } + auto prod1 = [&]() { + int64_t bdata; + for (int64_t jj = 10'000; jj < 1'010'000; ++jj) + { + bdata = jj; + queue.push(reinterpret_cast(&bdata), 8); + } + }; + + auto cons = [&]() { + int64_t data; + auto res = queue.try_pop(reinterpret_cast(&data), 8); + int64_t cnt = 0; + while ((res)) + { + ++cnt; + res = queue.try_pop(reinterpret_cast(&data), 8); + if (res == 0) + { // make an additional sleep period so the producer can catch up + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + res = queue.try_pop(reinterpret_cast(&data), 8); + } + } + return cnt; + }; + + + auto ret1 = std::async(std::launch::async, prod1); + auto ret2 = std::async(std::launch::async, prod1); + auto ret3 = std::async(std::launch::async, prod1); + + auto res1 = std::async(std::launch::async, cons); + auto res2 = std::async(std::launch::async, cons); + auto res3 = std::async(std::launch::async, cons); + ret1.wait(); + ret2.wait(); + ret3.wait(); + auto V1 = res1.get(); + auto V2 = res2.get(); + auto V3 = res3.get(); + + BOOST_CHECK_EQUAL(V1 + V2 + V3, 3'010'000); +} + +/** test with multiple producer/multiple consumer*/ +BOOST_AUTO_TEST_CASE(multithreaded_tests3_pop) +{ + std::unique_ptr memblock(new unsigned char[1048576]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 1048576); + + auto prod1 = [&]() { + int64_t bdata; + for (int64_t jj = 0; jj < 1'000'000; ++jj) + { + bdata = jj; + queue.push(reinterpret_cast(&bdata), 8); + } + bdata = (-1); + queue.push(reinterpret_cast(&bdata), 8); + }; + + auto cons = [&]() { + int64_t data=0; + int64_t cnt = 0; + while (data>=0) + { + ++cnt; + queue.pop(reinterpret_cast(&data), 8); + } + return cnt; + }; + + + auto ret1 = std::async(std::launch::async, prod1); + auto ret2 = std::async(std::launch::async, prod1); + auto ret3 = std::async(std::launch::async, prod1); + + auto res1 = std::async(std::launch::async, cons); + auto res2 = std::async(std::launch::async, cons); + auto res3 = std::async(std::launch::async, cons); + ret1.wait(); + ret2.wait(); + ret3.wait(); + auto V1 = res1.get(); + auto V2 = res2.get(); + auto V3 = res3.get(); + + BOOST_CHECK_EQUAL(V1 + V2 + V3, 3'000'003); +} + + +BOOST_AUTO_TEST_CASE(push_full_mem) +{ + std::unique_ptr memblock(new unsigned char[4096]); + + std::unique_ptr memblock2(new unsigned char[4096]); + + auto *queue =new(memblock2.get()) helics::ipc::detail::IpcBlockingPriorityQueueImpl(memblock.get(), 4096); + std::vector data(500, 'a'); + BOOST_CHECK(queue->try_push(data.data(), 420)); // this would go into the pull + BOOST_CHECK(queue->try_push(data.data(), 420)); // this would go into the push + BOOST_CHECK(queue->try_push(data.data(), 420)); // this would go into the push + BOOST_CHECK(queue->try_push(data.data(), 420)); // this would go into the push + + BOOST_CHECK(!queue->try_push(data.data(), 420)); // this should fail as it is full + BOOST_CHECK_EQUAL(queue->push(std::chrono::milliseconds(50), data.data(), 420), + 0); // this should return 0 as timeout +} + + + +/** test with multiple producer/multiple consumer*/ +BOOST_AUTO_TEST_CASE(multithreaded_tests3_pop_mem) +{ + std::unique_ptr memblock(new unsigned char[1048576]); + + std::unique_ptr memblock2(new unsigned char[4096]); + + auto *qn = new(memblock2.get()) helics::ipc::detail::IpcBlockingPriorityQueueImpl(memblock.get(), 1048576); + + auto prod1 = [&](void *data) { + auto *queue = reinterpret_cast(data); + int64_t bdata; + for (int64_t jj = 0; jj < 1'000'000; ++jj) + { + bdata = jj; + queue->push(reinterpret_cast(&bdata), 8); + } + bdata = (-1); + queue->push(reinterpret_cast(&bdata), 8); + }; + + auto cons = [&](void *mem) { + auto *queue = reinterpret_cast(mem); + int64_t data = 0; + int64_t cnt = 0; + while (data >= 0) + { + ++cnt; + queue->pop(reinterpret_cast(&data), 8); + } + return cnt; + }; + + + auto ret1 = std::async(std::launch::async, prod1, memblock2.get()); + auto ret2 = std::async(std::launch::async, prod1, memblock2.get()); + auto ret3 = std::async(std::launch::async, prod1, memblock2.get()); + + auto res1 = std::async(std::launch::async, cons, memblock2.get()); + auto res2 = std::async(std::launch::async, cons, memblock2.get()); + auto res3 = std::async(std::launch::async, cons, memblock2.get()); + ret1.wait(); + ret2.wait(); + ret3.wait(); + auto V1 = res1.get(); + auto V2 = res2.get(); + auto V3 = res3.get(); + + BOOST_CHECK_EQUAL(V1 + V2 + V3, 3'000'003); + +} + +BOOST_AUTO_TEST_SUITE_END () \ No newline at end of file From fcb1fc15f606b4b325e0460adc6ef030a40d00f3 Mon Sep 17 00:00:00 2001 From: Philip Top Date: Sat, 31 Aug 2019 14:58:29 -0700 Subject: [PATCH 5/8] finish fixing merge # Conflicts: # ThirdParty/fmtlib # tests/helics/common/CMakeLists.txt --- src/helics/common/CircularBuffer.hpp | 74 ---------------------------- tests/helics/common/CMakeLists.txt | 47 ++++++++++++++---- 2 files changed, 37 insertions(+), 84 deletions(-) delete mode 100644 src/helics/common/CircularBuffer.hpp diff --git a/src/helics/common/CircularBuffer.hpp b/src/helics/common/CircularBuffer.hpp deleted file mode 100644 index cd7f6e9fc6..0000000000 --- a/src/helics/common/CircularBuffer.hpp +++ /dev/null @@ -1,74 +0,0 @@ -/* -Copyright © 2017-2018, -Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable Energy, LLC -All rights reserved. See LICENSE file and DISCLAIMER for more details. -*/ -#pragma once - -#include -#include - -namespace helics -{ -namespace common -{ - -class CircularBufferRaw -{ - public: - CircularBufferRaw (unsigned char *dataBlock, int capacity); - - int capacity () const { return capacity_; } - bool isSpaceAvailable (int sz) const; - // Return true if push was successful - bool push (const unsigned char *data, int blockSize); - int nextDataSize () const; - // Return number of bytes read. - int pop (unsigned char *data, int maxSize); - /** check if the block is Empty or not*/ - bool empty () const; - /** clear the buffer*/ - void clear (); - - private: - unsigned char *origin; - unsigned char *next_write; - unsigned char *next_read; - int capacity_ = 0; - - private: - friend class CircularBuffer; -}; - -class CircularBuffer -{ - public: - CircularBuffer () noexcept; - explicit CircularBuffer (int size); - ~CircularBuffer () = default; - CircularBuffer (CircularBuffer &&cb) noexcept; - CircularBuffer (const CircularBuffer &cb); - - CircularBuffer &operator= (CircularBuffer &&cb) noexcept; - CircularBuffer &operator= (const CircularBuffer &cb); - - void resize (int newsize); - int capacity () const { return buffer.capacity (); } - bool isSpaceAvailable (int sz) const { return buffer.isSpaceAvailable (sz); } - bool empty () const { return buffer.empty (); } - - bool push (const unsigned char *block, int blockSize) { return buffer.push (block, blockSize); } - - int next_data_size () const { return buffer.nextDataSize (); } - - int pop (unsigned char *block, int maxSize) { return buffer.pop (block, maxSize); } - - void clear () { buffer.clear (); } - - private: - std::vector data; - CircularBufferRaw buffer; -}; - -} // namespace common -} // namespace helics diff --git a/tests/helics/common/CMakeLists.txt b/tests/helics/common/CMakeLists.txt index 3454aecc39..13fd0a8e1a 100644 --- a/tests/helics/common/CMakeLists.txt +++ b/tests/helics/common/CMakeLists.txt @@ -1,11 +1,9 @@ -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# Copyright (c) 2017-2022, Battelle Memorial Institute; Lawrence Livermore -# National Security, LLC; Alliance for Sustainable Energy, LLC. -# See the top-level NOTICE for additional details. -# All rights reserved. # +# Copyright © 2017-2019, Battelle Memorial Institute; Lawrence Livermore National +# Security, LLC; Alliance for Sustainable Energy, LLC. See the top-level NOTICE for additional details. All rights reserved. +# # SPDX-License-Identifier: BSD-3-Clause -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# # ----------------------------------------------------------------------------- # Common library tests using Boost @@ -13,11 +11,40 @@ set(common_test_headers) -set(common_test_sources TimeTests.cpp JsonGenerationTests.cpp SmallBufferTests.cpp) +set( + common_test_sources + common-tests.cpp + TimeTests.cpp +) add_executable(common-tests ${common_test_sources} ${common_test_headers}) -target_link_libraries(common-tests PRIVATE HELICS::core helics_test_base fmt::fmt) +target_link_libraries(common-tests PRIVATE helics-static helics_google_test_base) set_target_properties(common-tests PROPERTIES FOLDER tests) -add_test(NAME common-tests COMMAND common-tests) -set_property(TEST common-tests PROPERTY LABELS Common Daily Coverage Continuous) +add_test( + NAME common-tests + COMMAND common-tests --log_level=message --report_level=short +) +set_property( + TEST common-tests + PROPERTY + LABELS + Common + Daily + Coverage + Continuous +) + +foreach(keyfile IN LISTS KEY_LIBRARY_FILES) + add_custom_command( + TARGET + common-tests + POST_BUILD # Adds a post-build event to core tests + COMMAND + ${CMAKE_COMMAND} + -E + copy_if_different # which executes "cmake - E copy_if_different..." + "${keyfile}" # <--this is in-file + "$/" + ) # <--this is out-file path +endforeach(keyfile) From c57565558a87d8fc2b6b0b94f7b4050d9662558f Mon Sep 17 00:00:00 2001 From: Philip Top Date: Tue, 3 May 2022 15:02:41 -0700 Subject: [PATCH 6/8] move some files around to be in a more appropriate spot --- src/helics/common/CircularBuffer.cpp | 244 --------- src/helics/common/StackQueue.cpp | 200 ------- src/helics/common/StackQueue.hpp | 94 ---- .../ipc}/ipc/IpcBlockingPriorityQueueImpl.cpp | 0 .../ipc}/ipc/IpcBlockingPriorityQueueImpl.hpp | 0 tests/helics/common/StackQueueTests.cpp | 516 ------------------ tests/helics/core/CMakeLists.txt | 102 ++-- .../{core => network}/IpcQueueImpTests.cpp | 0 .../{core => network}/IpcQueueImplTests.cpp | 0 .../{core => network}/IpcQueueTests.cpp | 0 10 files changed, 36 insertions(+), 1120 deletions(-) delete mode 100644 src/helics/common/CircularBuffer.cpp delete mode 100644 src/helics/common/StackQueue.cpp delete mode 100644 src/helics/common/StackQueue.hpp rename src/helics/{core => network/ipc}/ipc/IpcBlockingPriorityQueueImpl.cpp (100%) rename src/helics/{core => network/ipc}/ipc/IpcBlockingPriorityQueueImpl.hpp (100%) delete mode 100644 tests/helics/common/StackQueueTests.cpp rename tests/helics/{core => network}/IpcQueueImpTests.cpp (100%) rename tests/helics/{core => network}/IpcQueueImplTests.cpp (100%) rename tests/helics/{core => network}/IpcQueueTests.cpp (100%) diff --git a/src/helics/common/CircularBuffer.cpp b/src/helics/common/CircularBuffer.cpp deleted file mode 100644 index ada93f9576..0000000000 --- a/src/helics/common/CircularBuffer.cpp +++ /dev/null @@ -1,244 +0,0 @@ -/* -Copyright © 2017-2018, -Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable Energy, LLC -All rights reserved. See LICENSE file and DISCLAIMER for more details. -*/ - -#include "CircularBuffer.hpp" -#include -#include - -namespace helics -{ -namespace common -{ - -CircularBufferRaw::CircularBufferRaw (unsigned char *dataBlock, int blockSize) - : origin (dataBlock), next_write (origin), next_read (origin), capacity_ (blockSize) -{ -} - -bool CircularBufferRaw::isSpaceAvailable (int sz) const -{ - if (next_write >= next_read) - { - if ((capacity_ - (next_write - origin)) >= sz + 4) - { - return true; - } - else if ((next_read - origin) >= sz + 4) - { - return true; - } - return false; - } - else if ((next_read - next_write) >= sz + 4) - { - return true; - } - return false; -} - -// Return number of bytes written. -bool CircularBufferRaw::push (const unsigned char *data, int blockSize) -{ - if (blockSize <= 0) - { - return false; - } - if (next_write >= next_read) - { - if ((capacity_ - (next_write - origin)) >= blockSize + 4) - { - *(reinterpret_cast (next_write)) = blockSize; - memcpy (next_write + 4, data, blockSize); - next_write += blockSize + 4; - // loop around if there isn't really space for another block of at least 4 bytes and the - // next_read>origin - if (((capacity_ - (next_write - origin)) < 8) && (next_read > origin)) - { - next_write = origin; - } - return true; - } - else if ((next_read - origin) >= blockSize + 4) - { - *(reinterpret_cast (next_write)) = -1; - *(reinterpret_cast (origin)) = blockSize; - memcpy (origin + 4, data, blockSize); - next_write = origin + blockSize + 4; - return true; - } - } - else if ((next_read - next_write) >= blockSize + 4) - { - *(reinterpret_cast (next_write)) = blockSize; - memcpy (next_write + 4, data, blockSize); - next_write += blockSize + 4; - return true; - } - return false; -} - - -int CircularBufferRaw::nextDataSize() const -{ - if (next_write == next_read) - { - return 0; - } - int size = *(reinterpret_cast (next_read)); - if (size < 0) - { - size = *(reinterpret_cast (origin)); - } - return size; -} - -// Return number of bytes read. -int CircularBufferRaw::pop (unsigned char *data, int maxLen) -{ - if (next_write == next_read) - { - return 0; - } - int size = *(reinterpret_cast (next_read)); - if (size < 0) - { - next_read = origin; - size = *(reinterpret_cast (next_read)); - } - if (size <= maxLen) - { - memcpy (data, next_read + 4, size); - next_read += size + 4; - if ((capacity_ - (next_read - origin)) < 8) - { - next_read = origin; - } - return size; - } - return 0; -} -/** check if the block is Empty or not*/ -bool CircularBufferRaw::empty () const { return (next_write == next_read); } -void CircularBufferRaw::clear () { next_write = next_read = origin; } - - -CircularBuffer::CircularBuffer () noexcept : buffer (nullptr, 0){} -CircularBuffer::CircularBuffer (int size) : data (size), buffer (data.data (), size) {} - -CircularBuffer::CircularBuffer (CircularBuffer &&cb) noexcept - : data (std::move (cb.data)), buffer (std::move (cb.buffer)) -{ - cb.buffer.capacity_ = 0; - cb.buffer.origin = nullptr; - cb.buffer.next_read = nullptr; - cb.buffer.next_write = nullptr; -} - -CircularBuffer::CircularBuffer (const CircularBuffer &cb) : data (cb.data), buffer(cb.buffer) -{ - auto read_offset = buffer.next_read - buffer.origin; - auto write_offset = buffer.next_write - buffer.origin; - buffer.origin = data.data (); - buffer.next_read=buffer.origin+read_offset; - buffer.next_write = buffer.origin+write_offset; -} - -CircularBuffer &CircularBuffer::operator= (CircularBuffer &&cb) noexcept -{ - buffer = std::move (cb.buffer); - data = std::move (cb.data); - - cb.buffer.capacity_ = 0; - cb.buffer.origin = nullptr; - cb.buffer.next_read = nullptr; - cb.buffer.next_write = nullptr; - return *this; -} - -CircularBuffer &CircularBuffer::operator= (const CircularBuffer &cb) -{ - buffer = cb.buffer; - data = cb.data; - auto read_offset = buffer.next_read - buffer.origin; - auto write_offset = buffer.next_write - buffer.origin; - buffer.origin = data.data (); - buffer.next_read = buffer.origin + read_offset; - buffer.next_write = buffer.origin + write_offset; - return *this; -} - -void CircularBuffer::resize (int newsize) -{ - if (newsize == buffer.capacity_) - { - return; - } - if (buffer.empty()) - { - data.resize (newsize); - buffer = CircularBufferRaw (data.data (), newsize); - } - else if (newsize > static_cast(data.size ())) - { - data.resize (newsize); - int read_offset = static_cast (buffer.next_read - buffer.origin); - int write_offset = static_cast (buffer.next_write - buffer.origin); - if (buffer.next_read < buffer.next_write) - { - - buffer.capacity_ = newsize; - buffer.origin = data.data (); - buffer.next_read = buffer.origin + read_offset; - buffer.next_write = buffer.origin + write_offset; - } - else - { - int readDiff = buffer.capacity_ - read_offset; - memmove (data.data () + newsize - readDiff, data.data () + read_offset, - buffer.capacity_ - read_offset); - buffer.origin = data.data (); - buffer.next_write = buffer.origin + write_offset; - buffer.next_read = buffer.origin + newsize - readDiff; - buffer.capacity_ = newsize; - } - - } - else // smaller size - { - int read_offset = static_cast(buffer.next_read - buffer.origin); - if (buffer.next_read < buffer.next_write) - { - if (read_offset <= newsize) - { - data.resize (newsize); - buffer.capacity_ = newsize; - } - } - else - { - int write_offset = static_cast (buffer.next_write - buffer.origin); - int readDiff = buffer.capacity_ - read_offset; - if (readDiff + write_offset < newsize) - { - memmove (data.data () + newsize - readDiff, data.data () + read_offset, - buffer.capacity_ - read_offset); - buffer.origin = data.data (); - buffer.next_write = buffer.origin + write_offset; - buffer.next_read = buffer.origin + newsize - readDiff; - buffer.capacity_ = newsize; - } - else - { - throw (std::runtime_error ( - "unable to resize, current data exceeds new size, please empty buffer before resizing")); - } - } - - } -} - -} // namespace common -} // namespace helics diff --git a/src/helics/common/StackQueue.cpp b/src/helics/common/StackQueue.cpp deleted file mode 100644 index c8dcac4ae0..0000000000 --- a/src/helics/common/StackQueue.cpp +++ /dev/null @@ -1,200 +0,0 @@ -/* -Copyright © 2017-2018, -Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable Energy, LLC -All rights reserved. See LICENSE file and DISCLAIMER for more details. -*/ -#pragma once - -#include "StackQueue.hpp" -#include -#include - -namespace helics -{ -namespace common -{ - -StackQueueRaw::StackQueueRaw (unsigned char *newBlock, int blockSize) - : origin (newBlock), next (newBlock), dataSize (blockSize) -{ - nextIndex = reinterpret_cast (origin + dataSize - sizeof (dataIndex)); -} - -void StackQueueRaw::swap (StackQueueRaw &other) noexcept -{ - std::swap (origin, other.origin); - std::swap (next, other.next); - std::swap (dataSize, other.dataSize); - std::swap (nextIndex, other.nextIndex); - std::swap (dataCount, other.dataCount); -} - -bool StackQueueRaw::isSpaceAvailable (int sz) const -{ - return (dataSize - (next - origin) - (dataCount + 1) * static_cast(sizeof (dataIndex))) >= sz; -} - -bool StackQueueRaw::push (const unsigned char *block, int blockSize) -{ - if (blockSize <= 0) - { - return false; - } - if (!isSpaceAvailable (blockSize)) - { - return false; - } - memcpy (next, block, blockSize); - nextIndex->offset = static_cast (next - origin); - nextIndex->dataSize = blockSize; - next += blockSize; - --nextIndex; - ++dataCount; - return true; -} - -int StackQueueRaw::nextDataSize () const -{ - if (dataCount > 0) - { - return nextIndex[1].dataSize; - } - return 0; -} - -int StackQueueRaw::pop (unsigned char *block, int maxSize) -{ - if (dataCount > 0) - { - int blkSize = nextIndex[1].dataSize; - if (maxSize >= blkSize) - { - memcpy (block, origin + nextIndex[1].offset, blkSize); - if (nextIndex[1].offset + blkSize == static_cast (next - origin)) - { - next -= blkSize; - } - ++nextIndex; - --dataCount; - if (dataCount == 0) - { - next = origin; - nextIndex = reinterpret_cast (origin + dataSize - sizeof (dataIndex)); - } - return blkSize; - } - } - return 0; -} - -/** reverse the order in which the data will be extracted*/ -void StackQueueRaw::reverse () -{ - if (dataCount <= 1) - { - return; - } - std::reverse (nextIndex + 1, nextIndex + dataCount + 1); -} - -void StackQueueRaw::clear () -{ - next = origin; - dataCount = 0; - nextIndex = reinterpret_cast (origin + dataSize - sizeof (dataIndex)); -} - - - StackQueue::StackQueue () noexcept : stack (nullptr, 0){}; - StackQueue::StackQueue(int size) : data(size), stack(data.data(), size) - { - - } - - StackQueue::StackQueue (StackQueue &&sq) noexcept : data(std::move(sq.data)),stack (std::move(sq.stack)) - { - sq.stack.dataSize = 0; - sq.stack.origin = nullptr; - sq.stack.next = nullptr; - sq.stack.dataCount = 0; - sq.stack.nextIndex = nullptr; - } - - StackQueue::StackQueue (const StackQueue &sq) : data (sq.data), stack (sq.stack) - { - auto offset = stack.next - stack.origin; - stack.origin = data.data (); - stack.next = stack.origin + offset; - stack.nextIndex = reinterpret_cast (stack.origin + stack.dataSize - sizeof (dataIndex)); - stack.nextIndex -= stack.dataCount; - } - - StackQueue &StackQueue::operator= (StackQueue &&sq) noexcept { - stack = std::move (sq.stack); - data = std::move (sq.data); - - sq.stack.dataSize = 0; - sq.stack.origin = nullptr; - sq.stack.next = nullptr; - sq.stack.dataCount = 0; - sq.stack.nextIndex = nullptr; - return *this; - } - - StackQueue &StackQueue::operator= (const StackQueue &sq) - { - stack = sq.stack; - data = sq.data; - auto offset = stack.next - stack.origin; - stack.origin = data.data (); - stack.next = stack.origin + offset; - stack.nextIndex = reinterpret_cast (stack.origin + stack.dataSize - sizeof (dataIndex)); - stack.nextIndex -= stack.dataCount; - return *this; - } - - void StackQueue::resize(int newsize) - { - if (newsize == stack.dataSize) - { - return; - } - if (stack.dataCount == 0) - { - data.resize (newsize); - stack = StackQueueRaw (data.data (), newsize); - } - else if (newsize>data.size()) - { - data.resize (newsize); - int indexOffset = stack.dataSize - sizeof (dataIndex) * stack.dataCount; - int newOffset = newsize - sizeof (dataIndex) * stack.dataCount; - memmove(data.data () + newOffset, data.data () + indexOffset, sizeof (dataIndex) * stack.dataCount); - stack.dataSize = newsize; - stack.origin = data.data (); - stack.next = stack.origin + newsize; - stack.nextIndex = reinterpret_cast (stack.origin + stack.dataSize - sizeof (dataIndex)); - stack.nextIndex -= stack.dataCount; - } - else //smaller size - { - int indexOffset = stack.dataSize - sizeof (dataIndex) * stack.dataCount; - int newOffset = newsize - sizeof (dataIndex) * stack.dataCount; - int dataOffset = static_cast(stack.next - stack.origin); - if (newsize < dataOffset + sizeof(dataIndex) * stack.dataCount) - { - throw (std::runtime_error ( - "unable to resize, current data exceeds new size, please empty stack before resizing")); - } - memmove (data.data () + newOffset, data.data () + indexOffset, sizeof (dataIndex) * stack.dataCount); - stack.dataSize = newsize; - stack.origin = data.data (); - stack.next = stack.origin + newsize; - stack.nextIndex = reinterpret_cast (stack.origin + stack.dataSize - sizeof (dataIndex)); - stack.nextIndex -= stack.dataCount; - data.resize (newsize); - } - } - -} // namespace common -} // namespace helics diff --git a/src/helics/common/StackQueue.hpp b/src/helics/common/StackQueue.hpp deleted file mode 100644 index 898dcada88..0000000000 --- a/src/helics/common/StackQueue.hpp +++ /dev/null @@ -1,94 +0,0 @@ -/* -Copyright © 2017-2018, -Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable Energy, LLC -All rights reserved. See LICENSE file and DISCLAIMER for more details. -*/ -#pragma once - -#include -#include -#include - -namespace helics -{ -namespace common -{ -struct dataIndex -{ - int32_t offset; - int32_t dataSize; -}; -/** class containing the raw stackQueue implementation -@details the stackQueueRaw class operates on raw memory -it is given a memory location and uses that for the life of the queue, it does not own the memory so care must be -taken for memory management It operates on blocks of raw data -*/ -class StackQueueRaw -{ - private: - unsigned char *origin = nullptr; - unsigned char *next = nullptr; - dataIndex *nextIndex = nullptr; - int dataSize = 0; - int dataCount = 0; - - public: - StackQueueRaw (unsigned char *newBlock, int blockSize); - - void swap (StackQueueRaw &other) noexcept; - - int capacity () const { return dataSize; }; - int getCurrentCount () const { return dataCount; } - bool isSpaceAvailable (int sz) const; - bool empty () const { return (dataCount == 0); } - - bool push (const unsigned char *block, int blockSize); - - int nextDataSize () const; - - int pop (unsigned char *block, int maxSize); - - /** reverse the order in which the data will be extracted*/ - void reverse (); - /** clear all data from the StackQueueRaw*/ - void clear (); - - private: - friend class StackQueue; -}; - -/** StackQueue manages memory for a StackQueueRaw and adds some convenience functions */ -class StackQueue -{ - public: - StackQueue () noexcept; - explicit StackQueue (int size); - ~StackQueue () = default; - StackQueue (StackQueue &&sq) noexcept; - StackQueue (const StackQueue &sq); - - StackQueue &operator= (StackQueue &&sq) noexcept; - StackQueue &operator= (const StackQueue &sq); - - void resize (int newsize); - int getCurrentCount () const { return stack.getCurrentCount (); } - int capacity () const { return stack.capacity (); } - bool isSpaceAvailable (int sz) const { return stack.isSpaceAvailable (sz); } - bool empty () const { return stack.empty (); } - - bool push (const unsigned char *block, int blockSize) { return stack.push (block, blockSize); } - - int nextDataSize () const { return stack.nextDataSize (); } - - int pop (unsigned char *block, int maxSize) { return stack.pop (block, maxSize); } - - void reverse () { stack.reverse (); } - void clear () { stack.clear (); } - - private: - std::vector data; - StackQueueRaw stack; -}; - -} // namespace common -} // namespace helics diff --git a/src/helics/core/ipc/IpcBlockingPriorityQueueImpl.cpp b/src/helics/network/ipc/ipc/IpcBlockingPriorityQueueImpl.cpp similarity index 100% rename from src/helics/core/ipc/IpcBlockingPriorityQueueImpl.cpp rename to src/helics/network/ipc/ipc/IpcBlockingPriorityQueueImpl.cpp diff --git a/src/helics/core/ipc/IpcBlockingPriorityQueueImpl.hpp b/src/helics/network/ipc/ipc/IpcBlockingPriorityQueueImpl.hpp similarity index 100% rename from src/helics/core/ipc/IpcBlockingPriorityQueueImpl.hpp rename to src/helics/network/ipc/ipc/IpcBlockingPriorityQueueImpl.hpp diff --git a/tests/helics/common/StackQueueTests.cpp b/tests/helics/common/StackQueueTests.cpp deleted file mode 100644 index d0d87c4815..0000000000 --- a/tests/helics/common/StackQueueTests.cpp +++ /dev/null @@ -1,516 +0,0 @@ -/* -Copyright © 2017-2018, -Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable Energy, LLC -All rights reserved. See LICENSE file and DISCLAIMER for more details. -*/ - -#include - -#include "helics/common/StackQueue.hpp" - - -namespace utf = boost::unit_test; -using namespace std::literals::chrono_literals; -using namespace helics::common; - -BOOST_AUTO_TEST_SUITE (StackQueue_tests, *utf::label ("ci")) - - -BOOST_AUTO_TEST_CASE (test_stackqueueraw_simple) -{ - unsigned char *block = new unsigned char[4096]; - StackQueueRaw stack (block, 4096); - - std::vector testData (1024, 'a'); - int res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 0); - - bool pushed = stack.push (testData.data (), 571); - BOOST_CHECK (pushed); - testData.assign (1024, '\0'); - - res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 571); - BOOST_CHECK_EQUAL (testData[0], 'a'); - BOOST_CHECK_EQUAL (testData[236], 'a'); - BOOST_CHECK_EQUAL (testData[570], 'a'); - BOOST_CHECK_EQUAL (testData[1023], 0); - delete[] block; -} - -BOOST_AUTO_TEST_CASE (test_stackqueueraw_3_push) -{ - unsigned char *block = new unsigned char[4096]; - StackQueueRaw stack (block, 4096); - - std::vector testData (1024, 'a'); - int res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 0); - - bool pushed = stack.push (testData.data (), 571); - BOOST_CHECK (pushed); - testData.assign (1024, 'b'); - pushed = stack.push (testData.data (), 249); - BOOST_CHECK (pushed); - testData.assign (1024, 'c'); - pushed = stack.push (testData.data (), 393); - BOOST_CHECK (pushed); - res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 393); - BOOST_CHECK_EQUAL (testData[0], 'c'); - BOOST_CHECK_EQUAL (testData[236], 'c'); - - res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 249); - BOOST_CHECK_EQUAL (testData[0], 'b'); - BOOST_CHECK_EQUAL (testData[236], 'b'); - BOOST_CHECK (!stack.empty ()); - - res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 571); - BOOST_CHECK_EQUAL (testData[0], 'a'); - BOOST_CHECK_EQUAL (testData[236], 'a'); - BOOST_CHECK_EQUAL (testData[570], 'a'); - - BOOST_CHECK (stack.empty ()); - delete[] block; -} - -BOOST_AUTO_TEST_CASE (test_stackqueueraw_push_full) -{ - unsigned char *block = new unsigned char[1024]; - StackQueueRaw stack (block, 1024); - - std::vector testData (1024, 'a'); - int res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 0); - - bool pushed = stack.push (testData.data (), 571); - BOOST_CHECK (pushed); - testData.assign (1024, 'b'); - pushed = stack.push (testData.data (), 249); - BOOST_CHECK (pushed); - testData.assign (1024, 'c'); - pushed = stack.push (testData.data (), 393); - BOOST_CHECK (!pushed); - - BOOST_CHECK (!stack.isSpaceAvailable (393)); - BOOST_CHECK (!stack.isSpaceAvailable (200)); - BOOST_CHECK (stack.isSpaceAvailable (180)); - BOOST_CHECK_EQUAL (stack.getCurrentCount (), 2); - - pushed = stack.push (testData.data (), 180); - BOOST_CHECK (pushed); - testData.assign (1024, 'd'); - BOOST_CHECK_EQUAL (stack.getCurrentCount (), 3); - - res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 180); - BOOST_CHECK_EQUAL (testData[0], 'c'); - BOOST_CHECK_EQUAL (testData[179], 'c'); - BOOST_CHECK_EQUAL (testData[180], 'd'); //this is one past the copy - - res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 249); - BOOST_CHECK_EQUAL (testData[0], 'b'); - BOOST_CHECK_EQUAL (testData[236], 'b'); - BOOST_CHECK (!stack.empty ()); - - res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 571); - BOOST_CHECK_EQUAL (testData[0], 'a'); - BOOST_CHECK_EQUAL (testData[236], 'a'); - BOOST_CHECK_EQUAL (testData[570], 'a'); - - res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 0); - BOOST_CHECK_EQUAL (testData[0], 'a'); - BOOST_CHECK_EQUAL (testData[236], 'a'); - BOOST_CHECK_EQUAL (testData[570], 'a'); - - BOOST_CHECK (stack.empty ()); - delete[] block; -} - - -BOOST_AUTO_TEST_CASE (test_stackqueueraw_reverse) -{ - unsigned char *block = new unsigned char[4096]; - StackQueueRaw stack (block, 4096); - - std::vector testData (1024, 'a'); - int res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 0); - - bool pushed = stack.push (testData.data (), 571); - BOOST_CHECK (pushed); - testData.assign (1024, 'b'); - pushed = stack.push (testData.data (), 249); - BOOST_CHECK (pushed); - testData.assign (1024, 'c'); - pushed = stack.push (testData.data (), 393); - BOOST_CHECK (pushed); - - stack.reverse (); - - res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 571); - BOOST_CHECK_EQUAL (testData[0], 'a'); - BOOST_CHECK_EQUAL (testData[236], 'a'); - BOOST_CHECK_EQUAL (testData[570], 'a'); - - res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 249); - BOOST_CHECK_EQUAL (testData[0], 'b'); - BOOST_CHECK_EQUAL (testData[236], 'b'); - BOOST_CHECK (!stack.empty ()); - - res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 393); - BOOST_CHECK_EQUAL (testData[0], 'c'); - BOOST_CHECK_EQUAL (testData[236], 'c'); - - - BOOST_CHECK (stack.empty ()); - delete[] block; -} - -BOOST_AUTO_TEST_CASE (test_stackqueue_simple) -{ - StackQueue stack (4096); - - std::vector testData (1024, 'a'); - int res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 0); - - bool pushed = stack.push (testData.data (), 571); - BOOST_CHECK (pushed); - testData.assign (1024, '\0'); - - res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 571); - BOOST_CHECK_EQUAL (testData[0], 'a'); - BOOST_CHECK_EQUAL (testData[236], 'a'); - BOOST_CHECK_EQUAL (testData[570], 'a'); - BOOST_CHECK_EQUAL (testData[1023], 0); -} - -BOOST_AUTO_TEST_CASE (test_stackqueue_3_push) -{ - StackQueue stack (4096); - - std::vector testData (1024, 'a'); - int res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 0); - - bool pushed = stack.push (testData.data (), 571); - BOOST_CHECK (pushed); - testData.assign (1024, 'b'); - pushed = stack.push (testData.data (), 249); - BOOST_CHECK (pushed); - testData.assign (1024, 'c'); - pushed = stack.push (testData.data (), 393); - BOOST_CHECK (pushed); - res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 393); - BOOST_CHECK_EQUAL (testData[0], 'c'); - BOOST_CHECK_EQUAL (testData[236], 'c'); - - res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 249); - BOOST_CHECK_EQUAL (testData[0], 'b'); - BOOST_CHECK_EQUAL (testData[236], 'b'); - BOOST_CHECK (!stack.empty ()); - - res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 571); - BOOST_CHECK_EQUAL (testData[0], 'a'); - BOOST_CHECK_EQUAL (testData[236], 'a'); - BOOST_CHECK_EQUAL (testData[570], 'a'); - - BOOST_CHECK (stack.empty ()); -} - -BOOST_AUTO_TEST_CASE (test_stackqueue_push_full) -{ - StackQueue stack (1024); - - std::vector testData (1024, 'a'); - int res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 0); - - bool pushed = stack.push (testData.data (), 571); - BOOST_CHECK (pushed); - testData.assign (1024, 'b'); - pushed = stack.push (testData.data (), 249); - BOOST_CHECK (pushed); - testData.assign (1024, 'c'); - pushed = stack.push (testData.data (), 393); - BOOST_CHECK (!pushed); - - BOOST_CHECK (!stack.isSpaceAvailable (393)); - BOOST_CHECK (!stack.isSpaceAvailable (200)); - BOOST_CHECK (stack.isSpaceAvailable (180)); - BOOST_CHECK_EQUAL (stack.getCurrentCount (), 2); - - pushed = stack.push (testData.data (), 180); - BOOST_CHECK (pushed); - testData.assign (1024, 'd'); - BOOST_CHECK_EQUAL (stack.getCurrentCount (), 3); - - res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 180); - BOOST_CHECK_EQUAL (testData[0], 'c'); - BOOST_CHECK_EQUAL (testData[179], 'c'); - BOOST_CHECK_EQUAL (testData[180], 'd'); // this is one past the copy - - res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 249); - BOOST_CHECK_EQUAL (testData[0], 'b'); - BOOST_CHECK_EQUAL (testData[236], 'b'); - BOOST_CHECK (!stack.empty ()); - - res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 571); - BOOST_CHECK_EQUAL (testData[0], 'a'); - BOOST_CHECK_EQUAL (testData[236], 'a'); - BOOST_CHECK_EQUAL (testData[570], 'a'); - - res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 0); - BOOST_CHECK_EQUAL (testData[0], 'a'); - BOOST_CHECK_EQUAL (testData[236], 'a'); - BOOST_CHECK_EQUAL (testData[570], 'a'); - - BOOST_CHECK (stack.empty ()); -} - -BOOST_AUTO_TEST_CASE (test_stackqueue_reverse) -{ - StackQueue stack ( 4096); - - std::vector testData (1024, 'a'); - int res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 0); - - bool pushed = stack.push (testData.data (), 571); - BOOST_CHECK (pushed); - testData.assign (1024, 'b'); - pushed = stack.push (testData.data (), 249); - BOOST_CHECK (pushed); - testData.assign (1024, 'c'); - pushed = stack.push (testData.data (), 393); - BOOST_CHECK (pushed); - - stack.reverse (); - - res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 571); - BOOST_CHECK_EQUAL (testData[0], 'a'); - BOOST_CHECK_EQUAL (testData[236], 'a'); - BOOST_CHECK_EQUAL (testData[570], 'a'); - - res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 249); - BOOST_CHECK_EQUAL (testData[0], 'b'); - BOOST_CHECK_EQUAL (testData[236], 'b'); - BOOST_CHECK (!stack.empty ()); - - res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 393); - BOOST_CHECK_EQUAL (testData[0], 'c'); - BOOST_CHECK_EQUAL (testData[236], 'c'); - - BOOST_CHECK (stack.empty ()); -} - - -BOOST_AUTO_TEST_CASE (test_stackqueue_move) -{ - - StackQueue stack (2048); - - std::vector testData (1024, 'a'); - int res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 0); - - stack.push (testData.data (), 571); - testData.assign (1024, '\0'); - - StackQueue mstack (std::move (stack)); - BOOST_CHECK_EQUAL (mstack.getCurrentCount (), 1); - res = mstack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 571); - BOOST_CHECK_EQUAL (testData[0], 'a'); - BOOST_CHECK_EQUAL (testData[236], 'a'); - BOOST_CHECK_EQUAL (testData[570], 'a'); - BOOST_CHECK_EQUAL (testData[1023], 0); -} - - -BOOST_AUTO_TEST_CASE (test_stackqueue_3_push_and_copy) -{ - StackQueue stack (4096); - - std::vector testData (1024, 'a'); - int res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 0); - - bool pushed = stack.push (testData.data (), 571); - BOOST_CHECK (pushed); - testData.assign (1024, 'b'); - pushed = stack.push (testData.data (), 249); - BOOST_CHECK (pushed); - testData.assign (1024, 'c'); - pushed = stack.push (testData.data (), 393); - BOOST_CHECK (pushed); - - StackQueue cstack (stack); - stack.reverse (); - - res = cstack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 393); - BOOST_CHECK_EQUAL (testData[0], 'c'); - BOOST_CHECK_EQUAL (testData[236], 'c'); - - res = cstack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 249); - BOOST_CHECK_EQUAL (testData[0], 'b'); - BOOST_CHECK_EQUAL (testData[236], 'b'); - BOOST_CHECK (!stack.empty ()); - - res = cstack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 571); - BOOST_CHECK_EQUAL (testData[0], 'a'); - BOOST_CHECK_EQUAL (testData[236], 'a'); - BOOST_CHECK_EQUAL (testData[570], 'a'); - - BOOST_CHECK (cstack.empty ()); - BOOST_CHECK (!stack.empty ()); - - //check the original still has data and was reversed - res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 571); - BOOST_CHECK_EQUAL (testData[0], 'a'); - BOOST_CHECK_EQUAL (testData[236], 'a'); - BOOST_CHECK_EQUAL (testData[570], 'a'); - - //now copy assign the stack - cstack = stack; - - res = cstack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 249); - BOOST_CHECK_EQUAL (testData[0], 'b'); - BOOST_CHECK_EQUAL (testData[236], 'b'); - BOOST_CHECK (!stack.empty ()); - -} - -BOOST_AUTO_TEST_CASE (test_stackqueue_move_assignement) -{ - StackQueue stack (2048); - StackQueue stack2 (1024); - std::vector testData (1024, 'a'); - int res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 0); - - stack.push (testData.data (), 571); - testData.assign (1024, 'b'); - stack2.push (testData.data (), 397); - - stack2 = std::move (stack); - testData.assign (1024, '\0'); - - BOOST_CHECK_EQUAL (stack2.getCurrentCount (), 1); - res = stack2.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 571); - BOOST_CHECK_EQUAL (testData[0], 'a'); - BOOST_CHECK_EQUAL (testData[236], 'a'); - BOOST_CHECK_EQUAL (testData[570], 'a'); - BOOST_CHECK_EQUAL (testData[1023], 0); -} - - -BOOST_AUTO_TEST_CASE (test_stackqueue_3_push_resize) -{ - StackQueue stack (2048); - - std::vector testData (1024, 'a'); - int res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 0); - - bool pushed = stack.push (testData.data (), 571); - BOOST_CHECK (pushed); - testData.assign (1024, 'b'); - pushed = stack.push (testData.data (), 249); - BOOST_CHECK (pushed); - testData.assign (1024, 'c'); - pushed = stack.push (testData.data (), 393); - BOOST_CHECK (pushed); - //make sure to trigger a reallocation in memory - stack.resize (100000); - res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 393); - BOOST_CHECK_EQUAL (testData[0], 'c'); - BOOST_CHECK_EQUAL (testData[236], 'c'); - - res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 249); - BOOST_CHECK_EQUAL (testData[0], 'b'); - BOOST_CHECK_EQUAL (testData[236], 'b'); - BOOST_CHECK (!stack.empty ()); - - res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 571); - BOOST_CHECK_EQUAL (testData[0], 'a'); - BOOST_CHECK_EQUAL (testData[236], 'a'); - BOOST_CHECK_EQUAL (testData[570], 'a'); - - BOOST_CHECK (stack.empty ()); -} - - - -BOOST_AUTO_TEST_CASE (test_stackqueue_3_push_resize_shrink) -{ - StackQueue stack (2048); - - std::vector testData (1024, 'a'); - int res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 0); - - bool pushed = stack.push (testData.data (), 571); - BOOST_CHECK (pushed); - testData.assign (1024, 'b'); - pushed = stack.push (testData.data (), 249); - BOOST_CHECK (pushed); - testData.assign (1024, 'c'); - pushed = stack.push (testData.data (), 393); - BOOST_CHECK (pushed); - - stack.resize (1400); - - BOOST_CHECK_EQUAL (stack.capacity (), 1400); - - BOOST_CHECK_THROW (stack.resize (95), std::runtime_error); - res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 393); - BOOST_CHECK_EQUAL (testData[0], 'c'); - BOOST_CHECK_EQUAL (testData[236], 'c'); - - res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 249); - BOOST_CHECK_EQUAL (testData[0], 'b'); - BOOST_CHECK_EQUAL (testData[236], 'b'); - BOOST_CHECK (!stack.empty ()); - - res = stack.pop (testData.data (), 1024); - BOOST_CHECK_EQUAL (res, 571); - BOOST_CHECK_EQUAL (testData[0], 'a'); - BOOST_CHECK_EQUAL (testData[236], 'a'); - BOOST_CHECK_EQUAL (testData[570], 'a'); - - BOOST_CHECK (stack.empty ()); -} - -BOOST_AUTO_TEST_SUITE_END () \ No newline at end of file diff --git a/tests/helics/core/CMakeLists.txt b/tests/helics/core/CMakeLists.txt index 26ce08fb7a..740424c0dc 100644 --- a/tests/helics/core/CMakeLists.txt +++ b/tests/helics/core/CMakeLists.txt @@ -1,79 +1,49 @@ +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Copyright (c) 2017-2022, Battelle Memorial Institute; Lawrence Livermore +# National Security, LLC; Alliance for Sustainable Energy, LLC. +# See the top-level NOTICE for additional details. +# All rights reserved. # -# Copyright © 2017-2018, Battelle Memorial Institute; Lawrence Livermore National -# Security, LLC; Alliance for Sustainable Energy, LLC All rights reserved. See LICENSE -# file and DISCLAIMER for more details. -# - -# ----------------------------------------------------------------------------- -# Core tests using Boost -# ----------------------------------------------------------------------------- - -set(core_test_headers testFixtures.h) - -set( - core_test_sources - core-tests.cpp - InfoClass-tests.cpp - FederateState-tests.cpp - ActionMessage-tests.cpp - # CoreBroker-tests.cpp - CoreFactory-tests.cpp - MessageTimerTests.cpp - # CommonCore-tests.cpp - TestCore-tests.cpp - testFixtures.cpp - IPCcore_tests.cpp - IpcQueueTests.cpp - IpcQueueImplTests.cpp - data-block-tests.cpp - UdpCore-tests.cpp - ForwardingTimeCoordinatorTests.cpp - TimeCoordinatorTests.cpp - networkInfoTests.cpp +# SPDX-License-Identifier: BSD-3-Clause +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +set(core_test_headers) + +set(core_test_sources + core-tests.cpp + InfoClass-tests.cpp + FederateState-tests.cpp + ActionMessage-tests.cpp + BrokerClassTests.cpp + CoreFactory-tests.cpp + ForwardingTimeCoordinatorTests.cpp + TimeCoordinatorTests.cpp + CoreConfigureTests.cpp + FilterFederateTests.cpp + TimeDependenciesTests.cpp + CoreOperationsTests.cpp ) -if(HELICS_HAVE_ZEROMQ) - list(APPEND core_test_sources ZeromqCore-tests.cpp) -endif(HELICS_HAVE_ZEROMQ) - -if(NOT DISABLE_TCP_CORE) - list(APPEND core_test_sources TcpCore-tests.cpp TcpSSCore-tests.cpp) -endif() - -if(MPI_ENABLE) - list(APPEND core_test_sources MpiCore-tests.cpp) +if(NOT HELICS_DISABLE_ASIO) + list(APPEND core_test_sources MessageTimerTests.cpp) endif() add_executable(core-tests ${core_test_sources} ${core_test_headers}) -target_link_libraries(core-tests helics-static helics_test_base) +target_link_libraries(core-tests helics_network helics_test_base) target_include_directories(core-tests PRIVATE ${PROJECT_SOURCE_DIR}/src) +target_compile_definitions(core-tests PRIVATE BOOST_DATE_TIME_NO_LIB) + +target_compile_definitions( + core-tests PRIVATE "-DTEST_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/../test_files/\"" +) + set_target_properties(core-tests PROPERTIES FOLDER tests) -add_test(NAME core-tests COMMAND core-tests --log_level=test_suite --report_level=short) +add_test(NAME core-tests COMMAND core-tests) set_property(TEST core-tests PROPERTY LABELS Core Coverage Daily) # Tests for Continuous Integration builds -add_test( - NAME core-ci-tests - COMMAND - core-tests - --run_test=@ci - --log_level=test_suite - --report_level=short -) -set_property(TEST core-ci-tests PROPERTY LABELS Continuous) -#set_property(TEST core-ci-tests PROPERTY LABELS DebugTest) -foreach(keyfile IN LISTS KEY_LIBRARY_FILES) - add_custom_command( - TARGET - core-tests - POST_BUILD # Adds a post-build event to core tests - COMMAND - ${CMAKE_COMMAND} - -E - copy_if_different # which executes "cmake - E copy_if_different..." - "${keyfile}" # <--this is in-file - "$/" - ) # <--this is out- file path -endforeach(keyfile) +add_test(NAME core-ci-tests COMMAND core-tests --gtest_filter=-*ci_skip*) +set_property(TEST core-ci-tests PROPERTY LABELS CoreCI Continuous) +# set_property(TEST core-ci-tests PROPERTY LABELS DebugTest) diff --git a/tests/helics/core/IpcQueueImpTests.cpp b/tests/helics/network/IpcQueueImpTests.cpp similarity index 100% rename from tests/helics/core/IpcQueueImpTests.cpp rename to tests/helics/network/IpcQueueImpTests.cpp diff --git a/tests/helics/core/IpcQueueImplTests.cpp b/tests/helics/network/IpcQueueImplTests.cpp similarity index 100% rename from tests/helics/core/IpcQueueImplTests.cpp rename to tests/helics/network/IpcQueueImplTests.cpp diff --git a/tests/helics/core/IpcQueueTests.cpp b/tests/helics/network/IpcQueueTests.cpp similarity index 100% rename from tests/helics/core/IpcQueueTests.cpp rename to tests/helics/network/IpcQueueTests.cpp From b7f8af6db78a924b20f38c74e6ed1f467a32b127 Mon Sep 17 00:00:00 2001 From: Philip Top Date: Wed, 4 May 2022 10:25:56 -0700 Subject: [PATCH 7/8] make a few more updates --- src/helics/network/CMakeLists.txt | 4 +- .../network/ipc/IpcBlockingPriorityQueue.cpp | 15 +- .../network/ipc/IpcBlockingPriorityQueue.hpp | 9 +- .../ipc/IpcBlockingPriorityQueueImpl.cpp | 8 +- .../ipc/IpcBlockingPriorityQueueImpl.hpp | 19 +- .../CombinationFederateTests.cpp | 405 ++++++++++-------- tests/helics/common/CMakeLists.txt | 47 +- tests/helics/network/IpcQueueImpTests.cpp | 9 +- tests/helics/network/IpcQueueImplTests.cpp | 9 +- tests/helics/network/IpcQueueTests.cpp | 9 +- 10 files changed, 276 insertions(+), 258 deletions(-) diff --git a/src/helics/network/CMakeLists.txt b/src/helics/network/CMakeLists.txt index 527173bcb6..571156c6b4 100644 --- a/src/helics/network/CMakeLists.txt +++ b/src/helics/network/CMakeLists.txt @@ -21,7 +21,7 @@ set(TESTCORE_SOURCE_FILES test/TestBroker.cpp test/TestCore.cpp test/TestComms.c set(INPROCCORE_SOURCE_FILES inproc/InprocBroker.cpp inproc/InprocCore.cpp inproc/InprocComms.cpp) set(IPC_SOURCE_FILES ipc/IpcCore.cpp ipc/IpcBroker.cpp ipc/IpcComms.cpp ipc/IpcQueueHelper.cpp - # ipc/IpcBlockingPriorityQueue.cpp ipc/IpcBlockingPriorityQueueImpl.cpp + ipc/IpcBlockingPriorityQueue.cpp ipc/IpcBlockingPriorityQueueImpl.cpp ) set(MPI_SOURCE_FILES mpi/MpiCore.cpp mpi/MpiBroker.cpp mpi/MpiComms.cpp mpi/MpiService.cpp) @@ -63,7 +63,7 @@ set(TESTCORE_HEADER_FILES test/TestCore.h test/TestBroker.h test/TestComms.h) set(INPROCCORE_HEADER_FILES inproc/InprocCore.h inproc/InprocBroker.h inproc/InprocComms.h) set(IPC_HEADER_FILES ipc/IpcCore.h ipc/IpcBroker.h ipc/IpcComms.h ipc/IpcQueueHelper.h - # ipc/IpcBlockingPriorityQueue.hpp ipc/IpcBlockingPriorityQueueImpl.hpp + ipc/IpcBlockingPriorityQueue.hpp ipc/IpcBlockingPriorityQueueImpl.hpp ) set(ZMQ_HEADER_FILES diff --git a/src/helics/network/ipc/IpcBlockingPriorityQueue.cpp b/src/helics/network/ipc/IpcBlockingPriorityQueue.cpp index f4c0f55a9e..e53964bb94 100644 --- a/src/helics/network/ipc/IpcBlockingPriorityQueue.cpp +++ b/src/helics/network/ipc/IpcBlockingPriorityQueue.cpp @@ -1,11 +1,12 @@ /* -Copyright (c) 2017-2018, +Copyright (c) 2017-2022, Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable -Energy, LLC All rights reserved. See LICENSE file and DISCLAIMER for more details. +Energy, LLC. See the top-level NOTICE for additional details. All rights reserved. +SPDX-License-Identifier: BSD-3-Clause */ #include "IpcBlockingPriorityQueue.hpp" -#include "helics/external/optional.hpp" +#include #include #include @@ -214,7 +215,7 @@ class BlockingPriorityQueue { @return an optional object with an object of type T if available */ template::value>> - stx::optional try_peek() const + std::optional try_peek() const { std::lock_guard lock(m_pullLock); if (!priorityQueue.empty()) { @@ -232,7 +233,7 @@ class BlockingPriorityQueue { @return an optional containing the value if successful the optional will be empty if there is no element in the queue */ - stx::optional try_pop(); + std::optional try_pop(); /** blocking call to wait on an object from the stack*/ T pop() @@ -273,7 +274,7 @@ class BlockingPriorityQueue { } /** blocking call to wait on an object from the stack with timeout*/ - stx::optional pop(std::chrono::milliseconds timeout) + std::optional pop(std::chrono::milliseconds timeout) { auto val = try_pop(); while (!val) { @@ -364,7 +365,7 @@ depending on the number of consumers }; template -stx::optional BlockingPriorityQueue::try_pop() +std::optional BlockingPriorityQueue::try_pop() { std::lock_guard pullLock(m_pullLock); // first pullLock if (!priorityQueue.empty()) { diff --git a/src/helics/network/ipc/IpcBlockingPriorityQueue.hpp b/src/helics/network/ipc/IpcBlockingPriorityQueue.hpp index ebbc61a75f..777747db63 100644 --- a/src/helics/network/ipc/IpcBlockingPriorityQueue.hpp +++ b/src/helics/network/ipc/IpcBlockingPriorityQueue.hpp @@ -1,11 +1,12 @@ /* -Copyright (c) 2017-2018, +Copyright (c) 2017-2022, Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable -Energy, LLC All rights reserved. See LICENSE file and DISCLAIMER for more details. +Energy, LLC. See the top-level NOTICE for additional details. All rights reserved. +SPDX-License-Identifier: BSD-3-Clause */ #pragma once -#include "helics/external/optional.hpp" +#include #include #include @@ -58,7 +59,7 @@ most cases. @details only available for copy assignable objects @return an optional object with an object of type T if available */ - stx::optional> try_peek() const; + std::optional> try_peek() const; /** try to pop an object from the queue @return an optional containing the value if successful the optional will be empty if there is no diff --git a/src/helics/network/ipc/IpcBlockingPriorityQueueImpl.cpp b/src/helics/network/ipc/IpcBlockingPriorityQueueImpl.cpp index 5fbcfbe166..f0a2e7555c 100644 --- a/src/helics/network/ipc/IpcBlockingPriorityQueueImpl.cpp +++ b/src/helics/network/ipc/IpcBlockingPriorityQueueImpl.cpp @@ -32,7 +32,7 @@ namespace ipc { using namespace boost::interprocess; // NOLINT /** default constructor*/ - IpcBlockingPriorityQueueImpl::IpcBlockingPriorityQueueImpl(void* dataBlock, + IpcBlockingPriorityQueueImpl::IpcBlockingPriorityQueueImpl(void* data, size_t blockSize) { } @@ -169,7 +169,7 @@ val the value to push on the queue @return an optional object with an object of type T if available */ template::value>> - stx::optional try_peek() const + std::optional try_peek() const { std::lock_guard lock(m_pullLock); if (!priorityQueue.empty()) { @@ -187,7 +187,7 @@ val the value to push on the queue @return an optional containing the value if successful the optional will be empty if there is no element in the queue */ - stx::optional try_pop(); + std::optional try_pop(); /** blocking call to wait on an object from the stack*/ T pop() @@ -228,7 +228,7 @@ element in the queue } /** blocking call to wait on an object from the stack with timeout*/ - stx::optional pop(std::chrono::milliseconds timeout) + std::optional pop(std::chrono::milliseconds timeout) { auto val = try_pop(); while (!val) { diff --git a/src/helics/network/ipc/IpcBlockingPriorityQueueImpl.hpp b/src/helics/network/ipc/IpcBlockingPriorityQueueImpl.hpp index 2c57a828a3..b9962f6f65 100644 --- a/src/helics/network/ipc/IpcBlockingPriorityQueueImpl.hpp +++ b/src/helics/network/ipc/IpcBlockingPriorityQueueImpl.hpp @@ -1,11 +1,12 @@ /* -Copyright (c) 2017-2018, +Copyright (c) 2017-2022, Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable -Energy, LLC All rights reserved. See LICENSE file and DISCLAIMER for more details. +Energy, LLC. See the top-level NOTICE for additional details. All rights reserved. +SPDX-License-Identifier: BSD-3-Clause */ #pragma once -#include "helics/external/optional.hpp" +#include #include #include @@ -24,11 +25,11 @@ namespace ipc { /** class containing the raw data block implementation*/ class dataBlock { private: - unsigned char* origin = nullptr; - unsigned char* next = nullptr; - size_t totalSize = 0; - dataIndex* next = nullptr; - int dataCount = 0; + unsigned char* origin{nullptr}; + unsigned char* next{nullptr}; + size_t totalSize{0}; + dataIndex* next{nullptr}; + int dataCount{0}; public: dataBlock(unsigned char* newBlock, size_t blockSize); @@ -112,7 +113,7 @@ most cases. @details only available for copy assignable objects @return an optional object with an object of type T if available */ - stx::optional> try_peek() const; + std::optional> try_peek() const; /** try to pop an object from the queue @return an optional containing the value if successful the optional will be empty if there is no diff --git a/tests/helics/application_api/CombinationFederateTests.cpp b/tests/helics/application_api/CombinationFederateTests.cpp index d6afbc969b..aac1cb72c1 100644 --- a/tests/helics/application_api/CombinationFederateTests.cpp +++ b/tests/helics/application_api/CombinationFederateTests.cpp @@ -1,10 +1,12 @@ /* -Copyright © 2017-2018, -Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable Energy, LLC -All rights reserved. See LICENSE file and DISCLAIMER for more details. +Copyright (c) 2017-2022, +Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable +Energy, LLC. See the top-level NOTICE for additional details. All rights reserved. +SPDX-License-Identifier: BSD-3-Clause */ #include "helics/application_api/CombinationFederate.hpp" +#include "helics/application_api/CoreApp.hpp" #include "helics/application_api/Endpoints.hpp" #include "helics/application_api/Publications.hpp" #include "helics/core/BrokerFactory.hpp" @@ -12,295 +14,332 @@ All rights reserved. See LICENSE file and DISCLAIMER for more details. #include "helics/core/CoreFactory.hpp" #include "helics/core/core-exceptions.hpp" #include "testFixtures.hpp" + #include -#include -#include -#include +#include -namespace bdata = boost::unit_test::data; -namespace utf = boost::unit_test; +class combofed_single_type_tests: + public ::testing::TestWithParam, + public FederateTestFixture {}; -BOOST_FIXTURE_TEST_SUITE (combo_federate_tests, FederateTestFixture) +class combofed_type_tests: + public ::testing::TestWithParam, + public FederateTestFixture {}; -// const std::string core_types[] = {"udp" }; +// const std::string CoreTypes[] = {"udp" }; /** test simple creation and destruction*/ -BOOST_DATA_TEST_CASE (combo_federate_initialize_tests, bdata::make (core_types_single), core_type) +TEST_P(combofed_single_type_tests, initialize_tests) { - SetupTest (core_type, 1); - auto vFed1 = GetFederateAs (0); + SetupTest(GetParam(), 1); + auto vFed1 = GetFederateAs(0); - vFed1->enterExecutingMode (); + vFed1->enterExecutingMode(); - BOOST_CHECK (vFed1->getCurrentState () == helics::Federate::states::execution); + EXPECT_TRUE(vFed1->getCurrentMode() == helics::Federate::Modes::EXECUTING); - vFed1->finalize (); + vFed1->disconnect(); - BOOST_CHECK (vFed1->getCurrentState () == helics::Federate::states::finalize); + EXPECT_TRUE(vFed1->getCurrentMode() == helics::Federate::Modes::FINALIZE); } -BOOST_DATA_TEST_CASE (combo_federate_publication_registration, bdata::make (core_types_single), core_type) +TEST_P(combofed_single_type_tests, publication_registration) { - SetupTest (core_type, 1); - auto vFed1 = GetFederateAs (0); + SetupTest(GetParam(), 1); + auto vFed1 = GetFederateAs(0); - auto &pubid = vFed1->registerPublication ("pub1"); - auto &pubid2 = vFed1->registerGlobalPublication ("pub2"); + auto& pubid = vFed1->registerPublication("pub1"); + auto& pubid2 = vFed1->registerGlobalPublication("pub2"); - auto &pubid3 = vFed1->registerPublication ("pub3", "double", "V"); - vFed1->enterExecutingMode (); + auto& pubid3 = vFed1->registerPublication("pub3", "double", "V"); + vFed1->enterExecutingMode(); - BOOST_CHECK (vFed1->getCurrentState () == helics::Federate::states::execution); + EXPECT_TRUE(vFed1->getCurrentMode() == helics::Federate::Modes::EXECUTING); - auto &sv = vFed1->getPublicationKey (pubid); - auto &sv2 = vFed1->getPublicationKey (pubid2); - BOOST_CHECK_EQUAL (sv, "fed0/pub1"); - BOOST_CHECK_EQUAL (sv2, "pub2"); - auto &pub3name = vFed1->getPublicationKey (pubid3); - BOOST_CHECK_EQUAL (pub3name, "fed0/pub3"); + auto& sv = pubid.getName(); + auto& sv2 = pubid2.getName(); + EXPECT_EQ(sv, "fed0/pub1"); + EXPECT_EQ(sv2, "pub2"); + auto& pub3name = pubid3.getName(); + EXPECT_EQ(pub3name, "fed0/pub3"); - BOOST_CHECK_EQUAL (vFed1->getPublicationType (pubid3), "double"); - BOOST_CHECK_EQUAL (vFed1->getPublicationUnits (pubid3), "V"); + EXPECT_EQ(pubid3.getExtractionType(), "double"); + EXPECT_EQ(pubid3.getUnits(), "V"); - BOOST_CHECK (vFed1->getPublication ("pub1").getHandle () == pubid.getHandle ()); - BOOST_CHECK (vFed1->getPublication ("pub2").getHandle () == pubid2.getHandle ()); - BOOST_CHECK (vFed1->getPublication ("fed0/pub1").getHandle () == pubid.getHandle ()); - vFed1->finalize (); + EXPECT_TRUE(vFed1->getPublication("pub1").getHandle() == pubid.getHandle()); + EXPECT_TRUE(vFed1->getPublication("pub2").getHandle() == pubid2.getHandle()); + EXPECT_TRUE(vFed1->getPublication("fed0/pub1").getHandle() == pubid.getHandle()); + vFed1->disconnect(); - BOOST_CHECK (vFed1->getCurrentState () == helics::Federate::states::finalize); + EXPECT_TRUE(vFed1->getCurrentMode() == helics::Federate::Modes::FINALIZE); } -BOOST_DATA_TEST_CASE (combo_federate_single_transfer, bdata::make (core_types_single), core_type) +TEST_P(combofed_single_type_tests, single_transfer) { - SetupTest (core_type, 1); - auto vFed1 = GetFederateAs (0); + SetupTest(GetParam(), 1); + auto vFed1 = GetFederateAs(0); // register the publications - auto &pubid = vFed1->registerGlobalPublication ("pub1"); + auto& pubid = vFed1->registerGlobalPublication("pub1"); - auto &subid = vFed1->registerSubscription ("pub1"); - vFed1->setTimeProperty (helics_property_time_delta, 1.0); - vFed1->enterExecutingMode (); + auto& subid = vFed1->registerSubscription("pub1"); + vFed1->setProperty(HELICS_PROPERTY_TIME_DELTA, 1.0); + vFed1->enterExecutingMode(); // publish string1 at time=0.0; - vFed1->publish (pubid, "string1"); - auto gtime = vFed1->requestTime (1.0); + pubid.publish("string1"); + auto gtime = vFed1->requestTime(1.0); - BOOST_CHECK_EQUAL (gtime, 1.0); + EXPECT_EQ(gtime, 1.0); // get the value - std::string s = vFed1->getString (subid); + std::string s = subid.getString(); // make sure the string is what we expect - BOOST_CHECK_EQUAL (s, "string1"); + EXPECT_EQ(s, "string1"); // publish a second string - vFed1->publish (pubid, "string2"); + pubid.publish("string2"); // make sure the value is still what we expect - s = vFed1->getString (subid); + s = subid.getString(); - BOOST_CHECK_EQUAL (s, "string1"); + EXPECT_EQ(s, "string1"); // advance time - gtime = vFed1->requestTime (2.0); + gtime = vFed1->requestTime(2.0); // make sure the value was updated - BOOST_CHECK_EQUAL (gtime, 2.0); - s = vFed1->getString (subid); + EXPECT_EQ(gtime, 2.0); + s = subid.getString(); - BOOST_CHECK_EQUAL (s, "string2"); + EXPECT_EQ(s, "string2"); + vFed1->disconnect(); } -BOOST_DATA_TEST_CASE (combo_federate_endpoint_registration, bdata::make (core_types_single), core_type) +TEST_P(combofed_single_type_tests, endpoint_registration) { - SetupTest (core_type, 1); - auto mFed1 = GetFederateAs (0); + SetupTest(GetParam(), 1); + auto mFed1 = GetFederateAs(0); - auto &epid = mFed1->registerEndpoint ("ep1"); - auto &epid2 = mFed1->registerGlobalEndpoint ("ep2", "random"); + auto& epid = mFed1->registerEndpoint("ep1"); + auto& epid2 = mFed1->registerGlobalEndpoint("ep2", "random"); - mFed1->enterExecutingMode (); + mFed1->enterExecutingMode(); - BOOST_CHECK (mFed1->getCurrentState () == helics::Federate::states::execution); + EXPECT_TRUE(mFed1->getCurrentMode() == helics::Federate::Modes::EXECUTING); - auto &sv = mFed1->getEndpointName (epid); - auto &sv2 = mFed1->getEndpointName (epid2); - BOOST_CHECK_EQUAL (sv, "fed0/ep1"); - BOOST_CHECK_EQUAL (sv2, "ep2"); + auto& sv = epid.getName(); + auto& sv2 = epid2.getName(); + EXPECT_EQ(sv, "fed0/ep1"); + EXPECT_EQ(sv2, "ep2"); - BOOST_CHECK_EQUAL (mFed1->getEndpointType (epid), ""); - BOOST_CHECK_EQUAL (mFed1->getEndpointType (epid2), "random"); + EXPECT_EQ(epid.getExtractionType(), ""); + EXPECT_EQ(epid2.getInjectionType(), "random"); - BOOST_CHECK (mFed1->getEndpoint ("ep1").getHandle () == epid.getHandle ()); - BOOST_CHECK (mFed1->getEndpoint ("fed0/ep1").getHandle () == epid.getHandle ()); - BOOST_CHECK (mFed1->getEndpoint ("ep2").getHandle () == epid2.getHandle ()); - mFed1->finalize (); + EXPECT_TRUE(mFed1->getEndpoint("ep1").getHandle() == epid.getHandle()); + EXPECT_TRUE(mFed1->getEndpoint("fed0/ep1").getHandle() == epid.getHandle()); + EXPECT_TRUE(mFed1->getEndpoint("ep2").getHandle() == epid2.getHandle()); + mFed1->disconnect(); - BOOST_CHECK (mFed1->getCurrentState () == helics::Federate::states::finalize); + EXPECT_TRUE(mFed1->getCurrentMode() == helics::Federate::Modes::FINALIZE); } -BOOST_DATA_TEST_CASE (combination_federate_send_receive_2fed, bdata::make (core_types), core_type) +TEST_P(combofed_type_tests, send_receive_2fed) { - SetupTest (core_type, 2); - auto mFed1 = GetFederateAs (0); - auto mFed2 = GetFederateAs (1); + SetupTest(GetParam(), 2); + auto mFed1 = GetFederateAs(0); + auto mFed2 = GetFederateAs(1); - auto &epid = mFed1->registerEndpoint ("ep1"); - auto &epid2 = mFed2->registerGlobalEndpoint ("ep2", "random"); + auto& epid = mFed1->registerEndpoint("ep1"); + auto& epid2 = mFed2->registerGlobalEndpoint("ep2", "random"); - mFed1->setTimeProperty (helics_property_time_delta, 1.0); - mFed2->setTimeProperty (helics_property_time_delta, 1.0); + mFed1->setProperty(HELICS_PROPERTY_TIME_DELTA, 1.0); + mFed2->setProperty(HELICS_PROPERTY_TIME_DELTA, 1.0); - auto f1finish = std::async (std::launch::async, [&]() { mFed1->enterExecutingMode (); }); - mFed2->enterExecutingMode (); - f1finish.wait (); + auto f1finish = std::async(std::launch::async, [&]() { mFed1->enterExecutingMode(); }); + mFed2->enterExecutingMode(); + f1finish.wait(); - BOOST_CHECK (mFed1->getCurrentState () == helics::Federate::states::execution); - BOOST_CHECK (mFed2->getCurrentState () == helics::Federate::states::execution); + EXPECT_TRUE(mFed1->getCurrentMode() == helics::Federate::Modes::EXECUTING); + EXPECT_TRUE(mFed2->getCurrentMode() == helics::Federate::Modes::EXECUTING); - helics::data_block data (500, 'a'); - helics::data_block data2 (400, 'b'); + helics::SmallBuffer data(500, 'a'); + helics::SmallBuffer data2(400, 'b'); - mFed1->sendMessage (epid, "ep2", data); - mFed2->sendMessage (epid2, "fed0/ep1", data2); + epid.sendTo(data, "ep2"); + epid2.sendTo(data2, "fed0/ep1"); // move the time to 1.0 - auto f1time = std::async (std::launch::async, [&]() { return mFed1->requestTime (1.0); }); - auto gtime = mFed2->requestTime (1.0); + auto f1time = std::async(std::launch::async, [&]() { return mFed1->requestTime(1.0); }); + auto gtime = mFed2->requestTime(1.0); - BOOST_CHECK_EQUAL (gtime, 1.0); - BOOST_CHECK_EQUAL (f1time.get (), 1.0); + EXPECT_EQ(gtime, 1.0); + EXPECT_EQ(f1time.get(), 1.0); - auto res = mFed1->hasMessage (); - BOOST_CHECK (res); - res = mFed1->hasMessage (epid); - BOOST_CHECK (res); - res = mFed2->hasMessage (epid2); - BOOST_CHECK (res); + auto res = mFed1->hasMessage(); + EXPECT_TRUE(res); + res = mFed1->hasMessage(epid); + EXPECT_TRUE(res); + res = mFed2->hasMessage(epid2); + EXPECT_TRUE(res); - auto M1 = mFed1->getMessage (epid); - BOOST_REQUIRE_EQUAL (M1->data.size (), data2.size ()); + auto M1 = mFed1->getMessage(epid); + ASSERT_EQ(M1->data.size(), data2.size()); - BOOST_CHECK_EQUAL (M1->data[245], data2[245]); + EXPECT_EQ(M1->data[245], data2[245]); - auto M2 = mFed2->getMessage (epid2); - BOOST_REQUIRE_EQUAL (M2->data.size (), data.size ()); + auto M2 = mFed2->getMessage(epid2); + ASSERT_EQ(M2->data.size(), data.size()); - BOOST_CHECK_EQUAL (M2->data[245], data[245]); - mFed1->finalize (); - mFed2->finalize (); + EXPECT_EQ(M2->data[245], data[245]); + mFed1->disconnect(); + mFed2->disconnect(); - BOOST_CHECK (mFed1->getCurrentState () == helics::Federate::states::finalize); - BOOST_CHECK (mFed2->getCurrentState () == helics::Federate::states::finalize); + EXPECT_TRUE(mFed1->getCurrentMode() == helics::Federate::Modes::FINALIZE); + EXPECT_TRUE(mFed2->getCurrentMode() == helics::Federate::Modes::FINALIZE); } -BOOST_DATA_TEST_CASE (combination_federate_multimode_transfer, bdata::make (core_types), core_type) +TEST_P(combofed_type_tests, multimode_transfer) { - SetupTest (core_type, 2); - auto cFed1 = GetFederateAs (0); - auto cFed2 = GetFederateAs (1); + SetupTest(GetParam(), 2); + auto cFed1 = GetFederateAs(0); + auto cFed2 = GetFederateAs(1); + + cFed1->setProperty(HELICS_PROPERTY_TIME_GRANT_TIMEOUT, 1.0); + cFed2->setProperty(HELICS_PROPERTY_TIME_GRANT_TIMEOUT, 1.0); - auto &epid = cFed1->registerEndpoint ("ep1"); - auto &epid2 = cFed2->registerGlobalEndpoint ("ep2", "random"); + auto& epid = cFed1->registerEndpoint("ep1"); + auto& epid2 = cFed2->registerGlobalEndpoint("ep2", "random"); // register the publications - auto &pubid = cFed1->registerGlobalPublication ("pub1"); + auto& pubid = cFed1->registerGlobalPublication("pub1"); - auto &subid = cFed2->registerSubscription ("pub1"); + auto& subid = cFed2->registerSubscription("pub1"); - cFed1->setTimeProperty (helics_property_time_delta, 1.0); - cFed2->setTimeProperty (helics_property_time_delta, 1.0); + cFed1->setProperty(HELICS_PROPERTY_TIME_DELTA, 1.0); + cFed2->setProperty(HELICS_PROPERTY_TIME_DELTA, 1.0); - auto f1finish = std::async (std::launch::async, [&]() { cFed1->enterExecutingMode (); }); - cFed2->enterExecutingMode (); - f1finish.wait (); + auto f1finish = std::async(std::launch::async, [&]() { cFed1->enterExecutingMode(); }); + cFed2->enterExecutingMode(); + f1finish.wait(); // publish string1 at time=0.0; - cFed1->publish (pubid, "string1"); + pubid.publish("string1"); - BOOST_CHECK (cFed1->getCurrentState () == helics::Federate::states::execution); - BOOST_CHECK (cFed2->getCurrentState () == helics::Federate::states::execution); + EXPECT_TRUE(cFed1->getCurrentMode() == helics::Federate::Modes::EXECUTING); + EXPECT_TRUE(cFed2->getCurrentMode() == helics::Federate::Modes::EXECUTING); - helics::data_block data (500, 'a'); - helics::data_block data2 (400, 'b'); + helics::SmallBuffer data(500, 'a'); + helics::SmallBuffer data2(400, 'b'); - cFed1->sendMessage (epid, "ep2", data); - cFed2->sendMessage (epid2, "fed0/ep1", data2); + epid.sendTo(data, "ep2"); + epid2.sendTo(data2, "fed0/ep1"); // move the time to 1.0 - auto f1time = std::async (std::launch::async, [&]() { return cFed1->requestTime (1.0); }); - auto gtime = cFed2->requestTime (1.0); + auto f1time = std::async(std::launch::async, [&]() { return cFed1->requestTime(1.0); }); + auto gtime = cFed2->requestTime(1.0); - BOOST_CHECK_EQUAL (gtime, 1.0); - BOOST_CHECK_EQUAL (f1time.get (), 1.0); + EXPECT_EQ(gtime, 1.0); + EXPECT_EQ(f1time.get(), 1.0); - std::string s = cFed2->getString (subid); + std::string s = subid.getString(); // get the value // make sure the string is what we expect - BOOST_CHECK_EQUAL (s, "string1"); + EXPECT_EQ(s, "string1"); // publish a second string - cFed1->publish (pubid, "string2"); + pubid.publish("string2"); // make sure the value is still what we expect - s = cFed2->getString (subid); + s = subid.getString(); - BOOST_CHECK_EQUAL (s, "string1"); + EXPECT_EQ(s, "string1"); - auto res = cFed1->hasMessage (); - BOOST_CHECK (res); - res = cFed1->hasMessage (epid); - BOOST_CHECK (res); - res = cFed2->hasMessage (epid2); - BOOST_CHECK (res); + auto res = cFed1->hasMessage(); + EXPECT_TRUE(res); + res = cFed1->hasMessage(epid); + EXPECT_TRUE(res); + res = cFed2->hasMessage(epid2); + EXPECT_TRUE(res); - auto M1 = cFed1->getMessage (epid); - BOOST_REQUIRE_EQUAL (M1->data.size (), data2.size ()); + auto M1 = cFed1->getMessage(epid); + ASSERT_EQ(M1->data.size(), data2.size()); - BOOST_CHECK_EQUAL (M1->data[245], data2[245]); + EXPECT_EQ(M1->data[245], data2[245]); - auto M2 = cFed2->getMessage (epid2); - BOOST_REQUIRE_EQUAL (M2->data.size (), data.size ()); + auto M2 = cFed2->getMessage(epid2); + ASSERT_EQ(M2->data.size(), data.size()); - BOOST_CHECK_EQUAL (M2->data[245], data[245]); + EXPECT_EQ(M2->data[245], data[245]); // advance time - f1time = std::async (std::launch::async, [&]() { return cFed1->requestTime (2.0); }); - gtime = cFed2->requestTime (2.0); + f1time = std::async(std::launch::async, [&]() { return cFed1->requestTime(2.0); }); + gtime = cFed2->requestTime(2.0); - BOOST_CHECK_EQUAL (gtime, 2.0); - BOOST_CHECK_EQUAL (f1time.get (), 2.0); + EXPECT_EQ(gtime, 2.0); + EXPECT_EQ(f1time.get(), 2.0); // make sure the value was updated - auto &ns = cFed2->getString (subid); + const auto& ns = subid.getString(); - BOOST_CHECK_EQUAL (ns, "string2"); + EXPECT_EQ(ns, "string2"); - cFed1->finalize (); - cFed2->finalize (); + cFed1->disconnect(); + cFed2->disconnect(); - BOOST_CHECK (cFed1->getCurrentState () == helics::Federate::states::finalize); - BOOST_CHECK (cFed2->getCurrentState () == helics::Federate::states::finalize); + EXPECT_TRUE(cFed1->getCurrentMode() == helics::Federate::Modes::FINALIZE); + EXPECT_TRUE(cFed2->getCurrentMode() == helics::Federate::Modes::FINALIZE); } -BOOST_AUTO_TEST_CASE (test_file_load) +INSTANTIATE_TEST_SUITE_P(combofed_tests, + combofed_single_type_tests, + ::testing::ValuesIn(CoreTypes_simple)); +INSTANTIATE_TEST_SUITE_P(combofed_tests, combofed_type_tests, ::testing::ValuesIn(CoreTypes)); + +static constexpr const char* combo_config_files[] = {"example_combo_fed.json", + "example_combo_fed.toml"}; + +class combofed_file_load_tests: + public ::testing::TestWithParam, + public FederateTestFixture {}; + +TEST_P(combofed_file_load_tests, test_file_load) { - helics::CombinationFederate cFed (std::string (TEST_DIR) + "/example_combo_fed.json"); + helics::CombinationFederate cFed(std::string(TEST_DIR) + GetParam()); - BOOST_CHECK_EQUAL (cFed.getName (), "comboFed"); + EXPECT_EQ(cFed.getName(), "comboFed"); - BOOST_CHECK_EQUAL (cFed.getEndpointCount (), 2); - auto &id = cFed.getEndpoint ("ept1"); - BOOST_CHECK_EQUAL (cFed.getEndpointType (id), "genmessage"); + EXPECT_EQ(cFed.getEndpointCount(), 2); + auto& id = cFed.getEndpoint("ept1"); + EXPECT_EQ(id.getExtractionType(), "genmessage"); - BOOST_CHECK_EQUAL (cFed.getInputCount (), 2); - BOOST_CHECK_EQUAL (cFed.getPublicationCount (), 2); + EXPECT_EQ(cFed.getInputCount(), 2); + EXPECT_EQ(cFed.getPublicationCount(), 2); - BOOST_CHECK (!cFed.getPublication (1).getInfo ().empty ()); - cFed.disconnect (); + EXPECT_TRUE(!cFed.getPublication(1).getInfo().empty()); + cFed.getCorePointer()->disconnect(); + cFed.disconnect(); } -BOOST_AUTO_TEST_CASE (test_file_load_toml) +INSTANTIATE_TEST_SUITE_P(combofed_tests, + combofed_file_load_tests, + ::testing::ValuesIn(combo_config_files)); + +TEST(comboFederate, constructor2) { - helics::CombinationFederate cFed (std::string (TEST_DIR) + "/example_combo_fed.toml"); + auto cr = helics::CoreFactory::create(helics::CoreType::TEST, "--name=mf --autobroker"); + helics::FederateInfo fi(helics::CoreType::TEST); + fi.setProperty(HELICS_PROPERTY_INT_LOG_LEVEL, HELICS_LOG_LEVEL_ERROR); + helics::CombinationFederate mf1("fed1", cr, fi); + + mf1.registerGlobalFilter("filt1"); + mf1.registerGlobalFilter("filt2"); - BOOST_CHECK_EQUAL (cFed.getName (), "comboFed"); + EXPECT_NO_THROW(mf1.enterExecutingMode()); + mf1.disconnect(); - BOOST_CHECK_EQUAL (cFed.getEndpointCount (), 2); - auto &id = cFed.getEndpoint ("ept1"); - BOOST_CHECK_EQUAL (cFed.getEndpointType (id), "genmessage"); + cr.reset(); +} + +TEST(comboFederate, constructor3) +{ + helics::CoreApp cr(helics::CoreType::TEST, "--name=mf2 --autobroker"); + helics::FederateInfo fi(helics::CoreType::TEST); + fi.setProperty(HELICS_PROPERTY_INT_LOG_LEVEL, HELICS_LOG_LEVEL_ERROR); + helics::CombinationFederate mf1("fed1", cr, fi); - BOOST_CHECK_EQUAL (cFed.getInputCount (), 2); - BOOST_CHECK_EQUAL (cFed.getPublicationCount (), 2); + mf1.registerGlobalFilter("filt1"); + mf1.registerGlobalFilter("filt2"); - cFed.disconnect (); + EXPECT_NO_THROW(mf1.enterExecutingMode()); + mf1.disconnect(); + EXPECT_TRUE(cr.waitForDisconnect(std::chrono::milliseconds(500))); } -BOOST_AUTO_TEST_SUITE_END () diff --git a/tests/helics/common/CMakeLists.txt b/tests/helics/common/CMakeLists.txt index 13fd0a8e1a..3454aecc39 100644 --- a/tests/helics/common/CMakeLists.txt +++ b/tests/helics/common/CMakeLists.txt @@ -1,9 +1,11 @@ +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Copyright (c) 2017-2022, Battelle Memorial Institute; Lawrence Livermore +# National Security, LLC; Alliance for Sustainable Energy, LLC. +# See the top-level NOTICE for additional details. +# All rights reserved. # -# Copyright © 2017-2019, Battelle Memorial Institute; Lawrence Livermore National -# Security, LLC; Alliance for Sustainable Energy, LLC. See the top-level NOTICE for additional details. All rights reserved. -# # SPDX-License-Identifier: BSD-3-Clause -# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ----------------------------------------------------------------------------- # Common library tests using Boost @@ -11,40 +13,11 @@ set(common_test_headers) -set( - common_test_sources - common-tests.cpp - TimeTests.cpp -) +set(common_test_sources TimeTests.cpp JsonGenerationTests.cpp SmallBufferTests.cpp) add_executable(common-tests ${common_test_sources} ${common_test_headers}) -target_link_libraries(common-tests PRIVATE helics-static helics_google_test_base) +target_link_libraries(common-tests PRIVATE HELICS::core helics_test_base fmt::fmt) set_target_properties(common-tests PROPERTIES FOLDER tests) -add_test( - NAME common-tests - COMMAND common-tests --log_level=message --report_level=short -) -set_property( - TEST common-tests - PROPERTY - LABELS - Common - Daily - Coverage - Continuous -) - -foreach(keyfile IN LISTS KEY_LIBRARY_FILES) - add_custom_command( - TARGET - common-tests - POST_BUILD # Adds a post-build event to core tests - COMMAND - ${CMAKE_COMMAND} - -E - copy_if_different # which executes "cmake - E copy_if_different..." - "${keyfile}" # <--this is in-file - "$/" - ) # <--this is out-file path -endforeach(keyfile) +add_test(NAME common-tests COMMAND common-tests) +set_property(TEST common-tests PROPERTY LABELS Common Daily Coverage Continuous) diff --git a/tests/helics/network/IpcQueueImpTests.cpp b/tests/helics/network/IpcQueueImpTests.cpp index 1b35c26930..7bb32cdada 100644 --- a/tests/helics/network/IpcQueueImpTests.cpp +++ b/tests/helics/network/IpcQueueImpTests.cpp @@ -1,7 +1,8 @@ /* -Copyright © 2017-2018, -Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable Energy, LLC -All rights reserved. See LICENSE file and DISCLAIMER for more details. +Copyright (c) 2017-2022, +Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable +Energy, LLC. See the top-level NOTICE for additional details. All rights reserved. +SPDX-License-Identifier: BSD-3-Clause */ #include @@ -503,4 +504,4 @@ BOOST_AUTO_TEST_CASE(multithreaded_tests3_pop_mem) } -BOOST_AUTO_TEST_SUITE_END () \ No newline at end of file +BOOST_AUTO_TEST_SUITE_END () diff --git a/tests/helics/network/IpcQueueImplTests.cpp b/tests/helics/network/IpcQueueImplTests.cpp index 28c08ee819..1236298228 100644 --- a/tests/helics/network/IpcQueueImplTests.cpp +++ b/tests/helics/network/IpcQueueImplTests.cpp @@ -1,7 +1,8 @@ /* -Copyright © 2017-2018, -Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable Energy, LLC -All rights reserved. See LICENSE file and DISCLAIMER for more details. +Copyright (c) 2017-2022, +Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable +Energy, LLC. See the top-level NOTICE for additional details. All rights reserved. +SPDX-License-Identifier: BSD-3-Clause */ #include @@ -503,4 +504,4 @@ BOOST_AUTO_TEST_CASE(multithreaded_tests3_pop_mem) } -BOOST_AUTO_TEST_SUITE_END () \ No newline at end of file +BOOST_AUTO_TEST_SUITE_END () diff --git a/tests/helics/network/IpcQueueTests.cpp b/tests/helics/network/IpcQueueTests.cpp index 1b35c26930..7bb32cdada 100644 --- a/tests/helics/network/IpcQueueTests.cpp +++ b/tests/helics/network/IpcQueueTests.cpp @@ -1,7 +1,8 @@ /* -Copyright © 2017-2018, -Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable Energy, LLC -All rights reserved. See LICENSE file and DISCLAIMER for more details. +Copyright (c) 2017-2022, +Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable +Energy, LLC. See the top-level NOTICE for additional details. All rights reserved. +SPDX-License-Identifier: BSD-3-Clause */ #include @@ -503,4 +504,4 @@ BOOST_AUTO_TEST_CASE(multithreaded_tests3_pop_mem) } -BOOST_AUTO_TEST_SUITE_END () \ No newline at end of file +BOOST_AUTO_TEST_SUITE_END () From 8618e3435658ecc0a942d4916efd00dd85b06aaf Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 4 May 2022 17:27:37 +0000 Subject: [PATCH 8/8] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/helics/network/CMakeLists.txt | 18 +- .../network/ipc/IpcBlockingPriorityQueue.cpp | 3 +- .../network/ipc/IpcBlockingPriorityQueue.hpp | 3 +- .../ipc/IpcBlockingPriorityQueueImpl.cpp | 5 +- .../ipc/IpcBlockingPriorityQueueImpl.hpp | 3 +- .../ipc/ipc/IpcBlockingPriorityQueueImpl.cpp | 749 ++++++++---------- .../ipc/ipc/IpcBlockingPriorityQueueImpl.hpp | 203 ++--- tests/helics/network/IpcQueueImpTests.cpp | 733 +++++++++-------- tests/helics/network/IpcQueueImplTests.cpp | 733 +++++++++-------- tests/helics/network/IpcQueueTests.cpp | 733 +++++++++-------- 10 files changed, 1538 insertions(+), 1645 deletions(-) diff --git a/src/helics/network/CMakeLists.txt b/src/helics/network/CMakeLists.txt index 571156c6b4..728b0afc49 100644 --- a/src/helics/network/CMakeLists.txt +++ b/src/helics/network/CMakeLists.txt @@ -20,8 +20,13 @@ set(TESTCORE_SOURCE_FILES test/TestBroker.cpp test/TestCore.cpp test/TestComms.c set(INPROCCORE_SOURCE_FILES inproc/InprocBroker.cpp inproc/InprocCore.cpp inproc/InprocComms.cpp) -set(IPC_SOURCE_FILES ipc/IpcCore.cpp ipc/IpcBroker.cpp ipc/IpcComms.cpp ipc/IpcQueueHelper.cpp - ipc/IpcBlockingPriorityQueue.cpp ipc/IpcBlockingPriorityQueueImpl.cpp +set(IPC_SOURCE_FILES + ipc/IpcCore.cpp + ipc/IpcBroker.cpp + ipc/IpcComms.cpp + ipc/IpcQueueHelper.cpp + ipc/IpcBlockingPriorityQueue.cpp + ipc/IpcBlockingPriorityQueueImpl.cpp ) set(MPI_SOURCE_FILES mpi/MpiCore.cpp mpi/MpiBroker.cpp mpi/MpiComms.cpp mpi/MpiService.cpp) @@ -62,8 +67,13 @@ set(TESTCORE_HEADER_FILES test/TestCore.h test/TestBroker.h test/TestComms.h) set(INPROCCORE_HEADER_FILES inproc/InprocCore.h inproc/InprocBroker.h inproc/InprocComms.h) -set(IPC_HEADER_FILES ipc/IpcCore.h ipc/IpcBroker.h ipc/IpcComms.h ipc/IpcQueueHelper.h - ipc/IpcBlockingPriorityQueue.hpp ipc/IpcBlockingPriorityQueueImpl.hpp +set(IPC_HEADER_FILES + ipc/IpcCore.h + ipc/IpcBroker.h + ipc/IpcComms.h + ipc/IpcQueueHelper.h + ipc/IpcBlockingPriorityQueue.hpp + ipc/IpcBlockingPriorityQueueImpl.hpp ) set(ZMQ_HEADER_FILES diff --git a/src/helics/network/ipc/IpcBlockingPriorityQueue.cpp b/src/helics/network/ipc/IpcBlockingPriorityQueue.cpp index e53964bb94..ac07a1b7ff 100644 --- a/src/helics/network/ipc/IpcBlockingPriorityQueue.cpp +++ b/src/helics/network/ipc/IpcBlockingPriorityQueue.cpp @@ -6,12 +6,11 @@ SPDX-License-Identifier: BSD-3-Clause */ #include "IpcBlockingPriorityQueue.hpp" -#include - #include #include #include #include +#include #include #include #include diff --git a/src/helics/network/ipc/IpcBlockingPriorityQueue.hpp b/src/helics/network/ipc/IpcBlockingPriorityQueue.hpp index 777747db63..bf8db39a1a 100644 --- a/src/helics/network/ipc/IpcBlockingPriorityQueue.hpp +++ b/src/helics/network/ipc/IpcBlockingPriorityQueue.hpp @@ -6,14 +6,13 @@ SPDX-License-Identifier: BSD-3-Clause */ #pragma once -#include - #include #include #include #include #include #include +#include #include namespace helics { diff --git a/src/helics/network/ipc/IpcBlockingPriorityQueueImpl.cpp b/src/helics/network/ipc/IpcBlockingPriorityQueueImpl.cpp index f0a2e7555c..7511624042 100644 --- a/src/helics/network/ipc/IpcBlockingPriorityQueueImpl.cpp +++ b/src/helics/network/ipc/IpcBlockingPriorityQueueImpl.cpp @@ -32,10 +32,7 @@ namespace ipc { using namespace boost::interprocess; // NOLINT /** default constructor*/ - IpcBlockingPriorityQueueImpl::IpcBlockingPriorityQueueImpl(void* data, - size_t blockSize) - { - } + IpcBlockingPriorityQueueImpl::IpcBlockingPriorityQueueImpl(void* data, size_t blockSize) {} /** clear the queue*/ void IpcBlockingPriorityQueueImpl::clear() diff --git a/src/helics/network/ipc/IpcBlockingPriorityQueueImpl.hpp b/src/helics/network/ipc/IpcBlockingPriorityQueueImpl.hpp index b9962f6f65..99e1e37370 100644 --- a/src/helics/network/ipc/IpcBlockingPriorityQueueImpl.hpp +++ b/src/helics/network/ipc/IpcBlockingPriorityQueueImpl.hpp @@ -6,13 +6,12 @@ SPDX-License-Identifier: BSD-3-Clause */ #pragma once -#include - #include #include #include #include #include +#include #include namespace helics { diff --git a/src/helics/network/ipc/ipc/IpcBlockingPriorityQueueImpl.cpp b/src/helics/network/ipc/ipc/IpcBlockingPriorityQueueImpl.cpp index 622b4674c9..59d791cea4 100644 --- a/src/helics/network/ipc/ipc/IpcBlockingPriorityQueueImpl.cpp +++ b/src/helics/network/ipc/ipc/IpcBlockingPriorityQueueImpl.cpp @@ -1,445 +1,400 @@ /* Copyright © 2017-2018, -Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable Energy, LLC -All rights reserved. See LICENSE file and DISCLAIMER for more details. +Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable +Energy, LLC All rights reserved. See LICENSE file and DISCLAIMER for more details. */ #pragma once #include "IpcBlockingPriorityQueueImpl.hpp" -#include + #include "boost/date_time/posix_time/posix_time.hpp" + +#include #include -namespace helics -{ -namespace ipc -{ -namespace detail -{ +namespace helics { +namespace ipc { + namespace detail { + using namespace boost::interprocess; -using namespace boost::interprocess; + /** function to verify the blocks are aligned as 8 byte alignment*/ + static constexpr int sizeAlign8(int fullSize, double fraction) + { + return (static_cast(fullSize * fraction) >> 3) * 8; + } -/** function to verify the blocks are aligned as 8 byte alignment*/ -static constexpr int sizeAlign8(int fullSize, double fraction) -{ - return (static_cast (fullSize * fraction) >> 3) * 8; -} + /** default constructor*/ + IpcBlockingPriorityQueueImpl::IpcBlockingPriorityQueueImpl(unsigned char* dataBlock, + int blockSize): + queueSize(sizeAlign8(blockSize, 0.4)), + prioritySize(sizeAlign8(blockSize, 0.2)), pushData(dataBlock, queueSize), + pullData(dataBlock + queueSize, queueSize), + priorityData(dataBlock + 2 * queueSize, prioritySize), dataBlock_(dataBlock), + dataSize(blockSize) + { + } - /** default constructor*/ -IpcBlockingPriorityQueueImpl::IpcBlockingPriorityQueueImpl (unsigned char *dataBlock, int blockSize) - : queueSize(sizeAlign8(blockSize, 0.4)),prioritySize(sizeAlign8(blockSize, 0.2)),pushData (dataBlock, queueSize), - pullData (dataBlock + queueSize, queueSize), - priorityData (dataBlock + 2*queueSize, prioritySize), - dataBlock_ (dataBlock), dataSize (blockSize) -{ -} + /** clear the queue*/ + void IpcBlockingPriorityQueueImpl::clear() + { + scoped_lock pullLock(m_pullLock); // first pullLock + scoped_lock pushLock(m_pushLock); // second pushLock + pullData.clear(); + pushData.clear(); + priorityData.clear(); + queueEmptyFlag = true; + queueFullFlag = false; + } -/** clear the queue*/ -void IpcBlockingPriorityQueueImpl::clear () -{ - scoped_lock pullLock (m_pullLock); // first pullLock - scoped_lock pushLock (m_pushLock); // second pushLock - pullData.clear (); - pushData.clear (); - priorityData.clear (); - queueEmptyFlag = true; - queueFullFlag = false; -} + bool IpcBlockingPriorityQueueImpl::try_push(const unsigned char* data, int size) + { + scoped_lock pushLock(m_pushLock); // only one lock on this branch + if (!pushData.empty()) { + return pushData.push(data, size); + } else { + scoped_lock conditionLock(m_conditionLock); + if (queueEmptyFlag) { + queueEmptyFlag = false; + conditionLock.unlock(); + // release the push lock so we don't get a potential deadlock condition + pushLock.unlock(); + // all locks released + // no lock the pullLock + scoped_lock pullLock(m_pullLock); + conditionLock.lock(); + queueEmptyFlag = false; // reset the queueEmptyflag + conditionLock.unlock(); + if (pullData.empty()) { + if (pullData.push(data, size)) { + // pullLock.unlock (); + condition_empty.notify_all(); + return true; + } + return false; + } else { + pushLock.lock(); + return pushData.push(data, size); + } + } else { + return pushData.push(data, size); + } + } + } -bool IpcBlockingPriorityQueueImpl::try_push(const unsigned char *data, int size) -{ - scoped_lock pushLock (m_pushLock); // only one lock on this branch - if (!pushData.empty ()) - { - return pushData.push (data, size); - } - else - { - scoped_lock conditionLock (m_conditionLock); - if (queueEmptyFlag) + /** push an element onto the queue + val the value to push on the queue + */ + bool IpcBlockingPriorityQueueImpl::try_pushPriority(const unsigned char* data, int size) { - queueEmptyFlag = false; - conditionLock.unlock (); - // release the push lock so we don't get a potential deadlock condition - pushLock.unlock (); - // all locks released - // no lock the pullLock - scoped_lock pullLock (m_pullLock); - conditionLock.lock (); - queueEmptyFlag = false; // reset the queueEmptyflag - conditionLock.unlock (); - if (pullData.empty ()) - { - if (pullData.push(data, size)) - { + scoped_lock conditionLock(m_conditionLock); + + if (queueEmptyFlag) { + conditionLock.unlock(); + scoped_lock pullLock(m_pullLock); + conditionLock.lock(); + queueEmptyFlag = + false; // need to set the flag again just in case after we get the lock + conditionLock.unlock(); + if (priorityData.push(data, size)) { // pullLock.unlock (); - condition_empty.notify_all (); + condition_empty.notify_all(); return true; - } - return false; - } - else - { - pushLock.lock (); - return pushData.push (data, size); + } + + } else { + conditionLock.unlock(); + scoped_lock pullLock(m_pullLock); + if (priorityData.push(data, size)) { + conditionLock.lock(); + if (queueEmptyFlag) { + queueEmptyFlag = false; + conditionLock.unlock(); + condition_empty.notify_all(); + } + return true; + } } + return false; } - else + + void IpcBlockingPriorityQueueImpl::push(const unsigned char* data, int size) { - return pushData.push (data, size); + if (try_push(data, size)) { + return; + } + while (true) { + scoped_lock pushLock( + m_pushLock); // only one lock on this branch + if (size > pushData.capacity() - 12) { + throw(std::invalid_argument("data size is greater than buffer capacity")); + } + if (pushData.isSpaceAvailable(size)) { + pushData.push(data, size); + return; + } + scoped_lock conditionLock(m_conditionLock); + queueFullFlag = true; + conditionLock.unlock(); + condition_full.wait(pushLock); // now wait + if (pushData.isSpaceAvailable(size)) { + pushData.push(data, size); + return; + } + } } - } -} - -/** push an element onto the queue -val the value to push on the queue -*/ -bool IpcBlockingPriorityQueueImpl::try_pushPriority(const unsigned char *data, int size) -{ - scoped_lock conditionLock (m_conditionLock); - if (queueEmptyFlag) - { - conditionLock.unlock (); - scoped_lock pullLock (m_pullLock); - conditionLock.lock (); - queueEmptyFlag = false; // need to set the flag again just in case after we get the lock - conditionLock.unlock (); - if (priorityData.push(data, size)) - { - // pullLock.unlock (); - condition_empty.notify_all (); - return true; - } - - } - else - { - conditionLock.unlock (); - scoped_lock pullLock (m_pullLock); - if (priorityData.push(data, size)) - { - conditionLock.lock (); - if (queueEmptyFlag) - { - queueEmptyFlag = false; - conditionLock.unlock (); - condition_empty.notify_all (); + int IpcBlockingPriorityQueueImpl::push(std::chrono::milliseconds timeout, + const unsigned char* data, + int size) + { + if (try_push(data, size)) { + return size; } - return true; - } - - } - return false; -} - -void IpcBlockingPriorityQueueImpl::push (const unsigned char *data, int size) -{ - if (try_push(data,size)) - { - return; - } - while (true) - { - scoped_lock pushLock(m_pushLock); // only one lock on this branch - if (size>pushData.capacity() - 12) - { - throw(std::invalid_argument("data size is greater than buffer capacity")); - } - if (pushData.isSpaceAvailable(size)) - { - pushData.push(data, size); - return; - } - scoped_lock conditionLock(m_conditionLock); - queueFullFlag = true; - conditionLock.unlock(); - condition_full.wait(pushLock); // now wait - if (pushData.isSpaceAvailable(size)) - { - pushData.push(data, size); - return; - } - } -} - - -int IpcBlockingPriorityQueueImpl::push(std::chrono::milliseconds timeout, const unsigned char *data, int size) -{ - if (try_push(data, size)) - { - return size; - } - while (true) - { - scoped_lock pushLock(m_pushLock); // only one lock on this branch - if (size>pushData.capacity() - 12) - { - throw(std::invalid_argument("data size is greater than buffer capacity")); - } - if (pushData.isSpaceAvailable(size)) - { - pushData.push(data, size); - return size; - } - scoped_lock conditionLock(m_conditionLock); - queueFullFlag = true; - conditionLock.unlock(); - bool conditionMet = - condition_empty.timed_wait(pushLock, boost::posix_time::microsec_clock::universal_time() + - boost::posix_time::milliseconds(timeout.count())); // now wait - if (pushData.isSpaceAvailable(size)) - { - pushData.push(data, size); - return size; - } - if (!conditionMet) - { - return 0; - } - } -} - -/** push an element onto the queue -val the value to push on the queue -*/ -void IpcBlockingPriorityQueueImpl::pushPriority (const unsigned char *data, int size) // forwarding reference -{ - - if (try_pushPriority(data, size)) - { - return; - } - - while (true) - { - scoped_lock pullLock(m_pullLock); - if (size>priorityData.capacity() - 8) - { - throw(std::invalid_argument("data size is greater than priority buffer capacity")); - } - if (priorityData.isSpaceAvailable(size)) - { - priorityData.push(data, size); - return; - } - scoped_lock conditionLock(m_conditionLock); - queueFullFlag = true; - conditionLock.unlock(); - condition_full.wait(pullLock); // now wait - if (priorityData.isSpaceAvailable(size)) - { - priorityData.push(data, size); - return; - } + while (true) { + scoped_lock pushLock( + m_pushLock); // only one lock on this branch + if (size > pushData.capacity() - 12) { + throw(std::invalid_argument("data size is greater than buffer capacity")); + } + if (pushData.isSpaceAvailable(size)) { + pushData.push(data, size); + return size; + } + scoped_lock conditionLock(m_conditionLock); + queueFullFlag = true; + conditionLock.unlock(); + bool conditionMet = + condition_empty.timed_wait(pushLock, + boost::posix_time::microsec_clock::universal_time() + + boost::posix_time::milliseconds( + timeout.count())); // now wait + if (pushData.isSpaceAvailable(size)) { + pushData.push(data, size); + return size; + } + if (!conditionMet) { + return 0; + } + } + } - } -} + /** push an element onto the queue + val the value to push on the queue + */ + void IpcBlockingPriorityQueueImpl::pushPriority(const unsigned char* data, + int size) // forwarding reference + { + if (try_pushPriority(data, size)) { + return; + } -/** push an element onto the queue -val the value to push on the queue -*/ -int IpcBlockingPriorityQueueImpl::pushPriority(std::chrono::milliseconds timeout, const unsigned char *data, int size) // forwarding reference -{ + while (true) { + scoped_lock pullLock(m_pullLock); + if (size > priorityData.capacity() - 8) { + throw(std::invalid_argument( + "data size is greater than priority buffer capacity")); + } + if (priorityData.isSpaceAvailable(size)) { + priorityData.push(data, size); + return; + } + scoped_lock conditionLock(m_conditionLock); + queueFullFlag = true; + conditionLock.unlock(); + condition_full.wait(pullLock); // now wait + if (priorityData.isSpaceAvailable(size)) { + priorityData.push(data, size); + return; + } + } + } - if (try_pushPriority(data, size)) - { - return size; - } + /** push an element onto the queue + val the value to push on the queue + */ + int IpcBlockingPriorityQueueImpl::pushPriority(std::chrono::milliseconds timeout, + const unsigned char* data, + int size) // forwarding reference + { + if (try_pushPriority(data, size)) { + return size; + } - while (true) - { - scoped_lock pullLock(m_pullLock); - if (size>priorityData.capacity() - 8) - { - throw(std::invalid_argument("data size is greater than priority buffer capacity")); - } - if (priorityData.isSpaceAvailable(size)) - { - priorityData.push(data, size); - return size; - } - scoped_lock conditionLock(m_conditionLock); - queueFullFlag = true; - conditionLock.unlock(); - bool conditionMet = - condition_empty.timed_wait(pullLock, boost::posix_time::microsec_clock::universal_time() + - boost::posix_time::milliseconds(timeout.count())); // now wait - if (priorityData.isSpaceAvailable(size)) - { - priorityData.push(data, size); - return size; - } - if (!conditionMet) - { - return 0; - } - } -} + while (true) { + scoped_lock pullLock(m_pullLock); + if (size > priorityData.capacity() - 8) { + throw(std::invalid_argument( + "data size is greater than priority buffer capacity")); + } + if (priorityData.isSpaceAvailable(size)) { + priorityData.push(data, size); + return size; + } + scoped_lock conditionLock(m_conditionLock); + queueFullFlag = true; + conditionLock.unlock(); + bool conditionMet = + condition_empty.timed_wait(pullLock, + boost::posix_time::microsec_clock::universal_time() + + boost::posix_time::milliseconds( + timeout.count())); // now wait + if (priorityData.isSpaceAvailable(size)) { + priorityData.push(data, size); + return size; + } + if (!conditionMet) { + return 0; + } + } + } -int IpcBlockingPriorityQueueImpl::try_pop (unsigned char *data, int maxSize) -{ - scoped_lock pullLock (m_pullLock); - if (!priorityData.empty ()) - { - scoped_lock conditionLock(m_conditionLock); - if (queueFullFlag) - { - queueFullFlag = false; - conditionLock.unlock(); - condition_full.notify_all(); - } - return priorityData.pop (data, maxSize); - } - if (pullData.empty ()) - { - scoped_lock pushLock (m_pushLock); - if (!pushData.empty ()) - { // on the off chance the queue got out of sync - pushData.swap (pullData); - scoped_lock conditionLock(m_conditionLock); - if (queueFullFlag) - { - queueFullFlag = false; - conditionLock.unlock(); - condition_full.notify_all(); - } - pushLock.unlock (); // we can free the push function to accept more elements after the swap call; - pullData.reverse (); - int ret = pullData.pop (data, maxSize); - if (pullData.empty ()) - { - pushLock.lock (); // second pushLock - if (!pushData.empty ()) // more elements could have been added - { // this is the potential for slow operations - pushData.swap (pullData); - // we can free the push function to accept more elements after the swap call; - pushLock.unlock (); - pullData.reverse (); + int IpcBlockingPriorityQueueImpl::try_pop(unsigned char* data, int maxSize) + { + scoped_lock pullLock(m_pullLock); + if (!priorityData.empty()) { + scoped_lock conditionLock(m_conditionLock); + if (queueFullFlag) { + queueFullFlag = false; + conditionLock.unlock(); + condition_full.notify_all(); } - else - { - conditionLock.lock(); + return priorityData.pop(data, maxSize); + } + if (pullData.empty()) { + scoped_lock pushLock(m_pushLock); + if (!pushData.empty()) { // on the off chance the queue got out of sync + pushData.swap(pullData); + scoped_lock conditionLock(m_conditionLock); + if (queueFullFlag) { + queueFullFlag = false; + conditionLock.unlock(); + condition_full.notify_all(); + } + pushLock.unlock(); // we can free the push function to accept more elements + // after the swap call; + pullData.reverse(); + int ret = pullData.pop(data, maxSize); + if (pullData.empty()) { + pushLock.lock(); // second pushLock + if (!pushData.empty()) // more elements could have been added + { // this is the potential for slow operations + pushData.swap(pullData); + // we can free the push function to accept more elements after the swap + // call; + pushLock.unlock(); + pullData.reverse(); + } else { + conditionLock.lock(); + queueEmptyFlag = true; + } + } + return ret; + } + scoped_lock conditionLock(m_conditionLock); + queueEmptyFlag = true; + return 0; // return the empty optional + } + int ret = pullData.pop(data, maxSize); + if (pullData.empty()) { + scoped_lock pushLock(m_pushLock); // second PushLock + if (!pushData.empty()) { // this has the potential for slow operations + pushData.swap(pullData); + scoped_lock conditionLock(m_conditionLock); + if (queueFullFlag) { + queueFullFlag = false; + conditionLock.unlock(); + condition_full.notify_all(); + } + // we can free the push function to accept more elements after the swap call; + pushLock.unlock(); + pullData.reverse(); + } else { + scoped_lock conditionLock(m_conditionLock); queueEmptyFlag = true; } } return ret; } - scoped_lock conditionLock (m_conditionLock); - queueEmptyFlag = true; - return 0; // return the empty optional - } - int ret = pullData.pop (data, maxSize); - if (pullData.empty ()) - { - scoped_lock pushLock (m_pushLock); // second PushLock - if (!pushData.empty ()) - { // this has the potential for slow operations - pushData.swap (pullData); - scoped_lock conditionLock(m_conditionLock); - if (queueFullFlag) - { - queueFullFlag = false; - conditionLock.unlock(); - condition_full.notify_all(); - } - // we can free the push function to accept more elements after the swap call; - pushLock.unlock (); - pullData.reverse (); - } - else - { - scoped_lock conditionLock (m_conditionLock); - queueEmptyFlag = true; - } - } - return ret; -} -/** blocking call to wait on an object from the stack*/ -int IpcBlockingPriorityQueueImpl::pop (unsigned char *data, int maxSize) -{ - auto val = try_pop (data, maxSize); - if (val > 0) - { - return val; - } - while (val == 0) - { - scoped_lock pullLock (m_pullLock); - if (!priorityData.empty ()) - { - return priorityData.pop (data, maxSize); - } - if (!pullData.empty ()) // make sure we are actually empty; + /** blocking call to wait on an object from the stack*/ + int IpcBlockingPriorityQueueImpl::pop(unsigned char* data, int maxSize) { - return pullData.pop (data, maxSize); - } - condition_empty.wait (pullLock); // now wait - if (!priorityData.empty ()) - { - return priorityData.pop (data, maxSize); - } - if (!pullData.empty ()) // make sure we are actually empty; - { - return pullData.pop (data, maxSize); + auto val = try_pop(data, maxSize); + if (val > 0) { + return val; + } + while (val == 0) { + scoped_lock pullLock(m_pullLock); + if (!priorityData.empty()) { + return priorityData.pop(data, maxSize); + } + if (!pullData.empty()) // make sure we are actually empty; + { + return pullData.pop(data, maxSize); + } + condition_empty.wait(pullLock); // now wait + if (!priorityData.empty()) { + return priorityData.pop(data, maxSize); + } + if (!pullData.empty()) // make sure we are actually empty; + { + return pullData.pop(data, maxSize); + } + pullLock.unlock(); + val = try_pop(data, maxSize); + } + // move the value out of the optional + return val; } - pullLock.unlock (); - val = try_pop (data, maxSize); - } - // move the value out of the optional - return val; -} -/** blocking call to wait on an object from the stack with timeout*/ -int IpcBlockingPriorityQueueImpl::pop (std::chrono::milliseconds timeout, unsigned char *data, int maxSize) -{ - auto val = try_pop (data, maxSize); - if (val > 0) - { - return val; - } - while (val == 0) - { - scoped_lock pullLock (m_pullLock); - if (!priorityData.empty ()) - { - return priorityData.pop (data, maxSize); - } - if (!pullData.empty ()) // make sure we are actually empty; - { - return pullData.pop (data, maxSize); - } - bool conditionMet = - condition_empty.timed_wait (pullLock, boost::posix_time::microsec_clock::universal_time () + - boost::posix_time::milliseconds (timeout.count ())); // now wait - if (!priorityData.empty ()) - { - return priorityData.pop (data, maxSize); - } - if (!pullData.empty ()) // make sure we are actually empty; + /** blocking call to wait on an object from the stack with timeout*/ + int IpcBlockingPriorityQueueImpl::pop(std::chrono::milliseconds timeout, + unsigned char* data, + int maxSize) { - return pullData.pop (data, maxSize); - } + auto val = try_pop(data, maxSize); + if (val > 0) { + return val; + } + while (val == 0) { + scoped_lock pullLock(m_pullLock); + if (!priorityData.empty()) { + return priorityData.pop(data, maxSize); + } + if (!pullData.empty()) // make sure we are actually empty; + { + return pullData.pop(data, maxSize); + } + bool conditionMet = + condition_empty.timed_wait(pullLock, + boost::posix_time::microsec_clock::universal_time() + + boost::posix_time::milliseconds( + timeout.count())); // now wait + if (!priorityData.empty()) { + return priorityData.pop(data, maxSize); + } + if (!pullData.empty()) // make sure we are actually empty; + { + return pullData.pop(data, maxSize); + } - pullLock.unlock (); - val = try_pop (data, maxSize); - if (!conditionMet) - { + pullLock.unlock(); + val = try_pop(data, maxSize); + if (!conditionMet) { + return val; + } + } return val; } - } - return val; -} -bool IpcBlockingPriorityQueueImpl::empty () const -{ - scoped_lock conditionLock (m_conditionLock); - return queueEmptyFlag; -} + bool IpcBlockingPriorityQueueImpl::empty() const + { + scoped_lock conditionLock(m_conditionLock); + return queueEmptyFlag; + } -} // namespace detail + } // namespace detail } // namespace ipc } // namespace helics diff --git a/src/helics/network/ipc/ipc/IpcBlockingPriorityQueueImpl.hpp b/src/helics/network/ipc/ipc/IpcBlockingPriorityQueueImpl.hpp index a3b85fdb5d..cfa5618d37 100644 --- a/src/helics/network/ipc/ipc/IpcBlockingPriorityQueueImpl.hpp +++ b/src/helics/network/ipc/ipc/IpcBlockingPriorityQueueImpl.hpp @@ -1,115 +1,118 @@ /* Copyright © 2017-2018, -Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable Energy, LLC -All rights reserved. See LICENSE file and DISCLAIMER for more details. +Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable +Energy, LLC All rights reserved. See LICENSE file and DISCLAIMER for more details. */ #pragma once -#include -#include -#include #include "helics/common/CircularBuffer.hpp" #include "helics/common/StackQueue.hpp" -namespace helics -{ -namespace ipc -{ -namespace detail -{ - -/** class implementing a blocking queue with a priority channel -@details this class uses locks one for push and pull it can exhibit longer blocking times if the internal -operations require a swap, however in high usage the two locks will reduce contention in most cases. -*/ - class IpcBlockingPriorityQueueImpl - { - private: - boost::interprocess::interprocess_mutex m_pushLock; //!< lock for operations on the pushElements vector - const int queueSize; - const int prioritySize; - common::StackQueueRaw pushData; - boost::interprocess::interprocess_mutex - m_pullLock; //!< lock for elements on the pullData and priority structure - common::StackQueueRaw pullData; - mutable boost::interprocess::interprocess_mutex m_conditionLock; //!< lock for the empty and full Flag - bool queueEmptyFlag{true}; //!< flag indicating the queue is empty - bool queueFullFlag{false}; - - // the condition variable should be keyed of the conditionLock - boost::interprocess::interprocess_condition - condition_empty; //!< condition variable for notification of new data - boost::interprocess::interprocess_condition - condition_full; //!< condition variable for notification of available space - unsigned char *dataBlock_; - const size_t dataSize; - common::CircularBufferRaw priorityData; - - public: - /** default constructor*/ - IpcBlockingPriorityQueueImpl (unsigned char *dataBlock, int blockSize); - - /** clear the queue*/ - void clear (); - - /** DISABLE_COPY_AND_ASSIGN */ - IpcBlockingPriorityQueueImpl (const IpcBlockingPriorityQueueImpl &) = delete; - IpcBlockingPriorityQueueImpl &operator= (const IpcBlockingPriorityQueueImpl &) = delete; - - /** push a data block - val the value to push on the queue - */ - void push (const unsigned char *data, int size); - - /** push a data block - val the value to push on the queue - */ - int push(std::chrono::milliseconds timeout, const unsigned char *data, int size); +#include +#include +#include - /** push an element onto the queue - val the value to push on the queue - */ - void pushPriority (const unsigned char *data, int size); - /** push an element onto the queue - @param timeout the time to wait - @param data the data to put into the queue - @param size the number of bytes of data to store - @return size if successful, 0 if not - @throws invalid_argument if the size is greater than the capacity - */ - int pushPriority(std::chrono::milliseconds timeout, const unsigned char *data, int size); - /** push a data block - val the value to push on the queue - */ - bool try_push (const unsigned char *data, int size); +namespace helics { +namespace ipc { + namespace detail { - /** push an element onto the queue - val the value to push on the queue + /** class implementing a blocking queue with a priority channel + @details this class uses locks one for push and pull it can exhibit longer blocking times if + the internal operations require a swap, however in high usage the two locks will reduce + contention in most cases. */ - bool try_pushPriority (const unsigned char *data, int size); - - /** try to pop an object from the queue - @details this function does not block, will return 0 if no data could be popped - @return an integer with the size of the popped value, + class IpcBlockingPriorityQueueImpl { + private: + boost::interprocess::interprocess_mutex + m_pushLock; //!< lock for operations on the pushElements vector + const int queueSize; + const int prioritySize; + common::StackQueueRaw pushData; + boost::interprocess::interprocess_mutex + m_pullLock; //!< lock for elements on the pullData and priority structure + common::StackQueueRaw pullData; + mutable boost::interprocess::interprocess_mutex + m_conditionLock; //!< lock for the empty and full Flag + bool queueEmptyFlag{true}; //!< flag indicating the queue is empty + bool queueFullFlag{false}; + + // the condition variable should be keyed of the conditionLock + boost::interprocess::interprocess_condition + condition_empty; //!< condition variable for notification of new data + boost::interprocess::interprocess_condition + condition_full; //!< condition variable for notification of available space + unsigned char* dataBlock_; + const size_t dataSize; + common::CircularBufferRaw priorityData; + + public: + /** default constructor*/ + IpcBlockingPriorityQueueImpl(unsigned char* dataBlock, int blockSize); + + /** clear the queue*/ + void clear(); + + /** DISABLE_COPY_AND_ASSIGN */ + IpcBlockingPriorityQueueImpl(const IpcBlockingPriorityQueueImpl&) = delete; + IpcBlockingPriorityQueueImpl& operator=(const IpcBlockingPriorityQueueImpl&) = delete; + + /** push a data block + val the value to push on the queue + */ + void push(const unsigned char* data, int size); + + /** push a data block + val the value to push on the queue + */ + int push(std::chrono::milliseconds timeout, const unsigned char* data, int size); + + /** push an element onto the queue + val the value to push on the queue + */ + void pushPriority(const unsigned char* data, int size); + /** push an element onto the queue + @param timeout the time to wait + @param data the data to put into the queue + @param size the number of bytes of data to store + @return size if successful, 0 if not + @throws invalid_argument if the size is greater than the capacity + */ + int pushPriority(std::chrono::milliseconds timeout, + const unsigned char* data, + int size); + /** push a data block + val the value to push on the queue + */ + bool try_push(const unsigned char* data, int size); + + /** push an element onto the queue + val the value to push on the queue + */ + bool try_pushPriority(const unsigned char* data, int size); + + /** try to pop an object from the queue + @details this function does not block, will return 0 if no data could be popped + @return an integer with the size of the popped value, + */ + int try_pop(unsigned char* data, int maxSize); + + /** pop an object from the queue + @details this function will block, will return 0 if the data does not fit into the max + size + @return an integer with the size of the popped value, + */ + int pop(unsigned char* data, int maxSize); + + /** blocking call to wait on an object from the stack with timeout*/ + int pop(std::chrono::milliseconds timeout, unsigned char* data, int maxSize); + + /** check whether there are any elements in the queue + because this is meant for multi-process applications this may or may not have any meaning + depending on the number of consumers */ - int try_pop (unsigned char *data, int maxSize); - - /** pop an object from the queue - @details this function will block, will return 0 if the data does not fit into the max size - @return an integer with the size of the popped value, - */ - int pop (unsigned char *data, int maxSize); - - /** blocking call to wait on an object from the stack with timeout*/ - int pop (std::chrono::milliseconds timeout, unsigned char *data, int maxSize); - - /** check whether there are any elements in the queue - because this is meant for multi-process applications this may or may not have any meaning - depending on the number of consumers - */ - bool empty () const; - }; + bool empty() const; + }; -} // namespace detail + } // namespace detail } // namespace ipc } // namespace helics diff --git a/tests/helics/network/IpcQueueImpTests.cpp b/tests/helics/network/IpcQueueImpTests.cpp index 7bb32cdada..b97bb21888 100644 --- a/tests/helics/network/IpcQueueImpTests.cpp +++ b/tests/helics/network/IpcQueueImpTests.cpp @@ -5,9 +5,9 @@ Energy, LLC. See the top-level NOTICE for additional details. All rights reserv SPDX-License-Identifier: BSD-3-Clause */ -#include - #include "helics/core/ipc/IpcBlockingPriorityQueueImpl.hpp" + +#include #include #include #include @@ -15,222 +15,221 @@ SPDX-License-Identifier: BSD-3-Clause namespace utf = boost::unit_test; using namespace std::literals::chrono_literals; -BOOST_AUTO_TEST_SUITE (IpcQueue_tests, *utf::label ("ci")) +BOOST_AUTO_TEST_SUITE(IpcQueue_tests, *utf::label("ci")) -BOOST_AUTO_TEST_CASE (creation_test) +BOOST_AUTO_TEST_CASE(creation_test) { - std::unique_ptr memblock (new unsigned char[10192]); + std::unique_ptr memblock(new unsigned char[10192]); - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 10192); + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 10192); - std::vector data (500, 'a'); - BOOST_CHECK (queue.try_push (data.data (), 500)); + std::vector data(500, 'a'); + BOOST_CHECK(queue.try_push(data.data(), 500)); - data.assign (500, 'b'); - int res = queue.pop (data.data (), 500); - BOOST_CHECK_EQUAL (res, 500); - BOOST_CHECK_EQUAL (data[234], 'a'); - BOOST_CHECK_EQUAL (data[499], 'a'); + data.assign(500, 'b'); + int res = queue.pop(data.data(), 500); + BOOST_CHECK_EQUAL(res, 500); + BOOST_CHECK_EQUAL(data[234], 'a'); + BOOST_CHECK_EQUAL(data[499], 'a'); } -BOOST_AUTO_TEST_CASE (push_pop_tests) +BOOST_AUTO_TEST_CASE(push_pop_tests) { - std::unique_ptr memblock (new unsigned char[10192]); - - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 10192); - - std::vector data (500, 'a'); - BOOST_CHECK (queue.try_push (data.data (), 400)); // this would go into the pull - queue.push (data.data (), 401); // this goes into push - queue.push (data.data (), 402); // this goes into push - queue.push (data.data (), 403); // this goes into push - - int sz = queue.pop (data.data (), 500); // pop from pull - BOOST_CHECK_EQUAL (sz, 400); - sz = queue.pop (data.data (), 500); // pull empty, so rotate - BOOST_CHECK_EQUAL (sz, 401); // pop from pull - queue.push (data.data (), 404); // this goes into push - queue.push (data.data (), 405); // this goes into push - queue.push (data.data (), 406); // this goes into push - - sz = queue.pop (data.data (), 500); // pull empty, so rotate - BOOST_CHECK_EQUAL (sz, 402); // pop from pull - - sz = queue.pop (data.data (), 500); - BOOST_CHECK_EQUAL (sz, 403); - - sz = queue.pop (data.data (), 500); - BOOST_CHECK_EQUAL (sz, 404); - sz = queue.pop (data.data (), 500); - BOOST_CHECK_EQUAL (sz, 405); - sz = queue.pop (data.data (), 500); - BOOST_CHECK_EQUAL (sz, 406); + std::unique_ptr memblock(new unsigned char[10192]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 10192); + + std::vector data(500, 'a'); + BOOST_CHECK(queue.try_push(data.data(), 400)); // this would go into the pull + queue.push(data.data(), 401); // this goes into push + queue.push(data.data(), 402); // this goes into push + queue.push(data.data(), 403); // this goes into push + + int sz = queue.pop(data.data(), 500); // pop from pull + BOOST_CHECK_EQUAL(sz, 400); + sz = queue.pop(data.data(), 500); // pull empty, so rotate + BOOST_CHECK_EQUAL(sz, 401); // pop from pull + queue.push(data.data(), 404); // this goes into push + queue.push(data.data(), 405); // this goes into push + queue.push(data.data(), 406); // this goes into push + + sz = queue.pop(data.data(), 500); // pull empty, so rotate + BOOST_CHECK_EQUAL(sz, 402); // pop from pull + + sz = queue.pop(data.data(), 500); + BOOST_CHECK_EQUAL(sz, 403); + + sz = queue.pop(data.data(), 500); + BOOST_CHECK_EQUAL(sz, 404); + sz = queue.pop(data.data(), 500); + BOOST_CHECK_EQUAL(sz, 405); + sz = queue.pop(data.data(), 500); + BOOST_CHECK_EQUAL(sz, 406); } -BOOST_AUTO_TEST_CASE (push_pop_priority_tests) +BOOST_AUTO_TEST_CASE(push_pop_priority_tests) { - std::unique_ptr memblock (new unsigned char[10192]); - - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 10192); - - std::vector data (500, 'a'); - BOOST_CHECK (queue.try_push (data.data (), 400)); // this would go into the pull - queue.push (data.data (), 401); // this goes into push - queue.push (data.data (), 402); // this goes into push - queue.push (data.data (), 403); // this goes into push - - int sz = queue.pop (data.data (), 500); // pop from pull - BOOST_CHECK_EQUAL (sz, 400); - queue.pushPriority (data.data (), 417); - sz = queue.pop (data.data (), 500); // pull from priority - BOOST_CHECK_EQUAL (sz, 417); // pop from pull - sz = queue.pop (data.data (), 500); // pull from priority - BOOST_CHECK_EQUAL (sz, 401); // pop from pull - queue.push (data.data (), 404); // this goes into push - queue.pushPriority (data.data (), 420); // this goes into priority - queue.pushPriority (data.data (), 421); // this goes into priority - queue.push (data.data (), 405); // this goes into push - queue.push (data.data (), 406); // this goes into push - - sz = queue.pop (data.data (), 500); // pull empty, so rotate - BOOST_CHECK_EQUAL (sz, 420); // pop from priority - sz = queue.pop (data.data (), 500); // pull empty, so rotate - BOOST_CHECK_EQUAL (sz, 421); // pop from priority - - sz = queue.pop (data.data (), 500); // pull empty, so rotate - BOOST_CHECK_EQUAL (sz, 402); // pop from pull - - sz = queue.pop (data.data (), 500); - BOOST_CHECK_EQUAL (sz, 403); - - sz = queue.pop (data.data (), 500); - BOOST_CHECK_EQUAL (sz, 404); - sz = queue.pop (data.data (), 500); - BOOST_CHECK_EQUAL (sz, 405); - BOOST_CHECK (!queue.empty ()); - sz = queue.try_pop (data.data (), 500); - BOOST_CHECK_EQUAL (sz, 406); - - BOOST_CHECK (queue.empty ()); + std::unique_ptr memblock(new unsigned char[10192]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 10192); + + std::vector data(500, 'a'); + BOOST_CHECK(queue.try_push(data.data(), 400)); // this would go into the pull + queue.push(data.data(), 401); // this goes into push + queue.push(data.data(), 402); // this goes into push + queue.push(data.data(), 403); // this goes into push + + int sz = queue.pop(data.data(), 500); // pop from pull + BOOST_CHECK_EQUAL(sz, 400); + queue.pushPriority(data.data(), 417); + sz = queue.pop(data.data(), 500); // pull from priority + BOOST_CHECK_EQUAL(sz, 417); // pop from pull + sz = queue.pop(data.data(), 500); // pull from priority + BOOST_CHECK_EQUAL(sz, 401); // pop from pull + queue.push(data.data(), 404); // this goes into push + queue.pushPriority(data.data(), 420); // this goes into priority + queue.pushPriority(data.data(), 421); // this goes into priority + queue.push(data.data(), 405); // this goes into push + queue.push(data.data(), 406); // this goes into push + + sz = queue.pop(data.data(), 500); // pull empty, so rotate + BOOST_CHECK_EQUAL(sz, 420); // pop from priority + sz = queue.pop(data.data(), 500); // pull empty, so rotate + BOOST_CHECK_EQUAL(sz, 421); // pop from priority + + sz = queue.pop(data.data(), 500); // pull empty, so rotate + BOOST_CHECK_EQUAL(sz, 402); // pop from pull + + sz = queue.pop(data.data(), 500); + BOOST_CHECK_EQUAL(sz, 403); + + sz = queue.pop(data.data(), 500); + BOOST_CHECK_EQUAL(sz, 404); + sz = queue.pop(data.data(), 500); + BOOST_CHECK_EQUAL(sz, 405); + BOOST_CHECK(!queue.empty()); + sz = queue.try_pop(data.data(), 500); + BOOST_CHECK_EQUAL(sz, 406); + + BOOST_CHECK(queue.empty()); } -BOOST_AUTO_TEST_CASE (push_full) +BOOST_AUTO_TEST_CASE(push_full) { - std::unique_ptr memblock (new unsigned char[4096]); - - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 4096); - std::vector data (500, 'a'); - BOOST_CHECK (queue.try_push (data.data (), 420)); // this would go into the pull - BOOST_CHECK (queue.try_push (data.data (), 420)); // this would go into the push - BOOST_CHECK (queue.try_push (data.data (), 420)); // this would go into the push - BOOST_CHECK (queue.try_push (data.data (), 420)); // this would go into the push - - BOOST_CHECK (!queue.try_push (data.data (), 420)); // this should fail as it is full - BOOST_CHECK_EQUAL (queue.push (std::chrono::milliseconds (50), data.data (), 420), - 0); // this should return 0 as timeout + std::unique_ptr memblock(new unsigned char[4096]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); + std::vector data(500, 'a'); + BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the pull + BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the push + BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the push + BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the push + + BOOST_CHECK(!queue.try_push(data.data(), 420)); // this should fail as it is full + BOOST_CHECK_EQUAL(queue.push(std::chrono::milliseconds(50), data.data(), 420), + 0); // this should return 0 as timeout } -BOOST_AUTO_TEST_CASE (push_priority_full) +BOOST_AUTO_TEST_CASE(push_priority_full) { - std::unique_ptr memblock (new unsigned char[4096]); + std::unique_ptr memblock(new unsigned char[4096]); - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 4096); - std::vector data (500, 'a'); - BOOST_CHECK (queue.try_pushPriority (data.data (), 390)); // this would go into the pull - BOOST_CHECK (queue.try_pushPriority (data.data (), 390)); // this would go into the push + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); + std::vector data(500, 'a'); + BOOST_CHECK(queue.try_pushPriority(data.data(), 390)); // this would go into the pull + BOOST_CHECK(queue.try_pushPriority(data.data(), 390)); // this would go into the push - BOOST_CHECK (!queue.try_pushPriority (data.data (), 390)); // this should fail as it is full - BOOST_CHECK_EQUAL (queue.pushPriority (std::chrono::milliseconds (50), data.data (), 420), - 0); // this should return 0 as timeout + BOOST_CHECK(!queue.try_pushPriority(data.data(), 390)); // this should fail as it is full + BOOST_CHECK_EQUAL(queue.pushPriority(std::chrono::milliseconds(50), data.data(), 420), + 0); // this should return 0 as timeout } -BOOST_AUTO_TEST_CASE (pop_wait) +BOOST_AUTO_TEST_CASE(pop_wait) { - std::unique_ptr memblock (new unsigned char[4096]); + std::unique_ptr memblock(new unsigned char[4096]); - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 4096); - std::vector data (500, 'a'); - BOOST_CHECK_EQUAL (queue.try_pop (data.data (), 390), 0); // this would go into the pull + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); + std::vector data(500, 'a'); + BOOST_CHECK_EQUAL(queue.try_pop(data.data(), 390), 0); // this would go into the pull - BOOST_CHECK_EQUAL (queue.pop (std::chrono::milliseconds (100), data.data (), 420), - 0); // this should return 0 as timeout + BOOST_CHECK_EQUAL(queue.pop(std::chrono::milliseconds(100), data.data(), 420), + 0); // this should return 0 as timeout } -BOOST_AUTO_TEST_CASE (pop_wait2) +BOOST_AUTO_TEST_CASE(pop_wait2) { - std::unique_ptr memblock (new unsigned char[4096]); + std::unique_ptr memblock(new unsigned char[4096]); - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 4096); - std::vector data (500, 'a'); + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); + std::vector data(500, 'a'); - auto def = std::async (std::launch::async, [&]() { - std::this_thread::sleep_for (std::chrono::milliseconds (50)); - queue.push (data.data (), 300); + auto def = std::async(std::launch::async, [&]() { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + queue.push(data.data(), 300); }); - int ret = queue.pop (data.data (), 500); - BOOST_CHECK_EQUAL (ret, 300); - def.wait(); + int ret = queue.pop(data.data(), 500); + BOOST_CHECK_EQUAL(ret, 300); + def.wait(); } BOOST_AUTO_TEST_CASE(pop_wait_priority) { - std::unique_ptr memblock(new unsigned char[4096]); + std::unique_ptr memblock(new unsigned char[4096]); - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); - std::vector data(500, 'a'); + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); + std::vector data(500, 'a'); - auto def = std::async(std::launch::async, [&]() { - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - queue.pushPriority(data.data(), 300); - }); + auto def = std::async(std::launch::async, [&]() { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + queue.pushPriority(data.data(), 300); + }); - int ret = queue.pop(data.data(), 500); - BOOST_CHECK_EQUAL(ret, 300); - def.wait(); + int ret = queue.pop(data.data(), 500); + BOOST_CHECK_EQUAL(ret, 300); + def.wait(); } - BOOST_AUTO_TEST_CASE(push_wait) { - std::unique_ptr memblock(new unsigned char[4096]); - - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); - std::vector data(500, 'a'); - std::vector data2(500, 'a'); - BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the pull - BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the push - BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the push - BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the push - - BOOST_CHECK(!queue.try_push(data.data(), 420)); // this should fail as it is full - auto def = std::async(std::launch::async, [&]() { - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - queue.pop(data.data(), 500); - }); - queue.push(data.data(), 420); - def.wait(); + std::unique_ptr memblock(new unsigned char[4096]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); + std::vector data(500, 'a'); + std::vector data2(500, 'a'); + BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the pull + BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the push + BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the push + BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the push + + BOOST_CHECK(!queue.try_push(data.data(), 420)); // this should fail as it is full + auto def = std::async(std::launch::async, [&]() { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + queue.pop(data.data(), 500); + }); + queue.push(data.data(), 420); + def.wait(); } BOOST_AUTO_TEST_CASE(priority_push_wait) { - std::unique_ptr memblock(new unsigned char[4096]); + std::unique_ptr memblock(new unsigned char[4096]); - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); - std::vector data(500, 'a'); - std::vector data2(500, 'a'); + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); + std::vector data(500, 'a'); + std::vector data2(500, 'a'); - BOOST_CHECK(queue.try_pushPriority(data.data(), 390)); // this would go into the pull - BOOST_CHECK(queue.try_pushPriority(data.data(), 390)); // this would go into the push + BOOST_CHECK(queue.try_pushPriority(data.data(), 390)); // this would go into the pull + BOOST_CHECK(queue.try_pushPriority(data.data(), 390)); // this would go into the push - BOOST_CHECK(!queue.try_pushPriority(data.data(), 390)); // this should fail as it is full + BOOST_CHECK(!queue.try_pushPriority(data.data(), 390)); // this should fail as it is full - auto def = std::async(std::launch::async, [&]() { - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - queue.pop(data.data(), 500); - }); - queue.pushPriority(data.data(), 390); - def.wait(); + auto def = std::async(std::launch::async, [&]() { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + queue.pop(data.data(), 500); + }); + queue.pushPriority(data.data(), 390); + def.wait(); } /** test with single consumer/single producer*/ @@ -240,33 +239,28 @@ BOOST_AUTO_TEST_CASE(multithreaded_tests) helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 1048576); - - for (int64_t ii = 0; ii < 10'000; ++ii) - { - queue.push(reinterpret_cast(&ii),8); + for (int64_t ii = 0; ii < 10'000; ++ii) { + queue.push(reinterpret_cast(&ii), 8); } auto prod1 = [&]() { int64_t bdata; - for (int64_t jj = 10'000; jj < 1'010'000; ++jj) - { + for (int64_t jj = 10'000; jj < 1'010'000; ++jj) { bdata = jj; - queue.push(reinterpret_cast(&bdata), 8); + queue.push(reinterpret_cast(&bdata), 8); } }; auto cons = [&]() { int64_t data; - auto res = queue.try_pop(reinterpret_cast(&data),8); + auto res = queue.try_pop(reinterpret_cast(&data), 8); int64_t cnt = 0; - while ((res)) - { + while ((res)) { ++cnt; - res = queue.try_pop(reinterpret_cast(&data), 8); - if (res==0) - { // make an additional sleep period so the producer can catch up + res = queue.try_pop(reinterpret_cast(&data), 8); + if (res == 0) { // make an additional sleep period so the producer can catch up std::this_thread::sleep_for(std::chrono::milliseconds(100)); - res = queue.try_pop(reinterpret_cast(&data), 8); + res = queue.try_pop(reinterpret_cast(&data), 8); } } return cnt; @@ -284,224 +278,207 @@ BOOST_AUTO_TEST_CASE(multithreaded_tests) /** test with multiple consumer/single producer*/ BOOST_AUTO_TEST_CASE(multithreaded_tests2) { - std::unique_ptr memblock(new unsigned char[1048576]); - - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 1048576); - for (int64_t ii = 0; ii < 10'000; ++ii) - { - queue.push(reinterpret_cast(&ii), 8); - } - auto prod1 = [&]() { - int64_t bdata; - for (int64_t jj = 10'000; jj < 1'010'000; ++jj) - { - bdata = jj; - queue.push(reinterpret_cast(&bdata), 8); - } - }; - - auto cons = [&]() { - int64_t data; - auto res = queue.try_pop(reinterpret_cast(&data), 8); - int64_t cnt = 0; - while ((res)) - { - ++cnt; - res = queue.try_pop(reinterpret_cast(&data), 8); - if (res == 0) - { // make an additional sleep period so the producer can catch up - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - res = queue.try_pop(reinterpret_cast(&data), 8); - } - } - return cnt; - }; - - auto ret = std::async(std::launch::async, prod1); - - auto res1 = std::async(std::launch::async, cons); - auto res2 = std::async(std::launch::async, cons); - auto res3 = std::async(std::launch::async, cons); - ret.wait(); - auto V1 = res1.get(); - auto V2 = res2.get(); - auto V3 = res3.get(); - - BOOST_CHECK_EQUAL(V1 + V2 + V3, 1'010'000); + std::unique_ptr memblock(new unsigned char[1048576]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 1048576); + for (int64_t ii = 0; ii < 10'000; ++ii) { + queue.push(reinterpret_cast(&ii), 8); + } + auto prod1 = [&]() { + int64_t bdata; + for (int64_t jj = 10'000; jj < 1'010'000; ++jj) { + bdata = jj; + queue.push(reinterpret_cast(&bdata), 8); + } + }; + + auto cons = [&]() { + int64_t data; + auto res = queue.try_pop(reinterpret_cast(&data), 8); + int64_t cnt = 0; + while ((res)) { + ++cnt; + res = queue.try_pop(reinterpret_cast(&data), 8); + if (res == 0) { // make an additional sleep period so the producer can catch up + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + res = queue.try_pop(reinterpret_cast(&data), 8); + } + } + return cnt; + }; + + auto ret = std::async(std::launch::async, prod1); + + auto res1 = std::async(std::launch::async, cons); + auto res2 = std::async(std::launch::async, cons); + auto res3 = std::async(std::launch::async, cons); + ret.wait(); + auto V1 = res1.get(); + auto V2 = res2.get(); + auto V3 = res3.get(); + + BOOST_CHECK_EQUAL(V1 + V2 + V3, 1'010'000); } /** test with multiple producer/multiple consumer*/ BOOST_AUTO_TEST_CASE(multithreaded_tests3) { - std::unique_ptr memblock(new unsigned char[1048576]); - - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 1048576); - for (int64_t ii = 0; ii < 10'000; ++ii) - { - queue.push(reinterpret_cast(&ii), 8); - } - auto prod1 = [&]() { - int64_t bdata; - for (int64_t jj = 10'000; jj < 1'010'000; ++jj) - { - bdata = jj; - queue.push(reinterpret_cast(&bdata), 8); - } - }; - - auto cons = [&]() { - int64_t data; - auto res = queue.try_pop(reinterpret_cast(&data), 8); - int64_t cnt = 0; - while ((res)) - { - ++cnt; - res = queue.try_pop(reinterpret_cast(&data), 8); - if (res == 0) - { // make an additional sleep period so the producer can catch up - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - res = queue.try_pop(reinterpret_cast(&data), 8); - } - } - return cnt; - }; - - - auto ret1 = std::async(std::launch::async, prod1); - auto ret2 = std::async(std::launch::async, prod1); - auto ret3 = std::async(std::launch::async, prod1); - - auto res1 = std::async(std::launch::async, cons); - auto res2 = std::async(std::launch::async, cons); - auto res3 = std::async(std::launch::async, cons); - ret1.wait(); - ret2.wait(); - ret3.wait(); - auto V1 = res1.get(); - auto V2 = res2.get(); - auto V3 = res3.get(); - - BOOST_CHECK_EQUAL(V1 + V2 + V3, 3'010'000); + std::unique_ptr memblock(new unsigned char[1048576]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 1048576); + for (int64_t ii = 0; ii < 10'000; ++ii) { + queue.push(reinterpret_cast(&ii), 8); + } + auto prod1 = [&]() { + int64_t bdata; + for (int64_t jj = 10'000; jj < 1'010'000; ++jj) { + bdata = jj; + queue.push(reinterpret_cast(&bdata), 8); + } + }; + + auto cons = [&]() { + int64_t data; + auto res = queue.try_pop(reinterpret_cast(&data), 8); + int64_t cnt = 0; + while ((res)) { + ++cnt; + res = queue.try_pop(reinterpret_cast(&data), 8); + if (res == 0) { // make an additional sleep period so the producer can catch up + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + res = queue.try_pop(reinterpret_cast(&data), 8); + } + } + return cnt; + }; + + auto ret1 = std::async(std::launch::async, prod1); + auto ret2 = std::async(std::launch::async, prod1); + auto ret3 = std::async(std::launch::async, prod1); + + auto res1 = std::async(std::launch::async, cons); + auto res2 = std::async(std::launch::async, cons); + auto res3 = std::async(std::launch::async, cons); + ret1.wait(); + ret2.wait(); + ret3.wait(); + auto V1 = res1.get(); + auto V2 = res2.get(); + auto V3 = res3.get(); + + BOOST_CHECK_EQUAL(V1 + V2 + V3, 3'010'000); } /** test with multiple producer/multiple consumer*/ BOOST_AUTO_TEST_CASE(multithreaded_tests3_pop) { - std::unique_ptr memblock(new unsigned char[1048576]); - - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 1048576); - - auto prod1 = [&]() { - int64_t bdata; - for (int64_t jj = 0; jj < 1'000'000; ++jj) - { - bdata = jj; - queue.push(reinterpret_cast(&bdata), 8); - } - bdata = (-1); - queue.push(reinterpret_cast(&bdata), 8); - }; - - auto cons = [&]() { - int64_t data=0; - int64_t cnt = 0; - while (data>=0) - { - ++cnt; - queue.pop(reinterpret_cast(&data), 8); - } - return cnt; - }; - - - auto ret1 = std::async(std::launch::async, prod1); - auto ret2 = std::async(std::launch::async, prod1); - auto ret3 = std::async(std::launch::async, prod1); - - auto res1 = std::async(std::launch::async, cons); - auto res2 = std::async(std::launch::async, cons); - auto res3 = std::async(std::launch::async, cons); - ret1.wait(); - ret2.wait(); - ret3.wait(); - auto V1 = res1.get(); - auto V2 = res2.get(); - auto V3 = res3.get(); - - BOOST_CHECK_EQUAL(V1 + V2 + V3, 3'000'003); -} + std::unique_ptr memblock(new unsigned char[1048576]); + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 1048576); + + auto prod1 = [&]() { + int64_t bdata; + for (int64_t jj = 0; jj < 1'000'000; ++jj) { + bdata = jj; + queue.push(reinterpret_cast(&bdata), 8); + } + bdata = (-1); + queue.push(reinterpret_cast(&bdata), 8); + }; + + auto cons = [&]() { + int64_t data = 0; + int64_t cnt = 0; + while (data >= 0) { + ++cnt; + queue.pop(reinterpret_cast(&data), 8); + } + return cnt; + }; + + auto ret1 = std::async(std::launch::async, prod1); + auto ret2 = std::async(std::launch::async, prod1); + auto ret3 = std::async(std::launch::async, prod1); + + auto res1 = std::async(std::launch::async, cons); + auto res2 = std::async(std::launch::async, cons); + auto res3 = std::async(std::launch::async, cons); + ret1.wait(); + ret2.wait(); + ret3.wait(); + auto V1 = res1.get(); + auto V2 = res2.get(); + auto V3 = res3.get(); + + BOOST_CHECK_EQUAL(V1 + V2 + V3, 3'000'003); +} BOOST_AUTO_TEST_CASE(push_full_mem) { - std::unique_ptr memblock(new unsigned char[4096]); + std::unique_ptr memblock(new unsigned char[4096]); - std::unique_ptr memblock2(new unsigned char[4096]); + std::unique_ptr memblock2(new unsigned char[4096]); - auto *queue =new(memblock2.get()) helics::ipc::detail::IpcBlockingPriorityQueueImpl(memblock.get(), 4096); - std::vector data(500, 'a'); - BOOST_CHECK(queue->try_push(data.data(), 420)); // this would go into the pull - BOOST_CHECK(queue->try_push(data.data(), 420)); // this would go into the push - BOOST_CHECK(queue->try_push(data.data(), 420)); // this would go into the push - BOOST_CHECK(queue->try_push(data.data(), 420)); // this would go into the push + auto* queue = new (memblock2.get()) + helics::ipc::detail::IpcBlockingPriorityQueueImpl(memblock.get(), 4096); + std::vector data(500, 'a'); + BOOST_CHECK(queue->try_push(data.data(), 420)); // this would go into the pull + BOOST_CHECK(queue->try_push(data.data(), 420)); // this would go into the push + BOOST_CHECK(queue->try_push(data.data(), 420)); // this would go into the push + BOOST_CHECK(queue->try_push(data.data(), 420)); // this would go into the push - BOOST_CHECK(!queue->try_push(data.data(), 420)); // this should fail as it is full - BOOST_CHECK_EQUAL(queue->push(std::chrono::milliseconds(50), data.data(), 420), - 0); // this should return 0 as timeout + BOOST_CHECK(!queue->try_push(data.data(), 420)); // this should fail as it is full + BOOST_CHECK_EQUAL(queue->push(std::chrono::milliseconds(50), data.data(), 420), + 0); // this should return 0 as timeout } - - /** test with multiple producer/multiple consumer*/ BOOST_AUTO_TEST_CASE(multithreaded_tests3_pop_mem) { - std::unique_ptr memblock(new unsigned char[1048576]); - - std::unique_ptr memblock2(new unsigned char[4096]); - - auto *qn = new(memblock2.get()) helics::ipc::detail::IpcBlockingPriorityQueueImpl(memblock.get(), 1048576); - - auto prod1 = [&](void *data) { - auto *queue = reinterpret_cast(data); - int64_t bdata; - for (int64_t jj = 0; jj < 1'000'000; ++jj) - { - bdata = jj; - queue->push(reinterpret_cast(&bdata), 8); - } - bdata = (-1); - queue->push(reinterpret_cast(&bdata), 8); - }; - - auto cons = [&](void *mem) { - auto *queue = reinterpret_cast(mem); - int64_t data = 0; - int64_t cnt = 0; - while (data >= 0) - { - ++cnt; - queue->pop(reinterpret_cast(&data), 8); - } - return cnt; - }; - - - auto ret1 = std::async(std::launch::async, prod1, memblock2.get()); - auto ret2 = std::async(std::launch::async, prod1, memblock2.get()); - auto ret3 = std::async(std::launch::async, prod1, memblock2.get()); - - auto res1 = std::async(std::launch::async, cons, memblock2.get()); - auto res2 = std::async(std::launch::async, cons, memblock2.get()); - auto res3 = std::async(std::launch::async, cons, memblock2.get()); - ret1.wait(); - ret2.wait(); - ret3.wait(); - auto V1 = res1.get(); - auto V2 = res2.get(); - auto V3 = res3.get(); - - BOOST_CHECK_EQUAL(V1 + V2 + V3, 3'000'003); + std::unique_ptr memblock(new unsigned char[1048576]); + + std::unique_ptr memblock2(new unsigned char[4096]); + + auto* qn = new (memblock2.get()) + helics::ipc::detail::IpcBlockingPriorityQueueImpl(memblock.get(), 1048576); + + auto prod1 = [&](void* data) { + auto* queue = reinterpret_cast(data); + int64_t bdata; + for (int64_t jj = 0; jj < 1'000'000; ++jj) { + bdata = jj; + queue->push(reinterpret_cast(&bdata), 8); + } + bdata = (-1); + queue->push(reinterpret_cast(&bdata), 8); + }; + + auto cons = [&](void* mem) { + auto* queue = reinterpret_cast(mem); + int64_t data = 0; + int64_t cnt = 0; + while (data >= 0) { + ++cnt; + queue->pop(reinterpret_cast(&data), 8); + } + return cnt; + }; + auto ret1 = std::async(std::launch::async, prod1, memblock2.get()); + auto ret2 = std::async(std::launch::async, prod1, memblock2.get()); + auto ret3 = std::async(std::launch::async, prod1, memblock2.get()); + + auto res1 = std::async(std::launch::async, cons, memblock2.get()); + auto res2 = std::async(std::launch::async, cons, memblock2.get()); + auto res3 = std::async(std::launch::async, cons, memblock2.get()); + ret1.wait(); + ret2.wait(); + ret3.wait(); + auto V1 = res1.get(); + auto V2 = res2.get(); + auto V3 = res3.get(); + + BOOST_CHECK_EQUAL(V1 + V2 + V3, 3'000'003); } -BOOST_AUTO_TEST_SUITE_END () +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/helics/network/IpcQueueImplTests.cpp b/tests/helics/network/IpcQueueImplTests.cpp index 1236298228..72a94d03c8 100644 --- a/tests/helics/network/IpcQueueImplTests.cpp +++ b/tests/helics/network/IpcQueueImplTests.cpp @@ -5,9 +5,9 @@ Energy, LLC. See the top-level NOTICE for additional details. All rights reserv SPDX-License-Identifier: BSD-3-Clause */ -#include - #include "helics/core/ipc/IpcBlockingPriorityQueueImpl.hpp" + +#include #include #include #include @@ -15,222 +15,221 @@ SPDX-License-Identifier: BSD-3-Clause namespace utf = boost::unit_test; using namespace std::literals::chrono_literals; -BOOST_AUTO_TEST_SUITE (IpcQueueImpl_tests, *utf::label ("ci")) +BOOST_AUTO_TEST_SUITE(IpcQueueImpl_tests, *utf::label("ci")) -BOOST_AUTO_TEST_CASE (creation_test) +BOOST_AUTO_TEST_CASE(creation_test) { - std::unique_ptr memblock (new unsigned char[10192]); + std::unique_ptr memblock(new unsigned char[10192]); - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 10192); + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 10192); - std::vector data (500, 'a'); - BOOST_CHECK (queue.try_push (data.data (), 500)); + std::vector data(500, 'a'); + BOOST_CHECK(queue.try_push(data.data(), 500)); - data.assign (500, 'b'); - int res = queue.pop (data.data (), 500); - BOOST_CHECK_EQUAL (res, 500); - BOOST_CHECK_EQUAL (data[234], 'a'); - BOOST_CHECK_EQUAL (data[499], 'a'); + data.assign(500, 'b'); + int res = queue.pop(data.data(), 500); + BOOST_CHECK_EQUAL(res, 500); + BOOST_CHECK_EQUAL(data[234], 'a'); + BOOST_CHECK_EQUAL(data[499], 'a'); } -BOOST_AUTO_TEST_CASE (push_pop_tests) +BOOST_AUTO_TEST_CASE(push_pop_tests) { - std::unique_ptr memblock (new unsigned char[10192]); - - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 10192); - - std::vector data (500, 'a'); - BOOST_CHECK (queue.try_push (data.data (), 400)); // this would go into the pull - queue.push (data.data (), 401); // this goes into push - queue.push (data.data (), 402); // this goes into push - queue.push (data.data (), 403); // this goes into push - - int sz = queue.pop (data.data (), 500); // pop from pull - BOOST_CHECK_EQUAL (sz, 400); - sz = queue.pop (data.data (), 500); // pull empty, so rotate - BOOST_CHECK_EQUAL (sz, 401); // pop from pull - queue.push (data.data (), 404); // this goes into push - queue.push (data.data (), 405); // this goes into push - queue.push (data.data (), 406); // this goes into push - - sz = queue.pop (data.data (), 500); // pull empty, so rotate - BOOST_CHECK_EQUAL (sz, 402); // pop from pull - - sz = queue.pop (data.data (), 500); - BOOST_CHECK_EQUAL (sz, 403); - - sz = queue.pop (data.data (), 500); - BOOST_CHECK_EQUAL (sz, 404); - sz = queue.pop (data.data (), 500); - BOOST_CHECK_EQUAL (sz, 405); - sz = queue.pop (data.data (), 500); - BOOST_CHECK_EQUAL (sz, 406); + std::unique_ptr memblock(new unsigned char[10192]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 10192); + + std::vector data(500, 'a'); + BOOST_CHECK(queue.try_push(data.data(), 400)); // this would go into the pull + queue.push(data.data(), 401); // this goes into push + queue.push(data.data(), 402); // this goes into push + queue.push(data.data(), 403); // this goes into push + + int sz = queue.pop(data.data(), 500); // pop from pull + BOOST_CHECK_EQUAL(sz, 400); + sz = queue.pop(data.data(), 500); // pull empty, so rotate + BOOST_CHECK_EQUAL(sz, 401); // pop from pull + queue.push(data.data(), 404); // this goes into push + queue.push(data.data(), 405); // this goes into push + queue.push(data.data(), 406); // this goes into push + + sz = queue.pop(data.data(), 500); // pull empty, so rotate + BOOST_CHECK_EQUAL(sz, 402); // pop from pull + + sz = queue.pop(data.data(), 500); + BOOST_CHECK_EQUAL(sz, 403); + + sz = queue.pop(data.data(), 500); + BOOST_CHECK_EQUAL(sz, 404); + sz = queue.pop(data.data(), 500); + BOOST_CHECK_EQUAL(sz, 405); + sz = queue.pop(data.data(), 500); + BOOST_CHECK_EQUAL(sz, 406); } -BOOST_AUTO_TEST_CASE (push_pop_priority_tests) +BOOST_AUTO_TEST_CASE(push_pop_priority_tests) { - std::unique_ptr memblock (new unsigned char[10192]); - - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 10192); - - std::vector data (500, 'a'); - BOOST_CHECK (queue.try_push (data.data (), 400)); // this would go into the pull - queue.push (data.data (), 401); // this goes into push - queue.push (data.data (), 402); // this goes into push - queue.push (data.data (), 403); // this goes into push - - int sz = queue.pop (data.data (), 500); // pop from pull - BOOST_CHECK_EQUAL (sz, 400); - queue.pushPriority (data.data (), 417); - sz = queue.pop (data.data (), 500); // pull from priority - BOOST_CHECK_EQUAL (sz, 417); // pop from pull - sz = queue.pop (data.data (), 500); // pull from priority - BOOST_CHECK_EQUAL (sz, 401); // pop from pull - queue.push (data.data (), 404); // this goes into push - queue.pushPriority (data.data (), 420); // this goes into priority - queue.pushPriority (data.data (), 421); // this goes into priority - queue.push (data.data (), 405); // this goes into push - queue.push (data.data (), 406); // this goes into push - - sz = queue.pop (data.data (), 500); // pull empty, so rotate - BOOST_CHECK_EQUAL (sz, 420); // pop from priority - sz = queue.pop (data.data (), 500); // pull empty, so rotate - BOOST_CHECK_EQUAL (sz, 421); // pop from priority - - sz = queue.pop (data.data (), 500); // pull empty, so rotate - BOOST_CHECK_EQUAL (sz, 402); // pop from pull - - sz = queue.pop (data.data (), 500); - BOOST_CHECK_EQUAL (sz, 403); - - sz = queue.pop (data.data (), 500); - BOOST_CHECK_EQUAL (sz, 404); - sz = queue.pop (data.data (), 500); - BOOST_CHECK_EQUAL (sz, 405); - BOOST_CHECK (!queue.empty ()); - sz = queue.try_pop (data.data (), 500); - BOOST_CHECK_EQUAL (sz, 406); - - BOOST_CHECK (queue.empty ()); + std::unique_ptr memblock(new unsigned char[10192]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 10192); + + std::vector data(500, 'a'); + BOOST_CHECK(queue.try_push(data.data(), 400)); // this would go into the pull + queue.push(data.data(), 401); // this goes into push + queue.push(data.data(), 402); // this goes into push + queue.push(data.data(), 403); // this goes into push + + int sz = queue.pop(data.data(), 500); // pop from pull + BOOST_CHECK_EQUAL(sz, 400); + queue.pushPriority(data.data(), 417); + sz = queue.pop(data.data(), 500); // pull from priority + BOOST_CHECK_EQUAL(sz, 417); // pop from pull + sz = queue.pop(data.data(), 500); // pull from priority + BOOST_CHECK_EQUAL(sz, 401); // pop from pull + queue.push(data.data(), 404); // this goes into push + queue.pushPriority(data.data(), 420); // this goes into priority + queue.pushPriority(data.data(), 421); // this goes into priority + queue.push(data.data(), 405); // this goes into push + queue.push(data.data(), 406); // this goes into push + + sz = queue.pop(data.data(), 500); // pull empty, so rotate + BOOST_CHECK_EQUAL(sz, 420); // pop from priority + sz = queue.pop(data.data(), 500); // pull empty, so rotate + BOOST_CHECK_EQUAL(sz, 421); // pop from priority + + sz = queue.pop(data.data(), 500); // pull empty, so rotate + BOOST_CHECK_EQUAL(sz, 402); // pop from pull + + sz = queue.pop(data.data(), 500); + BOOST_CHECK_EQUAL(sz, 403); + + sz = queue.pop(data.data(), 500); + BOOST_CHECK_EQUAL(sz, 404); + sz = queue.pop(data.data(), 500); + BOOST_CHECK_EQUAL(sz, 405); + BOOST_CHECK(!queue.empty()); + sz = queue.try_pop(data.data(), 500); + BOOST_CHECK_EQUAL(sz, 406); + + BOOST_CHECK(queue.empty()); } -BOOST_AUTO_TEST_CASE (push_full) +BOOST_AUTO_TEST_CASE(push_full) { - std::unique_ptr memblock (new unsigned char[4096]); - - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 4096); - std::vector data (500, 'a'); - BOOST_CHECK (queue.try_push (data.data (), 420)); // this would go into the pull - BOOST_CHECK (queue.try_push (data.data (), 420)); // this would go into the push - BOOST_CHECK (queue.try_push (data.data (), 420)); // this would go into the push - BOOST_CHECK (queue.try_push (data.data (), 420)); // this would go into the push - - BOOST_CHECK (!queue.try_push (data.data (), 420)); // this should fail as it is full - BOOST_CHECK_EQUAL (queue.push (std::chrono::milliseconds (50), data.data (), 420), - 0); // this should return 0 as timeout + std::unique_ptr memblock(new unsigned char[4096]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); + std::vector data(500, 'a'); + BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the pull + BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the push + BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the push + BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the push + + BOOST_CHECK(!queue.try_push(data.data(), 420)); // this should fail as it is full + BOOST_CHECK_EQUAL(queue.push(std::chrono::milliseconds(50), data.data(), 420), + 0); // this should return 0 as timeout } -BOOST_AUTO_TEST_CASE (push_priority_full) +BOOST_AUTO_TEST_CASE(push_priority_full) { - std::unique_ptr memblock (new unsigned char[4096]); + std::unique_ptr memblock(new unsigned char[4096]); - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 4096); - std::vector data (500, 'a'); - BOOST_CHECK (queue.try_pushPriority (data.data (), 390)); // this would go into the pull - BOOST_CHECK (queue.try_pushPriority (data.data (), 390)); // this would go into the push + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); + std::vector data(500, 'a'); + BOOST_CHECK(queue.try_pushPriority(data.data(), 390)); // this would go into the pull + BOOST_CHECK(queue.try_pushPriority(data.data(), 390)); // this would go into the push - BOOST_CHECK (!queue.try_pushPriority (data.data (), 390)); // this should fail as it is full - BOOST_CHECK_EQUAL (queue.pushPriority (std::chrono::milliseconds (50), data.data (), 420), - 0); // this should return 0 as timeout + BOOST_CHECK(!queue.try_pushPriority(data.data(), 390)); // this should fail as it is full + BOOST_CHECK_EQUAL(queue.pushPriority(std::chrono::milliseconds(50), data.data(), 420), + 0); // this should return 0 as timeout } -BOOST_AUTO_TEST_CASE (pop_wait) +BOOST_AUTO_TEST_CASE(pop_wait) { - std::unique_ptr memblock (new unsigned char[4096]); + std::unique_ptr memblock(new unsigned char[4096]); - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 4096); - std::vector data (500, 'a'); - BOOST_CHECK_EQUAL (queue.try_pop (data.data (), 390), 0); // this would go into the pull + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); + std::vector data(500, 'a'); + BOOST_CHECK_EQUAL(queue.try_pop(data.data(), 390), 0); // this would go into the pull - BOOST_CHECK_EQUAL (queue.pop (std::chrono::milliseconds (100), data.data (), 420), - 0); // this should return 0 as timeout + BOOST_CHECK_EQUAL(queue.pop(std::chrono::milliseconds(100), data.data(), 420), + 0); // this should return 0 as timeout } -BOOST_AUTO_TEST_CASE (pop_wait2) +BOOST_AUTO_TEST_CASE(pop_wait2) { - std::unique_ptr memblock (new unsigned char[4096]); + std::unique_ptr memblock(new unsigned char[4096]); - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 4096); - std::vector data (500, 'a'); + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); + std::vector data(500, 'a'); - auto def = std::async (std::launch::async, [&]() { - std::this_thread::sleep_for (std::chrono::milliseconds (50)); - queue.push (data.data (), 300); + auto def = std::async(std::launch::async, [&]() { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + queue.push(data.data(), 300); }); - int ret = queue.pop (data.data (), 500); - BOOST_CHECK_EQUAL (ret, 300); - def.wait(); + int ret = queue.pop(data.data(), 500); + BOOST_CHECK_EQUAL(ret, 300); + def.wait(); } BOOST_AUTO_TEST_CASE(pop_wait_priority) { - std::unique_ptr memblock(new unsigned char[4096]); + std::unique_ptr memblock(new unsigned char[4096]); - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); - std::vector data(500, 'a'); + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); + std::vector data(500, 'a'); - auto def = std::async(std::launch::async, [&]() { - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - queue.pushPriority(data.data(), 300); - }); + auto def = std::async(std::launch::async, [&]() { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + queue.pushPriority(data.data(), 300); + }); - int ret = queue.pop(data.data(), 500); - BOOST_CHECK_EQUAL(ret, 300); - def.wait(); + int ret = queue.pop(data.data(), 500); + BOOST_CHECK_EQUAL(ret, 300); + def.wait(); } - BOOST_AUTO_TEST_CASE(push_wait) { - std::unique_ptr memblock(new unsigned char[4096]); - - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); - std::vector data(500, 'a'); - std::vector data2(500, 'a'); - BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the pull - BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the push - BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the push - BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the push - - BOOST_CHECK(!queue.try_push(data.data(), 420)); // this should fail as it is full - auto def = std::async(std::launch::async, [&]() { - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - queue.pop(data.data(), 500); - }); - queue.push(data.data(), 420); - def.wait(); + std::unique_ptr memblock(new unsigned char[4096]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); + std::vector data(500, 'a'); + std::vector data2(500, 'a'); + BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the pull + BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the push + BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the push + BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the push + + BOOST_CHECK(!queue.try_push(data.data(), 420)); // this should fail as it is full + auto def = std::async(std::launch::async, [&]() { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + queue.pop(data.data(), 500); + }); + queue.push(data.data(), 420); + def.wait(); } BOOST_AUTO_TEST_CASE(priority_push_wait) { - std::unique_ptr memblock(new unsigned char[4096]); + std::unique_ptr memblock(new unsigned char[4096]); - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); - std::vector data(500, 'a'); - std::vector data2(500, 'a'); + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); + std::vector data(500, 'a'); + std::vector data2(500, 'a'); - BOOST_CHECK(queue.try_pushPriority(data.data(), 390)); // this would go into the pull - BOOST_CHECK(queue.try_pushPriority(data.data(), 390)); // this would go into the push + BOOST_CHECK(queue.try_pushPriority(data.data(), 390)); // this would go into the pull + BOOST_CHECK(queue.try_pushPriority(data.data(), 390)); // this would go into the push - BOOST_CHECK(!queue.try_pushPriority(data.data(), 390)); // this should fail as it is full + BOOST_CHECK(!queue.try_pushPriority(data.data(), 390)); // this should fail as it is full - auto def = std::async(std::launch::async, [&]() { - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - queue.pop(data.data(), 500); - }); - queue.pushPriority(data.data(), 390); - def.wait(); + auto def = std::async(std::launch::async, [&]() { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + queue.pop(data.data(), 500); + }); + queue.pushPriority(data.data(), 390); + def.wait(); } /** test with single consumer/single producer*/ @@ -240,33 +239,28 @@ BOOST_AUTO_TEST_CASE(multithreaded_tests) helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 1048576); - - for (int64_t ii = 0; ii < 10'000; ++ii) - { - queue.push(reinterpret_cast(&ii),8); + for (int64_t ii = 0; ii < 10'000; ++ii) { + queue.push(reinterpret_cast(&ii), 8); } auto prod1 = [&]() { int64_t bdata; - for (int64_t jj = 10'000; jj < 1'010'000; ++jj) - { + for (int64_t jj = 10'000; jj < 1'010'000; ++jj) { bdata = jj; - queue.push(reinterpret_cast(&bdata), 8); + queue.push(reinterpret_cast(&bdata), 8); } }; auto cons = [&]() { int64_t data; - auto res = queue.try_pop(reinterpret_cast(&data),8); + auto res = queue.try_pop(reinterpret_cast(&data), 8); int64_t cnt = 0; - while ((res)) - { + while ((res)) { ++cnt; - res = queue.try_pop(reinterpret_cast(&data), 8); - if (res==0) - { // make an additional sleep period so the producer can catch up + res = queue.try_pop(reinterpret_cast(&data), 8); + if (res == 0) { // make an additional sleep period so the producer can catch up std::this_thread::sleep_for(std::chrono::milliseconds(100)); - res = queue.try_pop(reinterpret_cast(&data), 8); + res = queue.try_pop(reinterpret_cast(&data), 8); } } return cnt; @@ -284,224 +278,207 @@ BOOST_AUTO_TEST_CASE(multithreaded_tests) /** test with multiple consumer/single producer*/ BOOST_AUTO_TEST_CASE(multithreaded_tests2) { - std::unique_ptr memblock(new unsigned char[1048576]); - - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 1048576); - for (int64_t ii = 0; ii < 10'000; ++ii) - { - queue.push(reinterpret_cast(&ii), 8); - } - auto prod1 = [&]() { - int64_t bdata; - for (int64_t jj = 10'000; jj < 1'010'000; ++jj) - { - bdata = jj; - queue.push(reinterpret_cast(&bdata), 8); - } - }; - - auto cons = [&]() { - int64_t data; - auto res = queue.try_pop(reinterpret_cast(&data), 8); - int64_t cnt = 0; - while ((res)) - { - ++cnt; - res = queue.try_pop(reinterpret_cast(&data), 8); - if (res == 0) - { // make an additional sleep period so the producer can catch up - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - res = queue.try_pop(reinterpret_cast(&data), 8); - } - } - return cnt; - }; - - auto ret = std::async(std::launch::async, prod1); - - auto res1 = std::async(std::launch::async, cons); - auto res2 = std::async(std::launch::async, cons); - auto res3 = std::async(std::launch::async, cons); - ret.wait(); - auto V1 = res1.get(); - auto V2 = res2.get(); - auto V3 = res3.get(); - - BOOST_CHECK_EQUAL(V1 + V2 + V3, 1'010'000); + std::unique_ptr memblock(new unsigned char[1048576]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 1048576); + for (int64_t ii = 0; ii < 10'000; ++ii) { + queue.push(reinterpret_cast(&ii), 8); + } + auto prod1 = [&]() { + int64_t bdata; + for (int64_t jj = 10'000; jj < 1'010'000; ++jj) { + bdata = jj; + queue.push(reinterpret_cast(&bdata), 8); + } + }; + + auto cons = [&]() { + int64_t data; + auto res = queue.try_pop(reinterpret_cast(&data), 8); + int64_t cnt = 0; + while ((res)) { + ++cnt; + res = queue.try_pop(reinterpret_cast(&data), 8); + if (res == 0) { // make an additional sleep period so the producer can catch up + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + res = queue.try_pop(reinterpret_cast(&data), 8); + } + } + return cnt; + }; + + auto ret = std::async(std::launch::async, prod1); + + auto res1 = std::async(std::launch::async, cons); + auto res2 = std::async(std::launch::async, cons); + auto res3 = std::async(std::launch::async, cons); + ret.wait(); + auto V1 = res1.get(); + auto V2 = res2.get(); + auto V3 = res3.get(); + + BOOST_CHECK_EQUAL(V1 + V2 + V3, 1'010'000); } /** test with multiple producer/multiple consumer*/ BOOST_AUTO_TEST_CASE(multithreaded_tests3) { - std::unique_ptr memblock(new unsigned char[1048576]); - - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 1048576); - for (int64_t ii = 0; ii < 10'000; ++ii) - { - queue.push(reinterpret_cast(&ii), 8); - } - auto prod1 = [&]() { - int64_t bdata; - for (int64_t jj = 10'000; jj < 1'010'000; ++jj) - { - bdata = jj; - queue.push(reinterpret_cast(&bdata), 8); - } - }; - - auto cons = [&]() { - int64_t data; - auto res = queue.try_pop(reinterpret_cast(&data), 8); - int64_t cnt = 0; - while ((res)) - { - ++cnt; - res = queue.try_pop(reinterpret_cast(&data), 8); - if (res == 0) - { // make an additional sleep period so the producer can catch up - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - res = queue.try_pop(reinterpret_cast(&data), 8); - } - } - return cnt; - }; - - - auto ret1 = std::async(std::launch::async, prod1); - auto ret2 = std::async(std::launch::async, prod1); - auto ret3 = std::async(std::launch::async, prod1); - - auto res1 = std::async(std::launch::async, cons); - auto res2 = std::async(std::launch::async, cons); - auto res3 = std::async(std::launch::async, cons); - ret1.wait(); - ret2.wait(); - ret3.wait(); - auto V1 = res1.get(); - auto V2 = res2.get(); - auto V3 = res3.get(); - - BOOST_CHECK_EQUAL(V1 + V2 + V3, 3'010'000); + std::unique_ptr memblock(new unsigned char[1048576]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 1048576); + for (int64_t ii = 0; ii < 10'000; ++ii) { + queue.push(reinterpret_cast(&ii), 8); + } + auto prod1 = [&]() { + int64_t bdata; + for (int64_t jj = 10'000; jj < 1'010'000; ++jj) { + bdata = jj; + queue.push(reinterpret_cast(&bdata), 8); + } + }; + + auto cons = [&]() { + int64_t data; + auto res = queue.try_pop(reinterpret_cast(&data), 8); + int64_t cnt = 0; + while ((res)) { + ++cnt; + res = queue.try_pop(reinterpret_cast(&data), 8); + if (res == 0) { // make an additional sleep period so the producer can catch up + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + res = queue.try_pop(reinterpret_cast(&data), 8); + } + } + return cnt; + }; + + auto ret1 = std::async(std::launch::async, prod1); + auto ret2 = std::async(std::launch::async, prod1); + auto ret3 = std::async(std::launch::async, prod1); + + auto res1 = std::async(std::launch::async, cons); + auto res2 = std::async(std::launch::async, cons); + auto res3 = std::async(std::launch::async, cons); + ret1.wait(); + ret2.wait(); + ret3.wait(); + auto V1 = res1.get(); + auto V2 = res2.get(); + auto V3 = res3.get(); + + BOOST_CHECK_EQUAL(V1 + V2 + V3, 3'010'000); } /** test with multiple producer/multiple consumer*/ BOOST_AUTO_TEST_CASE(multithreaded_tests3_pop) { - std::unique_ptr memblock(new unsigned char[1048576]); - - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 1048576); - - auto prod1 = [&]() { - int64_t bdata; - for (int64_t jj = 0; jj < 1'000'000; ++jj) - { - bdata = jj; - queue.push(reinterpret_cast(&bdata), 8); - } - bdata = (-1); - queue.push(reinterpret_cast(&bdata), 8); - }; - - auto cons = [&]() { - int64_t data=0; - int64_t cnt = 0; - while (data>=0) - { - ++cnt; - queue.pop(reinterpret_cast(&data), 8); - } - return cnt; - }; - - - auto ret1 = std::async(std::launch::async, prod1); - auto ret2 = std::async(std::launch::async, prod1); - auto ret3 = std::async(std::launch::async, prod1); - - auto res1 = std::async(std::launch::async, cons); - auto res2 = std::async(std::launch::async, cons); - auto res3 = std::async(std::launch::async, cons); - ret1.wait(); - ret2.wait(); - ret3.wait(); - auto V1 = res1.get(); - auto V2 = res2.get(); - auto V3 = res3.get(); - - BOOST_CHECK_EQUAL(V1 + V2 + V3, 3'000'003); -} + std::unique_ptr memblock(new unsigned char[1048576]); + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 1048576); + + auto prod1 = [&]() { + int64_t bdata; + for (int64_t jj = 0; jj < 1'000'000; ++jj) { + bdata = jj; + queue.push(reinterpret_cast(&bdata), 8); + } + bdata = (-1); + queue.push(reinterpret_cast(&bdata), 8); + }; + + auto cons = [&]() { + int64_t data = 0; + int64_t cnt = 0; + while (data >= 0) { + ++cnt; + queue.pop(reinterpret_cast(&data), 8); + } + return cnt; + }; + + auto ret1 = std::async(std::launch::async, prod1); + auto ret2 = std::async(std::launch::async, prod1); + auto ret3 = std::async(std::launch::async, prod1); + + auto res1 = std::async(std::launch::async, cons); + auto res2 = std::async(std::launch::async, cons); + auto res3 = std::async(std::launch::async, cons); + ret1.wait(); + ret2.wait(); + ret3.wait(); + auto V1 = res1.get(); + auto V2 = res2.get(); + auto V3 = res3.get(); + + BOOST_CHECK_EQUAL(V1 + V2 + V3, 3'000'003); +} BOOST_AUTO_TEST_CASE(push_full_mem) { - std::unique_ptr memblock(new unsigned char[4096]); + std::unique_ptr memblock(new unsigned char[4096]); - std::unique_ptr memblock2(new unsigned char[4096]); + std::unique_ptr memblock2(new unsigned char[4096]); - auto *queue =new(memblock2.get()) helics::ipc::detail::IpcBlockingPriorityQueueImpl(memblock.get(), 4096); - std::vector data(500, 'a'); - BOOST_CHECK(queue->try_push(data.data(), 420)); // this would go into the pull - BOOST_CHECK(queue->try_push(data.data(), 420)); // this would go into the push - BOOST_CHECK(queue->try_push(data.data(), 420)); // this would go into the push - BOOST_CHECK(queue->try_push(data.data(), 420)); // this would go into the push + auto* queue = new (memblock2.get()) + helics::ipc::detail::IpcBlockingPriorityQueueImpl(memblock.get(), 4096); + std::vector data(500, 'a'); + BOOST_CHECK(queue->try_push(data.data(), 420)); // this would go into the pull + BOOST_CHECK(queue->try_push(data.data(), 420)); // this would go into the push + BOOST_CHECK(queue->try_push(data.data(), 420)); // this would go into the push + BOOST_CHECK(queue->try_push(data.data(), 420)); // this would go into the push - BOOST_CHECK(!queue->try_push(data.data(), 420)); // this should fail as it is full - BOOST_CHECK_EQUAL(queue->push(std::chrono::milliseconds(50), data.data(), 420), - 0); // this should return 0 as timeout + BOOST_CHECK(!queue->try_push(data.data(), 420)); // this should fail as it is full + BOOST_CHECK_EQUAL(queue->push(std::chrono::milliseconds(50), data.data(), 420), + 0); // this should return 0 as timeout } - - /** test with multiple producer/multiple consumer*/ BOOST_AUTO_TEST_CASE(multithreaded_tests3_pop_mem) { - std::unique_ptr memblock(new unsigned char[1048576]); - - std::unique_ptr memblock2(new unsigned char[4096]); - - auto *qn = new(memblock2.get()) helics::ipc::detail::IpcBlockingPriorityQueueImpl(memblock.get(), 1048576); - - auto prod1 = [&](void *data) { - auto *queue = reinterpret_cast(data); - int64_t bdata; - for (int64_t jj = 0; jj < 1'000'000; ++jj) - { - bdata = jj; - queue->push(reinterpret_cast(&bdata), 8); - } - bdata = (-1); - queue->push(reinterpret_cast(&bdata), 8); - }; - - auto cons = [&](void *mem) { - auto *queue = reinterpret_cast(mem); - int64_t data = 0; - int64_t cnt = 0; - while (data >= 0) - { - ++cnt; - queue->pop(reinterpret_cast(&data), 8); - } - return cnt; - }; - - - auto ret1 = std::async(std::launch::async, prod1, memblock2.get()); - auto ret2 = std::async(std::launch::async, prod1, memblock2.get()); - auto ret3 = std::async(std::launch::async, prod1, memblock2.get()); - - auto res1 = std::async(std::launch::async, cons, memblock2.get()); - auto res2 = std::async(std::launch::async, cons, memblock2.get()); - auto res3 = std::async(std::launch::async, cons, memblock2.get()); - ret1.wait(); - ret2.wait(); - ret3.wait(); - auto V1 = res1.get(); - auto V2 = res2.get(); - auto V3 = res3.get(); - - BOOST_CHECK_EQUAL(V1 + V2 + V3, 3'000'003); + std::unique_ptr memblock(new unsigned char[1048576]); + + std::unique_ptr memblock2(new unsigned char[4096]); + + auto* qn = new (memblock2.get()) + helics::ipc::detail::IpcBlockingPriorityQueueImpl(memblock.get(), 1048576); + + auto prod1 = [&](void* data) { + auto* queue = reinterpret_cast(data); + int64_t bdata; + for (int64_t jj = 0; jj < 1'000'000; ++jj) { + bdata = jj; + queue->push(reinterpret_cast(&bdata), 8); + } + bdata = (-1); + queue->push(reinterpret_cast(&bdata), 8); + }; + + auto cons = [&](void* mem) { + auto* queue = reinterpret_cast(mem); + int64_t data = 0; + int64_t cnt = 0; + while (data >= 0) { + ++cnt; + queue->pop(reinterpret_cast(&data), 8); + } + return cnt; + }; + auto ret1 = std::async(std::launch::async, prod1, memblock2.get()); + auto ret2 = std::async(std::launch::async, prod1, memblock2.get()); + auto ret3 = std::async(std::launch::async, prod1, memblock2.get()); + + auto res1 = std::async(std::launch::async, cons, memblock2.get()); + auto res2 = std::async(std::launch::async, cons, memblock2.get()); + auto res3 = std::async(std::launch::async, cons, memblock2.get()); + ret1.wait(); + ret2.wait(); + ret3.wait(); + auto V1 = res1.get(); + auto V2 = res2.get(); + auto V3 = res3.get(); + + BOOST_CHECK_EQUAL(V1 + V2 + V3, 3'000'003); } -BOOST_AUTO_TEST_SUITE_END () +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/helics/network/IpcQueueTests.cpp b/tests/helics/network/IpcQueueTests.cpp index 7bb32cdada..b97bb21888 100644 --- a/tests/helics/network/IpcQueueTests.cpp +++ b/tests/helics/network/IpcQueueTests.cpp @@ -5,9 +5,9 @@ Energy, LLC. See the top-level NOTICE for additional details. All rights reserv SPDX-License-Identifier: BSD-3-Clause */ -#include - #include "helics/core/ipc/IpcBlockingPriorityQueueImpl.hpp" + +#include #include #include #include @@ -15,222 +15,221 @@ SPDX-License-Identifier: BSD-3-Clause namespace utf = boost::unit_test; using namespace std::literals::chrono_literals; -BOOST_AUTO_TEST_SUITE (IpcQueue_tests, *utf::label ("ci")) +BOOST_AUTO_TEST_SUITE(IpcQueue_tests, *utf::label("ci")) -BOOST_AUTO_TEST_CASE (creation_test) +BOOST_AUTO_TEST_CASE(creation_test) { - std::unique_ptr memblock (new unsigned char[10192]); + std::unique_ptr memblock(new unsigned char[10192]); - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 10192); + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 10192); - std::vector data (500, 'a'); - BOOST_CHECK (queue.try_push (data.data (), 500)); + std::vector data(500, 'a'); + BOOST_CHECK(queue.try_push(data.data(), 500)); - data.assign (500, 'b'); - int res = queue.pop (data.data (), 500); - BOOST_CHECK_EQUAL (res, 500); - BOOST_CHECK_EQUAL (data[234], 'a'); - BOOST_CHECK_EQUAL (data[499], 'a'); + data.assign(500, 'b'); + int res = queue.pop(data.data(), 500); + BOOST_CHECK_EQUAL(res, 500); + BOOST_CHECK_EQUAL(data[234], 'a'); + BOOST_CHECK_EQUAL(data[499], 'a'); } -BOOST_AUTO_TEST_CASE (push_pop_tests) +BOOST_AUTO_TEST_CASE(push_pop_tests) { - std::unique_ptr memblock (new unsigned char[10192]); - - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 10192); - - std::vector data (500, 'a'); - BOOST_CHECK (queue.try_push (data.data (), 400)); // this would go into the pull - queue.push (data.data (), 401); // this goes into push - queue.push (data.data (), 402); // this goes into push - queue.push (data.data (), 403); // this goes into push - - int sz = queue.pop (data.data (), 500); // pop from pull - BOOST_CHECK_EQUAL (sz, 400); - sz = queue.pop (data.data (), 500); // pull empty, so rotate - BOOST_CHECK_EQUAL (sz, 401); // pop from pull - queue.push (data.data (), 404); // this goes into push - queue.push (data.data (), 405); // this goes into push - queue.push (data.data (), 406); // this goes into push - - sz = queue.pop (data.data (), 500); // pull empty, so rotate - BOOST_CHECK_EQUAL (sz, 402); // pop from pull - - sz = queue.pop (data.data (), 500); - BOOST_CHECK_EQUAL (sz, 403); - - sz = queue.pop (data.data (), 500); - BOOST_CHECK_EQUAL (sz, 404); - sz = queue.pop (data.data (), 500); - BOOST_CHECK_EQUAL (sz, 405); - sz = queue.pop (data.data (), 500); - BOOST_CHECK_EQUAL (sz, 406); + std::unique_ptr memblock(new unsigned char[10192]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 10192); + + std::vector data(500, 'a'); + BOOST_CHECK(queue.try_push(data.data(), 400)); // this would go into the pull + queue.push(data.data(), 401); // this goes into push + queue.push(data.data(), 402); // this goes into push + queue.push(data.data(), 403); // this goes into push + + int sz = queue.pop(data.data(), 500); // pop from pull + BOOST_CHECK_EQUAL(sz, 400); + sz = queue.pop(data.data(), 500); // pull empty, so rotate + BOOST_CHECK_EQUAL(sz, 401); // pop from pull + queue.push(data.data(), 404); // this goes into push + queue.push(data.data(), 405); // this goes into push + queue.push(data.data(), 406); // this goes into push + + sz = queue.pop(data.data(), 500); // pull empty, so rotate + BOOST_CHECK_EQUAL(sz, 402); // pop from pull + + sz = queue.pop(data.data(), 500); + BOOST_CHECK_EQUAL(sz, 403); + + sz = queue.pop(data.data(), 500); + BOOST_CHECK_EQUAL(sz, 404); + sz = queue.pop(data.data(), 500); + BOOST_CHECK_EQUAL(sz, 405); + sz = queue.pop(data.data(), 500); + BOOST_CHECK_EQUAL(sz, 406); } -BOOST_AUTO_TEST_CASE (push_pop_priority_tests) +BOOST_AUTO_TEST_CASE(push_pop_priority_tests) { - std::unique_ptr memblock (new unsigned char[10192]); - - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 10192); - - std::vector data (500, 'a'); - BOOST_CHECK (queue.try_push (data.data (), 400)); // this would go into the pull - queue.push (data.data (), 401); // this goes into push - queue.push (data.data (), 402); // this goes into push - queue.push (data.data (), 403); // this goes into push - - int sz = queue.pop (data.data (), 500); // pop from pull - BOOST_CHECK_EQUAL (sz, 400); - queue.pushPriority (data.data (), 417); - sz = queue.pop (data.data (), 500); // pull from priority - BOOST_CHECK_EQUAL (sz, 417); // pop from pull - sz = queue.pop (data.data (), 500); // pull from priority - BOOST_CHECK_EQUAL (sz, 401); // pop from pull - queue.push (data.data (), 404); // this goes into push - queue.pushPriority (data.data (), 420); // this goes into priority - queue.pushPriority (data.data (), 421); // this goes into priority - queue.push (data.data (), 405); // this goes into push - queue.push (data.data (), 406); // this goes into push - - sz = queue.pop (data.data (), 500); // pull empty, so rotate - BOOST_CHECK_EQUAL (sz, 420); // pop from priority - sz = queue.pop (data.data (), 500); // pull empty, so rotate - BOOST_CHECK_EQUAL (sz, 421); // pop from priority - - sz = queue.pop (data.data (), 500); // pull empty, so rotate - BOOST_CHECK_EQUAL (sz, 402); // pop from pull - - sz = queue.pop (data.data (), 500); - BOOST_CHECK_EQUAL (sz, 403); - - sz = queue.pop (data.data (), 500); - BOOST_CHECK_EQUAL (sz, 404); - sz = queue.pop (data.data (), 500); - BOOST_CHECK_EQUAL (sz, 405); - BOOST_CHECK (!queue.empty ()); - sz = queue.try_pop (data.data (), 500); - BOOST_CHECK_EQUAL (sz, 406); - - BOOST_CHECK (queue.empty ()); + std::unique_ptr memblock(new unsigned char[10192]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 10192); + + std::vector data(500, 'a'); + BOOST_CHECK(queue.try_push(data.data(), 400)); // this would go into the pull + queue.push(data.data(), 401); // this goes into push + queue.push(data.data(), 402); // this goes into push + queue.push(data.data(), 403); // this goes into push + + int sz = queue.pop(data.data(), 500); // pop from pull + BOOST_CHECK_EQUAL(sz, 400); + queue.pushPriority(data.data(), 417); + sz = queue.pop(data.data(), 500); // pull from priority + BOOST_CHECK_EQUAL(sz, 417); // pop from pull + sz = queue.pop(data.data(), 500); // pull from priority + BOOST_CHECK_EQUAL(sz, 401); // pop from pull + queue.push(data.data(), 404); // this goes into push + queue.pushPriority(data.data(), 420); // this goes into priority + queue.pushPriority(data.data(), 421); // this goes into priority + queue.push(data.data(), 405); // this goes into push + queue.push(data.data(), 406); // this goes into push + + sz = queue.pop(data.data(), 500); // pull empty, so rotate + BOOST_CHECK_EQUAL(sz, 420); // pop from priority + sz = queue.pop(data.data(), 500); // pull empty, so rotate + BOOST_CHECK_EQUAL(sz, 421); // pop from priority + + sz = queue.pop(data.data(), 500); // pull empty, so rotate + BOOST_CHECK_EQUAL(sz, 402); // pop from pull + + sz = queue.pop(data.data(), 500); + BOOST_CHECK_EQUAL(sz, 403); + + sz = queue.pop(data.data(), 500); + BOOST_CHECK_EQUAL(sz, 404); + sz = queue.pop(data.data(), 500); + BOOST_CHECK_EQUAL(sz, 405); + BOOST_CHECK(!queue.empty()); + sz = queue.try_pop(data.data(), 500); + BOOST_CHECK_EQUAL(sz, 406); + + BOOST_CHECK(queue.empty()); } -BOOST_AUTO_TEST_CASE (push_full) +BOOST_AUTO_TEST_CASE(push_full) { - std::unique_ptr memblock (new unsigned char[4096]); - - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 4096); - std::vector data (500, 'a'); - BOOST_CHECK (queue.try_push (data.data (), 420)); // this would go into the pull - BOOST_CHECK (queue.try_push (data.data (), 420)); // this would go into the push - BOOST_CHECK (queue.try_push (data.data (), 420)); // this would go into the push - BOOST_CHECK (queue.try_push (data.data (), 420)); // this would go into the push - - BOOST_CHECK (!queue.try_push (data.data (), 420)); // this should fail as it is full - BOOST_CHECK_EQUAL (queue.push (std::chrono::milliseconds (50), data.data (), 420), - 0); // this should return 0 as timeout + std::unique_ptr memblock(new unsigned char[4096]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); + std::vector data(500, 'a'); + BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the pull + BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the push + BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the push + BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the push + + BOOST_CHECK(!queue.try_push(data.data(), 420)); // this should fail as it is full + BOOST_CHECK_EQUAL(queue.push(std::chrono::milliseconds(50), data.data(), 420), + 0); // this should return 0 as timeout } -BOOST_AUTO_TEST_CASE (push_priority_full) +BOOST_AUTO_TEST_CASE(push_priority_full) { - std::unique_ptr memblock (new unsigned char[4096]); + std::unique_ptr memblock(new unsigned char[4096]); - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 4096); - std::vector data (500, 'a'); - BOOST_CHECK (queue.try_pushPriority (data.data (), 390)); // this would go into the pull - BOOST_CHECK (queue.try_pushPriority (data.data (), 390)); // this would go into the push + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); + std::vector data(500, 'a'); + BOOST_CHECK(queue.try_pushPriority(data.data(), 390)); // this would go into the pull + BOOST_CHECK(queue.try_pushPriority(data.data(), 390)); // this would go into the push - BOOST_CHECK (!queue.try_pushPriority (data.data (), 390)); // this should fail as it is full - BOOST_CHECK_EQUAL (queue.pushPriority (std::chrono::milliseconds (50), data.data (), 420), - 0); // this should return 0 as timeout + BOOST_CHECK(!queue.try_pushPriority(data.data(), 390)); // this should fail as it is full + BOOST_CHECK_EQUAL(queue.pushPriority(std::chrono::milliseconds(50), data.data(), 420), + 0); // this should return 0 as timeout } -BOOST_AUTO_TEST_CASE (pop_wait) +BOOST_AUTO_TEST_CASE(pop_wait) { - std::unique_ptr memblock (new unsigned char[4096]); + std::unique_ptr memblock(new unsigned char[4096]); - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 4096); - std::vector data (500, 'a'); - BOOST_CHECK_EQUAL (queue.try_pop (data.data (), 390), 0); // this would go into the pull + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); + std::vector data(500, 'a'); + BOOST_CHECK_EQUAL(queue.try_pop(data.data(), 390), 0); // this would go into the pull - BOOST_CHECK_EQUAL (queue.pop (std::chrono::milliseconds (100), data.data (), 420), - 0); // this should return 0 as timeout + BOOST_CHECK_EQUAL(queue.pop(std::chrono::milliseconds(100), data.data(), 420), + 0); // this should return 0 as timeout } -BOOST_AUTO_TEST_CASE (pop_wait2) +BOOST_AUTO_TEST_CASE(pop_wait2) { - std::unique_ptr memblock (new unsigned char[4096]); + std::unique_ptr memblock(new unsigned char[4096]); - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue (memblock.get (), 4096); - std::vector data (500, 'a'); + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); + std::vector data(500, 'a'); - auto def = std::async (std::launch::async, [&]() { - std::this_thread::sleep_for (std::chrono::milliseconds (50)); - queue.push (data.data (), 300); + auto def = std::async(std::launch::async, [&]() { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + queue.push(data.data(), 300); }); - int ret = queue.pop (data.data (), 500); - BOOST_CHECK_EQUAL (ret, 300); - def.wait(); + int ret = queue.pop(data.data(), 500); + BOOST_CHECK_EQUAL(ret, 300); + def.wait(); } BOOST_AUTO_TEST_CASE(pop_wait_priority) { - std::unique_ptr memblock(new unsigned char[4096]); + std::unique_ptr memblock(new unsigned char[4096]); - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); - std::vector data(500, 'a'); + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); + std::vector data(500, 'a'); - auto def = std::async(std::launch::async, [&]() { - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - queue.pushPriority(data.data(), 300); - }); + auto def = std::async(std::launch::async, [&]() { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + queue.pushPriority(data.data(), 300); + }); - int ret = queue.pop(data.data(), 500); - BOOST_CHECK_EQUAL(ret, 300); - def.wait(); + int ret = queue.pop(data.data(), 500); + BOOST_CHECK_EQUAL(ret, 300); + def.wait(); } - BOOST_AUTO_TEST_CASE(push_wait) { - std::unique_ptr memblock(new unsigned char[4096]); - - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); - std::vector data(500, 'a'); - std::vector data2(500, 'a'); - BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the pull - BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the push - BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the push - BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the push - - BOOST_CHECK(!queue.try_push(data.data(), 420)); // this should fail as it is full - auto def = std::async(std::launch::async, [&]() { - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - queue.pop(data.data(), 500); - }); - queue.push(data.data(), 420); - def.wait(); + std::unique_ptr memblock(new unsigned char[4096]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); + std::vector data(500, 'a'); + std::vector data2(500, 'a'); + BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the pull + BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the push + BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the push + BOOST_CHECK(queue.try_push(data.data(), 420)); // this would go into the push + + BOOST_CHECK(!queue.try_push(data.data(), 420)); // this should fail as it is full + auto def = std::async(std::launch::async, [&]() { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + queue.pop(data.data(), 500); + }); + queue.push(data.data(), 420); + def.wait(); } BOOST_AUTO_TEST_CASE(priority_push_wait) { - std::unique_ptr memblock(new unsigned char[4096]); + std::unique_ptr memblock(new unsigned char[4096]); - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); - std::vector data(500, 'a'); - std::vector data2(500, 'a'); + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 4096); + std::vector data(500, 'a'); + std::vector data2(500, 'a'); - BOOST_CHECK(queue.try_pushPriority(data.data(), 390)); // this would go into the pull - BOOST_CHECK(queue.try_pushPriority(data.data(), 390)); // this would go into the push + BOOST_CHECK(queue.try_pushPriority(data.data(), 390)); // this would go into the pull + BOOST_CHECK(queue.try_pushPriority(data.data(), 390)); // this would go into the push - BOOST_CHECK(!queue.try_pushPriority(data.data(), 390)); // this should fail as it is full + BOOST_CHECK(!queue.try_pushPriority(data.data(), 390)); // this should fail as it is full - auto def = std::async(std::launch::async, [&]() { - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - queue.pop(data.data(), 500); - }); - queue.pushPriority(data.data(), 390); - def.wait(); + auto def = std::async(std::launch::async, [&]() { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + queue.pop(data.data(), 500); + }); + queue.pushPriority(data.data(), 390); + def.wait(); } /** test with single consumer/single producer*/ @@ -240,33 +239,28 @@ BOOST_AUTO_TEST_CASE(multithreaded_tests) helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 1048576); - - for (int64_t ii = 0; ii < 10'000; ++ii) - { - queue.push(reinterpret_cast(&ii),8); + for (int64_t ii = 0; ii < 10'000; ++ii) { + queue.push(reinterpret_cast(&ii), 8); } auto prod1 = [&]() { int64_t bdata; - for (int64_t jj = 10'000; jj < 1'010'000; ++jj) - { + for (int64_t jj = 10'000; jj < 1'010'000; ++jj) { bdata = jj; - queue.push(reinterpret_cast(&bdata), 8); + queue.push(reinterpret_cast(&bdata), 8); } }; auto cons = [&]() { int64_t data; - auto res = queue.try_pop(reinterpret_cast(&data),8); + auto res = queue.try_pop(reinterpret_cast(&data), 8); int64_t cnt = 0; - while ((res)) - { + while ((res)) { ++cnt; - res = queue.try_pop(reinterpret_cast(&data), 8); - if (res==0) - { // make an additional sleep period so the producer can catch up + res = queue.try_pop(reinterpret_cast(&data), 8); + if (res == 0) { // make an additional sleep period so the producer can catch up std::this_thread::sleep_for(std::chrono::milliseconds(100)); - res = queue.try_pop(reinterpret_cast(&data), 8); + res = queue.try_pop(reinterpret_cast(&data), 8); } } return cnt; @@ -284,224 +278,207 @@ BOOST_AUTO_TEST_CASE(multithreaded_tests) /** test with multiple consumer/single producer*/ BOOST_AUTO_TEST_CASE(multithreaded_tests2) { - std::unique_ptr memblock(new unsigned char[1048576]); - - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 1048576); - for (int64_t ii = 0; ii < 10'000; ++ii) - { - queue.push(reinterpret_cast(&ii), 8); - } - auto prod1 = [&]() { - int64_t bdata; - for (int64_t jj = 10'000; jj < 1'010'000; ++jj) - { - bdata = jj; - queue.push(reinterpret_cast(&bdata), 8); - } - }; - - auto cons = [&]() { - int64_t data; - auto res = queue.try_pop(reinterpret_cast(&data), 8); - int64_t cnt = 0; - while ((res)) - { - ++cnt; - res = queue.try_pop(reinterpret_cast(&data), 8); - if (res == 0) - { // make an additional sleep period so the producer can catch up - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - res = queue.try_pop(reinterpret_cast(&data), 8); - } - } - return cnt; - }; - - auto ret = std::async(std::launch::async, prod1); - - auto res1 = std::async(std::launch::async, cons); - auto res2 = std::async(std::launch::async, cons); - auto res3 = std::async(std::launch::async, cons); - ret.wait(); - auto V1 = res1.get(); - auto V2 = res2.get(); - auto V3 = res3.get(); - - BOOST_CHECK_EQUAL(V1 + V2 + V3, 1'010'000); + std::unique_ptr memblock(new unsigned char[1048576]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 1048576); + for (int64_t ii = 0; ii < 10'000; ++ii) { + queue.push(reinterpret_cast(&ii), 8); + } + auto prod1 = [&]() { + int64_t bdata; + for (int64_t jj = 10'000; jj < 1'010'000; ++jj) { + bdata = jj; + queue.push(reinterpret_cast(&bdata), 8); + } + }; + + auto cons = [&]() { + int64_t data; + auto res = queue.try_pop(reinterpret_cast(&data), 8); + int64_t cnt = 0; + while ((res)) { + ++cnt; + res = queue.try_pop(reinterpret_cast(&data), 8); + if (res == 0) { // make an additional sleep period so the producer can catch up + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + res = queue.try_pop(reinterpret_cast(&data), 8); + } + } + return cnt; + }; + + auto ret = std::async(std::launch::async, prod1); + + auto res1 = std::async(std::launch::async, cons); + auto res2 = std::async(std::launch::async, cons); + auto res3 = std::async(std::launch::async, cons); + ret.wait(); + auto V1 = res1.get(); + auto V2 = res2.get(); + auto V3 = res3.get(); + + BOOST_CHECK_EQUAL(V1 + V2 + V3, 1'010'000); } /** test with multiple producer/multiple consumer*/ BOOST_AUTO_TEST_CASE(multithreaded_tests3) { - std::unique_ptr memblock(new unsigned char[1048576]); - - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 1048576); - for (int64_t ii = 0; ii < 10'000; ++ii) - { - queue.push(reinterpret_cast(&ii), 8); - } - auto prod1 = [&]() { - int64_t bdata; - for (int64_t jj = 10'000; jj < 1'010'000; ++jj) - { - bdata = jj; - queue.push(reinterpret_cast(&bdata), 8); - } - }; - - auto cons = [&]() { - int64_t data; - auto res = queue.try_pop(reinterpret_cast(&data), 8); - int64_t cnt = 0; - while ((res)) - { - ++cnt; - res = queue.try_pop(reinterpret_cast(&data), 8); - if (res == 0) - { // make an additional sleep period so the producer can catch up - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - res = queue.try_pop(reinterpret_cast(&data), 8); - } - } - return cnt; - }; - - - auto ret1 = std::async(std::launch::async, prod1); - auto ret2 = std::async(std::launch::async, prod1); - auto ret3 = std::async(std::launch::async, prod1); - - auto res1 = std::async(std::launch::async, cons); - auto res2 = std::async(std::launch::async, cons); - auto res3 = std::async(std::launch::async, cons); - ret1.wait(); - ret2.wait(); - ret3.wait(); - auto V1 = res1.get(); - auto V2 = res2.get(); - auto V3 = res3.get(); - - BOOST_CHECK_EQUAL(V1 + V2 + V3, 3'010'000); + std::unique_ptr memblock(new unsigned char[1048576]); + + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 1048576); + for (int64_t ii = 0; ii < 10'000; ++ii) { + queue.push(reinterpret_cast(&ii), 8); + } + auto prod1 = [&]() { + int64_t bdata; + for (int64_t jj = 10'000; jj < 1'010'000; ++jj) { + bdata = jj; + queue.push(reinterpret_cast(&bdata), 8); + } + }; + + auto cons = [&]() { + int64_t data; + auto res = queue.try_pop(reinterpret_cast(&data), 8); + int64_t cnt = 0; + while ((res)) { + ++cnt; + res = queue.try_pop(reinterpret_cast(&data), 8); + if (res == 0) { // make an additional sleep period so the producer can catch up + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + res = queue.try_pop(reinterpret_cast(&data), 8); + } + } + return cnt; + }; + + auto ret1 = std::async(std::launch::async, prod1); + auto ret2 = std::async(std::launch::async, prod1); + auto ret3 = std::async(std::launch::async, prod1); + + auto res1 = std::async(std::launch::async, cons); + auto res2 = std::async(std::launch::async, cons); + auto res3 = std::async(std::launch::async, cons); + ret1.wait(); + ret2.wait(); + ret3.wait(); + auto V1 = res1.get(); + auto V2 = res2.get(); + auto V3 = res3.get(); + + BOOST_CHECK_EQUAL(V1 + V2 + V3, 3'010'000); } /** test with multiple producer/multiple consumer*/ BOOST_AUTO_TEST_CASE(multithreaded_tests3_pop) { - std::unique_ptr memblock(new unsigned char[1048576]); - - helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 1048576); - - auto prod1 = [&]() { - int64_t bdata; - for (int64_t jj = 0; jj < 1'000'000; ++jj) - { - bdata = jj; - queue.push(reinterpret_cast(&bdata), 8); - } - bdata = (-1); - queue.push(reinterpret_cast(&bdata), 8); - }; - - auto cons = [&]() { - int64_t data=0; - int64_t cnt = 0; - while (data>=0) - { - ++cnt; - queue.pop(reinterpret_cast(&data), 8); - } - return cnt; - }; - - - auto ret1 = std::async(std::launch::async, prod1); - auto ret2 = std::async(std::launch::async, prod1); - auto ret3 = std::async(std::launch::async, prod1); - - auto res1 = std::async(std::launch::async, cons); - auto res2 = std::async(std::launch::async, cons); - auto res3 = std::async(std::launch::async, cons); - ret1.wait(); - ret2.wait(); - ret3.wait(); - auto V1 = res1.get(); - auto V2 = res2.get(); - auto V3 = res3.get(); - - BOOST_CHECK_EQUAL(V1 + V2 + V3, 3'000'003); -} + std::unique_ptr memblock(new unsigned char[1048576]); + helics::ipc::detail::IpcBlockingPriorityQueueImpl queue(memblock.get(), 1048576); + + auto prod1 = [&]() { + int64_t bdata; + for (int64_t jj = 0; jj < 1'000'000; ++jj) { + bdata = jj; + queue.push(reinterpret_cast(&bdata), 8); + } + bdata = (-1); + queue.push(reinterpret_cast(&bdata), 8); + }; + + auto cons = [&]() { + int64_t data = 0; + int64_t cnt = 0; + while (data >= 0) { + ++cnt; + queue.pop(reinterpret_cast(&data), 8); + } + return cnt; + }; + + auto ret1 = std::async(std::launch::async, prod1); + auto ret2 = std::async(std::launch::async, prod1); + auto ret3 = std::async(std::launch::async, prod1); + + auto res1 = std::async(std::launch::async, cons); + auto res2 = std::async(std::launch::async, cons); + auto res3 = std::async(std::launch::async, cons); + ret1.wait(); + ret2.wait(); + ret3.wait(); + auto V1 = res1.get(); + auto V2 = res2.get(); + auto V3 = res3.get(); + + BOOST_CHECK_EQUAL(V1 + V2 + V3, 3'000'003); +} BOOST_AUTO_TEST_CASE(push_full_mem) { - std::unique_ptr memblock(new unsigned char[4096]); + std::unique_ptr memblock(new unsigned char[4096]); - std::unique_ptr memblock2(new unsigned char[4096]); + std::unique_ptr memblock2(new unsigned char[4096]); - auto *queue =new(memblock2.get()) helics::ipc::detail::IpcBlockingPriorityQueueImpl(memblock.get(), 4096); - std::vector data(500, 'a'); - BOOST_CHECK(queue->try_push(data.data(), 420)); // this would go into the pull - BOOST_CHECK(queue->try_push(data.data(), 420)); // this would go into the push - BOOST_CHECK(queue->try_push(data.data(), 420)); // this would go into the push - BOOST_CHECK(queue->try_push(data.data(), 420)); // this would go into the push + auto* queue = new (memblock2.get()) + helics::ipc::detail::IpcBlockingPriorityQueueImpl(memblock.get(), 4096); + std::vector data(500, 'a'); + BOOST_CHECK(queue->try_push(data.data(), 420)); // this would go into the pull + BOOST_CHECK(queue->try_push(data.data(), 420)); // this would go into the push + BOOST_CHECK(queue->try_push(data.data(), 420)); // this would go into the push + BOOST_CHECK(queue->try_push(data.data(), 420)); // this would go into the push - BOOST_CHECK(!queue->try_push(data.data(), 420)); // this should fail as it is full - BOOST_CHECK_EQUAL(queue->push(std::chrono::milliseconds(50), data.data(), 420), - 0); // this should return 0 as timeout + BOOST_CHECK(!queue->try_push(data.data(), 420)); // this should fail as it is full + BOOST_CHECK_EQUAL(queue->push(std::chrono::milliseconds(50), data.data(), 420), + 0); // this should return 0 as timeout } - - /** test with multiple producer/multiple consumer*/ BOOST_AUTO_TEST_CASE(multithreaded_tests3_pop_mem) { - std::unique_ptr memblock(new unsigned char[1048576]); - - std::unique_ptr memblock2(new unsigned char[4096]); - - auto *qn = new(memblock2.get()) helics::ipc::detail::IpcBlockingPriorityQueueImpl(memblock.get(), 1048576); - - auto prod1 = [&](void *data) { - auto *queue = reinterpret_cast(data); - int64_t bdata; - for (int64_t jj = 0; jj < 1'000'000; ++jj) - { - bdata = jj; - queue->push(reinterpret_cast(&bdata), 8); - } - bdata = (-1); - queue->push(reinterpret_cast(&bdata), 8); - }; - - auto cons = [&](void *mem) { - auto *queue = reinterpret_cast(mem); - int64_t data = 0; - int64_t cnt = 0; - while (data >= 0) - { - ++cnt; - queue->pop(reinterpret_cast(&data), 8); - } - return cnt; - }; - - - auto ret1 = std::async(std::launch::async, prod1, memblock2.get()); - auto ret2 = std::async(std::launch::async, prod1, memblock2.get()); - auto ret3 = std::async(std::launch::async, prod1, memblock2.get()); - - auto res1 = std::async(std::launch::async, cons, memblock2.get()); - auto res2 = std::async(std::launch::async, cons, memblock2.get()); - auto res3 = std::async(std::launch::async, cons, memblock2.get()); - ret1.wait(); - ret2.wait(); - ret3.wait(); - auto V1 = res1.get(); - auto V2 = res2.get(); - auto V3 = res3.get(); - - BOOST_CHECK_EQUAL(V1 + V2 + V3, 3'000'003); + std::unique_ptr memblock(new unsigned char[1048576]); + + std::unique_ptr memblock2(new unsigned char[4096]); + + auto* qn = new (memblock2.get()) + helics::ipc::detail::IpcBlockingPriorityQueueImpl(memblock.get(), 1048576); + + auto prod1 = [&](void* data) { + auto* queue = reinterpret_cast(data); + int64_t bdata; + for (int64_t jj = 0; jj < 1'000'000; ++jj) { + bdata = jj; + queue->push(reinterpret_cast(&bdata), 8); + } + bdata = (-1); + queue->push(reinterpret_cast(&bdata), 8); + }; + + auto cons = [&](void* mem) { + auto* queue = reinterpret_cast(mem); + int64_t data = 0; + int64_t cnt = 0; + while (data >= 0) { + ++cnt; + queue->pop(reinterpret_cast(&data), 8); + } + return cnt; + }; + auto ret1 = std::async(std::launch::async, prod1, memblock2.get()); + auto ret2 = std::async(std::launch::async, prod1, memblock2.get()); + auto ret3 = std::async(std::launch::async, prod1, memblock2.get()); + + auto res1 = std::async(std::launch::async, cons, memblock2.get()); + auto res2 = std::async(std::launch::async, cons, memblock2.get()); + auto res3 = std::async(std::launch::async, cons, memblock2.get()); + ret1.wait(); + ret2.wait(); + ret3.wait(); + auto V1 = res1.get(); + auto V2 = res2.get(); + auto V3 = res3.get(); + + BOOST_CHECK_EQUAL(V1 + V2 + V3, 3'000'003); } -BOOST_AUTO_TEST_SUITE_END () +BOOST_AUTO_TEST_SUITE_END()