Skip to content

Commit e1c2c04

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 e1c2c04

File tree

3 files changed

+115
-25
lines changed

3 files changed

+115
-25
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: 61 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,18 @@
4646
#include "llvm/ADT/StringRef.h"
4747
#include "llvm/Demangle/Demangle.h"
4848
#include "llvm/Support/Casting.h"
49+
#include "llvm/Support/CommandLine.h"
4950
#include "llvm/Support/Debug.h"
5051
#include "llvm/Support/raw_os_ostream.h"
5152

5253
#include <algorithm>
54+
#include <cassert>
5355
#include <iterator>
5456
#include <map>
57+
#include <memory>
5558
#include <set>
5659
#include <sstream>
60+
#include <stack>
5761
#include <string>
5862

5963
// Stream redirect.
@@ -71,30 +75,29 @@
7175
#include <unistd.h>
7276
#endif // WIN32
7377

74-
#include <stack>
75-
7678
namespace Cpp {
7779

7880
using namespace clang;
7981
using namespace llvm;
8082
using namespace std;
8183

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;
84+
struct InterpreterInfo {
85+
compat::Interpreter* Interpreter = nullptr;
86+
bool isOwned = true;
87+
88+
// Valgrind complains about __cxa_pure_virtual called when deleting
89+
// llvm::SectionMemoryManager::~SectionMemoryManager as part of the dtor
90+
// chain of the Interpreter.
91+
// This might fix the issue https://reviews.llvm.org/D107087
92+
// FIXME: For now we just leak the Interpreter.
93+
~InterpreterInfo() = default;
94+
};
95+
static llvm::SmallVector<InterpreterInfo, 8> sInterpreters;
9396

9497
static compat::Interpreter& getInterp() {
95-
assert(sInterpreter &&
98+
assert(!sInterpreters.empty() &&
9699
"Interpreter instance must be set before calling this!");
97-
return *sInterpreter;
100+
return *sInterpreters.back().Interpreter;
98101
}
99102
static clang::Sema& getSema() { return getInterp().getCI()->getSema(); }
100103
static clang::ASTContext& getASTContext() { return getSema().getASTContext(); }
@@ -2920,19 +2923,54 @@ TInterp_t CreateInterpreter(const std::vector<const char*>& Args /*={}*/,
29202923
Args[NumArgs + 1] = nullptr;
29212924
llvm::cl::ParseCommandLineOptions(NumArgs + 1, Args.get());
29222925
}
2923-
// FIXME: Enable this assert once we figure out how to fix the multiple
2924-
// calls to CreateInterpreter.
2925-
// assert(!sInterpreter && "Interpreter already set.");
2926-
sInterpreter = I;
2926+
2927+
sInterpreters.push_back({I, /*isOwned=*/true});
2928+
29272929
return I;
29282930
}
29292931

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

29322970
void UseExternalInterpreter(TInterp_t I) {
2933-
assert(!sInterpreter && "sInterpreter already in use!");
2934-
sInterpreter = static_cast<compat::Interpreter*>(I);
2935-
OwningSInterpreter = false;
2971+
assert(sInterpreters.empty() && "sInterpreter already in use!");
2972+
sInterpreters.push_back(
2973+
{static_cast<compat::Interpreter*>(I), /*isOwned=*/false});
29362974
}
29372975

29382976
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*>(static_cast<std::uintptr_t>(~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*>(static_cast<std::uintptr_t>(~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)