Feng Xiao | 6ef984a | 2014-11-10 17:34:54 -0800 | [diff] [blame] | 1 | // Protocol Buffers - Google's data interchange format |
| 2 | // Copyright 2008 Google Inc. All rights reserved. |
| 3 | // https://developers.google.com/protocol-buffers/ |
| 4 | // |
| 5 | // Redistribution and use in source and binary forms, with or without |
| 6 | // modification, are permitted provided that the following conditions are |
| 7 | // met: |
| 8 | // |
| 9 | // * Redistributions of source code must retain the above copyright |
| 10 | // notice, this list of conditions and the following disclaimer. |
| 11 | // * Redistributions in binary form must reproduce the above |
| 12 | // copyright notice, this list of conditions and the following disclaimer |
| 13 | // in the documentation and/or other materials provided with the |
| 14 | // distribution. |
| 15 | // * Neither the name of Google Inc. nor the names of its |
| 16 | // contributors may be used to endorse or promote products derived from |
| 17 | // this software without specific prior written permission. |
| 18 | // |
| 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 30 | |
| 31 | // This header is logically internal, but is made public because it is used |
| 32 | // from protocol-compiler-generated code, which may reside in other components. |
| 33 | |
| 34 | #ifndef GOOGLE_PROTOBUF_ARENA_H__ |
| 35 | #define GOOGLE_PROTOBUF_ARENA_H__ |
| 36 | |
Jisi Liu | 885b612 | 2015-02-28 14:51:22 -0800 | [diff] [blame^] | 37 | #include <typeinfo> |
| 38 | |
Feng Xiao | 6ef984a | 2014-11-10 17:34:54 -0800 | [diff] [blame] | 39 | #include <google/protobuf/stubs/atomic_sequence_num.h> |
| 40 | #include <google/protobuf/stubs/atomicops.h> |
Jisi Liu | 885b612 | 2015-02-28 14:51:22 -0800 | [diff] [blame^] | 41 | #include <google/protobuf/stubs/common.h> |
Feng Xiao | 6ef984a | 2014-11-10 17:34:54 -0800 | [diff] [blame] | 42 | #include <google/protobuf/stubs/type_traits.h> |
| 43 | |
| 44 | namespace google { |
| 45 | namespace protobuf { |
| 46 | |
| 47 | class Arena; // defined below |
| 48 | class Message; // message.h |
| 49 | |
| 50 | namespace internal { |
| 51 | class ArenaString; // arenastring.h |
| 52 | class LazyField; // lazy_field.h |
| 53 | |
| 54 | template<typename Type> |
| 55 | class GenericTypeHandler; // repeated_field.h |
| 56 | |
| 57 | // Templated cleanup methods. |
| 58 | template<typename T> void arena_destruct_object(void* object) { |
| 59 | reinterpret_cast<T*>(object)->~T(); |
| 60 | } |
| 61 | template<typename T> void arena_delete_object(void* object) { |
| 62 | delete reinterpret_cast<T*>(object); |
| 63 | } |
| 64 | inline void arena_free(void* object, size_t size) { |
| 65 | free(object); |
| 66 | } |
| 67 | |
| 68 | } // namespace internal |
| 69 | |
| 70 | // ArenaOptions provides optional additional parameters to arena construction |
| 71 | // that control its block-allocation behavior. |
| 72 | struct ArenaOptions { |
| 73 | // This defines the size of the first block requested from the system malloc. |
| 74 | // Subsequent block sizes will increase in a geometric series up to a maximum. |
| 75 | size_t start_block_size; |
| 76 | |
| 77 | // This defines the maximum block size requested from system malloc (unless an |
| 78 | // individual arena allocation request occurs with a size larger than this |
| 79 | // maximum). Requested block sizes increase up to this value, then remain |
| 80 | // here. |
| 81 | size_t max_block_size; |
| 82 | |
| 83 | // An initial block of memory for the arena to use, or NULL for none. If |
| 84 | // provided, the block must live at least as long as the arena itself. The |
| 85 | // creator of the Arena retains ownership of the block after the Arena is |
| 86 | // destroyed. |
| 87 | char* initial_block; |
| 88 | |
| 89 | // The size of the initial block, if provided. |
| 90 | size_t initial_block_size; |
| 91 | |
| 92 | // A function pointer to an alloc method that returns memory blocks of size |
| 93 | // requested. By default, it contains a ptr to the malloc function. |
Jisi Liu | 885b612 | 2015-02-28 14:51:22 -0800 | [diff] [blame^] | 94 | // |
| 95 | // NOTE: block_alloc and dealloc functions are expected to behave like |
| 96 | // malloc and free, including Asan poisoning. |
Feng Xiao | 6ef984a | 2014-11-10 17:34:54 -0800 | [diff] [blame] | 97 | void* (*block_alloc)(size_t); |
| 98 | // A function pointer to a dealloc method that takes ownership of the blocks |
| 99 | // from the arena. By default, it contains a ptr to a wrapper function that |
| 100 | // calls free. |
| 101 | void (*block_dealloc)(void*, size_t); |
| 102 | |
Jisi Liu | 885b612 | 2015-02-28 14:51:22 -0800 | [diff] [blame^] | 103 | // Hooks for adding external functionality such as user-specific metrics |
| 104 | // collection, specific debugging abilities, etc. |
| 105 | // Init hook may return a pointer to a cookie to be stored in the arena. |
| 106 | // reset and destruction hooks will then be called with the same cookie |
| 107 | // pointer. This allows us to save an external object per arena instance and |
| 108 | // use it on the other hooks (Note: It is just as legal for init to return |
| 109 | // NULL and not use the cookie feature). |
| 110 | // on_arena_reset and on_arena_destruction also receive the space used in |
| 111 | // the arena just before the reset. |
| 112 | void* (*on_arena_init)(Arena* arena); |
| 113 | void (*on_arena_reset)(Arena* arena, void* cookie, uint64 space_used); |
| 114 | void (*on_arena_destruction)(Arena* arena, void* cookie, uint64 space_used); |
| 115 | |
| 116 | // type_name is promised to be a static string - its lifetime extends to |
| 117 | // match program's lifetime. |
| 118 | void (*on_arena_allocation)(const char* type_name, uint64 alloc_size, |
| 119 | Arena* arena, void* cookie); |
| 120 | |
Feng Xiao | 6ef984a | 2014-11-10 17:34:54 -0800 | [diff] [blame] | 121 | ArenaOptions() |
| 122 | : start_block_size(kDefaultStartBlockSize), |
| 123 | max_block_size(kDefaultMaxBlockSize), |
| 124 | initial_block(NULL), |
| 125 | initial_block_size(0), |
| 126 | block_alloc(&malloc), |
Jisi Liu | 885b612 | 2015-02-28 14:51:22 -0800 | [diff] [blame^] | 127 | block_dealloc(&internal::arena_free), |
| 128 | on_arena_init(NULL), |
| 129 | on_arena_reset(NULL), |
| 130 | on_arena_destruction(NULL), |
| 131 | on_arena_allocation(NULL) {} |
Feng Xiao | 6ef984a | 2014-11-10 17:34:54 -0800 | [diff] [blame] | 132 | |
| 133 | private: |
| 134 | // Constants define default starting block size and max block size for |
| 135 | // arena allocator behavior -- see descriptions above. |
| 136 | static const size_t kDefaultStartBlockSize = 256; |
| 137 | static const size_t kDefaultMaxBlockSize = 8192; |
| 138 | }; |
| 139 | |
| 140 | // Arena allocator. Arena allocation replaces ordinary (heap-based) allocation |
| 141 | // with new/delete, and improves performance by aggregating allocations into |
| 142 | // larger blocks and freeing allocations all at once. Protocol messages are |
| 143 | // allocated on an arena by using Arena::CreateMessage<T>(Arena*), below, and |
| 144 | // are automatically freed when the arena is destroyed. |
| 145 | // |
| 146 | // This is a thread-safe implementation: multiple threads may allocate from the |
| 147 | // arena concurrently. Destruction is not thread-safe and the destructing |
| 148 | // thread must synchronize with users of the arena first. |
| 149 | class LIBPROTOBUF_EXPORT Arena { |
| 150 | public: |
| 151 | // Arena constructor taking custom options. See ArenaOptions below for |
| 152 | // descriptions of the options available. |
Jisi Liu | 885b612 | 2015-02-28 14:51:22 -0800 | [diff] [blame^] | 153 | explicit Arena(const ArenaOptions& options) : options_(options) { |
| 154 | Init(); |
Feng Xiao | 6ef984a | 2014-11-10 17:34:54 -0800 | [diff] [blame] | 155 | } |
| 156 | |
| 157 | // Default constructor with sensible default options, tuned for average |
| 158 | // use-cases. |
| 159 | Arena() { |
Jisi Liu | 885b612 | 2015-02-28 14:51:22 -0800 | [diff] [blame^] | 160 | Init(); |
Feng Xiao | 6ef984a | 2014-11-10 17:34:54 -0800 | [diff] [blame] | 161 | } |
| 162 | |
| 163 | // Destructor deletes all owned heap allocated objects, and destructs objects |
| 164 | // that have non-trivial destructors, except for proto2 message objects whose |
| 165 | // destructors can be skipped. Also, frees all blocks except the initial block |
| 166 | // if it was passed in. |
Jisi Liu | 885b612 | 2015-02-28 14:51:22 -0800 | [diff] [blame^] | 167 | ~Arena(); |
Feng Xiao | 6ef984a | 2014-11-10 17:34:54 -0800 | [diff] [blame] | 168 | |
| 169 | // API to create proto2 message objects on the arena. If the arena passed in |
| 170 | // is NULL, then a heap allocated object is returned. Type T must be a message |
| 171 | // defined in a .proto file with cc_enable_arenas set to true, otherwise a |
| 172 | // compilation error will occur. |
| 173 | // |
| 174 | // RepeatedField and RepeatedPtrField may also be instantiated directly on an |
| 175 | // arena with this method: they act as "arena-capable message types" for the |
| 176 | // purposes of the Arena API. |
| 177 | template <typename T> GOOGLE_ATTRIBUTE_ALWAYS_INLINE |
| 178 | static T* CreateMessage(::google::protobuf::Arena* arena) { |
| 179 | if (arena == NULL) { |
| 180 | return new T; |
| 181 | } else { |
| 182 | return arena->CreateMessageInternal<T>(static_cast<T*>(0)); |
| 183 | } |
| 184 | } |
| 185 | |
| 186 | // API to create any objects on the arena. Note that only the object will |
| 187 | // be created on the arena; the underlying ptrs (in case of a proto2 message) |
| 188 | // will be still heap allocated. Proto messages should usually be allocated |
| 189 | // with CreateMessage<T>() instead. |
| 190 | template <typename T> GOOGLE_ATTRIBUTE_ALWAYS_INLINE |
| 191 | static T* Create(::google::protobuf::Arena* arena) { |
| 192 | if (arena == NULL) { |
| 193 | return new T(); |
| 194 | } else { |
| 195 | return arena->CreateInternal<T>( |
| 196 | SkipDeleteList<T>(static_cast<T*>(0))); |
| 197 | } |
| 198 | } |
| 199 | |
| 200 | // Version of the above with one constructor argument for the created object. |
| 201 | template <typename T, typename Arg> GOOGLE_ATTRIBUTE_ALWAYS_INLINE |
| 202 | static T* Create(::google::protobuf::Arena* arena, const Arg& arg) { |
| 203 | if (arena == NULL) { |
| 204 | return new T(arg); |
| 205 | } else { |
| 206 | return arena->CreateInternal<T>(SkipDeleteList<T>(static_cast<T*>(0)), |
| 207 | arg); |
| 208 | } |
| 209 | } |
| 210 | |
| 211 | // Version of the above with two constructor arguments for the created object. |
| 212 | template <typename T, typename Arg1, typename Arg2> GOOGLE_ATTRIBUTE_ALWAYS_INLINE |
| 213 | static T* Create(::google::protobuf::Arena* arena, const Arg1& arg1, const Arg2& arg2) { |
| 214 | if (arena == NULL) { |
| 215 | return new T(arg1, arg2); |
| 216 | } else { |
| 217 | return arena->CreateInternal<T>(SkipDeleteList<T>(static_cast<T*>(0)), |
| 218 | arg1, |
| 219 | arg2); |
| 220 | } |
| 221 | } |
| 222 | |
Jisi Liu | 885b612 | 2015-02-28 14:51:22 -0800 | [diff] [blame^] | 223 | // Version of the above with three constructor arguments for the created |
| 224 | // object. |
| 225 | template <typename T, typename Arg1, typename Arg2, typename Arg3> |
| 226 | GOOGLE_ATTRIBUTE_ALWAYS_INLINE static T* Create(::google::protobuf::Arena* arena, |
| 227 | const Arg1& arg1, const Arg2& arg2, |
| 228 | const Arg3& arg3) { |
| 229 | if (arena == NULL) { |
| 230 | return new T(arg1, arg2, arg3); |
| 231 | } else { |
| 232 | return arena->CreateInternal<T>(SkipDeleteList<T>(static_cast<T*>(0)), |
| 233 | arg1, |
| 234 | arg2, |
| 235 | arg3); |
| 236 | } |
| 237 | } |
| 238 | |
| 239 | // Version of the above with four constructor arguments for the created |
| 240 | // object. |
| 241 | template <typename T, typename Arg1, typename Arg2, typename Arg3, |
| 242 | typename Arg4> |
| 243 | GOOGLE_ATTRIBUTE_ALWAYS_INLINE static T* Create(::google::protobuf::Arena* arena, |
| 244 | const Arg1& arg1, const Arg2& arg2, |
| 245 | const Arg3& arg3, const Arg4& arg4) { |
| 246 | if (arena == NULL) { |
| 247 | return new T(arg1, arg2, arg3, arg4); |
| 248 | } else { |
| 249 | return arena->CreateInternal<T>(SkipDeleteList<T>(static_cast<T*>(0)), |
| 250 | arg1, |
| 251 | arg2, |
| 252 | arg3, |
| 253 | arg4); |
| 254 | } |
| 255 | } |
| 256 | |
Feng Xiao | 6ef984a | 2014-11-10 17:34:54 -0800 | [diff] [blame] | 257 | // Create an array of object type T on the arena. Type T must have a trivial |
| 258 | // constructor, as it will not be invoked when created on the arena. |
| 259 | template <typename T> GOOGLE_ATTRIBUTE_ALWAYS_INLINE |
| 260 | static T* CreateArray(::google::protobuf::Arena* arena, size_t num_elements) { |
| 261 | if (arena == NULL) { |
| 262 | return new T[num_elements]; |
| 263 | } else { |
Jisi Liu | 885b612 | 2015-02-28 14:51:22 -0800 | [diff] [blame^] | 264 | return arena->CreateInternalRawArray<T>(num_elements); |
Feng Xiao | 6ef984a | 2014-11-10 17:34:54 -0800 | [diff] [blame] | 265 | } |
| 266 | } |
| 267 | |
| 268 | // Returns the total space used by the arena, which is the sums of the sizes |
| 269 | // of the underlying blocks. The total space used may not include the new |
| 270 | // blocks that are allocated by this arena from other threads concurrently |
| 271 | // with the call to this method. |
Jisi Liu | 885b612 | 2015-02-28 14:51:22 -0800 | [diff] [blame^] | 272 | uint64 SpaceAllocated() const GOOGLE_ATTRIBUTE_NOINLINE; |
| 273 | // As above, but does not include any free space in underlying blocks. |
Feng Xiao | 6ef984a | 2014-11-10 17:34:54 -0800 | [diff] [blame] | 274 | uint64 SpaceUsed() const GOOGLE_ATTRIBUTE_NOINLINE; |
| 275 | |
| 276 | // Frees all storage allocated by this arena after calling destructors |
| 277 | // registered with OwnDestructor() and freeing objects registered with Own(). |
| 278 | // Any objects allocated on this arena are unusable after this call. It also |
| 279 | // returns the total space used by the arena which is the sums of the sizes |
| 280 | // of the allocated blocks. This method is not thread-safe. |
| 281 | uint64 Reset() GOOGLE_ATTRIBUTE_NOINLINE; |
| 282 | |
| 283 | // Adds |object| to a list of heap-allocated objects to be freed with |delete| |
| 284 | // when the arena is destroyed or reset. |
| 285 | template <typename T> GOOGLE_ATTRIBUTE_NOINLINE |
| 286 | void Own(T* object) { |
| 287 | OwnInternal(object, google::protobuf::internal::is_convertible<T*, ::google::protobuf::Message*>()); |
| 288 | } |
| 289 | |
| 290 | // Adds |object| to a list of objects whose destructors will be manually |
| 291 | // called when the arena is destroyed or reset. This differs from Own() in |
| 292 | // that it does not free the underlying memory with |delete|; hence, it is |
| 293 | // normally only used for objects that are placement-newed into |
| 294 | // arena-allocated memory. |
| 295 | template <typename T> GOOGLE_ATTRIBUTE_NOINLINE |
| 296 | void OwnDestructor(T* object) { |
| 297 | if (object != NULL) { |
| 298 | AddListNode(object, &internal::arena_destruct_object<T>); |
| 299 | } |
| 300 | } |
| 301 | |
| 302 | // Adds a custom member function on an object to the list of destructors that |
| 303 | // will be manually called when the arena is destroyed or reset. This differs |
| 304 | // from OwnDestructor() in that any member function may be specified, not only |
| 305 | // the class destructor. |
| 306 | void OwnCustomDestructor(void* object, void (*destruct)(void*)) |
| 307 | GOOGLE_ATTRIBUTE_NOINLINE { |
| 308 | AddListNode(object, destruct); |
| 309 | } |
| 310 | |
| 311 | // Retrieves the arena associated with |value| if |value| is an arena-capable |
| 312 | // message, or NULL otherwise. This differs from value->GetArena() in that the |
| 313 | // latter is a virtual call, while this method is a templated call that |
| 314 | // resolves at compile-time. |
| 315 | template<typename T> GOOGLE_ATTRIBUTE_ALWAYS_INLINE |
Jisi Liu | 885b612 | 2015-02-28 14:51:22 -0800 | [diff] [blame^] | 316 | static inline ::google::protobuf::Arena* GetArena(const T* value) { |
Feng Xiao | 6ef984a | 2014-11-10 17:34:54 -0800 | [diff] [blame] | 317 | return GetArenaInternal(value, static_cast<T*>(0)); |
| 318 | } |
| 319 | |
| 320 | // Helper typetrait that indicates support for arenas in a type T at compile |
| 321 | // time. This is public only to allow construction of higher-level templated |
| 322 | // utilities. is_arena_constructable<T>::value is an instance of |
| 323 | // google::protobuf::internal::true_type if the message type T has arena support enabled, and |
| 324 | // google::protobuf::internal::false_type otherwise. |
| 325 | // |
| 326 | // This is inside Arena because only Arena has the friend relationships |
| 327 | // necessary to see the underlying generated code traits. |
| 328 | template<typename T> |
| 329 | struct is_arena_constructable { |
| 330 | template<typename U> |
| 331 | static char ArenaConstructable( |
| 332 | const typename U::InternalArenaConstructable_*); |
| 333 | template<typename U> |
| 334 | static double ArenaConstructable(...); |
| 335 | |
| 336 | // This will resolve to either google::protobuf::internal::true_type or google::protobuf::internal::false_type. |
| 337 | typedef google::protobuf::internal::integral_constant<bool, |
| 338 | sizeof(ArenaConstructable<const T>(static_cast<const T*>(0))) == |
| 339 | sizeof(char)> type; |
| 340 | static const type value; |
| 341 | }; |
| 342 | |
| 343 | private: |
| 344 | // Blocks are variable length malloc-ed objects. The following structure |
| 345 | // describes the common header for all blocks. |
| 346 | struct Block { |
| 347 | void* owner; // &ThreadCache of thread that owns this block, or |
| 348 | // &this->owner if not yet owned by a thread. |
| 349 | Block* next; // Next block in arena (may have different owner) |
| 350 | // ((char*) &block) + pos is next available byte. It is always |
| 351 | // aligned at a multiple of 8 bytes. |
| 352 | size_t pos; |
| 353 | size_t size; // total size of the block. |
| 354 | size_t avail() const GOOGLE_ATTRIBUTE_ALWAYS_INLINE { return size - pos; } |
| 355 | // data follows |
| 356 | }; |
| 357 | |
Feng Xiao | 6ef984a | 2014-11-10 17:34:54 -0800 | [diff] [blame] | 358 | template<typename Type> friend class ::google::protobuf::internal::GenericTypeHandler; |
| 359 | friend class MockArena; // For unit-testing. |
| 360 | friend class internal::ArenaString; // For AllocateAligned. |
| 361 | friend class internal::LazyField; // For CreateMaybeMessage. |
| 362 | |
| 363 | struct ThreadCache { |
| 364 | // The ThreadCache is considered valid as long as this matches the |
| 365 | // lifecycle_id of the arena being used. |
| 366 | int64 last_lifecycle_id_seen; |
| 367 | Block* last_block_used_; |
| 368 | }; |
| 369 | |
| 370 | static const size_t kHeaderSize = sizeof(Block); |
| 371 | static google::protobuf::internal::SequenceNumber lifecycle_id_generator_; |
Feng Xiao | 8d5d7cc | 2014-12-09 17:05:10 -0800 | [diff] [blame] | 372 | #ifdef PROTOBUF_USE_DLLS |
Josh Haberman | cb3caf1 | 2015-02-17 18:23:41 -0800 | [diff] [blame] | 373 | // Thread local variables cannot be exposed through DLL interface but we can |
| 374 | // wrap them in static functions. |
Feng Xiao | 8d5d7cc | 2014-12-09 17:05:10 -0800 | [diff] [blame] | 375 | static ThreadCache& thread_cache(); |
| 376 | #else |
Jisi Liu | 885b612 | 2015-02-28 14:51:22 -0800 | [diff] [blame^] | 377 | static __thread ThreadCache thread_cache_; |
Feng Xiao | 8d5d7cc | 2014-12-09 17:05:10 -0800 | [diff] [blame] | 378 | static ThreadCache& thread_cache() { return thread_cache_; } |
| 379 | #endif |
Feng Xiao | 6ef984a | 2014-11-10 17:34:54 -0800 | [diff] [blame] | 380 | |
| 381 | // SFINAE for skipping addition to delete list for a Type. This is mainly to |
| 382 | // skip proto2/proto1 message objects with cc_enable_arenas=true from being |
| 383 | // part of the delete list. Also, note, compiler will optimize out the branch |
| 384 | // in CreateInternal<T>. |
| 385 | // |
| 386 | template<typename T> |
| 387 | static inline bool SkipDeleteList(typename T::DestructorSkippable_*) { |
| 388 | return true; |
| 389 | } |
| 390 | |
| 391 | // For non message objects, we skip addition to delete list if the object has |
| 392 | // a trivial destructor. |
| 393 | template<typename T> |
| 394 | static inline bool SkipDeleteList(...) { |
| 395 | return google::protobuf::internal::has_trivial_destructor<T>::value; |
| 396 | } |
| 397 | |
| 398 | // CreateMessage<T> requires that T supports arenas, but this private method |
| 399 | // works whether or not T supports arenas. These are not exposed to user code |
| 400 | // as it can cause confusing API usages, and end up having double free in |
| 401 | // user code. These are used only internally from LazyField and Repeated |
| 402 | // fields, since they are designed to work in all mode combinations. |
| 403 | template<typename Msg> GOOGLE_ATTRIBUTE_ALWAYS_INLINE |
| 404 | static Msg* CreateMaybeMessage( |
| 405 | Arena* arena, typename Msg::InternalArenaConstructable_*) { |
| 406 | return CreateMessage<Msg>(arena); |
| 407 | } |
| 408 | |
| 409 | template<typename T> GOOGLE_ATTRIBUTE_ALWAYS_INLINE |
| 410 | static T* CreateMaybeMessage(Arena* arena, ...) { |
| 411 | return Create<T>(arena); |
| 412 | } |
| 413 | |
Jisi Liu | 885b612 | 2015-02-28 14:51:22 -0800 | [diff] [blame^] | 414 | // Just allocate the required size for the given type assuming the |
| 415 | // type has a trivial constructor. |
| 416 | template<typename T> GOOGLE_ATTRIBUTE_ALWAYS_INLINE |
| 417 | inline T* CreateInternalRawArray(uint32 num_elements) { |
| 418 | return static_cast<T*>(AllocateAligned(sizeof(T) * num_elements)); |
| 419 | } |
| 420 | |
Feng Xiao | 6ef984a | 2014-11-10 17:34:54 -0800 | [diff] [blame] | 421 | template <typename T> GOOGLE_ATTRIBUTE_ALWAYS_INLINE |
| 422 | inline T* CreateInternal( |
| 423 | bool skip_explicit_ownership) { |
| 424 | T* t = new (AllocateAligned(sizeof(T))) T(); |
| 425 | if (!skip_explicit_ownership) { |
| 426 | AddListNode(t, &internal::arena_destruct_object<T>); |
| 427 | } |
| 428 | return t; |
| 429 | } |
| 430 | |
| 431 | template <typename T, typename Arg> GOOGLE_ATTRIBUTE_ALWAYS_INLINE |
| 432 | inline T* CreateInternal( |
| 433 | bool skip_explicit_ownership, const Arg& arg) { |
| 434 | T* t = new (AllocateAligned(sizeof(T))) T(arg); |
| 435 | if (!skip_explicit_ownership) { |
| 436 | AddListNode(t, &internal::arena_destruct_object<T>); |
| 437 | } |
| 438 | return t; |
| 439 | } |
| 440 | |
| 441 | template <typename T, typename Arg1, typename Arg2> GOOGLE_ATTRIBUTE_ALWAYS_INLINE |
| 442 | inline T* CreateInternal( |
| 443 | bool skip_explicit_ownership, const Arg1& arg1, const Arg2& arg2) { |
| 444 | T* t = new (AllocateAligned(sizeof(T))) T(arg1, arg2); |
| 445 | if (!skip_explicit_ownership) { |
| 446 | AddListNode(t, &internal::arena_destruct_object<T>); |
| 447 | } |
| 448 | return t; |
| 449 | } |
| 450 | |
Jisi Liu | 885b612 | 2015-02-28 14:51:22 -0800 | [diff] [blame^] | 451 | template <typename T, typename Arg1, typename Arg2, typename Arg3> |
| 452 | GOOGLE_ATTRIBUTE_ALWAYS_INLINE inline T* CreateInternal(bool skip_explicit_ownership, |
| 453 | const Arg1& arg1, |
| 454 | const Arg2& arg2, |
| 455 | const Arg3& arg3) { |
| 456 | T* t = new (AllocateAligned(sizeof(T))) T(arg1, arg2, arg3); |
| 457 | if (!skip_explicit_ownership) { |
| 458 | AddListNode(t, &internal::arena_destruct_object<T>); |
| 459 | } |
| 460 | return t; |
| 461 | } |
| 462 | |
| 463 | template <typename T, typename Arg1, typename Arg2, typename Arg3, |
| 464 | typename Arg4> |
| 465 | GOOGLE_ATTRIBUTE_ALWAYS_INLINE inline T* CreateInternal(bool skip_explicit_ownership, |
| 466 | const Arg1& arg1, |
| 467 | const Arg2& arg2, |
| 468 | const Arg3& arg3, |
| 469 | const Arg4& arg4) { |
| 470 | T* t = new (AllocateAligned(sizeof(T))) T(arg1, arg2, arg3, arg4); |
| 471 | if (!skip_explicit_ownership) { |
| 472 | AddListNode(t, &internal::arena_destruct_object<T>); |
| 473 | } |
| 474 | return t; |
| 475 | } |
| 476 | |
Feng Xiao | 6ef984a | 2014-11-10 17:34:54 -0800 | [diff] [blame] | 477 | template <typename T> GOOGLE_ATTRIBUTE_ALWAYS_INLINE |
| 478 | inline T* CreateMessageInternal(typename T::InternalArenaConstructable_*) { |
| 479 | return CreateInternal<T, Arena*>(SkipDeleteList<T>(static_cast<T*>(0)), |
| 480 | this); |
| 481 | } |
| 482 | |
Jisi Liu | 885b612 | 2015-02-28 14:51:22 -0800 | [diff] [blame^] | 483 | // CreateInArenaStorage is used to implement map field. Without it, |
| 484 | // google::protobuf::Map need to call generated message's protected arena constructor, |
| 485 | // which needs to declare google::protobuf::Map as friend of generated message. |
| 486 | template <typename T> |
| 487 | static void CreateInArenaStorage(T* ptr, Arena* arena) { |
| 488 | CreateInArenaStorageInternal(ptr, arena, is_arena_constructable<T>::value); |
| 489 | } |
| 490 | template <typename T> |
| 491 | static void CreateInArenaStorageInternal( |
| 492 | T* ptr, Arena* arena, google::protobuf::internal::true_type) { |
| 493 | new (ptr) T(arena); |
| 494 | } |
| 495 | |
| 496 | template <typename T> |
| 497 | static void CreateInArenaStorageInternal( |
| 498 | T* ptr, Arena* arena, google::protobuf::internal::false_type) { |
| 499 | new (ptr) T; |
| 500 | } |
| 501 | |
Feng Xiao | 6ef984a | 2014-11-10 17:34:54 -0800 | [diff] [blame] | 502 | // These implement Own(), which registers an object for deletion (destructor |
| 503 | // call and operator delete()). The second parameter has type 'true_type' if T |
| 504 | // is a subtype of ::google::protobuf::Message and 'false_type' otherwise. Collapsing |
| 505 | // all template instantiations to one for generic Message reduces code size, |
| 506 | // using the virtual destructor instead. |
| 507 | template<typename T> GOOGLE_ATTRIBUTE_ALWAYS_INLINE |
| 508 | void OwnInternal(T* object, google::protobuf::internal::true_type) { |
| 509 | if (object != NULL) { |
| 510 | AddListNode(object, &internal::arena_delete_object< ::google::protobuf::Message >); |
| 511 | } |
| 512 | } |
| 513 | template<typename T> GOOGLE_ATTRIBUTE_ALWAYS_INLINE |
| 514 | void OwnInternal(T* object, google::protobuf::internal::false_type) { |
| 515 | if (object != NULL) { |
| 516 | AddListNode(object, &internal::arena_delete_object<T>); |
| 517 | } |
| 518 | } |
| 519 | |
| 520 | // Implementation for GetArena(). Only message objects with |
| 521 | // InternalArenaConstructable_ tags can be associated with an arena, and such |
| 522 | // objects must implement a GetArenaNoVirtual() method. |
| 523 | template<typename T> GOOGLE_ATTRIBUTE_ALWAYS_INLINE |
Jisi Liu | 885b612 | 2015-02-28 14:51:22 -0800 | [diff] [blame^] | 524 | static inline ::google::protobuf::Arena* GetArenaInternal(const T* value, |
Feng Xiao | 6ef984a | 2014-11-10 17:34:54 -0800 | [diff] [blame] | 525 | typename T::InternalArenaConstructable_*) { |
| 526 | return value->GetArenaNoVirtual(); |
| 527 | } |
| 528 | |
| 529 | template<typename T> GOOGLE_ATTRIBUTE_ALWAYS_INLINE |
Jisi Liu | 885b612 | 2015-02-28 14:51:22 -0800 | [diff] [blame^] | 530 | static inline ::google::protobuf::Arena* GetArenaInternal(const T* value, ...) { |
Feng Xiao | 6ef984a | 2014-11-10 17:34:54 -0800 | [diff] [blame] | 531 | return NULL; |
| 532 | } |
| 533 | |
| 534 | |
| 535 | void* AllocateAligned(size_t size); |
| 536 | |
Jisi Liu | 885b612 | 2015-02-28 14:51:22 -0800 | [diff] [blame^] | 537 | void Init(); |
Feng Xiao | 6ef984a | 2014-11-10 17:34:54 -0800 | [diff] [blame] | 538 | |
| 539 | // Free all blocks and return the total space used which is the sums of sizes |
| 540 | // of the all the allocated blocks. |
| 541 | uint64 FreeBlocks(); |
| 542 | |
| 543 | // Add object pointer and cleanup function pointer to the list. |
| 544 | // TODO(rohananil, cfallin): We could pass in a sub-arena into this method |
| 545 | // to avoid polluting blocks of this arena with list nodes. This would help in |
| 546 | // mixed mode (where many protobufs have cc_enable_arenas=false), and is an |
| 547 | // alternative to a chunked linked-list, but with extra overhead of *next. |
| 548 | void AddListNode(void* elem, void (*cleanup)(void*)); |
| 549 | // Delete or Destruct all objects owned by the arena. |
| 550 | void CleanupList(); |
| 551 | |
| 552 | inline void SetThreadCacheBlock(Block* block) { |
Feng Xiao | 8d5d7cc | 2014-12-09 17:05:10 -0800 | [diff] [blame] | 553 | thread_cache().last_block_used_ = block; |
| 554 | thread_cache().last_lifecycle_id_seen = lifecycle_id_; |
Feng Xiao | 6ef984a | 2014-11-10 17:34:54 -0800 | [diff] [blame] | 555 | } |
| 556 | |
| 557 | int64 lifecycle_id_; // Unique for each arena. Changes on Reset(). |
Feng Xiao | 6ef984a | 2014-11-10 17:34:54 -0800 | [diff] [blame] | 558 | |
| 559 | google::protobuf::internal::AtomicWord blocks_; // Head of linked list of all allocated blocks |
| 560 | google::protobuf::internal::AtomicWord hint_; // Fast thread-local block access |
| 561 | |
| 562 | // Node contains the ptr of the object to be cleaned up and the associated |
| 563 | // cleanup function ptr. |
| 564 | struct Node { |
| 565 | void* elem; // Pointer to the object to be cleaned up. |
| 566 | void (*cleanup)(void*); // Function pointer to the destructor or deleter. |
| 567 | Node* next; // Next node in the list. |
| 568 | }; |
| 569 | |
| 570 | google::protobuf::internal::AtomicWord cleanup_list_; // Head of a linked list of nodes containing object |
| 571 | // ptrs and cleanup methods. |
| 572 | |
| 573 | bool owns_first_block_; // Indicates that arena owns the first block |
| 574 | Mutex blocks_lock_; |
| 575 | |
| 576 | void AddBlock(Block* b); |
| 577 | void* SlowAlloc(size_t n); |
| 578 | Block* FindBlock(void* me); |
| 579 | Block* NewBlock(void* me, Block* my_last_block, size_t n, |
| 580 | size_t start_block_size, size_t max_block_size); |
| 581 | static void* AllocFromBlock(Block* b, size_t n); |
Jisi Liu | 885b612 | 2015-02-28 14:51:22 -0800 | [diff] [blame^] | 582 | template <typename Key, typename T> |
| 583 | friend class Map; |
| 584 | |
| 585 | // The arena may save a cookie it receives from the external on_init hook |
| 586 | // and then use it when calling the on_reset and on_destruction hooks. |
| 587 | void* hooks_cookie_; |
| 588 | |
| 589 | ArenaOptions options_; |
| 590 | |
Feng Xiao | 6ef984a | 2014-11-10 17:34:54 -0800 | [diff] [blame] | 591 | GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Arena); |
| 592 | }; |
| 593 | |
| 594 | template<typename T> |
| 595 | const typename Arena::is_arena_constructable<T>::type |
| 596 | Arena::is_arena_constructable<T>::value = |
| 597 | typename Arena::is_arena_constructable<T>::type(); |
| 598 | |
| 599 | } // namespace protobuf |
| 600 | |
| 601 | } // namespace google |
| 602 | #endif // GOOGLE_PROTOBUF_ARENA_H__ |