Commit 73c6b0ea authored by Mark Florisson's avatar Mark Florisson

Allow nogil bounds checking for buffers/memoryviews

parent 49e7d509
...@@ -412,7 +412,7 @@ def put_assign_to_buffer(lhs_cname, rhs_cname, buf_entry, ...@@ -412,7 +412,7 @@ def put_assign_to_buffer(lhs_cname, rhs_cname, buf_entry,
code.putln("}") # Release stack code.putln("}") # Release stack
def put_buffer_lookup_code(entry, index_signeds, index_cnames, directives, def put_buffer_lookup_code(entry, index_signeds, index_cnames, directives,
pos, code, negative_indices): pos, code, negative_indices, in_nogil_context):
""" """
Generates code to process indices and calculate an offset into Generates code to process indices and calculate an offset into
a buffer. Returns a C string which gives a pointer which can be a buffer. Returns a C string which gives a pointer which can be
...@@ -455,9 +455,16 @@ def put_buffer_lookup_code(entry, index_signeds, index_cnames, directives, ...@@ -455,9 +455,16 @@ def put_buffer_lookup_code(entry, index_signeds, index_cnames, directives,
code.putln("if (%s) %s = %d;" % ( code.putln("if (%s) %s = %d;" % (
code.unlikely("%s >= %s%s" % (cname, cast, shape)), code.unlikely("%s >= %s%s" % (cname, cast, shape)),
tmp_cname, dim)) tmp_cname, dim))
code.globalstate.use_utility_code(raise_indexerror_code)
if in_nogil_context:
code.globalstate.use_utility_code(raise_indexerror_nogil)
func = '__Pyx_RaiseBufferIndexErrorNogil'
else:
code.globalstate.use_utility_code(raise_indexerror_code)
func = '__Pyx_RaiseBufferIndexError'
code.putln("if (%s) {" % code.unlikely("%s != -1" % tmp_cname)) code.putln("if (%s) {" % code.unlikely("%s != -1" % tmp_cname))
code.putln('__Pyx_RaiseBufferIndexError(%s);' % tmp_cname) code.putln('%s(%s);' % (func, tmp_cname))
code.putln(code.error_goto(pos)) code.putln(code.error_goto(pos))
code.putln('}') code.putln('}')
code.funcstate.release_temp(tmp_cname) code.funcstate.release_temp(tmp_cname)
...@@ -768,6 +775,7 @@ buffer_struct_declare_code = load_buffer_utility("BufferStructDeclare", ...@@ -768,6 +775,7 @@ buffer_struct_declare_code = load_buffer_utility("BufferStructDeclare",
# Utility function to set the right exception # Utility function to set the right exception
# The caller should immediately goto_error # The caller should immediately goto_error
raise_indexerror_code = load_buffer_utility("BufferIndexError") raise_indexerror_code = load_buffer_utility("BufferIndexError")
raise_indexerror_nogil = load_buffer_utility("BufferIndexErrorNogil")
parse_typestring_repeat_code = UtilityCode( parse_typestring_repeat_code = UtilityCode(
proto = """ proto = """
......
...@@ -2812,9 +2812,11 @@ class IndexNode(ExprNode): ...@@ -2812,9 +2812,11 @@ class IndexNode(ExprNode):
def nogil_check(self, env): def nogil_check(self, env):
if self.is_buffer_access or self.memslice_index or self.memslice_slice: if self.is_buffer_access or self.memslice_index or self.memslice_slice:
if not self.memslice_slice and env.directives['boundscheck']: if not self.memslice_slice and env.directives['boundscheck']:
error(self.pos, "Cannot check buffer index bounds without gil; use boundscheck(False) directive") # error(self.pos, "Cannot check buffer index bounds without gil; "
return # "use boundscheck(False) directive")
elif self.type.is_pyobject: warning(self.pos, "Use boundscheck(False) for faster access",
level=1)
if self.type.is_pyobject:
error(self.pos, "Cannot access buffer with object dtype without gil") error(self.pos, "Cannot access buffer with object dtype without gil")
return return
super(IndexNode, self).nogil_check(env) super(IndexNode, self).nogil_check(env)
...@@ -3084,7 +3086,8 @@ class IndexNode(ExprNode): ...@@ -3084,7 +3086,8 @@ class IndexNode(ExprNode):
index_cnames=index_temps, index_cnames=index_temps,
directives=code.globalstate.directives, directives=code.globalstate.directives,
pos=self.pos, code=code, pos=self.pos, code=code,
negative_indices=negative_indices) negative_indices=negative_indices,
in_nogil_context=self.in_nogil_context)
def put_memoryviewslice_slice_code(self, code): def put_memoryviewslice_slice_code(self, code):
"memslice[:]" "memslice[:]"
...@@ -9681,7 +9684,7 @@ static void __Pyx_RaiseUnboundMemoryviewSliceNogil(const char *varname) { ...@@ -9681,7 +9684,7 @@ static void __Pyx_RaiseUnboundMemoryviewSliceNogil(const char *varname) {
PyGILState_STATE gilstate = PyGILState_Ensure(); PyGILState_STATE gilstate = PyGILState_Ensure();
#endif #endif
__Pyx_RaiseUnboundLocalError(varname); __Pyx_RaiseUnboundLocalError(varname);
#ifdef WITH_THREAD") #ifdef WITH_THREAD
PyGILState_Release(gilstate); PyGILState_Release(gilstate);
#endif #endif
} }
......
...@@ -26,6 +26,22 @@ static void __Pyx_RaiseBufferIndexError(int axis) { ...@@ -26,6 +26,22 @@ static void __Pyx_RaiseBufferIndexError(int axis) {
"Out of bounds on buffer access (axis %d)", axis); "Out of bounds on buffer access (axis %d)", axis);
} }
/////////////// BufferIndexErrorNogil.proto ///////////////
//@requires: BufferIndexError
static void __Pyx_RaiseBufferIndexErrorNogil(int axis); /*proto*/
/////////////// BufferIndexErrorNogil ///////////////
static void __Pyx_RaiseBufferIndexErrorNogil(int axis) {
#ifdef WITH_THREAD
PyGILState_STATE gilstate = PyGILState_Ensure();
#endif
__Pyx_RaiseBufferIndexError(axis);
#ifdef WITH_THREAD
PyGILState_Release(gilstate);
#endif
}
/////////////// BufferFallbackError.proto /////////////// /////////////// BufferFallbackError.proto ///////////////
static void __Pyx_RaiseBufferFallbackError(void); /*proto*/ static void __Pyx_RaiseBufferFallbackError(void); /*proto*/
......
...@@ -1114,3 +1114,16 @@ def buffer_nogil(): ...@@ -1114,3 +1114,16 @@ def buffer_nogil():
with nogil: with nogil:
buf[1] = 10 buf[1] = 10
return buf[1] return buf[1]
@testcase
def buffer_nogil_oob():
"""
>>> buffer_nogil_oob()
Traceback (most recent call last):
...
IndexError: Out of bounds on buffer access (axis 0)
"""
cdef object[int] buf = IntMockBuffer(None, [1,2,3])
with nogil:
buf[5] = 10
return buf[1]
...@@ -209,6 +209,18 @@ def test_nogil_unbound_localerror(): ...@@ -209,6 +209,18 @@ def test_nogil_unbound_localerror():
with nogil: with nogil:
m[0] = 10 m[0] = 10
def test_nogil_oob():
"""
>>> test_nogil_oob()
Traceback (most recent call last):
...
IndexError: Out of bounds on buffer access (axis 0)
"""
cdef int[5] a
cdef int[:] m = a
with nogil:
m[5] = 1
def basic_struct(MyStruct[:] mslice): def basic_struct(MyStruct[:] mslice):
""" """
See also buffmt.pyx See also buffmt.pyx
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment