Add MoveOnly and Pinned (#4526)

This commit is contained in:
Alexander Shabalin
2020-11-17 18:43:11 +03:00
committed by Stanislav Erokhin
parent 98b2f3feec
commit 0dfc99ab1a
13 changed files with 149 additions and 37 deletions
+2
View File
@@ -6,6 +6,8 @@
* Headers should live in the same folder with it's implementation counterpart (if there's one) **TODO**: This does not work with multiple implementations of a single header. * Headers should live in the same folder with it's implementation counterpart (if there's one) **TODO**: This does not work with multiple implementations of a single header.
* Headers should use header guards * Headers should use header guards
* Headers that are designed to be included into both `.cpp`/`.mm` and `.c`/`.m` should have extension `.h`
* Headers that are designed to be included into `.cpp`/`.mm` only should have extension `.hpp`
## Namespace usage ## Namespace usage
@@ -41,10 +41,11 @@
#include "KString.h" #include "KString.h"
#include "Memory.h" #include "Memory.h"
#include "MemoryPrivate.hpp" #include "MemoryPrivate.hpp"
#include "Mutex.hpp"
#include "Natives.h" #include "Natives.h"
#include "Porting.h" #include "Porting.h"
#include "Runtime.h" #include "Runtime.h"
#include "Utils.h" #include "Utils.hpp"
#include "WorkerBoundReference.h" #include "WorkerBoundReference.h"
#include "Weak.h" #include "Weak.h"
@@ -150,20 +151,16 @@ KBoolean g_hasCyclicCollector = true;
#endif // USE_CYCLIC_GC #endif // USE_CYCLIC_GC
// TODO: Consider using ObjHolder. // TODO: Consider using ObjHolder.
class ScopedRefHolder { class ScopedRefHolder : private kotlin::MoveOnly {
public: public:
ScopedRefHolder() = default; ScopedRefHolder() = default;
explicit ScopedRefHolder(KRef obj); explicit ScopedRefHolder(KRef obj);
ScopedRefHolder(const ScopedRefHolder&) = delete;
ScopedRefHolder(ScopedRefHolder&& other) noexcept: obj_(other.obj_) { ScopedRefHolder(ScopedRefHolder&& other) noexcept: obj_(other.obj_) {
other.obj_ = nullptr; other.obj_ = nullptr;
} }
ScopedRefHolder& operator=(const ScopedRefHolder&) = delete;
ScopedRefHolder& operator=(ScopedRefHolder&& other) noexcept { ScopedRefHolder& operator=(ScopedRefHolder&& other) noexcept {
ScopedRefHolder tmp(std::move(other)); ScopedRefHolder tmp(std::move(other));
swap(tmp); swap(tmp);
@@ -191,7 +188,7 @@ struct CycleDetectorRootset {
KStdVector<ScopedRefHolder> heldRefs; KStdVector<ScopedRefHolder> heldRefs;
}; };
class CycleDetector { class CycleDetector : private kotlin::Pinned {
public: public:
static void insertCandidateIfNeeded(KRef object) { static void insertCandidateIfNeeded(KRef object) {
if (canBeACandidate(object)) if (canBeACandidate(object))
@@ -208,10 +205,6 @@ class CycleDetector {
private: private:
CycleDetector() = default; CycleDetector() = default;
~CycleDetector() = default; ~CycleDetector() = default;
CycleDetector(const CycleDetector&) = delete;
CycleDetector(CycleDetector&&) = delete;
CycleDetector& operator=(const CycleDetector&) = delete;
CycleDetector& operator=(CycleDetector&&) = delete;
static CycleDetector& instance() { static CycleDetector& instance() {
// Only store a pointer to CycleDetector in .bss // Only store a pointer to CycleDetector in .bss
@@ -38,11 +38,12 @@
#include "Exceptions.h" #include "Exceptions.h"
#include "ExecFormat.h" #include "ExecFormat.h"
#include "Memory.h" #include "Memory.h"
#include "Mutex.hpp"
#include "Natives.h" #include "Natives.h"
#include "KString.h" #include "KString.h"
#include "SourceInfo.h" #include "SourceInfo.h"
#include "Types.h" #include "Types.h"
#include "Utils.h" #include "Utils.hpp"
#include "ObjCExceptions.h" #include "ObjCExceptions.h"
namespace { namespace {
@@ -279,7 +280,8 @@ RUNTIME_NORETURN void TerminateWithUnhandledException(KRef throwable) {
#if KONAN_HAS_CXX11_EXCEPTION_FUNCTIONS #if KONAN_HAS_CXX11_EXCEPTION_FUNCTIONS
namespace { namespace {
class TerminateHandler { // Copy, move and assign would be safe, but not much useful, so let's delete all (rule of 5)
class TerminateHandler : private kotlin::Pinned {
// In fact, it's safe to call my_handler directly from outside: it will do the job and then invoke original handler, // In fact, it's safe to call my_handler directly from outside: it will do the job and then invoke original handler,
// even if it has not been initialized yet. So one may want to make it public and/or not the class member // even if it has not been initialized yet. So one may want to make it public and/or not the class member
@@ -313,11 +315,6 @@ class TerminateHandler {
return singleton; return singleton;
} }
// Copy, move and assign would be safe, but not much useful, so let's delete all (rule of 5)
TerminateHandler(const TerminateHandler&) = delete;
TerminateHandler(TerminateHandler&&) = delete;
TerminateHandler& operator=(const TerminateHandler&) = delete;
TerminateHandler& operator=(TerminateHandler&&) = delete;
// Dtor might be in use to restore original handler. However, consequent install // Dtor might be in use to restore original handler. However, consequent install
// will not reconstruct handler anyway, so let's keep dtor deleted to avoid confusion. // will not reconstruct handler anyway, so let's keep dtor deleted to avoid confusion.
~TerminateHandler() = delete; ~TerminateHandler() = delete;
@@ -14,8 +14,12 @@
* limitations under the License. * limitations under the License.
*/ */
#ifndef RUNTIME_MUTEX_H
#define RUNTIME_MUTEX_H
#include <cstdint> #include <cstdint>
#include "KAssert.h" #include "KAssert.h"
#include "Utils.hpp"
class SimpleMutex { class SimpleMutex {
private: private:
@@ -37,7 +41,7 @@ class SimpleMutex {
// TODO: use std::lock_guard instead? // TODO: use std::lock_guard instead?
template <class Mutex> template <class Mutex>
class LockGuard { class LockGuard : private kotlin::Pinned {
public: public:
explicit LockGuard(Mutex& mutex_) : mutex(mutex_) { explicit LockGuard(Mutex& mutex_) : mutex(mutex_) {
mutex.lock(); mutex.lock();
@@ -49,7 +53,6 @@ class LockGuard {
private: private:
Mutex& mutex; Mutex& mutex;
LockGuard(const LockGuard&) = delete;
LockGuard& operator=(const LockGuard&) = delete;
}; };
#endif // RUNTIME_MUTEX_H
@@ -39,7 +39,7 @@
#import "ObjCExportPrivate.h" #import "ObjCExportPrivate.h"
#import "ObjCMMAPI.h" #import "ObjCMMAPI.h"
#import "Runtime.h" #import "Runtime.h"
#import "Utils.h" #import "Mutex.hpp"
#import "Exceptions.h" #import "Exceptions.h"
struct ObjCToKotlinMethodAdapter { struct ObjCToKotlinMethodAdapter {
@@ -24,7 +24,7 @@
#import "ObjCExport.h" #import "ObjCExport.h"
#import "Porting.h" #import "Porting.h"
#import "Runtime.h" #import "Runtime.h"
#import "Utils.h" #import "Mutex.hpp"
#import "ObjCExportErrors.h" #import "ObjCExportErrors.h"
@@ -33,7 +33,7 @@
#include "ObjCExportPrivate.h" #include "ObjCExportPrivate.h"
#include "ObjCMMAPI.h" #include "ObjCMMAPI.h"
#include "Types.h" #include "Types.h"
#include "Utils.h" #include "Mutex.hpp"
// Replaced in ObjCExportCodeGenerator. // Replaced in ObjCExportCodeGenerator.
__attribute__((weak)) const char* Kotlin_ObjCInterop_uniquePrefix = nullptr; __attribute__((weak)) const char* Kotlin_ObjCInterop_uniquePrefix = nullptr;
@@ -7,22 +7,18 @@
#define RUNTIME_OBJCMMAPI_H #define RUNTIME_OBJCMMAPI_H
#include "Common.h" #include "Common.h"
#include "Utils.hpp"
#if KONAN_OBJC_INTEROP #if KONAN_OBJC_INTEROP
extern "C" ALWAYS_INLINE void Kotlin_ObjCExport_releaseAssociatedObject(void* associatedObject); extern "C" ALWAYS_INLINE void Kotlin_ObjCExport_releaseAssociatedObject(void* associatedObject);
namespace konan { namespace konan {
class AutoreleasePool { class AutoreleasePool : private kotlin::Pinned {
public: public:
AutoreleasePool(); AutoreleasePool();
~AutoreleasePool(); ~AutoreleasePool();
AutoreleasePool(const AutoreleasePool&) = delete;
AutoreleasePool(AutoreleasePool&&) = delete;
AutoreleasePool& operator=(const AutoreleasePool&) = delete;
AutoreleasePool& operator=(AutoreleasePool&&) = delete;
private: private:
void* handle; void* handle;
}; };
@@ -10,9 +10,10 @@
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "Types.h" #include "Types.h"
#include "Utils.hpp"
template <class F> template <class F>
class ScopedStrictMockFunction { class ScopedStrictMockFunction : private kotlin::MoveOnly {
public: public:
using Mock = testing::StrictMock<testing::MockFunction<F>>; using Mock = testing::StrictMock<testing::MockFunction<F>>;
@@ -24,9 +25,6 @@ public:
*globalMockLocation_ = mock_.get(); *globalMockLocation_ = mock_.get();
} }
ScopedStrictMockFunction(const ScopedStrictMockFunction&) = delete;
ScopedStrictMockFunction& operator=(const ScopedStrictMockFunction&) = delete;
ScopedStrictMockFunction(ScopedStrictMockFunction&& rhs) : globalMockLocation_(rhs.globalMockLocation_), mock_(std::move(rhs.mock_)) { ScopedStrictMockFunction(ScopedStrictMockFunction&& rhs) : globalMockLocation_(rhs.globalMockLocation_), mock_(std::move(rhs.mock_)) {
rhs.globalMockLocation_ = nullptr; rhs.globalMockLocation_ = nullptr;
} }
@@ -0,0 +1,57 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#ifndef RUNTIME_UTILS_H
#define RUNTIME_UTILS_H
namespace kotlin {
// A helper for implementing classes with disabled copy constructor and copy assignment.
// Usage:
// class A: private MoveOnly {
// ...
// };
// Prefer private inheritance to discourage casting instances of `A` to instances
// of `MoveOnly`.
class MoveOnly {
// Hide constructors, assignments and destructor, to discourage operating on an instance of `MoveOnly`.
protected:
MoveOnly() noexcept = default;
MoveOnly(const MoveOnly&) = delete;
MoveOnly(MoveOnly&&) noexcept = default;
MoveOnly& operator=(const MoveOnly&) = delete;
MoveOnly& operator=(MoveOnly&&) noexcept = default;
// Not virtual by design. Since this class hides this destructor, no one can destroy an
// instance of `MoveOnly` directly, so this destructor is never called in a virtual manner.
~MoveOnly() = default;
};
// A helper for implementing classes with disabled copy and move constructors, and copy and move assignments.
// Usage:
// class A: private Pinned {
// ...
// };
// Prefer private inheritance to discourage casting instances of `A` to instances
// of `Pinned`.
class Pinned {
// Hide constructors, assignments and destructor, to discourage operating on an instance of `Pinned`.
protected:
Pinned() noexcept = default;
Pinned(const Pinned&) = delete;
Pinned(Pinned&&) = delete;
Pinned& operator=(const Pinned&) = delete;
Pinned& operator=(Pinned&&) = delete;
// Not virtual by design. Since this class hides this destructor, no one can destroy an
// instance of `Pinned` directly, so this destructor is never called in a virtual manner.
~Pinned() = default;
};
} // namespace kotlin
#endif // RUNTIME_UTILS_H
@@ -0,0 +1,66 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#include "Utils.hpp"
#include <type_traits>
#include "gtest/gtest.h"
namespace {
// TODO: use std variants when we move to C++17
template <typename T>
constexpr bool is_nothrow_default_constructible_v = std::is_nothrow_default_constructible<T>::value;
template <typename T>
constexpr bool is_nothrow_destructible_v = std::is_nothrow_destructible<T>::value;
template <typename T>
constexpr bool is_copy_constructible_v = std::is_copy_constructible<T>::value;
template <typename T>
constexpr bool is_copy_assignable_v = std::is_copy_assignable<T>::value;
template <typename T>
constexpr bool is_move_constructible_v = std::is_move_constructible<T>::value;
template <typename T>
constexpr bool is_move_assignable_v = std::is_move_assignable<T>::value;
template <typename T>
constexpr bool is_nothrow_move_constructible_v = std::is_nothrow_move_constructible<T>::value;
template <typename T>
constexpr bool is_nothrow_move_assignable_v = std::is_nothrow_move_assignable<T>::value;
struct A {
int field;
};
class MoveOnlyImpl : private kotlin::MoveOnly {
public:
A a;
};
class PinnedImpl : private kotlin::Pinned {
public:
A a;
};
} // namespace
TEST(UtilsTest, MoveOnlyImpl) {
static_assert(is_nothrow_default_constructible_v<MoveOnlyImpl>, "Must be nothrow default constructible");
static_assert(is_nothrow_destructible_v<MoveOnlyImpl>, "Must be nothrow destructible");
static_assert(!is_copy_constructible_v<MoveOnlyImpl>, "Must not be copy constructible");
static_assert(!is_copy_assignable_v<MoveOnlyImpl>, "Must not be copy assignable");
static_assert(is_nothrow_move_constructible_v<MoveOnlyImpl>, "Must be nothrow move constructible");
static_assert(is_nothrow_move_assignable_v<MoveOnlyImpl>, "Must be nothrow move assignable");
static_assert(sizeof(MoveOnlyImpl) == sizeof(A), "Must not increase size");
}
TEST(UtilsTest, PinnedImpl) {
static_assert(is_nothrow_default_constructible_v<PinnedImpl>, "Must be nothrow default constructible");
static_assert(is_nothrow_destructible_v<PinnedImpl>, "Must be nothrow destructible");
static_assert(!is_copy_constructible_v<PinnedImpl>, "Must not be copy constructible");
static_assert(!is_copy_assignable_v<PinnedImpl>, "Must not be copy assignable");
static_assert(!is_move_constructible_v<PinnedImpl>, "Must not be move constructible");
static_assert(!is_move_assignable_v<PinnedImpl>, "Must not be move assignable");
static_assert(sizeof(PinnedImpl) == sizeof(A), "Must not increase size");
}
@@ -22,7 +22,7 @@
#import "ObjCExportInit.h" #import "ObjCExportInit.h"
#import "ObjCExportPrivate.h" #import "ObjCExportPrivate.h"
#import "Runtime.h" #import "Runtime.h"
#import "Utils.h" #import "Mutex.hpp"
#import "Exceptions.h" #import "Exceptions.h"
extern "C" id objc_retainAutoreleaseReturnValue(id self); extern "C" id objc_retainAutoreleaseReturnValue(id self);
@@ -23,7 +23,7 @@
#import "ObjCExport.h" #import "ObjCExport.h"
#import "Runtime.h" #import "Runtime.h"
#import "Utils.h" #import "Mutex.hpp"
extern "C" { extern "C" {