mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
SharedResourcePointer: Get a SharedResourcePointer instance without creating one
- Also refactored internals to use weak/shared pointers
This commit is contained in:
parent
6e8210d6b1
commit
418d7b9c38
40 changed files with 256 additions and 43 deletions
|
|
@ -285,6 +285,7 @@
|
|||
#include "misc/juce_EnumHelpers_test.cpp"
|
||||
#include "containers/juce_FixedSizeFunction_test.cpp"
|
||||
#include "javascript/juce_JSONSerialisation_test.cpp"
|
||||
#include "memory/juce_SharedResourcePointer_test.cpp"
|
||||
#if JUCE_MAC || JUCE_IOS
|
||||
#include "native/juce_ObjCHelpers_mac_test.mm"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -87,74 +87,94 @@ public:
|
|||
using. Otherwise, if this is the first SharedResourcePointer to be created,
|
||||
then a shared object will be created automatically.
|
||||
*/
|
||||
SharedResourcePointer()
|
||||
{
|
||||
initialise();
|
||||
}
|
||||
SharedResourcePointer() = default;
|
||||
|
||||
SharedResourcePointer (const SharedResourcePointer&)
|
||||
{
|
||||
initialise();
|
||||
}
|
||||
/** Copy constructor. */
|
||||
SharedResourcePointer (const SharedResourcePointer&) = default;
|
||||
|
||||
/** Move constructor. */
|
||||
SharedResourcePointer (SharedResourcePointer&&) noexcept = default;
|
||||
|
||||
/** Destructor.
|
||||
If no other SharedResourcePointer objects exist, this will also delete
|
||||
the shared object to which it refers.
|
||||
*/
|
||||
~SharedResourcePointer()
|
||||
{
|
||||
auto& holder = getSharedObjectHolder();
|
||||
const SpinLock::ScopedLockType sl (holder.lock);
|
||||
~SharedResourcePointer() = default;
|
||||
|
||||
if (--(holder.refCount) == 0)
|
||||
holder.sharedInstance = nullptr;
|
||||
}
|
||||
/** Returns a pointer to the shared object. */
|
||||
operator SharedObjectType*() const noexcept { return sharedObject.get(); }
|
||||
|
||||
/** Returns the shared object. */
|
||||
operator SharedObjectType*() const noexcept { return sharedObject; }
|
||||
|
||||
/** Returns the shared object. */
|
||||
/** Returns a reference to the shared object. */
|
||||
SharedObjectType& get() const noexcept { return *sharedObject; }
|
||||
|
||||
/** Returns the object that this pointer references. */
|
||||
/** Returns a reference to the shared object. */
|
||||
SharedObjectType& getObject() const noexcept { return *sharedObject; }
|
||||
|
||||
/** Returns the shared object pointer. */
|
||||
SharedObjectType* operator->() const noexcept { return sharedObject; }
|
||||
/** Returns a pointer to the shared object. */
|
||||
SharedObjectType* operator->() const noexcept { return sharedObject.get(); }
|
||||
|
||||
/** Returns the number of SharedResourcePointers that are currently holding the shared object. */
|
||||
int getReferenceCount() const noexcept { return getSharedObjectHolder().refCount; }
|
||||
/** Returns a reference to the shared object. */
|
||||
SharedObjectType& operator*() const noexcept { return *sharedObject; }
|
||||
|
||||
#ifndef DOXYGEN
|
||||
[[deprecated ("If you are relying on this function please inform the JUCE team as we are planing on removing this in a subsequent release")]]
|
||||
int getReferenceCount() const noexcept { return (int) sharedObject.use_count(); }
|
||||
#endif
|
||||
|
||||
/** Returns the SharedResourcePointer if one already exists, or a null optional otherwise. */
|
||||
static std::optional<SharedResourcePointer> getSharedObjectWithoutCreating()
|
||||
{
|
||||
if (auto sharedPtr = weak().lock())
|
||||
return SharedResourcePointer { std::move (sharedPtr) };
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
struct SharedObjectHolder
|
||||
explicit SharedResourcePointer (std::shared_ptr<SharedObjectType>&& other) noexcept
|
||||
: sharedObject (std::move (other))
|
||||
{
|
||||
SpinLock lock;
|
||||
std::unique_ptr<SharedObjectType> sharedInstance;
|
||||
int refCount;
|
||||
jassert (sharedObject != nullptr);
|
||||
}
|
||||
|
||||
class Weak
|
||||
{
|
||||
public:
|
||||
std::shared_ptr<SharedObjectType> lock()
|
||||
{
|
||||
const SpinLock::ScopedLockType lock { mutex };
|
||||
return ptr.lock();
|
||||
}
|
||||
|
||||
std::shared_ptr<SharedObjectType> lockOrCreate()
|
||||
{
|
||||
const SpinLock::ScopedLockType lock { mutex };
|
||||
|
||||
if (auto locked = ptr.lock())
|
||||
return locked;
|
||||
|
||||
auto shared = std::make_shared<SharedObjectType>();
|
||||
ptr = shared;
|
||||
return shared;
|
||||
}
|
||||
|
||||
private:
|
||||
SpinLock mutex;
|
||||
std::weak_ptr<SharedObjectType> ptr;
|
||||
};
|
||||
|
||||
static SharedObjectHolder& getSharedObjectHolder() noexcept
|
||||
inline static Weak& weak()
|
||||
{
|
||||
static void* holder [(sizeof (SharedObjectHolder) + sizeof (void*) - 1) / sizeof (void*)] = { nullptr };
|
||||
return *reinterpret_cast<SharedObjectHolder*> (holder);
|
||||
static Weak weak;
|
||||
return weak;
|
||||
}
|
||||
|
||||
SharedObjectType* sharedObject;
|
||||
|
||||
void initialise()
|
||||
{
|
||||
auto& holder = getSharedObjectHolder();
|
||||
const SpinLock::ScopedLockType sl (holder.lock);
|
||||
|
||||
if (++(holder.refCount) == 1)
|
||||
holder.sharedInstance.reset (new SharedObjectType());
|
||||
|
||||
sharedObject = holder.sharedInstance.get();
|
||||
}
|
||||
std::shared_ptr<SharedObjectType> sharedObject = weak().lockOrCreate();
|
||||
|
||||
// There's no need to assign to a SharedResourcePointer because every
|
||||
// instance of the class is exactly the same!
|
||||
SharedResourcePointer& operator= (const SharedResourcePointer&) = delete;
|
||||
SharedResourcePointer& operator= (SharedResourcePointer&&) noexcept = delete;
|
||||
|
||||
JUCE_LEAK_DETECTOR (SharedResourcePointer)
|
||||
};
|
||||
|
|
|
|||
87
modules/juce_core/memory/juce_SharedResourcePointer_test.cpp
Normal file
87
modules/juce_core/memory/juce_SharedResourcePointer_test.cpp
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
class SharedResourcePointerTest final : public UnitTest
|
||||
{
|
||||
public:
|
||||
SharedResourcePointerTest()
|
||||
: UnitTest ("SharedResourcePointer", UnitTestCategories::memory) {}
|
||||
|
||||
void runTest() final
|
||||
{
|
||||
beginTest ("Only one instance is created");
|
||||
{
|
||||
static int count = 0;
|
||||
struct CountIncrementer { CountIncrementer() { ++count; } };
|
||||
expect (count == 0);
|
||||
|
||||
const SharedResourcePointer<CountIncrementer> instance1;
|
||||
expect (count == 1);
|
||||
|
||||
const SharedResourcePointer<CountIncrementer> instance2;
|
||||
expect (count == 1);
|
||||
|
||||
expect (&instance1.get() == &instance2.get());
|
||||
}
|
||||
|
||||
beginTest ("The shared object is destroyed when the reference count reaches 0");
|
||||
{
|
||||
static int count = 0;
|
||||
struct ReferenceCounter
|
||||
{
|
||||
ReferenceCounter() { ++count; }
|
||||
~ReferenceCounter() { --count; }
|
||||
};
|
||||
|
||||
expect (count == 0);
|
||||
|
||||
{
|
||||
const SharedResourcePointer<ReferenceCounter> instance1;
|
||||
const SharedResourcePointer<ReferenceCounter> instance2;
|
||||
expect (count == 1);
|
||||
}
|
||||
|
||||
expect (count == 0);
|
||||
}
|
||||
|
||||
beginTest ("getInstanceWithoutCreating()");
|
||||
{
|
||||
struct Object{};
|
||||
|
||||
expect (SharedResourcePointer<Object>::getSharedObjectWithoutCreating() == std::nullopt);
|
||||
|
||||
{
|
||||
const SharedResourcePointer<Object> instance;
|
||||
expect (&SharedResourcePointer<Object>::getSharedObjectWithoutCreating()->get() == &instance.get());
|
||||
}
|
||||
|
||||
expect (SharedResourcePointer<Object>::getSharedObjectWithoutCreating() == std::nullopt);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static SharedResourcePointerTest sharedResourcePointerTest;
|
||||
|
||||
} // namespace juce
|
||||
|
|
@ -37,6 +37,7 @@ namespace juce::UnitTestCategories
|
|||
static const String gui { "GUI" };
|
||||
static const String json { "JSON" };
|
||||
static const String maths { "Maths" };
|
||||
static const String memory { "Memory" };
|
||||
static const String midi { "MIDI" };
|
||||
static const String native { "Native" };
|
||||
static const String networking { "Networking" };
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue