1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef INCLUDE_PERFETTO_PROTOZERO_MESSAGE_HANDLE_H_ 18 #define INCLUDE_PERFETTO_PROTOZERO_MESSAGE_HANDLE_H_ 19 20 #include <functional> 21 22 #include "perfetto/base/export.h" 23 #include "perfetto/protozero/message.h" 24 #include "perfetto/protozero/scattered_stream_writer.h" 25 26 namespace protozero { 27 28 class Message; 29 30 class PERFETTO_EXPORT_COMPONENT MessageFinalizationListener { 31 public: 32 virtual ~MessageFinalizationListener(); 33 virtual void OnMessageFinalized(Message* message) = 0; 34 }; 35 36 // MessageHandle allows to decouple the lifetime of a proto message 37 // from the underlying storage. It gives the following guarantees: 38 // - The underlying message is finalized (if still alive) if the handle goes 39 // out of scope. 40 // - In Debug / DCHECK_ALWAYS_ON builds, the handle becomes null once the 41 // message is finalized. This is to enforce the append-only API. For instance 42 // when adding two repeated messages, the addition of the 2nd one forces 43 // the finalization of the first. 44 // Think about this as a WeakPtr<Message> which calls 45 // Message::Finalize() when going out of scope. 46 47 class PERFETTO_EXPORT_COMPONENT MessageHandleBase { 48 public: ~MessageHandleBase()49 ~MessageHandleBase() { 50 if (message_) { 51 #if PERFETTO_DCHECK_IS_ON() 52 PERFETTO_DCHECK(generation_ == message_->generation_); 53 #endif 54 FinalizeMessage(); 55 } 56 } 57 58 // Move-only type. MessageHandleBase(MessageHandleBase && other)59 MessageHandleBase(MessageHandleBase&& other) noexcept { 60 Move(std::move(other)); 61 } 62 63 MessageHandleBase& operator=(MessageHandleBase&& other) noexcept { 64 // If the current handle was pointing to a message and is being reset to a 65 // new one, finalize the old message. However, if the other message is the 66 // same as the one we point to, don't finalize. 67 if (message_ && message_ != other.message_) 68 FinalizeMessage(); 69 Move(std::move(other)); 70 return *this; 71 } 72 73 explicit operator bool() const { 74 #if PERFETTO_DCHECK_IS_ON() 75 PERFETTO_DCHECK(!message_ || generation_ == message_->generation_); 76 #endif 77 return !!message_; 78 } 79 set_finalization_listener(MessageFinalizationListener * listener)80 void set_finalization_listener(MessageFinalizationListener* listener) { 81 listener_ = listener; 82 } 83 84 // Returns a (non-owned, it should not be deleted) pointer to the 85 // ScatteredStreamWriter used to write the message data. The Message becomes 86 // unusable after this point. 87 // 88 // The caller can now write directly, without using protozero::Message. TakeStreamWriter()89 ScatteredStreamWriter* TakeStreamWriter() { 90 ScatteredStreamWriter* stream_writer = message_->stream_writer_; 91 #if PERFETTO_DCHECK_IS_ON() 92 message_->set_handle(nullptr); 93 #endif 94 message_ = nullptr; 95 listener_ = nullptr; 96 return stream_writer; 97 } 98 99 protected: message_(message)100 explicit MessageHandleBase(Message* message = nullptr) : message_(message) { 101 #if PERFETTO_DCHECK_IS_ON() 102 generation_ = message_ ? message->generation_ : 0; 103 if (message_) 104 message_->set_handle(this); 105 #endif 106 } 107 108 Message* operator->() const { 109 #if PERFETTO_DCHECK_IS_ON() 110 PERFETTO_DCHECK(!message_ || generation_ == message_->generation_); 111 #endif 112 return message_; 113 } 114 Message& operator*() const { return *(operator->()); } 115 116 private: 117 friend class Message; 118 MessageHandleBase(const MessageHandleBase&) = delete; 119 MessageHandleBase& operator=(const MessageHandleBase&) = delete; 120 reset_message()121 void reset_message() { 122 // This is called by Message::Finalize(). 123 PERFETTO_DCHECK(message_->is_finalized()); 124 message_ = nullptr; 125 listener_ = nullptr; 126 } 127 Move(MessageHandleBase && other)128 void Move(MessageHandleBase&& other) { 129 message_ = other.message_; 130 other.message_ = nullptr; 131 listener_ = other.listener_; 132 other.listener_ = nullptr; 133 #if PERFETTO_DCHECK_IS_ON() 134 if (message_) { 135 generation_ = message_->generation_; 136 message_->set_handle(this); 137 } 138 #endif 139 } 140 FinalizeMessage()141 void FinalizeMessage() { 142 // |message_| and |listener_| may be cleared by reset_message() during 143 // Message::Finalize(). 144 auto* listener = listener_; 145 auto* message = message_; 146 message->Finalize(); 147 if (listener) 148 listener->OnMessageFinalized(message); 149 } 150 151 Message* message_; 152 MessageFinalizationListener* listener_ = nullptr; 153 #if PERFETTO_DCHECK_IS_ON() 154 uint32_t generation_; 155 #endif 156 }; 157 158 template <typename T> 159 class MessageHandle : public MessageHandleBase { 160 public: MessageHandle()161 MessageHandle() : MessageHandle(nullptr) {} MessageHandle(T * message)162 explicit MessageHandle(T* message) : MessageHandleBase(message) {} 163 164 explicit operator bool() const { return MessageHandleBase::operator bool(); } 165 166 T& operator*() const { 167 return static_cast<T&>(MessageHandleBase::operator*()); 168 } 169 170 T* operator->() const { 171 return static_cast<T*>(MessageHandleBase::operator->()); 172 } 173 get()174 T* get() const { return static_cast<T*>(MessageHandleBase::operator->()); } 175 }; 176 177 } // namespace protozero 178 179 #endif // INCLUDE_PERFETTO_PROTOZERO_MESSAGE_HANDLE_H_ 180