blob: 85a74e73a3af5089db33e5c3719f22c8c07c2282 [file] [log] [blame]
// 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_