[K/N] Refactor allocators ^KT-52130
Merge-request: KT-MR-6135 Merged-by: Alexander Shabalin <Alexander.Shabalin@jetbrains.com>
This commit is contained in:
committed by
Space
parent
a05bb3c77c
commit
663e843544
@@ -5,7 +5,7 @@
|
|||||||
## Headers
|
## Headers
|
||||||
|
|
||||||
* 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 can use either `#pragma once` or 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 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`
|
* Headers that are designed to be included into `.cpp`/`.mm` only should have extension `.hpp`
|
||||||
|
|
||||||
@@ -23,8 +23,8 @@
|
|||||||
* For `extern "C"` declarations emulate namespaces with `Kotlin_[module_name]_` prefixes.
|
* For `extern "C"` declarations emulate namespaces with `Kotlin_[module_name]_` prefixes.
|
||||||
* To mark type as move-only, privately inherit from `kotlin::MoveOnly`
|
* To mark type as move-only, privately inherit from `kotlin::MoveOnly`
|
||||||
* To mark type unmovable and uncopyable, privately inherit from `kotlin::Pinned`
|
* To mark type unmovable and uncopyable, privately inherit from `kotlin::Pinned`
|
||||||
* All heap-allocated classes should publicly inherit from `KonanAllocatorAware`
|
* Use `std_support::*` containers and smart pointers instead of `std::*` ones. The former ones default to runtime-specific allocator.
|
||||||
* Use `KStd*` containers and smart pointers instead of `std::*` ones.
|
* Use `new (std_support::kalloc) T(...)` (defined in `std_support/New.hpp` instead of `new T(...)` and `std_support::kdelete(ptr)` instead of `delete ptr`. The former ones use runtime-specific allocator.
|
||||||
|
|
||||||
## Naming
|
## Naming
|
||||||
|
|
||||||
|
|||||||
@@ -53,6 +53,7 @@
|
|||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "WorkerBoundReference.h"
|
#include "WorkerBoundReference.h"
|
||||||
#include "Weak.h"
|
#include "Weak.h"
|
||||||
|
#include "std_support/New.hpp"
|
||||||
|
|
||||||
#ifdef KONAN_OBJC_INTEROP
|
#ifdef KONAN_OBJC_INTEROP
|
||||||
#include "ObjCMMAPI.h"
|
#include "ObjCMMAPI.h"
|
||||||
@@ -75,6 +76,8 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
using namespace kotlin;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
ALWAYS_INLINE bool IsStrictMemoryModel() noexcept {
|
ALWAYS_INLINE bool IsStrictMemoryModel() noexcept {
|
||||||
@@ -196,7 +199,7 @@ struct CycleDetectorRootset {
|
|||||||
KStdVector<ScopedRefHolder> heldRefs;
|
KStdVector<ScopedRefHolder> heldRefs;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CycleDetector : private kotlin::Pinned, public KonanAllocatorAware {
|
class CycleDetector : private kotlin::Pinned {
|
||||||
public:
|
public:
|
||||||
static void insertCandidateIfNeeded(KRef object) {
|
static void insertCandidateIfNeeded(KRef object) {
|
||||||
if (canBeACandidate(object))
|
if (canBeACandidate(object))
|
||||||
@@ -216,7 +219,7 @@ class CycleDetector : private kotlin::Pinned, public KonanAllocatorAware {
|
|||||||
|
|
||||||
static CycleDetector& instance() {
|
static CycleDetector& instance() {
|
||||||
// Only store a pointer to CycleDetector in .bss
|
// Only store a pointer to CycleDetector in .bss
|
||||||
static CycleDetector* result = new CycleDetector();
|
static CycleDetector* result = new (std_support::kalloc) CycleDetector();
|
||||||
return *result;
|
return *result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +1,32 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2017 JetBrains s.r.o.
|
* Copyright 2010-2022 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.
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef RUNTIME_ALLOC_H
|
#pragma once
|
||||||
#define RUNTIME_ALLOC_H
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
#include <new>
|
#include <new>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "Porting.h"
|
#include "std_support/CStdlib.hpp"
|
||||||
|
#include "std_support/New.hpp"
|
||||||
|
|
||||||
|
namespace konan {
|
||||||
|
|
||||||
|
inline void* calloc(size_t count, size_t size) {
|
||||||
|
return kotlin::std_support::calloc(count, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void* calloc_aligned(size_t count, size_t size, size_t alignment) {
|
||||||
|
return kotlin::std_support::aligned_calloc(alignment, count, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void free(void* ptr) {
|
||||||
|
kotlin::std_support::free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace konan
|
||||||
|
|
||||||
inline void* konanAllocMemory(size_t size) {
|
inline void* konanAllocMemory(size_t size) {
|
||||||
return konan::calloc(1, size);
|
return konan::calloc(1, size);
|
||||||
@@ -44,7 +47,7 @@ inline T* konanAllocArray(size_t length) {
|
|||||||
|
|
||||||
template <typename T, typename ...A>
|
template <typename T, typename ...A>
|
||||||
inline T* konanConstructInstance(A&& ...args) {
|
inline T* konanConstructInstance(A&& ...args) {
|
||||||
return new (konanAllocMemory(sizeof(T))) T(::std::forward<A>(args)...);
|
return new (kotlin::std_support::kalloc) T(std::forward<A>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename ...A>
|
template <typename T, typename ...A>
|
||||||
@@ -54,113 +57,5 @@ inline T* konanConstructSizedInstance(size_t size, A&& ...args) {
|
|||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline void konanDestructInstance(T* instance) {
|
inline void konanDestructInstance(T* instance) {
|
||||||
instance->~T();
|
kotlin::std_support::kdelete(instance);
|
||||||
konanFreeMemory(instance);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T> class KonanAllocator {
|
|
||||||
public:
|
|
||||||
typedef size_t size_type;
|
|
||||||
typedef ptrdiff_t difference_type;
|
|
||||||
typedef T* pointer;
|
|
||||||
typedef const T* const_pointer;
|
|
||||||
typedef T& reference;
|
|
||||||
typedef const T& const_reference;
|
|
||||||
typedef T value_type;
|
|
||||||
|
|
||||||
KonanAllocator() {}
|
|
||||||
KonanAllocator(const KonanAllocator&) {}
|
|
||||||
|
|
||||||
pointer allocate(size_type n, const void * = 0) {
|
|
||||||
return reinterpret_cast<T*>(konanAllocMemory(n * sizeof(T)));
|
|
||||||
}
|
|
||||||
|
|
||||||
void deallocate(void* p, size_type) {
|
|
||||||
if (p != nullptr) konanFreeMemory(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
pointer address(reference x) const { return &x; }
|
|
||||||
|
|
||||||
const_pointer address(const_reference x) const { return &x; }
|
|
||||||
|
|
||||||
KonanAllocator<T>& operator=(const KonanAllocator&) { return *this; }
|
|
||||||
|
|
||||||
void construct(pointer p, const T& val) { new ((T*) p) T(val); }
|
|
||||||
|
|
||||||
// C++-11 wants that.
|
|
||||||
template <class U, class ...A>
|
|
||||||
void construct(U* const p, A&& ...args) {
|
|
||||||
new (p) U(::std::forward<A>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
void destroy(pointer p) { p->~T(); }
|
|
||||||
|
|
||||||
size_type max_size() const { return size_t(-1); }
|
|
||||||
|
|
||||||
template <class U>
|
|
||||||
struct rebind { typedef KonanAllocator<U> other; };
|
|
||||||
|
|
||||||
template <class U>
|
|
||||||
KonanAllocator(const KonanAllocator<U>&) {}
|
|
||||||
|
|
||||||
template <class U>
|
|
||||||
KonanAllocator& operator=(const KonanAllocator<U>&) { return *this; }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class T, class U>
|
|
||||||
bool operator==(
|
|
||||||
KonanAllocator<T> const&, KonanAllocator<U> const&) noexcept {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T, class U>
|
|
||||||
bool operator!=(
|
|
||||||
KonanAllocator<T> const& x, KonanAllocator<U> const& y) noexcept {
|
|
||||||
return !(x == y);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
class KonanDeleter {
|
|
||||||
public:
|
|
||||||
KonanDeleter() = default;
|
|
||||||
|
|
||||||
// This is needed for `KStdUniquePtr` covariance: if `U*` converts to `T*` then `KStdUniquePtr<U>` converts to `KStdUniquePtr<T>`.
|
|
||||||
template <class U, std::enable_if_t<std::is_convertible_v<U*, T*>, std::nullptr_t> = nullptr>
|
|
||||||
KonanDeleter(KonanDeleter<U>) {}
|
|
||||||
|
|
||||||
void operator()(T* instance) noexcept { konanDestructInstance(instance); }
|
|
||||||
};
|
|
||||||
|
|
||||||
// Force a class to be heap-allocated using `konanAllocMemory`. Does not prevent stack allocation, or
|
|
||||||
// allocation as part of another object.
|
|
||||||
// Usage:
|
|
||||||
// class A : public KonanAllocatorAware {
|
|
||||||
// ...
|
|
||||||
// };
|
|
||||||
class KonanAllocatorAware {
|
|
||||||
public:
|
|
||||||
static void* operator new(size_t count) noexcept { return konanAllocMemory(count); }
|
|
||||||
static void* operator new[](size_t count) noexcept { return konanAllocMemory(count); }
|
|
||||||
|
|
||||||
static void* operator new(size_t count, void* ptr) noexcept { return ptr; }
|
|
||||||
static void* operator new[](size_t count, void* ptr) noexcept { return ptr; }
|
|
||||||
|
|
||||||
static void operator delete(void* ptr) noexcept { konanFreeMemory(ptr); }
|
|
||||||
static void operator delete[](void* ptr) noexcept { konanFreeMemory(ptr); }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// Hide constructors, assignments and destructor to discourage operating on instance of `KonanAllocatorAware`
|
|
||||||
KonanAllocatorAware() = default;
|
|
||||||
|
|
||||||
KonanAllocatorAware(const KonanAllocatorAware&) = default;
|
|
||||||
KonanAllocatorAware(KonanAllocatorAware&&) = default;
|
|
||||||
|
|
||||||
KonanAllocatorAware& operator=(const KonanAllocatorAware&) = default;
|
|
||||||
KonanAllocatorAware& operator=(KonanAllocatorAware&&) = default;
|
|
||||||
|
|
||||||
// Not virtual by design. Since this class hides this destructor, no one can destroy an
|
|
||||||
// instance of `KonanAllocatorAware` directly, so this destructor is never called in a virtual manner.
|
|
||||||
~KonanAllocatorAware() = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // RUNTIME_ALLOC_H
|
|
||||||
|
|||||||
@@ -1,133 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 "Alloc.h"
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
|
|
||||||
#include "gmock/gmock.h"
|
|
||||||
#include "gtest/gtest.h"
|
|
||||||
|
|
||||||
#include "Types.h"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
class A : public KonanAllocatorAware {
|
|
||||||
public:
|
|
||||||
using DestructorHook = testing::StrictMock<testing::MockFunction<void(int)>>;
|
|
||||||
|
|
||||||
static THREAD_LOCAL_VARIABLE DestructorHook* destructorHook;
|
|
||||||
|
|
||||||
explicit A(int value = -1) : value_(value) {}
|
|
||||||
|
|
||||||
~A() { destructorHook->Call(value_); }
|
|
||||||
|
|
||||||
int value() const { return value_; }
|
|
||||||
|
|
||||||
bool operator==(const A& rhs) const { return value_ == rhs.value_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
int value_;
|
|
||||||
};
|
|
||||||
|
|
||||||
// static
|
|
||||||
THREAD_LOCAL_VARIABLE A::DestructorHook* A::destructorHook = nullptr;
|
|
||||||
|
|
||||||
struct B {
|
|
||||||
explicit B(int value) : a(value) {}
|
|
||||||
|
|
||||||
A a;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
class KonanAllocatorAwareTest : public testing::Test {
|
|
||||||
public:
|
|
||||||
KStdUniquePtr<A::DestructorHook> destructorHook;
|
|
||||||
|
|
||||||
void SetUp() override {
|
|
||||||
Test::SetUp();
|
|
||||||
|
|
||||||
destructorHook = make_unique<A::DestructorHook>();
|
|
||||||
A::destructorHook = destructorHook.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TearDown() override {
|
|
||||||
A::destructorHook = nullptr;
|
|
||||||
destructorHook.reset();
|
|
||||||
|
|
||||||
Test::TearDown();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST_F(KonanAllocatorAwareTest, AllocatedOnStack) {
|
|
||||||
A a(42);
|
|
||||||
EXPECT_THAT(a.value(), 42);
|
|
||||||
EXPECT_CALL(*destructorHook, Call(42));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(KonanAllocatorAwareTest, AllocatedInAnotherObject) {
|
|
||||||
// We do not control how `B` is allocated.
|
|
||||||
B* b = new B(42);
|
|
||||||
EXPECT_THAT(b->a.value(), 42);
|
|
||||||
EXPECT_CALL(*destructorHook, Call(42));
|
|
||||||
delete b;
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(KonanAllocatorAwareTest, AllocatedByItself) {
|
|
||||||
A* a = new A(42);
|
|
||||||
EXPECT_THAT(a->value(), 42);
|
|
||||||
EXPECT_CALL(*destructorHook, Call(42));
|
|
||||||
delete a;
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(KonanAllocatorAwareTest, AllocateArray) {
|
|
||||||
constexpr size_t kCount = 5;
|
|
||||||
A* as = new A[kCount];
|
|
||||||
|
|
||||||
std::vector<int> actual;
|
|
||||||
for (A* a = as; a != as + kCount; ++a) {
|
|
||||||
actual.push_back(a->value());
|
|
||||||
}
|
|
||||||
std::array<int, kCount> expected;
|
|
||||||
for (int& element : expected) {
|
|
||||||
element = -1;
|
|
||||||
}
|
|
||||||
EXPECT_THAT(actual, testing::ElementsAreArray(expected));
|
|
||||||
|
|
||||||
EXPECT_CALL(*destructorHook, Call(-1)).Times(kCount);
|
|
||||||
delete[] as;
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(KonanAllocatorAwareTest, PlacementAllocated) {
|
|
||||||
std::array<uint8_t, sizeof(A)> buffer;
|
|
||||||
A* a = new (buffer.data()) A(42);
|
|
||||||
EXPECT_THAT(a->value(), 42);
|
|
||||||
EXPECT_CALL(*destructorHook, Call(42));
|
|
||||||
a->~A();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(KonanAllocatorAwareTest, PlacementConstructedArray) {
|
|
||||||
constexpr size_t kCount = 5;
|
|
||||||
// TODO: Consider removing support for placement new[] altogether, since there's no
|
|
||||||
// portable way to know needed storage size ahead of time.
|
|
||||||
alignas(A) std::array<uint8_t, sizeof(A) * kCount + sizeof(size_t)> buffer;
|
|
||||||
A* as = new (buffer.data()) A[kCount];
|
|
||||||
|
|
||||||
std::vector<int> actual;
|
|
||||||
for (A* a = as; a != as + kCount; ++a) {
|
|
||||||
actual.push_back(a->value());
|
|
||||||
}
|
|
||||||
std::array<int, kCount> expected;
|
|
||||||
for (int& element : expected) {
|
|
||||||
element = -1;
|
|
||||||
}
|
|
||||||
EXPECT_THAT(actual, testing::ElementsAreArray(expected));
|
|
||||||
|
|
||||||
EXPECT_CALL(*destructorHook, Call(-1)).Times(kCount);
|
|
||||||
for (A* a = as; a != as + kCount; ++a) {
|
|
||||||
a->~A();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include "gmock/gmock.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
#include "Utils.hpp"
|
||||||
|
#include "std_support/CStdlib.hpp"
|
||||||
|
#include "std_support/Map.hpp"
|
||||||
|
|
||||||
|
namespace kotlin::test_support {
|
||||||
|
|
||||||
|
class MockAllocatorCore : private Pinned {
|
||||||
|
public:
|
||||||
|
MOCK_METHOD(void*, allocate, (std::size_t), (noexcept));
|
||||||
|
MOCK_METHOD(void, deallocate, (void*, std::size_t), (noexcept));
|
||||||
|
};
|
||||||
|
|
||||||
|
class SpyAllocatorCore : private Pinned {
|
||||||
|
public:
|
||||||
|
SpyAllocatorCore() noexcept {
|
||||||
|
ON_CALL(*this, allocate(testing::_)).WillByDefault([](std::size_t size) { return std_support::malloc(size); });
|
||||||
|
ON_CALL(*this, deallocate(testing::_, testing::_)).WillByDefault([](void* ptr, std::size_t size) { std_support::free(ptr); });
|
||||||
|
}
|
||||||
|
|
||||||
|
MOCK_METHOD(void*, allocate, (std::size_t), (noexcept));
|
||||||
|
MOCK_METHOD(void, deallocate, (void*, std::size_t), (noexcept));
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename Core>
|
||||||
|
struct Allocator {
|
||||||
|
using value_type = T;
|
||||||
|
using size_type = std::size_t;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using propagate_on_container_move_assignment = std::true_type;
|
||||||
|
using is_always_equal = std::false_type;
|
||||||
|
|
||||||
|
explicit Allocator(Core& core) : core_(&core) {}
|
||||||
|
|
||||||
|
Allocator(const Allocator&) noexcept = default;
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
Allocator(const Allocator<U, Core>& other) noexcept : core_(other.core_) {}
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
Allocator& operator=(const Allocator<U, Core>& other) noexcept {
|
||||||
|
core_ = other.core_;
|
||||||
|
}
|
||||||
|
|
||||||
|
T* allocate(std::size_t n) noexcept { return static_cast<T*>(core_->allocate(sizeof(T) * n)); }
|
||||||
|
|
||||||
|
void deallocate(T* p, std::size_t n) noexcept { core_->deallocate(p, sizeof(T) * n); }
|
||||||
|
|
||||||
|
Core* core_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename Core>
|
||||||
|
auto MakeAllocator(Core& core) {
|
||||||
|
return Allocator<T, Core>(core);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename U, typename Core>
|
||||||
|
bool operator==(const Allocator<T, Core>& lhs, const Allocator<U, Core>& rhs) noexcept {
|
||||||
|
return lhs.core_ == rhs.core_;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename U, typename Core>
|
||||||
|
bool operator!=(const Allocator<T, Core>& lhs, const Allocator<U, Core>& rhs) noexcept {
|
||||||
|
return !(lhs == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace kotlin::test_support
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 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 "AllocatorTestSupport.hpp"
|
||||||
|
|
||||||
|
#include "gmock/gmock.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
using namespace kotlin;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct EmptyClass {};
|
||||||
|
|
||||||
|
struct Class {
|
||||||
|
int32_t x;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(Class) > sizeof(EmptyClass));
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST(AllocatorTestSupportTest, MockAllocate) {
|
||||||
|
testing::StrictMock<test_support::MockAllocatorCore> allocatorCore;
|
||||||
|
auto allocator = test_support::MakeAllocator<Class>(allocatorCore);
|
||||||
|
|
||||||
|
auto* expectedPtr = reinterpret_cast<Class*>(13);
|
||||||
|
EXPECT_CALL(allocatorCore, allocate(2 * sizeof(Class))).WillOnce(testing::Return(expectedPtr));
|
||||||
|
auto* ptr = std::allocator_traits<decltype(allocator)>::allocate(allocator, 2);
|
||||||
|
EXPECT_THAT(ptr, expectedPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(AllocatorTestSupportTest, MockDeallocate) {
|
||||||
|
testing::StrictMock<test_support::MockAllocatorCore> allocatorCore;
|
||||||
|
auto allocator = test_support::MakeAllocator<Class>(allocatorCore);
|
||||||
|
|
||||||
|
auto* ptr = reinterpret_cast<Class*>(13);
|
||||||
|
EXPECT_CALL(allocatorCore, deallocate(ptr, 2 * sizeof(Class)));
|
||||||
|
std::allocator_traits<decltype(allocator)>::deallocate(allocator, ptr, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(AllocatorTestSupportTest, MockAdjustType) {
|
||||||
|
testing::StrictMock<test_support::MockAllocatorCore> allocatorCore;
|
||||||
|
auto initial = test_support::MakeAllocator<EmptyClass>(allocatorCore);
|
||||||
|
|
||||||
|
using Allocator = std::allocator_traits<decltype(initial)>::template rebind_alloc<Class>;
|
||||||
|
using Traits = std::allocator_traits<decltype(initial)>::template rebind_traits<Class>;
|
||||||
|
Allocator allocator = Allocator(initial);
|
||||||
|
|
||||||
|
auto* expectedPtr = reinterpret_cast<Class*>(13);
|
||||||
|
EXPECT_CALL(allocatorCore, allocate(2 * sizeof(Class))).WillOnce(testing::Return(expectedPtr));
|
||||||
|
auto* ptr = Traits::allocate(allocator, 2);
|
||||||
|
EXPECT_THAT(ptr, expectedPtr);
|
||||||
|
testing::Mock::VerifyAndClearExpectations(&allocatorCore);
|
||||||
|
|
||||||
|
EXPECT_CALL(allocatorCore, deallocate(ptr, 2 * sizeof(Class)));
|
||||||
|
Traits::deallocate(allocator, ptr, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(AllocatorTestSupportTest, Spy) {
|
||||||
|
test_support::SpyAllocatorCore allocatorCore;
|
||||||
|
auto allocator = test_support::MakeAllocator<Class>(allocatorCore);
|
||||||
|
|
||||||
|
EXPECT_CALL(allocatorCore, allocate(2 * sizeof(Class)));
|
||||||
|
auto* ptr1 = std::allocator_traits<decltype(allocator)>::allocate(allocator, 2);
|
||||||
|
testing::Mock::VerifyAndClearExpectations(&allocatorCore);
|
||||||
|
|
||||||
|
using Allocator = std::allocator_traits<decltype(allocator)>::template rebind_alloc<EmptyClass>;
|
||||||
|
using Traits = std::allocator_traits<decltype(allocator)>::template rebind_traits<EmptyClass>;
|
||||||
|
Allocator b = Allocator(allocator);
|
||||||
|
|
||||||
|
EXPECT_CALL(allocatorCore, allocate(2 * sizeof(EmptyClass)));
|
||||||
|
auto* ptr2 = Traits::allocate(b, 2);
|
||||||
|
testing::Mock::VerifyAndClearExpectations(&allocatorCore);
|
||||||
|
|
||||||
|
EXPECT_CALL(allocatorCore, deallocate(ptr1, 2 * sizeof(Class)));
|
||||||
|
std::allocator_traits<decltype(allocator)>::deallocate(allocator, ptr1, 2);
|
||||||
|
testing::Mock::VerifyAndClearExpectations(&allocatorCore);
|
||||||
|
|
||||||
|
EXPECT_CALL(allocatorCore, deallocate(ptr2, 2 * sizeof(EmptyClass)));
|
||||||
|
Traits::deallocate(b, ptr2, 2);
|
||||||
|
testing::Mock::VerifyAndClearExpectations(&allocatorCore);
|
||||||
|
}
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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_CPP_SUPPORT_H
|
|
||||||
#define RUNTIME_CPP_SUPPORT_H
|
|
||||||
|
|
||||||
// A collection of backported utilities from future C++ versions.
|
|
||||||
|
|
||||||
namespace kotlin {
|
|
||||||
namespace std_support {
|
|
||||||
|
|
||||||
} // namespace std_support
|
|
||||||
} // namespace kotlin
|
|
||||||
|
|
||||||
#endif // RUNTIME_CPP_SUPPORT_H
|
|
||||||
@@ -26,6 +26,7 @@
|
|||||||
#include "ExecFormat.h"
|
#include "ExecFormat.h"
|
||||||
#include "Memory.h"
|
#include "Memory.h"
|
||||||
#include "Mutex.hpp"
|
#include "Mutex.hpp"
|
||||||
|
#include "Porting.h"
|
||||||
#include "Types.h"
|
#include "Types.h"
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "ObjCExceptions.h"
|
#include "ObjCExceptions.h"
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "ExecFormat.h"
|
#include "ExecFormat.h"
|
||||||
|
#include "Porting.h"
|
||||||
#include "Types.h"
|
#include "Types.h"
|
||||||
|
|
||||||
#if USE_ELF_SYMBOLS
|
#if USE_ELF_SYMBOLS
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
|
|
||||||
#include "cpp_support/Span.hpp"
|
#include "std_support/Span.hpp"
|
||||||
|
|
||||||
namespace kotlin {
|
namespace kotlin {
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
#include "gmock/gmock.h"
|
#include "gmock/gmock.h"
|
||||||
|
|
||||||
#include "cpp_support/Span.hpp"
|
#include "std_support/Span.hpp"
|
||||||
|
|
||||||
using namespace kotlin;
|
using namespace kotlin;
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,9 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
|
|
||||||
#include "cpp_support/Span.hpp"
|
#include "std_support/Span.hpp"
|
||||||
#include "Format.h"
|
#include "Format.h"
|
||||||
|
#include "Porting.h"
|
||||||
#include "StackTrace.hpp"
|
#include "StackTrace.hpp"
|
||||||
|
|
||||||
using namespace kotlin;
|
using namespace kotlin;
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ inline constexpr auto nullopt = std::experimental::nullopt;
|
|||||||
|
|
||||||
#include "Format.h"
|
#include "Format.h"
|
||||||
#include "KAssert.h"
|
#include "KAssert.h"
|
||||||
|
#include "Porting.h"
|
||||||
|
|
||||||
using namespace kotlin;
|
using namespace kotlin;
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ using string_view = std::experimental::string_view;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "CompilerConstants.hpp"
|
#include "CompilerConstants.hpp"
|
||||||
#include "cpp_support/Span.hpp"
|
#include "std_support/Span.hpp"
|
||||||
#include "Types.h"
|
#include "Types.h"
|
||||||
|
|
||||||
namespace kotlin {
|
namespace kotlin {
|
||||||
|
|||||||
@@ -13,18 +13,25 @@
|
|||||||
#include "Mutex.hpp"
|
#include "Mutex.hpp"
|
||||||
#include "Types.h"
|
#include "Types.h"
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
|
#include "std_support/List.hpp"
|
||||||
|
#include "std_support/Memory.hpp"
|
||||||
|
|
||||||
namespace kotlin {
|
namespace kotlin {
|
||||||
|
|
||||||
// A queue that is constructed by collecting subqueues from several `Producer`s.
|
// A queue that is constructed by collecting subqueues from several `Producer`s.
|
||||||
template <typename T, typename Mutex>
|
template <typename T, typename Mutex, typename Allocator = std_support::allocator<T>>
|
||||||
class MultiSourceQueue {
|
class MultiSourceQueue {
|
||||||
|
// Using `std_support::list` as it allows to implement `Collect` without memory allocations,
|
||||||
|
// which is important for GC mark phase.
|
||||||
|
template <typename U>
|
||||||
|
using List = std_support::list<U, typename std::allocator_traits<Allocator>::template rebind_alloc<U>>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
class Producer;
|
class Producer;
|
||||||
|
|
||||||
// TODO: Consider switching from `KStdList` to `SingleLockList` to hide the constructor
|
// TODO: Consider switching from `KStdList` to `SingleLockList` to hide the constructor
|
||||||
// and to not store the iterator.
|
// and to not store the iterator.
|
||||||
class Node : private Pinned, public KonanAllocatorAware {
|
class Node : private Pinned {
|
||||||
public:
|
public:
|
||||||
Node(Producer* owner, const T& value) noexcept : value_(value), owner_(owner) {}
|
Node(Producer* owner, const T& value) noexcept : value_(value), owner_(owner) {}
|
||||||
|
|
||||||
@@ -43,12 +50,13 @@ public:
|
|||||||
|
|
||||||
T value_;
|
T value_;
|
||||||
std::atomic<Producer*> owner_; // `nullptr` signifies that `MultiSourceQueue` owns it.
|
std::atomic<Producer*> owner_; // `nullptr` signifies that `MultiSourceQueue` owns it.
|
||||||
typename KStdList<Node>::iterator position_;
|
typename List<Node>::iterator position_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Producer {
|
class Producer {
|
||||||
public:
|
public:
|
||||||
explicit Producer(MultiSourceQueue& owner) noexcept : owner_(owner) {}
|
explicit Producer(MultiSourceQueue& owner) noexcept :
|
||||||
|
owner_(owner), queue_(owner.queue_.get_allocator()), deletionQueue_(owner.deletionQueue_.get_allocator()) {}
|
||||||
|
|
||||||
~Producer() { Publish(); }
|
~Producer() { Publish(); }
|
||||||
|
|
||||||
@@ -84,7 +92,9 @@ public:
|
|||||||
node.owner_ = nullptr;
|
node.owner_ = nullptr;
|
||||||
}
|
}
|
||||||
std::lock_guard<Mutex> guard(owner_.mutex_);
|
std::lock_guard<Mutex> guard(owner_.mutex_);
|
||||||
|
RuntimeAssert(owner_.queue_.get_allocator() == queue_.get_allocator(), "queue_ allocators must match");
|
||||||
owner_.queue_.splice(owner_.queue_.end(), queue_);
|
owner_.queue_.splice(owner_.queue_.end(), queue_);
|
||||||
|
RuntimeAssert(owner_.deletionQueue_.get_allocator() == deletionQueue_.get_allocator(), "deletionQueue_ allocators must match");
|
||||||
owner_.deletionQueue_.splice(owner_.deletionQueue_.end(), deletionQueue_);
|
owner_.deletionQueue_.splice(owner_.deletionQueue_.end(), deletionQueue_);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,8 +105,8 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
MultiSourceQueue& owner_; // weak
|
MultiSourceQueue& owner_; // weak
|
||||||
KStdList<Node> queue_;
|
List<Node> queue_;
|
||||||
KStdList<Node*> deletionQueue_;
|
List<Node*> deletionQueue_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Iterator {
|
class Iterator {
|
||||||
@@ -115,9 +125,9 @@ public:
|
|||||||
private:
|
private:
|
||||||
friend class MultiSourceQueue;
|
friend class MultiSourceQueue;
|
||||||
|
|
||||||
explicit Iterator(const typename KStdList<Node>::iterator& position) noexcept : position_(position) {}
|
explicit Iterator(const typename List<Node>::iterator& position) noexcept : position_(position) {}
|
||||||
|
|
||||||
typename KStdList<Node>::iterator position_;
|
typename List<Node>::iterator position_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Iterable : MoveOnly {
|
class Iterable : MoveOnly {
|
||||||
@@ -134,6 +144,8 @@ public:
|
|||||||
std::unique_lock<Mutex> guard_;
|
std::unique_lock<Mutex> guard_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
explicit MultiSourceQueue(const Allocator& allocator = Allocator()) noexcept : queue_(allocator), deletionQueue_(allocator) {}
|
||||||
|
|
||||||
// Lock `MultiSourceQueue` for safe iteration. If element was scheduled for deletion,
|
// Lock `MultiSourceQueue` for safe iteration. If element was scheduled for deletion,
|
||||||
// it'll still be iterated. Use `ApplyDeletions` to remove those elements.
|
// it'll still be iterated. Use `ApplyDeletions` to remove those elements.
|
||||||
Iterable LockForIter() noexcept { return Iterable(*this); }
|
Iterable LockForIter() noexcept { return Iterable(*this); }
|
||||||
@@ -141,7 +153,7 @@ public:
|
|||||||
// Lock `MultiSourceQueue` and apply deletions. Only deletes elements that were published.
|
// Lock `MultiSourceQueue` and apply deletions. Only deletes elements that were published.
|
||||||
void ApplyDeletions() noexcept {
|
void ApplyDeletions() noexcept {
|
||||||
std::lock_guard<Mutex> guard(mutex_);
|
std::lock_guard<Mutex> guard(mutex_);
|
||||||
KStdList<Node*> remainingDeletions;
|
List<Node*> remainingDeletions(deletionQueue_.get_allocator());
|
||||||
|
|
||||||
auto it = deletionQueue_.begin();
|
auto it = deletionQueue_.begin();
|
||||||
while (it != deletionQueue_.end()) {
|
while (it != deletionQueue_.end()) {
|
||||||
@@ -176,10 +188,8 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Using `KStdList` as it allows to implement `Collect` without memory allocations,
|
List<Node> queue_;
|
||||||
// which is important for GC mark phase.
|
List<Node*> deletionQueue_;
|
||||||
KStdList<Node> queue_;
|
|
||||||
KStdList<Node*> deletionQueue_;
|
|
||||||
Mutex mutex_;
|
Mutex mutex_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -10,12 +10,15 @@
|
|||||||
#include "gmock/gmock.h"
|
#include "gmock/gmock.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
#include "AllocatorTestSupport.hpp"
|
||||||
#include "ScopedThread.hpp"
|
#include "ScopedThread.hpp"
|
||||||
#include "TestSupport.hpp"
|
#include "TestSupport.hpp"
|
||||||
#include "Types.h"
|
#include "Types.h"
|
||||||
|
|
||||||
using namespace kotlin;
|
using namespace kotlin;
|
||||||
|
|
||||||
|
using ::testing::_;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
template <typename T, typename Mutex>
|
template <typename T, typename Mutex>
|
||||||
@@ -312,3 +315,42 @@ TEST(MultiSourceQueueTest, ConcurrentPublishAndApplyDeletions) {
|
|||||||
auto actual = Collect(queue);
|
auto actual = Collect(queue);
|
||||||
EXPECT_THAT(actual, testing::IsEmpty());
|
EXPECT_THAT(actual, testing::IsEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(MultiSourceQueueTest, CustomAllocator) {
|
||||||
|
testing::StrictMock<test_support::SpyAllocatorCore> allocatorCore;
|
||||||
|
auto allocator = test_support::MakeAllocator<int>(allocatorCore);
|
||||||
|
|
||||||
|
using Queue = MultiSourceQueue<int, SpinLock<MutexThreadStateHandling::kIgnore>, decltype(allocator)>;
|
||||||
|
Queue queue(allocator);
|
||||||
|
Queue::Producer producer1(queue);
|
||||||
|
Queue::Producer producer2(queue);
|
||||||
|
|
||||||
|
EXPECT_CALL(allocatorCore, allocate(_)).Times(5);
|
||||||
|
auto* node11 = producer1.Insert(1);
|
||||||
|
auto* node12 = producer1.Insert(2);
|
||||||
|
auto* node21 = producer2.Insert(10);
|
||||||
|
auto* node22 = producer2.Insert(20);
|
||||||
|
auto* node23 = producer2.Insert(30);
|
||||||
|
testing::Mock::VerifyAndClearExpectations(&allocatorCore);
|
||||||
|
|
||||||
|
EXPECT_CALL(allocatorCore, deallocate(_, _));
|
||||||
|
producer2.Erase(node22);
|
||||||
|
testing::Mock::VerifyAndClearExpectations(&allocatorCore);
|
||||||
|
|
||||||
|
producer1.Publish();
|
||||||
|
producer2.Publish();
|
||||||
|
|
||||||
|
EXPECT_CALL(allocatorCore, allocate(_)).Times(4);
|
||||||
|
producer1.Erase(node11);
|
||||||
|
producer1.Erase(node23);
|
||||||
|
producer2.Erase(node12);
|
||||||
|
producer2.Erase(node21);
|
||||||
|
testing::Mock::VerifyAndClearExpectations(&allocatorCore);
|
||||||
|
|
||||||
|
producer1.Publish();
|
||||||
|
producer2.Publish();
|
||||||
|
|
||||||
|
EXPECT_CALL(allocatorCore, deallocate(_, _)).Times(8);
|
||||||
|
queue.ApplyDeletions();
|
||||||
|
testing::Mock::VerifyAndClearExpectations(&allocatorCore);
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include "KString.h"
|
#include "KString.h"
|
||||||
#include "StackTrace.hpp"
|
#include "StackTrace.hpp"
|
||||||
#include "Memory.h"
|
#include "Memory.h"
|
||||||
|
#include "Porting.h"
|
||||||
#include "Natives.h"
|
#include "Natives.h"
|
||||||
#include "Types.h"
|
#include "Types.h"
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,9 @@
|
|||||||
#include "CompilerConstants.hpp"
|
#include "CompilerConstants.hpp"
|
||||||
#include "Porting.h"
|
#include "Porting.h"
|
||||||
#include "KAssert.h"
|
#include "KAssert.h"
|
||||||
|
#include "std_support/CStdlib.hpp"
|
||||||
|
|
||||||
|
using namespace kotlin;
|
||||||
|
|
||||||
#if KONAN_WASM || KONAN_ZEPHYR
|
#if KONAN_WASM || KONAN_ZEPHYR
|
||||||
extern "C" RUNTIME_NORETURN void Konan_abort(const char*);
|
extern "C" RUNTIME_NORETURN void Konan_abort(const char*);
|
||||||
@@ -205,7 +208,7 @@ static void onThreadExitCallback(void* value) {
|
|||||||
while (record != nullptr) {
|
while (record != nullptr) {
|
||||||
record->destructor(record->destructorParameter);
|
record->destructor(record->destructorParameter);
|
||||||
auto next = record->next;
|
auto next = record->next;
|
||||||
free(record);
|
std_support::free(record);
|
||||||
record = next;
|
record = next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -242,7 +245,7 @@ void onThreadExit(void (*destructor)(void*), void* destructorParameter) {
|
|||||||
#else // !KONAN_NO_THREADS
|
#else // !KONAN_NO_THREADS
|
||||||
// We cannot use pthread_cleanup_push() as it is lexical scope bound.
|
// We cannot use pthread_cleanup_push() as it is lexical scope bound.
|
||||||
pthread_once(&terminationKeyOnceControl, onThreadExitInit);
|
pthread_once(&terminationKeyOnceControl, onThreadExitInit);
|
||||||
DestructorRecord* destructorRecord = (DestructorRecord*)calloc(1, sizeof(DestructorRecord));
|
DestructorRecord* destructorRecord = (DestructorRecord*)std_support::calloc(1, sizeof(DestructorRecord));
|
||||||
destructorRecord->destructor = destructor;
|
destructorRecord->destructor = destructor;
|
||||||
destructorRecord->destructorParameter = destructorParameter;
|
destructorRecord->destructorParameter = destructorParameter;
|
||||||
destructorRecord->next =
|
destructorRecord->next =
|
||||||
@@ -332,35 +335,6 @@ size_t strnlen(const char* buffer, size_t maxSize) {
|
|||||||
return ::strnlen(buffer, maxSize);
|
return ::strnlen(buffer, maxSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Memory operations.
|
|
||||||
#if KONAN_INTERNAL_DLMALLOC
|
|
||||||
extern "C" void* dlcalloc(size_t, size_t);
|
|
||||||
extern "C" void dlfree(void*);
|
|
||||||
#define calloc_impl dlcalloc
|
|
||||||
#define free_impl dlfree
|
|
||||||
#define calloc_aligned_impl(count, size, alignment) dlcalloc(count, size)
|
|
||||||
|
|
||||||
#else
|
|
||||||
extern "C" void* konan_calloc_impl(size_t, size_t);
|
|
||||||
extern "C" void konan_free_impl(void*);
|
|
||||||
extern "C" void* konan_calloc_aligned_impl(size_t count, size_t size, size_t alignment);
|
|
||||||
#define calloc_impl konan_calloc_impl
|
|
||||||
#define free_impl konan_free_impl
|
|
||||||
#define calloc_aligned_impl konan_calloc_aligned_impl
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void* calloc(size_t count, size_t size) {
|
|
||||||
return calloc_impl(count, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void* calloc_aligned(size_t count, size_t size, size_t alignment) {
|
|
||||||
return calloc_aligned_impl(count, size, alignment);
|
|
||||||
}
|
|
||||||
|
|
||||||
void free(void* pointer) {
|
|
||||||
free_impl(pointer);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if KONAN_INTERNAL_NOW
|
#if KONAN_INTERNAL_NOW
|
||||||
|
|
||||||
#ifdef KONAN_ZEPHYR
|
#ifdef KONAN_ZEPHYR
|
||||||
@@ -406,72 +380,6 @@ uint64_t getTimeMicros() {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if KONAN_INTERNAL_DLMALLOC
|
|
||||||
// This function is being called when memory allocator needs more RAM.
|
|
||||||
|
|
||||||
#if KONAN_WASM
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
constexpr uint32_t MFAIL = ~(uint32_t)0;
|
|
||||||
constexpr uint32_t WASM_PAGESIZE_EXPONENT = 16;
|
|
||||||
constexpr uint32_t WASM_PAGESIZE = 1u << WASM_PAGESIZE_EXPONENT;
|
|
||||||
constexpr uint32_t WASM_PAGEMASK = WASM_PAGESIZE-1;
|
|
||||||
|
|
||||||
uint32_t pageAlign(int32_t value) {
|
|
||||||
return (value + WASM_PAGEMASK) & ~ (WASM_PAGEMASK);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t inBytes(uint32_t pageCount) {
|
|
||||||
return pageCount << WASM_PAGESIZE_EXPONENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t inPages(uint32_t value) {
|
|
||||||
return value >> WASM_PAGESIZE_EXPONENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" void Konan_notify_memory_grow();
|
|
||||||
|
|
||||||
uint32_t memorySize() {
|
|
||||||
return __builtin_wasm_memory_size(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t growMemory(uint32_t delta) {
|
|
||||||
int32_t oldLength = __builtin_wasm_memory_grow(0, delta);
|
|
||||||
Konan_notify_memory_grow();
|
|
||||||
return oldLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void* moreCore(int32_t delta) {
|
|
||||||
uint32_t top = inBytes(memorySize());
|
|
||||||
if (delta > 0) {
|
|
||||||
if (growMemory(inPages(pageAlign(delta))) == 0) {
|
|
||||||
return (void *) MFAIL;
|
|
||||||
}
|
|
||||||
} else if (delta < 0) {
|
|
||||||
return (void *) MFAIL;
|
|
||||||
}
|
|
||||||
return (void *) top;
|
|
||||||
}
|
|
||||||
|
|
||||||
// dlmalloc() wants to know the page size.
|
|
||||||
long getpagesize() {
|
|
||||||
return WASM_PAGESIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
void* moreCore(int size) {
|
|
||||||
return sbrk(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
long getpagesize() {
|
|
||||||
return sysconf(_SC_PAGESIZE);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // namespace konan
|
} // namespace konan
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|||||||
@@ -79,11 +79,6 @@ void *memset(void *b, int c, size_t len);
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Memory operations.
|
|
||||||
void* calloc(size_t count, size_t size);
|
|
||||||
void* calloc_aligned(size_t count, size_t size, size_t alignment);
|
|
||||||
void free(void* ptr);
|
|
||||||
|
|
||||||
// Time operations.
|
// Time operations.
|
||||||
uint64_t getTimeMillis();
|
uint64_t getTimeMillis();
|
||||||
uint64_t getTimeMicros();
|
uint64_t getTimeMicros();
|
||||||
|
|||||||
@@ -12,41 +12,36 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
#include "Alloc.h"
|
|
||||||
#include "Mutex.hpp"
|
#include "Mutex.hpp"
|
||||||
#include "Types.h"
|
#include "Types.h"
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
|
#include "std_support/Memory.hpp"
|
||||||
|
|
||||||
namespace kotlin {
|
namespace kotlin {
|
||||||
|
|
||||||
// TODO: Consider different locking mechanisms.
|
// TODO: Consider different locking mechanisms.
|
||||||
template <typename Value, typename Mutex>
|
template <typename Value, typename Mutex, typename Allocator = std_support::allocator<Value>>
|
||||||
class SingleLockList : private Pinned {
|
class SingleLockList : private Pinned {
|
||||||
public:
|
public:
|
||||||
class Node;
|
class Node;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class NodeDeleter {
|
using NodeAllocator = typename std::allocator_traits<Allocator>::template rebind_alloc<Node>;
|
||||||
public:
|
using NodeOwner = std_support::unique_ptr<Node, std_support::allocator_deleter<Node, NodeAllocator>>;
|
||||||
void operator()(Node* node) const { delete node; }
|
|
||||||
};
|
|
||||||
|
|
||||||
using NodeOwner = std::unique_ptr<Node, NodeDeleter>;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
class Node : private Pinned, public KonanAllocatorAware {
|
// TODO: Maybe just hide `Node` altogether?
|
||||||
|
class Node : private Pinned {
|
||||||
public:
|
public:
|
||||||
|
template <typename... Args>
|
||||||
|
explicit Node(const Allocator& allocator, Args&&... args) noexcept :
|
||||||
|
value_(std::forward<Args>(args)...), next_(std_support::nullptr_unique<Node>(allocator)) {}
|
||||||
|
|
||||||
Value* Get() noexcept { return &value_; }
|
Value* Get() noexcept { return &value_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class SingleLockList;
|
friend class SingleLockList;
|
||||||
|
|
||||||
template <typename... Args>
|
|
||||||
Node(Args&&... args) noexcept : value_(std::forward<Args>(args)...) {}
|
|
||||||
|
|
||||||
// Make sure `Node` can only be deleted by `SingleLockList` itself.
|
|
||||||
~Node() = default;
|
|
||||||
|
|
||||||
Value value_;
|
Value value_;
|
||||||
NodeOwner next_;
|
NodeOwner next_;
|
||||||
Node* previous_ = nullptr; // weak
|
Node* previous_ = nullptr; // weak
|
||||||
@@ -90,6 +85,11 @@ public:
|
|||||||
std::unique_lock<Mutex> guard_;
|
std::unique_lock<Mutex> guard_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
SingleLockList() noexcept = default;
|
||||||
|
|
||||||
|
explicit SingleLockList(const Allocator& allocator) noexcept :
|
||||||
|
allocator_(allocator), root_(std_support::nullptr_unique<Node, NodeAllocator>(allocator)) {}
|
||||||
|
|
||||||
~SingleLockList() {
|
~SingleLockList() {
|
||||||
AssertCorrectUnsafe();
|
AssertCorrectUnsafe();
|
||||||
// Make sure not to blow up the stack by nested `~Node` calls.
|
// Make sure not to blow up the stack by nested `~Node` calls.
|
||||||
@@ -102,12 +102,12 @@ public:
|
|||||||
// TODO: Consider making `Emplace` append to `last_`.
|
// TODO: Consider making `Emplace` append to `last_`.
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
Node* Emplace(Args&&... args) noexcept {
|
Node* Emplace(Args&&... args) noexcept {
|
||||||
auto* nodePtr = new Node(std::forward<Args>(args)...);
|
auto node = std_support::allocate_unique<Node>(allocator_, allocator_, std::forward<Args>(args)...);
|
||||||
NodeOwner node(nodePtr);
|
auto* nodePtr = node.get();
|
||||||
std::lock_guard<Mutex> guard(mutex_);
|
std::lock_guard<Mutex> guard(mutex_);
|
||||||
AssertCorrectUnsafe();
|
AssertCorrectUnsafe();
|
||||||
if (root_) {
|
if (root_) {
|
||||||
root_->previous_ = node.get();
|
root_->previous_ = nodePtr;
|
||||||
} else {
|
} else {
|
||||||
last_ = nodePtr;
|
last_ = nodePtr;
|
||||||
}
|
}
|
||||||
@@ -125,7 +125,8 @@ public:
|
|||||||
last_ = node->previous_;
|
last_ = node->previous_;
|
||||||
}
|
}
|
||||||
if (root_.get() == node) {
|
if (root_.get() == node) {
|
||||||
root_ = std::move(node->next_);
|
auto next = std::move(node->next_);
|
||||||
|
root_ = std::move(next);
|
||||||
if (root_) {
|
if (root_) {
|
||||||
root_->previous_ = nullptr;
|
root_->previous_ = nullptr;
|
||||||
}
|
}
|
||||||
@@ -165,6 +166,7 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[no_unique_address]] NodeAllocator allocator_;
|
||||||
NodeOwner root_;
|
NodeOwner root_;
|
||||||
Node* last_ = nullptr;
|
Node* last_ = nullptr;
|
||||||
Mutex mutex_;
|
Mutex mutex_;
|
||||||
|
|||||||
@@ -11,12 +11,15 @@
|
|||||||
#include "gmock/gmock.h"
|
#include "gmock/gmock.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
#include "AllocatorTestSupport.hpp"
|
||||||
#include "ScopedThread.hpp"
|
#include "ScopedThread.hpp"
|
||||||
#include "TestSupport.hpp"
|
#include "TestSupport.hpp"
|
||||||
#include "Types.h"
|
#include "Types.h"
|
||||||
|
|
||||||
using namespace kotlin;
|
using namespace kotlin;
|
||||||
|
|
||||||
|
using ::testing::_;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using IntList = SingleLockList<int, SpinLock<MutexThreadStateHandling::kIgnore>>;
|
using IntList = SingleLockList<int, SpinLock<MutexThreadStateHandling::kIgnore>>;
|
||||||
@@ -417,3 +420,26 @@ TEST(SingleLockListTest, Destructor) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(SingleLockListTest, CustomAllocator) {
|
||||||
|
testing::StrictMock<test_support::SpyAllocatorCore> allocatorCore;
|
||||||
|
auto allocator = test_support::MakeAllocator<int>(allocatorCore);
|
||||||
|
SingleLockList<int, SpinLock<MutexThreadStateHandling::kIgnore>, decltype(allocator)> list(allocator);
|
||||||
|
|
||||||
|
EXPECT_CALL(allocatorCore, allocate(_)).Times(3);
|
||||||
|
auto* node1 = list.Emplace(1);
|
||||||
|
auto* node2 = list.Emplace(2);
|
||||||
|
auto* node3 = list.Emplace(3);
|
||||||
|
testing::Mock::VerifyAndClearExpectations(&allocatorCore);
|
||||||
|
|
||||||
|
{
|
||||||
|
testing::InSequence seq;
|
||||||
|
EXPECT_CALL(allocatorCore, deallocate(node1, _));
|
||||||
|
EXPECT_CALL(allocatorCore, deallocate(node2, _));
|
||||||
|
EXPECT_CALL(allocatorCore, deallocate(node3, _));
|
||||||
|
}
|
||||||
|
list.Erase(node1);
|
||||||
|
list.Erase(node2);
|
||||||
|
list.Erase(node3);
|
||||||
|
testing::Mock::VerifyAndClearExpectations(&allocatorCore);
|
||||||
|
}
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ NO_INLINE size_t kotlin::internal::GetCurrentStackTrace(size_t skipFrames, std_s
|
|||||||
#if ! KONAN_NO_BACKTRACE
|
#if ! KONAN_NO_BACKTRACE
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include "cpp_support/Span.hpp"
|
#include "std_support/Span.hpp"
|
||||||
#include "Format.h"
|
#include "Format.h"
|
||||||
|
|
||||||
#if __has_include("dlfcn.h")
|
#if __has_include("dlfcn.h")
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
#ifndef RUNTIME_STACK_TRACE_H
|
#ifndef RUNTIME_STACK_TRACE_H
|
||||||
#define RUNTIME_STACK_TRACE_H
|
#define RUNTIME_STACK_TRACE_H
|
||||||
|
|
||||||
#include "cpp_support/Span.hpp"
|
#include "std_support/Span.hpp"
|
||||||
#include "Memory.h"
|
#include "Memory.h"
|
||||||
#include "Types.h"
|
#include "Types.h"
|
||||||
|
|
||||||
|
|||||||
@@ -19,25 +19,19 @@
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#if (KONAN_WASM || KONAN_ZEPHYR) && !defined(assert)
|
|
||||||
// assert() is needed by STLport.
|
|
||||||
#define assert(cond) if (!(cond)) abort()
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <deque>
|
|
||||||
#include <list>
|
|
||||||
#include <map>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <set>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <unordered_set>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "Alloc.h"
|
#include "Alloc.h"
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
#include "Memory.h"
|
#include "Memory.h"
|
||||||
#include "TypeInfo.h"
|
#include "TypeInfo.h"
|
||||||
|
#include "std_support/Deque.hpp"
|
||||||
|
#include "std_support/List.hpp"
|
||||||
|
#include "std_support/Map.hpp"
|
||||||
|
#include "std_support/Memory.hpp"
|
||||||
|
#include "std_support/Set.hpp"
|
||||||
|
#include "std_support/String.hpp"
|
||||||
|
#include "std_support/UnorderedMap.hpp"
|
||||||
|
#include "std_support/UnorderedSet.hpp"
|
||||||
|
#include "std_support/Vector.hpp"
|
||||||
|
|
||||||
// Note that almost all types are signed.
|
// Note that almost all types are signed.
|
||||||
typedef bool KBoolean;
|
typedef bool KBoolean;
|
||||||
@@ -61,36 +55,25 @@ typedef ObjHeader* KRef;
|
|||||||
typedef const ObjHeader* KConstRef;
|
typedef const ObjHeader* KConstRef;
|
||||||
typedef const ArrayHeader* KString;
|
typedef const ArrayHeader* KString;
|
||||||
|
|
||||||
// TODO: Consider moving these into `kotlin::std_support` namespace keeping STL names.
|
// TODO: Remove these typedefs. Use std_support directly everywhere.
|
||||||
|
using KStdString = kotlin::std_support::string;
|
||||||
// Definitions of STL classes used inside Konan runtime.
|
template <typename Value>
|
||||||
typedef std::basic_string<char, std::char_traits<char>,
|
using KStdDeque = kotlin::std_support::deque<Value>;
|
||||||
KonanAllocator<char>> KStdString;
|
template <typename Key, typename Value>
|
||||||
template<class Value>
|
using KStdUnorderedMap = kotlin::std_support::unordered_map<Key, Value>;
|
||||||
using KStdDeque = std::deque<Value, KonanAllocator<Value>>;
|
template <typename Value>
|
||||||
template<class Key, class Value>
|
using KStdUnorderedSet = kotlin::std_support::unordered_set<Value>;
|
||||||
using KStdUnorderedMap = std::unordered_map<Key, Value,
|
template <typename Value, typename Compare = std::less<Value>>
|
||||||
std::hash<Key>, std::equal_to<Key>,
|
using KStdOrderedMultiset = kotlin::std_support::multiset<Value, Compare>;
|
||||||
KonanAllocator<std::pair<const Key, Value>>>;
|
template <typename Key, typename Value>
|
||||||
template<class Value>
|
using KStdOrderedMap = kotlin::std_support::map<Key, Value>;
|
||||||
using KStdUnorderedSet = std::unordered_set<Value,
|
template <typename Value>
|
||||||
std::hash<Value>, std::equal_to<Value>,
|
using KStdVector = kotlin::std_support::vector<Value>;
|
||||||
KonanAllocator<Value>>;
|
template <typename Value>
|
||||||
template<class Value, class Compare = std::less<Value>>
|
using KStdList = kotlin::std_support::list<Value>;
|
||||||
using KStdOrderedMultiset = std::multiset<Value, Compare, KonanAllocator<Value>>;
|
template <typename Value>
|
||||||
template<class Key, class Value, class Compare = std::less<Key>>
|
using KStdUniquePtr = kotlin::std_support::unique_ptr<Value>;
|
||||||
using KStdOrderedMap = std::map<Key, Value, Compare, KonanAllocator<std::pair<const Key, Value>>>;
|
using kotlin::std_support::make_unique;
|
||||||
template<class Value>
|
|
||||||
using KStdVector = std::vector<Value, KonanAllocator<Value>>;
|
|
||||||
template<class Value>
|
|
||||||
using KStdList = std::list<Value, KonanAllocator<Value>>;
|
|
||||||
template <class Value>
|
|
||||||
using KStdUniquePtr = std::unique_ptr<Value, KonanDeleter<Value>>;
|
|
||||||
|
|
||||||
template <typename T, typename... Args>
|
|
||||||
KStdUniquePtr<T> make_unique(Args&&... args) noexcept {
|
|
||||||
return KStdUniquePtr<T>(konanConstructInstance<T>(std::forward<Args>(args)...));
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include "../Exceptions.h"
|
#include "../Exceptions.h"
|
||||||
#include "../KString.h"
|
#include "../KString.h"
|
||||||
#include "../Natives.h"
|
#include "../Natives.h"
|
||||||
|
#include "../Porting.h"
|
||||||
#include "../utf8.h"
|
#include "../utf8.h"
|
||||||
#include "../KotlinMath.h"
|
#include "../KotlinMath.h"
|
||||||
#include "../ReturnSlot.h"
|
#include "../ReturnSlot.h"
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include "../Exceptions.h"
|
#include "../Exceptions.h"
|
||||||
#include "../KString.h"
|
#include "../KString.h"
|
||||||
#include "../Natives.h"
|
#include "../Natives.h"
|
||||||
|
#include "../Porting.h"
|
||||||
#include "../utf8.h"
|
#include "../utf8.h"
|
||||||
|
|
||||||
#if defined(LINUX) || defined(FREEBSD) || defined(MACOSX) || defined(ZOS) || defined(AIX)
|
#if defined(LINUX) || defined(FREEBSD) || defined(MACOSX) || defined(ZOS) || defined(AIX)
|
||||||
|
|||||||
@@ -0,0 +1,131 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 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 "std_support/CStdlib.hpp"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
using namespace kotlin;
|
||||||
|
|
||||||
|
#if KONAN_INTERNAL_DLMALLOC
|
||||||
|
extern "C" void* dlmalloc(size_t);
|
||||||
|
extern "C" void* dlcalloc(size_t, size_t);
|
||||||
|
extern "C" void* dlrealloc(void*, size_t);
|
||||||
|
extern "C" void dlfree(void*);
|
||||||
|
#define malloc_impl dlmalloc
|
||||||
|
#define aligned_alloc_impl(alignment, size) dlmalloc(size)
|
||||||
|
#define calloc_impl dlcalloc
|
||||||
|
#define aligned_calloc_impl(alignment, num, size) dlcalloc(num, size)
|
||||||
|
#define realloc_impl dlrealloc
|
||||||
|
#define free_impl dlfree
|
||||||
|
#else
|
||||||
|
extern "C" void* konan_malloc_impl(size_t);
|
||||||
|
extern "C" void* konan_aligned_alloc_impl(size_t, size_t);
|
||||||
|
extern "C" void* konan_calloc_impl(size_t, size_t);
|
||||||
|
extern "C" void* konan_aligned_calloc_impl(size_t, size_t, size_t);
|
||||||
|
extern "C" void* konan_realloc_impl(void*, size_t);
|
||||||
|
extern "C" void konan_free_impl(void*);
|
||||||
|
#define malloc_impl konan_malloc_impl
|
||||||
|
#define aligned_alloc_impl konan_aligned_alloc_impl
|
||||||
|
#define calloc_impl konan_calloc_impl
|
||||||
|
#define aligned_calloc_impl konan_aligned_calloc_impl
|
||||||
|
#define realloc_impl konan_realloc_impl
|
||||||
|
#define free_impl konan_free_impl
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void* std_support::malloc(std::size_t size) noexcept {
|
||||||
|
return malloc_impl(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* std_support::aligned_alloc(std::size_t alignment, std::size_t size) noexcept {
|
||||||
|
return aligned_alloc_impl(alignment, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* std_support::calloc(std::size_t num, std::size_t size) noexcept {
|
||||||
|
return calloc_impl(num, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* std_support::aligned_calloc(std::size_t alignment, std::size_t num, std::size_t size) noexcept {
|
||||||
|
return aligned_calloc_impl(alignment, num, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* std_support::realloc(void* ptr, std::size_t size) noexcept {
|
||||||
|
return realloc_impl(ptr, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void std_support::free(void* ptr) noexcept {
|
||||||
|
return free_impl(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace konan {
|
||||||
|
|
||||||
|
#if KONAN_INTERNAL_DLMALLOC
|
||||||
|
// This function is being called when memory allocator needs more RAM.
|
||||||
|
|
||||||
|
#if KONAN_WASM
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr uint32_t MFAIL = ~(uint32_t)0;
|
||||||
|
constexpr uint32_t WASM_PAGESIZE_EXPONENT = 16;
|
||||||
|
constexpr uint32_t WASM_PAGESIZE = 1u << WASM_PAGESIZE_EXPONENT;
|
||||||
|
constexpr uint32_t WASM_PAGEMASK = WASM_PAGESIZE - 1;
|
||||||
|
|
||||||
|
uint32_t pageAlign(int32_t value) {
|
||||||
|
return (value + WASM_PAGEMASK) & ~(WASM_PAGEMASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t inBytes(uint32_t pageCount) {
|
||||||
|
return pageCount << WASM_PAGESIZE_EXPONENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t inPages(uint32_t value) {
|
||||||
|
return value >> WASM_PAGESIZE_EXPONENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void Konan_notify_memory_grow();
|
||||||
|
|
||||||
|
uint32_t memorySize() {
|
||||||
|
return __builtin_wasm_memory_size(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t growMemory(uint32_t delta) {
|
||||||
|
int32_t oldLength = __builtin_wasm_memory_grow(0, delta);
|
||||||
|
Konan_notify_memory_grow();
|
||||||
|
return oldLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void* moreCore(int32_t delta) {
|
||||||
|
uint32_t top = inBytes(memorySize());
|
||||||
|
if (delta > 0) {
|
||||||
|
if (growMemory(inPages(pageAlign(delta))) == 0) {
|
||||||
|
return (void*)MFAIL;
|
||||||
|
}
|
||||||
|
} else if (delta < 0) {
|
||||||
|
return (void*)MFAIL;
|
||||||
|
}
|
||||||
|
return (void*)top;
|
||||||
|
}
|
||||||
|
|
||||||
|
// dlmalloc() wants to know the page size.
|
||||||
|
long getpagesize() {
|
||||||
|
return WASM_PAGESIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
void* moreCore(int size) {
|
||||||
|
return sbrk(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
long getpagesize() {
|
||||||
|
return sysconf(_SC_PAGESIZE);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace konan
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
namespace kotlin::std_support {
|
||||||
|
|
||||||
|
void* malloc(std::size_t size) noexcept;
|
||||||
|
void* aligned_alloc(std::size_t alignment, std::size_t size) noexcept;
|
||||||
|
|
||||||
|
void* calloc(std::size_t num, std::size_t size) noexcept;
|
||||||
|
void* aligned_calloc(std::size_t alignment, std::size_t num, std::size_t size) noexcept;
|
||||||
|
|
||||||
|
void* realloc(void* ptr, std::size_t size) noexcept;
|
||||||
|
|
||||||
|
void free(void* ptr) noexcept;
|
||||||
|
|
||||||
|
} // namespace kotlin::std_support
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <deque>
|
||||||
|
|
||||||
|
#include "std_support/Memory.hpp"
|
||||||
|
|
||||||
|
namespace kotlin::std_support {
|
||||||
|
|
||||||
|
template <typename T, typename Allocator = allocator<T>>
|
||||||
|
using deque = std::deque<T, Allocator>;
|
||||||
|
|
||||||
|
} // namespace kotlin::std_support
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <forward_list>
|
||||||
|
|
||||||
|
#include "std_support/Memory.hpp"
|
||||||
|
|
||||||
|
namespace kotlin::std_support {
|
||||||
|
|
||||||
|
template <typename T, typename Allocator = allocator<T>>
|
||||||
|
using forward_list = std::forward_list<T, Allocator>;
|
||||||
|
|
||||||
|
} // namespace kotlin::std_support
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
#include "std_support/Memory.hpp"
|
||||||
|
|
||||||
|
namespace kotlin::std_support {
|
||||||
|
|
||||||
|
template <typename T, typename Allocator = allocator<T>>
|
||||||
|
using list = std::list<T, Allocator>;
|
||||||
|
|
||||||
|
} // namespace kotlin::std_support
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include "std_support/Memory.hpp"
|
||||||
|
|
||||||
|
namespace kotlin::std_support {
|
||||||
|
|
||||||
|
template <typename Key, typename T, typename Compare = std::less<Key>, typename Allocator = allocator<std::pair<const Key, T>>>
|
||||||
|
using map = std::map<Key, T, Compare, Allocator>;
|
||||||
|
|
||||||
|
template <typename Key, typename T, typename Compare = std::less<Key>, typename Allocator = allocator<std::pair<const Key, T>>>
|
||||||
|
using multimap = std::multimap<Key, T, Compare, Allocator>;
|
||||||
|
|
||||||
|
} // namespace kotlin::std_support
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <memory>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "std_support/CStdlib.hpp"
|
||||||
|
|
||||||
|
namespace kotlin::std_support {
|
||||||
|
|
||||||
|
// Default allocator for Kotlin.
|
||||||
|
// TODO: Consider overriding global operator new and operator delete instead. However, make sure this does
|
||||||
|
// not extend over to interop.
|
||||||
|
template <typename T>
|
||||||
|
struct allocator {
|
||||||
|
using value_type = T;
|
||||||
|
using size_type = std::size_t;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using propagate_on_container_move_assignment = std::true_type;
|
||||||
|
using is_always_equal = std::true_type;
|
||||||
|
|
||||||
|
allocator() noexcept = default;
|
||||||
|
|
||||||
|
allocator(const allocator&) noexcept = default;
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
allocator(const allocator<U>&) noexcept {}
|
||||||
|
|
||||||
|
// TODO: maybe malloc, actually?
|
||||||
|
T* allocate(std::size_t n) noexcept { return static_cast<T*>(std_support::calloc(n, sizeof(T))); }
|
||||||
|
|
||||||
|
void deallocate(T* p, std::size_t n) noexcept { std_support::free(p); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
bool operator==(const allocator<T>&, const allocator<U>&) noexcept {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
bool operator!=(const allocator<T>&, const allocator<U>&) noexcept {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename Allocator, typename... Args>
|
||||||
|
T* allocator_new(const Allocator& allocator, Args&&... args) {
|
||||||
|
static_assert(!std::is_array_v<T>, "T cannot be an array");
|
||||||
|
|
||||||
|
using TAllocatorTraits = typename std::allocator_traits<Allocator>::template rebind_traits<T>;
|
||||||
|
using TAllocator = typename std::allocator_traits<Allocator>::template rebind_alloc<T>;
|
||||||
|
|
||||||
|
auto a = TAllocator(allocator);
|
||||||
|
T* ptr = TAllocatorTraits::allocate(a, 1);
|
||||||
|
|
||||||
|
#if KONAN_NO_EXCEPTIONS
|
||||||
|
TAllocatorTraits::construct(a, ptr, std::forward<Args>(args)...);
|
||||||
|
return ptr;
|
||||||
|
#else
|
||||||
|
try {
|
||||||
|
TAllocatorTraits::construct(a, ptr, std::forward<Args>(args)...);
|
||||||
|
return ptr;
|
||||||
|
} catch (...) {
|
||||||
|
TAllocatorTraits::deallocate(a, ptr, 1);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename Allocator>
|
||||||
|
void allocator_delete(const Allocator& allocator, T* ptr) noexcept {
|
||||||
|
static_assert(!std::is_array_v<T>, "T cannot be an array");
|
||||||
|
|
||||||
|
using TAllocatorTraits = typename std::allocator_traits<Allocator>::template rebind_traits<T>;
|
||||||
|
using TAllocator = typename std::allocator_traits<Allocator>::template rebind_alloc<T>;
|
||||||
|
|
||||||
|
auto a = TAllocator(allocator);
|
||||||
|
TAllocatorTraits::destroy(a, ptr);
|
||||||
|
TAllocatorTraits::deallocate(a, ptr, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename Allocator>
|
||||||
|
class allocator_deleter {
|
||||||
|
static_assert(!std::is_array_v<T>, "T cannot be an array");
|
||||||
|
|
||||||
|
template <typename A, typename V>
|
||||||
|
using Rebind = typename std::allocator_traits<A>::template rebind_alloc<V>;
|
||||||
|
|
||||||
|
template <typename A1, typename A2>
|
||||||
|
static inline constexpr bool allocatorsCompatible = std::is_same_v<Rebind<A1, T>, Rebind<A2, T>>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
allocator_deleter() noexcept = default;
|
||||||
|
|
||||||
|
explicit allocator_deleter(const Allocator& allocator) noexcept : allocator(allocator) {}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename U,
|
||||||
|
typename Other,
|
||||||
|
typename = std::enable_if_t<std::is_convertible_v<U*, T*> && allocatorsCompatible<Allocator, Other>>>
|
||||||
|
allocator_deleter(const allocator_deleter<U, Other>& rhs) noexcept : allocator(rhs.allocator) {}
|
||||||
|
|
||||||
|
void operator()(T* ptr) noexcept { allocator_delete(allocator, ptr); }
|
||||||
|
|
||||||
|
[[no_unique_address]] Allocator allocator;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename Allocator, typename... Args>
|
||||||
|
auto allocate_unique(const Allocator& allocator, Args&&... args) {
|
||||||
|
static_assert(!std::is_array_v<T>, "T cannot be an array");
|
||||||
|
|
||||||
|
using TAllocator = typename std::allocator_traits<Allocator>::template rebind_alloc<T>;
|
||||||
|
using TDeleter = allocator_deleter<T, TAllocator>;
|
||||||
|
return std::unique_ptr<T, TDeleter>(allocator_new<T>(allocator, std::forward<Args>(args)...), TDeleter(allocator));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using default_delete = allocator_deleter<T, allocator<T>>;
|
||||||
|
|
||||||
|
template <typename T, typename Deleter = default_delete<T>>
|
||||||
|
using unique_ptr = std::unique_ptr<T, Deleter>;
|
||||||
|
|
||||||
|
template <typename T, typename... Args>
|
||||||
|
auto make_unique(Args&&... args) {
|
||||||
|
static_assert(!std::is_array_v<T>, "T cannot be an array");
|
||||||
|
|
||||||
|
return allocate_unique<T>(allocator<T>(), std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename... Args>
|
||||||
|
auto make_shared(Args&&... args) {
|
||||||
|
static_assert(!std::is_array_v<T>, "T cannot be an array");
|
||||||
|
|
||||||
|
return std::allocate_shared<T>(allocator<T>(), std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename Allocator>
|
||||||
|
auto nullptr_unique(const Allocator& allocator = Allocator()) noexcept {
|
||||||
|
static_assert(!std::is_array_v<T>, "T cannot be an array");
|
||||||
|
|
||||||
|
using TAllocator = typename std::allocator_traits<Allocator>::template rebind_alloc<T>;
|
||||||
|
using TDeleter = allocator_deleter<T, TAllocator>;
|
||||||
|
return std::unique_ptr<T, TDeleter>(nullptr, TDeleter(allocator));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace kotlin::std_support
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 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 "std_support/New.hpp"
|
||||||
|
|
||||||
|
#include "std_support/CStdlib.hpp"
|
||||||
|
|
||||||
|
using namespace kotlin;
|
||||||
|
|
||||||
|
// TODO: Maybe malloc instead of calloc?
|
||||||
|
|
||||||
|
void* operator new(std::size_t count, kotlin::std_support::kalloc_t) noexcept {
|
||||||
|
return std_support::calloc(1, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* operator new[](std::size_t count, kotlin::std_support::kalloc_t) noexcept {
|
||||||
|
return std_support::calloc(1, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator delete(void* ptr, kotlin::std_support::kalloc_t) noexcept {
|
||||||
|
std_support::free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator delete[](void* ptr, kotlin::std_support::kalloc_t) noexcept {
|
||||||
|
std_support::free(ptr);
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <new>
|
||||||
|
|
||||||
|
namespace kotlin::std_support {
|
||||||
|
|
||||||
|
struct kalloc_t {};
|
||||||
|
inline constexpr kalloc_t kalloc = kotlin::std_support::kalloc_t{};
|
||||||
|
|
||||||
|
} // namespace kotlin::std_support
|
||||||
|
|
||||||
|
// TODO: Add align_val_t overloads once we make sure all targets support aligned allocation.
|
||||||
|
// (also requires removing `-fno-aligned-allocation` compiler flag).
|
||||||
|
|
||||||
|
void* operator new(std::size_t count, kotlin::std_support::kalloc_t) noexcept;
|
||||||
|
void* operator new[](std::size_t count, kotlin::std_support::kalloc_t) noexcept;
|
||||||
|
void operator delete(void* ptr, kotlin::std_support::kalloc_t) noexcept;
|
||||||
|
void operator delete[](void* ptr, kotlin::std_support::kalloc_t) noexcept;
|
||||||
|
|
||||||
|
namespace kotlin::std_support {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void kdelete(T* ptr) noexcept {
|
||||||
|
ptr->~T();
|
||||||
|
::operator delete(ptr, kalloc);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace kotlin::std_support
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 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 "std_support/New.hpp"
|
||||||
|
|
||||||
|
#include "gmock/gmock.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
using namespace kotlin;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class Class {
|
||||||
|
public:
|
||||||
|
explicit Class(int32_t x = 17) : x_(x) {}
|
||||||
|
|
||||||
|
int32_t x() const { return x_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int32_t x_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ClassThrows {
|
||||||
|
public:
|
||||||
|
explicit ClassThrows(int32_t x = 17) : x_(x) { throw 13; }
|
||||||
|
|
||||||
|
int32_t x() const { return x_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int32_t x_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST(NewTest, NewDelete) {
|
||||||
|
Class* ptr = new (std_support::kalloc) Class(42);
|
||||||
|
EXPECT_THAT(ptr->x(), 42);
|
||||||
|
std_support::kdelete(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(NewTest, NewDeleteArray) {
|
||||||
|
Class* ptr = new (std_support::kalloc) Class[13];
|
||||||
|
EXPECT_THAT(ptr[3].x(), 17);
|
||||||
|
std_support::kdelete(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(NewTest, NewThrows) {
|
||||||
|
EXPECT_THROW(new (std_support::kalloc) ClassThrows(42), int);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(NewTest, NewThrowsArray) {
|
||||||
|
EXPECT_THROW(new (std_support::kalloc) ClassThrows[13], int);
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
## Support for C++ standard library.
|
||||||
|
|
||||||
|
This contains backported utilities from future standards, implementation of not-yet-standardized proposals, and adjustments of standardized utilities to be used within Kotlin/Native runtime.
|
||||||
|
|
||||||
|
Everything here lives in `kotlin::std_support` namespace.
|
||||||
|
|
||||||
|
Backporting from C++20:
|
||||||
|
* `Span.hpp` - [`std::span`](https://en.cppreference.com/w/cpp/container/span)
|
||||||
|
|
||||||
|
Proposals:
|
||||||
|
* `Memory.hpp` - [`p0211r3`](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p0211r3.html)
|
||||||
|
|
||||||
|
Adjustments:
|
||||||
|
* `CStdlib.hpp` -
|
||||||
|
`std_support::malloc`, `std_support::aligned_alloc`, `std_support::calloc`, `std_support::realloc`, `std_support::free` that use custom allocation scheme,
|
||||||
|
`std_support::aligned_calloc` as a version of `calloc` that allows changing alignment.
|
||||||
|
* `Memory.hpp` -
|
||||||
|
`std_support::allocator` using `std_support::calloc`/`std_support::free`,
|
||||||
|
`std_support::default_delete` that uses `std_support::free`,
|
||||||
|
`std_support::unique_ptr` that uses `std_support::default_delete`,
|
||||||
|
`std_support::make_unique` and `std_support::make_shared` that use `std_support::allocator`,
|
||||||
|
`std_support::nullptr_unique` - `nullptr` replacement for `unique_ptr` that takes an allocator.
|
||||||
|
* `New.hpp` -
|
||||||
|
custom operator `new` with `std_support::kalloc` marker argument that delegates to `std_support` allocator,
|
||||||
|
`std_support::kdelete` as a replacement for operator `delete` for objects created with custom `new`.
|
||||||
|
* `Deque.hpp`, `ForwardList.hpp`, `List.hpp`, `Map.hpp`, `Set.hpp`, `String.hpp`, `UnorderedMap.hpp`, `UnorderedSet.hpp`, `Vector.hpp` -
|
||||||
|
standard containers and `std_support::string` that default to using `std_support::allocator`.
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
#include "std_support/Memory.hpp"
|
||||||
|
|
||||||
|
namespace kotlin::std_support {
|
||||||
|
|
||||||
|
template <typename Key, typename Compare = std::less<Key>, typename Allocator = allocator<Key>>
|
||||||
|
using set = std::set<Key, Compare, Allocator>;
|
||||||
|
|
||||||
|
template <typename Key, typename Compare = std::less<Key>, typename Allocator = allocator<Key>>
|
||||||
|
using multiset = std::multiset<Key, Compare, Allocator>;
|
||||||
|
|
||||||
|
} // namespace kotlin::std_support
|
||||||
-2
@@ -13,8 +13,6 @@
|
|||||||
|
|
||||||
#include "KAssert.h"
|
#include "KAssert.h"
|
||||||
|
|
||||||
// Modelling https://en.cppreference.com/w/cpp/container/span from C++20.
|
|
||||||
|
|
||||||
namespace kotlin {
|
namespace kotlin {
|
||||||
namespace std_support {
|
namespace std_support {
|
||||||
|
|
||||||
@@ -0,0 +1,315 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 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 "std_support/Memory.hpp"
|
||||||
|
|
||||||
|
#include "gmock/gmock.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
#include "AllocatorTestSupport.hpp"
|
||||||
|
#include "KAssert.h"
|
||||||
|
#include "Utils.hpp"
|
||||||
|
|
||||||
|
using namespace kotlin;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class EmptyClass {};
|
||||||
|
|
||||||
|
class Class {
|
||||||
|
public:
|
||||||
|
explicit Class(int32_t x) : x_(x) {}
|
||||||
|
|
||||||
|
int32_t x() const { return x_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int32_t x_;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(Class) > sizeof(EmptyClass));
|
||||||
|
|
||||||
|
class ClassThrows {
|
||||||
|
public:
|
||||||
|
explicit ClassThrows(int32_t x) : x_(x) { throw 13; }
|
||||||
|
|
||||||
|
int32_t x() const { return x_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int32_t x_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DerivedClass : public Class {
|
||||||
|
public:
|
||||||
|
DerivedClass(int32_t x, int32_t y) : Class(x), y_(y) {}
|
||||||
|
|
||||||
|
int32_t y() const { return y_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int32_t y_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MockClass : private Pinned {
|
||||||
|
public:
|
||||||
|
class Mocker : private Pinned {
|
||||||
|
public:
|
||||||
|
Mocker() noexcept {
|
||||||
|
RuntimeAssert(instance_ == nullptr, "Only one MockClass::Mocker at a time allowed");
|
||||||
|
instance_ = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
~Mocker() {
|
||||||
|
RuntimeAssert(instance_ == this, "MockClass::Mocker::instance_ is broken.");
|
||||||
|
instance_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOCK_METHOD(void, ctor, (MockClass*, int));
|
||||||
|
MOCK_METHOD(void, dtor, (MockClass*), (noexcept));
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class MockClass;
|
||||||
|
|
||||||
|
static Mocker* instance_;
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit MockClass(int x) { Mocker::instance_->ctor(this, x); }
|
||||||
|
|
||||||
|
~MockClass() noexcept { Mocker::instance_->dtor(this); }
|
||||||
|
|
||||||
|
int32_t x() const { return x_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int32_t x_;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(MockClass) > sizeof(EmptyClass));
|
||||||
|
|
||||||
|
// static
|
||||||
|
MockClass::Mocker* MockClass::Mocker::instance_ = nullptr;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST(StdSupportMemoryTest, Allocator) {
|
||||||
|
using Allocator = std_support::allocator<Class>;
|
||||||
|
using Traits = std::allocator_traits<Allocator>;
|
||||||
|
Allocator allocator;
|
||||||
|
Class* ptr = Traits::allocate(allocator, 1);
|
||||||
|
new (ptr) Class(42);
|
||||||
|
EXPECT_THAT(ptr->x(), 42);
|
||||||
|
Traits::deallocate(allocator, ptr, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StdSupportMemoryTest, AllocatorFromWrongClass) {
|
||||||
|
using WrongClassAllocator = std_support::allocator<EmptyClass>;
|
||||||
|
WrongClassAllocator base;
|
||||||
|
using Allocator = typename std::allocator_traits<WrongClassAllocator>::template rebind_alloc<Class>;
|
||||||
|
using Traits = typename std::allocator_traits<WrongClassAllocator>::template rebind_traits<Class>;
|
||||||
|
Allocator allocator = Allocator(base);
|
||||||
|
Class* ptr = Traits::allocate(allocator, 1);
|
||||||
|
new (ptr) Class(42);
|
||||||
|
EXPECT_THAT(ptr->x(), 42);
|
||||||
|
Traits::deallocate(allocator, ptr, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StdSupportMemoryTest, MakeUnique) {
|
||||||
|
auto ptr = std_support::make_unique<Class>(42);
|
||||||
|
EXPECT_THAT(ptr->x(), 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StdSupportMemoryTest, MakeUniqueThrows) {
|
||||||
|
EXPECT_THROW(std_support::make_unique<ClassThrows>(42), int);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StdSupportMemoryTest, MakeShared) {
|
||||||
|
auto ptr = std_support::make_shared<Class>(42);
|
||||||
|
EXPECT_THAT(ptr->x(), 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StdSupportMemoryTest, MakeSharedThrows) {
|
||||||
|
EXPECT_THROW(std_support::make_shared<ClassThrows>(42), int);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StdSupportMemoryTest, AllocatorNew) {
|
||||||
|
testing::StrictMock<test_support::MockAllocatorCore> allocatorCore;
|
||||||
|
testing::StrictMock<MockClass::Mocker> mocker;
|
||||||
|
|
||||||
|
MockClass* expectedPtr = reinterpret_cast<MockClass*>(13);
|
||||||
|
|
||||||
|
{
|
||||||
|
testing::InSequence s;
|
||||||
|
EXPECT_CALL(allocatorCore, allocate(sizeof(MockClass))).WillOnce(testing::Return(expectedPtr));
|
||||||
|
EXPECT_CALL(mocker, ctor(expectedPtr, 42));
|
||||||
|
}
|
||||||
|
auto* ptr = std_support::allocator_new<MockClass>(test_support::MakeAllocator<MockClass>(allocatorCore), 42);
|
||||||
|
EXPECT_THAT(ptr, expectedPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StdSupportMemoryTest, AllocatorNewThrows) {
|
||||||
|
testing::StrictMock<test_support::MockAllocatorCore> allocatorCore;
|
||||||
|
testing::StrictMock<MockClass::Mocker> mocker;
|
||||||
|
|
||||||
|
MockClass* expectedPtr = reinterpret_cast<MockClass*>(13);
|
||||||
|
|
||||||
|
{
|
||||||
|
testing::InSequence s;
|
||||||
|
EXPECT_CALL(allocatorCore, allocate(sizeof(MockClass))).WillOnce(testing::Return(expectedPtr));
|
||||||
|
EXPECT_CALL(mocker, ctor(expectedPtr, 42)).WillOnce([] { throw 17; });
|
||||||
|
EXPECT_CALL(allocatorCore, deallocate(expectedPtr, sizeof(MockClass)));
|
||||||
|
}
|
||||||
|
EXPECT_THROW(std_support::allocator_new<MockClass>(test_support::MakeAllocator<MockClass>(allocatorCore), 42), int);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StdSupportMemoryTest, AllocatorNewWrongType) {
|
||||||
|
testing::StrictMock<test_support::MockAllocatorCore> allocatorCore;
|
||||||
|
testing::StrictMock<MockClass::Mocker> mocker;
|
||||||
|
|
||||||
|
MockClass* expectedPtr = reinterpret_cast<MockClass*>(13);
|
||||||
|
|
||||||
|
{
|
||||||
|
testing::InSequence s;
|
||||||
|
EXPECT_CALL(allocatorCore, allocate(sizeof(MockClass))).WillOnce(testing::Return(expectedPtr));
|
||||||
|
EXPECT_CALL(mocker, ctor(expectedPtr, 42));
|
||||||
|
}
|
||||||
|
auto* ptr = std_support::allocator_new<MockClass>(test_support::MakeAllocator<EmptyClass>(allocatorCore), 42);
|
||||||
|
EXPECT_THAT(ptr, expectedPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StdSupportMemoryTest, AllocatorDelete) {
|
||||||
|
testing::StrictMock<test_support::MockAllocatorCore> allocatorCore;
|
||||||
|
testing::StrictMock<MockClass::Mocker> mocker;
|
||||||
|
|
||||||
|
MockClass* expectedPtr = reinterpret_cast<MockClass*>(13);
|
||||||
|
|
||||||
|
{
|
||||||
|
testing::InSequence s;
|
||||||
|
EXPECT_CALL(mocker, dtor(expectedPtr));
|
||||||
|
EXPECT_CALL(allocatorCore, deallocate(expectedPtr, sizeof(MockClass)));
|
||||||
|
}
|
||||||
|
std_support::allocator_delete(test_support::MakeAllocator<MockClass>(allocatorCore), expectedPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StdSupportMemoryTest, AllocatorDeleteWrongType) {
|
||||||
|
testing::StrictMock<test_support::MockAllocatorCore> allocatorCore;
|
||||||
|
testing::StrictMock<MockClass::Mocker> mocker;
|
||||||
|
|
||||||
|
MockClass* expectedPtr = reinterpret_cast<MockClass*>(13);
|
||||||
|
|
||||||
|
{
|
||||||
|
testing::InSequence s;
|
||||||
|
EXPECT_CALL(mocker, dtor(expectedPtr));
|
||||||
|
EXPECT_CALL(allocatorCore, deallocate(expectedPtr, sizeof(MockClass)));
|
||||||
|
}
|
||||||
|
std_support::allocator_delete(test_support::MakeAllocator<EmptyClass>(allocatorCore), expectedPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StdSupportMemoryTest, AllocateUnique) {
|
||||||
|
testing::StrictMock<test_support::MockAllocatorCore> allocatorCore;
|
||||||
|
testing::StrictMock<MockClass::Mocker> mocker;
|
||||||
|
|
||||||
|
MockClass* expectedPtr = reinterpret_cast<MockClass*>(13);
|
||||||
|
|
||||||
|
{
|
||||||
|
testing::InSequence s;
|
||||||
|
EXPECT_CALL(allocatorCore, allocate(sizeof(MockClass))).WillOnce(testing::Return(expectedPtr));
|
||||||
|
EXPECT_CALL(mocker, ctor(expectedPtr, 42));
|
||||||
|
}
|
||||||
|
auto ptr = std_support::allocate_unique<MockClass>(test_support::MakeAllocator<MockClass>(allocatorCore), 42);
|
||||||
|
EXPECT_THAT(ptr.get(), expectedPtr);
|
||||||
|
|
||||||
|
{
|
||||||
|
testing::InSequence s;
|
||||||
|
EXPECT_CALL(mocker, dtor(expectedPtr));
|
||||||
|
EXPECT_CALL(allocatorCore, deallocate(expectedPtr, sizeof(MockClass)));
|
||||||
|
}
|
||||||
|
ptr.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StdSupportMemoryTest, AllocateUniqueThrows) {
|
||||||
|
testing::StrictMock<test_support::MockAllocatorCore> allocatorCore;
|
||||||
|
testing::StrictMock<MockClass::Mocker> mocker;
|
||||||
|
|
||||||
|
MockClass* expectedPtr = reinterpret_cast<MockClass*>(13);
|
||||||
|
|
||||||
|
{
|
||||||
|
testing::InSequence s;
|
||||||
|
EXPECT_CALL(allocatorCore, allocate(sizeof(MockClass))).WillOnce(testing::Return(expectedPtr));
|
||||||
|
EXPECT_CALL(mocker, ctor(expectedPtr, 42)).WillOnce([] { throw 17; });
|
||||||
|
EXPECT_CALL(allocatorCore, deallocate(expectedPtr, sizeof(MockClass)));
|
||||||
|
}
|
||||||
|
EXPECT_THROW(std_support::allocate_unique<MockClass>(test_support::MakeAllocator<MockClass>(allocatorCore), 42), int);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StdSupportMemoryTest, AllocateUniqueWrongType) {
|
||||||
|
testing::StrictMock<test_support::MockAllocatorCore> allocatorCore;
|
||||||
|
testing::StrictMock<MockClass::Mocker> mocker;
|
||||||
|
|
||||||
|
MockClass* expectedPtr = reinterpret_cast<MockClass*>(13);
|
||||||
|
|
||||||
|
{
|
||||||
|
testing::InSequence s;
|
||||||
|
EXPECT_CALL(allocatorCore, allocate(sizeof(MockClass))).WillOnce(testing::Return(expectedPtr));
|
||||||
|
EXPECT_CALL(mocker, ctor(expectedPtr, 42));
|
||||||
|
}
|
||||||
|
auto ptr = std_support::allocate_unique<MockClass>(test_support::MakeAllocator<EmptyClass>(allocatorCore), 42);
|
||||||
|
EXPECT_THAT(ptr.get(), expectedPtr);
|
||||||
|
|
||||||
|
{
|
||||||
|
testing::InSequence s;
|
||||||
|
EXPECT_CALL(mocker, dtor(expectedPtr));
|
||||||
|
EXPECT_CALL(allocatorCore, deallocate(expectedPtr, sizeof(MockClass)));
|
||||||
|
}
|
||||||
|
ptr.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename Allocator>
|
||||||
|
using UniquePtr = std_support::unique_ptr<T, std_support::allocator_deleter<T, Allocator>>;
|
||||||
|
|
||||||
|
TEST(StdSupportMemoryTest, UniquePtrConversions) {
|
||||||
|
static_assert(std::is_convertible_v<std_support::unique_ptr<DerivedClass>, std_support::unique_ptr<Class>>);
|
||||||
|
static_assert(!std::is_convertible_v<std_support::unique_ptr<Class>, std_support::unique_ptr<DerivedClass>>);
|
||||||
|
static_assert(!std::is_convertible_v<std_support::unique_ptr<Class>, std_support::unique_ptr<int>>);
|
||||||
|
static_assert(!std::is_convertible_v<std_support::unique_ptr<int>, std_support::unique_ptr<Class>>);
|
||||||
|
|
||||||
|
static_assert(!std::is_assignable_v<std_support::unique_ptr<DerivedClass>, std_support::unique_ptr<Class>>);
|
||||||
|
static_assert(std::is_assignable_v<std_support::unique_ptr<Class>, std_support::unique_ptr<DerivedClass>>);
|
||||||
|
static_assert(!std::is_assignable_v<std_support::unique_ptr<Class>, std_support::unique_ptr<int>>);
|
||||||
|
static_assert(!std::is_assignable_v<std_support::unique_ptr<int>, std_support::unique_ptr<Class>>);
|
||||||
|
|
||||||
|
using AllocatorClass = test_support::Allocator<Class, test_support::MockAllocatorCore>;
|
||||||
|
using AllocatorDerivedClass = test_support::Allocator<DerivedClass, test_support::MockAllocatorCore>;
|
||||||
|
using AllocatorInt = test_support::Allocator<int, test_support::MockAllocatorCore>;
|
||||||
|
|
||||||
|
static_assert(std::is_convertible_v<UniquePtr<DerivedClass, AllocatorClass>, UniquePtr<Class, AllocatorClass>>);
|
||||||
|
static_assert(std::is_convertible_v<UniquePtr<DerivedClass, AllocatorDerivedClass>, UniquePtr<Class, AllocatorClass>>);
|
||||||
|
static_assert(std::is_convertible_v<UniquePtr<DerivedClass, AllocatorClass>, UniquePtr<Class, AllocatorDerivedClass>>);
|
||||||
|
static_assert(std::is_convertible_v<UniquePtr<DerivedClass, AllocatorDerivedClass>, UniquePtr<Class, AllocatorDerivedClass>>);
|
||||||
|
static_assert(!std::is_convertible_v<UniquePtr<Class, AllocatorClass>, UniquePtr<DerivedClass, AllocatorClass>>);
|
||||||
|
static_assert(!std::is_convertible_v<UniquePtr<Class, AllocatorDerivedClass>, UniquePtr<DerivedClass, AllocatorClass>>);
|
||||||
|
static_assert(!std::is_convertible_v<UniquePtr<Class, AllocatorClass>, UniquePtr<DerivedClass, AllocatorDerivedClass>>);
|
||||||
|
static_assert(!std::is_convertible_v<UniquePtr<Class, AllocatorDerivedClass>, UniquePtr<DerivedClass, AllocatorDerivedClass>>);
|
||||||
|
static_assert(!std::is_convertible_v<UniquePtr<Class, AllocatorClass>, UniquePtr<int, AllocatorInt>>);
|
||||||
|
static_assert(!std::is_convertible_v<UniquePtr<Class, AllocatorDerivedClass>, UniquePtr<int, AllocatorInt>>);
|
||||||
|
static_assert(!std::is_convertible_v<UniquePtr<int, AllocatorInt>, UniquePtr<Class, AllocatorClass>>);
|
||||||
|
static_assert(!std::is_convertible_v<UniquePtr<int, AllocatorInt>, UniquePtr<DerivedClass, AllocatorDerivedClass>>);
|
||||||
|
|
||||||
|
static_assert(!std::is_assignable_v<UniquePtr<DerivedClass, AllocatorClass>, UniquePtr<Class, AllocatorClass>>);
|
||||||
|
static_assert(!std::is_assignable_v<UniquePtr<DerivedClass, AllocatorDerivedClass>, UniquePtr<Class, AllocatorClass>>);
|
||||||
|
static_assert(!std::is_assignable_v<UniquePtr<DerivedClass, AllocatorClass>, UniquePtr<Class, AllocatorDerivedClass>>);
|
||||||
|
static_assert(!std::is_assignable_v<UniquePtr<DerivedClass, AllocatorDerivedClass>, UniquePtr<Class, AllocatorDerivedClass>>);
|
||||||
|
static_assert(std::is_assignable_v<UniquePtr<Class, AllocatorClass>, UniquePtr<DerivedClass, AllocatorClass>>);
|
||||||
|
static_assert(std::is_assignable_v<UniquePtr<Class, AllocatorDerivedClass>, UniquePtr<DerivedClass, AllocatorClass>>);
|
||||||
|
static_assert(std::is_assignable_v<UniquePtr<Class, AllocatorClass>, UniquePtr<DerivedClass, AllocatorDerivedClass>>);
|
||||||
|
static_assert(std::is_assignable_v<UniquePtr<Class, AllocatorDerivedClass>, UniquePtr<DerivedClass, AllocatorDerivedClass>>);
|
||||||
|
static_assert(!std::is_assignable_v<UniquePtr<Class, AllocatorClass>, UniquePtr<int, AllocatorInt>>);
|
||||||
|
static_assert(!std::is_assignable_v<UniquePtr<Class, AllocatorDerivedClass>, UniquePtr<int, AllocatorInt>>);
|
||||||
|
static_assert(!std::is_assignable_v<UniquePtr<int, AllocatorInt>, UniquePtr<Class, AllocatorClass>>);
|
||||||
|
static_assert(!std::is_assignable_v<UniquePtr<int, AllocatorInt>, UniquePtr<DerivedClass, AllocatorDerivedClass>>);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StdSupportMemoryTest, NullptrUnique) {
|
||||||
|
testing::StrictMock<test_support::MockAllocatorCore> allocatorCore;
|
||||||
|
auto allocator = test_support::MakeAllocator<int>(allocatorCore);
|
||||||
|
|
||||||
|
std_support::unique_ptr<int, std_support::allocator_deleter<int, decltype(allocator)>> ptr =
|
||||||
|
std_support::nullptr_unique<int>(allocator);
|
||||||
|
EXPECT_THAT(ptr.get(), nullptr);
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "std_support/Memory.hpp"
|
||||||
|
|
||||||
|
namespace kotlin::std_support {
|
||||||
|
|
||||||
|
template <typename CharT, typename Traits = std::char_traits<CharT>, typename Allocator = allocator<CharT>>
|
||||||
|
using basic_string = std::basic_string<CharT, Traits, Allocator>;
|
||||||
|
|
||||||
|
using string = basic_string<char>;
|
||||||
|
using wstring = basic_string<wchar_t>;
|
||||||
|
using u16string = basic_string<char16_t>;
|
||||||
|
using u32string = basic_string<char32_t>;
|
||||||
|
|
||||||
|
} // namespace kotlin::std_support
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include "std_support/Memory.hpp"
|
||||||
|
|
||||||
|
namespace kotlin::std_support {
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename Key,
|
||||||
|
typename T,
|
||||||
|
typename Hash = std::hash<Key>,
|
||||||
|
typename KeyEqual = std::equal_to<Key>,
|
||||||
|
typename Allocator = allocator<std::pair<const Key, T>>>
|
||||||
|
using unordered_map = std::unordered_map<Key, T, Hash, KeyEqual, Allocator>;
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename Key,
|
||||||
|
typename T,
|
||||||
|
typename Hash = std::hash<Key>,
|
||||||
|
typename KeyEqual = std::equal_to<Key>,
|
||||||
|
typename Allocator = allocator<std::pair<const Key, T>>>
|
||||||
|
using unordered_multimap = std::unordered_multimap<Key, T, Hash, KeyEqual, Allocator>;
|
||||||
|
|
||||||
|
} // namespace kotlin::std_support
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
#include "std_support/Memory.hpp"
|
||||||
|
|
||||||
|
namespace kotlin::std_support {
|
||||||
|
|
||||||
|
template <typename Key, typename Hash = std::hash<Key>, typename KeyEqual = std::equal_to<Key>, typename Allocator = allocator<Key>>
|
||||||
|
using unordered_set = std::unordered_set<Key, Hash, KeyEqual, Allocator>;
|
||||||
|
|
||||||
|
template <typename Key, typename Hash = std::hash<Key>, typename KeyEqual = std::equal_to<Key>, typename Allocator = allocator<Key>>
|
||||||
|
using unordered_multiset = std::unordered_multiset<Key, Hash, KeyEqual, Allocator>;
|
||||||
|
|
||||||
|
} // namespace kotlin::std_support
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "std_support/Memory.hpp"
|
||||||
|
|
||||||
|
namespace kotlin::std_support {
|
||||||
|
|
||||||
|
template <typename T, typename Allocator = allocator<T>>
|
||||||
|
using vector = std::vector<T, Allocator>;
|
||||||
|
|
||||||
|
} // namespace kotlin::std_support
|
||||||
@@ -21,7 +21,7 @@ namespace kotlin {
|
|||||||
namespace mm {
|
namespace mm {
|
||||||
|
|
||||||
// Optional data that's lazily allocated only for objects that need it.
|
// Optional data that's lazily allocated only for objects that need it.
|
||||||
class ExtraObjectData : private Pinned, public KonanAllocatorAware {
|
class ExtraObjectData : private Pinned {
|
||||||
public:
|
public:
|
||||||
// flags are stored as single atomic uint32, values are bit numbers in that uint32
|
// flags are stored as single atomic uint32, values are bit numbers in that uint32
|
||||||
enum Flags : uint32_t {
|
enum Flags : uint32_t {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include "FinalizerHooks.hpp"
|
#include "FinalizerHooks.hpp"
|
||||||
#include "Memory.h"
|
#include "Memory.h"
|
||||||
#include "Mutex.hpp"
|
#include "Mutex.hpp"
|
||||||
|
#include "Porting.h"
|
||||||
#include "Types.h"
|
#include "Types.h"
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
|
|
||||||
@@ -45,8 +46,7 @@ class ObjectFactoryStorage : private Pinned {
|
|||||||
using unique_ptr = std::unique_ptr<T, Deleter<T>>;
|
using unique_ptr = std::unique_ptr<T, Deleter<T>>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// This class does not know its size at compile-time. Does not inherit from `KonanAllocatorAware` because
|
// This class does not know its size at compile-time.
|
||||||
// in `KonanAllocatorAware::operator new(size_t size, KonanAllocTag)` `size` would be incorrect.
|
|
||||||
class Node : private Pinned {
|
class Node : private Pinned {
|
||||||
constexpr static size_t DataOffset() noexcept { return AlignUp(sizeof(Node), DataAlignment); }
|
constexpr static size_t DataOffset() noexcept { return AlignUp(sizeof(Node), DataAlignment); }
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,39 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
* Copyright 2010-2022 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.
|
* that can be found in the LICENSE file.
|
||||||
*/
|
*/
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
#include <cstddef>
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
void* mi_calloc(size_t, size_t);
|
void* mi_malloc(size_t size);
|
||||||
void mi_free(void*);
|
void* mi_malloc_aligned(size_t size, size_t alignment);
|
||||||
|
void* mi_calloc(size_t count, size_t size);
|
||||||
void* mi_calloc_aligned(size_t count, size_t size, size_t alignment);
|
void* mi_calloc_aligned(size_t count, size_t size, size_t alignment);
|
||||||
|
void* mi_realloc(void* ptr, size_t size);
|
||||||
void* konan_calloc_impl(size_t n_elements, size_t elem_size) {
|
void mi_free(void* ptr);
|
||||||
return mi_calloc(n_elements, elem_size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void* konan_calloc_aligned_impl(size_t count, size_t size, size_t alignment) {
|
extern "C" void* konan_malloc_impl(size_t size) {
|
||||||
return mi_calloc_aligned(count, size, alignment);
|
return mi_malloc(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void konan_free_impl (void* mem) {
|
extern "C" void* konan_aligned_alloc_impl(size_t alignment, size_t size) {
|
||||||
mi_free(mem);
|
return mi_malloc_aligned(size, alignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void* konan_calloc_impl(size_t num, size_t size) {
|
||||||
|
return mi_calloc(num, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void* konan_aligned_calloc_impl(size_t alignment, size_t num, size_t size) {
|
||||||
|
return mi_calloc_aligned(num, size, alignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void* konan_realloc_impl(void* ptr, size_t size) {
|
||||||
|
return mi_realloc(ptr, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void konan_free_impl(void* ptr) {
|
||||||
|
return mi_free(ptr);
|
||||||
}
|
}
|
||||||
} // extern "C"
|
|
||||||
|
|||||||
@@ -1,23 +1,30 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
* Copyright 2010-2022 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.
|
* that can be found in the LICENSE file.
|
||||||
*/
|
*/
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
extern "C" {
|
#include <cstdlib>
|
||||||
// Memory operations.
|
|
||||||
void* konan_calloc_impl(size_t n_elements, size_t elem_size) {
|
extern "C" void* konan_malloc_impl(size_t size) {
|
||||||
return calloc(n_elements, elem_size);
|
return ::malloc(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* konan_calloc_aligned_impl(size_t count, size_t size, size_t alignment) {
|
extern "C" void* konan_aligned_alloc_impl(size_t alignment, size_t size) {
|
||||||
// alignment is not supported by std alloc - use mimalloc
|
return ::malloc(size);
|
||||||
return calloc(count, size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void konan_free_impl (void* mem) {
|
extern "C" void* konan_calloc_impl(size_t num, size_t size) {
|
||||||
free(mem);
|
return ::calloc(num, size);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" void* konan_aligned_calloc_impl(size_t alignment, size_t num, size_t size) {
|
||||||
|
return ::calloc(num, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void* konan_realloc_impl(void* ptr, size_t size) {
|
||||||
|
return ::realloc(ptr, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void konan_free_impl(void* ptr) {
|
||||||
|
return ::free(ptr);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user