/* * Copyright (C) 2012-2017 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 . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_MALLOC_H #include #endif #include #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 #include #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!] and is unspeakably * slow even if writing to an unbuffered fmemopen()ed * stream. */ 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).", limit_str, usage_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 command.", limit_str); g_free(limit_str); throw err; } } void * Object::operator new(size_t size) noexcept { #ifdef USE_MEMORY_COUNTING memory_usage += size; #endif /* * Since we've got the sized-delete operator * below, we could allocate via g_slice. * * Using g_slice however would render malloc_trim() * ineffective. Also, it has been shown to be * unnecessary on Linux/glibc. * Glib is guaranteed to use the system malloc(), * so g_malloc() cooperates with malloc_trim(). * * On Windows (even Windows 2000), the slice allocator * did not show any significant performance boost * either. Also, since g_slice never seems to return * memory to the OS and we cannot force it to do so, * it will not cooperate with the Windows-specific * memory measurement and it is hard to recover * from memory limit exhaustions. */ return g_malloc(size); } void Object::operator delete(void *ptr, size_t size) noexcept { g_free(ptr); #ifdef USE_MEMORY_COUNTING memory_usage -= size; #endif } } /* namespace SciTECO */