Skip to content

Commit 9c4439e

Browse files
committed
Stack the created interpreters.
This patch offers an API to switch back and forth different interpreters which can come at handy in several occasions including research into implementing threading. Fixes #302.
1 parent de1e51b commit 9c4439e

File tree

3 files changed

+107
-23
lines changed

3 files changed

+107
-23
lines changed

include/CppInterOp/CppInterOp.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -595,15 +595,26 @@ CPPINTEROP_API void GetOperator(TCppScope_t scope, Operator op,
595595
std::vector<TCppFunction_t>& operators,
596596
OperatorArity kind = kBoth);
597597

598-
/// Creates an instance of the interpreter we need for the various interop
599-
/// services.
598+
/// Creates an owned instance of the interpreter we need for the various interop
599+
/// services and pushes it onto a stack.
600600
///\param[in] Args - the list of arguments for interpreter constructor.
601601
///\param[in] CPPINTEROP_EXTRA_INTERPRETER_ARGS - an env variable, if defined,
602602
/// adds additional arguments to the interpreter.
603+
///\returns nullptr on failure.
603604
CPPINTEROP_API TInterp_t
604605
CreateInterpreter(const std::vector<const char*>& Args = {},
605606
const std::vector<const char*>& GpuArgs = {});
606607

608+
/// Deletes an instance of an interpreter.
609+
///\param[in] I - the interpreter to be deleted, if nullptr, deletes the last.
610+
///\returns false on failure or if \c I is not tracked in the stack.
611+
CPPINTEROP_API bool DeleteInterpreter(TInterp_t I = nullptr);
612+
613+
/// Activates an instance of an interpreter to handle subsequent API requests
614+
///\param[in] I - the interpreter to be activated.
615+
///\returns false on failure.
616+
CPPINTEROP_API bool ActivateInterpreter(TInterp_t I);
617+
607618
/// Checks which Interpreter backend was CppInterOp library built with (Cling,
608619
/// Clang-REPL, etcetera). In practice, the selected interpreter should not
609620
/// matter, since the library will function in the same way.

lib/CppInterOp/CppInterOp.cpp

Lines changed: 53 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -71,30 +71,29 @@
7171
#include <unistd.h>
7272
#endif // WIN32
7373

74-
#include <stack>
75-
7674
namespace Cpp {
7775

7876
using namespace clang;
7977
using namespace llvm;
8078
using namespace std;
8179

82-
// Flag to indicate ownership when an external interpreter instance is used.
83-
static bool OwningSInterpreter = true;
84-
static compat::Interpreter* sInterpreter = nullptr;
85-
// Valgrind complains about __cxa_pure_virtual called when deleting
86-
// llvm::SectionMemoryManager::~SectionMemoryManager as part of the dtor chain
87-
// of the Interpreter.
88-
// This might fix the issue https://reviews.llvm.org/D107087
89-
// FIXME: For now we just leak the Interpreter.
90-
struct InterpDeleter {
91-
~InterpDeleter() = default;
92-
} Deleter;
80+
struct InterpreterInfo {
81+
compat::Interpreter* Interpreter = nullptr;
82+
bool isOwned = true;
83+
84+
// Valgrind complains about __cxa_pure_virtual called when deleting
85+
// llvm::SectionMemoryManager::~SectionMemoryManager as part of the dtor
86+
// chain of the Interpreter.
87+
// This might fix the issue https://reviews.llvm.org/D107087
88+
// FIXME: For now we just leak the Interpreter.
89+
~InterpreterInfo() {}
90+
};
91+
static llvm::SmallVector<InterpreterInfo, 8> sInterpreters;
9392

9493
static compat::Interpreter& getInterp() {
95-
assert(sInterpreter &&
94+
assert(!sInterpreters.empty() &&
9695
"Interpreter instance must be set before calling this!");
97-
return *sInterpreter;
96+
return *sInterpreters.back().Interpreter;
9897
}
9998
static clang::Sema& getSema() { return getInterp().getCI()->getSema(); }
10099
static clang::ASTContext& getASTContext() { return getSema().getASTContext(); }
@@ -2922,17 +2921,50 @@ TInterp_t CreateInterpreter(const std::vector<const char*>& Args /*={}*/,
29222921
}
29232922
// FIXME: Enable this assert once we figure out how to fix the multiple
29242923
// calls to CreateInterpreter.
2925-
// assert(!sInterpreter && "Interpreter already set.");
2926-
sInterpreter = I;
2924+
// assert(sInterpreters.empty() && "Interpreter already set.");
2925+
sInterpreters.push_back({I, /*isOwned=*/true});
2926+
29272927
return I;
29282928
}
29292929

2930-
TInterp_t GetInterpreter() { return sInterpreter; }
2930+
bool DeleteInterpreter(TInterp_t I /*=nullptr*/) {
2931+
if (!I) {
2932+
sInterpreters.pop_back();
2933+
return true;
2934+
}
2935+
2936+
auto found =
2937+
std::find_if(sInterpreters.begin(), sInterpreters.end(),
2938+
[&I](const auto& Info) { return Info.Interpreter == I; });
2939+
if (found == sInterpreters.end())
2940+
return false; // failure
2941+
2942+
sInterpreters.erase(found);
2943+
return true;
2944+
}
2945+
2946+
bool ActivateInterpreter(TInterp_t I) {
2947+
if (!I)
2948+
return false;
2949+
2950+
auto found =
2951+
std::find_if(sInterpreters.begin(), sInterpreters.end(),
2952+
[&I](const auto& Info) { return Info.Interpreter == I; });
2953+
if (found == sInterpreters.end())
2954+
return false;
2955+
2956+
if (std::next(found) != sInterpreters.end()) // if not already last element.
2957+
std::rotate(found, found + 1, sInterpreters.end());
2958+
2959+
return true; // success
2960+
}
2961+
2962+
TInterp_t GetInterpreter() { return sInterpreters.back().Interpreter; }
29312963

29322964
void UseExternalInterpreter(TInterp_t I) {
2933-
assert(!sInterpreter && "sInterpreter already in use!");
2934-
sInterpreter = static_cast<compat::Interpreter*>(I);
2935-
OwningSInterpreter = false;
2965+
assert(sInterpreters.empty() && "sInterpreter already in use!");
2966+
sInterpreters.push_back(
2967+
{static_cast<compat::Interpreter*>(I), /*isOwned=*/false});
29362968
}
29372969

29382970
void AddSearchPath(const char* dir, bool isUser, bool prepend) {

unittests/CppInterOp/InterpreterTest.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,47 @@ TEST(InterpreterTest, Evaluate) {
8080
EXPECT_FALSE(HadError) ;
8181
}
8282

83+
TEST(InterpreterTest, DeleteInterpreter) {
84+
auto* I1 = Cpp::CreateInterpreter();
85+
auto* I2 = Cpp::CreateInterpreter();
86+
auto* I3 = Cpp::CreateInterpreter();
87+
EXPECT_TRUE(I1 && I2 && I3) << "Failed to create interpreters";
88+
89+
EXPECT_EQ(I3, Cpp::GetInterpreter()) << "I3 is not active";
90+
91+
EXPECT_TRUE(Cpp::DeleteInterpreter(nullptr));
92+
EXPECT_EQ(I2, Cpp::GetInterpreter());
93+
94+
auto* I4 = reinterpret_cast<void*>(~0U);
95+
EXPECT_FALSE(Cpp::DeleteInterpreter(I4));
96+
97+
EXPECT_TRUE(Cpp::DeleteInterpreter(I1));
98+
EXPECT_EQ(I2, Cpp::GetInterpreter()) << "I2 is not active";
99+
}
100+
101+
TEST(InterpreterTest, ActivateInterpreter) {
102+
EXPECT_FALSE(Cpp::ActivateInterpreter(nullptr));
103+
auto* Cpp11 = Cpp::CreateInterpreter({"-std=c++11"});
104+
auto* Cpp14 = Cpp::CreateInterpreter({"-std=c++14"});
105+
auto* Cpp17 = Cpp::CreateInterpreter({"-std=c++17"});
106+
107+
EXPECT_TRUE(Cpp11 && Cpp14 && Cpp17);
108+
EXPECT_TRUE(Cpp::Evaluate("__cplusplus") == 201703L)
109+
<< "Failed to activate C++17";
110+
111+
auto* UntrackedI = reinterpret_cast<void*>(~0U);
112+
EXPECT_FALSE(Cpp::ActivateInterpreter(UntrackedI));
113+
114+
EXPECT_TRUE(Cpp::ActivateInterpreter(Cpp11));
115+
EXPECT_TRUE(Cpp::Evaluate("__cplusplus") == 201103L);
116+
117+
Cpp::DeleteInterpreter(Cpp11);
118+
EXPECT_EQ(Cpp::GetInterpreter(), Cpp17);
119+
120+
EXPECT_TRUE(Cpp::ActivateInterpreter(Cpp14));
121+
EXPECT_TRUE(Cpp::Evaluate("__cplusplus") == 201402L);
122+
}
123+
83124
TEST(InterpreterTest, Process) {
84125
#ifdef _WIN32
85126
GTEST_SKIP() << "Disabled on Windows. Needs fixing.";

0 commit comments

Comments
 (0)