numexpr 2.10.2__cp313-cp313-win32.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- numexpr/__init__.py +66 -0
- numexpr/complex_functions.hpp +427 -0
- numexpr/cpuinfo.py +859 -0
- numexpr/expressions.py +523 -0
- numexpr/functions.hpp +140 -0
- numexpr/interp_body.cpp +543 -0
- numexpr/interpreter.cp313-win32.pyd +0 -0
- numexpr/interpreter.cpp +1528 -0
- numexpr/interpreter.hpp +107 -0
- numexpr/missing_posix_functions.hpp +102 -0
- numexpr/module.cpp +537 -0
- numexpr/module.hpp +60 -0
- numexpr/msvc_function_stubs.hpp +150 -0
- numexpr/necompiler.py +1005 -0
- numexpr/numexpr_config.hpp +51 -0
- numexpr/numexpr_object.cpp +408 -0
- numexpr/numexpr_object.hpp +34 -0
- numexpr/opcodes.hpp +179 -0
- numexpr/str-two-way.hpp +435 -0
- numexpr/tests/__init__.py +14 -0
- numexpr/tests/test_numexpr.py +1421 -0
- numexpr/utils.py +309 -0
- numexpr/version.py +5 -0
- numexpr/win32/pthread.c +218 -0
- numexpr/win32/pthread.h +119 -0
- numexpr/win32/stdint.h +235 -0
- numexpr-2.10.2.dist-info/AUTHORS.txt +31 -0
- numexpr-2.10.2.dist-info/LICENSE.txt +21 -0
- numexpr-2.10.2.dist-info/METADATA +214 -0
- numexpr-2.10.2.dist-info/RECORD +32 -0
- numexpr-2.10.2.dist-info/WHEEL +5 -0
- numexpr-2.10.2.dist-info/top_level.txt +1 -0
numexpr/utils.py
ADDED
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
###################################################################
|
|
2
|
+
# Numexpr - Fast numerical array expression evaluator for NumPy.
|
|
3
|
+
#
|
|
4
|
+
# License: MIT
|
|
5
|
+
# Author: See AUTHORS.txt
|
|
6
|
+
#
|
|
7
|
+
# See LICENSE.txt and LICENSES/*.txt for details about copyright and
|
|
8
|
+
# rights to use.
|
|
9
|
+
####################################################################
|
|
10
|
+
|
|
11
|
+
import logging
|
|
12
|
+
log = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
import os
|
|
15
|
+
import subprocess
|
|
16
|
+
import contextvars
|
|
17
|
+
|
|
18
|
+
from numexpr.interpreter import _set_num_threads, _get_num_threads, MAX_THREADS
|
|
19
|
+
from numexpr import use_vml
|
|
20
|
+
from . import version
|
|
21
|
+
|
|
22
|
+
if use_vml:
|
|
23
|
+
from numexpr.interpreter import (
|
|
24
|
+
_get_vml_version, _set_vml_accuracy_mode, _set_vml_num_threads,
|
|
25
|
+
_get_vml_num_threads)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def get_vml_version():
|
|
29
|
+
"""
|
|
30
|
+
Get the VML/MKL library version.
|
|
31
|
+
"""
|
|
32
|
+
if use_vml:
|
|
33
|
+
return _get_vml_version()
|
|
34
|
+
else:
|
|
35
|
+
return None
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def set_vml_accuracy_mode(mode):
|
|
39
|
+
"""
|
|
40
|
+
Set the accuracy mode for VML operations.
|
|
41
|
+
|
|
42
|
+
The `mode` parameter can take the values:
|
|
43
|
+
- 'high': high accuracy mode (HA), <1 least significant bit
|
|
44
|
+
- 'low': low accuracy mode (LA), typically 1-2 least significant bits
|
|
45
|
+
- 'fast': enhanced performance mode (EP)
|
|
46
|
+
- None: mode settings are ignored
|
|
47
|
+
|
|
48
|
+
This call is equivalent to the `vmlSetMode()` in the VML library.
|
|
49
|
+
See:
|
|
50
|
+
|
|
51
|
+
http://www.intel.com/software/products/mkl/docs/webhelp/vml/vml_DataTypesAccuracyModes.html
|
|
52
|
+
|
|
53
|
+
for more info on the accuracy modes.
|
|
54
|
+
|
|
55
|
+
Returns old accuracy settings.
|
|
56
|
+
"""
|
|
57
|
+
if use_vml:
|
|
58
|
+
acc_dict = {None: 0, 'low': 1, 'high': 2, 'fast': 3}
|
|
59
|
+
acc_reverse_dict = {1: 'low', 2: 'high', 3: 'fast'}
|
|
60
|
+
if mode not in list(acc_dict.keys()):
|
|
61
|
+
raise ValueError(
|
|
62
|
+
"mode argument must be one of: None, 'high', 'low', 'fast'")
|
|
63
|
+
retval = _set_vml_accuracy_mode(acc_dict.get(mode, 0))
|
|
64
|
+
return acc_reverse_dict.get(retval)
|
|
65
|
+
else:
|
|
66
|
+
return None
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def set_vml_num_threads(nthreads):
|
|
70
|
+
"""
|
|
71
|
+
Suggests a maximum number of threads to be used in VML operations.
|
|
72
|
+
|
|
73
|
+
This function is equivalent to the call
|
|
74
|
+
`mkl_domain_set_num_threads(nthreads, MKL_DOMAIN_VML)` in the MKL
|
|
75
|
+
library. See:
|
|
76
|
+
|
|
77
|
+
http://www.intel.com/software/products/mkl/docs/webhelp/support/functn_mkl_domain_set_num_threads.html
|
|
78
|
+
|
|
79
|
+
for more info about it.
|
|
80
|
+
"""
|
|
81
|
+
if use_vml:
|
|
82
|
+
_set_vml_num_threads(nthreads)
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
def get_vml_num_threads():
|
|
86
|
+
"""
|
|
87
|
+
Gets the maximum number of threads to be used in VML operations.
|
|
88
|
+
|
|
89
|
+
This function is equivalent to the call
|
|
90
|
+
`mkl_domain_get_max_threads (MKL_DOMAIN_VML)` in the MKL
|
|
91
|
+
library. See:
|
|
92
|
+
|
|
93
|
+
http://software.intel.com/en-us/node/522118
|
|
94
|
+
|
|
95
|
+
for more info about it.
|
|
96
|
+
"""
|
|
97
|
+
if use_vml:
|
|
98
|
+
return _get_vml_num_threads()
|
|
99
|
+
return None
|
|
100
|
+
|
|
101
|
+
def set_num_threads(nthreads):
|
|
102
|
+
"""
|
|
103
|
+
Sets a number of threads to be used in operations.
|
|
104
|
+
|
|
105
|
+
DEPRECATED: returns the previous setting for the number of threads.
|
|
106
|
+
|
|
107
|
+
During initialization time NumExpr sets this number to the number
|
|
108
|
+
of detected cores in the system (see `detect_number_of_cores()`).
|
|
109
|
+
"""
|
|
110
|
+
old_nthreads = _set_num_threads(nthreads)
|
|
111
|
+
return old_nthreads
|
|
112
|
+
|
|
113
|
+
def get_num_threads():
|
|
114
|
+
"""
|
|
115
|
+
Gets the number of threads currently in use for operations.
|
|
116
|
+
"""
|
|
117
|
+
return _get_num_threads()
|
|
118
|
+
|
|
119
|
+
def _init_num_threads():
|
|
120
|
+
"""
|
|
121
|
+
Detects the environment variable 'NUMEXPR_MAX_THREADS' to set the threadpool
|
|
122
|
+
size, and if necessary the slightly redundant 'NUMEXPR_NUM_THREADS' or
|
|
123
|
+
'OMP_NUM_THREADS' env vars to set the initial number of threads used by
|
|
124
|
+
the virtual machine.
|
|
125
|
+
"""
|
|
126
|
+
# Any platform-specific short-circuits
|
|
127
|
+
if 'sparc' in version.platform_machine:
|
|
128
|
+
log.warning('The number of threads have been set to 1 because problems related '
|
|
129
|
+
'to threading have been reported on some sparc machine. '
|
|
130
|
+
'The number of threads can be changed using the "set_num_threads" '
|
|
131
|
+
'function.')
|
|
132
|
+
set_num_threads(1)
|
|
133
|
+
return 1
|
|
134
|
+
|
|
135
|
+
env_configured = False
|
|
136
|
+
n_cores = detect_number_of_cores()
|
|
137
|
+
if ('NUMEXPR_MAX_THREADS' in os.environ and os.environ['NUMEXPR_MAX_THREADS'] != '' or
|
|
138
|
+
'OMP_NUM_THREADS' in os.environ and os.environ['OMP_NUM_THREADS'] != ''):
|
|
139
|
+
# The user has configured NumExpr in the expected way, so suppress logs.
|
|
140
|
+
env_configured = True
|
|
141
|
+
n_cores = MAX_THREADS
|
|
142
|
+
else:
|
|
143
|
+
# The use has not set 'NUMEXPR_MAX_THREADS', so likely they have not
|
|
144
|
+
# configured NumExpr as desired, so we emit info logs.
|
|
145
|
+
if n_cores > MAX_THREADS:
|
|
146
|
+
log.info('Note: detected %d virtual cores but NumExpr set to maximum of %d, check "NUMEXPR_MAX_THREADS" environment variable.'%(n_cores, MAX_THREADS))
|
|
147
|
+
if n_cores > 16:
|
|
148
|
+
# Back in 2019, 8 threads would be considered safe for performance. We are in 2024 now, so adjusting.
|
|
149
|
+
log.info('Note: NumExpr detected %d cores but "NUMEXPR_MAX_THREADS" not set, so enforcing safe limit of 16.'%n_cores)
|
|
150
|
+
n_cores = 16
|
|
151
|
+
|
|
152
|
+
# Now we check for 'NUMEXPR_NUM_THREADS' or 'OMP_NUM_THREADS' to set the
|
|
153
|
+
# actual number of threads used.
|
|
154
|
+
if 'NUMEXPR_NUM_THREADS' in os.environ and os.environ['NUMEXPR_NUM_THREADS'] != '':
|
|
155
|
+
requested_threads = int(os.environ['NUMEXPR_NUM_THREADS'])
|
|
156
|
+
elif 'OMP_NUM_THREADS' in os.environ and os.environ['OMP_NUM_THREADS'] != '':
|
|
157
|
+
# Empty string is commonly used to unset the variable
|
|
158
|
+
requested_threads = int(os.environ['OMP_NUM_THREADS'])
|
|
159
|
+
else:
|
|
160
|
+
requested_threads = n_cores
|
|
161
|
+
if not env_configured:
|
|
162
|
+
log.info('NumExpr defaulting to %d threads.'%n_cores)
|
|
163
|
+
|
|
164
|
+
# The C-extension function performs its own checks against `MAX_THREADS`
|
|
165
|
+
set_num_threads(requested_threads)
|
|
166
|
+
return requested_threads
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def detect_number_of_cores():
|
|
170
|
+
"""
|
|
171
|
+
Detects the number of cores on a system. Cribbed from pp.
|
|
172
|
+
"""
|
|
173
|
+
# Linux, Unix and MacOS:
|
|
174
|
+
if hasattr(os, "sysconf"):
|
|
175
|
+
if "SC_NPROCESSORS_ONLN" in os.sysconf_names:
|
|
176
|
+
# Linux & Unix:
|
|
177
|
+
ncpus = os.sysconf("SC_NPROCESSORS_ONLN")
|
|
178
|
+
if isinstance(ncpus, int) and ncpus > 0:
|
|
179
|
+
return ncpus
|
|
180
|
+
else: # OSX:
|
|
181
|
+
return int(subprocess.check_output(["sysctl", "-n", "hw.ncpu"]))
|
|
182
|
+
# Windows:
|
|
183
|
+
try:
|
|
184
|
+
ncpus = int(os.environ.get("NUMBER_OF_PROCESSORS", ""))
|
|
185
|
+
if ncpus > 0:
|
|
186
|
+
return ncpus
|
|
187
|
+
except ValueError:
|
|
188
|
+
pass
|
|
189
|
+
return 1 # Default
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def detect_number_of_threads():
|
|
193
|
+
"""
|
|
194
|
+
DEPRECATED: use `_init_num_threads` instead.
|
|
195
|
+
If this is modified, please update the note in: https://github.com/pydata/numexpr/wiki/Numexpr-Users-Guide
|
|
196
|
+
"""
|
|
197
|
+
log.warning('Deprecated, use `init_num_threads` instead.')
|
|
198
|
+
try:
|
|
199
|
+
nthreads = int(os.environ.get('NUMEXPR_NUM_THREADS', ''))
|
|
200
|
+
except ValueError:
|
|
201
|
+
try:
|
|
202
|
+
nthreads = int(os.environ.get('OMP_NUM_THREADS', ''))
|
|
203
|
+
except ValueError:
|
|
204
|
+
nthreads = detect_number_of_cores()
|
|
205
|
+
|
|
206
|
+
# Check that we don't surpass the MAX_THREADS in interpreter.cpp
|
|
207
|
+
if nthreads > MAX_THREADS:
|
|
208
|
+
nthreads = MAX_THREADS
|
|
209
|
+
return nthreads
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
class CacheDict(dict):
|
|
213
|
+
"""
|
|
214
|
+
A dictionary that prevents itself from growing too much.
|
|
215
|
+
"""
|
|
216
|
+
|
|
217
|
+
def __init__(self, maxentries):
|
|
218
|
+
self.maxentries = maxentries
|
|
219
|
+
super(CacheDict, self).__init__(self)
|
|
220
|
+
|
|
221
|
+
def __setitem__(self, key, value):
|
|
222
|
+
# Protection against growing the cache too much
|
|
223
|
+
if len(self) > self.maxentries:
|
|
224
|
+
# Remove a 10% of (arbitrary) elements from the cache
|
|
225
|
+
entries_to_remove = self.maxentries // 10
|
|
226
|
+
for k in list(self.keys())[:entries_to_remove]:
|
|
227
|
+
super(CacheDict, self).__delitem__(k)
|
|
228
|
+
super(CacheDict, self).__setitem__(key, value)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
class ContextDict:
|
|
232
|
+
"""
|
|
233
|
+
A context aware version dictionary
|
|
234
|
+
"""
|
|
235
|
+
def __init__(self):
|
|
236
|
+
self._context_data = contextvars.ContextVar('context_data', default={})
|
|
237
|
+
|
|
238
|
+
def set(self, key=None, value=None, **kwargs):
|
|
239
|
+
data = self._context_data.get().copy()
|
|
240
|
+
|
|
241
|
+
if key is not None:
|
|
242
|
+
data[key] = value
|
|
243
|
+
|
|
244
|
+
for k, v in kwargs.items():
|
|
245
|
+
data[k] = v
|
|
246
|
+
|
|
247
|
+
self._context_data.set(data)
|
|
248
|
+
|
|
249
|
+
def get(self, key, default=None):
|
|
250
|
+
data = self._context_data.get()
|
|
251
|
+
return data.get(key, default)
|
|
252
|
+
|
|
253
|
+
def delete(self, key):
|
|
254
|
+
data = self._context_data.get().copy()
|
|
255
|
+
if key in data:
|
|
256
|
+
del data[key]
|
|
257
|
+
self._context_data.set(data)
|
|
258
|
+
|
|
259
|
+
def clear(self):
|
|
260
|
+
self._context_data.set({})
|
|
261
|
+
|
|
262
|
+
def all(self):
|
|
263
|
+
return self._context_data.get()
|
|
264
|
+
|
|
265
|
+
def update(self, *args, **kwargs):
|
|
266
|
+
data = self._context_data.get().copy()
|
|
267
|
+
|
|
268
|
+
if args:
|
|
269
|
+
if len(args) > 1:
|
|
270
|
+
raise TypeError(f"update() takes at most 1 positional argument ({len(args)} given)")
|
|
271
|
+
other = args[0]
|
|
272
|
+
if isinstance(other, dict):
|
|
273
|
+
data.update(other)
|
|
274
|
+
else:
|
|
275
|
+
for k, v in other:
|
|
276
|
+
data[k] = v
|
|
277
|
+
|
|
278
|
+
data.update(kwargs)
|
|
279
|
+
self._context_data.set(data)
|
|
280
|
+
|
|
281
|
+
def keys(self):
|
|
282
|
+
return self._context_data.get().keys()
|
|
283
|
+
|
|
284
|
+
def values(self):
|
|
285
|
+
return self._context_data.get().values()
|
|
286
|
+
|
|
287
|
+
def items(self):
|
|
288
|
+
return self._context_data.get().items()
|
|
289
|
+
|
|
290
|
+
def __getitem__(self, key):
|
|
291
|
+
return self.get(key)
|
|
292
|
+
|
|
293
|
+
def __setitem__(self, key, value):
|
|
294
|
+
self.set(key, value)
|
|
295
|
+
|
|
296
|
+
def __delitem__(self, key):
|
|
297
|
+
self.delete(key)
|
|
298
|
+
|
|
299
|
+
def __contains__(self, key):
|
|
300
|
+
return key in self._context_data.get()
|
|
301
|
+
|
|
302
|
+
def __len__(self):
|
|
303
|
+
return len(self._context_data.get())
|
|
304
|
+
|
|
305
|
+
def __iter__(self):
|
|
306
|
+
return iter(self._context_data.get())
|
|
307
|
+
|
|
308
|
+
def __repr__(self):
|
|
309
|
+
return repr(self._context_data.get())
|
numexpr/version.py
ADDED
numexpr/win32/pthread.c
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Code for simulating pthreads API on Windows. This is Git-specific,
|
|
3
|
+
* but it is enough for Numexpr needs too.
|
|
4
|
+
*
|
|
5
|
+
* Copyright (C) 2009 Andrzej K. Haczewski <ahaczewski@gmail.com>
|
|
6
|
+
*
|
|
7
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
* in the Software without restriction, including without limitation the rights
|
|
10
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
* furnished to do so, subject to the following conditions:
|
|
13
|
+
*
|
|
14
|
+
* The above copyright notice and this permission notice shall be included in
|
|
15
|
+
* all copies or substantial portions of the Software.
|
|
16
|
+
*
|
|
17
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
23
|
+
* THE SOFTWARE.
|
|
24
|
+
*
|
|
25
|
+
* DISCLAIMER: The implementation is Git-specific, it is subset of original
|
|
26
|
+
* Pthreads API, without lots of other features that Git doesn't use.
|
|
27
|
+
* Git also makes sure that the passed arguments are valid, so there's
|
|
28
|
+
* no need for double-checking.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
#include "pthread.h"
|
|
32
|
+
|
|
33
|
+
#include <stdio.h>
|
|
34
|
+
#include <stdlib.h>
|
|
35
|
+
#include <errno.h>
|
|
36
|
+
#include <limits.h>
|
|
37
|
+
#include <process.h>
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
void die(const char *err, ...)
|
|
41
|
+
{
|
|
42
|
+
printf("%s", err);
|
|
43
|
+
exit(-1);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static unsigned __stdcall win32_start_routine(void *arg)
|
|
47
|
+
{
|
|
48
|
+
pthread_t *thread = arg;
|
|
49
|
+
thread->arg = thread->start_routine(thread->arg);
|
|
50
|
+
return 0;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
int pthread_create(pthread_t *thread, const void *unused,
|
|
54
|
+
void *(*start_routine)(void*), void *arg)
|
|
55
|
+
{
|
|
56
|
+
thread->arg = arg;
|
|
57
|
+
thread->start_routine = start_routine;
|
|
58
|
+
thread->handle = (HANDLE)
|
|
59
|
+
_beginthreadex(NULL, 0, win32_start_routine, thread, 0, NULL);
|
|
60
|
+
|
|
61
|
+
if (!thread->handle)
|
|
62
|
+
return errno;
|
|
63
|
+
else
|
|
64
|
+
return 0;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
int win32_pthread_join(pthread_t *thread, void **value_ptr)
|
|
68
|
+
{
|
|
69
|
+
DWORD result = WaitForSingleObject(thread->handle, INFINITE);
|
|
70
|
+
switch (result) {
|
|
71
|
+
case WAIT_OBJECT_0:
|
|
72
|
+
if (value_ptr)
|
|
73
|
+
*value_ptr = thread->arg;
|
|
74
|
+
return 0;
|
|
75
|
+
case WAIT_ABANDONED:
|
|
76
|
+
return EINVAL;
|
|
77
|
+
default:
|
|
78
|
+
return GetLastError();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
int pthread_cond_init(pthread_cond_t *cond, const void *unused)
|
|
83
|
+
{
|
|
84
|
+
cond->waiters = 0;
|
|
85
|
+
cond->was_broadcast = 0;
|
|
86
|
+
InitializeCriticalSection(&cond->waiters_lock);
|
|
87
|
+
|
|
88
|
+
cond->sema = CreateSemaphore(NULL, 0, LONG_MAX, NULL);
|
|
89
|
+
if (!cond->sema)
|
|
90
|
+
die("CreateSemaphore() failed");
|
|
91
|
+
|
|
92
|
+
cond->continue_broadcast = CreateEvent(NULL, /* security */
|
|
93
|
+
FALSE, /* auto-reset */
|
|
94
|
+
FALSE, /* not signaled */
|
|
95
|
+
NULL); /* name */
|
|
96
|
+
if (!cond->continue_broadcast)
|
|
97
|
+
die("CreateEvent() failed");
|
|
98
|
+
|
|
99
|
+
return 0;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
int pthread_cond_destroy(pthread_cond_t *cond)
|
|
103
|
+
{
|
|
104
|
+
CloseHandle(cond->sema);
|
|
105
|
+
CloseHandle(cond->continue_broadcast);
|
|
106
|
+
DeleteCriticalSection(&cond->waiters_lock);
|
|
107
|
+
return 0;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
int pthread_cond_wait(pthread_cond_t *cond, CRITICAL_SECTION *mutex)
|
|
111
|
+
{
|
|
112
|
+
int last_waiter;
|
|
113
|
+
|
|
114
|
+
EnterCriticalSection(&cond->waiters_lock);
|
|
115
|
+
cond->waiters++;
|
|
116
|
+
LeaveCriticalSection(&cond->waiters_lock);
|
|
117
|
+
|
|
118
|
+
/*
|
|
119
|
+
* Unlock external mutex and wait for signal.
|
|
120
|
+
* NOTE: we've held mutex locked long enough to increment
|
|
121
|
+
* waiters count above, so there's no problem with
|
|
122
|
+
* leaving mutex unlocked before we wait on semaphore.
|
|
123
|
+
*/
|
|
124
|
+
LeaveCriticalSection(mutex);
|
|
125
|
+
|
|
126
|
+
/* let's wait - ignore return value */
|
|
127
|
+
WaitForSingleObject(cond->sema, INFINITE);
|
|
128
|
+
|
|
129
|
+
/*
|
|
130
|
+
* Decrease waiters count. If we are the last waiter, then we must
|
|
131
|
+
* notify the broadcasting thread that it can continue.
|
|
132
|
+
* But if we continued due to cond_signal, we do not have to do that
|
|
133
|
+
* because the signaling thread knows that only one waiter continued.
|
|
134
|
+
*/
|
|
135
|
+
EnterCriticalSection(&cond->waiters_lock);
|
|
136
|
+
cond->waiters--;
|
|
137
|
+
last_waiter = cond->was_broadcast && cond->waiters == 0;
|
|
138
|
+
LeaveCriticalSection(&cond->waiters_lock);
|
|
139
|
+
|
|
140
|
+
if (last_waiter) {
|
|
141
|
+
/*
|
|
142
|
+
* cond_broadcast was issued while mutex was held. This means
|
|
143
|
+
* that all other waiters have continued, but are contending
|
|
144
|
+
* for the mutex at the end of this function because the
|
|
145
|
+
* broadcasting thread did not leave cond_broadcast, yet.
|
|
146
|
+
* (This is so that it can be sure that each waiter has
|
|
147
|
+
* consumed exactly one slice of the semaphor.)
|
|
148
|
+
* The last waiter must tell the broadcasting thread that it
|
|
149
|
+
* can go on.
|
|
150
|
+
*/
|
|
151
|
+
SetEvent(cond->continue_broadcast);
|
|
152
|
+
/*
|
|
153
|
+
* Now we go on to contend with all other waiters for
|
|
154
|
+
* the mutex. Auf in den Kampf!
|
|
155
|
+
*/
|
|
156
|
+
}
|
|
157
|
+
/* lock external mutex again */
|
|
158
|
+
EnterCriticalSection(mutex);
|
|
159
|
+
|
|
160
|
+
return 0;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/*
|
|
164
|
+
* IMPORTANT: This implementation requires that pthread_cond_signal
|
|
165
|
+
* is called while the mutex is held that is used in the corresponding
|
|
166
|
+
* pthread_cond_wait calls!
|
|
167
|
+
*/
|
|
168
|
+
int pthread_cond_signal(pthread_cond_t *cond)
|
|
169
|
+
{
|
|
170
|
+
int have_waiters;
|
|
171
|
+
|
|
172
|
+
EnterCriticalSection(&cond->waiters_lock);
|
|
173
|
+
have_waiters = cond->waiters > 0;
|
|
174
|
+
LeaveCriticalSection(&cond->waiters_lock);
|
|
175
|
+
|
|
176
|
+
/*
|
|
177
|
+
* Signal only when there are waiters
|
|
178
|
+
*/
|
|
179
|
+
if (have_waiters)
|
|
180
|
+
return ReleaseSemaphore(cond->sema, 1, NULL) ?
|
|
181
|
+
0 : GetLastError();
|
|
182
|
+
else
|
|
183
|
+
return 0;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/*
|
|
187
|
+
* DOUBLY IMPORTANT: This implementation requires that pthread_cond_broadcast
|
|
188
|
+
* is called while the mutex is held that is used in the corresponding
|
|
189
|
+
* pthread_cond_wait calls!
|
|
190
|
+
*/
|
|
191
|
+
int pthread_cond_broadcast(pthread_cond_t *cond)
|
|
192
|
+
{
|
|
193
|
+
EnterCriticalSection(&cond->waiters_lock);
|
|
194
|
+
|
|
195
|
+
if ((cond->was_broadcast = cond->waiters > 0)) {
|
|
196
|
+
/* wake up all waiters */
|
|
197
|
+
ReleaseSemaphore(cond->sema, cond->waiters, NULL);
|
|
198
|
+
LeaveCriticalSection(&cond->waiters_lock);
|
|
199
|
+
/*
|
|
200
|
+
* At this point all waiters continue. Each one takes its
|
|
201
|
+
* slice of the semaphor. Now it's our turn to wait: Since
|
|
202
|
+
* the external mutex is held, no thread can leave cond_wait,
|
|
203
|
+
* yet. For this reason, we can be sure that no thread gets
|
|
204
|
+
* a chance to eat *more* than one slice. OTOH, it means
|
|
205
|
+
* that the last waiter must send us a wake-up.
|
|
206
|
+
*/
|
|
207
|
+
WaitForSingleObject(cond->continue_broadcast, INFINITE);
|
|
208
|
+
/*
|
|
209
|
+
* Since the external mutex is held, no thread can enter
|
|
210
|
+
* cond_wait, and, hence, it is safe to reset this flag
|
|
211
|
+
* without cond->waiters_lock held.
|
|
212
|
+
*/
|
|
213
|
+
cond->was_broadcast = 0;
|
|
214
|
+
} else {
|
|
215
|
+
LeaveCriticalSection(&cond->waiters_lock);
|
|
216
|
+
}
|
|
217
|
+
return 0;
|
|
218
|
+
}
|
numexpr/win32/pthread.h
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Code for simulating pthreads API on Windows. This is Git-specific,
|
|
3
|
+
* but it is enough for Numexpr needs too.
|
|
4
|
+
*
|
|
5
|
+
* Copyright (C) 2009 Andrzej K. Haczewski <ahaczewski@gmail.com>
|
|
6
|
+
*
|
|
7
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
* in the Software without restriction, including without limitation the rights
|
|
10
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
* furnished to do so, subject to the following conditions:
|
|
13
|
+
*
|
|
14
|
+
* The above copyright notice and this permission notice shall be included in
|
|
15
|
+
* all copies or substantial portions of the Software.
|
|
16
|
+
*
|
|
17
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
23
|
+
* THE SOFTWARE.
|
|
24
|
+
*
|
|
25
|
+
* DISCLAIMER: The implementation is Git-specific, it is subset of original
|
|
26
|
+
* Pthreads API, without lots of other features that Git doesn't use.
|
|
27
|
+
* Git also makes sure that the passed arguments are valid, so there's
|
|
28
|
+
* no need for double-checking.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
#ifndef PTHREAD_H
|
|
32
|
+
#define PTHREAD_H
|
|
33
|
+
|
|
34
|
+
#ifndef WIN32_LEAN_AND_MEAN
|
|
35
|
+
#define WIN32_LEAN_AND_MEAN
|
|
36
|
+
#endif
|
|
37
|
+
|
|
38
|
+
#include <windows.h>
|
|
39
|
+
|
|
40
|
+
#ifdef __cplusplus
|
|
41
|
+
extern "C" {
|
|
42
|
+
#endif
|
|
43
|
+
|
|
44
|
+
/*
|
|
45
|
+
* Defines that adapt Windows API threads to pthreads API
|
|
46
|
+
*/
|
|
47
|
+
#define pthread_mutex_t CRITICAL_SECTION
|
|
48
|
+
|
|
49
|
+
#define pthread_mutex_init(a,b) InitializeCriticalSection((a))
|
|
50
|
+
#define pthread_mutex_destroy(a) DeleteCriticalSection((a))
|
|
51
|
+
#define pthread_mutex_lock EnterCriticalSection
|
|
52
|
+
#define pthread_mutex_unlock LeaveCriticalSection
|
|
53
|
+
|
|
54
|
+
/*
|
|
55
|
+
* Implement simple condition variable for Windows threads, based on ACE
|
|
56
|
+
* implementation.
|
|
57
|
+
*
|
|
58
|
+
* See original implementation: http://bit.ly/1vkDjo
|
|
59
|
+
* ACE homepage: http://www.cse.wustl.edu/~schmidt/ACE.html
|
|
60
|
+
* See also: http://www.cse.wustl.edu/~schmidt/win32-cv-1.html
|
|
61
|
+
*/
|
|
62
|
+
typedef struct {
|
|
63
|
+
LONG waiters;
|
|
64
|
+
int was_broadcast;
|
|
65
|
+
CRITICAL_SECTION waiters_lock;
|
|
66
|
+
HANDLE sema;
|
|
67
|
+
HANDLE continue_broadcast;
|
|
68
|
+
} pthread_cond_t;
|
|
69
|
+
|
|
70
|
+
extern int pthread_cond_init(pthread_cond_t *cond, const void *unused);
|
|
71
|
+
extern int pthread_cond_destroy(pthread_cond_t *cond);
|
|
72
|
+
extern int pthread_cond_wait(pthread_cond_t *cond, CRITICAL_SECTION *mutex);
|
|
73
|
+
extern int pthread_cond_signal(pthread_cond_t *cond);
|
|
74
|
+
extern int pthread_cond_broadcast(pthread_cond_t *cond);
|
|
75
|
+
|
|
76
|
+
/*
|
|
77
|
+
* Simple thread creation implementation using pthread API
|
|
78
|
+
*/
|
|
79
|
+
typedef struct {
|
|
80
|
+
HANDLE handle;
|
|
81
|
+
void *(*start_routine)(void*);
|
|
82
|
+
void *arg;
|
|
83
|
+
} pthread_t;
|
|
84
|
+
|
|
85
|
+
extern int pthread_create(pthread_t *thread, const void *unused,
|
|
86
|
+
void *(*start_routine)(void*), void *arg);
|
|
87
|
+
|
|
88
|
+
/*
|
|
89
|
+
* To avoid the need of copying a struct, we use small macro wrapper to pass
|
|
90
|
+
* pointer to win32_pthread_join instead.
|
|
91
|
+
*/
|
|
92
|
+
#define pthread_join(a, b) win32_pthread_join(&(a), (b))
|
|
93
|
+
|
|
94
|
+
extern int win32_pthread_join(pthread_t *thread, void **value_ptr);
|
|
95
|
+
|
|
96
|
+
/*
|
|
97
|
+
* The POSIX signal system has a more developed interface than what's in
|
|
98
|
+
* Windows. We create a no-op shim layer to proivde enough of the API to
|
|
99
|
+
* pretend to support what's used when creating threads on POSIX systems.
|
|
100
|
+
*/
|
|
101
|
+
typedef int sigset_t;
|
|
102
|
+
enum sigop {
|
|
103
|
+
SIG_BLOCK,
|
|
104
|
+
SIG_UNBLOCK,
|
|
105
|
+
SIG_SETMASK
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
static inline int sigemptyset(sigset_t *sigs) { return 0; }
|
|
109
|
+
static inline int sigfillset(sigset_t *sigs) { return 0; }
|
|
110
|
+
static inline int sigaddset(sigset_t *sigs, int sig) { return 0; }
|
|
111
|
+
static inline int sigdelset(sigset_t *sigs, int sig) { return 0; }
|
|
112
|
+
static inline int pthread_sigmask(int how, sigset_t *newmask,
|
|
113
|
+
sigset_t *oldmask) { return 0; }
|
|
114
|
+
|
|
115
|
+
#ifdef __cplusplus
|
|
116
|
+
} // extern "C"
|
|
117
|
+
#endif
|
|
118
|
+
|
|
119
|
+
#endif /* PTHREAD_H */
|