blob: b805a74ab343d8cb5741f75e19b73b1513090e6e [file] [log] [blame]
/*
* Copyright (C) 2014-2019 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include "BInline.h"
#include "Mutex.h"
#include "Sizes.h"
namespace bmalloc {
// StaticPerProcess<T> behaves like PerProcess<T>, but we need to explicitly define storage for T with EXTERN.
// In this way, we allocate storage for a per-process object statically instead of allocating memory at runtime.
// 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.
//
// Usage:
// In Object.h
// class Object : public StaticPerProcess<Object> {
// ...
// };
// DECLARE_STATIC_PER_PROCESS_STORAGE(Object);
//
// In Object.cpp
// DEFINE_STATIC_PER_PROCESS_STORAGE(Object);
//
// Object* object = Object::get();
// x = object->field->field;
//
// Object will be instantiated only once, even in the presence of concurrency.
//
template<typename T> struct StaticPerProcessStorageTraits;
template<typename T>
class BEXPORT StaticPerProcess {
public:
static T* get()
{
T* object = getFastCase();
if (!object)
return getSlowCase();
return object;
}
static T* getFastCase()
{
using Storage = typename StaticPerProcessStorageTraits<T>::Storage;
return (Storage::s_object).load(std::memory_order_relaxed);
}
static Mutex& mutex()
{
using Storage = typename StaticPerProcessStorageTraits<T>::Storage;
return Storage::s_mutex;
}
private:
BNO_INLINE static T* getSlowCase()
{
using Storage = typename StaticPerProcessStorageTraits<T>::Storage;
LockHolder lock(Storage::s_mutex);
if (!Storage::s_object.load(std::memory_order_consume)) {
T* t = new (&Storage::s_memory) T(lock);
Storage::s_object.store(t, std::memory_order_release);
}
return Storage::s_object.load(std::memory_order_consume);
}
};
#define DECLARE_STATIC_PER_PROCESS_STORAGE(Type) \
template<> struct StaticPerProcessStorageTraits<Type> { \
using Memory = typename std::aligned_storage<sizeof(Type), std::alignment_of<Type>::value>::type; \
struct BEXPORT Storage { \
BEXPORT static std::atomic<Type*> s_object; \
BEXPORT static Mutex s_mutex; \
BEXPORT static Memory s_memory; \
}; \
};
#define DEFINE_STATIC_PER_PROCESS_STORAGE(Type) \
std::atomic<Type*> StaticPerProcessStorageTraits<Type>::Storage::s_object { nullptr }; \
Mutex StaticPerProcessStorageTraits<Type>::Storage::s_mutex { }; \
StaticPerProcessStorageTraits<Type>::Memory StaticPerProcessStorageTraits<Type>::Storage::s_memory { };
} // namespace bmalloc