mql5/Experts/Advisors/DualEA/Include/CResourceManager.mqh

403 lines
13 KiB
MQL5

2026-02-24 12:47:37 -05:00
//+------------------------------------------------------------------+
//| CResourceManager.mqh - Automated Resource Lifecycle Management |
//| Handles: indicator handles, file handles, memory allocations |
//+------------------------------------------------------------------+
#ifndef CRESOURCE_MANAGER_MQH
#define CRESOURCE_MANAGER_MQH
//+------------------------------------------------------------------+
//| Resource Types |
//+------------------------------------------------------------------+
enum ENUM_RESOURCE_TYPE
{
RES_INDICATOR_HANDLE = 0, // iMA, iRSI, etc. handles
RES_FILE_HANDLE = 1, // FileOpen handles
RES_DYNAMIC_ARRAY = 2, // Dynamically allocated arrays
RES_OBJECT_PTR = 3, // CObject pointers
RES_NETWORK_HANDLE = 4 // Socket/HTTP handles
};
//+------------------------------------------------------------------+
//| Resource Entry Structure |
//+------------------------------------------------------------------+
struct SResourceEntry
{
long resource_id; // Handle or pointer value
ENUM_RESOURCE_TYPE type; // Resource type
string owner; // Component that allocated it
string description; // Human-readable description
datetime alloc_time; // When it was allocated
int alloc_line; // Line number in source
string alloc_file; // Source file
void Set(long id, ENUM_RESOURCE_TYPE t, const string own,
const string desc, const string file, int line)
{
resource_id = id;
type = t;
owner = own;
description = desc;
alloc_time = TimeCurrent();
alloc_line = line;
alloc_file = file;
}
bool IsValid() { return resource_id != 0; }
void Clear()
{
resource_id = 0;
type = RES_INDICATOR_HANDLE;
owner = "";
description = "";
alloc_time = 0;
alloc_line = 0;
alloc_file = "";
}
};
//+------------------------------------------------------------------+
//| Resource Manager Class |
//+------------------------------------------------------------------+
class CResourceManager
{
private:
SResourceEntry m_resources[];
int m_capacity;
int m_count;
// Statistics
int m_total_allocated[5];
int m_total_released[5];
int m_leak_detected[5];
public:
CResourceManager()
{
m_capacity = 256;
m_count = 0;
ArrayResize(m_resources, m_capacity);
for(int i = 0; i < 5; i++)
{
m_total_allocated[i] = 0;
m_total_released[i] = 0;
m_leak_detected[i] = 0;
}
}
~CResourceManager()
{
CleanupAllResources();
}
//+------------------------------------------------------------------+
//| Track indicator handle |
//+------------------------------------------------------------------+
void TrackIndicator(int handle, const string owner, const string description,
const string file, int line)
{
if(handle == INVALID_HANDLE) return;
int idx = FindFreeSlot();
if(idx >= 0)
{
m_resources[idx].Set(handle, RES_INDICATOR_HANDLE, owner,
description, file, line);
m_total_allocated[RES_INDICATOR_HANDLE]++;
m_count++;
}
}
//+------------------------------------------------------------------+
//| Track file handle |
//+------------------------------------------------------------------+
void TrackFile(int handle, const string owner, const string description,
const string file, int line)
{
if(handle == INVALID_HANDLE) return;
int idx = FindFreeSlot();
if(idx >= 0)
{
m_resources[idx].Set(handle, RES_FILE_HANDLE, owner,
description, file, line);
m_total_allocated[RES_FILE_HANDLE]++;
m_count++;
}
}
//+------------------------------------------------------------------+
//| Release indicator handle |
//+------------------------------------------------------------------+
bool ReleaseIndicator(int handle, const string owner)
{
if(handle == INVALID_HANDLE) return true;
int idx = FindResource(handle, RES_INDICATOR_HANDLE, owner);
if(idx >= 0)
{
// Actually release the indicator
bool released = IndicatorRelease((int)m_resources[idx].resource_id);
m_resources[idx].Clear();
m_total_released[RES_INDICATOR_HANDLE]++;
m_count--;
return released;
}
// Not tracked - try to release anyway
return IndicatorRelease(handle);
}
//+------------------------------------------------------------------+
//| Release file handle |
//+------------------------------------------------------------------+
bool ReleaseFile(int handle, const string owner)
{
if(handle == INVALID_HANDLE) return true;
int idx = FindResource(handle, RES_FILE_HANDLE, owner);
if(idx >= 0)
{
// Actually close the file
FileClose((int)m_resources[idx].resource_id);
m_resources[idx].Clear();
m_total_released[RES_FILE_HANDLE]++;
m_count--;
return true;
}
// Not tracked - try to close anyway
FileClose(handle);
return true;
}
//+------------------------------------------------------------------+
//| Release all resources for an owner |
//+------------------------------------------------------------------+
int ReleaseAllForOwner(const string owner)
{
int released = 0;
for(int i = 0; i < m_capacity; i++)
{
if(m_resources[i].IsValid() && m_resources[i].owner == owner)
{
switch(m_resources[i].type)
{
case RES_INDICATOR_HANDLE:
IndicatorRelease((int)m_resources[i].resource_id);
break;
case RES_FILE_HANDLE:
FileClose((int)m_resources[i].resource_id);
break;
}
m_total_released[m_resources[i].type]++;
m_resources[i].Clear();
released++;
m_count--;
}
}
return released;
}
//+------------------------------------------------------------------+
//| Cleanup all resources (emergency cleanup) |
//+------------------------------------------------------------------+
void CleanupAllResources()
{
for(int i = 0; i < m_capacity; i++)
{
if(m_resources[i].IsValid())
{
switch(m_resources[i].type)
{
case RES_INDICATOR_HANDLE:
IndicatorRelease((int)m_resources[i].resource_id);
m_total_released[RES_INDICATOR_HANDLE]++;
break;
case RES_FILE_HANDLE:
FileClose((int)m_resources[i].resource_id);
m_total_released[RES_FILE_HANDLE]++;
break;
}
m_resources[i].Clear();
m_count--;
}
}
}
//+------------------------------------------------------------------+
//| Get leak report |
//+------------------------------------------------------------------+
string GetLeakReport()
{
string report = "=== Resource Leak Report ===\n";
// Calculate leaks per type
for(int type = 0; type < 5; type++)
{
int leaked = m_total_allocated[type] - m_total_released[type];
if(leaked > 0)
{
m_leak_detected[type] = leaked;
report += StringFormat(" %s: %d leaks detected\n",
TypeToString(type), leaked);
}
}
// Show active resources
if(m_count > 0)
{
report += StringFormat("\nActive Resources (%d total):\n", m_count);
for(int i = 0; i < m_capacity; i++)
{
if(m_resources[i].IsValid())
{
report += StringFormat(" [%s] %s: %s (owner: %s, file: %s:%d)\n",
TypeToString(m_resources[i].type),
m_resources[i].resource_id,
m_resources[i].description,
m_resources[i].owner,
m_resources[i].alloc_file,
m_resources[i].alloc_line);
}
}
}
return report;
}
//+------------------------------------------------------------------+
//| Get statistics |
//+------------------------------------------------------------------+
string GetStatistics()
{
string stats = "=== Resource Statistics ===\n";
for(int type = 0; type < 5; type++)
{
if(m_total_allocated[type] > 0)
{
int leaked = m_total_allocated[type] - m_total_released[type];
stats += StringFormat("%s: Alloc=%d, Rel=%d, Leaked=%d\n",
TypeToString(type),
m_total_allocated[type],
m_total_released[type],
leaked);
}
}
stats += StringFormat("\nCurrent Active: %d\n", m_count);
return stats;
}
private:
int FindFreeSlot()
{
for(int i = 0; i < m_capacity; i++)
{
if(!m_resources[i].IsValid())
return i;
}
// No free slots - expand array
int old_capacity = m_capacity;
m_capacity += 64;
ArrayResize(m_resources, m_capacity);
return old_capacity; // Return first new slot
}
int FindResource(long id, ENUM_RESOURCE_TYPE type, const string owner)
{
for(int i = 0; i < m_capacity; i++)
{
if(m_resources[i].IsValid() &&
m_resources[i].resource_id == id &&
m_resources[i].type == type &&
(owner == "" || m_resources[i].owner == owner))
{
return i;
}
}
return -1;
}
static string TypeToString(int type)
{
switch(type)
{
case RES_INDICATOR_HANDLE: return "INDICATOR";
case RES_FILE_HANDLE: return "FILE";
case RES_DYNAMIC_ARRAY: return "ARRAY";
case RES_OBJECT_PTR: return "OBJECT";
case RES_NETWORK_HANDLE: return "NETWORK";
}
return "UNKNOWN";
}
};
// Global resource manager
CResourceManager* g_resource_manager = NULL;
//+------------------------------------------------------------------+
//| Initialize resource management |
//+------------------------------------------------------------------+
bool InitializeResourceManager()
{
if(g_resource_manager != NULL)
delete g_resource_manager;
g_resource_manager = new CResourceManager();
Print("[ResourceManager] Initialized");
return true;
}
//+------------------------------------------------------------------+
//| Shutdown resource management |
//+------------------------------------------------------------------+
void ShutdownResourceManager()
{
if(g_resource_manager != NULL)
{
Print(g_resource_manager.GetStatistics());
Print(g_resource_manager.GetLeakReport());
// Cleanup any remaining resources
g_resource_manager.CleanupAllResources();
delete g_resource_manager;
g_resource_manager = NULL;
}
}
//+------------------------------------------------------------------+
//| Convenience macros for resource tracking |
//+------------------------------------------------------------------+
#define TRACK_INDICATOR(handle, desc) \
do { if(g_resource_manager != NULL && handle != INVALID_HANDLE) \
g_resource_manager.TrackIndicator(handle, __FILE__, desc, __FILE__, __LINE__); } while(0)
#define RELEASE_INDICATOR(handle) \
do { if(g_resource_manager != NULL) \
g_resource_manager.ReleaseIndicator(handle, __FILE__); \
else if(handle != INVALID_HANDLE) IndicatorRelease(handle); } while(0)
#define TRACK_FILE(handle, desc) \
do { if(g_resource_manager != NULL && handle != INVALID_HANDLE) \
g_resource_manager.TrackFile(handle, __FILE__, desc, __FILE__, __LINE__); } while(0)
#define RELEASE_FILE(handle) \
do { if(g_resource_manager != NULL) \
g_resource_manager.ReleaseFile(handle, __FILE__); \
else if(handle != INVALID_HANDLE) FileClose(handle); } while(0)
#define RELEASE_ALL_FOR_OWNER(owner) \
do { if(g_resource_manager != NULL) \
g_resource_manager.ReleaseAllForOwner(owner); } while(0)
#endif // CRESOURCE_MANAGER_MQH