1/*
2 * Copyright (C) 2014-2019 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#pragma once
27
28#include "BInline.h"
29#include "Mutex.h"
30#include "Sizes.h"
31
32namespace bmalloc {
33
34// StaticPerProcess<T> behaves like PerProcess<T>, but we need to explicitly define storage for T with EXTERN.
35// In this way, we allocate storage for a per-process object statically instead of allocating memory at runtime.
36// To enforce this, we have DECLARE and DEFINE macros. If you do not know about T of StaticPerProcess<T>, you should use PerProcess<T> instead.
37//
38// Usage:
39// In Object.h
40// class Object : public StaticPerProcess<Object> {
41// ...
42// };
43// DECLARE_STATIC_PER_PROCESS_STORAGE(Object);
44//
45// In Object.cpp
46// DEFINE_STATIC_PER_PROCESS_STORAGE(Object);
47//
48// Object* object = Object::get();
49// x = object->field->field;
50//
51// Object will be instantiated only once, even in the presence of concurrency.
52//
53template<typename T> struct StaticPerProcessStorageTraits;
54
55template<typename T>
56class BEXPORT StaticPerProcess {
57public:
58 static T* get()
59 {
60 T* object = getFastCase();
61 if (!object)
62 return getSlowCase();
63 return object;
64 }
65
66 static T* getFastCase()
67 {
68 using Storage = typename StaticPerProcessStorageTraits<T>::Storage;
69 return (Storage::s_object).load(std::memory_order_relaxed);
70 }
71
72 static Mutex& mutex()
73 {
74 using Storage = typename StaticPerProcessStorageTraits<T>::Storage;
75 return Storage::s_mutex;
76 }
77
78private:
79 BNO_INLINE static T* getSlowCase()
80 {
81 using Storage = typename StaticPerProcessStorageTraits<T>::Storage;
82 std::lock_guard<Mutex> lock(Storage::s_mutex);
83 if (!Storage::s_object.load(std::memory_order_consume)) {
84 T* t = new (&Storage::s_memory) T(lock);
85 Storage::s_object.store(t, std::memory_order_release);
86 }
87 return Storage::s_object.load(std::memory_order_consume);
88 }
89};
90
91#define DECLARE_STATIC_PER_PROCESS_STORAGE(Type) \
92template<> struct StaticPerProcessStorageTraits<Type> { \
93 using Memory = typename std::aligned_storage<sizeof(Type), std::alignment_of<Type>::value>::type; \
94 struct BEXPORT Storage { \
95 BEXPORT static std::atomic<Type*> s_object; \
96 BEXPORT static Mutex s_mutex; \
97 BEXPORT static Memory s_memory; \
98 }; \
99};
100
101#define DEFINE_STATIC_PER_PROCESS_STORAGE(Type) \
102 std::atomic<Type*> StaticPerProcessStorageTraits<Type>::Storage::s_object { nullptr }; \
103 Mutex StaticPerProcessStorageTraits<Type>::Storage::s_mutex { }; \
104 StaticPerProcessStorageTraits<Type>::Memory StaticPerProcessStorageTraits<Type>::Storage::s_memory { };
105
106} // namespace bmalloc
107