aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--TODO26
-rw-r--r--configure.ac8
-rw-r--r--src/Makefile.am1
-rw-r--r--src/cmdline.h3
-rw-r--r--src/document.h3
-rw-r--r--src/eol.h5
-rw-r--r--src/error.h9
-rw-r--r--src/expressions.h9
-rw-r--r--src/glob.h3
-rw-r--r--src/goto.h9
-rw-r--r--src/help.h3
-rw-r--r--src/interface-curses/curses-info-popup.h4
-rw-r--r--src/interface.h14
-rw-r--r--src/ioview.cpp11
-rw-r--r--src/ioview.h6
-rw-r--r--src/main.cpp57
-rw-r--r--src/memory.cpp168
-rw-r--r--src/memory.h88
-rw-r--r--src/parser.cpp49
-rw-r--r--src/parser.h12
-rw-r--r--src/qregisters.h24
-rw-r--r--src/rbtree.h9
-rw-r--r--src/ring.h12
-rw-r--r--src/symbols.h4
-rw-r--r--src/undo.cpp45
-rw-r--r--src/undo.h72
26 files changed, 358 insertions, 296 deletions
diff --git a/TODO b/TODO
index 7708132..e32bf31 100644
--- a/TODO
+++ b/TODO
@@ -39,19 +39,6 @@ Known Bugs:
regex support, UNIX regex (unportable) or some other library.
Perhaps allowing us to interpret the SciTECO matching language
directly.
- * the glib allocators are fundamentally broken:
- throwing exceptions is unsafe from C-linkage callbacks.
- We should abandon the custom allocators and rely on
- SciTECO's memory limiting.
- All C++ allocations could be based on g_malloc/g_slice so we
- assert on OOM. Instead we improve memory limiting using platform-specific
- API like malloc_info(). Since the remaining platforms are only obscure
- ones, the overloaded C++ operators should be sufficient to count the
- bulk of memory used. Since the necessary sized delete operators are
- only available beginning with C++14, there'd have to be yet another
- fallback that stores the memory chunk size at the beginning of the
- heap object.
- The UndoToken::get_size() workaround can be removed.
* It is still possible to crash SciTECO using recursive functions,
since they map to the C++ program's call stack.
It is perhaps best to use another ValueStack as a stack of
@@ -257,19 +244,6 @@ Optimizations:
However, this would mean to make some more Cmdline methods public.
The implementations of the States' commandline editing handlers
could all be concentrated in cmdline.cpp.
- * C++14 is supported by GCC 5 and supports new() and delete()
- operators with a size argument. Replacing these operators
- with versions using g_slice_alloc() and g_slice_free() should
- speed up things, especially Q-Register handling and the undo
- stack.
- This compiler capability should be checked by the build system.
- C++11 already allows sized allocators in a class.
- Testing shows that this does not speed up things on Linux
- and prevents freeing memory on command line termination
- (it would be glibc-specific), so it should probably depend on
- HAVE_MALLOC_TRIM. On all other platforms, it could be assumed
- to be beneficial or at least not hurt.
- Best is to test its effect on MSVCRT.
* String::append() could be optimized by ORing a padding
into the realloc() size (e.g. 0xFF).
However, this has not proven effective on Linux/glibc
diff --git a/configure.ac b/configure.ac
index 517f4a0..42094ec 100644
--- a/configure.ac
+++ b/configure.ac
@@ -124,9 +124,13 @@ AC_CHECK_HEADERS([bsd/sys/queue.h], [], [
case $host in
*-mingw*)
- AC_CHECK_HEADERS([windows.h], , [
+ AC_CHECK_HEADERS([windows.h psapi.h], , [
AC_MSG_ERROR([Missing Windows headers!])
])
+
+ # Make sure we get GetProcessMemoryInfo():
+ AM_CPPFLAGS="$AM_CPPFLAGS -DPSAPI_VERSION=1"
+ LIBS="$LIBS -lpsapi"
;;
esac
@@ -154,7 +158,7 @@ esac
# Check for optional GNU libc features.
# Will probably only be found on Linux.
AC_CHECK_HEADERS([malloc.h])
-AC_CHECK_FUNCS([malloc_trim])
+AC_CHECK_FUNCS([malloc_trim mallinfo])
#
# Config options
diff --git a/src/Makefile.am b/src/Makefile.am
index facf963..2778f4f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -30,6 +30,7 @@ EXTRA_DIST = sciteco.html
noinst_LTLIBRARIES = libsciteco-base.la
libsciteco_base_la_SOURCES = main.cpp sciteco.h \
+ memory.cpp memory.h \
string-utils.cpp string-utils.h \
error.cpp error.h \
cmdline.cpp cmdline.h \
diff --git a/src/cmdline.h b/src/cmdline.h
index dad3a42..7c3b861 100644
--- a/src/cmdline.h
+++ b/src/cmdline.h
@@ -20,13 +20,14 @@
#include <glib.h>
+#include "memory.h"
#include "parser.h"
#include "qregisters.h"
#include "undo.h"
namespace SciTECO {
-extern class Cmdline {
+extern class Cmdline : public Object {
public:
/**
* String containing the current command line.
diff --git a/src/document.h b/src/document.h
index d8205b9..760c9e6 100644
--- a/src/document.h
+++ b/src/document.h
@@ -23,6 +23,7 @@
#include <Scintilla.h>
#include "sciteco.h"
+#include "memory.h"
#include "interface.h"
#include "undo.h"
@@ -32,7 +33,7 @@ namespace SciTECO {
* Classes
*/
-class Document {
+class Document : public Object {
typedef const void *SciDoc;
SciDoc doc;
diff --git a/src/eol.h b/src/eol.h
index 81ed4ef..7c425a9 100644
--- a/src/eol.h
+++ b/src/eol.h
@@ -23,10 +23,11 @@
#include <glib.h>
#include "sciteco.h"
+#include "memory.h"
namespace SciTECO {
-class EOLReader {
+class EOLReader : public Object {
gchar *buffer;
gsize read_len;
guint offset;
@@ -91,7 +92,7 @@ public:
gchar *convert_all(gsize *out_len = NULL);
};
-class EOLWriter {
+class EOLWriter : public Object {
enum {
STATE_START = 0,
STATE_WRITE_LF
diff --git a/src/error.h b/src/error.h
index 62063ff..6801271 100644
--- a/src/error.h
+++ b/src/error.h
@@ -25,6 +25,7 @@
#include <glib/gprintf.h>
#include "sciteco.h"
+#include "memory.h"
#include "string-utils.h"
namespace SciTECO {
@@ -33,20 +34,20 @@ namespace SciTECO {
* Thrown as exception to signify that program
* should be terminated.
*/
-class Quit {};
+class Quit : public Object {};
/**
* Thrown as exception to cause a macro to
* return or a command-line termination.
*/
-class Return {
+class Return : public Object {
public:
guint args;
Return(guint _args = 0) : args(_args) {}
};
-class Error {
+class Error : public Object {
GSList *frames;
public:
@@ -54,7 +55,7 @@ public:
gint pos;
gint line, column;
- class Frame {
+ class Frame : public Object {
public:
gint pos;
gint line, column;
diff --git a/src/expressions.h b/src/expressions.h
index cdb2d3e..50962fd 100644
--- a/src/expressions.h
+++ b/src/expressions.h
@@ -20,20 +20,21 @@
#include <glib.h>
+#include "memory.h"
#include "undo.h"
#include "error.h"
namespace SciTECO {
template <typename Type>
-class ValueStack {
+class ValueStack : public Object {
/*
* NOTE: Since value stacks are usually singleton,
* we pass them as a template parameter, saving space
* in the undo token.
*/
template <ValueStack<Type> &stack>
- class UndoTokenPush : public UndoTokenWithSize<UndoTokenPush<stack>> {
+ class UndoTokenPush : public UndoToken {
Type value;
guint index;
@@ -49,7 +50,7 @@ class ValueStack {
};
template <ValueStack<Type> &stack>
- class UndoTokenPop : public UndoTokenWithSize<UndoTokenPop<stack>> {
+ class UndoTokenPop : public UndoToken {
guint index;
public:
@@ -158,7 +159,7 @@ public:
/**
* Arithmetic expression stacks
*/
-extern class Expressions {
+extern class Expressions : public Object {
public:
/**
* Operator type.
diff --git a/src/glob.h b/src/glob.h
index 1577242..739a191 100644
--- a/src/glob.h
+++ b/src/glob.h
@@ -24,11 +24,12 @@
#include <glib/gstdio.h>
#include "sciteco.h"
+#include "memory.h"
#include "parser.h"
namespace SciTECO {
-class Globber {
+class Globber : public Object {
GFileTest test;
gchar *dirname;
GDir *dir;
diff --git a/src/goto.h b/src/goto.h
index 5d29e8b..de4432d 100644
--- a/src/goto.h
+++ b/src/goto.h
@@ -24,13 +24,14 @@
#include <glib/gprintf.h>
#include "sciteco.h"
+#include "memory.h"
#include "parser.h"
#include "undo.h"
#include "rbtree.h"
namespace SciTECO {
-class GotoTable : private RBTreeString {
+class GotoTable : private RBTreeString, public Object {
class UndoTokenSet : public UndoToken {
GotoTable *table;
@@ -53,12 +54,6 @@ class GotoTable : private RBTreeString {
table->dump();
#endif
}
-
- gsize
- get_size(void) const
- {
- return sizeof(*this) + strlen(name) + 1;
- }
};
class Label : public RBEntryOwnString {
diff --git a/src/help.h b/src/help.h
index 55fb2ee..85c2384 100644
--- a/src/help.h
+++ b/src/help.h
@@ -24,13 +24,14 @@
#include <glib/gprintf.h>
#include "sciteco.h"
+#include "memory.h"
#include "parser.h"
#include "undo.h"
#include "rbtree.h"
namespace SciTECO {
-class HelpIndex : private RBTreeStringCase {
+class HelpIndex : private RBTreeStringCase, public Object {
public:
class Topic : public RBEntryOwnString {
public:
diff --git a/src/interface-curses/curses-info-popup.h b/src/interface-curses/curses-info-popup.h
index 01afc1f..cbda4dc 100644
--- a/src/interface-curses/curses-info-popup.h
+++ b/src/interface-curses/curses-info-popup.h
@@ -22,9 +22,11 @@
#include <curses.h>
+#include "memory.h"
+
namespace SciTECO {
-class CursesInfoPopup {
+class CursesInfoPopup : public Object {
public:
/**
* @bug This is identical to the type defined in
diff --git a/src/interface.h b/src/interface.h
index e841680..9805a42 100644
--- a/src/interface.h
+++ b/src/interface.h
@@ -25,6 +25,7 @@
#include <Scintilla.h>
+#include "memory.h"
#include "undo.h"
#include "error.h"
@@ -59,14 +60,14 @@ extern sig_atomic_t sigint_occurred;
* base-class implementations to interface.cpp.
*/
template <class ViewImpl>
-class View {
+class View : public Object {
inline ViewImpl &
impl(void)
{
return *(ViewImpl *)this;
}
- class UndoTokenMessage : public UndoTokenWithSize<UndoTokenMessage> {
+ class UndoTokenMessage : public UndoToken {
ViewImpl &view;
unsigned int iMessage;
@@ -86,8 +87,7 @@ class View {
}
};
- class UndoTokenSetRepresentations : public
- UndoTokenWithSize<UndoTokenSetRepresentations> {
+ class UndoTokenSetRepresentations : public UndoToken {
ViewImpl &view;
public:
@@ -153,14 +153,14 @@ protected:
* user interface at runtime.
*/
template <class InterfaceImpl, class ViewImpl>
-class Interface {
+class Interface : public Object {
inline InterfaceImpl &
impl(void)
{
return *(InterfaceImpl *)this;
}
- class UndoTokenShowView : public UndoTokenWithSize<UndoTokenShowView> {
+ class UndoTokenShowView : public UndoToken {
ViewImpl *view;
public:
@@ -171,7 +171,7 @@ class Interface {
};
template <class Type>
- class UndoTokenInfoUpdate : public UndoTokenWithSize< UndoTokenInfoUpdate<Type> > {
+ class UndoTokenInfoUpdate : public UndoToken {
const Type *obj;
public:
diff --git a/src/ioview.cpp b/src/ioview.cpp
index a5685fe..00d24a4 100644
--- a/src/ioview.cpp
+++ b/src/ioview.cpp
@@ -253,17 +253,6 @@ public:
savepoint);
}
}
-
- gsize
- get_size(void) const
- {
- gsize ret = sizeof(*this) + strlen(filename) + 1;
-
- if (savepoint)
- ret += strlen(savepoint) + 1;
-
- return ret;
- }
};
static void
diff --git a/src/ioview.h b/src/ioview.h
index 18a3be6..6d93915 100644
--- a/src/ioview.h
+++ b/src/ioview.h
@@ -115,12 +115,6 @@ class IOView : public ViewCurrent {
{
g_unlink(filename);
}
-
- gsize
- get_size(void) const
- {
- return sizeof(*this) + strlen(filename) + 1;
- }
};
public:
diff --git a/src/main.cpp b/src/main.cpp
index e9be929..bc82093 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -23,7 +23,6 @@
#include <string.h>
#include <stdlib.h>
#include <signal.h>
-#include <new>
#include <glib.h>
#include <glib/gprintf.h>
@@ -81,12 +80,10 @@ static gboolean mung_profile = TRUE;
sig_atomic_t sigint_occurred = FALSE;
extern "C" {
-static gpointer g_malloc_exception(gsize n_bytes);
-static gpointer g_calloc_exception(gsize n_blocks, gsize n_block_bytes);
-static gpointer g_realloc_exception(gpointer mem, gsize n_bytes);
static void sigint_handler(int signal);
-}
+
+} /* extern "C" */
#if defined(G_OS_UNIX) || defined(G_OS_HAIKU)
@@ -342,45 +339,6 @@ initialize_environment(const gchar *program)
* Callbacks
*/
-class g_bad_alloc : public std::bad_alloc {
-public:
- const char *
- what() const throw()
- {
- return "glib allocation";
- }
-};
-
-static gpointer
-g_malloc_exception(gsize n_bytes)
-{
- gpointer p = malloc(n_bytes);
-
- if (!p)
- throw g_bad_alloc();
- return p;
-}
-
-static gpointer
-g_calloc_exception(gsize n_blocks, gsize n_block_bytes)
-{
- gpointer p = calloc(n_blocks, n_block_bytes);
-
- if (!p)
- throw g_bad_alloc();
- return p;
-}
-
-static gpointer
-g_realloc_exception(gpointer mem, gsize n_bytes)
-{
- gpointer p = realloc(mem, n_bytes);
-
- if (!p)
- throw g_bad_alloc();
- return p;
-}
-
static void
sigint_handler(int signal)
{
@@ -403,15 +361,6 @@ main(int argc, char **argv)
static GotoTable cmdline_goto_table;
static QRegisterTable local_qregs;
- static GMemVTable vtable = {
- g_malloc_exception, /* malloc */
- g_realloc_exception, /* realloc */
- free, /* free */
- g_calloc_exception, /* calloc */
- malloc, /* try_malloc */
- realloc /* try_realloc */
- };
-
gchar *mung_filename;
#ifdef DEBUG_PAUSE
@@ -422,8 +371,6 @@ main(int argc, char **argv)
signal(SIGINT, sigint_handler);
signal(SIGTERM, sigint_handler);
- g_mem_set_vtable(&vtable);
-
mung_filename = process_options(argc, argv);
/*
* All remaining arguments in argv are arguments
diff --git a/src/memory.cpp b/src/memory.cpp
new file mode 100644
index 0000000..853e52f
--- /dev/null
+++ b/src/memory.cpp
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2012-2016 Robin Haberkorn
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+
+#include <glib.h>
+
+#include "sciteco.h"
+#include "memory.h"
+#include "error.h"
+#include "undo.h"
+
+#ifdef HAVE_WINDOWS_H
+/* here it shouldn't cause conflicts with other headers */
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <psapi.h>
+#endif
+
+namespace SciTECO {
+
+MemoryLimit memlimit;
+
+#ifdef HAVE_MALLINFO
+
+gsize
+MemoryLimit::get_usage(void)
+{
+ struct mallinfo info = mallinfo();
+
+ /*
+ * NOTE: `uordblks` is an int and thus prone
+ * to wrap-around issues.
+ * Unfortunately, the only other machine readable
+ * alternative is malloc_info() which prints
+ * into a FILE * stream [sic!]
+ */
+ return info.uordblks;
+}
+
+#elif defined(G_OS_WIN32)
+
+gsize
+MemoryLimit::get_usage(void)
+{
+ PROCESS_MEMORY_COUNTERS info;
+
+ /*
+ * This __should__ not fail since the current process has
+ * PROCESS_ALL_ACCESS, but who knows...
+ * Since memory limiting cannot be turned off when this
+ * happens, we can just as well terminate abnormally.
+ */
+ if (G_UNLIKELY(!GetProcessMemoryInfo(GetCurrentProcess(),
+ &info, sizeof(info))) {
+ gchar *msg = g_win32_error_message(GetLastError());
+ g_error("Cannot get memory usage: %s", msg);
+ /* shouldn't be reached */
+ g_free(msg);
+ return 0;
+ }
+
+ return info.WorkingSetSize;
+}
+
+#else
+
+#define USE_MEMORY_COUNTING
+
+static gsize memory_usage = 0;
+
+gsize
+MemoryLimit::get_usage(void)
+{
+ return memory_usage;
+}
+
+#endif /* !HAVE_MALLINFO && !G_OS_WIN32 */
+
+void
+MemoryLimit::set_limit(gsize new_limit)
+{
+ gsize memory_usage = get_usage();
+
+ if (G_UNLIKELY(new_limit && memory_usage > new_limit)) {
+ gchar *usage_str = g_format_size(memory_usage);
+ gchar *limit_str = g_format_size(new_limit);
+
+ Error err("Cannot set undo memory limit (%s): "
+ "Current usage too large (%s).",
+ usage_str, limit_str);
+
+ g_free(limit_str);
+ g_free(usage_str);
+ throw err;
+ }
+
+ undo.push_var(limit) = new_limit;
+}
+
+void
+MemoryLimit::check(void)
+{
+ if (G_UNLIKELY(limit && get_usage() > limit)) {
+ gchar *limit_str = g_format_size(limit);
+
+ Error err("Memory limit (%s) exceeded. See <EJ> command.",
+ limit_str);
+
+ g_free(limit_str);
+ throw err;
+ }
+}
+
+void *
+Object::operator new(size_t size) noexcept
+{
+#ifdef USE_MEMORY_COUNTING
+ SciTECO::memory_usage += size;
+#endif
+
+#ifdef HAVE_MALLOC_TRIM
+ /*
+ * Using g_slice would render malloc_trim()
+ * ineffective. Also, it has been shown to be
+ * unnecessary on Linux/glibc.
+ */
+ return g_malloc(size);
+#else
+ return g_slice_alloc(size);
+#endif
+}
+
+void
+Object::operator delete(void *ptr, size_t size) noexcept
+{
+#ifdef HAVE_MALLOC_TRIM
+ g_free(ptr);
+#else
+ g_slice_free1(size, ptr);
+#endif
+
+#ifdef USE_MEMORY_COUNTING
+ SciTECO::memory_usage -= size;
+#endif
+}
+
+} /* namespace SciTECO */
diff --git a/src/memory.h b/src/memory.h
new file mode 100644
index 0000000..e1596ea
--- /dev/null
+++ b/src/memory.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2012-2016 Robin Haberkorn
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __MEMORY_H
+#define __MEMORY_H
+
+#include <glib.h>
+
+/**
+ * Default memory limit (500mb, assuming SI units).
+ */
+#define MEMORY_LIMIT_DEFAULT (500*1000*1000)
+
+namespace SciTECO {
+
+/**
+ * Common base class for all objects in SciTECO.
+ * This is currently only used to provide custom new/delete
+ * replacements in order to support unified allocation via
+ * Glib (g_malloc and g_slice) and as a memory usage
+ * counting fallback.
+ *
+ * This approach has certain drawbacks, e.g. you cannot
+ * derive from Object privately; nor is it possible to
+ * influence allocations in other libraries or even of
+ * scalars (e.g. new char[5]).
+ *
+ * C++14 (supported by GCC >= 5) has global sized delete
+ * replacements which would be effective in the entire application
+ * but we're still using the base-class approach since
+ * we must support the older compilers anyway.
+ */
+class Object {
+public:
+ static void *operator new(size_t size) noexcept;
+ static inline void *
+ operator new[](size_t size) noexcept
+ {
+ return operator new(size);
+ }
+ static inline void *
+ operator new(size_t size, void *ptr) noexcept
+ {
+ return ptr;
+ }
+
+ static void operator delete(void *ptr, size_t size) noexcept;
+ static inline void
+ operator delete[](void *ptr, size_t size) noexcept
+ {
+ operator delete(ptr, size);
+ }
+};
+
+extern class MemoryLimit : public Object {
+public:
+ /**
+ * Undo stack memory limit in bytes.
+ * 0 means no limiting.
+ */
+ gsize limit;
+
+ MemoryLimit() : limit(MEMORY_LIMIT_DEFAULT) {}
+
+ gsize get_usage(void);
+
+ void set_limit(gsize new_limit = 0);
+
+ void check(void);
+} memlimit;
+
+} /* namespace SciTECO */
+
+#endif
diff --git a/src/parser.cpp b/src/parser.cpp
index dd936f6..05694a2 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -21,13 +21,13 @@
#include <string.h>
#include <exception>
-#include <new>
#include <glib.h>
#include <glib/gprintf.h>
#include <glib/gstdio.h>
#include "sciteco.h"
+#include "memory.h"
#include "string-utils.h"
#include "interface.h"
#include "undo.h"
@@ -107,8 +107,11 @@ Execute::step(const gchar *macro, gint stop_pos)
{
try {
/*
- * convert bad_alloc and other C++ standard
- * library exceptions
+ * Convert bad_alloc and other C++ standard
+ * library exceptions.
+ * bad_alloc should no longer be thrown, though
+ * since new/delete uses Glib allocations and we
+ * uniformly terminate abnormally in case of OOM.
*/
try {
while (macro_pc < stop_pos) {
@@ -121,6 +124,8 @@ Execute::step(const gchar *macro, gint stop_pos)
if (interface.is_interrupted())
throw Error("Interrupted");
+ memlimit.check();
+
State::input(macro[macro_pc]);
macro_pc++;
}
@@ -2305,23 +2310,31 @@ StateECommand::custom(gchar chr)
* of buffers in the ring.
* (\fBread-only\fP)
* .IP 2
- * The current undo stack memory limit in bytes.
+ * The current memory limit in bytes.
* This limit helps to prevent dangerous out-of-memory
* conditions (e.g. resulting from infinite loops) by
- * approximating the memory used by \*(ST's undo stack and is only
- * effective in interactive mode.
- * Commands which would exceed that limit fail instead.
+ * constantly sampling the memory requirements of \*(ST.
+ * Note that not all platforms support precise measurements
+ * of the current memory usage \(em \*(ST will fall back
+ * to an approximation which might be less than the actual
+ * usage on those platforms.
+ * Memory limiting is effective in batch and interactive mode.
+ * Commands which would exceed that limit will fail instead
+ * allowing users to recover in interactive mode, e.g. by
+ * terminating the command line.
* When getting, a zero value indicates that memory limiting is
* disabled.
* Setting a value less than or equal to 0 as in
* \(lq0,2EJ\(rq disables the limit.
* \fBWarning:\fP Disabling memory limiting may provoke
- * uncontrollable out-of-memory errors in long running
- * or infinite loops.
- * Setting a new limit may fail if the current undo stack
- * is too large for the new limit \(em if this happens
- * you may have to clear your command-line first.
- * Undo stack memory limiting is enabled by default.
+ * out-of-memory errors in long running or infinite loops
+ * (interactive mode) that result in abnormal program
+ * termination.
+ * Setting a new limit may fail if the current memory
+ * requirements are too large for the new limit \(em if
+ * this happens you may have to clear your command-line
+ * first.
+ * Memory limiting is enabled by default.
* .IP 3
* This \fBwrite-only\fP property allows redefining the
* first 16 entries of the terminal color palette \(em a
@@ -2370,7 +2383,7 @@ StateECommand::custom(gchar chr)
enum {
EJ_USER_INTERFACE = 0,
EJ_BUFFERS,
- EJ_UNDO_MEMORY_LIMIT,
+ EJ_MEMORY_LIMIT,
EJ_INIT_COLOR
};
tecoInt property;
@@ -2382,8 +2395,8 @@ StateECommand::custom(gchar chr)
tecoInt value = expressions.pop_num_calc();
switch (property) {
- case EJ_UNDO_MEMORY_LIMIT:
- undo.set_memory_limit(MAX(0, value));
+ case EJ_MEMORY_LIMIT:
+ memlimit.set_limit(MAX(0, value));
break;
case EJ_INIT_COLOR:
@@ -2419,8 +2432,8 @@ StateECommand::custom(gchar chr)
expressions.push(ring.get_id(ring.last()));
break;
- case EJ_UNDO_MEMORY_LIMIT:
- expressions.push(undo.memory_limit);
+ case EJ_MEMORY_LIMIT:
+ expressions.push(memlimit.limit);
break;
default:
diff --git a/src/parser.h b/src/parser.h
index f5dcba5..3356600 100644
--- a/src/parser.h
+++ b/src/parser.h
@@ -23,6 +23,7 @@
#include <glib.h>
#include "sciteco.h"
+#include "memory.h"
#include "undo.h"
#include "error.h"
#include "expressions.h"
@@ -32,7 +33,7 @@ namespace SciTECO {
/* TECO uses only lower 7 bits for commands */
#define MAX_TRANSITIONS 127
-class State {
+class State : public Object {
protected:
/* static transitions */
State *transitions[MAX_TRANSITIONS];
@@ -78,7 +79,7 @@ protected:
};
template <typename Type>
-class MicroStateMachine {
+class MicroStateMachine : public Object {
protected:
/* label pointers */
typedef const void *MicroState;
@@ -259,13 +260,6 @@ public:
}
void run(void);
-
- gsize
- get_size(void) const
- {
- return dir ? sizeof(*this) + strlen(dir)
- : sizeof(*this);
- }
};
class StateChangeDir : public StateExpectDir {
diff --git a/src/qregisters.h b/src/qregisters.h
index bcea2f2..cb50b6f 100644
--- a/src/qregisters.h
+++ b/src/qregisters.h
@@ -28,6 +28,7 @@
#include <Scintilla.h>
#include "sciteco.h"
+#include "memory.h"
#include "error.h"
#include "interface.h"
#include "ioview.h"
@@ -47,7 +48,7 @@ namespace QRegisters {
* Classes
*/
-class QRegisterData {
+class QRegisterData : public Object {
protected:
tecoInt integer;
@@ -241,12 +242,6 @@ class QRegisterClipboard : public QRegister {
}
void run(void);
-
- gsize
- get_size(void) const
- {
- return sizeof(*this) + strlen(name) + str_len;
- }
};
/**
@@ -289,8 +284,8 @@ public:
void undo_exchange_string(QRegisterData &reg);
};
-class QRegisterTable : private RBTreeString {
- class UndoTokenRemove : public UndoTokenWithSize<UndoTokenRemove> {
+class QRegisterTable : private RBTreeString, public Object {
+ class UndoTokenRemove : public UndoToken {
QRegisterTable *table;
QRegister *reg;
@@ -392,7 +387,7 @@ public:
}
};
-class QRegisterStack {
+class QRegisterStack : public Object {
class Entry : public QRegisterData {
public:
SLIST_ENTRY(Entry) entries;
@@ -415,16 +410,9 @@ class QRegisterStack {
}
void run(void);
-
- gsize
- get_size(void) const
- {
- return entry ? sizeof(*this) + sizeof(*entry)
- : sizeof(*this);
- }
};
- class UndoTokenPop : public UndoTokenWithSize<UndoTokenPop> {
+ class UndoTokenPop : public UndoToken {
QRegisterStack *stack;
public:
diff --git a/src/rbtree.h b/src/rbtree.h
index 7730fc5..db0d430 100644
--- a/src/rbtree.h
+++ b/src/rbtree.h
@@ -25,12 +25,19 @@
#include <glib.h>
#include <glib/gprintf.h>
+#include "memory.h"
+
namespace SciTECO {
+/*
+ * NOTE: RBTree is not derived from Object,
+ * so we can derive from RBTree privately.
+ * Add Object to every RBTree subclass explicitly.
+ */
template <class RBEntryType>
class RBTree {
public:
- class RBEntry {
+ class RBEntry : public Object {
public:
RB_ENTRY(RBEntry) nodes;
diff --git a/src/ring.h b/src/ring.h
index 9b8b9d7..1d69d94 100644
--- a/src/ring.h
+++ b/src/ring.h
@@ -25,6 +25,7 @@
#include <Scintilla.h>
#include "sciteco.h"
+#include "memory.h"
#include "interface.h"
#include "undo.h"
#include "qregisters.h"
@@ -40,7 +41,7 @@ namespace SciTECO {
class Buffer : private IOView {
TAILQ_ENTRY(Buffer) buffers;
- class UndoTokenClose : public UndoTokenWithSize<UndoTokenClose> {
+ class UndoTokenClose : public UndoToken {
Buffer *buffer;
public:
@@ -129,7 +130,7 @@ public:
};
/* object declared in main.cpp */
-extern class Ring {
+extern class Ring : public Object {
/*
* Emitted after a buffer close
* The pointer is the only remaining reference to the buffer!
@@ -147,13 +148,6 @@ extern class Ring {
}
void run(void);
-
- gsize
- get_size(void) const
- {
- return buffer ? sizeof(*this) + sizeof(*buffer)
- : sizeof(*this);
- }
};
TAILQ_HEAD(Head, Buffer) head;
diff --git a/src/symbols.h b/src/symbols.h
index c8bca04..2c2623d 100644
--- a/src/symbols.h
+++ b/src/symbols.h
@@ -21,9 +21,11 @@
#include <string.h>
#include <glib.h>
+#include "memory.h"
+
namespace SciTECO {
-class SymbolList {
+class SymbolList : public Object {
public:
struct Entry {
const gchar *name;
diff --git a/src/undo.cpp b/src/undo.cpp
index e189e23..4593fdc 100644
--- a/src/undo.cpp
+++ b/src/undo.cpp
@@ -29,12 +29,8 @@
#include "sciteco.h"
#include "cmdline.h"
-#include "interface.h"
-#include "error.h"
#include "undo.h"
-#define SIZE_TO_MB(SIZE) ((gdouble)(SIZE))/(1024*1024)
-
namespace SciTECO {
//#define DEBUG
@@ -42,29 +38,6 @@ namespace SciTECO {
UndoStack undo;
void
-UndoStack::set_memory_limit(gsize new_limit)
-{
- if (new_limit) {
- if (!memory_limit) {
- /* memory_usage outdated - recalculate */
- UndoToken *token;
-
- memory_usage = 0;
- SLIST_FOREACH(token, &head, tokens)
- memory_usage += token->get_size();
- }
-
- if (memory_usage > new_limit)
- throw Error("Cannot set undo memory limit (%gmb): "
- "Current stack too large (%gmb).",
- SIZE_TO_MB(new_limit),
- SIZE_TO_MB(memory_usage));
- }
-
- push_var(memory_limit) = new_limit;
-}
-
-void
UndoStack::push(UndoToken *token)
{
/*
@@ -74,19 +47,6 @@ UndoStack::push(UndoToken *token)
*/
g_assert(enabled == true);
- if (memory_limit) {
- gsize token_size = token->get_size();
-
- if (memory_usage + token_size > memory_limit) {
- delete token;
- throw Error("Undo stack memory limit (%gmb) exceeded. "
- "See <EJ> command.",
- SIZE_TO_MB(memory_limit));
- }
-
- memory_usage += token_size;
- }
-
#ifdef DEBUG
g_printf("UNDO PUSH %p\n", token);
#endif
@@ -103,9 +63,6 @@ UndoStack::pop(gint pc)
g_printf("UNDO POP %p\n", top);
fflush(stdout);
#endif
-
- if (memory_limit)
- memory_usage -= top->get_size();
top->run();
SLIST_REMOVE_HEAD(&head, tokens);
@@ -122,8 +79,6 @@ UndoStack::clear(void)
SLIST_REMOVE_HEAD(&head, tokens);
delete cur;
}
-
- memory_usage = 0;
}
UndoStack::~UndoStack()
diff --git a/src/undo.h b/src/undo.h
index 1e92b16..7ca6c30 100644
--- a/src/undo.h
+++ b/src/undo.h
@@ -25,18 +25,15 @@
#include <glib.h>
#include <glib/gprintf.h>
+#include "memory.h"
+
#ifdef DEBUG
#include "parser.h"
#endif
namespace SciTECO {
-/**
- * Default undo stack memory limit (500mb).
- */
-#define UNDO_MEMORY_LIMIT_DEFAULT (500*1024*1024)
-
-class UndoToken {
+class UndoToken : public Object {
public:
SLIST_ENTRY(UndoToken) tokens;
@@ -55,35 +52,10 @@ public:
virtual ~UndoToken() {}
virtual void run(void) = 0;
-
- /**
- * Return approximated size of this object.
- * If possible it should take all heap objects
- * into account that are memory-managed by the undo
- * token.
- * For a simple implementation, you may derive
- * from UndoTokenWithSize.
- */
- virtual gsize get_size(void) const = 0;
-};
-
-/**
- * UndoToken base class employing the CRTP idiom.
- * Deriving this class adds a size approximation based
- * on the shallow size of the template parameter.
- */
-template <class UndoTokenImpl>
-class UndoTokenWithSize : public UndoToken {
-public:
- gsize
- get_size(void) const
- {
- return sizeof(UndoTokenImpl);
- }
};
template <typename Type>
-class UndoTokenVariable : public UndoTokenWithSize< UndoTokenVariable<Type> > {
+class UndoTokenVariable : public UndoToken {
Type *ptr;
Type value;
@@ -125,13 +97,6 @@ public:
*ptr = str;
str = NULL;
}
-
- gsize
- get_size(void) const
- {
- return str ? sizeof(*this) + strlen(str) + 1
- : sizeof(*this);
- }
};
template <class Type>
@@ -155,47 +120,22 @@ public:
*ptr = obj;
obj = NULL;
}
-
- gsize
- get_size(void) const
- {
- return obj ? sizeof(*this) + sizeof(*obj)
- : sizeof(*this);
- }
};
-extern class UndoStack {
+extern class UndoStack : public Object {
SLIST_HEAD(Head, UndoToken) head;
- /**
- * Current approx. memory usage of all
- * undo tokens in the stack.
- * It is only up to date if memory limiting
- * is enabled.
- */
- gsize memory_usage;
-
void push(UndoToken *token);
public:
bool enabled;
- /**
- * Undo stack memory limit in bytes.
- * 0 means no limiting.
- */
- gsize memory_limit;
-
- UndoStack(bool _enabled = false)
- : memory_usage(0), enabled(_enabled),
- memory_limit(UNDO_MEMORY_LIMIT_DEFAULT)
+ UndoStack(bool _enabled = false) : enabled(_enabled)
{
SLIST_INIT(&head);
}
~UndoStack();
- void set_memory_limit(gsize new_limit = 0);
-
/**
* Allocate and push undo token.
*