blob: 46698a30e6d98896ae023f10c7b57b3e33b8b4cb [file] [log] [blame]
/*
* 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_