1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7
8 #ifndef GOOGLE_PROTOBUF_ARENA_CLEANUP_H__
9 #define GOOGLE_PROTOBUF_ARENA_CLEANUP_H__
10
11 #include <cstddef>
12 #include <cstring>
13 #include <vector>
14
15 #include "absl/base/attributes.h"
16 #include "absl/base/prefetch.h"
17
18 // Must be included last.
19 #include "google/protobuf/port_def.inc"
20
21 namespace google {
22 namespace protobuf {
23 namespace internal {
24
25 class SerialArena;
26
27 namespace cleanup {
28
29 // Helper function invoking the destructor of `object`
30 template <typename T>
arena_destruct_object(void * object)31 void arena_destruct_object(void* object) {
32 reinterpret_cast<T*>(object)->~T();
33 }
34
35 // CleanupNode contains the object (`elem`) that needs to be
36 // destroyed, and the function to destroy it (`destructor`)
37 // elem must be aligned at minimum on a 4 byte boundary.
38 struct CleanupNode {
39 // Optimization: performs a prefetch on the elem for the cleanup node. We
40 // explicitly use NTA prefetch here to avoid polluting remote caches: we are
41 // destroying these instances, there is no purpose for these cache lines to
42 // linger around in remote caches.
PrefetchCleanupNode43 ABSL_ATTRIBUTE_ALWAYS_INLINE void Prefetch() {
44 // TODO: we should also prefetch the destructor code once
45 // processors support code prefetching.
46 absl::PrefetchToLocalCacheNta(elem);
47 }
48
49 // Destroys the object referenced by the cleanup node.
DestroyCleanupNode50 ABSL_ATTRIBUTE_ALWAYS_INLINE void Destroy() { destructor(elem); }
51
52 void* elem;
53 void (*destructor)(void*);
54 };
55
56 // Manages the list of cleanup nodes in a chunked linked list. Chunks grow by
57 // factors of two up to a limit. Trivially destructible, but Cleanup() must be
58 // called before destruction.
59 class ChunkList {
60 public:
Add(void * elem,void (* destructor)(void *),SerialArena & arena)61 PROTOBUF_ALWAYS_INLINE void Add(void* elem, void (*destructor)(void*),
62 SerialArena& arena) {
63 if (PROTOBUF_PREDICT_TRUE(next_ < limit_)) {
64 AddFromExisting(elem, destructor);
65 return;
66 }
67 AddFallback(elem, destructor, arena);
68 }
69
70 // Runs all inserted cleanups and frees allocated chunks. Must be called
71 // before destruction.
72 void Cleanup(const SerialArena& arena);
73
74 private:
75 struct Chunk;
76 friend class internal::SerialArena;
77
78 void AddFallback(void* elem, void (*destructor)(void*), SerialArena& arena);
AddFromExisting(void * elem,void (* destructor)(void *))79 ABSL_ATTRIBUTE_ALWAYS_INLINE void AddFromExisting(void* elem,
80 void (*destructor)(void*)) {
81 *next_++ = CleanupNode{elem, destructor};
82 }
83
84 // Returns the pointers to the to-be-cleaned objects.
85 std::vector<void*> PeekForTesting();
86
87 Chunk* head_ = nullptr;
88 CleanupNode* next_ = nullptr;
89 CleanupNode* limit_ = nullptr;
90 // Current prefetch position. Data from `next_` up to but not including
91 // `prefetch_ptr_` is software prefetched. Used in SerialArena prefetching.
92 const char* prefetch_ptr_ = nullptr;
93 };
94
95 } // namespace cleanup
96 } // namespace internal
97 } // namespace protobuf
98 } // namespace google
99
100 #include "google/protobuf/port_undef.inc"
101
102 #endif // GOOGLE_PROTOBUF_ARENA_CLEANUP_H__
103