| // 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. |
| |
| #ifndef SRC_DEVELOPER_DEBUG_ZXDB_CONSOLE_ASYNC_OUTPUT_BUFFER_H_ |
| #define SRC_DEVELOPER_DEBUG_ZXDB_CONSOLE_ASYNC_OUTPUT_BUFFER_H_ |
| |
| #include <functional> |
| #include <variant> |
| |
| #include "src/developer/debug/zxdb/console/output_buffer.h" |
| #include "src/lib/fxl/memory/ref_counted.h" |
| |
| namespace zxdb { |
| |
| class Err; |
| |
| // This class is for collecting formatted output that might be produced in an |
| // asynchronous manner across many components. |
| // |
| // Formatted text (in the form of an OutputBuffer) can be appended or more |
| // AsyncOutputBuffers can be appended, building up a tree of output. The |
| // various parts of this tree can be filled in asynchronously and the toplevel |
| // buffer's callback will be issued when everything is marked complete. |
| // |
| // Example: |
| // |
| // // Creates a buffer and appends some text to it. |
| // auto out = fxl::MakeRefCounted<AsyncOutputBuffer>(); |
| // out->Append("Hello"); |
| // |
| // // Now have somebody asynchronously append to this placeholder in the |
| // // middle. This might happen after the next call that append "Goodbye". |
| // auto sub = fxl::MakeRefCounted<AsyncOutputBuffer>(); |
| // // Could also Append(sub) here, the order won't matter. |
| // DoSomethingWithACallback( |
| // [sub](std::string value) { |
| // sub->Append(); |
| // sub->Complete(); |
| // }); |
| // out->Append(sub); |
| // |
| // out->Append("Goodbye"); |
| // out->Complete(); |
| class AsyncOutputBuffer : public fxl::RefCountedThreadSafe<AsyncOutputBuffer> { |
| public: |
| using CompletionCallback = std::function<void()>; |
| |
| ~AsyncOutputBuffer() = default; |
| |
| // Setting the completion callback will assert if the buffer is_complete() |
| // because in that case it will never be called. |
| // |
| // This can only be set to a nonempty function once, but it can be set with |
| // an empty function to clear it. |
| void SetCompletionCallback(CompletionCallback cb); |
| |
| // Returns true if the buffer has been marked complete (there will be no |
| // more nodes appended to it) and all of the children are also is_complete(). |
| // Marking a buffer complete and it having complete children are independent |
| // events. |
| bool is_complete() { return pending_resolution_ == 0 && marked_complete_; } |
| |
| // Mirrors the OutputBuffer API with the addition of being aboe to append |
| // AsyncOutputBuffers. |
| void Append(std::string str, TextForegroundColor fg = TextForegroundColor::kDefault, |
| TextBackgroundColor bg = TextBackgroundColor::kDefault); |
| void Append(fxl::RefPtr<AsyncOutputBuffer> buf); |
| void Append(Syntax syntax, std::string str); |
| void Append(OutputBuffer buf); |
| void Append(const Err& err); |
| |
| // Call to mark this output buffer complete. This will issue the callback |
| // if there is one registered. See is_complete() for additional discussion. |
| // |
| // Doing additional appends or making it complete again after this call wil |
| // trigger a debug assertion. |
| void Complete(); |
| |
| // Helper functions that do Append(...) + Complete() since this is a very |
| // common use case. |
| void Complete(std::string str, TextForegroundColor fg = TextForegroundColor::kDefault, |
| TextBackgroundColor bg = TextBackgroundColor::kDefault); |
| void Complete(fxl::RefPtr<AsyncOutputBuffer> buf); |
| void Complete(Syntax syntax, std::string str); |
| void Complete(OutputBuffer buf); |
| void Complete(const Err& err); |
| |
| // Once this buffer is_complete(), the spans and any sub-AsyncOutputBuffers |
| // can be flattened into one vector. |
| // |
| // This operation is destructive so can only be called once. This node and |
| // all child nodes will be empty after this call. |
| OutputBuffer DestructiveFlatten(); |
| |
| private: |
| FRIEND_REF_COUNTED_THREAD_SAFE(AsyncOutputBuffer); |
| FRIEND_MAKE_REF_COUNTED(AsyncOutputBuffer); |
| |
| AsyncOutputBuffer() = default; |
| |
| // Called when something happened that could have affected is_complete() to |
| // issue the callback. |
| void CheckComplete(); |
| |
| // Retursive callback for DestructiveFlatten() that destructively moves all |
| // spans out of this node and its children into the given output buffer. |
| void DestructiveCollectNodes(OutputBuffer* out); |
| |
| CompletionCallback completion_callback_; |
| |
| // Reference count for how many children in nodes_ are uncompleted. |
| int pending_resolution_ = 0; |
| |
| // Set when Complete() has been called. This does not necessarily mean that |
| // all children have been completed (a prerequisite for is_complete(). |
| bool marked_complete_ = false; |
| |
| // This buffer is a sequence of nodes. A node is either a span of text that's |
| // synchronously available or an owning reference to another async output |
| // buffer that may or may not be filled. |
| using Span = OutputBuffer::Span; |
| using Ref = fxl::RefPtr<AsyncOutputBuffer>; |
| using Node = std::variant<Span, Ref>; |
| std::vector<Node> nodes_; |
| }; |
| |
| } // namespace zxdb |
| |
| #endif // SRC_DEVELOPER_DEBUG_ZXDB_CONSOLE_ASYNC_OUTPUT_BUFFER_H_ |