Use a thread-safe temporary arena on every platform.

This commit continues the work started in commits 521473ee and
e84fd464 that parallelizes certain geometric operations. This commit
cleans up the temporary arena implementations and makes them
thread-safe.

Also, in commit 521473ee, a call to FreeAllTemporary() was added
during initialization to create the heap on Windows. This is now
not necessary as the heap is created transparently on the first call
to AllocTemporary().
pull/616/head
whitequark 2020-05-22 13:45:37 +00:00
parent 060a3280f7
commit c89a2e4f62
7 changed files with 68 additions and 100 deletions

View File

@ -26,14 +26,6 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in
# platform utilities
if(WIN32)
set(util_SOURCES
platform/utilwin.cpp)
else()
set(util_SOURCES
platform/utilunix.cpp)
endif()
if(APPLE)
set(util_LIBRARIES
${APPKIT_LIBRARY})

View File

@ -67,9 +67,6 @@ void Slvs_MakeQuaternion(double ux, double uy, double uz,
void Slvs_Solve(Slvs_System *ssys, Slvs_hGroup shg)
{
// Create the temporary heap, if this is the first time we're solving.
FreeAllTemporary();
int i;
for(i = 0; i < ssys->params; i++) {
Slvs_Param *sp = &(ssys->param[i]);

View File

@ -18,6 +18,7 @@
#else
# include <unistd.h>
# include <sys/stat.h>
# include <mutex>
#endif
namespace SolveSpace {
@ -605,9 +606,6 @@ std::vector<std::string> InitCli(int argc, char **argv) {
}
#endif
// Create the heap that we use to store Exprs and other temp stuff.
FreeAllTemporary();
// Extract the command-line arguments; the ones from main() are ignored,
// since they are in the OEM encoding.
int argcW;
@ -681,5 +679,65 @@ void DebugPrint(const char *fmt, ...) {
#endif
//-----------------------------------------------------------------------------
// Temporary arena, on Windows.
//-----------------------------------------------------------------------------
#if defined(WIN32)
static HANDLE TempArena = NULL;
void *AllocTemporary(size_t size)
{
if(!TempArena)
TempArena = HeapCreate(0, 0, 0);
void *ptr = HeapAlloc(TempArena, HEAP_ZERO_MEMORY, size);
ssassert(ptr != NULL, "out of memory");
return ptr;
}
void FreeAllTemporary()
{
HeapDestroy(TempArena);
TempArena = NULL;
}
#endif
//-----------------------------------------------------------------------------
// Temporary arena, on Linux.
//-----------------------------------------------------------------------------
#if !defined(WIN32)
struct ArenaChunk {
ArenaChunk *next;
};
static std::mutex TempArenaMutex;
static ArenaChunk *TempArena = NULL;
void *AllocTemporary(size_t size)
{
ArenaChunk *chunk = (ArenaChunk *)calloc(1, sizeof(ArenaChunk) + size);
ssassert(chunk != NULL, "out of memory");
std::lock_guard<std::mutex> guard(TempArenaMutex);
chunk->next = TempArena;
TempArena = chunk;
return (void *)(chunk + 1);
}
void FreeAllTemporary()
{
std::lock_guard<std::mutex> guard(TempArenaMutex);
while(TempArena) {
ArenaChunk *chunk = TempArena;
TempArena = TempArena->next;
free(chunk);
}
}
#endif
}
}

View File

@ -70,6 +70,10 @@ std::vector<std::string> InitCli(int argc, char **argv);
// Debug print function.
void DebugPrint(const char *fmt, ...);
// Temporary arena functions.
void *AllocTemporary(size_t size);
void FreeAllTemporary();
}
#endif

View File

@ -1,49 +0,0 @@
//-----------------------------------------------------------------------------
// Utility functions used by the Unix port.
//
// Copyright 2008-2013 Jonathan Westhues.
// Copyright 2013 Daniel Richard G. <skunk@iSKUNK.ORG>
//-----------------------------------------------------------------------------
#include "solvespace.h"
namespace SolveSpace {
//-----------------------------------------------------------------------------
// A separate heap, on which we allocate expressions. Maybe a bit faster,
// since fragmentation is less of a concern, and it also makes it possible
// to be sloppy with our memory management, and just free everything at once
// at the end.
//-----------------------------------------------------------------------------
typedef struct _AllocTempHeader AllocTempHeader;
typedef struct _AllocTempHeader {
AllocTempHeader *prev;
AllocTempHeader *next;
} AllocTempHeader;
static AllocTempHeader *Head = NULL;
void *AllocTemporary(size_t n)
{
AllocTempHeader *h =
(AllocTempHeader *)malloc(n + sizeof(AllocTempHeader));
h->prev = NULL;
h->next = Head;
if(Head) Head->prev = h;
Head = h;
memset(&h[1], 0, n);
return (void *)&h[1];
}
void FreeAllTemporary() {
AllocTempHeader *h = Head;
while(h) {
AllocTempHeader *f = h;
h = h->next;
free(f);
}
Head = NULL;
}
};

View File

@ -1,33 +0,0 @@
//-----------------------------------------------------------------------------
// Utility functions that depend on Win32.
//
// Copyright 2008-2013 Jonathan Westhues.
//-----------------------------------------------------------------------------
#include "solvespace.h"
// Include after solvespace.h to avoid identifier clashes.
#include <windows.h>
namespace SolveSpace {
//-----------------------------------------------------------------------------
// A separate heap, on which we allocate expressions. Maybe a bit faster,
// since no fragmentation issues whatsoever, and it also makes it possible
// to be sloppy with our memory management, and just free everything at once
// at the end.
//-----------------------------------------------------------------------------
static HANDLE TempHeap;
void *AllocTemporary(size_t n)
{
void *v = HeapAlloc(TempHeap, HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, n);
ssassert(v != NULL, "Cannot allocate memory");
return v;
}
void FreeAllTemporary()
{
if(TempHeap) HeapDestroy(TempHeap);
TempHeap = HeapCreate(HEAP_NO_SERIALIZE, 1024*1024*20, 0);
}
}

View File

@ -132,16 +132,15 @@ inline double Random(double vmax) {
#include "platform/gui.h"
#include "resource.h"
using Platform::AllocTemporary;
using Platform::FreeAllTemporary;
class Expr;
class ExprVector;
class ExprQuaternion;
class RgbaColor;
enum class Command : uint32_t;
// Temporary heap, defined in the platform-specific code.
void *AllocTemporary(size_t n);
void FreeAllTemporary();
enum class Unit : uint32_t {
MM = 0,
INCHES,