| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifndef INCLUDE_PERFETTO_PROTOZERO_MESSAGE_HANDLE_H_ |
| #define INCLUDE_PERFETTO_PROTOZERO_MESSAGE_HANDLE_H_ |
| |
| #include <functional> |
| |
| #include "perfetto/base/export.h" |
| #include "perfetto/protozero/message.h" |
| #include "perfetto/protozero/scattered_stream_writer.h" |
| |
| namespace protozero { |
| |
| class Message; |
| |
| // MessageHandle allows to decouple the lifetime of a proto message |
| // from the underlying storage. It gives the following guarantees: |
| // - The underlying message is finalized (if still alive) if the handle goes |
| // out of scope. |
| // - In Debug / DCHECK_ALWAYS_ON builds, the handle becomes null once the |
| // message is finalized. This is to enforce the append-only API. For instance |
| // when adding two repeated messages, the addition of the 2nd one forces |
| // the finalization of the first. |
| // Think about this as a WeakPtr<Message> which calls |
| // Message::Finalize() when going out of scope. |
| |
| class PERFETTO_EXPORT_COMPONENT MessageHandleBase { |
| public: |
| ~MessageHandleBase() { |
| if (message_) { |
| #if PERFETTO_DCHECK_IS_ON() |
| PERFETTO_DCHECK(generation_ == message_->generation_); |
| #endif |
| FinalizeMessage(); |
| } |
| } |
| |
| // Move-only type. |
| MessageHandleBase(MessageHandleBase&& other) noexcept { |
| Move(std::move(other)); |
| } |
| |
| MessageHandleBase& operator=(MessageHandleBase&& other) noexcept { |
| // If the current handle was pointing to a message and is being reset to a |
| // new one, finalize the old message. However, if the other message is the |
| // same as the one we point to, don't finalize. |
| if (message_ && message_ != other.message_) |
| FinalizeMessage(); |
| Move(std::move(other)); |
| return *this; |
| } |
| |
| explicit operator bool() const { |
| #if PERFETTO_DCHECK_IS_ON() |
| PERFETTO_DCHECK(!message_ || generation_ == message_->generation_); |
| #endif |
| return !!message_; |
| } |
| |
| // Returns a (non-owned, it should not be deleted) pointer to the |
| // ScatteredStreamWriter used to write the message data. The Message becomes |
| // unusable after this point. |
| // |
| // The caller can now write directly, without using protozero::Message. |
| ScatteredStreamWriter* TakeStreamWriter() { |
| ScatteredStreamWriter* stream_writer = message_->stream_writer_; |
| #if PERFETTO_DCHECK_IS_ON() |
| message_->set_handle(nullptr); |
| #endif |
| message_ = nullptr; |
| return stream_writer; |
| } |
| |
| protected: |
| explicit MessageHandleBase(Message* message = nullptr) : message_(message) { |
| #if PERFETTO_DCHECK_IS_ON() |
| generation_ = message_ ? message->generation_ : 0; |
| if (message_) |
| message_->set_handle(this); |
| #endif |
| } |
| |
| Message* operator->() const { |
| #if PERFETTO_DCHECK_IS_ON() |
| PERFETTO_DCHECK(!message_ || generation_ == message_->generation_); |
| #endif |
| return message_; |
| } |
| Message& operator*() const { return *(operator->()); } |
| |
| private: |
| friend class Message; |
| MessageHandleBase(const MessageHandleBase&) = delete; |
| MessageHandleBase& operator=(const MessageHandleBase&) = delete; |
| |
| void reset_message() { |
| // This is called by Message::Finalize(). |
| PERFETTO_DCHECK(message_->is_finalized()); |
| message_ = nullptr; |
| } |
| |
| void Move(MessageHandleBase&& other) { |
| message_ = other.message_; |
| other.message_ = nullptr; |
| #if PERFETTO_DCHECK_IS_ON() |
| if (message_) { |
| generation_ = message_->generation_; |
| message_->set_handle(this); |
| } |
| #endif |
| } |
| |
| void FinalizeMessage() { message_->Finalize(); } |
| |
| Message* message_; |
| #if PERFETTO_DCHECK_IS_ON() |
| uint32_t generation_; |
| #endif |
| }; |
| |
| template <typename T> |
| class MessageHandle : public MessageHandleBase { |
| public: |
| MessageHandle() : MessageHandle(nullptr) {} |
| explicit MessageHandle(T* message) : MessageHandleBase(message) {} |
| |
| explicit operator bool() const { return MessageHandleBase::operator bool(); } |
| |
| T& operator*() const { |
| return static_cast<T&>(MessageHandleBase::operator*()); |
| } |
| |
| T* operator->() const { |
| return static_cast<T*>(MessageHandleBase::operator->()); |
| } |
| |
| T* get() const { return static_cast<T*>(MessageHandleBase::operator->()); } |
| }; |
| |
| } // namespace protozero |
| |
| #endif // INCLUDE_PERFETTO_PROTOZERO_MESSAGE_HANDLE_H_ |