[usb-request] Add c++ list wrapper.

TEST= runtests -t usb-unittest

Change-Id: Iced2817e04aafc5cd2ee5ee53452245c20945449
diff --git a/zircon/system/dev/lib/operation/include/lib/operation/operation.h b/zircon/system/dev/lib/operation/include/lib/operation/operation.h
index 378d74e..97088bf 100644
--- a/zircon/system/dev/lib/operation/include/lib/operation/operation.h
+++ b/zircon/system/dev/lib/operation/include/lib/operation/operation.h
@@ -550,6 +550,8 @@
     BaseList& operator=(BaseList&& other) {
         list_.clear();
         list_.swap(other.list_);
+        size_ = other.size_;
+        other.size_ = 0;
         return *this;
     }
 
diff --git a/zircon/system/dev/lib/usb/include/usb/request-cpp.h b/zircon/system/dev/lib/usb/include/usb/request-cpp.h
index 92bb61b..6a69981 100644
--- a/zircon/system/dev/lib/usb/include/usb/request-cpp.h
+++ b/zircon/system/dev/lib/usb/include/usb/request-cpp.h
@@ -39,6 +39,12 @@
 // usb::RequestQueue provides a queue interface for tracking usb::Request and
 // usb::UnownedRequest objects.
 //
+// usb::RequestList provides a list interface for tracking usb::Request and
+// usb::UnownedRequest objects.
+//
+// A Request or UnownedRequest cannot be stored simultaneously in both a
+// usb::RequestQueue and usb::RequestList in the same driver layer.
+//
 // Available methods for both Request and UnownedRequest include:
 //
 //   usb_request_t* request(); // accessor for inner type.
@@ -91,6 +97,24 @@
 // }
 //
 ///////////////////////////////////////////////////////////////////////////////
+// Example: Add incoming requests into a usb::RequestList:
+//
+// class Driver {
+// public:
+//     <...>
+// private:
+//     usb::UnownedRequestList<> request_;
+//     const size_t parent_req_size_;
+// };
+//
+// void Driver::UsbRequestQueue(usb_request_t* req, const usb_request_callback_t* completion_cb) {
+//     auto opt_unowned = usb::UnownedRequest<>(op, cb, parent_req_size);
+//     auto unowned = *std::move(opt_unowned);
+//     request_.push_back(&unowned);
+//     // Pass unowned_.take() to next layer.
+// }
+//
+///////////////////////////////////////////////////////////////////////////////
 // Example: Using private context only visible to your driver:
 //
 // struct PrivateStorage {
@@ -100,8 +124,8 @@
 //
 // using UsbRequest = usb::UnownedRequest<PrivateStorage>;
 //
-// void Driver::UsbReqeustQueue(usb_request_t* req, const usb_request_t* completion_cb) {
-//     UsbReqeust usb_req(op, cb, parent_req_size_));
+// void Driver::UsbRequestQueue(usb_request_t* req, const usb_request_t* completion_cb) {
+//     UsbRequest usb_req(op, cb, parent_req_size_));
 //     ZX_DEBUG_ASSERT(usb_req.request()->command == USB_ERASE);
 //     usb_req.private_storage()->valid = true;
 //     usb_req.private_storage()->count_metric += 1;
@@ -287,8 +311,9 @@
                                                   OperationTraits, CallbackTraits, Storage>;
 
     UnownedRequest(usb_request_t* request, const usb_request_complete_t& complete_cb,
-                   size_t parent_req_size)
-        : BaseClass(request, complete_cb.callback, complete_cb.ctx, parent_req_size) {}
+                   size_t parent_req_size, bool allow_destruct = true)
+        : BaseClass(request, complete_cb.callback, complete_cb.ctx, parent_req_size,
+                    allow_destruct) {}
 
     UnownedRequest(usb_request_t* request, size_t parent_req_size, bool allow_destruct = true)
         : BaseClass(request, parent_req_size, allow_destruct) {}
@@ -353,4 +378,12 @@
 template <typename Storage = void>
 using RequestQueue = operation::OperationQueue<Request<Storage>, OperationTraits, Storage>;
 
+template <typename Storage = void>
+using UnownedRequestList = operation::UnownedOperationList<UnownedRequest<Storage>,
+                                                           OperationTraits,
+                                                           CallbackTraits, Storage>;
+
+template <typename Storage = void>
+using RequestList = operation::OperationList<Request<Storage>, OperationTraits, Storage>;
+
 } // namespace usb
diff --git a/zircon/system/dev/test/usb/rules.mk b/zircon/system/dev/test/usb/rules.mk
index 9ed8c06..47d3780 100644
--- a/zircon/system/dev/test/usb/rules.mk
+++ b/zircon/system/dev/test/usb/rules.mk
@@ -12,6 +12,7 @@
 
 MODULE_SRCS += \
     $(LOCAL_DIR)/main.cpp \
+    $(LOCAL_DIR)/usb-request-list-test.cpp \
     $(LOCAL_DIR)/usb-request-pool-test.cpp \
     $(LOCAL_DIR)/usb-request-queue-test.cpp \
     $(LOCAL_DIR)/usb-request-test.cpp \
diff --git a/zircon/system/dev/test/usb/usb-request-list-test.cpp b/zircon/system/dev/test/usb/usb-request-list-test.cpp
new file mode 100644
index 0000000..64ac34c
--- /dev/null
+++ b/zircon/system/dev/test/usb/usb-request-list-test.cpp
@@ -0,0 +1,401 @@
+// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "usb/request-cpp.h"
+
+#include <unittest/unittest.h>
+
+namespace {
+
+using Request = usb::Request<void>;
+
+constexpr size_t kParentReqSize = sizeof(usb_request_t);
+constexpr usb_request_complete_t kNoCallback = {};
+
+bool TrivialLifetimeTest() {
+    BEGIN_TEST;
+    usb::RequestList<void> list;
+    usb::UnownedRequestList<void> unowned_list;
+    END_TEST;
+}
+
+bool SingleRequestTest() {
+    BEGIN_TEST;
+    std::optional<Request> opt_request;
+    ASSERT_EQ(Request::Alloc(&opt_request, 0, 0, kParentReqSize),
+              ZX_OK);
+    Request request = *std::move(opt_request);
+
+    usb::RequestList<void> list;
+    // Empty list.
+    EXPECT_EQ(list.size(), 0u);
+    EXPECT_TRUE(list.begin() == std::nullopt);
+
+    list.push_back(&request);
+    EXPECT_EQ(list.size(), 1u);
+
+    // List only has one request.
+    EXPECT_TRUE(list.prev(&request) == std::nullopt);
+    EXPECT_TRUE(list.next(&request) == std::nullopt);
+
+    std::optional<size_t> idx = list.find(&request);
+    EXPECT_TRUE(idx.has_value());
+    EXPECT_EQ(idx.value(), 0u);
+
+    // Delete the request and verify it's no longer in the list.
+    EXPECT_TRUE(list.erase(&request));
+    EXPECT_EQ(list.size(), 0u);
+
+    idx = list.find(&request);
+    EXPECT_FALSE(idx.has_value());
+    END_TEST;
+}
+
+bool MultipleRequestTest() {
+    BEGIN_TEST;
+    usb::RequestList<void> list;
+    // This is for verifying prev / next pointer values when iterating the list.
+    usb_request_t* raw_reqs[10];
+
+    for (size_t i = 0; i < 10; i++) {
+        std::optional<Request> opt_request;
+        ASSERT_EQ(Request::Alloc(&opt_request, 0, 0, kParentReqSize),
+                  ZX_OK);
+        Request request = *std::move(opt_request);
+
+        list.push_back(&request);
+        EXPECT_EQ(list.size(), i + 1);
+
+        raw_reqs[i] = request.take();
+    }
+    EXPECT_EQ(list.size(), 10u);
+
+    // Verify iterating in both directions.
+    auto opt_request = list.begin();
+    for (size_t i = 0; i < 10; i++) {
+        EXPECT_TRUE(opt_request.has_value());
+        Request request = *std::move(opt_request);
+
+        std::optional<size_t> idx = list.find(&request);
+        EXPECT_TRUE(idx.has_value());
+        EXPECT_EQ(idx.value(), i);
+
+        auto prev = list.prev(&request);
+        if (i == 0) {
+            EXPECT_FALSE(prev.has_value());
+        } else {
+            EXPECT_TRUE(prev.has_value());
+            EXPECT_EQ(prev->request(), raw_reqs[i - 1]);
+        }
+
+        auto next = list.next(&request);
+        if (i == 9) {
+            EXPECT_FALSE(next.has_value());
+        } else {
+            EXPECT_TRUE(next.has_value());
+            EXPECT_EQ(next->request(), raw_reqs[i + 1]);
+        }
+
+        opt_request = std::move(next);
+    }
+    EXPECT_FALSE(opt_request.has_value());
+
+    for (size_t i = 0; i < 10; i++) {
+        auto opt_request = list.begin();
+        EXPECT_TRUE(opt_request.has_value());
+        Request request = *std::move(opt_request);
+        EXPECT_TRUE(list.erase(&request));
+
+        // Force the destructor to run.
+        __UNUSED auto req = Request(raw_reqs[i], kParentReqSize);
+    }
+    EXPECT_EQ(list.size(), 0u);
+    EXPECT_FALSE(list.begin().has_value());
+    END_TEST;
+}
+
+bool MoveTest() {
+    BEGIN_TEST;
+    usb::RequestList<void> list1;
+    usb::RequestList<void> list2;
+
+    usb_request_t* raw_reqs[10];
+
+    for (size_t i = 0; i < 10; i++) {
+        std::optional<Request> opt_request;
+        ASSERT_EQ(Request::Alloc(&opt_request, 0, 0, kParentReqSize),
+                  ZX_OK);
+        Request request = *std::move(opt_request);
+        list1.push_back(&request);
+        raw_reqs[i] = request.take();
+    }
+    EXPECT_EQ(list1.size(), 10u);
+    EXPECT_EQ(list2.size(), 0u);
+
+    list2 = std::move(list1);
+    EXPECT_EQ(list1.size(), 0u);
+    EXPECT_EQ(list2.size(), 10u);
+
+    size_t count = 0;
+    std::optional<Request> opt_request = list2.begin();
+    while (opt_request) {
+        Request request = *std::move(opt_request);
+        std::optional<Request> next = list2.next(&request);
+
+        EXPECT_EQ(request.request(), raw_reqs[count]);
+        EXPECT_TRUE(list2.erase(&request));
+
+        // Force the destructor to run.
+        __UNUSED auto req = Request(raw_reqs[count], kParentReqSize);
+
+        count++;
+        opt_request = std::move(next);
+    }
+    EXPECT_EQ(count, 10u);
+    EXPECT_TRUE(list2.begin() == std::nullopt);
+    END_TEST;
+}
+
+bool ReleaseTest() {
+    BEGIN_TEST;
+    usb::RequestList<void> list;
+    usb_request_t* raw_reqs[10];
+
+    for (size_t i = 0; i < 10; i++) {
+        std::optional<Request> opt_request;
+        ASSERT_EQ(Request::Alloc(&opt_request, 0, 0, kParentReqSize),
+                  ZX_OK);
+        Request request = *std::move(opt_request);
+        list.push_back(&request);
+        EXPECT_EQ(list.size(), i + 1);
+
+        raw_reqs[i] = request.take();
+    }
+
+    list.Release();
+    EXPECT_EQ(list.size(), 0u);
+    EXPECT_FALSE(list.begin().has_value());
+
+    for (size_t i = 0; i < 10; i++) {
+        // Force the destructor to run.
+        __UNUSED auto req = Request(raw_reqs[i], kParentReqSize);
+    }
+
+    END_TEST;
+}
+
+bool MultipleLayerTest() {
+    BEGIN_TEST;
+
+    using FirstLayerReq = usb::UnownedRequest<void>;
+    using SecondLayerReq = usb::Request<void>;
+
+    constexpr size_t kBaseReqSize = sizeof(usb_request_t);
+    constexpr size_t kFirstLayerReqSize = FirstLayerReq::RequestSize(kBaseReqSize);
+
+    usb_request_t* raw_reqs[10];
+
+    usb::RequestList<void> second_layer_list;
+    for (size_t i = 0; i < 10; i++) {
+        std::optional<SecondLayerReq> opt_request;
+        ASSERT_EQ(SecondLayerReq::Alloc(&opt_request, 0, 0, kFirstLayerReqSize),
+                  ZX_OK);
+        ASSERT_TRUE(opt_request.has_value());
+        Request request = *std::move(opt_request);
+        second_layer_list.push_back(&request);
+        raw_reqs[i] = request.take();
+    }
+    EXPECT_EQ(second_layer_list.size(), 10u);
+
+    usb::UnownedRequestList<void> first_layer_list;
+    // Add the requests also into the first layer list.
+    for (size_t i = 0; i < 10; i++) {
+        FirstLayerReq unowned(raw_reqs[i], kNoCallback, kBaseReqSize, /* allow_destruct */ false);
+        first_layer_list.push_back(&unowned);
+    }
+    EXPECT_EQ(first_layer_list.size(), 10u);
+
+    // Remove the requests from both lists.
+    for (size_t i = 0; i < 10; i++) {
+        FirstLayerReq unowned(raw_reqs[i], kBaseReqSize);
+        std::optional<size_t> idx = first_layer_list.find(&unowned);
+        EXPECT_TRUE(idx.has_value());
+        EXPECT_EQ(idx.value(), 0u);
+        EXPECT_TRUE(first_layer_list.erase(&unowned));
+
+        SecondLayerReq request(unowned.take(), kFirstLayerReqSize);
+        idx = second_layer_list.find(&request);
+        EXPECT_TRUE(idx.has_value());
+        EXPECT_EQ(idx.value(), 0u);
+        EXPECT_TRUE(second_layer_list.erase(&request));
+    }
+    EXPECT_EQ(first_layer_list.size(), 0u);
+    EXPECT_EQ(second_layer_list.size(), 0u);
+
+    END_TEST;
+}
+
+bool MultipleLayerWithStorageTest() {
+    BEGIN_TEST;
+
+    using FirstLayerReq = usb::UnownedRequest<char>;
+    using SecondLayerReq = usb::Request<uint64_t>;
+
+    constexpr size_t kBaseReqSize = sizeof(usb_request_t);
+    constexpr size_t kFirstLayerReqSize = FirstLayerReq::RequestSize(kBaseReqSize);
+
+    usb_request_t* raw_reqs[10];
+
+    usb::RequestList<uint64_t> second_layer_list;
+    for (size_t i = 0; i < 10; i++) {
+        std::optional<SecondLayerReq> opt_request;
+        ASSERT_EQ(SecondLayerReq::Alloc(&opt_request, 0, 0, kFirstLayerReqSize),
+                  ZX_OK);
+        SecondLayerReq request = *std::move(opt_request);
+
+        *request.private_storage() = i;
+        EXPECT_EQ(*request.private_storage(), i);
+        second_layer_list.push_back(&request);
+        raw_reqs[i] = request.take();
+    }
+    EXPECT_EQ(second_layer_list.size(), 10u);
+
+    usb::UnownedRequestList<char> first_layer_list;
+    size_t count = 0;
+    // Add the requests also into the first layer list.
+    for (size_t i = 0; i < 10; i++) {
+        FirstLayerReq unowned(raw_reqs[i], kNoCallback, kBaseReqSize, /* allow_destruct */ false);
+        *unowned.private_storage() = static_cast<char>('a' + first_layer_list.size());
+        first_layer_list.push_back(&unowned);
+    }
+    EXPECT_EQ(first_layer_list.size(), 10u);
+
+    // Verify the first layer list node's private storage and also erase them along the way.
+    count = 0;
+    auto opt_unowned = first_layer_list.begin();
+    while (opt_unowned) {
+        auto unowned = *std::move(opt_unowned);
+        auto next = first_layer_list.next(&unowned);
+
+        EXPECT_EQ(*unowned.private_storage(), static_cast<char>('a' + count));
+        EXPECT_TRUE(first_layer_list.erase(&unowned));
+
+        ++count;
+        opt_unowned = std::move(next);
+    }
+    EXPECT_EQ(count, 10);
+    EXPECT_EQ(first_layer_list.size(), 0u);
+
+    // Verify the second layer list node's private storage and also erase them along the way.
+    count = 0;
+    auto opt_request = second_layer_list.begin();
+    while (opt_request) {
+        auto request = *std::move(opt_request);
+        auto next = second_layer_list.next(&request);
+
+        EXPECT_EQ(*request.private_storage(), count);
+        EXPECT_TRUE(second_layer_list.erase(&request));
+
+        ++count;
+        opt_request = std::move(next);
+    }
+    EXPECT_EQ(count, 10);
+    EXPECT_EQ(second_layer_list.size(), 0u);
+
+    for (size_t i = 0; i < 10; i++) {
+        // Force the destructor to run.
+        __UNUSED auto req = SecondLayerReq(raw_reqs[i], kFirstLayerReqSize);
+    }
+
+    END_TEST;
+}
+
+bool MultipleLayerWithCallbackTest() {
+    BEGIN_TEST;
+
+    using FirstLayerReq = usb::UnownedRequest<char>;
+    using SecondLayerReq = usb::Request<uint64_t>;
+
+    constexpr size_t kBaseReqSize = sizeof(usb_request_t);
+    constexpr size_t kFirstLayerReqSize = FirstLayerReq::RequestSize(kBaseReqSize);
+
+    usb_request_t* raw_reqs[10];
+
+    usb::RequestList<uint64_t> second_layer_list;
+    for (size_t i = 0; i < 10; i++) {
+        std::optional<SecondLayerReq> opt_request;
+        ASSERT_EQ(SecondLayerReq::Alloc(&opt_request, 0, 0, kFirstLayerReqSize),
+                  ZX_OK);
+        SecondLayerReq request = *std::move(opt_request);
+
+        *request.private_storage() = i;
+        EXPECT_EQ(*request.private_storage(), i);
+        second_layer_list.push_back(&request);
+
+        raw_reqs[i] = request.take();
+    }
+    EXPECT_EQ(second_layer_list.size(), 10u);
+
+    std::atomic<size_t> num_callbacks{0};
+
+    auto callback = [](void* ctx, usb_request_t* request) {
+        auto counter = static_cast<std::atomic<size_t>*>(ctx);
+        ++(*counter);
+    };
+
+    usb_request_complete_t complete_cb = {
+        .callback = callback,
+        .ctx = &num_callbacks,
+    };
+
+    {
+        usb::UnownedRequestList<char> first_layer_list;
+
+        // Store the requests into the first layer list.
+        for (size_t i = 0; i < 10; i++) {
+            FirstLayerReq unowned(raw_reqs[i], complete_cb, kBaseReqSize,
+                                  /* allow_destruct */ false);
+            first_layer_list.push_back(&unowned);
+        }
+        EXPECT_EQ(first_layer_list.size(), 10u);
+        EXPECT_EQ(second_layer_list.size(), 10u);
+    }
+    // The first layer list destruction should not trigger any callbacks.
+    EXPECT_EQ(num_callbacks.load(), 0u);
+
+    // Verify the second layer list node's private storage and also erase them along the way.
+    size_t count = 0;
+    auto opt_request = second_layer_list.begin();
+    while (opt_request) {
+        auto request = *std::move(opt_request);
+        auto next = second_layer_list.next(&request);
+
+        EXPECT_EQ(*request.private_storage(), count);
+        EXPECT_TRUE(second_layer_list.erase(&request));
+
+        ++count;
+        opt_request = std::move(next);
+    }
+    EXPECT_EQ(count, 10);
+    EXPECT_EQ(second_layer_list.size(), 0u);
+
+    for (size_t i = 0; i < 10; i++) {
+        // Force the destructor to run.
+        __UNUSED auto req = SecondLayerReq(raw_reqs[i], kFirstLayerReqSize);
+    }
+
+    END_TEST;
+}
+} // namespace
+
+BEGIN_TEST_CASE(UsbRequestListTests)
+RUN_TEST_SMALL(TrivialLifetimeTest)
+RUN_TEST_SMALL(SingleRequestTest)
+RUN_TEST_SMALL(MultipleRequestTest)
+RUN_TEST_SMALL(MoveTest)
+RUN_TEST_SMALL(ReleaseTest)
+RUN_TEST_SMALL(MultipleLayerTest)
+RUN_TEST_SMALL(MultipleLayerWithStorageTest)
+RUN_TEST_SMALL(MultipleLayerWithCallbackTest)
+END_TEST_CASE(UsbRequestListTests);