403 lines
13 KiB
MQL5
403 lines
13 KiB
MQL5
//+------------------------------------------------------------------+
| |||
//| 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
|