Skip to content

Commit db4cfca

Browse files
authored
Merge pull request #80 from ix-dcourtois/fix/threadsafe_interpreter
Make Interpreter's evaluations thread safe
2 parents 9e2e4f1 + 0479862 commit db4cfca

File tree

4 files changed

+57
-13
lines changed

4 files changed

+57
-13
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,7 @@
88
CMakeCache.txt
99
src/SeExpr/parser
1010
src/SeExprEditor/generated
11+
.vs
12+
.vscode
13+
CMakeLists.txt.user
14+
CMakeSettings.json

src/SeExpr/Expression.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ const double* Expression::evalFP(VarBlock* varBlock) const {
306306
if (_isValid) {
307307
if (_evaluationStrategy == UseInterpreter) {
308308
_interpreter->eval(varBlock);
309-
return &_interpreter->d[_returnSlot];
309+
return (varBlock && varBlock->threadSafe) ? &(varBlock->d[_returnSlot]) : &_interpreter->d[_returnSlot];
310310
} else { // useLLVM
311311
return _llvmEvaluator->evalFP(varBlock);
312312
}
@@ -341,7 +341,7 @@ const char* Expression::evalStr(VarBlock* varBlock) const {
341341
if (_isValid) {
342342
if (_evaluationStrategy == UseInterpreter) {
343343
_interpreter->eval(varBlock);
344-
return _interpreter->s[_returnSlot];
344+
return (varBlock && varBlock->threadSafe) ? varBlock->s[_returnSlot] : _interpreter->s[_returnSlot];
345345
} else { // useLLVM
346346
return _llvmEvaluator->evalStr(varBlock);
347347
}

src/SeExpr/Interpreter.cpp

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,30 @@
2929
namespace SeExpr2 {
3030

3131
void Interpreter::eval(VarBlock* block, bool debug) {
32+
// get pointers to the working data
33+
double* fp = d.data();
34+
char** str = s.data();
35+
36+
// if we have a VarBlock instance, we need to update the working data
3237
if (block) {
33-
static_assert(sizeof(char*) == sizeof(size_t), "Expect to fit size_t in char*");
34-
s[0] = reinterpret_cast<char*>(block->data());
35-
s[1] = reinterpret_cast<char*>(block->indirectIndex);
38+
// if the VarBlock is flagged as thread safe, copy the interpreter's data to it.
39+
if (block->threadSafe == true) {
40+
// copy double data
41+
block->d.resize(d.size());
42+
fp = block->d.data();
43+
memcpy(fp, d.data(), d.size() * sizeof(double));
44+
45+
// copy string data
46+
block->s.resize(s.size());
47+
str = block->s.data();
48+
memcpy(str, s.data(), s.size() * sizeof(char*));
49+
}
50+
51+
// set the variable evaluation data
52+
str[0] = reinterpret_cast<char*>(block->data());
53+
str[1] = reinterpret_cast<char*>(block->indirectIndex);
3654
}
37-
double* fp = &d[0];
38-
char** str = &s[0];
55+
3956
int pc = _pcStart;
4057
int end = ops.size();
4158
while (pc < end) {

src/SeExpr/VarBlock.h

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,22 +26,26 @@ namespace SeExpr2 {
2626
class ExprNode;
2727
class ExprVarNode;
2828
class ExprFunc;
29-
class Expression;
30-
class Interpreter;
3129

3230
class VarBlockCreator;
3331

3432
/// A thread local evaluation context. Just allocate and fill in with data.
3533
class VarBlock {
3634
private:
3735
/// Allocate an VarBlock
38-
VarBlock(int size) : indirectIndex(0) { _dataPtrs.resize(size); }
36+
VarBlock(int size, bool makeThreadSafe) : indirectIndex(0), threadSafe(makeThreadSafe), _dataPtrs(size) {}
3937

4038
public:
4139
friend class VarBlockCreator;
4240

4341
/// Move semantics is the only allowed way to change the structure
44-
VarBlock(VarBlock&& other) { _dataPtrs = std::move(other._dataPtrs); }
42+
VarBlock(VarBlock&& other) {
43+
threadSafe = other.threadSafe;
44+
d = std::move(other.d);
45+
s = std::move(other.s);
46+
_dataPtrs = std::move(other._dataPtrs);
47+
indirectIndex = other.indirectIndex;
48+
}
4549

4650
~VarBlock() {}
4751

@@ -57,11 +61,20 @@ class VarBlock {
5761
// i.e. _dataPtrs[someAttributeOffset][indirectIndex]
5862
int indirectIndex;
5963

64+
/// if true, interpreter's data will be copied to this instance before evaluation.
65+
bool threadSafe;
66+
67+
/// copy of Interpreter's double data
68+
std::vector<double> d;
69+
70+
/// copy of Interpreter's str data
71+
std::vector<char*> s;
72+
6073
/// Raw data of the data block pointer (used by compiler)
6174
char** data() { return _dataPtrs.data(); }
6275

6376
private:
64-
/// This stores double* or char** ptrs
77+
/// This stores double* or char** ptrs to variables
6578
std::vector<char*> _dataPtrs;
6679
};
6780

@@ -97,7 +110,17 @@ class VarBlockCreator {
97110
}
98111

99112
/// Get an evaluation handle (one needed per thread)
100-
VarBlock create() { return VarBlock(_nextOffset); }
113+
/// \param makeThreadSafe
114+
/// If true, right before evaluating the expression, all data used
115+
/// by the interpreter will be copied to the var block, to make the
116+
/// evaluation thread safe (assuming there's one var block instead
117+
/// per thread)
118+
/// If false or not specified, the old behavior occurs (var block
119+
/// will only hold variables sources and optionally output data,
120+
/// and the interpreter will work on its internal data)
121+
VarBlock create(bool makeThreadSafe = false) {
122+
return VarBlock(_nextOffset, makeThreadSafe);
123+
}
101124

102125
/// Resolve the variable using anything in the data block (call from resolveVar in Expr subclass)
103126
ExprVarRef* resolveVar(const std::string& name) const {

0 commit comments

Comments
 (0)