Skip to content

Commit 888ec0b

Browse files
committed
SimpleTreeLearner: Thread safety
Remove use of global `compar_attr` variable. Use `qsort_(r|s)` with explicit context passing. Use GIL releasing ctypes call.
1 parent 9ba1d91 commit 888ec0b

File tree

2 files changed

+39
-9
lines changed

2 files changed

+39
-9
lines changed

Orange/classification/_simple_tree.c

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,16 +47,45 @@ enum { DiscreteNode, ContinuousNode, PredictorNode };
4747
enum { Classification, Regression };
4848
enum { IntVar, FloatVar };
4949

50-
int compar_attr;
50+
/*
51+
* Common interface for qsort_r
52+
* (there are 3 (possibly more) different qsort_r call styles in the wild).
53+
*
54+
*/
55+
56+
#if (defined __APPLE__ || defined __DARWIN__ || defined __BSD__)
57+
#define QSORT_R_STYLE_BSD
58+
#define QSORT_R_FUNC(base, nel, size, thunk, compar) \
59+
qsort_r(base, nel, size, thunk, compar)
60+
#elif (defined __GLIBC__ || defined __GNU__ || defined __linux__)
61+
#define QSORT_R_STYLE_GNU
62+
#define QSORT_R_FUNC(base, nel, size, thunk, compar) \
63+
qsort_r(base, nel, size, compar, thunk)
64+
#elif (defined _MSC_VER)
65+
#define QSORT_R_STYLE_MSVC
66+
#define QSORT_R_FUNC(base, nel, size, thunk, compar) \
67+
qsort_s(base, nel, size, compar, thunk)
68+
#endif
5169

52-
/* This function uses the global variable compar_attr.
70+
71+
#if (defined QSORT_R_STYLE_BSD || defined QSORT_R_STYLE_MSVC)
72+
#define SORT_CMP_FUNC(name) \
73+
int name(void *context, const void *ptr1, const void *ptr2)
74+
#elif (defined QSORT_R_STYLE_GNU)
75+
#define SORT_CMP_FUNC(name) \
76+
int name(const void *ptr1, const void *ptr2, void *context)
77+
#else
78+
#error "Unkown qsort_r comparator call convention"
79+
#endif
80+
81+
82+
/*
5383
* Examples with unknowns are larger so that, when sorted, they appear at the bottom.
5484
*/
55-
int
56-
compar_examples(const void *ptr1, const void *ptr2)
85+
SORT_CMP_FUNC(compar_examples)
5786
{
5887
double x1, x2;
59-
88+
int compar_attr = *(int *)context;
6089
x1 = ((struct Example *)ptr1)->x[compar_attr];
6190
x2 = ((struct Example *)ptr2)->x[compar_attr];
6291
if (isnan(x1))
@@ -102,7 +131,7 @@ gain_ratio_c(struct Example *examples, int size, int attr, float cls_entropy, st
102131
struct Example *ex, *ex_end, *ex_next;
103132
int i, cls, cls_vals, min_instances, size_known;
104133
float score, *dist_lt, *dist_ge, *attr_dist, best_score, size_weight;
105-
134+
int compar_attr;
106135
cls_vals = args->cls_vals;
107136

108137
/* min_instances should be at least 1, otherwise there is no point in splitting */
@@ -115,7 +144,7 @@ gain_ratio_c(struct Example *examples, int size, int attr, float cls_entropy, st
115144

116145
/* sort */
117146
compar_attr = attr;
118-
qsort(examples, size, sizeof(struct Example), compar_examples);
147+
QSORT_R_FUNC(examples, size, sizeof(struct Example), (void*) &compar_attr, compar_examples);
119148

120149
/* compute gain ratio for every split */
121150
size_known = size;
@@ -236,6 +265,7 @@ mse_c(struct Example *examples, int size, int attr, float cls_mse, struct Args *
236265
struct Example *ex, *ex_end, *ex_next;
237266
int i, min_instances, size_known;
238267
float size_attr_known, size_weight, cls_val, best_score, size_attr_cls_known, score;
268+
int compar_attr;
239269

240270
struct Variance {
241271
double n, sum, sum2;
@@ -246,7 +276,7 @@ mse_c(struct Example *examples, int size, int attr, float cls_mse, struct Args *
246276

247277
/* sort */
248278
compar_attr = attr;
249-
qsort(examples, size, sizeof(struct Example), compar_examples);
279+
QSORT_R_FUNC(examples, size, sizeof(struct Example), (void *)&compar_attr, compar_examples);
250280

251281
/* compute mse for every split */
252282
size_known = size;

Orange/classification/simple_tree.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
__all__ = ['SimpleTreeLearner']
77

88
from . import _simple_tree
9-
_tree = ct.pydll.LoadLibrary(_simple_tree.__file__)
9+
_tree = ct.cdll.LoadLibrary(_simple_tree.__file__)
1010

1111
DiscreteNode = 0
1212
ContinuousNode = 1

0 commit comments

Comments
 (0)