403 lignes
13 Kio
MQL5
403 lignes
13 Kio
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
|