From cc08015c4a8e87d2aa9394e42c4075a3c20bf3e7 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 7 Jan 2011 21:25:51 -0500 Subject: [PATCH 01/11] Simplify the test for circular_buffer initialization --- src/test/run-pass/chan-poweroftwo.rs | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/test/run-pass/chan-poweroftwo.rs b/src/test/run-pass/chan-poweroftwo.rs index ce2751fab9b60..fc05d7cc0476a 100644 --- a/src/test/run-pass/chan-poweroftwo.rs +++ b/src/test/run-pass/chan-poweroftwo.rs @@ -1,6 +1,7 @@ // -*- rust -*- -// Regression test for circular_buffer initialization +// Regression tests for circular_buffer when using a unit +// that has a size that is not a power of two use std; @@ -8,22 +9,24 @@ import std.option; import std._uint; import std._vec; -// 12-byte unit for the channel buffer. Assuming that the default -// buffer size needs to hold 8 units, then the minimum buffer size -// needs to be 96. That's not a power of two so needs to be rounded up. +// A 12-byte unit to send over the channel type record = rec(i32 val1, i32 val2, i32 val3); -impure fn worker(chan[record] channel) { +// Assuming that the default buffer size needs to hold 8 units, +// then the minimum buffer size needs to be 96. That's not a +// power of two so needs to be rounded up. Don't trigger any +// assertions. +impure fn test_init() { + let port[record] myport = port(); + auto mychan = chan(myport); + let record val = rec(val1=0i32, val2=0i32, val3=0i32); - channel <| val; + + mychan <| val; } impure fn main() { - let port[record] myport = port(); - auto mychan = chan(myport); - - auto temp = spawn worker(mychan); - auto val <- myport; + test_init(); } // Local Variables: From d115492b3e7b0f86c7e65e34b2cb60e131b3a422 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 7 Jan 2011 21:29:32 -0500 Subject: [PATCH 02/11] Fix the check for growing the circular_buffer --- src/rt/circular_buffer.cpp | 2 +- src/test/run-pass/chan-poweroftwo.rs | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/rt/circular_buffer.cpp b/src/rt/circular_buffer.cpp index bdf251248cd82..77509abb06940 100644 --- a/src/rt/circular_buffer.cpp +++ b/src/rt/circular_buffer.cpp @@ -62,7 +62,7 @@ circular_buffer::enqueue(void *src) { I(dom, _unread <= _buffer_sz); // Grow if necessary. - if (_unread == _buffer_sz) { + if (_unread + unit_sz > _buffer_sz) { size_t new_buffer_sz = _buffer_sz << 1; I(dom, new_buffer_sz <= MAX_CIRCULAR_BUFFFER_SIZE); void *new_buffer = dom->malloc(new_buffer_sz); diff --git a/src/test/run-pass/chan-poweroftwo.rs b/src/test/run-pass/chan-poweroftwo.rs index fc05d7cc0476a..f3a4b9b5d660c 100644 --- a/src/test/run-pass/chan-poweroftwo.rs +++ b/src/test/run-pass/chan-poweroftwo.rs @@ -25,8 +25,22 @@ impure fn test_init() { mychan <| val; } +// Dump lots of items into the channel so it has to grow. +// Don't trigger any assertions. +impure fn test_grow() { + let port[record] myport = port(); + auto mychan = chan(myport); + + let record val = rec(val1=0i32, val2=0i32, val3=0i32); + + for each (uint i in _uint.range(0u, 100u)) { + mychan <| val; + } +} + impure fn main() { test_init(); + test_grow(); } // Local Variables: From ef04700dc7bc79bce1b74e6835884a279353386a Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 7 Jan 2011 22:13:52 -0500 Subject: [PATCH 03/11] Don't allow circular_buffer to shrink below it's initial size --- src/rt/circular_buffer.cpp | 4 +++- src/test/run-pass/chan-poweroftwo.rs | 10 ++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/rt/circular_buffer.cpp b/src/rt/circular_buffer.cpp index 77509abb06940..8f12a1154a5dd 100644 --- a/src/rt/circular_buffer.cpp +++ b/src/rt/circular_buffer.cpp @@ -121,13 +121,15 @@ circular_buffer::dequeue(void *dst) { } // Shrink if possible. - if (_buffer_sz >= INITIAL_CIRCULAR_BUFFFER_SIZE_IN_UNITS * unit_sz && + if (_buffer_sz > INITIAL_CIRCULAR_BUFFFER_SIZE_IN_UNITS * unit_sz && _unread <= _buffer_sz / 4) { dom->log(rust_log::MEM | rust_log::COMM, "circular_buffer is shrinking to %d bytes", _buffer_sz / 2); void *tmp = dom->malloc(_buffer_sz / 2); transfer(tmp); _buffer_sz >>= 1; + I(dom, _buffer_sz >= + next_power_of_two(INITIAL_CIRCULAR_BUFFFER_SIZE_IN_UNITS * unit_sz)); dom->free(_buffer); _buffer = (uint8_t *)tmp; _next = 0; diff --git a/src/test/run-pass/chan-poweroftwo.rs b/src/test/run-pass/chan-poweroftwo.rs index f3a4b9b5d660c..da9a62cc9f851 100644 --- a/src/test/run-pass/chan-poweroftwo.rs +++ b/src/test/run-pass/chan-poweroftwo.rs @@ -38,9 +38,19 @@ impure fn test_grow() { } } +// Don't allow the buffer to shrink below it's original size +impure fn test_shrink() { + let port[i8] myport = port(); + auto mychan = chan(myport); + + mychan <| 0i8; + auto x <- myport; +} + impure fn main() { test_init(); test_grow(); + test_shrink(); } // Local Variables: From c3f0c0e40ff530992da1aff52079bf58e93d303a Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 7 Jan 2011 22:22:35 -0500 Subject: [PATCH 04/11] Don't allow circular_buffer to shrink below its original size when unit_sz is not a power of two --- src/rt/circular_buffer.cpp | 9 ++++----- src/rt/circular_buffer.h | 3 +++ src/test/run-pass/chan-poweroftwo.rs | 20 ++++++++++++++++++-- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/rt/circular_buffer.cpp b/src/rt/circular_buffer.cpp index 8f12a1154a5dd..b458deddd9073 100644 --- a/src/rt/circular_buffer.cpp +++ b/src/rt/circular_buffer.cpp @@ -15,8 +15,9 @@ is_power_of_two(size_t value) { circular_buffer::circular_buffer(rust_dom *dom, size_t unit_sz) : dom(dom), unit_sz(unit_sz), - _buffer_sz(next_power_of_two( + _initial_sz(next_power_of_two( INITIAL_CIRCULAR_BUFFFER_SIZE_IN_UNITS * unit_sz)), + _buffer_sz(_initial_sz), _next(0), _unread(0), _buffer((uint8_t *)dom->calloc(_buffer_sz)) { @@ -121,15 +122,13 @@ circular_buffer::dequeue(void *dst) { } // Shrink if possible. - if (_buffer_sz > INITIAL_CIRCULAR_BUFFFER_SIZE_IN_UNITS * unit_sz && - _unread <= _buffer_sz / 4) { + if (_buffer_sz > _initial_sz && _unread <= _buffer_sz / 4) { dom->log(rust_log::MEM | rust_log::COMM, "circular_buffer is shrinking to %d bytes", _buffer_sz / 2); void *tmp = dom->malloc(_buffer_sz / 2); transfer(tmp); _buffer_sz >>= 1; - I(dom, _buffer_sz >= - next_power_of_two(INITIAL_CIRCULAR_BUFFFER_SIZE_IN_UNITS * unit_sz)); + I(dom, _initial_sz <= _buffer_sz); dom->free(_buffer); _buffer = (uint8_t *)tmp; _next = 0; diff --git a/src/rt/circular_buffer.h b/src/rt/circular_buffer.h index 9ddaba42dc9f6..40fb1e241e0b7 100644 --- a/src/rt/circular_buffer.h +++ b/src/rt/circular_buffer.h @@ -24,6 +24,9 @@ circular_buffer : public dom_owned { size_t size(); private: + // Initial size of the buffer in bytes. + size_t _initial_sz; + // Size of the buffer in bytes, should always be a power of two so that // modulo arithmetic (x % _buffer_sz) can optimized away with // (x & (_buffer_sz - 1)). diff --git a/src/test/run-pass/chan-poweroftwo.rs b/src/test/run-pass/chan-poweroftwo.rs index da9a62cc9f851..be7c18ad222a7 100644 --- a/src/test/run-pass/chan-poweroftwo.rs +++ b/src/test/run-pass/chan-poweroftwo.rs @@ -39,7 +39,7 @@ impure fn test_grow() { } // Don't allow the buffer to shrink below it's original size -impure fn test_shrink() { +impure fn test_shrink1() { let port[i8] myport = port(); auto mychan = chan(myport); @@ -47,10 +47,26 @@ impure fn test_shrink() { auto x <- myport; } +impure fn test_shrink2() { + let port[record] myport = port(); + auto mychan = chan(myport); + + let record val = rec(val1=0i32, val2=0i32, val3=0i32); + + for each (uint i in _uint.range(0u, 100u)) { + mychan <| val; + } + + for each (uint i in _uint.range(0u, 100u)) { + auto x <- myport; + } +} + impure fn main() { test_init(); test_grow(); - test_shrink(); + test_shrink1(); + test_shrink2(); } // Local Variables: From 89b0be55328e243724298770da5ed2db8d1fbf9a Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 7 Jan 2011 23:05:09 -0500 Subject: [PATCH 05/11] Remove tabs --- src/rt/circular_buffer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rt/circular_buffer.cpp b/src/rt/circular_buffer.cpp index b458deddd9073..c3b068ac3a0d4 100644 --- a/src/rt/circular_buffer.cpp +++ b/src/rt/circular_buffer.cpp @@ -128,7 +128,7 @@ circular_buffer::dequeue(void *dst) { void *tmp = dom->malloc(_buffer_sz / 2); transfer(tmp); _buffer_sz >>= 1; - I(dom, _initial_sz <= _buffer_sz); + I(dom, _initial_sz <= _buffer_sz); dom->free(_buffer); _buffer = (uint8_t *)tmp; _next = 0; From f61a24f0b4d9443764232a60be5ae039276b6008 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 7 Jan 2011 23:09:16 -0500 Subject: [PATCH 06/11] Fix circular_buffer growth when _next != 0 --- src/rt/circular_buffer.cpp | 13 ++++++----- src/test/run-pass/chan-poweroftwo.rs | 33 ++++++++++++++++++++++++---- 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/src/rt/circular_buffer.cpp b/src/rt/circular_buffer.cpp index c3b068ac3a0d4..54898fb77c240 100644 --- a/src/rt/circular_buffer.cpp +++ b/src/rt/circular_buffer.cpp @@ -63,7 +63,7 @@ circular_buffer::enqueue(void *src) { I(dom, _unread <= _buffer_sz); // Grow if necessary. - if (_unread + unit_sz > _buffer_sz) { + if (_next + _unread + unit_sz > _buffer_sz) { size_t new_buffer_sz = _buffer_sz << 1; I(dom, new_buffer_sz <= MAX_CIRCULAR_BUFFFER_SIZE); void *new_buffer = dom->malloc(new_buffer_sz); @@ -76,15 +76,16 @@ circular_buffer::enqueue(void *src) { dom->log(rust_log::MEM | rust_log::COMM, "circular_buffer enqueue " - "unread: %d, buffer_sz: %d, unit_sz: %d", - _unread, _buffer_sz, unit_sz); + "unread: %d, next: %d, buffer_sz: %d, unit_sz: %d", + _unread, _next, _buffer_sz, unit_sz); I(dom, is_power_of_two(_buffer_sz)); I(dom, _unread < _buffer_sz); - I(dom, _unread + unit_sz <= _buffer_sz); + I(dom, _next + _unread + unit_sz <= _buffer_sz); // Copy data size_t i = (_next + _unread) & (_buffer_sz - 1); + I(dom, i + unit_sz <= _buffer_sz); memcpy(&_buffer[i], src, unit_sz); _unread += unit_sz; @@ -106,8 +107,8 @@ circular_buffer::dequeue(void *dst) { dom->log(rust_log::MEM | rust_log::COMM, "circular_buffer dequeue " - "unread: %d, buffer_sz: %d, unit_sz: %d", - _unread, _buffer_sz, unit_sz); + "unread: %d, next: %d, buffer_sz: %d, unit_sz: %d", + _unread, _next, _buffer_sz, unit_sz); if (dst != NULL) { memcpy(dst, &_buffer[_next], unit_sz); diff --git a/src/test/run-pass/chan-poweroftwo.rs b/src/test/run-pass/chan-poweroftwo.rs index be7c18ad222a7..044d8238079d6 100644 --- a/src/test/run-pass/chan-poweroftwo.rs +++ b/src/test/run-pass/chan-poweroftwo.rs @@ -10,7 +10,7 @@ import std._uint; import std._vec; // A 12-byte unit to send over the channel -type record = rec(i32 val1, i32 val2, i32 val3); +type record = rec(u32 val1, u32 val2, u32 val3); // Assuming that the default buffer size needs to hold 8 units, // then the minimum buffer size needs to be 96. That's not a @@ -20,7 +20,7 @@ impure fn test_init() { let port[record] myport = port(); auto mychan = chan(myport); - let record val = rec(val1=0i32, val2=0i32, val3=0i32); + let record val = rec(val1=0u32, val2=0u32, val3=0u32); mychan <| val; } @@ -31,7 +31,7 @@ impure fn test_grow() { let port[record] myport = port(); auto mychan = chan(myport); - let record val = rec(val1=0i32, val2=0i32, val3=0i32); + let record val = rec(val1=0u32, val2=0u32, val3=0u32); for each (uint i in _uint.range(0u, 100u)) { mychan <| val; @@ -51,7 +51,7 @@ impure fn test_shrink2() { let port[record] myport = port(); auto mychan = chan(myport); - let record val = rec(val1=0i32, val2=0i32, val3=0i32); + let record val = rec(val1=0u32, val2=0u32, val3=0u32); for each (uint i in _uint.range(0u, 100u)) { mychan <| val; @@ -62,11 +62,36 @@ impure fn test_shrink2() { } } +// Test rotating the buffer when the unit size is not a power of two +impure fn test_rotate() { + let port[record] myport = port(); + auto mychan = chan(myport); + + let record val = rec(val1=0u32, val2=0u32, val3=0u32); + + for each (uint j in _uint.range(0u, 10u)) { + for each (uint i in _uint.range(0u, 10u)) { + let record val = rec(val1=i as u32, + val2=i as u32, + val3=i as u32); + mychan <| val; + } + + for each (uint i in _uint.range(0u, 10u)) { + auto x <- myport; + check (x.val1 == i as u32); + check (x.val2 == i as u32); + check (x.val3 == i as u32); + } + } +} + impure fn main() { test_init(); test_grow(); test_shrink1(); test_shrink2(); + test_rotate(); } // Local Variables: From fe47a264eb4290c1be0d64f31ab145e6025539ad Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 7 Jan 2011 23:14:16 -0500 Subject: [PATCH 07/11] Rename test to reflect that the circular_buffer runtime class is what's being tested --- src/test/run-pass/{chan-poweroftwo.rs => rt-circular-buffer.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/test/run-pass/{chan-poweroftwo.rs => rt-circular-buffer.rs} (100%) diff --git a/src/test/run-pass/chan-poweroftwo.rs b/src/test/run-pass/rt-circular-buffer.rs similarity index 100% rename from src/test/run-pass/chan-poweroftwo.rs rename to src/test/run-pass/rt-circular-buffer.rs From 9af6c7c9fe04a354a32471c0d2a294a38bf345b6 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 7 Jan 2011 23:35:29 -0500 Subject: [PATCH 08/11] Cleanup circular_buffer grow / shrink routines --- src/rt/circular_buffer.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/rt/circular_buffer.cpp b/src/rt/circular_buffer.cpp index 54898fb77c240..247e494f90f41 100644 --- a/src/rt/circular_buffer.cpp +++ b/src/rt/circular_buffer.cpp @@ -66,6 +66,8 @@ circular_buffer::enqueue(void *src) { if (_next + _unread + unit_sz > _buffer_sz) { size_t new_buffer_sz = _buffer_sz << 1; I(dom, new_buffer_sz <= MAX_CIRCULAR_BUFFFER_SIZE); + dom->log(rust_log::MEM | rust_log::COMM, + "circular_buffer is growing to %d bytes", new_buffer_sz); void *new_buffer = dom->malloc(new_buffer_sz); transfer(new_buffer); dom->free(_buffer); @@ -124,15 +126,16 @@ circular_buffer::dequeue(void *dst) { // Shrink if possible. if (_buffer_sz > _initial_sz && _unread <= _buffer_sz / 4) { + size_t new_buffer_sz = _buffer_sz >> 1; + I(dom, _initial_sz <= new_buffer_sz); dom->log(rust_log::MEM | rust_log::COMM, - "circular_buffer is shrinking to %d bytes", _buffer_sz / 2); - void *tmp = dom->malloc(_buffer_sz / 2); + "circular_buffer is shrinking to %d bytes", new_buffer_sz); + void *tmp = dom->malloc(new_buffer_sz); transfer(tmp); - _buffer_sz >>= 1; - I(dom, _initial_sz <= _buffer_sz); dom->free(_buffer); _buffer = (uint8_t *)tmp; _next = 0; + _buffer_sz = new_buffer_sz; } } From 3bcb332fa97a0019b8a6bcce4cebd7bd3d46b5ae Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 8 Jan 2011 13:45:18 -0500 Subject: [PATCH 09/11] Remove unused variable in circular_buffer tests --- src/test/run-pass/rt-circular-buffer.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/test/run-pass/rt-circular-buffer.rs b/src/test/run-pass/rt-circular-buffer.rs index 044d8238079d6..d582a4157db78 100644 --- a/src/test/run-pass/rt-circular-buffer.rs +++ b/src/test/run-pass/rt-circular-buffer.rs @@ -67,8 +67,6 @@ impure fn test_rotate() { let port[record] myport = port(); auto mychan = chan(myport); - let record val = rec(val1=0u32, val2=0u32, val3=0u32); - for each (uint j in _uint.range(0u, 10u)) { for each (uint i in _uint.range(0u, 10u)) { let record val = rec(val1=i as u32, From 28f91d89d8fb6e91b7029b19958ff4119bb2479b Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 8 Jan 2011 18:19:55 -0500 Subject: [PATCH 10/11] Remove the assumption that circular_buffer's buffer has a power of two size It was not obvious how to make this implementation work when the unit size was not also a power of two, so for now just make the buffer size a multiple of the unit size so it can pass all the tests. --- src/rt/circular_buffer.cpp | 76 +++++++++++++++++-------- src/rt/circular_buffer.h | 22 ++++--- src/test/run-pass/rt-circular-buffer.rs | 20 +++++++ 3 files changed, 85 insertions(+), 33 deletions(-) diff --git a/src/rt/circular_buffer.cpp b/src/rt/circular_buffer.cpp index 247e494f90f41..142e7574743cb 100644 --- a/src/rt/circular_buffer.cpp +++ b/src/rt/circular_buffer.cpp @@ -4,20 +4,10 @@ #include "rust_internal.h" -bool -is_power_of_two(size_t value) { - if (value > 0) { - return (value & (value - 1)) == 0; - } - return false; -} - circular_buffer::circular_buffer(rust_dom *dom, size_t unit_sz) : dom(dom), unit_sz(unit_sz), - _initial_sz(next_power_of_two( - INITIAL_CIRCULAR_BUFFFER_SIZE_IN_UNITS * unit_sz)), - _buffer_sz(_initial_sz), + _buffer_sz(INITIAL_CIRCULAR_BUFFER_SIZE_IN_UNITS * unit_sz), _next(0), _unread(0), _buffer((uint8_t *)dom->calloc(_buffer_sz)) { @@ -46,11 +36,27 @@ circular_buffer::~circular_buffer() { void circular_buffer::transfer(void *dst) { I(dom, dst); - I(dom, is_power_of_two(_buffer_sz)); + I(dom, _unread <= _buffer_sz); + uint8_t *ptr = (uint8_t *) dst; - for (size_t i = 0; i < _unread; i += unit_sz) { - memcpy(&ptr[i], &_buffer[(_next + i) & (_buffer_sz - 1)], unit_sz); + + // First copy from _next to either the end of the unread + // items or the end of the buffer + size_t head_sz; + if (_next + _unread > _buffer_sz) { + head_sz = _buffer_sz - _next; + } else { + head_sz = _unread; } + I(dom, _next + head_sz <= _buffer_sz); + I(dom, _next < _buffer_sz); + memcpy(ptr, _buffer + _next, head_sz); + + // Then copy any other items from the beginning of the buffer + I(dom, _unread >= head_sz); + size_t tail_sz = _unread - head_sz; + I(dom, tail_sz <= _buffer_sz); + memcpy(ptr + head_sz, _buffer, tail_sz); } /** @@ -61,11 +67,12 @@ void circular_buffer::enqueue(void *src) { I(dom, src); I(dom, _unread <= _buffer_sz); + I(dom, _buffer); // Grow if necessary. - if (_next + _unread + unit_sz > _buffer_sz) { - size_t new_buffer_sz = _buffer_sz << 1; - I(dom, new_buffer_sz <= MAX_CIRCULAR_BUFFFER_SIZE); + if (_unread == _buffer_sz) { + size_t new_buffer_sz = _buffer_sz * 2; + I(dom, new_buffer_sz <= MAX_CIRCULAR_BUFFER_SIZE); dom->log(rust_log::MEM | rust_log::COMM, "circular_buffer is growing to %d bytes", new_buffer_sz); void *new_buffer = dom->malloc(new_buffer_sz); @@ -81,12 +88,19 @@ circular_buffer::enqueue(void *src) { "unread: %d, next: %d, buffer_sz: %d, unit_sz: %d", _unread, _next, _buffer_sz, unit_sz); - I(dom, is_power_of_two(_buffer_sz)); I(dom, _unread < _buffer_sz); - I(dom, _next + _unread + unit_sz <= _buffer_sz); + I(dom, _unread + unit_sz <= _buffer_sz); // Copy data - size_t i = (_next + _unread) & (_buffer_sz - 1); + size_t i; + if (_next + _unread < _buffer_sz) { + i = _next + _unread; + } else { + I(dom, _next >= unit_sz); + i = _next + _unread - _buffer_sz; + I(dom, i <= _next - unit_sz); + } + I(dom, i + unit_sz <= _buffer_sz); memcpy(&_buffer[i], src, unit_sz); _unread += unit_sz; @@ -112,6 +126,7 @@ circular_buffer::dequeue(void *dst) { "unread: %d, next: %d, buffer_sz: %d, unit_sz: %d", _unread, _next, _buffer_sz, unit_sz); + I(dom, _next + unit_sz <= _buffer_sz); if (dst != NULL) { memcpy(dst, &_buffer[_next], unit_sz); } @@ -119,15 +134,16 @@ circular_buffer::dequeue(void *dst) { "shifted data from index %d", _next); _unread -= unit_sz; _next += unit_sz; - I(dom, _next <= _buffer_sz); if (_next == _buffer_sz) { _next = 0; } // Shrink if possible. - if (_buffer_sz > _initial_sz && _unread <= _buffer_sz / 4) { - size_t new_buffer_sz = _buffer_sz >> 1; - I(dom, _initial_sz <= new_buffer_sz); + if (_buffer_sz > INITIAL_CIRCULAR_BUFFER_SIZE_IN_UNITS * unit_sz && + _unread <= _buffer_sz / 4) { + size_t new_buffer_sz = _buffer_sz / 2; + I(dom, + INITIAL_CIRCULAR_BUFFER_SIZE_IN_UNITS * unit_sz <= new_buffer_sz); dom->log(rust_log::MEM | rust_log::COMM, "circular_buffer is shrinking to %d bytes", new_buffer_sz); void *tmp = dom->malloc(new_buffer_sz); @@ -137,7 +153,6 @@ circular_buffer::dequeue(void *dst) { _next = 0; _buffer_sz = new_buffer_sz; } - } uint8_t * @@ -154,3 +169,14 @@ size_t circular_buffer::size() { return _unread; } + +// +// Local Variables: +// mode: C++ +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'"; +// End: +// diff --git a/src/rt/circular_buffer.h b/src/rt/circular_buffer.h index 40fb1e241e0b7..d44721b5d2253 100644 --- a/src/rt/circular_buffer.h +++ b/src/rt/circular_buffer.h @@ -7,8 +7,8 @@ class circular_buffer : public dom_owned { - static const size_t INITIAL_CIRCULAR_BUFFFER_SIZE_IN_UNITS = 8; - static const size_t MAX_CIRCULAR_BUFFFER_SIZE = 1 << 24; + static const size_t INITIAL_CIRCULAR_BUFFER_SIZE_IN_UNITS = 8; + static const size_t MAX_CIRCULAR_BUFFER_SIZE = 1 << 24; public: rust_dom *dom; @@ -24,12 +24,7 @@ circular_buffer : public dom_owned { size_t size(); private: - // Initial size of the buffer in bytes. - size_t _initial_sz; - - // Size of the buffer in bytes, should always be a power of two so that - // modulo arithmetic (x % _buffer_sz) can optimized away with - // (x & (_buffer_sz - 1)). + // Size of the buffer in bytes. size_t _buffer_sz; // Byte offset within the buffer where to read the next unit of data. @@ -42,4 +37,15 @@ circular_buffer : public dom_owned { uint8_t *_buffer; }; +// +// Local Variables: +// mode: C++ +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'"; +// End: +// + #endif /* CIRCULAR_BUFFER_H */ diff --git a/src/test/run-pass/rt-circular-buffer.rs b/src/test/run-pass/rt-circular-buffer.rs index d582a4157db78..542846b4dcdc9 100644 --- a/src/test/run-pass/rt-circular-buffer.rs +++ b/src/test/run-pass/rt-circular-buffer.rs @@ -67,6 +67,25 @@ impure fn test_rotate() { let port[record] myport = port(); auto mychan = chan(myport); + for each (uint i in _uint.range(0u, 100u)) { + auto val = rec(val1=i as u32, + val2=i as u32, + val3=i as u32); + mychan <| val; + + auto x <- myport; + check (x.val1 == i as u32); + check (x.val2 == i as u32); + check (x.val3 == i as u32); + } +} + +// Test rotating and growing the buffer when +// the unit size is not a power of two +impure fn test_rotate_grow() { + let port[record] myport = port(); + auto mychan = chan(myport); + for each (uint j in _uint.range(0u, 10u)) { for each (uint i in _uint.range(0u, 10u)) { let record val = rec(val1=i as u32, @@ -90,6 +109,7 @@ impure fn main() { test_shrink1(); test_shrink2(); test_rotate(); + test_rotate_grow(); } // Local Variables: From 5cc6cf6187d4e24d6f53411ce6207b33e8e44a09 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 8 Jan 2011 18:52:37 -0500 Subject: [PATCH 11/11] Cleanup circular_buffer --- src/rt/circular_buffer.cpp | 91 ++++++++++++++++++++++---------------- src/rt/circular_buffer.h | 4 ++ 2 files changed, 56 insertions(+), 39 deletions(-) diff --git a/src/rt/circular_buffer.cpp b/src/rt/circular_buffer.cpp index 142e7574743cb..a466724702caf 100644 --- a/src/rt/circular_buffer.cpp +++ b/src/rt/circular_buffer.cpp @@ -7,10 +7,10 @@ circular_buffer::circular_buffer(rust_dom *dom, size_t unit_sz) : dom(dom), unit_sz(unit_sz), - _buffer_sz(INITIAL_CIRCULAR_BUFFER_SIZE_IN_UNITS * unit_sz), + _buffer_sz(initial_size()), _next(0), _unread(0), - _buffer((uint8_t *)dom->calloc(_buffer_sz)) { + _buffer((uint8_t *)dom->malloc(_buffer_sz)) { A(dom, unit_sz, "Unit size must be larger than zero."); @@ -30,6 +30,12 @@ circular_buffer::~circular_buffer() { dom->free(_buffer); } +size_t +circular_buffer::initial_size() { + I(dom, unit_sz > 0); + return INITIAL_CIRCULAR_BUFFER_SIZE_IN_UNITS * unit_sz; +} + /** * Copies the unread data from this buffer to the "dst" address. */ @@ -43,19 +49,18 @@ circular_buffer::transfer(void *dst) { // First copy from _next to either the end of the unread // items or the end of the buffer size_t head_sz; - if (_next + _unread > _buffer_sz) { - head_sz = _buffer_sz - _next; - } else { + if (_next + _unread <= _buffer_sz) { head_sz = _unread; + } else { + head_sz = _buffer_sz - _next; } I(dom, _next + head_sz <= _buffer_sz); - I(dom, _next < _buffer_sz); memcpy(ptr, _buffer + _next, head_sz); // Then copy any other items from the beginning of the buffer I(dom, _unread >= head_sz); size_t tail_sz = _unread - head_sz; - I(dom, tail_sz <= _buffer_sz); + I(dom, head_sz + tail_sz <= _buffer_sz); memcpy(ptr + head_sz, _buffer, tail_sz); } @@ -71,16 +76,7 @@ circular_buffer::enqueue(void *src) { // Grow if necessary. if (_unread == _buffer_sz) { - size_t new_buffer_sz = _buffer_sz * 2; - I(dom, new_buffer_sz <= MAX_CIRCULAR_BUFFER_SIZE); - dom->log(rust_log::MEM | rust_log::COMM, - "circular_buffer is growing to %d bytes", new_buffer_sz); - void *new_buffer = dom->malloc(new_buffer_sz); - transfer(new_buffer); - dom->free(_buffer); - _buffer = (uint8_t *)new_buffer; - _next = 0; - _buffer_sz = new_buffer_sz; + grow(); } dom->log(rust_log::MEM | rust_log::COMM, @@ -92,21 +88,21 @@ circular_buffer::enqueue(void *src) { I(dom, _unread + unit_sz <= _buffer_sz); // Copy data - size_t i; - if (_next + _unread < _buffer_sz) { - i = _next + _unread; - } else { + size_t dst_idx = _next + _unread; + I(dom, dst_idx >= _buffer_sz || dst_idx + unit_sz <= _buffer_sz); + if (dst_idx >= _buffer_sz) { + dst_idx -= _buffer_sz; + I(dom, _next >= unit_sz); - i = _next + _unread - _buffer_sz; - I(dom, i <= _next - unit_sz); + I(dom, dst_idx <= _next - unit_sz); } - I(dom, i + unit_sz <= _buffer_sz); - memcpy(&_buffer[i], src, unit_sz); + I(dom, dst_idx + unit_sz <= _buffer_sz); + memcpy(&_buffer[dst_idx], src, unit_sz); _unread += unit_sz; dom->log(rust_log::MEM | rust_log::COMM, - "circular_buffer pushed data at index: %d", i); + "circular_buffer pushed data at index: %d", dst_idx); } /** @@ -139,22 +135,39 @@ circular_buffer::dequeue(void *dst) { } // Shrink if possible. - if (_buffer_sz > INITIAL_CIRCULAR_BUFFER_SIZE_IN_UNITS * unit_sz && - _unread <= _buffer_sz / 4) { - size_t new_buffer_sz = _buffer_sz / 2; - I(dom, - INITIAL_CIRCULAR_BUFFER_SIZE_IN_UNITS * unit_sz <= new_buffer_sz); - dom->log(rust_log::MEM | rust_log::COMM, - "circular_buffer is shrinking to %d bytes", new_buffer_sz); - void *tmp = dom->malloc(new_buffer_sz); - transfer(tmp); - dom->free(_buffer); - _buffer = (uint8_t *)tmp; - _next = 0; - _buffer_sz = new_buffer_sz; + if (_buffer_sz > initial_size() && _unread <= _buffer_sz / 4) { + shrink(); } } +void +circular_buffer::grow() { + size_t new_buffer_sz = _buffer_sz * 2; + I(dom, new_buffer_sz <= MAX_CIRCULAR_BUFFER_SIZE); + dom->log(rust_log::MEM | rust_log::COMM, + "circular_buffer is growing to %d bytes", new_buffer_sz); + void *new_buffer = dom->malloc(new_buffer_sz); + transfer(new_buffer); + dom->free(_buffer); + _buffer = (uint8_t *)new_buffer; + _next = 0; + _buffer_sz = new_buffer_sz; +} + +void +circular_buffer::shrink() { + size_t new_buffer_sz = _buffer_sz / 2; + I(dom, initial_size() <= new_buffer_sz); + dom->log(rust_log::MEM | rust_log::COMM, + "circular_buffer is shrinking to %d bytes", new_buffer_sz); + void *new_buffer = dom->malloc(new_buffer_sz); + transfer(new_buffer); + dom->free(_buffer); + _buffer = (uint8_t *)new_buffer; + _next = 0; + _buffer_sz = new_buffer_sz; +} + uint8_t * circular_buffer::peek() { return &_buffer[_next]; diff --git a/src/rt/circular_buffer.h b/src/rt/circular_buffer.h index d44721b5d2253..cdd0b03b09107 100644 --- a/src/rt/circular_buffer.h +++ b/src/rt/circular_buffer.h @@ -24,6 +24,10 @@ circular_buffer : public dom_owned { size_t size(); private: + size_t initial_size(); + void grow(); + void shrink(); + // Size of the buffer in bytes. size_t _buffer_sz;