The change in TROCustomSessionManager.DeleteSession
has been working steadily for the past two months, we are happy with it.
However, it is not enough as there is another potential deadlock when the timer in TROCustomSessionManager
elapses. At that point, it calls TROCustomSessionManager.ClearSessions
that starts by locking itself (fCritical.Enter
) and then calls DoClearSessions
In my case, this goes into TROInMemorySessionManager
that then calls TROCustomSessionManager.DeleteSession
with an already locked session manager. This means that the call to DoNotifySessionsChangesListener
is made with a locked session manager, which is exactly what we tried to avoid by doing the change mentioned above.
And in my case, sure enough, the event repository is locked, waiting for an event reply to come back. But this reply comes back in a thread that waits for the session manager to be free, effectively leading to a deadlock.
Now, the event repository is locked because we set BlockingEvents
to true
back in 2011 when it seemed to be required. The situation might have changed, I’ll evaluate reverting it back to its default value.
But still, even if this is no longer blocking, it may happen that the event repository is locked while the session manager is also from ClearSessions
which is why I suggest the following change:
procedure DoClearSessions(aSessionIds: TROSessionIDs; OnlyExpired : boolean); virtual; abstract;
procedure TROCustomSessionManager.ClearSessions(OnlyExpired : boolean);
var
lRetry: Boolean;
begin
CreateTimerByRequest;
try
fClearing := True;
lRetry := True;
while lRetry do begin
lRetry := False;
try
DoClearSessions(GetAllSessions, OnlyExpired);
except
on e: Exception do begin
if assigned(fOnException) then fOnException(EmptyGUID, e, lRetry);
if not lRetry then raise;
end;
end;
end;
finally
fClearing := False;
end;
end;
procedure TROInMemorySessionManager.DoClearSessions(aSessionIds: TROSessionIDs; OnlyExpired: boolean);
var
i : integer;
begin
if OnlyExpired then begin
for i := Low(aSessionIds) to High(aSessionIds) do
if CheckSessionIsExpired(aSessionIds[i]) then DeleteSession(aSessionIds[i], True);
end
else begin
for i := Low(aSessionIds) to High(aSessionIds) do
DeleteSession(aSessionIds[i], True);
end;
end;
Basically, remove the lock in TROCustomSessionManager.ClearSessions
and pass the list of session IDs to DoClearSessions
so that it can work on them without being locked.