Stream read error

14,736

Solution 1

You're not using correct locking. You're acquiring a read lock on the array of cache entries, but once you find the item you want, you modify it. First, you explicitly modify it by assigning its Position property, and then you implicitly modify it by reading from it, which modifies its Position property again. When other code attempts to read from that same cache item, you'll have interference. If the source stream's Position property changes between the time the destination stream calculates how many bytes are available and the time it actually requests to read those bytes, you'll get a stream-read error.

I have a couple pieces of advice related to this:

  • Don't use streams as a storage device in the first place. You're apparently holding the contents of files. You're not going to change those, so you don't need a data structure designed for making sequential changes. Instead, just store the data in simple arrays of bytes: TBytes. (Also, use of TStringStream in particular introduces confusion over whether those strings' encodings are important. A simple file cache shouldn't be concerned with string encodings at all. If you must use a stream, use a content-agnostic class like TMemoryStream.)
  • Don't quell an exception that you haven't actually handled. In this code, you're catching all exception types, logging some information, clearing the cache, and then proceeding as though everything is normal. But you haven't done anything to resolve the problem that triggered the exception, so everything is not normal. Since you're not really handling the exception, you need to make sure it propagates to the caller. Call raise after to call Clear. (And when you log the exception, make sure you log the exception's ClassName value as well as its message.)

Solution 2

It looks like something external is blocking your stream files.

You could try to use Process Monitor to see what blocks it.

Another thing you can try is to open the stream in read-deny-write mode (please show us how you open the stream).

Something like this:

Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite) ;

Edit 1: Disregard the strike through part: you are using TStringStream.
I'll keep the answer just in case anyone ever gets this kind of error when using TFileStream.

Edit 2: Yuriy posted this interesting addendum, but I'm not sure it will work, as the BlobStream is not initialized, just like Robert Love suspected:

Function TCacheInMemory.CacheCheck(cName: String; Out BlobStream: TStringStream): Boolean; 
Begin 
  Result := False; 
  Try 
    If Not IfUseCache Then 
      exit; 
    BlobStream.SetSize(0);  
    BlobStream.Size := 0;  
    StreamValue(trim(cName), True, BlobStream);  
    If BlobStream.Size > 0 Then  
      Result := True;  
  Except  
    On E: Exception Do  
    Begin  
      x.xLogError('LogErrorCacheInMemory.txt', 'CheckCacheOutStream:' + E.Message + ' ItemsCount:' + IntToStr( High(fItems)) + 'Memory:' + IntToStr(x.GetMemoryInfoMemory));  
    End;  
  End;  
End; 

--jeroen

Share:
14,736
Yuriy
Author by

Yuriy

Updated on June 05, 2022

Comments

  • Yuriy
    Yuriy almost 2 years

    I'm getting this error message under heavy load. Here is code abstract and message from my error log. I tried everything I could think of. Any suggestion would be greatly appreciated.

    Procedure tCacheInMemory.StreamValue(Name: String; IgnoreCase: Boolean; Var Stream:     TStringStream);
    Var
      i: Integer;
    Begin
      i := 0;
      Try
        If Not active Then
          exit;
        arrayLock.BeginRead;
        Try
          i := Search(Name);
          If i > -1 Then Begin
            If fItems[i].value = Nil Then
              exit;
            fItems[i].value.Position := 0;
            Stream.Position := 0;
            Stream.CopyFrom(fItems[i].value, fItems[i].value.Size);
          End;
        Finally
          arrayLock.EndRead;
        End;
      Except { ...execution jumps to here }
        On E: Exception Do Begin
          x.xLogError('LogErrorCacheInMemory.txt', 'StreamValue:' + E.Message + ' ItemsCount:' + IntToStr( High(fItems)) + 'Memory:' + IntToStr(x.GetMemoryInfoMemory) + endLn + 'StreamSize : ' + IntToStr(fItems[i].value.Size) + ' i=' + IntToStr(i) + 'Name: ' + Name);
          Clear;
        End
      End;
    End;
    

    Log Entries:

     3/10/2011 10:52:59 AM: StreamValue:Stream read error ItemsCount:7562 Memory:240816
       StreamSize : 43 i=7506 Name: \\xxxxxxxx\WebRoot\\images\1x1.gif
    3/10/2011 12:39:14 PM: StreamValue:Stream read error ItemsCount:10172 Memory:345808
       StreamSize : 849 i=10108 Name: \\xxxxxxxx\WebRoot\\css\screen.add.css
    3/10/2011 3:45:29 PM: StreamValue:Stream read error ItemsCount:11200 Memory:425464
       StreamSize : 3743 i=11198 Name: \\xxxxxxxx\WebRoot\\JS\ArtWeb.js
    

    P.S.

    arrayLock: TMultiReadExclusiveWriteSynchronizer;
     fItems: Array Of rCache;
    Type
      rCache = Record
        Name: String;
        value: TStringStream;
        expired: TDateTime;
      End;
    

    And calling function:

    Function tCacheInMemory.CacheCheck(cName: String; Out BlobStream: TStringStream):   Boolean;
    Begin
    Result := False;
       If Not IfUseCache Then
          exit;
        BlobStream.SetSize(0);
        BlobStream.Size := 0;
        StreamValue(trim(cName), True, BlobStream);
        If BlobStream.Size > 0 Then
        Result := True;
    End;
    

    `