#include "swift/Syntax/SyntaxFactory.h" #include "llvm/ADT/SmallString.h" #include "gtest/gtest.h" #include #include #include using namespace swift; using namespace swift::syntax; static uintptr_t getExpressionFrom(ReturnStmtSyntax Return) { auto Expression = Return.getExpression().getValue(); return reinterpret_cast(Expression.getDataPointer()); } class Pool { static constexpr size_t NumThreads = 2; using FuncTy = std::function; std::vector Workers; std::queue> Tasks; std::mutex QueueLock; std::condition_variable Condition; bool Stop; public: Pool() : Stop(false) { for (size_t i = 0; i < NumThreads; ++i) Workers.emplace_back([this] { while (true) { std::function Task; { std::unique_lock L(QueueLock); Condition.wait(L, [this]{ return Stop || !Tasks.empty(); }); if (Stop && Tasks.empty()) { return; } Task = std::move(Tasks.front()); Tasks.pop(); } Task(); } }); } std::future run(FuncTy Func, ReturnStmtSyntax Return) { auto Task = std::make_shared>( std::bind(Func, Return)); auto Future = Task->get_future(); { std::unique_lock L(QueueLock); Tasks.emplace([Task](){ (*Task)(); }); } Condition.notify_one(); return Future; } ~Pool() { { std::lock_guard L(QueueLock); Stop = true; } Condition.notify_all(); for (auto &Worker : Workers) { Worker.join(); } } }; // Tests that, when multiple threads ask for a child node of the same syntax // node: // - Only one thread inserts the realized child into the parent // - Both threads get the exact same child (by identity) TEST(ThreadSafeCachingTests, ReturnGetExpression) { auto ReturnKW = SyntaxFactory::makeReturnKeyword({}, Trivia::spaces(1)); auto Minus = SyntaxFactory::makePrefixOperator("-", {}, {}); auto One = SyntaxFactory::makeIntegerLiteral("1", {}, {}); auto MinusOne = SyntaxFactory::makePrefixOperatorExpr(Minus, SyntaxFactory::makeIntegerLiteralExpr(One)); Pool P; for (unsigned i = 0; i < 10000; ++i) { auto Return = SyntaxFactory::makeReturnStmt(ReturnKW, MinusOne); auto Future1 = P.run(getExpressionFrom, Return); auto Future2 = P.run(getExpressionFrom, Return); auto FirstDataPointer = Future1.get(); auto SecondDataPointer = Future2.get(); auto DataPointer = reinterpret_cast( Return.getExpression().getValue().getDataPointer()); ASSERT_EQ(FirstDataPointer, SecondDataPointer); ASSERT_EQ(FirstDataPointer, DataPointer); } }