[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 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 `.cpp`/`.mm` only should have extension `.hpp`
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
* 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 unmovable and uncopyable, privately inherit from `kotlin::Pinned`
|
||||
* All heap-allocated classes should publicly inherit from `KonanAllocatorAware`
|
||||
* Use `KStd*` containers and smart pointers instead of `std::*` ones.
|
||||
* Use `std_support::*` containers and smart pointers instead of `std::*` ones. The former ones default to runtime-specific allocator.
|
||||
* 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
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
#include "Utils.hpp"
|
||||
#include "WorkerBoundReference.h"
|
||||
#include "Weak.h"
|
||||
#include "std_support/New.hpp"
|
||||
|
||||
#ifdef KONAN_OBJC_INTEROP
|
||||
#include "ObjCMMAPI.h"
|
||||
@@ -75,6 +76,8 @@
|
||||
#include <algorithm>
|
||||
#endif
|
||||
|
||||
using namespace kotlin;
|
||||
|
||||
namespace {
|
||||
|
||||
ALWAYS_INLINE bool IsStrictMemoryModel() noexcept {
|
||||
@@ -196,7 +199,7 @@ struct CycleDetectorRootset {
|
||||
KStdVector<ScopedRefHolder> heldRefs;
|
||||
};
|
||||
|
||||
class CycleDetector : private kotlin::Pinned, public KonanAllocatorAware {
|
||||
class CycleDetector : private kotlin::Pinned {
|
||||
public:
|
||||
static void insertCandidateIfNeeded(KRef object) {
|
||||
if (canBeACandidate(object))
|
||||
@@ -216,7 +219,7 @@ class CycleDetector : private kotlin::Pinned, public KonanAllocatorAware {
|
||||
|
||||
static CycleDetector& instance() {
|
||||
// Only store a pointer to CycleDetector in .bss
|
||||
static CycleDetector* result = new CycleDetector();
|
||||
static CycleDetector* result = new (std_support::kalloc) CycleDetector();
|
||||
return *result;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,29 +1,32 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* 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.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef RUNTIME_ALLOC_H
|
||||
#define RUNTIME_ALLOC_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <new>
|
||||
#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) {
|
||||
return konan::calloc(1, size);
|
||||
@@ -44,7 +47,7 @@ inline T* konanAllocArray(size_t length) {
|
||||
|
||||
template <typename T, typename ...A>
|
||||
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>
|
||||
@@ -54,113 +57,5 @@ inline T* konanConstructSizedInstance(size_t size, A&& ...args) {
|
||||
|
||||
template <typename T>
|
||||
inline void konanDestructInstance(T* instance) {
|
||||
instance->~T();
|
||||
konanFreeMemory(instance);
|
||||
kotlin::std_support::kdelete(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 "Memory.h"
|
||||
#include "Mutex.hpp"
|
||||
#include "Porting.h"
|
||||
#include "Types.h"
|
||||
#include "Utils.hpp"
|
||||
#include "ObjCExceptions.h"
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
|
||||
#include "ExecFormat.h"
|
||||
#include "Porting.h"
|
||||
#include "Types.h"
|
||||
|
||||
#if USE_ELF_SYMBOLS
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#include <cstdarg>
|
||||
|
||||
#include "cpp_support/Span.hpp"
|
||||
#include "std_support/Span.hpp"
|
||||
|
||||
namespace kotlin {
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "gmock/gmock.h"
|
||||
|
||||
#include "cpp_support/Span.hpp"
|
||||
#include "std_support/Span.hpp"
|
||||
|
||||
using namespace kotlin;
|
||||
|
||||
|
||||
@@ -8,8 +8,9 @@
|
||||
#include <array>
|
||||
#include <cstdarg>
|
||||
|
||||
#include "cpp_support/Span.hpp"
|
||||
#include "std_support/Span.hpp"
|
||||
#include "Format.h"
|
||||
#include "Porting.h"
|
||||
#include "StackTrace.hpp"
|
||||
|
||||
using namespace kotlin;
|
||||
|
||||
@@ -22,6 +22,7 @@ inline constexpr auto nullopt = std::experimental::nullopt;
|
||||
|
||||
#include "Format.h"
|
||||
#include "KAssert.h"
|
||||
#include "Porting.h"
|
||||
|
||||
using namespace kotlin;
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ using string_view = std::experimental::string_view;
|
||||
#endif
|
||||
|
||||
#include "CompilerConstants.hpp"
|
||||
#include "cpp_support/Span.hpp"
|
||||
#include "std_support/Span.hpp"
|
||||
#include "Types.h"
|
||||
|
||||
namespace kotlin {
|
||||
|
||||
@@ -13,18 +13,25 @@
|
||||
#include "Mutex.hpp"
|
||||
#include "Types.h"
|
||||
#include "Utils.hpp"
|
||||
#include "std_support/List.hpp"
|
||||
#include "std_support/Memory.hpp"
|
||||
|
||||
namespace kotlin {
|
||||
|
||||
// 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 {
|
||||
// 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:
|
||||
class Producer;
|
||||
|
||||
// TODO: Consider switching from `KStdList` to `SingleLockList` to hide the constructor
|
||||
// and to not store the iterator.
|
||||
class Node : private Pinned, public KonanAllocatorAware {
|
||||
class Node : private Pinned {
|
||||
public:
|
||||
Node(Producer* owner, const T& value) noexcept : value_(value), owner_(owner) {}
|
||||
|
||||
@@ -43,12 +50,13 @@ public:
|
||||
|
||||
T value_;
|
||||
std::atomic<Producer*> owner_; // `nullptr` signifies that `MultiSourceQueue` owns it.
|
||||
typename KStdList<Node>::iterator position_;
|
||||
typename List<Node>::iterator position_;
|
||||
};
|
||||
|
||||
class Producer {
|
||||
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(); }
|
||||
|
||||
@@ -84,7 +92,9 @@ public:
|
||||
node.owner_ = nullptr;
|
||||
}
|
||||
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_);
|
||||
RuntimeAssert(owner_.deletionQueue_.get_allocator() == deletionQueue_.get_allocator(), "deletionQueue_ allocators must match");
|
||||
owner_.deletionQueue_.splice(owner_.deletionQueue_.end(), deletionQueue_);
|
||||
}
|
||||
|
||||
@@ -95,8 +105,8 @@ public:
|
||||
|
||||
private:
|
||||
MultiSourceQueue& owner_; // weak
|
||||
KStdList<Node> queue_;
|
||||
KStdList<Node*> deletionQueue_;
|
||||
List<Node> queue_;
|
||||
List<Node*> deletionQueue_;
|
||||
};
|
||||
|
||||
class Iterator {
|
||||
@@ -115,9 +125,9 @@ public:
|
||||
private:
|
||||
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 {
|
||||
@@ -134,6 +144,8 @@ public:
|
||||
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,
|
||||
// it'll still be iterated. Use `ApplyDeletions` to remove those elements.
|
||||
Iterable LockForIter() noexcept { return Iterable(*this); }
|
||||
@@ -141,7 +153,7 @@ public:
|
||||
// Lock `MultiSourceQueue` and apply deletions. Only deletes elements that were published.
|
||||
void ApplyDeletions() noexcept {
|
||||
std::lock_guard<Mutex> guard(mutex_);
|
||||
KStdList<Node*> remainingDeletions;
|
||||
List<Node*> remainingDeletions(deletionQueue_.get_allocator());
|
||||
|
||||
auto it = deletionQueue_.begin();
|
||||
while (it != deletionQueue_.end()) {
|
||||
@@ -176,10 +188,8 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
// Using `KStdList` as it allows to implement `Collect` without memory allocations,
|
||||
// which is important for GC mark phase.
|
||||
KStdList<Node> queue_;
|
||||
KStdList<Node*> deletionQueue_;
|
||||
List<Node> queue_;
|
||||
List<Node*> deletionQueue_;
|
||||
Mutex mutex_;
|
||||
};
|
||||
|
||||
|
||||
@@ -10,12 +10,15 @@
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "AllocatorTestSupport.hpp"
|
||||
#include "ScopedThread.hpp"
|
||||
#include "TestSupport.hpp"
|
||||
#include "Types.h"
|
||||
|
||||
using namespace kotlin;
|
||||
|
||||
using ::testing::_;
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename T, typename Mutex>
|
||||
@@ -312,3 +315,42 @@ TEST(MultiSourceQueueTest, ConcurrentPublishAndApplyDeletions) {
|
||||
auto actual = Collect(queue);
|
||||
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 "StackTrace.hpp"
|
||||
#include "Memory.h"
|
||||
#include "Porting.h"
|
||||
#include "Natives.h"
|
||||
#include "Types.h"
|
||||
|
||||
|
||||
@@ -36,6 +36,9 @@
|
||||
#include "CompilerConstants.hpp"
|
||||
#include "Porting.h"
|
||||
#include "KAssert.h"
|
||||
#include "std_support/CStdlib.hpp"
|
||||
|
||||
using namespace kotlin;
|
||||
|
||||
#if KONAN_WASM || KONAN_ZEPHYR
|
||||
extern "C" RUNTIME_NORETURN void Konan_abort(const char*);
|
||||
@@ -205,7 +208,7 @@ static void onThreadExitCallback(void* value) {
|
||||
while (record != nullptr) {
|
||||
record->destructor(record->destructorParameter);
|
||||
auto next = record->next;
|
||||
free(record);
|
||||
std_support::free(record);
|
||||
record = next;
|
||||
}
|
||||
}
|
||||
@@ -242,7 +245,7 @@ void onThreadExit(void (*destructor)(void*), void* destructorParameter) {
|
||||
#else // !KONAN_NO_THREADS
|
||||
// We cannot use pthread_cleanup_push() as it is lexical scope bound.
|
||||
pthread_once(&terminationKeyOnceControl, onThreadExitInit);
|
||||
DestructorRecord* destructorRecord = (DestructorRecord*)calloc(1, sizeof(DestructorRecord));
|
||||
DestructorRecord* destructorRecord = (DestructorRecord*)std_support::calloc(1, sizeof(DestructorRecord));
|
||||
destructorRecord->destructor = destructor;
|
||||
destructorRecord->destructorParameter = destructorParameter;
|
||||
destructorRecord->next =
|
||||
@@ -332,35 +335,6 @@ size_t strnlen(const char* buffer, size_t 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
|
||||
|
||||
#ifdef KONAN_ZEPHYR
|
||||
@@ -406,72 +380,6 @@ uint64_t getTimeMicros() {
|
||||
}
|
||||
#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
|
||||
|
||||
extern "C" {
|
||||
|
||||
@@ -79,11 +79,6 @@ void *memset(void *b, int c, size_t len);
|
||||
#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.
|
||||
uint64_t getTimeMillis();
|
||||
uint64_t getTimeMicros();
|
||||
|
||||
@@ -12,41 +12,36 @@
|
||||
#include <mutex>
|
||||
#include <type_traits>
|
||||
|
||||
#include "Alloc.h"
|
||||
#include "Mutex.hpp"
|
||||
#include "Types.h"
|
||||
#include "Utils.hpp"
|
||||
#include "std_support/Memory.hpp"
|
||||
|
||||
namespace kotlin {
|
||||
|
||||
// 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 {
|
||||
public:
|
||||
class Node;
|
||||
|
||||
private:
|
||||
class NodeDeleter {
|
||||
public:
|
||||
void operator()(Node* node) const { delete node; }
|
||||
};
|
||||
|
||||
using NodeOwner = std::unique_ptr<Node, NodeDeleter>;
|
||||
using NodeAllocator = typename std::allocator_traits<Allocator>::template rebind_alloc<Node>;
|
||||
using NodeOwner = std_support::unique_ptr<Node, std_support::allocator_deleter<Node, NodeAllocator>>;
|
||||
|
||||
public:
|
||||
class Node : private Pinned, public KonanAllocatorAware {
|
||||
// TODO: Maybe just hide `Node` altogether?
|
||||
class Node : private Pinned {
|
||||
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_; }
|
||||
|
||||
private:
|
||||
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_;
|
||||
NodeOwner next_;
|
||||
Node* previous_ = nullptr; // weak
|
||||
@@ -90,6 +85,11 @@ public:
|
||||
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() {
|
||||
AssertCorrectUnsafe();
|
||||
// Make sure not to blow up the stack by nested `~Node` calls.
|
||||
@@ -102,12 +102,12 @@ public:
|
||||
// TODO: Consider making `Emplace` append to `last_`.
|
||||
template <typename... Args>
|
||||
Node* Emplace(Args&&... args) noexcept {
|
||||
auto* nodePtr = new Node(std::forward<Args>(args)...);
|
||||
NodeOwner node(nodePtr);
|
||||
auto node = std_support::allocate_unique<Node>(allocator_, allocator_, std::forward<Args>(args)...);
|
||||
auto* nodePtr = node.get();
|
||||
std::lock_guard<Mutex> guard(mutex_);
|
||||
AssertCorrectUnsafe();
|
||||
if (root_) {
|
||||
root_->previous_ = node.get();
|
||||
root_->previous_ = nodePtr;
|
||||
} else {
|
||||
last_ = nodePtr;
|
||||
}
|
||||
@@ -125,7 +125,8 @@ public:
|
||||
last_ = node->previous_;
|
||||
}
|
||||
if (root_.get() == node) {
|
||||
root_ = std::move(node->next_);
|
||||
auto next = std::move(node->next_);
|
||||
root_ = std::move(next);
|
||||
if (root_) {
|
||||
root_->previous_ = nullptr;
|
||||
}
|
||||
@@ -165,6 +166,7 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
[[no_unique_address]] NodeAllocator allocator_;
|
||||
NodeOwner root_;
|
||||
Node* last_ = nullptr;
|
||||
Mutex mutex_;
|
||||
|
||||
@@ -11,12 +11,15 @@
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "AllocatorTestSupport.hpp"
|
||||
#include "ScopedThread.hpp"
|
||||
#include "TestSupport.hpp"
|
||||
#include "Types.h"
|
||||
|
||||
using namespace kotlin;
|
||||
|
||||
using ::testing::_;
|
||||
|
||||
namespace {
|
||||
|
||||
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
|
||||
#include <cstdarg>
|
||||
#include <cstring>
|
||||
#include "cpp_support/Span.hpp"
|
||||
#include "std_support/Span.hpp"
|
||||
#include "Format.h"
|
||||
|
||||
#if __has_include("dlfcn.h")
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#ifndef RUNTIME_STACK_TRACE_H
|
||||
#define RUNTIME_STACK_TRACE_H
|
||||
|
||||
#include "cpp_support/Span.hpp"
|
||||
#include "std_support/Span.hpp"
|
||||
#include "Memory.h"
|
||||
#include "Types.h"
|
||||
|
||||
|
||||
@@ -19,25 +19,19 @@
|
||||
|
||||
#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 "Common.h"
|
||||
#include "Memory.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.
|
||||
typedef bool KBoolean;
|
||||
@@ -61,36 +55,25 @@ typedef ObjHeader* KRef;
|
||||
typedef const ObjHeader* KConstRef;
|
||||
typedef const ArrayHeader* KString;
|
||||
|
||||
// TODO: Consider moving these into `kotlin::std_support` namespace keeping STL names.
|
||||
|
||||
// Definitions of STL classes used inside Konan runtime.
|
||||
typedef std::basic_string<char, std::char_traits<char>,
|
||||
KonanAllocator<char>> KStdString;
|
||||
template<class Value>
|
||||
using KStdDeque = std::deque<Value, KonanAllocator<Value>>;
|
||||
template<class Key, class Value>
|
||||
using KStdUnorderedMap = std::unordered_map<Key, Value,
|
||||
std::hash<Key>, std::equal_to<Key>,
|
||||
KonanAllocator<std::pair<const Key, Value>>>;
|
||||
template<class Value>
|
||||
using KStdUnorderedSet = std::unordered_set<Value,
|
||||
std::hash<Value>, std::equal_to<Value>,
|
||||
KonanAllocator<Value>>;
|
||||
template<class Value, class Compare = std::less<Value>>
|
||||
using KStdOrderedMultiset = std::multiset<Value, Compare, KonanAllocator<Value>>;
|
||||
template<class Key, class Value, class Compare = std::less<Key>>
|
||||
using KStdOrderedMap = std::map<Key, Value, Compare, KonanAllocator<std::pair<const Key, Value>>>;
|
||||
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)...));
|
||||
}
|
||||
// TODO: Remove these typedefs. Use std_support directly everywhere.
|
||||
using KStdString = kotlin::std_support::string;
|
||||
template <typename Value>
|
||||
using KStdDeque = kotlin::std_support::deque<Value>;
|
||||
template <typename Key, typename Value>
|
||||
using KStdUnorderedMap = kotlin::std_support::unordered_map<Key, Value>;
|
||||
template <typename Value>
|
||||
using KStdUnorderedSet = kotlin::std_support::unordered_set<Value>;
|
||||
template <typename Value, typename Compare = std::less<Value>>
|
||||
using KStdOrderedMultiset = kotlin::std_support::multiset<Value, Compare>;
|
||||
template <typename Key, typename Value>
|
||||
using KStdOrderedMap = kotlin::std_support::map<Key, Value>;
|
||||
template <typename Value>
|
||||
using KStdVector = kotlin::std_support::vector<Value>;
|
||||
template <typename Value>
|
||||
using KStdList = kotlin::std_support::list<Value>;
|
||||
template <typename Value>
|
||||
using KStdUniquePtr = kotlin::std_support::unique_ptr<Value>;
|
||||
using kotlin::std_support::make_unique;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "../Exceptions.h"
|
||||
#include "../KString.h"
|
||||
#include "../Natives.h"
|
||||
#include "../Porting.h"
|
||||
#include "../utf8.h"
|
||||
#include "../KotlinMath.h"
|
||||
#include "../ReturnSlot.h"
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "../Exceptions.h"
|
||||
#include "../KString.h"
|
||||
#include "../Natives.h"
|
||||
#include "../Porting.h"
|
||||
#include "../utf8.h"
|
||||
|
||||
#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"
|
||||
|
||||
// Modelling https://en.cppreference.com/w/cpp/container/span from C++20.
|
||||
|
||||
namespace kotlin {
|
||||
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 {
|
||||
|
||||
// Optional data that's lazily allocated only for objects that need it.
|
||||
class ExtraObjectData : private Pinned, public KonanAllocatorAware {
|
||||
class ExtraObjectData : private Pinned {
|
||||
public:
|
||||
// flags are stored as single atomic uint32, values are bit numbers in that uint32
|
||||
enum Flags : uint32_t {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "FinalizerHooks.hpp"
|
||||
#include "Memory.h"
|
||||
#include "Mutex.hpp"
|
||||
#include "Porting.h"
|
||||
#include "Types.h"
|
||||
#include "Utils.hpp"
|
||||
|
||||
@@ -45,8 +46,7 @@ class ObjectFactoryStorage : private Pinned {
|
||||
using unique_ptr = std::unique_ptr<T, Deleter<T>>;
|
||||
|
||||
public:
|
||||
// This class does not know its size at compile-time. Does not inherit from `KonanAllocatorAware` because
|
||||
// in `KonanAllocatorAware::operator new(size_t size, KonanAllocTag)` `size` would be incorrect.
|
||||
// This class does not know its size at compile-time.
|
||||
class Node : private Pinned {
|
||||
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.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
extern "C" {
|
||||
void* mi_calloc(size_t, size_t);
|
||||
void mi_free(void*);
|
||||
void* mi_malloc(size_t size);
|
||||
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* konan_calloc_impl(size_t n_elements, size_t elem_size) {
|
||||
return mi_calloc(n_elements, elem_size);
|
||||
void* mi_realloc(void* ptr, size_t size);
|
||||
void mi_free(void* ptr);
|
||||
}
|
||||
|
||||
void* konan_calloc_aligned_impl(size_t count, size_t size, size_t alignment) {
|
||||
return mi_calloc_aligned(count, size, alignment);
|
||||
extern "C" void* konan_malloc_impl(size_t size) {
|
||||
return mi_malloc(size);
|
||||
}
|
||||
|
||||
void konan_free_impl (void* mem) {
|
||||
mi_free(mem);
|
||||
extern "C" void* konan_aligned_alloc_impl(size_t alignment, size_t size) {
|
||||
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.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
extern "C" {
|
||||
// Memory operations.
|
||||
void* konan_calloc_impl(size_t n_elements, size_t elem_size) {
|
||||
return calloc(n_elements, elem_size);
|
||||
#include <cstdlib>
|
||||
|
||||
extern "C" void* konan_malloc_impl(size_t size) {
|
||||
return ::malloc(size);
|
||||
}
|
||||
|
||||
void* konan_calloc_aligned_impl(size_t count, size_t size, size_t alignment) {
|
||||
// alignment is not supported by std alloc - use mimalloc
|
||||
return calloc(count, size);
|
||||
extern "C" void* konan_aligned_alloc_impl(size_t alignment, size_t size) {
|
||||
return ::malloc(size);
|
||||
}
|
||||
|
||||
void konan_free_impl (void* mem) {
|
||||
free(mem);
|
||||
}
|
||||
extern "C" void* konan_calloc_impl(size_t num, size_t size) {
|
||||
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