| // 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_ |