1/*
2 * Copyright (C) 2014-2018 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// Usage:
35// Object* object = PerProcess<Object>::get();
36// x = object->field->field;
37//
38// Object will be instantiated only once, even in the face of concurrency.
39//
40// NOTE: If you observe global side-effects of the Object constructor, be
41// sure to lock the Object mutex. For example:
42//
43// Object() : m_field(...) { globalFlag = true }
44//
45// Object* object = PerProcess<Object>::get();
46// x = object->m_field; // OK
47// if (globalFlag) { ... } // Undefined behavior.
48//
49// std::lock_guard<Mutex> lock(PerProcess<Object>::mutex());
50// Object* object = PerProcess<Object>::get(lock);
51// if (globalFlag) { ... } // OK.
52
53struct PerProcessData {
54 const char* disambiguator;
55 void* memory;
56 size_t size;
57 size_t alignment;
58 Mutex mutex;
59 bool isInitialized;
60 PerProcessData* next;
61};
62
63constexpr unsigned stringHash(const char* string)
64{
65 unsigned result = 5381;
66 while (char c = *string++)
67 result = result * 33 + c;
68 return result;
69}
70
71BEXPORT PerProcessData* getPerProcessData(unsigned disambiguatorHash, const char* disambiguator, size_t size, size_t alignment);
72
73template<typename T>
74class PerProcess {
75public:
76 static T* get()
77 {
78 T* object = getFastCase();
79 if (!object)
80 return getSlowCase();
81 return object;
82 }
83
84 static T* getFastCase()
85 {
86 return s_object.load(std::memory_order_relaxed);
87 }
88
89 static Mutex& mutex()
90 {
91 if (!s_data)
92 coalesce();
93 return s_data->mutex;
94 }
95
96private:
97 static void coalesce()
98 {
99 if (s_data)
100 return;
101
102 const char* disambiguator = __PRETTY_FUNCTION__;
103 s_data = getPerProcessData(stringHash(disambiguator), disambiguator, sizeof(T), std::alignment_of<T>::value);
104 }
105
106 BNO_INLINE static T* getSlowCase()
107 {
108 std::lock_guard<Mutex> lock(mutex());
109 if (!s_object.load()) {
110 if (s_data->isInitialized)
111 s_object.store(static_cast<T*>(s_data->memory));
112 else {
113 T* t = new (s_data->memory) T(lock);
114 s_object.store(t);
115 s_data->isInitialized = true;
116 }
117 }
118 return s_object.load();
119 }
120
121 static std::atomic<T*> s_object;
122 static PerProcessData* s_data;
123};
124
125template<typename T>
126std::atomic<T*> PerProcess<T>::s_object { nullptr };
127
128template<typename T>
129PerProcessData* PerProcess<T>::s_data { nullptr };
130
131} // namespace bmalloc
132