Skip to content

Commit 1d44e93

Browse files
authored
Merge pull request #3034 from finos/fix-multi-server-threadsafe
Fix thread safety of multiple `Server` instances in the same process
2 parents f6dff11 + 89b4224 commit 1d44e93

File tree

8 files changed

+106
-111
lines changed

8 files changed

+106
-111
lines changed

cpp/perspective/src/cpp/computed_expression.cpp

Lines changed: 26 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,6 @@
1616

1717
namespace perspective {
1818

19-
// Change ExprTk's default compilation options to only check for correctness
20-
// of brackets and sequences. ExprTk defaults will replace "true" and "false"
21-
// with 1 and 0, which we don't want. Using the tokens "true" and "false"
22-
// will raise a syntax error, which is the correct behavior.
23-
std::size_t t_computed_expression_parser::PARSER_COMPILE_OPTIONS =
24-
exprtk::parser<t_tscalar>::settings_t::e_joiner
25-
+ exprtk::parser<t_tscalar>::settings_t::e_numeric_check
26-
+ exprtk::parser<t_tscalar>::settings_t::e_bracket_check
27-
+ exprtk::parser<t_tscalar>::settings_t::e_sequence_check;
28-
// exprtk::parser<t_tscalar>::settings_t::e_commutative_check;
29-
// exprtk::parser<t_tscalar>::settings_t::e_strength_reduction;
30-
31-
std::shared_ptr<exprtk::parser<t_tscalar>>
32-
t_computed_expression_parser::PARSER =
33-
std::make_shared<exprtk::parser<t_tscalar>>(
34-
t_computed_expression_parser::PARSER_COMPILE_OPTIONS
35-
);
36-
3719
computed_function::bucket t_computed_expression_parser::BUCKET_FN =
3820
computed_function::bucket();
3921

@@ -160,13 +142,13 @@ t_computed_expression::compute(
160142

161143
expr_definition.register_symbol_table(sym_table);
162144

163-
if (!t_computed_expression_parser::PARSER->compile(
145+
if (!m_computed_expression_parser.m_parser->compile(
164146
m_parsed_expression_string, expr_definition
165147
)) {
166148
std::stringstream ss;
167149
ss << "[t_computed_expression::compute] Failed to parse expression: `"
168150
<< m_parsed_expression_string << "`, failed with error: "
169-
<< t_computed_expression_parser::PARSER->error() << '\n';
151+
<< m_computed_expression_parser.m_parser->error() << '\n';
170152

171153
PSP_COMPLAIN_AND_ABORT(ss.str());
172154
}
@@ -227,9 +209,22 @@ t_computed_expression::get_dtype() const {
227209
* t_computed_expression_parser
228210
*/
229211

230-
void
231-
t_computed_expression_parser::init() {
232-
t_computed_expression_parser::PARSER->settings()
212+
t_computed_expression_parser::t_computed_expression_parser() {
213+
// Change ExprTk's default compilation options to only check for correctness
214+
// of brackets and sequences. ExprTk defaults will replace "true" and
215+
// "false" with 1 and 0, which we don't want. Using the tokens "true" and
216+
// "false" will raise a syntax error, which is the correct behavior.
217+
218+
m_parser = std::make_shared<exprtk::parser<t_tscalar>>(
219+
exprtk::parser<t_tscalar>::settings_t::e_joiner
220+
+ exprtk::parser<t_tscalar>::settings_t::e_numeric_check
221+
+ exprtk::parser<t_tscalar>::settings_t::e_bracket_check
222+
+ exprtk::parser<t_tscalar>::settings_t::e_sequence_check
223+
// exprtk::parser<t_tscalar>::settings_t::e_commutative_check;
224+
// exprtk::parser<t_tscalar>::settings_t::e_strength_reduction;
225+
);
226+
227+
m_parser->settings()
233228
.disable_control_structure(
234229
exprtk::parser<t_tscalar>::settings_store::e_ctrl_repeat_loop
235230
)
@@ -255,7 +250,7 @@ t_computed_expression_parser::precompute(
255250
const std::shared_ptr<t_schema>& schema,
256251
t_expression_vocab& vocab,
257252
t_regex_mapping& regex_mapping
258-
) {
253+
) const {
259254
exprtk::symbol_table<t_tscalar> sym_table;
260255
sym_table.add_constants();
261256

@@ -299,14 +294,12 @@ t_computed_expression_parser::precompute(
299294
exprtk::expression<t_tscalar> expr_definition;
300295
expr_definition.register_symbol_table(sym_table);
301296

302-
if (!t_computed_expression_parser::PARSER->compile(
303-
parsed_expression_string, expr_definition
304-
)) {
297+
if (!m_parser->compile(parsed_expression_string, expr_definition)) {
305298
std::stringstream ss;
306299
ss << "[t_computed_expression_parser::precompute] Failed to parse "
307300
"expression: `"
308-
<< parsed_expression_string << "`, failed with error: "
309-
<< t_computed_expression_parser::PARSER->error() << '\n';
301+
<< parsed_expression_string
302+
<< "`, failed with error: " << m_parser->error() << '\n';
310303
PSP_COMPLAIN_AND_ABORT(ss.str());
311304
}
312305

@@ -334,7 +327,7 @@ t_computed_expression_parser::get_dtype(
334327
t_expression_error& error,
335328
t_expression_vocab& vocab,
336329
t_regex_mapping& regex_mapping
337-
) {
330+
) const {
338331
exprtk::symbol_table<t_tscalar> sym_table;
339332
sym_table.add_constants();
340333

@@ -391,14 +384,11 @@ t_computed_expression_parser::get_dtype(
391384
exprtk::expression<t_tscalar> expr_definition;
392385
expr_definition.register_symbol_table(sym_table);
393386

394-
if (!t_computed_expression_parser::PARSER->compile(
395-
parsed_expression_string, expr_definition
396-
)) {
387+
if (!m_parser->compile(parsed_expression_string, expr_definition)) {
397388
// Error count should always be above 0 if there is a compile error -
398389
// We simply take the first error and return it.
399-
if (t_computed_expression_parser::PARSER->error_count() > 0) {
400-
auto parser_error =
401-
t_computed_expression_parser::PARSER->get_error(0);
390+
if (m_parser->error_count() > 0) {
391+
auto parser_error = m_parser->get_error(0);
402392

403393
// Given an error object and an expression, `update_error` maps the
404394
// error to a line and column number inside the expression.

cpp/perspective/src/cpp/computed_function.cpp

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1182,13 +1182,13 @@ bucket::operator()(t_parameter_list parameters) {
11821182
t_scalar_view temp_val(gt_val);
11831183
val.set(temp_val());
11841184

1185+
// Bucket by numeric value
1186+
t_scalar_view temp_unit2(gt_unit);
1187+
unit.set(temp_unit2());
1188+
11851189
if (val.is_numeric()) {
11861190
rval.m_type = DTYPE_FLOAT64;
11871191

1188-
// Bucket by numeric value
1189-
t_scalar_view temp_unit(gt_unit);
1190-
unit.set(temp_unit());
1191-
11921192
// type-check
11931193
if (!unit.is_numeric() || val.m_status == STATUS_CLEAR
11941194
|| unit.m_status == STATUS_CLEAR) {
@@ -1206,10 +1206,10 @@ bucket::operator()(t_parameter_list parameters) {
12061206
}
12071207

12081208
// Must be a datetime - second parameter is a string
1209-
t_string_view temp_string(gt_unit);
1210-
std::string unit_str = std::string(temp_string.begin(), temp_string.end());
1209+
const auto unit_str = std::string(unit.get<const char*>());
12111210
char temp_unit = 0;
1212-
auto len = unit_str.size();
1211+
const auto len = unit_str.size();
1212+
12131213
unsigned long multiplicity;
12141214
t_date_bucket_unit date_unit;
12151215
if (len == 0) {
@@ -1245,13 +1245,6 @@ bucket::operator()(t_parameter_list parameters) {
12451245
// type-check multiplicity
12461246
switch (date_unit) {
12471247
case t_date_bucket_unit::SECONDS:
1248-
if (multiplicity != 1 && multiplicity != 5 && multiplicity != 10
1249-
&& multiplicity != 15 && multiplicity != 20
1250-
&& multiplicity != 30) {
1251-
rval.m_status = STATUS_CLEAR;
1252-
return rval;
1253-
}
1254-
break;
12551248
case t_date_bucket_unit::MINUTES:
12561249
if (multiplicity != 1 && multiplicity != 5 && multiplicity != 10
12571250
&& multiplicity != 15 && multiplicity != 20
@@ -1270,11 +1263,6 @@ bucket::operator()(t_parameter_list parameters) {
12701263
break;
12711264
case t_date_bucket_unit::DAYS:
12721265
// TODO: day multiplicity.
1273-
if (multiplicity != 1) {
1274-
rval.m_status = STATUS_CLEAR;
1275-
return rval;
1276-
}
1277-
break;
12781266
case t_date_bucket_unit::WEEKS:
12791267
// TODO: week multiplicity
12801268
if (multiplicity != 1) {

cpp/perspective/src/cpp/server.cpp

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ re_intern_strings(std::string&& expression) {
307307
static auto
308308
re_unintern_some_exprs(std::string&& expression) {
309309
static const RE2 interned_param(
310-
"(?:bucket|match|match_all|search|indexof|replace|replace_all)\\("
310+
"(?:match|match_all|search|indexof|replace|replace_all)\\("
311311
"(?:.*?,\\s*(intern\\(('.*?')\\)))"
312312
);
313313
static const RE2 intern_match("intern\\(('.*?')\\)");
@@ -1291,12 +1291,6 @@ coerce_to(const t_dtype dtype, const A& val) {
12911291

12921292
std::vector<ProtoServerResp<ProtoServer::Response>>
12931293
ProtoServer::_handle_request(std::uint32_t client_id, Request&& req) {
1294-
static bool is_init_expr = false;
1295-
if (!is_init_expr) {
1296-
t_computed_expression_parser::init();
1297-
is_init_expr = true;
1298-
}
1299-
13001294
std::vector<ProtoServerResp<ProtoServer::Response>> proto_resp;
13011295
// proto::Response resp_env;
13021296

@@ -1810,7 +1804,7 @@ ProtoServer::_handle_request(std::uint32_t client_id, Request&& req) {
18101804
t_regex_mapping& regex_mapping = *expression_regex_mapping;
18111805

18121806
std::shared_ptr<t_computed_expression> computed_expression =
1813-
t_computed_expression_parser::precompute(
1807+
m_computed_expression_parser.precompute(
18141808
expr.expression_alias,
18151809
expr.expression,
18161810
expr.parse_expression_string,

cpp/perspective/src/cpp/table.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "perspective/arrow_loader.h"
1414
#include "perspective/base.h"
1515
#include "perspective/column.h"
16+
#include "perspective/computed_expression.h"
1617
#include "perspective/data_table.h"
1718
#include "perspective/raw_types.h"
1819
#include "perspective/schema.h"
@@ -49,6 +50,7 @@ Table::Table(
4950
m_limit(limit),
5051
m_index(std::move(index)),
5152
m_gnode_set(false) {
53+
5254
validate_columns(m_column_names);
5355
}
5456

@@ -152,7 +154,7 @@ Table::validate_expressions(
152154

153155
const auto& column_ids = std::get<3>(expr);
154156

155-
t_dtype expression_dtype = t_computed_expression_parser::get_dtype(
157+
t_dtype expression_dtype = m_computed_expression_parser.get_dtype(
156158
expression_alias,
157159
expression_string,
158160
parsed_expression_string,

cpp/perspective/src/include/perspective/computed_expression.h

Lines changed: 47 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -40,48 +40,11 @@ struct PERSPECTIVE_EXPORT t_expression_error {
4040
t_index m_column;
4141
};
4242

43-
/**
44-
* @brief Contains the metadata for a single expression and the methods which
45-
* will compute the expression's output.
46-
*/
47-
class PERSPECTIVE_EXPORT t_computed_expression {
48-
public:
49-
PSP_NON_COPYABLE(t_computed_expression);
50-
51-
t_computed_expression(
52-
std::string expression_alias,
53-
std::string expression_string,
54-
std::string parsed_expression_string,
55-
const std::vector<std::pair<std::string, std::string>>& column_ids,
56-
t_dtype dtype
57-
);
58-
59-
void compute(
60-
const std::shared_ptr<t_data_table>& source_table,
61-
const t_gstate::t_mapping& pkey_map,
62-
const std::shared_ptr<t_data_table>& destination_table,
63-
t_expression_vocab& vocab,
64-
t_regex_mapping& regex_mapping
65-
) const;
66-
67-
const std::string& get_expression_alias() const;
68-
const std::string& get_expression_string() const;
69-
const std::string& get_parsed_expression_string() const;
70-
const std::vector<std::pair<std::string, std::string>>&
71-
get_column_ids() const;
72-
t_dtype get_dtype() const;
73-
74-
private:
75-
std::string m_expression_alias;
76-
std::string m_expression_string;
77-
std::string m_parsed_expression_string;
78-
std::vector<std::pair<std::string, std::string>> m_column_ids;
79-
t_dtype m_dtype;
80-
};
43+
class PERSPECTIVE_EXPORT t_computed_expression;
8144

8245
class PERSPECTIVE_EXPORT t_computed_expression_parser {
8346
public:
84-
static void init();
47+
t_computed_expression_parser();
8548

8649
/**
8750
* @brief Given expression strings, validate the expression's dtype and
@@ -100,7 +63,7 @@ class PERSPECTIVE_EXPORT t_computed_expression_parser {
10063
* @param schema
10164
* @return std::shared_ptr<t_computed_expression>
10265
*/
103-
static std::shared_ptr<t_computed_expression> precompute(
66+
std::shared_ptr<t_computed_expression> precompute(
10467
const std::string& expression_alias,
10568
const std::string& expression_string,
10669
const std::string& parsed_expression_string,
@@ -110,7 +73,7 @@ class PERSPECTIVE_EXPORT t_computed_expression_parser {
11073
const std::shared_ptr<t_schema>& schema,
11174
t_expression_vocab& vocab,
11275
t_regex_mapping& regex_mapping
113-
);
76+
) const;
11477

11578
/**
11679
* @brief Returns the dtype of the given expression, or `DTYPE_NONE`
@@ -132,7 +95,7 @@ class PERSPECTIVE_EXPORT t_computed_expression_parser {
13295
* @param error_string
13396
* @return t_dtype
13497
*/
135-
static t_dtype get_dtype(
98+
t_dtype get_dtype(
13699
const std::string& expression_alias,
137100
const std::string& expression_string,
138101
const std::string& parsed_expression_string,
@@ -143,12 +106,9 @@ class PERSPECTIVE_EXPORT t_computed_expression_parser {
143106
t_expression_error& error,
144107
t_expression_vocab& vocab,
145108
t_regex_mapping& regex_mapping
146-
);
147-
148-
static std::shared_ptr<exprtk::parser<t_tscalar>> PARSER;
109+
) const;
149110

150-
// Applied to the parser
151-
static std::size_t PARSER_COMPILE_OPTIONS;
111+
std::shared_ptr<exprtk::parser<t_tscalar>> m_parser;
152112

153113
// Static computed functions have no state
154114
static computed_function::bucket BUCKET_FN;
@@ -176,6 +136,46 @@ class PERSPECTIVE_EXPORT t_computed_expression_parser {
176136
static t_tscalar FALSE_SCALAR;
177137
};
178138

139+
/**
140+
* @brief Contains the metadata for a single expression and the methods which
141+
* will compute the expression's output.
142+
*/
143+
class PERSPECTIVE_EXPORT t_computed_expression {
144+
public:
145+
PSP_NON_COPYABLE(t_computed_expression);
146+
147+
t_computed_expression(
148+
std::string expression_alias,
149+
std::string expression_string,
150+
std::string parsed_expression_string,
151+
const std::vector<std::pair<std::string, std::string>>& column_ids,
152+
t_dtype dtype
153+
);
154+
155+
void compute(
156+
const std::shared_ptr<t_data_table>& source_table,
157+
const t_gstate::t_mapping& pkey_map,
158+
const std::shared_ptr<t_data_table>& destination_table,
159+
t_expression_vocab& vocab,
160+
t_regex_mapping& regex_mapping
161+
) const;
162+
163+
const std::string& get_expression_alias() const;
164+
const std::string& get_expression_string() const;
165+
const std::string& get_parsed_expression_string() const;
166+
const std::vector<std::pair<std::string, std::string>>&
167+
get_column_ids() const;
168+
t_dtype get_dtype() const;
169+
170+
private:
171+
std::string m_expression_alias;
172+
std::string m_expression_string;
173+
std::string m_parsed_expression_string;
174+
t_computed_expression_parser m_computed_expression_parser;
175+
std::vector<std::pair<std::string, std::string>> m_column_ids;
176+
t_dtype m_dtype;
177+
};
178+
179179
/**
180180
* @brief a `t_schema`-like container for validated expression results that
181181
* offers fast lookups.

cpp/perspective/src/include/perspective/server.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,7 @@ namespace server {
663663
static std::uint32_t m_client_id;
664664
bool m_realtime_mode;
665665
ServerResources m_resources;
666+
t_computed_expression_parser m_computed_expression_parser;
666667
};
667668

668669
} // namespace server

cpp/perspective/src/include/perspective/table.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
1212

1313
#pragma once
14+
#include "perspective/computed_expression.h"
1415
#include <perspective/first.h>
1516
#include <perspective/exports.h>
1617
#include <perspective/base.h>
@@ -268,6 +269,7 @@ class PERSPECTIVE_EXPORT Table {
268269
std::shared_ptr<t_gnode> m_gnode;
269270
std::vector<std::string> m_column_names;
270271
std::vector<t_dtype> m_data_types;
272+
t_computed_expression_parser m_computed_expression_parser;
271273

272274
/**
273275
* @brief The row number at which we start to write into the Table.

0 commit comments

Comments
 (0)