forked from mirrors/gecko-dev
		
	 ad6c02c994
			
		
	
	
		ad6c02c994
		
	
	
	
	
		
			
			Differential Revision: https://phabricator.services.mozilla.com/D62542 --HG-- extra : moz-landing-system : lando
		
			
				
	
	
		
			248 lines
		
	
	
	
		
			6.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			248 lines
		
	
	
	
		
			6.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #include "mozilla/SPSCQueue.h"
 | |
| #include "mozilla/PodOperations.h"
 | |
| #include <vector>
 | |
| #include <iostream>
 | |
| #include <thread>
 | |
| #include <chrono>
 | |
| #include <memory>
 | |
| #include <string>
 | |
| 
 | |
| #ifdef _WIN32
 | |
| #  include <windows.h>
 | |
| #endif
 | |
| 
 | |
| using namespace mozilla;
 | |
| 
 | |
| /* Generate a monotonically increasing sequence of numbers. */
 | |
| template <typename T>
 | |
| class SequenceGenerator {
 | |
|  public:
 | |
|   SequenceGenerator() = default;
 | |
|   void Get(T* aElements, size_t aCount) {
 | |
|     for (size_t i = 0; i < aCount; i++) {
 | |
|       aElements[i] = static_cast<T>(mIndex);
 | |
|       mIndex++;
 | |
|     }
 | |
|   }
 | |
|   void Rewind(size_t aCount) { mIndex -= aCount; }
 | |
| 
 | |
|  private:
 | |
|   size_t mIndex = 0;
 | |
| };
 | |
| 
 | |
| /* Checks that a sequence is monotonically increasing. */
 | |
| template <typename T>
 | |
| class SequenceVerifier {
 | |
|  public:
 | |
|   SequenceVerifier() = default;
 | |
|   void Check(T* aElements, size_t aCount) {
 | |
|     for (size_t i = 0; i < aCount; i++) {
 | |
|       if (aElements[i] != static_cast<T>(mIndex)) {
 | |
|         std::cerr << "Element " << i << " is different. Expected "
 | |
|                   << static_cast<T>(mIndex) << ", got " << aElements[i] << "."
 | |
|                   << std::endl;
 | |
|         MOZ_RELEASE_ASSERT(false);
 | |
|       }
 | |
|       mIndex++;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   size_t mIndex = 0;
 | |
| };
 | |
| 
 | |
| const int BLOCK_SIZE = 127;
 | |
| 
 | |
| template <typename T>
 | |
| void TestRing(int capacity) {
 | |
|   SPSCQueue<T> buf(capacity);
 | |
|   std::unique_ptr<T[]> seq(new T[capacity]);
 | |
|   SequenceGenerator<T> gen;
 | |
|   SequenceVerifier<T> checker;
 | |
| 
 | |
|   int iterations = 1002;
 | |
| 
 | |
|   while (iterations--) {
 | |
|     gen.Get(seq.get(), BLOCK_SIZE);
 | |
|     int rv = buf.Enqueue(seq.get(), BLOCK_SIZE);
 | |
|     MOZ_RELEASE_ASSERT(rv == BLOCK_SIZE);
 | |
|     PodZero(seq.get(), BLOCK_SIZE);
 | |
|     rv = buf.Dequeue(seq.get(), BLOCK_SIZE);
 | |
|     MOZ_RELEASE_ASSERT(rv == BLOCK_SIZE);
 | |
|     checker.Check(seq.get(), BLOCK_SIZE);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void Delay() {
 | |
|   // On Windows and x86 Android, the timer resolution is so bad that, even if
 | |
|   // we used `timeBeginPeriod(1)`, any nonzero sleep from the test's inner loops
 | |
|   // would make this program take far too long.
 | |
| #ifdef _WIN32
 | |
|   Sleep(0);
 | |
| #elif defined(ANDROID)
 | |
|   std::this_thread::sleep_for(std::chrono::microseconds(0));
 | |
| #else
 | |
|   std::this_thread::sleep_for(std::chrono::microseconds(10));
 | |
| #endif
 | |
| }
 | |
| 
 | |
| template <typename T>
 | |
| void TestRingMultiThread(int capacity) {
 | |
|   SPSCQueue<T> buf(capacity);
 | |
|   SequenceVerifier<T> checker;
 | |
|   std::unique_ptr<T[]> outBuffer(new T[capacity]);
 | |
| 
 | |
|   std::thread t([&buf, capacity] {
 | |
|     int iterations = 1002;
 | |
|     std::unique_ptr<T[]> inBuffer(new T[capacity]);
 | |
|     SequenceGenerator<T> gen;
 | |
| 
 | |
|     while (iterations--) {
 | |
|       Delay();
 | |
|       gen.Get(inBuffer.get(), BLOCK_SIZE);
 | |
|       int rv = buf.Enqueue(inBuffer.get(), BLOCK_SIZE);
 | |
|       MOZ_RELEASE_ASSERT(rv <= BLOCK_SIZE);
 | |
|       if (rv != BLOCK_SIZE) {
 | |
|         gen.Rewind(BLOCK_SIZE - rv);
 | |
|       }
 | |
|     }
 | |
|   });
 | |
| 
 | |
|   int remaining = 1002;
 | |
| 
 | |
|   while (remaining--) {
 | |
|     Delay();
 | |
|     int rv = buf.Dequeue(outBuffer.get(), BLOCK_SIZE);
 | |
|     MOZ_RELEASE_ASSERT(rv <= BLOCK_SIZE);
 | |
|     checker.Check(outBuffer.get(), rv);
 | |
|   }
 | |
| 
 | |
|   t.join();
 | |
| }
 | |
| 
 | |
| template <typename T>
 | |
| void BasicAPITest(T& ring) {
 | |
|   MOZ_RELEASE_ASSERT(ring.Capacity() == 128);
 | |
| 
 | |
|   MOZ_RELEASE_ASSERT(ring.AvailableRead() == 0);
 | |
|   MOZ_RELEASE_ASSERT(ring.AvailableWrite() == 128);
 | |
| 
 | |
|   int rv = ring.EnqueueDefault(63);
 | |
| 
 | |
|   MOZ_RELEASE_ASSERT(rv == 63);
 | |
|   MOZ_RELEASE_ASSERT(ring.AvailableRead() == 63);
 | |
|   MOZ_RELEASE_ASSERT(ring.AvailableWrite() == 65);
 | |
| 
 | |
|   rv = ring.EnqueueDefault(65);
 | |
| 
 | |
|   MOZ_RELEASE_ASSERT(rv == 65);
 | |
|   MOZ_RELEASE_ASSERT(ring.AvailableRead() == 128);
 | |
|   MOZ_RELEASE_ASSERT(ring.AvailableWrite() == 0);
 | |
| 
 | |
|   rv = ring.Dequeue(nullptr, 63);
 | |
| 
 | |
|   MOZ_RELEASE_ASSERT(ring.AvailableRead() == 65);
 | |
|   MOZ_RELEASE_ASSERT(ring.AvailableWrite() == 63);
 | |
| 
 | |
|   rv = ring.Dequeue(nullptr, 65);
 | |
| 
 | |
|   MOZ_RELEASE_ASSERT(ring.AvailableRead() == 0);
 | |
|   MOZ_RELEASE_ASSERT(ring.AvailableWrite() == 128);
 | |
| }
 | |
| 
 | |
| const size_t RING_BUFFER_SIZE = 128;
 | |
| const size_t ENQUEUE_SIZE = RING_BUFFER_SIZE / 2;
 | |
| 
 | |
| void TestResetAPI() {
 | |
|   SPSCQueue<float> ring(RING_BUFFER_SIZE);
 | |
|   std::thread t([&ring] {
 | |
|     std::unique_ptr<float[]> inBuffer(new float[ENQUEUE_SIZE]);
 | |
|     int rv = ring.Enqueue(inBuffer.get(), ENQUEUE_SIZE);
 | |
|     MOZ_RELEASE_ASSERT(rv > 0);
 | |
|   });
 | |
| 
 | |
|   t.join();
 | |
| 
 | |
|   ring.ResetThreadIds();
 | |
| 
 | |
|   // Enqueue with a different thread. We have reset the thread ID
 | |
|   // in the ring buffer, this should work.
 | |
|   std::thread t2([&ring] {
 | |
|     std::unique_ptr<float[]> inBuffer(new float[ENQUEUE_SIZE]);
 | |
|     int rv = ring.Enqueue(inBuffer.get(), ENQUEUE_SIZE);
 | |
|     MOZ_RELEASE_ASSERT(rv > 0);
 | |
|   });
 | |
| 
 | |
|   t2.join();
 | |
| }
 | |
| 
 | |
| void TestMove() {
 | |
|   const size_t ELEMENT_COUNT = 16;
 | |
|   struct Thing {
 | |
|     Thing() : mStr("") {}
 | |
|     explicit Thing(const std::string& aStr) : mStr(aStr) {}
 | |
|     Thing(Thing&& aOtherThing) {
 | |
|       mStr = std::move(aOtherThing.mStr);
 | |
|       // aOtherThing.mStr.clear();
 | |
|     }
 | |
|     Thing& operator=(Thing&& aOtherThing) {
 | |
|       mStr = std::move(aOtherThing.mStr);
 | |
|       return *this;
 | |
|     }
 | |
|     std::string mStr;
 | |
|   };
 | |
| 
 | |
|   std::vector<Thing> vec_in;
 | |
|   std::vector<Thing> vec_out;
 | |
| 
 | |
|   for (uint32_t i = 0; i < ELEMENT_COUNT; i++) {
 | |
|     vec_in.push_back(Thing(std::to_string(i)));
 | |
|     vec_out.push_back(Thing());
 | |
|   }
 | |
| 
 | |
|   SPSCQueue<Thing> queue(ELEMENT_COUNT);
 | |
| 
 | |
|   int rv = queue.Enqueue(&vec_in[0], ELEMENT_COUNT);
 | |
|   MOZ_RELEASE_ASSERT(rv == ELEMENT_COUNT);
 | |
| 
 | |
|   // Check that we've moved the std::string into the queue.
 | |
|   for (uint32_t i = 0; i < ELEMENT_COUNT; i++) {
 | |
|     MOZ_RELEASE_ASSERT(vec_in[i].mStr.empty());
 | |
|   }
 | |
| 
 | |
|   rv = queue.Dequeue(&vec_out[0], ELEMENT_COUNT);
 | |
|   MOZ_RELEASE_ASSERT(rv == ELEMENT_COUNT);
 | |
| 
 | |
|   for (uint32_t i = 0; i < ELEMENT_COUNT; i++) {
 | |
|     MOZ_RELEASE_ASSERT(std::stoul(vec_out[i].mStr) == i);
 | |
|   }
 | |
| }
 | |
| 
 | |
| int main() {
 | |
|   const int minCapacity = 199;
 | |
|   const int maxCapacity = 1277;
 | |
|   const int capacityIncrement = 27;
 | |
| 
 | |
|   SPSCQueue<float> q1(128);
 | |
|   BasicAPITest(q1);
 | |
|   SPSCQueue<char> q2(128);
 | |
|   BasicAPITest(q2);
 | |
| 
 | |
|   for (uint32_t i = minCapacity; i < maxCapacity; i += capacityIncrement) {
 | |
|     TestRing<uint32_t>(i);
 | |
|     TestRingMultiThread<uint32_t>(i);
 | |
|     TestRing<float>(i);
 | |
|     TestRingMultiThread<float>(i);
 | |
|   }
 | |
| 
 | |
|   TestResetAPI();
 | |
|   TestMove();
 | |
| 
 | |
|   return 0;
 | |
| }
 |