forked from animatedread/Warrior_EA
AcquireConfigLock/ReleaseConfigLock moved off CExpertSignalAIBase into Expert/ConfigLock/CConfigLock, same view+adapter shape as BarrierHorizon/ExcursionHead. Stateful: m_configLockName is exclusive (grep-verified, nothing outside Lifecycle.mqh's old body touched it). Pure relocation - same FNV-1a hash, same owner-liveness check, same log wording. Left uncommitted mid-campaign; independently compile-verified in isolation now (0 errors/0 warnings) before this commit. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
118 lines
5.8 KiB
MQL5
118 lines
5.8 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| Warrior_EA |
|
|
//| AnimateDread |
|
|
//| |
|
|
//| THE PER-CONFIG CHART LOCK. STATELESS-except-one-field, same |
|
|
//| doctrine as BarrierHorizon\BarrierHorizon.mqh: m_configLockName |
|
|
//| is genuinely exclusive (grep-verified against the rest of |
|
|
//| Expert\ - nothing outside Lifecycle.mqh ever touched it), so this |
|
|
//| collaborator owns it as a real member instead of reaching it |
|
|
//| through the view. Every method below is a pure relocation of |
|
|
//| Expert\AIBase\Lifecycle.mqh's original AcquireConfigLock/ |
|
|
//| ReleaseConfigLock bodies - same order, same conditionals, no |
|
|
//| logic changes (the repeated m_activeFileName reads are cached to |
|
|
//| one local, since the view call replaces a direct field read that |
|
|
//| was itself invariant for the duration of the call). AcquireConfigLock |
|
|
//| has exactly one external caller (Topology.mqh, grep-verified), |
|
|
//| which keeps calling it unqualified - nothing to rewire. |
|
|
//+------------------------------------------------------------------+
|
|
#ifndef WARRIOR_CONFIGLOCK_CONFIGLOCK_MQH
|
|
#define WARRIOR_CONFIGLOCK_CONFIGLOCK_MQH
|
|
//--- Set when a start is refused for a reason no retry can change. OnInit's retry loop (Warrior_EA.mq5)
|
|
//--- reads it so the operator's LAST log line names the cause instead of "Failed to initialize Indicators".
|
|
string g_initFatalReason = "";
|
|
class CConfigLock
|
|
{
|
|
private:
|
|
CConfigLockView *m_view; // BORROWED - the signal owns the adapter, not the reverse
|
|
string m_configLockName;
|
|
public:
|
|
CConfigLock(void) : m_view(NULL), m_configLockName("") { }
|
|
void Bind(CConfigLockView *view) { m_view = view; }
|
|
|
|
bool Acquire(void);
|
|
void Release(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Claim the resolved model filename for this chart, terminal-wide. |
|
|
//+------------------------------------------------------------------+
|
|
bool CConfigLock::Acquire(void)
|
|
{
|
|
string activeFileName = m_view.ActiveFileName();
|
|
//--- FNV-1a over the resolved filename: every retrain-affecting input is already folded into that
|
|
//--- name, so equal names mean genuinely equal configs and nothing else has to be compared. Hashed
|
|
//--- because MQL5 caps global-variable names at 63 characters and the path alone exceeds that.
|
|
uint h = 2166136261;
|
|
int len = StringLen(activeFileName);
|
|
for(int i = 0; i < len; i++)
|
|
{
|
|
h ^= (uint)StringGetCharacter(activeFileName, i);
|
|
h *= 16777619;
|
|
}
|
|
string name = "WarriorAI_" + m_view.ShortId() + "_" + StringFormat("%08x", h);
|
|
long self = ChartID();
|
|
//--- Atomic: true means it did not exist and is now ours.
|
|
if(GlobalVariableTemp(name))
|
|
{
|
|
GlobalVariableSet(name, (double)self);
|
|
m_configLockName = name;
|
|
return true;
|
|
}
|
|
long owner = (long)GlobalVariableGet(name);
|
|
//--- Our own entry: this chart is re-initializing after a parameter change or a recompile whose
|
|
//--- OnDeinit never reached Release(). Reclaim it instead of refusing to start.
|
|
if(owner == self)
|
|
{
|
|
m_configLockName = name;
|
|
return true;
|
|
}
|
|
//--- Owner recorded but its chart no longer runs an expert - take the claim over. owner == 0 is
|
|
//--- deliberately NOT treated as stale: it means another instance created the variable microseconds
|
|
//--- ago and has not stamped its id yet, which is a live claim, not a dead one.
|
|
bool ownerAlive = false;
|
|
if(owner != 0)
|
|
{
|
|
long id = ChartFirst();
|
|
while(id >= 0)
|
|
{
|
|
if(id == owner)
|
|
{
|
|
ownerAlive = (StringLen(ChartGetString(id, CHART_EXPERT_NAME)) > 0);
|
|
break;
|
|
}
|
|
id = ChartNext(id);
|
|
}
|
|
}
|
|
if(owner != 0 && !ownerAlive)
|
|
{
|
|
GlobalVariableSet(name, (double)self);
|
|
m_configLockName = name;
|
|
return true;
|
|
}
|
|
Print(m_view.Id() + ": REFUSED to start - another chart is already training this exact configuration. Both" +
|
|
" would save into the same files (" + activeFileName + ".nnw plus its .cfg/.stats/checkpoints)" +
|
|
" and overwrite each other's progress with no error reported anywhere. Owner: " +
|
|
(owner != 0 ? "chart " + IntegerToString(owner) + " (" + ChartSymbol(owner) + " " +
|
|
EnumToString((ENUM_TIMEFRAMES)ChartPeriod(owner)) + ")" : "another chart, still initializing") +
|
|
". Change the enabled NN set (Use_MLP/Use_CONV/Use_LSTM/Use_CONVLSTM) or any retrain-affecting" +
|
|
" input on THIS chart so it trains its own model, or remove one of the two charts. Note the" +
|
|
" private build defaults ALL FOUR direction NNs on - two same-symbol charts left at their" +
|
|
" defaults land here.");
|
|
g_initFatalReason = "another chart already owns this configuration (" + activeFileName + ")";
|
|
return false;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Drop this instance's claim (see Acquire). |
|
|
//+------------------------------------------------------------------+
|
|
void CConfigLock::Release(void)
|
|
{
|
|
if(StringLen(m_configLockName) == 0)
|
|
return;
|
|
//--- Only delete a claim we still hold: if a later instance took this entry over via the stale-owner
|
|
//--- path above, deleting it here would silently hand the config to a third chart.
|
|
if((long)GlobalVariableGet(m_configLockName) == ChartID())
|
|
GlobalVariableDel(m_configLockName);
|
|
m_configLockName = "";
|
|
}
|
|
#endif // WARRIOR_CONFIGLOCK_CONFIGLOCK_MQH
|
|
//+------------------------------------------------------------------+
|