Author: Armin Rigo <[email protected]>
Branch: gc-incminimark-pinning
Changeset: r74070:8e1098ac3210
Date: 2014-10-22 12:42 +0200
http://bitbucket.org/pypy/pypy/changeset/8e1098ac3210/
Log: Change alloc_buffer() to once again use prebuilt GC strings, pinned.
diff --git a/rpython/rtyper/lltypesystem/rffi.py
b/rpython/rtyper/lltypesystem/rffi.py
--- a/rpython/rtyper/lltypesystem/rffi.py
+++ b/rpython/rtyper/lltypesystem/rffi.py
@@ -793,6 +793,7 @@
return cast(TYPEP, data_start), pinned, False
# ^^^ already nonmovable. Therefore it's not raw allocated nor
# pinned.
+ get_nonmovingbuffer._always_inline_ = 'try' # get rid of the returned tuple
get_nonmovingbuffer._annenforceargs_ = [strtype]
# (str, char*, bool, bool) -> None
@@ -812,52 +813,67 @@
keepalive_until_here(data)
free_nonmovingbuffer._annenforceargs_ = [strtype, None, bool, bool]
- # int -> (char*, str)
+ # int -> (char*, str, int)
def alloc_buffer(count):
"""
- Returns a (raw_buffer, gc_buffer) pair, allocated with count bytes.
+ Returns a (raw_buffer, gc_buffer, case_num) triple,
+ allocated with count bytes.
The raw_buffer can be safely passed to a native function which expects
it to not move. Call str_from_buffer with the returned values to get a
safe high-level string. When the garbage collector cooperates, this
allows for the process to be performed without an extra copy.
Make sure to call keep_buffer_alive_until_here on the returned values.
"""
- raw_buf = lltype.malloc(TYPEP.TO, count, flavor='raw')
- return raw_buf, lltype.nullptr(STRTYPE)
+ new_buf = lltype.malloc(STRTYPE, count)
+ pinned = 0
+ if rgc.can_move(new_buf):
+ if rgc.pin(new_buf):
+ pinned = 1
+ else:
+ raw_buf = lltype.malloc(TYPEP.TO, count, flavor='raw')
+ return raw_buf, new_buf, 2
+ #
+ # following code is executed if:
+ # - rgc.can_move(data) and rgc.pin(data) both returned true
+ # - rgc.can_move(data) returned false
+ data_start = cast_ptr_to_adr(new_buf) + \
+ offsetof(STRTYPE, 'chars') + itemoffsetof(STRTYPE.chars, 0)
+ return cast(TYPEP, data_start), new_buf, pinned
alloc_buffer._always_inline_ = 'try' # to get rid of the returned tuple
alloc_buffer._annenforceargs_ = [int]
# (char*, str, int, int) -> None
@jit.dont_look_inside
- @enforceargs(None, None, int, int)
- def str_from_buffer(raw_buf, gc_buf, allocated_size, needed_size):
+ @enforceargs(None, None, int, int, int)
+ def str_from_buffer(raw_buf, gc_buf, case_num, allocated_size,
needed_size):
"""
Converts from a pair returned by alloc_buffer to a high-level string.
The returned string will be truncated to needed_size.
"""
assert allocated_size >= needed_size
+ if allocated_size != needed_size:
+ from rpython.rtyper.lltypesystem.lloperation import llop
+ if llop.shrink_array(lltype.Bool, gc_buf, needed_size):
+ pass # now 'gc_buf' is smaller
+ else:
+ gc_buf = lltype.malloc(STRTYPE, needed_size)
+ case_num = 2
+ if case_num == 2:
+ copy_raw_to_string(raw_buf, gc_buf, 0, needed_size)
+ return hlstrtype(gc_buf)
- if gc_buf and (allocated_size == needed_size):
- return hlstrtype(gc_buf)
-
- new_buf = lltype.malloc(STRTYPE, needed_size)
- if gc_buf:
- copy_string_contents(gc_buf, new_buf, 0, 0, needed_size)
- else:
- copy_raw_to_string(raw_buf, new_buf, 0, needed_size)
- return hlstrtype(new_buf)
-
- # (char*, str) -> None
+ # (char*, str, int) -> None
@jit.dont_look_inside
- def keep_buffer_alive_until_here(raw_buf, gc_buf):
+ def keep_buffer_alive_until_here(raw_buf, gc_buf, case_num):
"""
Keeps buffers alive or frees temporary buffers created by alloc_buffer.
This must be called after a call to alloc_buffer, usually in a
try/finally block.
"""
- if gc_buf:
- keepalive_until_here(gc_buf)
- elif raw_buf:
+ keepalive_until_here(gc_buf)
+ if case_num == 1:
+ rgc.unpin(gc_buf)
+ if case_num == 2:
lltype.free(raw_buf, flavor='raw')
# char* -> str, with an upper bound on the length in case there is no \x00
@@ -1144,23 +1160,25 @@
def __init__(self, size):
self.size = size
def __enter__(self):
- self.raw, self.gc_buf = alloc_buffer(self.size)
+ self.raw, self.gc_buf, self.case_num = alloc_buffer(self.size)
return self
def __exit__(self, *args):
- keep_buffer_alive_until_here(self.raw, self.gc_buf)
+ keep_buffer_alive_until_here(self.raw, self.gc_buf, self.case_num)
def str(self, length):
- return str_from_buffer(self.raw, self.gc_buf, self.size, length)
+ return str_from_buffer(self.raw, self.gc_buf, self.case_num,
+ self.size, length)
class scoped_alloc_unicodebuffer:
def __init__(self, size):
self.size = size
def __enter__(self):
- self.raw, self.gc_buf = alloc_unicodebuffer(self.size)
+ self.raw, self.gc_buf, self.case_num = alloc_unicodebuffer(self.size)
return self
def __exit__(self, *args):
- keep_unicodebuffer_alive_until_here(self.raw, self.gc_buf)
+ keep_unicodebuffer_alive_until_here(self.raw, self.gc_buf,
self.case_num)
def str(self, length):
- return unicode_from_buffer(self.raw, self.gc_buf, self.size, length)
+ return unicode_from_buffer(self.raw, self.gc_buf, self.case_num,
+ self.size, length)
# You would have to have a *huge* amount of data for this to block long enough
# to be worth it to release the GIL.
diff --git a/rpython/rtyper/lltypesystem/test/test_rffi.py
b/rpython/rtyper/lltypesystem/test/test_rffi.py
--- a/rpython/rtyper/lltypesystem/test/test_rffi.py
+++ b/rpython/rtyper/lltypesystem/test/test_rffi.py
@@ -498,13 +498,10 @@
def test_nonmoving(self):
d = 'non-moving data stuff'
def f():
- raw_buf, gc_buf = alloc_buffer(len(d))
- try:
+ with scoped_alloc_buffer(len(d)) as s:
for i in range(len(d)):
- raw_buf[i] = d[i]
- return str_from_buffer(raw_buf, gc_buf, len(d), len(d)-1)
- finally:
- keep_buffer_alive_until_here(raw_buf, gc_buf)
+ s.raw[i] = d[i]
+ return s.str(len(d)-1)
assert f() == d[:-1]
fn = self.compile(f, [], gcpolicy='ref')
assert fn() == d[:-1]
@@ -512,14 +509,10 @@
def test_nonmoving_unicode(self):
d = u'non-moving data'
def f():
- raw_buf, gc_buf = alloc_unicodebuffer(len(d))
- try:
+ with scoped_alloc_unicodebuffer(len(d)) as s:
for i in range(len(d)):
- raw_buf[i] = d[i]
- return (unicode_from_buffer(raw_buf, gc_buf, len(d), len(d)-1)
- .encode('ascii'))
- finally:
- keep_unicodebuffer_alive_until_here(raw_buf, gc_buf)
+ s.raw[i] = d[i]
+ return s.str(len(d)-1).encode('ascii')
assert f() == d[:-1]
fn = self.compile(f, [], gcpolicy='ref')
assert fn() == d[:-1]
@@ -561,6 +554,27 @@
res = fn(expected_extra_mallocs=range(30))
assert res == 32 * len(d)
+ def test_nonmovingbuffer_incminimark(self):
+ d = 'cool data'
+ def f():
+ counter = 0
+ for n in range(32):
+ buf, is_pinned, is_raw = get_nonmovingbuffer(d)
+ try:
+ for i in range(len(d)):
+ if buf[i] == d[i]:
+ counter += 1
+ finally:
+ free_nonmovingbuffer(d, buf, is_pinned, is_raw)
+ return counter
+ fn = self.compile(f, [], gcpolicy='incminimark')
+ # The incminimark gc uses raw_malloc for its internal data structs
+ # but hopefully less than 30 times. So we should get < 30 leaks
+ # unless the get_nonmovingbuffer()/free_nonmovingbuffer() pair
+ # leaks at each iteration. This is what the following line checks.
+ res = fn(expected_extra_mallocs=range(30))
+ assert res == 32 * len(d)
+
class TestRffiInternals:
def test_struct_create(self):
X = CStruct('xx', ('one', INT))
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit