From 97e6b5b17b6ebe3f22593662ad06c83e8a00c95d Mon Sep 17 00:00:00 2001
From: Todd Eisenberger <teisenbe@google.com>
Date: Tue, 23 Apr 2019 19:55:45 +0000
Subject: [PATCH] [devcoordinator] Include composite devices in child iteration

With this patch, suspending of a composite device should work.

Change-Id: I2a6cdda90a3ba0b111dbba650621d2c1b71ea60a
---
 .../core/devmgr/devcoordinator/device.h       | 122 ++++++++++++++----
 1 file changed, 99 insertions(+), 23 deletions(-)

diff --git a/zircon/system/core/devmgr/devcoordinator/device.h b/zircon/system/core/devmgr/devcoordinator/device.h
index 6dcfaa60b44..d019569f70a 100644
--- a/zircon/system/core/devmgr/devcoordinator/device.h
+++ b/zircon/system/core/devmgr/devcoordinator/device.h
@@ -13,13 +13,12 @@
 #include <lib/zx/channel.h>
 #include <variant>
 
+#include "composite-device.h"
 #include "metadata.h"
 #include "../shared/async-loop-ref-counted-rpc-handler.h"
 
 namespace devmgr {
 
-class CompositeDevice;
-class CompositeDeviceComponent;
 class Coordinator;
 class Devhost;
 struct Devnode;
@@ -90,57 +89,134 @@ struct Device : public fbl::RefCounted<Device>, public AsyncLoopRefCountedRpcHan
     // access to a device in the list.  This is achieved by making the linked
     // list iterator opaque. It is not safe to modify the underlying list while
     // this iterator is in use.
-    template <typename IterType, typename ValueType>
+    template <typename IterType, typename DeviceType>
     class ChildListIterator {
     public:
-        explicit ChildListIterator(IterType itr) : itr_(std::move(itr)) {}
+        ChildListIterator() : state_(Done{}) {}
+        explicit ChildListIterator(DeviceType* device)
+                : state_(device->children_.begin()), device_(device) {
+            SkipInvalidStates();
+        }
 
-        ChildListIterator& operator++() { ++itr_; return *this; }
         ChildListIterator operator++(int) {
             auto other = *this;
             ++*this;
             return other;
         }
-        ValueType& operator*() const { return *itr_; }
-        bool operator==(const ChildListIterator& other) const { return itr_ == other.itr_; }
-        bool operator!=(const ChildListIterator& other) const { return itr_ != other.itr_; }
+        bool operator==(const ChildListIterator& other) const { return state_ == other.state_; }
+        bool operator!=(const ChildListIterator& other) const { return !(state_ == other.state_); }
+
+        // The iterator implementation for the child list.  This is the source of truth
+        // for what devices are children of the device.
+        ChildListIterator& operator++() {
+            std::visit([this](auto&& arg) {
+                using T = std::decay_t<decltype(arg)>;
+                if constexpr (std::is_same_v<T, IterType>) {
+                    ++arg;
+                } else if constexpr (std::is_same_v<T, Composite>) {
+                    state_ = Done{};
+                } else if constexpr (std::is_same_v<T, Done>) {
+                    state_ = Done{};
+                }
+            }, state_);
+            SkipInvalidStates();
+            return *this;
+        }
+
+        DeviceType& operator*() const {
+            return std::visit([this](auto&& arg) -> DeviceType& {
+                using T = std::decay_t<decltype(arg)>;
+                if constexpr (std::is_same_v<T, IterType>) {
+                    return *arg;
+                } else if constexpr (std::is_same_v<T, Composite>) {
+                    return *device_->parent_->component()->composite()->device();
+                } else {
+                    __builtin_trap();
+                }
+            }, state_);
+        }
     private:
-        IterType itr_;
+        // Advance the iterator to the next valid state or reach the done state.
+        // This is used to handle advancement between the different state variants.
+        void SkipInvalidStates() {
+            bool more = true;
+            while (more) {
+                more = std::visit([this](auto&& arg) {
+                    using T = std::decay_t<decltype(arg)>;
+                    if constexpr (std::is_same_v<T, IterType>) {
+                        // Check if there are any more children in the list.  If
+                        // there are, we're in a valid state and can stop.
+                        // Otherwise, advance to the next variant and check if
+                        // it's a valid state.
+                        if (arg != device_->children_.end()) {
+                            return false;
+                        }
+                        state_ = Composite{};
+                        return true;
+                    } else if constexpr (std::is_same_v<T, Composite>) {
+                        // Check if this device is an internal component device
+                        // that bound to a composite component.  If it is, and
+                        // the composite has been constructed, the iterator
+                        // should yield the composite.
+                        CompositeDeviceComponent* component = nullptr;
+                        if (device_->parent_) {
+                            component = device_->parent_->component();
+                        }
+                        if (component != nullptr && component->composite()->device() != nullptr) {
+                            return false;
+                        }
+                        state_ = Done{};
+                        return false;
+                    } else if constexpr (std::is_same_v<T, Done>) {
+                        return false;
+                    }
+                }, state_);
+            }
+        }
+
+        struct Composite {
+            bool operator==(Composite) const { return true; }
+        };
+        struct Done {
+            bool operator==(Done) const { return true; }
+        };
+        std::variant<IterType, Composite, Done> state_;
+        DeviceType* device_;
     };
 
     // This class exists to allow consumers of the Device class to write
     //   for (auto& child : dev->children())
     // and get mutable access to the children without getting mutable access to
     // the list.
-    template <typename ListType, typename IterType>
+    template <typename DeviceType, typename IterType>
     class ChildListIteratorFactory {
     public:
-        explicit ChildListIteratorFactory(ListType& list) : list_(list) {}
+        explicit ChildListIteratorFactory(DeviceType* device) : device_(device) {}
 
-        IterType begin() const { return IterType(list_.begin()); }
-        IterType end() const { return IterType(list_.end()); }
+        IterType begin() const { return IterType(device_); }
+        IterType end() const { return IterType(); }
 
         bool is_empty() const { return begin() == end(); }
     private:
-        ListType& list_;
+        DeviceType* device_;
     };
 
     using NonConstChildListIterator =
             ChildListIterator<fbl::DoublyLinkedList<Device*, Node>::iterator, Device>;
     using ConstChildListIterator =
             ChildListIterator<fbl::DoublyLinkedList<Device*, Node>::const_iterator, const Device>;
-    using NonConstChildListIteratorFactory = ChildListIteratorFactory<
-            fbl::DoublyLinkedList<Device*, Node>, NonConstChildListIterator>;
-    using ConstChildListIteratorFactory = ChildListIteratorFactory<
-            const fbl::DoublyLinkedList<Device*, Node>, ConstChildListIterator>;
+    using NonConstChildListIteratorFactory =
+            ChildListIteratorFactory<Device, NonConstChildListIterator>;
+    using ConstChildListIteratorFactory =
+            ChildListIteratorFactory<const Device, ConstChildListIterator>;
     // We do not want to expose the list itself for mutation, even if the
     // children are allowed to be mutated.  We manage this by making the
     // iterator opaque.
     NonConstChildListIteratorFactory children() {
-        return NonConstChildListIteratorFactory(children_);
+        return NonConstChildListIteratorFactory(this);
     }
     ConstChildListIteratorFactory children() const {
-        return ConstChildListIteratorFactory(children_);
+        return ConstChildListIteratorFactory(this);
     }
 
     // Signal that this device is ready for bind to happen.  This should happen
@@ -219,7 +295,6 @@ struct Device : public fbl::RefCounted<Device>, public AsyncLoopRefCountedRpcHan
     CompositeDeviceComponent* component() const {
         auto val = std::get_if<CompositeDeviceComponent*>(&composite_);
         return val ? *val : nullptr;
-
     }
     void set_component(CompositeDeviceComponent* component) {
         ZX_ASSERT(std::holds_alternative<UnassociatedWithComposite>(composite_));
@@ -230,7 +305,6 @@ struct Device : public fbl::RefCounted<Device>, public AsyncLoopRefCountedRpcHan
     CompositeDevice* composite() const {
         auto val = std::get_if<CompositeDevice*>(&composite_);
         return val ? *val : nullptr;
-
     }
     void set_composite(CompositeDevice* composite) {
         ZX_ASSERT(std::holds_alternative<UnassociatedWithComposite>(composite_));
@@ -271,7 +345,9 @@ private:
     // listnode for this device in its parent's list-of-children
     fbl::DoublyLinkedListNodeState<Device*> node_;
 
-    // list of all child devices of this device
+    // List of all child devices of this device, except for composite devices.
+    // Composite devices are excluded because their multiple-parent nature
+    // precludes using the same intrusive nodes as single-parent devices.
     fbl::DoublyLinkedList<Device*, Node> children_;
 
     // - If this device is part of a composite device, this is inhabited by
-- 
GitLab