Add SynchronizedFixedQueue class
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 11 Oct 2016 19:35:36 +0000 (19:35 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 11 Oct 2016 19:35:36 +0000 (19:35 +0000)
https://bugs.webkit.org/show_bug.cgi?id=162478

Patch by Said Abou-Hallawa <sabouhallawa@apple.com> on 2016-10-11
Reviewed by Geoffrey Garen.

Source/WTF:

This class represents a simple producer/consumer worker. It facilitates
synchronizing enqueuing to and dequeuing from a fixed size-queue. It uses
a single lock and a single condition to synchronize all its members among
the working threads. This means a single thread is active at any time and
and the other threads are blocked waiting for the lock to be released. Or
they are sleeping waiting for the condition to be satisfied.

* WTF.xcodeproj/project.pbxproj:
* wtf/SynchronizedFixedQueue.h: Added.
(WTF::SynchronizedFixedQueue::SynchronizedFixedQueue):
(WTF::SynchronizedFixedQueue::open): Restore the queue to its original state.
(WTF::SynchronizedFixedQueue::close): Wake all the sleeping threads with a closing state.
(WTF::SynchronizedFixedQueue::isOpen): Does the queue accept new items?
(WTF::SynchronizedFixedQueue::enqueue): Enqueue an item into the queue.
(WTF::SynchronizedFixedQueue::dequeue): Dequeue an item form the queue.

Tools:

Add a new test for SynchronizedFixedQueue. The test defines a new class
called ToUpperConverter which converts strings from lower case to upper
case. It creates two threads : (1) produce thread and (2) consume thread.
Here is what each thread does:

1. Main threads: Enqueues lower case strings into m_lowerQueue.
2. Produce thread: Dequeues lower case strings from m_lowerQueue and
   enqueue their upper case strings in the m_upperQueue.
3. Consume thread: Dequeues upper case strings from m_upperQueue.

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WTF/SynchronizedFixedQueue.cpp: Added.
(TestWebKitAPI::textItem): A helper function which returns a lower case string given an index.
(TestWebKitAPI::toUpper): A helper function which Returns the upper case of a string.
(TestWebKitAPI::ToUpperConverter::ToUpperConverter):
(TestWebKitAPI::ToUpperConverter::produceQueue): Returns a workQueue for the produce thread.
(TestWebKitAPI::ToUpperConverter::consumeQueue): Returns a workQueue for the consume thread.
(TestWebKitAPI::ToUpperConverter::startProducing): Creates a thread for the producer.
(TestWebKitAPI::ToUpperConverter::startConsuming): Creates a thread for the consumer.
(TestWebKitAPI::ToUpperConverter::start): Starts both the producer and the consumer threads.
(TestWebKitAPI::ToUpperConverter::stopProducing): Terminates the producer thread.
(TestWebKitAPI::ToUpperConverter::stopConsuming): Terminates the consumer thread.
(TestWebKitAPI::ToUpperConverter::stop): Terminates both the producer and the consumer threads.
(TestWebKitAPI::ToUpperConverter::enqueueLower): Adds a lower case string to the m_lowerQueue on the main thread.
(TestWebKitAPI::ToUpperConverter::isProducing): Returns whether the producing thread is active.
(TestWebKitAPI::ToUpperConverter::isConsuming): Returns whether the consuming thread is active.
(TestWebKitAPI::ToUpperConverter::produceCount): Returns the number of produced elements.
(TestWebKitAPI::ToUpperConverter::consumeCount): Returns the number of consumed elements.
(TestWebKitAPI::TEST):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@207156 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WTF/ChangeLog
Source/WTF/WTF.xcodeproj/project.pbxproj
Source/WTF/wtf/SynchronizedFixedQueue.h [new file with mode: 0644]
Tools/ChangeLog
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Tools/TestWebKitAPI/Tests/WTF/SynchronizedFixedQueue.cpp [new file with mode: 0644]

index cfa2ec9..4b42d13 100644 (file)
@@ -1,3 +1,26 @@
+2016-10-11  Said Abou-Hallawa  <sabouhallawa@apple.com>
+
+        Add SynchronizedFixedQueue class
+        https://bugs.webkit.org/show_bug.cgi?id=162478
+
+        Reviewed by Geoffrey Garen.
+
+        This class represents a simple producer/consumer worker. It facilitates
+        synchronizing enqueuing to and dequeuing from a fixed size-queue. It uses
+        a single lock and a single condition to synchronize all its members among
+        the working threads. This means a single thread is active at any time and
+        and the other threads are blocked waiting for the lock to be released. Or
+        they are sleeping waiting for the condition to be satisfied.
+
+        * WTF.xcodeproj/project.pbxproj:
+        * wtf/SynchronizedFixedQueue.h: Added.
+        (WTF::SynchronizedFixedQueue::SynchronizedFixedQueue):
+        (WTF::SynchronizedFixedQueue::open): Restore the queue to its original state.
+        (WTF::SynchronizedFixedQueue::close): Wake all the sleeping threads with a closing state.
+        (WTF::SynchronizedFixedQueue::isOpen): Does the queue accept new items?
+        (WTF::SynchronizedFixedQueue::enqueue): Enqueue an item into the queue.
+        (WTF::SynchronizedFixedQueue::dequeue): Dequeue an item form the queue.
+
 2016-10-11  Alex Christensen  <achristensen@webkit.org>
 
         Remove dead networking code
index 6b7357e..d2426b1 100644 (file)
                515F79561CFD3A6900CCED93 /* CrossThreadQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 515F79551CFD3A6900CCED93 /* CrossThreadQueue.h */; };
                539EB0631D55284200C82EF7 /* LEBDecoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 539EB0621D55284200C82EF7 /* LEBDecoder.h */; };
                553071CA1C40427200384898 /* TinyLRUCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 553071C91C40427200384898 /* TinyLRUCache.h */; };
+               5597F82F1D94B9970066BC21 /* SynchronizedFixedQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 5597F82C1D94B9970066BC21 /* SynchronizedFixedQueue.h */; };
                5C7C88D41D0A3A0A009D2F6D /* UniqueRef.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C7C88D31D0A3A0A009D2F6D /* UniqueRef.h */; };
                70A993FE1AD7151300FA615B /* SymbolRegistry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 70A993FC1AD7151300FA615B /* SymbolRegistry.cpp */; };
                70A993FF1AD7151300FA615B /* SymbolRegistry.h in Headers */ = {isa = PBXBuildFile; fileRef = 70A993FD1AD7151300FA615B /* SymbolRegistry.h */; };
                515F79551CFD3A6900CCED93 /* CrossThreadQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CrossThreadQueue.h; sourceTree = "<group>"; };
                539EB0621D55284200C82EF7 /* LEBDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LEBDecoder.h; sourceTree = "<group>"; };
                553071C91C40427200384898 /* TinyLRUCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TinyLRUCache.h; sourceTree = "<group>"; };
+               5597F82C1D94B9970066BC21 /* SynchronizedFixedQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SynchronizedFixedQueue.h; sourceTree = "<group>"; };
                5B43383A5D0B463C9433D933 /* IndexMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IndexMap.h; sourceTree = "<group>"; };
                5C7C88D31D0A3A0A009D2F6D /* UniqueRef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UniqueRef.h; sourceTree = "<group>"; };
                5D247B6214689B8600E78B76 /* libWTF.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libWTF.a; sourceTree = BUILT_PRODUCTS_DIR; };
                                A748745117A0BDAE00FA04CB /* StringHashDumpContext.h */,
                                0FDDBFA51666DFA300C55FEF /* StringPrintStream.cpp */,
                                0FDDBFA61666DFA300C55FEF /* StringPrintStream.h */,
+                               5597F82C1D94B9970066BC21 /* SynchronizedFixedQueue.h */,
                                0FB317C31C488001007E395A /* SystemTracing.h */,
                                A8A4731A151A825B004123FF /* TemporaryChange.h */,
                                A8A4732F151A825B004123FF /* ThreadFunctionInvocation.h */,
                                C4F8A93719C65EB400B2B15D /* Stopwatch.h in Headers */,
                                1A6BB769162F300500DD16DB /* StreamBuffer.h in Headers */,
                                A8A4743B151A825B004123FF /* StringBuffer.h in Headers */,
+                               5597F82F1D94B9970066BC21 /* SynchronizedFixedQueue.h in Headers */,
                                A8A4743D151A825B004123FF /* StringBuilder.h in Headers */,
                                430B47891AAAAC1A001223DA /* StringCommon.h in Headers */,
                                A8A4743E151A825B004123FF /* StringConcatenate.h in Headers */,
diff --git a/Source/WTF/wtf/SynchronizedFixedQueue.h b/Source/WTF/wtf/SynchronizedFixedQueue.h
new file mode 100644 (file)
index 0000000..15f97f9
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2016 Apple Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <wtf/Condition.h>
+#include <wtf/Deque.h>
+#include <wtf/HashSet.h>
+#include <wtf/Lock.h>
+#include <wtf/Locker.h>
+
+namespace WTF {
+
+template<typename T, size_t BufferSize>
+class SynchronizedFixedQueue {
+public:
+    SynchronizedFixedQueue()
+    {
+        static_assert(!((BufferSize - 1) & BufferSize), "BufferSize must be power of 2.");
+    }
+    
+    void open()
+    {
+        LockHolder lockHolder(m_mutex);
+        if (m_open)
+            return;
+
+        // Restore the queue to its original state.
+        m_open = true;
+        m_queue.clear();
+    }
+    
+    void close()
+    {
+        LockHolder lockHolder(m_mutex);
+        if (!m_open)
+            return;
+
+        // Wake all the sleeping threads up with a closing state.
+        m_open = false;
+        m_condition.notifyAll();
+    }
+    
+    bool isOpen()
+    {
+        LockHolder lockHolder(m_mutex);
+        return m_open;
+    }
+
+    bool enqueue(const T& value)
+    {
+        LockHolder lockHolder(m_mutex);
+
+        // Wait for an empty place to be available in the queue.
+        m_condition.wait(m_mutex, [this]() { return !m_open || m_queue.size() < BufferSize; });
+        
+        // The queue is closing, exit immediately.
+        if (!m_open)
+            return false;
+        
+        // Add the item in the queue.
+        m_queue.append(value);
+
+        // Notify the other threads that an item was added to the queue.
+        m_condition.notifyAll();
+        return true;
+    }
+    
+    bool dequeue(T& value)
+    {
+        LockHolder lockHolder(m_mutex);
+        
+        // Wait for an item to be added.
+        m_condition.wait(m_mutex, [this]() { return !m_open || m_queue.size(); });
+
+        // The queue is closing, exit immediately.
+        if (!m_open)
+            return false;
+
+        // Get a copy from m_queue.first and then remove it.
+        value = m_queue.first();
+        m_queue.removeFirst();
+
+        // Notify the other threads that an item was removed from the queue.
+        m_condition.notifyAll();
+        return true;
+    }
+
+private:
+    Lock m_mutex;
+    Condition m_condition;
+
+    bool m_open { true };
+    Deque<T, BufferSize> m_queue;
+};
+
+}
+
+using WTF::SynchronizedFixedQueue;
index dfe6d48..becd2f3 100644 (file)
@@ -1,3 +1,40 @@
+2016-10-11  Said Abou-Hallawa  <sabouhallawa@apple.com>
+
+        Add SynchronizedFixedQueue class
+        https://bugs.webkit.org/show_bug.cgi?id=162478
+
+        Reviewed by Geoffrey Garen.
+
+        Add a new test for SynchronizedFixedQueue. The test defines a new class
+        called ToUpperConverter which converts strings from lower case to upper
+        case. It creates two threads : (1) produce thread and (2) consume thread.
+        Here is what each thread does:
+
+        1. Main threads: Enqueues lower case strings into m_lowerQueue.
+        2. Produce thread: Dequeues lower case strings from m_lowerQueue and 
+           enqueue their upper case strings in the m_upperQueue.
+        3. Consume thread: Dequeues upper case strings from m_upperQueue.
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WTF/SynchronizedFixedQueue.cpp: Added.
+        (TestWebKitAPI::textItem): A helper function which returns a lower case string given an index.
+        (TestWebKitAPI::toUpper): A helper function which Returns the upper case of a string.
+        (TestWebKitAPI::ToUpperConverter::ToUpperConverter):
+        (TestWebKitAPI::ToUpperConverter::produceQueue): Returns a workQueue for the produce thread.
+        (TestWebKitAPI::ToUpperConverter::consumeQueue): Returns a workQueue for the consume thread.
+        (TestWebKitAPI::ToUpperConverter::startProducing): Creates a thread for the producer.
+        (TestWebKitAPI::ToUpperConverter::startConsuming): Creates a thread for the consumer.
+        (TestWebKitAPI::ToUpperConverter::start): Starts both the producer and the consumer threads.
+        (TestWebKitAPI::ToUpperConverter::stopProducing): Terminates the producer thread.
+        (TestWebKitAPI::ToUpperConverter::stopConsuming): Terminates the consumer thread.
+        (TestWebKitAPI::ToUpperConverter::stop): Terminates both the producer and the consumer threads.
+        (TestWebKitAPI::ToUpperConverter::enqueueLower): Adds a lower case string to the m_lowerQueue on the main thread.
+        (TestWebKitAPI::ToUpperConverter::isProducing): Returns whether the producing thread is active.
+        (TestWebKitAPI::ToUpperConverter::isConsuming): Returns whether the consuming thread is active.
+        (TestWebKitAPI::ToUpperConverter::produceCount): Returns the number of produced elements.
+        (TestWebKitAPI::ToUpperConverter::consumeCount): Returns the number of consumed elements.
+        (TestWebKitAPI::TEST):
+
 2016-10-11  Megan Gardner  <megan_gardner@apple.com>
 
         Extend event stream to include interpolated events and add a force press test that uses that interpolation
index 140cb13..d96abfa 100644 (file)
                52E5CE4914D21EAB003B2BD8 /* ParentFrame_Bundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 52E5CE4814D21EAB003B2BD8 /* ParentFrame_Bundle.cpp */; };
                536770341CC8022800D425B1 /* WebScriptObjectDescription.mm in Sources */ = {isa = PBXBuildFile; fileRef = 536770331CC8022800D425B1 /* WebScriptObjectDescription.mm */; };
                536770361CC81B6100D425B1 /* WebScriptObjectDescription.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 536770351CC812F900D425B1 /* WebScriptObjectDescription.html */; };
+               5597F8361D9596C80066BC21 /* SynchronizedFixedQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5597F8341D9596C80066BC21 /* SynchronizedFixedQueue.cpp */; };
                5714ECB91CA8B5B000051AC8 /* DownloadRequestOriginalURL.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 5714ECB81CA8B58800051AC8 /* DownloadRequestOriginalURL.html */; };
                5714ECBB1CA8BFE400051AC8 /* DownloadRequestOriginalURLFrame.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 5714ECBA1CA8BFD100051AC8 /* DownloadRequestOriginalURLFrame.html */; };
                5714ECBD1CA8C22A00051AC8 /* DownloadRequestOriginalURL2.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 5714ECBC1CA8C21800051AC8 /* DownloadRequestOriginalURL2.html */; };
                52E5CE4814D21EAB003B2BD8 /* ParentFrame_Bundle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ParentFrame_Bundle.cpp; sourceTree = "<group>"; };
                536770331CC8022800D425B1 /* WebScriptObjectDescription.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WebScriptObjectDescription.mm; sourceTree = "<group>"; };
                536770351CC812F900D425B1 /* WebScriptObjectDescription.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = WebScriptObjectDescription.html; sourceTree = "<group>"; };
+               5597F8341D9596C80066BC21 /* SynchronizedFixedQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SynchronizedFixedQueue.cpp; sourceTree = "<group>"; };
                5714ECB81CA8B58800051AC8 /* DownloadRequestOriginalURL.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = DownloadRequestOriginalURL.html; sourceTree = "<group>"; };
                5714ECBA1CA8BFD100051AC8 /* DownloadRequestOriginalURLFrame.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = DownloadRequestOriginalURLFrame.html; sourceTree = "<group>"; };
                5714ECBC1CA8C21800051AC8 /* DownloadRequestOriginalURL2.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = DownloadRequestOriginalURL2.html; sourceTree = "<group>"; };
                                26F1B44315CA434F00D1E4BF /* StringImpl.cpp */,
                                C01363C713C3997300EF3964 /* StringOperators.cpp */,
                                7C74D42D188228F300E5ED57 /* StringView.cpp */,
+                               5597F8341D9596C80066BC21 /* SynchronizedFixedQueue.cpp */,
                                0BCD85691485C98B00EA2003 /* TemporaryChange.cpp */,
                                5C5E633D1D0B67940085A025 /* UniqueRef.cpp */,
                                7CD0D5AA1D5534DE000CC9E1 /* Variant.cpp */,
                                7C83DF361D0A590C00FEBCF3 /* StringHasher.cpp in Sources */,
                                7C83DF371D0A590C00FEBCF3 /* StringImpl.cpp in Sources */,
                                7C83DF381D0A590C00FEBCF3 /* StringOperators.cpp in Sources */,
+                               5597F8361D9596C80066BC21 /* SynchronizedFixedQueue.cpp in Sources */,
                                7C83DF3A1D0A590C00FEBCF3 /* StringView.cpp in Sources */,
                                7C83DF3D1D0A590C00FEBCF3 /* TemporaryChange.cpp in Sources */,
                                7C83DF401D0A590C00FEBCF3 /* TestsController.cpp in Sources */,
diff --git a/Tools/TestWebKitAPI/Tests/WTF/SynchronizedFixedQueue.cpp b/Tools/TestWebKitAPI/Tests/WTF/SynchronizedFixedQueue.cpp
new file mode 100644 (file)
index 0000000..4638de9
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <chrono>
+#include <thread>
+
+#include <wtf/ASCIICType.h>
+#include <wtf/SynchronizedFixedQueue.h>
+#include <wtf/WorkQueue.h>
+#include <wtf/text/CString.h>
+#include <wtf/threads/BinarySemaphore.h>
+
+namespace TestWebKitAPI {
+
+static char const* textItem(size_t index)
+{
+    static char const* items[] = { "first", "second", "third", "fourth", "fifth", "sixth" };
+    return index < sizeof(items) / sizeof(items[0]) ? items[index] : nullptr;
+}
+
+static CString toUpper(const CString& lower)
+{
+    CString upper = lower;
+
+    for (char* buffer = upper.mutableData(); *buffer; ++buffer)
+        *buffer = toASCIIUpper(*buffer);
+
+    return upper;
+}
+
+template <size_t BufferSize>
+class ToUpperConverter {
+public:
+    ToUpperConverter()
+    {
+    }
+
+    WorkQueue* produceQueue()
+    {
+        if (!m_produceQueue)
+            m_produceQueue = WorkQueue::create("org.webkit.Produce");
+        return m_produceQueue.get();
+    }
+
+    WorkQueue* consumeQueue()
+    {
+        if (!m_consumeQueue)
+            m_consumeQueue = WorkQueue::create("org.webkit.Consume");
+        return m_consumeQueue.get();
+    }
+
+    void startProducing()
+    {
+        if (isProducing())
+            return;
+
+        produceQueue()->dispatch([this] {
+            CString lower;
+            while (m_lowerQueue.dequeue(lower)) {
+                m_upperQueue.enqueue(toUpper(lower));
+                EXPECT_TRUE(lower == textItem(m_produceCount++));
+                std::this_thread::sleep_for(std::chrono::milliseconds(10));
+            }
+            m_produceCloseSemaphore.signal();
+        });
+    }
+
+    void startConsuming()
+    {
+        if (isConsuming())
+            return;
+
+        consumeQueue()->dispatch([this] {
+            CString upper;
+            while (m_upperQueue.dequeue(upper)) {
+                EXPECT_TRUE(upper == toUpper(textItem(m_consumeCount++)));
+                std::this_thread::sleep_for(std::chrono::milliseconds(50));
+            }
+            m_consumeCloseSemaphore.signal();
+        });
+    }
+    
+    void start()
+    {
+        startProducing();
+        startConsuming();
+    }
+
+    void stopProducing()
+    {
+        if (!isProducing())
+            return;
+
+        m_lowerQueue.close();
+        m_produceCloseSemaphore.wait(std::numeric_limits<double>::max());
+        m_produceQueue = nullptr;
+    }
+    
+    void stopConsuming()
+    {
+        if (!isConsuming())
+            return;
+
+        m_upperQueue.close();
+        m_consumeCloseSemaphore.wait(std::numeric_limits<double>::max());
+        m_consumeQueue = nullptr;
+    }
+    
+    void stop()
+    {
+        stopProducing();
+        stopConsuming();
+    }
+
+    void enqueueLower(const CString& lower)
+    {
+        m_lowerQueue.enqueue(lower);
+    }
+
+    bool isProducing() { return m_produceQueue; }
+    bool isConsuming() { return m_consumeQueue; }
+
+    size_t produceCount() const { return m_produceCount; }
+    size_t consumeCount() const { return m_consumeCount; }
+
+private:
+    SynchronizedFixedQueue<CString, BufferSize> m_lowerQueue;
+    SynchronizedFixedQueue<CString, BufferSize> m_upperQueue;
+    RefPtr<WorkQueue> m_produceQueue;
+    RefPtr<WorkQueue> m_consumeQueue;
+    BinarySemaphore m_produceCloseSemaphore;
+    BinarySemaphore m_consumeCloseSemaphore;
+    size_t m_produceCount { 0 };
+    size_t m_consumeCount { 0 };
+};
+
+TEST(WTF_SynchronizedFixedQueue, Basic)
+{
+    ToUpperConverter<4U> converter;
+
+    converter.start();
+    EXPECT_TRUE(converter.isProducing() && converter.isConsuming());
+
+    converter.stop();
+    EXPECT_FALSE(converter.isProducing() || converter.isConsuming());
+
+    EXPECT_EQ(converter.produceCount(), 0U);
+    EXPECT_EQ(converter.consumeCount(), 0U);
+}
+
+TEST(WTF_SynchronizedFixedQueue, ProduceOnly)
+{
+    ToUpperConverter<4U> converter;
+    
+    converter.startProducing();
+    EXPECT_TRUE(converter.isProducing() && !converter.isConsuming());
+
+    size_t count = 0;
+    while (char const* item = textItem(count)) {
+        converter.enqueueLower(item);
+        ++count;
+        
+        std::this_thread::sleep_for(std::chrono::milliseconds(1));
+    }
+
+    converter.stop();
+    EXPECT_FALSE(converter.isProducing() || converter.isConsuming());
+}
+
+TEST(WTF_SynchronizedFixedQueue, ConsumeOnly)
+{
+    ToUpperConverter<4U> converter;
+    
+    converter.startConsuming();
+    EXPECT_TRUE(!converter.isProducing() && converter.isConsuming());
+    
+    converter.stop();
+    EXPECT_FALSE(converter.isProducing() || converter.isConsuming());
+}
+
+TEST(WTF_SynchronizedFixedQueue, Limits)
+{
+    ToUpperConverter<4U> converter;
+
+    converter.start();
+    EXPECT_TRUE(converter.isProducing() && converter.isConsuming());
+
+    size_t count = 0;
+    while (char const* item = textItem(count)) {
+        converter.enqueueLower(item);
+        ++count;
+
+        std::this_thread::sleep_for(std::chrono::milliseconds(1));
+    }
+
+    std::this_thread::sleep_for(std::chrono::milliseconds(400));
+
+    converter.stop();
+    EXPECT_FALSE(converter.isProducing() || converter.isConsuming());
+    
+    EXPECT_EQ(converter.produceCount(), count);
+    EXPECT_EQ(converter.consumeCount(), count);
+}
+
+}