Use Alloc.h everywhere (#4611)

This commit is contained in:
Alexander Shabalin
2021-01-13 14:37:41 +03:00
committed by Nikolay Krasko
parent 37bcd0894f
commit d57802633c
20 changed files with 403 additions and 129 deletions
@@ -119,4 +119,42 @@ bool operator!=(
return !(x == y);
}
template <class T>
class KonanDeleter {
public:
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
@@ -0,0 +1,133 @@
/*
* 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 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 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();
testing::Mock::VerifyAndClearExpectations(destructorHook.get());
}
TEST_F(KonanAllocatorAwareTest, PlacementConstructedArray) {
constexpr size_t kCount = 5;
std::array<uint8_t, sizeof(A) * kCount> 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();
}
testing::Mock::VerifyAndClearExpectations(destructorHook.get());
}
@@ -6,7 +6,6 @@
#include "Cleaner.h"
#include <future>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
@@ -14,6 +13,7 @@
#include "Atomic.h"
#include "TestSupport.hpp"
#include "TestSupportCompilerGenerated.hpp"
#include "Types.h"
using testing::_;
@@ -31,7 +31,7 @@ TEST(CleanerTest, ConcurrentCreation) {
int startedThreads = 0;
bool allowRunning = false;
std::vector<std::future<KInt>> futures;
KStdVector<std::future<KInt>> futures;
for (int i = 0; i < threadCount; ++i) {
auto future = std::async(std::launch::async, [&startedThreads, &allowRunning]() {
atomicAdd(&startedThreads, 1);
@@ -44,7 +44,7 @@ TEST(CleanerTest, ConcurrentCreation) {
while (atomicGet(&startedThreads) != threadCount) {
}
atomicSet(&allowRunning, true);
std::vector<KInt> values;
KStdVector<KInt> values;
for (auto& future : futures) {
values.push_back(future.get());
}
@@ -7,7 +7,6 @@
#define RUNTIME_CPP_SUPPORT_H
#include <type_traits>
#include <memory>
// A collection of backported utilities from future C++ versions.
@@ -16,11 +15,6 @@ namespace std_support {
////////////////////////// C++14 //////////////////////////
template <typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
template <typename T>
using make_unsigned_t = typename std::make_unsigned<T>::type;
@@ -11,6 +11,7 @@
#include <mutex>
#include "Mutex.hpp"
#include "Types.h"
namespace kotlin {
@@ -20,9 +21,9 @@ class MultiSourceQueue {
public:
class Producer;
// TODO: Consider switching from `std::list` to `SingleLockList` to hide the constructor
// TODO: Consider switching from `KStdList` to `SingleLockList` to hide the constructor
// and to not store the iterator.
class Node : private Pinned {
class Node : private Pinned, public KonanAllocatorAware {
public:
Node(const T& value, Producer* owner) noexcept : value_(value), owner_(owner) {}
@@ -33,7 +34,7 @@ public:
T value_;
std::atomic<Producer*> owner_; // `nullptr` signifies that `MultiSourceQueue` owns it.
typename std::list<Node>::iterator position_;
typename KStdList<Node>::iterator position_;
};
class Producer {
@@ -72,8 +73,8 @@ public:
private:
MultiSourceQueue& owner_; // weak
std::list<Node> queue_;
std::list<Node*> deletionQueue_;
KStdList<Node> queue_;
KStdList<Node*> deletionQueue_;
};
class Iterator {
@@ -92,9 +93,9 @@ public:
private:
friend class MultiSourceQueue;
explicit Iterator(const typename std::list<Node>::iterator& position) noexcept : position_(position) {}
explicit Iterator(const typename KStdList<Node>::iterator& position) noexcept : position_(position) {}
typename std::list<Node>::iterator position_;
typename KStdList<Node>::iterator position_;
};
class Iterable : MoveOnly {
@@ -118,7 +119,7 @@ public:
// Lock `MultiSourceQueue` and apply deletions. Only deletes elements that were published.
void ApplyDeletions() noexcept {
std::lock_guard<SpinLock> guard(mutex_);
std::list<Node*> remainingDeletions;
KStdList<Node*> remainingDeletions;
auto it = deletionQueue_.begin();
while (it != deletionQueue_.end()) {
@@ -139,10 +140,10 @@ public:
}
private:
// Using `std::list` as it allows to implement `Collect` without memory allocations,
// Using `KStdList` as it allows to implement `Collect` without memory allocations,
// which is important for GC mark phase.
std::list<Node> queue_;
std::list<Node*> deletionQueue_;
KStdList<Node> queue_;
KStdList<Node*> deletionQueue_;
SpinLock mutex_;
};
@@ -12,14 +12,15 @@
#include "gtest/gtest.h"
#include "TestSupport.hpp"
#include "Types.h"
using namespace kotlin;
namespace {
template <typename T>
std::vector<T> Collect(MultiSourceQueue<T>& queue) {
std::vector<T> result;
KStdVector<T> Collect(MultiSourceQueue<T>& queue) {
KStdVector<T> result;
for (const auto& element : queue.Iter()) {
result.push_back(element);
}
@@ -192,8 +193,8 @@ TEST(MultiSourceQueueTest, ConcurrentPublish) {
constexpr int kThreadCount = kDefaultThreadCount;
std::atomic<bool> canStart(false);
std::atomic<int> readyCount(0);
std::vector<std::thread> threads;
std::vector<int> expected;
KStdVector<std::thread> threads;
KStdVector<int> expected;
for (int i = 0; i < kThreadCount; ++i) {
expected.push_back(i);
@@ -223,8 +224,8 @@ TEST(MultiSourceQueueTest, IterWhileConcurrentPublish) {
constexpr int kStartCount = 50;
constexpr int kThreadCount = kDefaultThreadCount;
std::vector<int> expectedBefore;
std::vector<int> expectedAfter;
KStdVector<int> expectedBefore;
KStdVector<int> expectedAfter;
IntQueue::Producer producer(queue);
for (int i = 0; i < kStartCount; ++i) {
expectedBefore.push_back(i);
@@ -236,7 +237,7 @@ TEST(MultiSourceQueueTest, IterWhileConcurrentPublish) {
std::atomic<bool> canStart(false);
std::atomic<int> readyCount(0);
std::atomic<int> startedCount(0);
std::vector<std::thread> threads;
KStdVector<std::thread> threads;
for (int i = 0; i < kThreadCount; ++i) {
int j = i + kStartCount;
expectedAfter.push_back(j);
@@ -251,7 +252,7 @@ TEST(MultiSourceQueueTest, IterWhileConcurrentPublish) {
});
}
std::vector<int> actualBefore;
KStdVector<int> actualBefore;
{
auto iter = queue.Iter();
while (readyCount < kThreadCount) {
@@ -282,7 +283,7 @@ TEST(MultiSourceQueueTest, ConcurrentPublishAndApplyDeletions) {
std::atomic<bool> canStart(false);
std::atomic<int> readyCount(0);
std::atomic<int> startedCount(0);
std::vector<std::thread> threads;
KStdVector<std::thread> threads;
for (int i = 0; i < kThreadCount; ++i) {
threads.emplace_back([&queue, i, &canStart, &readyCount, &startedCount]() {
IntQueue::Producer producer(queue);
@@ -10,8 +10,9 @@
#include <memory>
#include <mutex>
#include "CppSupport.hpp"
#include "Alloc.h"
#include "Mutex.hpp"
#include "Types.h"
#include "Utils.hpp"
namespace kotlin {
@@ -20,29 +21,43 @@ namespace kotlin {
template <typename Value, typename Mutex = SpinLock>
class SingleLockList : private Pinned {
public:
class Node : Pinned {
class Node;
private:
class NodeDeleter {
public:
Value* Get() noexcept { return &value; }
void operator()(Node* node) const { delete node; }
};
using NodeOwner = std::unique_ptr<Node, NodeDeleter>;
public:
class Node : private Pinned, public KonanAllocatorAware {
public:
Value* Get() noexcept { return &value_; }
private:
friend class SingleLockList;
template <typename... Args>
Node(Args... args) noexcept : value(args...) {}
Node(Args&&... args) noexcept : value_(std::forward<Args>(args)...) {}
Value value;
std::unique_ptr<Node> next;
Node* previous = nullptr; // weak
// Make sure `Node` can only be deleted by `SingleLockList` itself.
~Node() = default;
Value value_;
NodeOwner next_;
Node* previous_ = nullptr; // weak
};
class Iterator {
public:
explicit Iterator(Node* node) noexcept : node_(node) {}
Value& operator*() noexcept { return node_->value; }
Value& operator*() noexcept { return node_->value_; }
Iterator& operator++() noexcept {
node_ = node_->next.get();
node_ = node_->next_.get();
return *this;
}
@@ -67,36 +82,56 @@ public:
std::unique_lock<Mutex> guard_;
};
template <typename... Args>
Node* Emplace(Args... args) noexcept {
auto* nodePtr = new Node(args...);
std::unique_ptr<Node> node(nodePtr);
std::lock_guard<Mutex> guard(mutex_);
if (root_) {
root_->previous = node.get();
~SingleLockList() {
AssertCorrectUnsafe();
// Make sure not to blow up the stack by nested `~Node` calls.
for (auto node = std::move(root_); node != nullptr; node = std::move(node->next_)) {
}
node->next = std::move(root_);
last_ = nullptr;
AssertCorrectUnsafe();
}
// 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);
std::lock_guard<Mutex> guard(mutex_);
AssertCorrectUnsafe();
if (root_) {
root_->previous_ = node.get();
} else {
last_ = nodePtr;
}
node->next_ = std::move(root_);
root_ = std::move(node);
AssertCorrectUnsafe();
return nodePtr;
}
// Using `node` including its referred `Value` after `Erase` is undefined behaviour.
void Erase(Node* node) noexcept {
std::lock_guard<Mutex> guard(mutex_);
AssertCorrectUnsafe();
if (last_ == node) {
last_ = node->previous_;
}
if (root_.get() == node) {
root_ = std::move(node->next);
root_ = std::move(node->next_);
if (root_) {
root_->previous = nullptr;
root_->previous_ = nullptr;
}
AssertCorrectUnsafe();
return;
}
auto* previous = node->previous;
auto* previous = node->previous_;
RuntimeAssert(previous != nullptr, "Only the root node doesn't have the previous node");
auto ownedNode = std::move(previous->next);
previous->next = std::move(node->next);
if (auto& next = previous->next) {
next->previous = previous;
auto ownedNode = std::move(previous->next_);
previous->next_ = std::move(node->next_);
if (auto& next = previous->next_) {
next->previous_ = previous;
}
AssertCorrectUnsafe();
}
// Returned value locks `this` to perform safe iteration. `this` unlocks when
@@ -109,7 +144,19 @@ public:
Iterable Iter() noexcept { return Iterable(this); }
private:
std::unique_ptr<Node> root_;
// Expects `mutex_` to be held by the current thread.
ALWAYS_INLINE void AssertCorrectUnsafe() const noexcept {
if (root_ == nullptr) {
RuntimeAssert(last_ == nullptr, "last_ must be null");
} else {
RuntimeAssert(root_->previous_ == nullptr, "root_ must not have previous_");
RuntimeAssert(last_ != nullptr, "last_ must not be null");
RuntimeAssert(last_->next_ == nullptr, "last_ must not have next_");
}
}
NodeOwner root_;
Node* last_ = nullptr;
Mutex mutex_;
};
@@ -6,13 +6,14 @@
#include "SingleLockList.hpp"
#include <atomic>
#include <deque>
#include <functional>
#include <thread>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "TestSupport.hpp"
#include "Types.h"
using namespace kotlin;
@@ -47,7 +48,7 @@ TEST(SingleLockListTest, EmplaceAndIter) {
list.Emplace(kSecond);
list.Emplace(kThird);
std::vector<int> actual;
KStdVector<int> actual;
for (int element : list.Iter()) {
actual.push_back(element);
}
@@ -65,7 +66,7 @@ TEST(SingleLockListTest, EmplaceEraseAndIter) {
list.Emplace(kThird);
list.Erase(secondNode);
std::vector<int> actual;
KStdVector<int> actual;
for (int element : list.Iter()) {
actual.push_back(element);
}
@@ -76,7 +77,7 @@ TEST(SingleLockListTest, EmplaceEraseAndIter) {
TEST(SingleLockListTest, IterEmpty) {
IntList list;
std::vector<int> actual;
KStdVector<int> actual;
for (int element : list.Iter()) {
actual.push_back(element);
}
@@ -97,7 +98,7 @@ TEST(SingleLockListTest, EraseToEmptyEmplaceAndIter) {
list.Emplace(kThird);
list.Emplace(kFourth);
std::vector<int> actual;
KStdVector<int> actual;
for (int element : list.Iter()) {
actual.push_back(element);
}
@@ -110,8 +111,8 @@ TEST(SingleLockListTest, ConcurrentEmplace) {
constexpr int kThreadCount = kDefaultThreadCount;
std::atomic<bool> canStart(false);
std::atomic<int> readyCount(0);
std::vector<std::thread> threads;
std::vector<int> expected;
KStdVector<std::thread> threads;
KStdVector<int> expected;
for (int i = 0; i < kThreadCount; ++i) {
expected.push_back(i);
threads.emplace_back([i, &list, &canStart, &readyCount]() {
@@ -129,7 +130,7 @@ TEST(SingleLockListTest, ConcurrentEmplace) {
t.join();
}
std::vector<int> actual;
KStdVector<int> actual;
for (int element : list.Iter()) {
actual.push_back(element);
}
@@ -140,14 +141,14 @@ TEST(SingleLockListTest, ConcurrentEmplace) {
TEST(SingleLockListTest, ConcurrentErase) {
IntList list;
constexpr int kThreadCount = kDefaultThreadCount;
std::vector<IntList::Node*> items;
KStdVector<IntList::Node*> items;
for (int i = 0; i < kThreadCount; ++i) {
items.push_back(list.Emplace(i));
}
std::atomic<bool> canStart(false);
std::atomic<int> readyCount(0);
std::vector<std::thread> threads;
KStdVector<std::thread> threads;
for (auto* item : items) {
threads.emplace_back([item, &list, &canStart, &readyCount]() {
++readyCount;
@@ -164,7 +165,7 @@ TEST(SingleLockListTest, ConcurrentErase) {
t.join();
}
std::vector<int> actual;
KStdVector<int> actual;
for (int element : list.Iter()) {
actual.push_back(element);
}
@@ -177,8 +178,8 @@ TEST(SingleLockListTest, IterWhileConcurrentEmplace) {
constexpr int kStartCount = 50;
constexpr int kThreadCount = kDefaultThreadCount;
std::deque<int> expectedBefore;
std::vector<int> expectedAfter;
KStdDeque<int> expectedBefore;
KStdVector<int> expectedAfter;
for (int i = 0; i < kStartCount; ++i) {
expectedBefore.push_front(i);
expectedAfter.push_back(i);
@@ -187,7 +188,7 @@ TEST(SingleLockListTest, IterWhileConcurrentEmplace) {
std::atomic<bool> canStart(false);
std::atomic<int> startedCount(0);
std::vector<std::thread> threads;
KStdVector<std::thread> threads;
for (int i = 0; i < kThreadCount; ++i) {
int j = i + kStartCount;
expectedAfter.push_back(j);
@@ -199,7 +200,7 @@ TEST(SingleLockListTest, IterWhileConcurrentEmplace) {
});
}
std::vector<int> actualBefore;
KStdVector<int> actualBefore;
{
auto iter = list.Iter();
canStart = true;
@@ -217,7 +218,7 @@ TEST(SingleLockListTest, IterWhileConcurrentEmplace) {
EXPECT_THAT(actualBefore, testing::ElementsAreArray(expectedBefore));
std::vector<int> actualAfter;
KStdVector<int> actualAfter;
for (int element : list.Iter()) {
actualAfter.push_back(element);
}
@@ -229,8 +230,8 @@ TEST(SingleLockListTest, IterWhileConcurrentErase) {
IntList list;
constexpr int kThreadCount = kDefaultThreadCount;
std::deque<int> expectedBefore;
std::vector<IntList::Node*> items;
KStdDeque<int> expectedBefore;
KStdVector<IntList::Node*> items;
for (int i = 0; i < kThreadCount; ++i) {
expectedBefore.push_front(i);
items.push_back(list.Emplace(i));
@@ -238,7 +239,7 @@ TEST(SingleLockListTest, IterWhileConcurrentErase) {
std::atomic<bool> canStart(false);
std::atomic<int> startedCount(0);
std::vector<std::thread> threads;
KStdVector<std::thread> threads;
for (auto* item : items) {
threads.emplace_back([item, &list, &canStart, &startedCount]() {
while (!canStart) {
@@ -248,7 +249,7 @@ TEST(SingleLockListTest, IterWhileConcurrentErase) {
});
}
std::vector<int> actualBefore;
KStdVector<int> actualBefore;
{
auto iter = list.Iter();
canStart = true;
@@ -266,7 +267,7 @@ TEST(SingleLockListTest, IterWhileConcurrentErase) {
EXPECT_THAT(actualBefore, testing::ElementsAreArray(expectedBefore));
std::vector<int> actualAfter;
KStdVector<int> actualAfter;
for (int element : list.Iter()) {
actualAfter.push_back(element);
}
@@ -298,10 +299,48 @@ TEST(SingleLockListTest, PinnedType) {
list.Erase(itemNode);
std::vector<PinnedType*> actualAfter;
KStdVector<PinnedType*> actualAfter;
for (auto& element : list.Iter()) {
actualAfter.push_back(&element);
}
EXPECT_THAT(actualAfter, testing::IsEmpty());
}
namespace {
class WithDestructorHook;
using DestructorHook = void(WithDestructorHook*);
class WithDestructorHook : private Pinned {
public:
explicit WithDestructorHook(std::function<DestructorHook> hook) : hook_(std::move(hook)) {}
~WithDestructorHook() { hook_(this); }
private:
std::function<DestructorHook> hook_;
};
} // namespace
TEST(SingleLockListTest, Destructor) {
testing::StrictMock<testing::MockFunction<DestructorHook>> hook;
{
SingleLockList<WithDestructorHook> list;
auto* first = list.Emplace(hook.AsStdFunction())->Get();
auto* second = list.Emplace(hook.AsStdFunction())->Get();
auto* third = list.Emplace(hook.AsStdFunction())->Get();
{
testing::InSequence seq;
// `list` is `third`->`second`->`first`. If destruction
// were to cause recursion, the order of destructors
// would've been backwards.
EXPECT_CALL(hook, Call(third));
EXPECT_CALL(hook, Call(second));
EXPECT_CALL(hook, Call(first));
}
}
testing::Mock::VerifyAndClear(&hook);
}
@@ -9,7 +9,6 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "CppSupport.hpp"
#include "Types.h"
#include "Utils.hpp"
@@ -21,7 +20,7 @@ public:
explicit ScopedStrictMockFunction(Mock** globalMockLocation) : globalMockLocation_(globalMockLocation) {
RuntimeCheck(globalMockLocation != nullptr, "ScopedStrictMockFunction needs non-null global mock location");
RuntimeCheck(*globalMockLocation == nullptr, "ScopedStrictMockFunction needs null global mock");
mock_ = kotlin::std_support::make_unique<Mock>();
mock_ = make_unique<Mock>();
*globalMockLocation_ = mock_.get();
}
@@ -57,7 +56,7 @@ public:
private:
// Can be null if moved-out of.
Mock** globalMockLocation_;
std::unique_ptr<Mock> mock_;
KStdUniquePtr<Mock> mock_;
};
ScopedStrictMockFunction<KInt()> ScopedCreateCleanerWorkerMock();
@@ -27,6 +27,7 @@
#include <deque>
#include <list>
#include <map>
#include <memory>
#include <string>
#include <set>
#include <unordered_map>
@@ -60,6 +61,8 @@ 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;
@@ -81,6 +84,13 @@ 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
extern "C" {
@@ -476,7 +476,7 @@ class State {
template <typename F>
void waitNativeWorkersTerminationUnlocked(bool checkLeaks, F waitForWorker) {
std::vector<std::pair<KInt, pthread_t>> workersToWait;
KStdVector<std::pair<KInt, pthread_t>> workersToWait;
{
Locker locker(&lock_);