| //+------------------------------------------------------------------+
|
| //| 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
|