From 2b288a82a644eec011034afd0bfe38e028bc1d5e Mon Sep 17 00:00:00 2001
From: nakst <>
Date: Sun, 14 Nov 2021 18:59:45 +0000
Subject: [PATCH] reference counting on API instances

---
 desktop/api.cpp    | 27 ++++++++++++++++++++++++++-
 desktop/gui.cpp    | 14 ++++++--------
 desktop/os.header  |  2 ++
 util/api_table.ini |  2 ++
 4 files changed, 36 insertions(+), 9 deletions(-)

diff --git a/desktop/api.cpp b/desktop/api.cpp
index b564d5a..284d0c8 100644
--- a/desktop/api.cpp
+++ b/desktop/api.cpp
@@ -203,6 +203,8 @@ struct EsUndoManager {
 };
 
 struct APIInstance {
+	uintptr_t referenceCount;
+
 	HashStore<uint32_t, EsCommand *> commands;
 
 	_EsApplicationStartupInformation *startupInformation;
@@ -814,6 +816,7 @@ APIInstance *InstanceSetup(EsInstance *instance) {
 	APIInstance *apiInstance = (APIInstance *) EsHeapAllocate(sizeof(APIInstance), true);
 
 	instance->_private = apiInstance;
+	apiInstance->referenceCount = 1;
 
 	instance->undoManager = &apiInstance->undoManager;
 	instance->undoManager->instance = instance;
@@ -885,6 +888,7 @@ EsInstance *_EsInstanceCreate(size_t bytes, EsMessage *message, const char *appl
 	if (message) {
 		apiInstance->mainWindowHandle = message->createInstance.window;
 		instance->window = EsWindowCreate(instance, ES_WINDOW_NORMAL);
+		EsInstanceOpenReference(instance);
 		EsWindowSetTitle(instance->window, nullptr, 0);
 
 		if (apiInstance->startupInformation && apiInstance->startupInformation->readHandle) {
@@ -912,7 +916,28 @@ EsApplicationStartupRequest EsInstanceGetStartupRequest(EsInstance *_instance) {
 	return request;
 }
 
+void EsInstanceOpenReference(EsInstance *_instance) {
+	EsMessageMutexCheck();
+	APIInstance *instance = (APIInstance *) _instance->_private;
+	EsAssert(instance->referenceCount);
+	instance->referenceCount++;
+}
+
+void EsInstanceCloseReference(EsInstance *_instance) {
+	EsMessageMutexCheck();
+	APIInstance *instance = (APIInstance *) _instance->_private;
+	instance->referenceCount--;
+
+	if (!instance->referenceCount) {
+		EsMessage m = {};
+		m.type = ES_MSG_INSTANCE_DESTROY;
+		m.instanceDestroy.instance = _instance;
+		EsMessagePost(nullptr, &m); 
+	}
+}
+
 void EsInstanceDestroy(EsInstance *instance) {
+	EsMessageMutexCheck();
 	InspectorWindow **inspector = &((APIInstance *) instance->_private)->attachedInspector;
 
 	if (*inspector) {
@@ -923,8 +948,8 @@ void EsInstanceDestroy(EsInstance *instance) {
 
 	UndoManagerDestroy(instance->undoManager);
 	EsAssert(instance->window->instance == instance);
-	instance->window->destroyInstanceAfterClose = true;
 	EsElementDestroy(instance->window);
+	EsInstanceCloseReference(instance);
 }
 
 EsWindow *WindowFromWindowID(EsObjectID id) {
diff --git a/desktop/gui.cpp b/desktop/gui.cpp
index cd564f3..c91145c 100644
--- a/desktop/gui.cpp
+++ b/desktop/gui.cpp
@@ -463,7 +463,7 @@ struct EsWindow : EsElement {
 	uint32_t windowWidth, windowHeight;
 
 	// TODO Replace this with a bitset?
-	bool willUpdate, toolbarFillMode, destroyInstanceAfterClose, doNotPaint;
+	bool willUpdate, toolbarFillMode, doNotPaint;
 	bool restoreOnNextMove, resetPositionOnNextMove, receivedFirstResize, isMaximised;
 	bool hovering, activated, appearActivated;
 	bool visualizeRepaints, visualizeLayoutBounds, visualizePaintSteps; // Inspector properties.
@@ -7253,14 +7253,10 @@ void UIProcessWindowManagerMessage(EsWindow *window, EsMessage *message, Process
 	// Check if the window has been destroyed.
 
 	if (message->type == ES_MSG_WINDOW_DESTROYED) {
-		if (window->destroyInstanceAfterClose) {
-			EsMessage m = {};
-			m.type = ES_MSG_INSTANCE_DESTROY;
-			m.instanceDestroy.instance = window->instance;
+		if (window->instance) {
 			EsAssert(window->instance->window == window);
 			window->instance->window = nullptr;
-			window->instance = nullptr;
-			EsMessagePost(nullptr, &m); 
+			EsInstanceCloseReference(window->instance);
 		}
 
 		EsAssert(window->handle == ES_INVALID_HANDLE);
@@ -7546,7 +7542,7 @@ struct InspectorWindow : EsInstance {
 	EsInstance *instance; // The instance being inspected.
 
 	EsListView *elementList;
-	Array<InspectorElementEntry> elements;
+	Array<InspectorElementEntry> elements; // TODO This is being leaked.
 	InspectorElementEntry hoveredElement;
 	char *cCategoryFilter;
 
@@ -8028,6 +8024,8 @@ void InspectorSetup(EsWindow *window) {
 	InspectorWindow *inspector = (InspectorWindow *) EsHeapAllocate(sizeof(InspectorWindow), true); // TODO Freeing this.
 	inspector->window = window;
 	InstanceSetup(inspector);
+	EsInstanceOpenReference(inspector);
+	
 	inspector->instance = window->instance;
 	((APIInstance *) inspector->_private)->internalOnly = true;
 	window->instance = inspector;
diff --git a/desktop/os.header b/desktop/os.header
index c0402b7..6be18ab 100644
--- a/desktop/os.header
+++ b/desktop/os.header
@@ -2366,6 +2366,8 @@ function void EsCommandSetCallback(EsCommand *command, EsCommandCallback callbac
 function void EsCommandSetDisabled(EsCommand *command, bool disabled);
 function void EsCommandSetCheck(EsCommand *command, EsCheckState check, bool sendUpdatedMessage);
 
+function void EsInstanceOpenReference(EsInstance *_instance);
+function void EsInstanceCloseReference(EsInstance *_instance);
 function void EsInstanceDestroy(ES_INSTANCE_TYPE *instance);
 function void EsInstanceSetActiveUndoManager(ES_INSTANCE_TYPE *instance, EsUndoManager *manager);
 function void EsInstanceSetClassEditor(ES_INSTANCE_TYPE *instance, const EsInstanceClassEditorSettings *settings);
diff --git a/util/api_table.ini b/util/api_table.ini
index 5ad7a8c..f8e16f3 100644
--- a/util/api_table.ini
+++ b/util/api_table.ini
@@ -131,9 +131,11 @@ EsPipeRead=129
 EsListViewInsert=130
 EsListViewRemove=131
 EsEventCreate=132
+EsInstanceOpenReference=133
 EsBufferFormat=134
 EsEventReset=135
 EsEventSet=136
+EsInstanceCloseReference=137
 EsMutexAcquire=140
 EsMutexDestroy=141
 EsMutexRelease=142