Mercurial > octave
changeset 23775:7b43a96c2179
Replace OCTAVE_LOCAL_BUFFER code with std::unique_ptr (bug #48793).
* oct-locbuf.h: Replace handwritten code with call to std::unique_ptr.
Rewrite OCTAVE_LOCAL_BUFFER_INIT to use std::fill_n rather than for loop
with temporary variables that could clash with existing code.
* liboctave/util/oct-locbuf.cc: Delete file.
* liboctave/util/module.mk: Remove oct-locbuf.cc from build system.
* interpreter.cc (interpreter::cleanup): Remove code to clean up chunk_buffer.
author | Rik <rik@octave.org> |
---|---|
date | Thu, 20 Jul 2017 09:17:03 -0700 |
parents | 41795b504a8b |
children | a0b7a29338d5 |
files | libinterp/corefcn/interpreter.cc liboctave/util/module.mk liboctave/util/oct-locbuf.cc liboctave/util/oct-locbuf.h |
diffstat | 4 files changed, 12 insertions(+), 341 deletions(-) [+] |
line wrap: on
line diff
--- a/libinterp/corefcn/interpreter.cc Fri Jul 14 16:05:59 2017 -0400 +++ b/libinterp/corefcn/interpreter.cc Thu Jul 20 09:17:03 2017 -0700 @@ -1162,8 +1162,6 @@ // is unloaded. // // OCTAVE_SAFE_CALL (singleton_cleanup_list::cleanup, ()); - - OCTAVE_SAFE_CALL (chunk_buffer::clear, ()); } tree_evaluator& interpreter::get_evaluator (void)
--- a/liboctave/util/module.mk Fri Jul 14 16:05:59 2017 -0400 +++ b/liboctave/util/module.mk Thu Jul 20 09:17:03 2017 -0700 @@ -81,7 +81,6 @@ %reldir%/oct-base64.cc \ %reldir%/oct-glob.cc \ %reldir%/oct-inttypes.cc \ - %reldir%/oct-locbuf.cc \ %reldir%/oct-mutex.cc \ %reldir%/oct-shlib.cc \ %reldir%/oct-sparse.cc \
--- a/liboctave/util/oct-locbuf.cc Fri Jul 14 16:05:59 2017 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,155 +0,0 @@ -/* - -Copyright (C) 2008-2017 Jaroslav Hajek - -This file is part of Octave. - -Octave is free software; you can redistribute it and/or modify it -under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 3 of the License, or -(at your option) any later version. - -Octave is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Octave; see the file COPYING. If not, see -<http://www.gnu.org/licenses/>. - -*/ - -#if defined (HAVE_CONFIG_H) -# include "config.h" -#endif - -#include <iostream> - -#include "lo-error.h" -#include "oct-locbuf.h" - -// FIXME: Maybe we should querying for available physical memory? - -#if ! defined (OCTAVE_LOCBUF_CHUNKSIZE_MB) -# define OCTAVE_LOCBUF_CHUNKSIZE_MB 32 -#endif - -namespace octave -{ -// Each chunk will be at least this big. - - const size_t chunk_buffer::chunk_size = - static_cast<size_t> (OCTAVE_LOCBUF_CHUNKSIZE_MB) << 20; - - char *chunk_buffer::top = nullptr; - char *chunk_buffer::chunk = nullptr; - size_t chunk_buffer::left = 0; - size_t chunk_buffer::active = 0; - - chunk_buffer::chunk_buffer (size_t size) : cnk (0), dat (0) - { - // Alignment mask. The size of double or long int, whichever is - // greater. All data will be aligned to this size. If it's not - // enough for a type, that type should not be declared as POD. - - static const size_t align_mask = (sizeof (long) < sizeof (double) - ? sizeof (double) - : sizeof (long)) - 1; - - // Always allocate, even for zero-size buffers so that local buffers - // always have valid addresses, same as for directly using operator - // new. - - active++; - - // Align size. Note that size_t is unsigned, so size-1 must correctly - // wrap around. - - size = ((size - 1) | align_mask) + 1; - - if (size > left) - { - // Big buffers (> 1/8 chunk) will be allocated as stand-alone and - // won't disrupt the chain. - - if (size > chunk_size >> 3) - { - // Use new [] to get std::bad_alloc if out of memory. - - dat = new char [size]; - return; - } - - dat = new char [chunk_size]; - chunk = top = dat; - left = chunk_size; - } - - // Now allocate memory from the chunk and update state. - - cnk = chunk; - dat = top; - left -= size; - top += size; - } - - chunk_buffer::~chunk_buffer (void) - { - active--; - - if (cnk == chunk) - { - // Our chunk is still the active one. Just restore the state. - - left += top - dat; - top = dat; - } - else - { - if (cnk) - { - // Responsible for deletion. - - delete [] chunk; - chunk = cnk; - top = dat; - - // FIXME: the following calcuation of remaining data will - // only work if each chunk has the same chunk_size. - - left = chunk_size - (dat - cnk); - } - else - { - // We were a stand-alone buffer. - - delete [] dat; - } - } - } - - // Clear the final chunk of allocated memory. - - void - chunk_buffer::clear (void) - { - if (active == 0) - { - delete [] chunk; - chunk = 0; - top = 0; - left = 0; - } - else - { - // FIXME: Doesn't this situation represent a programming error of - // some kind? If so, maybe this should be a fatal error? - - (*current_liboctave_warning_with_id_handler) - ("Octave:local-buffer-inconsistency", - "chunk_buffer::clear: %d active allocations remain!", - active); - } - } -}
--- a/liboctave/util/oct-locbuf.h Fri Jul 14 16:05:59 2017 -0400 +++ b/liboctave/util/oct-locbuf.h Thu Jul 20 09:17:03 2017 -0700 @@ -26,198 +26,27 @@ #include "octave-config.h" #include <cstddef> -#include "oct-cmplx.h" - -namespace octave -{ - // The default local buffer simply encapsulates an *array* pointer - // that gets deleted automatically. For common POD types, we provide - // specializations. - - template <typename T> - class local_buffer - { - public: - - // Always allocate, even for zero-size buffers so that local - // buffers always have valid addresses, same as for directly using - // operator new. - - local_buffer (size_t size) : data (new T [size]) { } - - // No copying! - - local_buffer (const local_buffer&) = delete; - - local_buffer& operator = (const local_buffer&) = delete; - - ~local_buffer (void) { delete [] data; } - - operator T *() const { return data; } - - private: - - T *data; - }; - - // For buffers of POD types, we'll be smarter. There is one thing - // that differentiates a local buffer from a dynamic array - the local - // buffers, if not manipulated improperly, have a FIFO semantics, - // meaning that if buffer B is allocated after buffer A, B *must* be - // deallocated before A. This is *guaranteed* if you use local buffer - // exclusively through the OCTAVE_LOCAL_BUFFER macro, because the C++ - // standard requires that explicit local objects be destroyed in - // reverse order of declaration. Therefore, we can avoid memory - // fragmentation by allocating fairly large chunks of memory and - // serving local buffers from them in a stack-like manner. The first - // returning buffer in previous chunk will be responsible for - // deallocating the chunk. - - class chunk_buffer - { - public: - - OCTAVE_API chunk_buffer (size_t size); - - // No copying! - - chunk_buffer (const chunk_buffer&) = delete; - - chunk_buffer& operator = (const chunk_buffer&) = delete; - - OCTAVE_API virtual ~chunk_buffer (void); - - char * data (void) const { return dat; } - - static OCTAVE_API void clear (void); - - private: - - // The number of bytes we allocate for each large chunk of memory we - // manage. - static const size_t chunk_size; - - // Pointer to the end end of the last allocation. - static char *top; - - // Pointer to the current active chunk. - static char *chunk; - - // The number of bytes remaining in the active chunk. - static size_t left; - // The number of active allocations. - static size_t active; - - // Pointer to the current chunk. - char *cnk; - - // Pointer to the beginning of the most recent allocation. - char *dat; - }; - - // This specializes local_buffer to use the chunked buffer - // mechanism for POD types. -#define SPECIALIZE_POD_BUFFER(TYPE) \ - template <> \ - class local_buffer<TYPE> : private chunk_buffer \ - { \ - public: \ - local_buffer (size_t size) \ - : chunk_buffer (size * sizeof (TYPE)) { } \ - \ - operator TYPE *() const \ - { \ - return reinterpret_cast<TYPE *> (this->data ()); \ - } \ - } - - SPECIALIZE_POD_BUFFER (bool); - SPECIALIZE_POD_BUFFER (char); - SPECIALIZE_POD_BUFFER (unsigned short); - SPECIALIZE_POD_BUFFER (short); - SPECIALIZE_POD_BUFFER (int); - SPECIALIZE_POD_BUFFER (unsigned int); - SPECIALIZE_POD_BUFFER (long); - SPECIALIZE_POD_BUFFER (unsigned long); - SPECIALIZE_POD_BUFFER (float); - SPECIALIZE_POD_BUFFER (double); - // FIXME: Are these guaranteed to be POD and satisfy alignment? - SPECIALIZE_POD_BUFFER (Complex); - SPECIALIZE_POD_BUFFER (FloatComplex); - // MORE ? +#include <algorithm> +#include <memory> - // All pointers and const pointers are also POD types. - template <typename T> - class local_buffer<T *> : private chunk_buffer - { - public: - local_buffer (size_t size) - : chunk_buffer (size * sizeof (T *)) - { } - - operator T **() const { return reinterpret_cast<T **> (this->data ()); } - }; - - template <typename T> - class local_buffer<const T *> : private chunk_buffer - { - public: - local_buffer (size_t size) - : chunk_buffer (size * sizeof (const T *)) - { } - - operator const T **() const - { - return reinterpret_cast<const T **> (this->data ()); - } - }; -} - -// If the compiler supports dynamic stack arrays, we can use the -// attached hack to place small buffer arrays on the stack. It may be -// even faster than our obstack-like optimization, but is dangerous -// because stack is a very limited resource, so we disable it. - -#if 0 // defined (HAVE_DYNAMIC_AUTO_ARRAYS) - -// Maximum buffer size (in bytes) to be placed on the stack. - -#define OCTAVE_LOCAL_BUFFER_MAX_STACK_SIZE 8192 - -// If we have automatic arrays, we use an automatic array if the size -// is small enough. To avoid possibly evaluating 'size' multiple -// times, we first cache it. Note that we always construct both the -// stack array and the octave::local_buffer object, but only one of -// them will be nonempty. +#if __cplusplus >= 201402L #define OCTAVE_LOCAL_BUFFER(T, buf, size) \ - const size_t _bufsize_ ## buf = size; \ - const bool _lbufaut_ ## buf = _bufsize_ ## buf * sizeof (T) \ - <= OCTAVE_LOCAL_BUFFER_MAX_STACK_SIZE; \ - T _bufaut_ ## buf [_lbufaut_ ## buf ? _bufsize_ ## buf : 0]; \ - octave::local_buffer<T> _bufheap_ ## buf (! _lbufaut_ ## buf ? _bufsize_ ## buf : 0); \ - T *buf = (_lbufaut_ ## buf \ - ? _bufaut_ ## buf : static_cast<T *> (_bufheap_ ## buf)) + auto octave_local_buffer_ ## buf = std::make_unique<T []> (size); \ + T *buf = octave_local_buffer_ ## buf.get (); \ + assert (0); #else -// If we don't have automatic arrays, we simply always use -// octave::local_buffer. - -#define OCTAVE_LOCAL_BUFFER(T, buf, size) \ - octave::local_buffer<T> _buffer_ ## buf (size); \ - T *buf = _buffer_ ## buf +#define OCTAVE_LOCAL_BUFFER(T, buf, size) \ + std::unique_ptr<T []> octave_local_buffer_ ## buf { new T [size] }; \ + T *buf = octave_local_buffer_ ## buf.get () #endif -// Note: we use weird variables in the for loop to avoid warnings -// about shadowed parameters. - -#define OCTAVE_LOCAL_BUFFER_INIT(T, buf, size, value) \ - OCTAVE_LOCAL_BUFFER (T, buf, size); \ - for (size_t _buf_iter = 0, _buf_size = size; \ - _buf_iter < _buf_size; _buf_iter++) \ - buf[_buf_iter] = value +#define OCTAVE_LOCAL_BUFFER_INIT(T, buf, size, value) \ + OCTAVE_LOCAL_BUFFER (T, buf, size); \ + std::fill_n (buf, size, value) #endif