blob: 4905dae4557f6ccfff26dd3b5ff8556ca3240370 [file] [log] [blame]
Primiano Tucciacb6ca32020-08-19 13:27:52 +02001/*
2 * Copyright (C) 2020 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_ARENA_H_
18#define INCLUDE_PERFETTO_PROTOZERO_MESSAGE_ARENA_H_
19
20#include <stdint.h>
21
22#include <list>
23#include <type_traits>
24
25#include "perfetto/base/export.h"
26#include "perfetto/base/logging.h"
27#include "perfetto/protozero/message.h"
28
29namespace protozero {
30
31class Message;
32
33// Object allocator for fixed-sized protozero::Message objects.
34// It's a simple bump-pointer allocator which leverages the stack-alike
35// usage pattern of protozero nested messages. It avoids hitting the system
36// allocator in most cases, by reusing the same block, and falls back on
37// allocating new blocks only when using deeply nested messages (which are
38// extremely rare).
39// This is used by RootMessage<T> to handle the storage for root-level messages.
40class PERFETTO_EXPORT MessageArena {
41 public:
42 MessageArena();
43 ~MessageArena();
44
45 // Strictly no copies or moves as this is used to hand out pointers.
46 MessageArena(const MessageArena&) = delete;
47 MessageArena& operator=(const MessageArena&) = delete;
48 MessageArena(MessageArena&&) = delete;
49 MessageArena& operator=(MessageArena&&) = delete;
50
51 // Allocates a new Message object.
52 Message* NewMessage();
53
54 // Deletes the last message allocated. The |msg| argument is used only for
55 // DCHECKs, it MUST be the pointer obtained by the last NewMessage() call.
56 void DeleteLastMessage(Message* msg) {
57 PERFETTO_DCHECK(!blocks_.empty() && blocks_.back().entries > 0);
58 PERFETTO_DCHECK(&blocks_.back().storage[blocks_.back().entries - 1] ==
59 static_cast<void*>(msg));
60 DeleteLastMessageInternal();
61 }
62
63 // Resets the state of the arena, clearing up all but one block. This is used
64 // to avoid leaking outstanding unfinished sub-messages while recycling the
65 // RootMessage object (this is extremely rare due to the RAII scoped handles
66 // but could happen if some client does some overly clever std::move() trick).
67 void Reset() {
68 PERFETTO_DCHECK(!blocks_.empty());
69 blocks_.resize(1);
70 auto& block = blocks_.back();
71 block.entries = 0;
72 PERFETTO_ASAN_POISON(block.storage, sizeof(block.storage));
73 }
74
75 private:
76 void DeleteLastMessageInternal();
77
78 struct Block {
79 static constexpr size_t kCapacity = 16;
80
81 Block() { PERFETTO_ASAN_POISON(storage, sizeof(storage)); }
82
83 std::aligned_storage<sizeof(Message), alignof(Message)>::type
84 storage[kCapacity];
85 uint32_t entries = 0; // # Message entries used (<= kCapacity).
86 };
87
88 // blocks are used to hand out pointers and must not be moved. Hence why
89 // std::list rather than std::vector.
90 std::list<Block> blocks_;
91};
92
93} // namespace protozero
94
95#endif // INCLUDE_PERFETTO_PROTOZERO_MESSAGE_ARENA_H_