DEADSOFTWARE

sfs: API to temporary disable volume GC
[d2df-sdl.git] / src / sfs / sfs.pas
index 0153c91379793a6ad188f32819c15fffa34fdec0..eaabb964a9e0bb9f55a8f952deabbd9ecf29b8fc 100644 (file)
@@ -1,6 +1,7 @@
 // streaming file system (virtual)
 {$MODE DELPHI}
 {.$R-}
+{.$DEFINE SFS_VOLDEBUG}
 unit sfs;
 
 interface
@@ -168,6 +169,9 @@ procedure SFSUnregisterVolumeFactory (factory: TSFSVolumeFactory);
 // ïðèíèìàåòñÿ âî âíèìàíèå òîëüêî ïîñëåäíÿÿ òðóáà.
 function SFSAddDataFile (const dataFileName: TSFSString; top: Boolean=false): Boolean;
 
+// äîáàâèòü ñáîðíèê âðåìåííî
+function SFSAddDataFileTemp (const dataFileName: TSFSString; top: Boolean=false): Boolean;
+
 // äîáàâèòü â ïîñòîÿííûé ñïèñîê ñáîðíèê èç ïîòîêà ds.
 // åñëè âîçâðàùàåò èñòèíó, òî SFS ñòàíîâèòñÿ âëÿäåëüöåì ïîòîêà ds è ñàìà
 // óãðîáèò ñåé ïîòîê ïî íåîáõîäèìîñòè.
@@ -195,6 +199,16 @@ function SFSFileOpen (const fName: TSFSString): TStream;
 // ïîñëå èñïîëüçîâàíèÿ, íàòóðàëüíî, èòåðàòîð íàäî ãðîõíóòü %-)
 function SFSFileList (const dataFileName: TSFSString): TSFSFileList;
 
+// çàïðåòèòü îñâîáîæäåíèå âðåìåííûõ òîìîâ (ìîæíî âûçûâàòü ðåêóðñèâíî)
+procedure sfsGCDisable ();
+
+// ðàçðåøèòü îñâîáîæäåíèå âðåìåííûõ òîìîâ (ìîæíî âûçûâàòü ðåêóðñèâíî)
+procedure sfsGCEnable ();
+
+// for completeness sake
+procedure sfsGCCollect ();
+
+
 function SFSReplacePathDelims (const s: TSFSString; newDelim: TSFSChar): TSFSString;
 // èãíîðèðóåò ðåãèñòð ñèìâîëîâ
 function SFSStrEqu (const s0, s1: TSFSString): Boolean;
@@ -474,6 +488,63 @@ type
 var
   factories: TObjectList; // TSFSVolumeFactory
   volumes: TObjectList;   // TVolumeInfo
+  gcdisabled: Integer = 0; // >0: disabled
+
+
+procedure sfsGCCollect ();
+var
+  f, c: Integer;
+  vi: TVolumeInfo;
+  used: Boolean;
+begin
+  // collect garbage
+  f := 0;
+  while f < volumes.Count do
+  begin
+    vi := TVolumeInfo(volumes[f]);
+    if vi = nil then continue;
+    if (not vi.fPermanent) and (vi.fVolume.fRC = 0) and (vi.fOpenedFilesCount = 0) then
+    begin
+      // this volume probably can be removed
+      used := false;
+      c := volumes.Count-1;
+      while not used and (c >= 0) do
+      begin
+        if (c <> f) and (volumes[c] <> nil) then
+        begin
+          used := (TVolumeInfo(volumes[c]).fStream = vi.fStream);
+          if not used then used := (TVolumeInfo(volumes[c]).fVolume.fFileStream = vi.fStream);
+          if used then break;
+        end;
+        Dec(c);
+      end;
+      if not used then
+      begin
+        {$IFDEF SFS_VOLDEBUG}writeln('000: destroying volume "', TVolumeInfo(volumes[f]).fPackName, '"');{$ENDIF}
+        volumes.extract(vi); // remove from list
+        vi.Free; // and kill
+        f := 0;
+        continue;
+      end;
+    end;
+    Inc(f); // next volume
+  end;
+end;
+
+procedure sfsGCDisable ();
+begin
+  Inc(gcdisabled);
+end;
+
+procedure sfsGCEnable ();
+begin
+  Dec(gcdisabled);
+  if gcdisabled <= 0 then
+  begin
+    gcdisabled := 0;
+    sfsGCCollect();
+  end;
+end;
 
 
 // ðàçáèòü èìÿ ôàéëà íà ÷àñòè: ïðåôèêñ ôàéëîâîé ñèñòåìû, èìÿ ôàéëà äàííûõ,
@@ -750,10 +821,14 @@ begin
   if fOwner <> nil then
   begin
     Dec(fOwner.fOpenedFilesCount);
-    if not fOwner.fPermanent and (fOwner.fOpenedFilesCount < 1) then
+    if (gcdisabled = 0) and not fOwner.fPermanent and (fOwner.fOpenedFilesCount < 1) then
     begin
       f := volumes.IndexOf(fOwner);
-      if f <> -1 then volumes[f] := nil; // this will destroy the volume
+      if f <> -1 then
+      begin
+        {$IFDEF SFS_VOLDEBUG}writeln('001: destroying volume "', TVolumeInfo(volumes[f]).fPackName, '"');{$ENDIF}
+        volumes[f] := nil; // this will destroy the volume
+      end;
     end;
   end;
 end;
@@ -918,8 +993,11 @@ begin
   if fVolume <> nil then Dec(fVolume.fRC);
   Dec(TVolumeInfo(volumes[f]).fOpenedFilesCount);
   // óáü¸ì çàïèñü, åñëè îíà âðåìåííàÿ, è â íåé íåò áîëüøå íè÷åãî îòêðûòîãî
-  if not TVolumeInfo(volumes[f]).fPermanent and
-     (TVolumeInfo(volumes[f]).fOpenedFilesCount < 1) then volumes[f] := nil;
+  if (gcdisabled = 0) and not TVolumeInfo(volumes[f]).fPermanent and (TVolumeInfo(volumes[f]).fOpenedFilesCount < 1) then
+  begin
+    {$IFDEF SFS_VOLDEBUG}writeln('002: destroying volume "', TVolumeInfo(volumes[f]).fPackName, '"');{$ENDIF}
+    volumes[f] := nil;
+  end;
   inherited Destroy();
 end;
 
@@ -1012,7 +1090,7 @@ begin
     except
       FreeAndNil(st);
       // óäàëèì íåèñïîëüçóåìûé âðåìåííûé òîì.
-      if not vi.fPermanent and (vi.fOpenedFilesCount < 1) then volumes[result] := nil;
+      if (gcdisabled = 0) and not vi.fPermanent and (vi.fOpenedFilesCount < 1) then volumes[result] := nil;
       raise;
     end;
     // óðà. îòêðûëè ôàéë. êèäàåì â âîçäóõ ÷åï÷èêè, ïðîäîëæàåì ðàçâëå÷åíèå.
@@ -1094,8 +1172,7 @@ begin
   vi.fOpenedFilesCount := 0;
 end;
 
-function SFSAddSubDataFile (const virtualName: TSFSString; ds: TStream;
-  top: Boolean = false): Boolean;
+function SFSAddSubDataFile (const virtualName: TSFSString; ds: TStream; top: Boolean=false): Boolean;
 var
   tv: Integer;
 begin
@@ -1109,7 +1186,7 @@ begin
   end;
 end;
 
-function SFSAddDataFile (const dataFileName: TSFSString; top: Boolean = false): Boolean;
+function SFSAddDataFile (const dataFileName: TSFSString; top: Boolean=false): Boolean;
 var
   tv: Integer;
 begin
@@ -1122,6 +1199,20 @@ begin
   end;
 end;
 
+function SFSAddDataFileTemp (const dataFileName: TSFSString; top: Boolean=false): Boolean;
+var
+  tv: Integer;
+begin
+  try
+    if top then tv := -1 else tv := 1;
+    SFSAddDataFileEx(dataFileName, nil, tv, 0);
+    result := true;
+  except
+    result := false;
+  end;
+end;
+
+
 
 function SFSExpandDirName (const s: TSFSString): TSFSString;
 var
@@ -1208,7 +1299,7 @@ begin
       ps := TOwnedPartialStream.Create(vi, result, 0, result.Size, true);
     except
       result.Free();
-      if not vi.fPermanent and (vi.fOpenedFilesCount < 1) then volumes[f] := nil;
+      if (gcdisabled = 0) and not vi.fPermanent and (vi.fOpenedFilesCount < 1) then volumes[f] := nil;
       result := CheckDisk(); // îáëîì ñ datafile, ïðîâåðèì äèñê
       if result = nil then raise ESFSError.Create('file not found: "'+fName+'"');
       exit;
@@ -1281,7 +1372,7 @@ begin
     result := TSFSFileList.Create(vi.fVolume);
     Inc(vi.fVolume.fRC);
   except
-    if not vi.fPermanent and (vi.fOpenedFilesCount < 1) then volumes[f] := nil;
+    if (gcdisabled = 0) and not vi.fPermanent and (vi.fOpenedFilesCount < 1) then volumes[f] := nil;
   end;
 end;