| // 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 <variant> |
| |
| #include "lib/fit/function.h" |
| #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. |
| // |
| // Usage guidelines for general sanity: |
| // |
| // - The same code is responsible for Complete()ing an AsyncOutputBuffer as for creating it. |
| // |
| // - Don't pass an AsyncOutputBuffer to another function and have the function Complete() it. |
| // |
| // - Functions that need async output should generally return an AsyncOutputBuffer that the |
| // function arranges to be Complete() when possible. Callers can append this to other buffers as |
| // needed. |
| // |
| // - If a function needs to append to an existing AsyncOutputBuffer, pass by raw pointer and do |
| // not have the function Complete() it. If that function needs to append asynchronously do |
| // something, it should append a new AsyncOutputBuffer that it will take responsibility for |
| // Complete()ing. |
| // |
| // 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 = fit::callback<void()>; |
| |
| // 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 able 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 will 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; |
| ~AsyncOutputBuffer() = default; |
| |
| // Called when something happened that could have affected is_complete() to issue the callback. |
| void CheckComplete(); |
| |
| // Recursive 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_ |