Here's a problem to stretch the brain for those inclined.

I have implemented an audio buffering system using linked memory buffers
(Patent Pending - just joking).  The basic code is below.  WriteBuf is
called by the audio decoder in an independent thread, and read in a separate
thread in ReadBuf.

WriteBuf allocates new memory space for a new buffer, and ReadBuf function
frees the memory after it's read.  ReadBuf can ask for a different amount of
data than that written.

There is a buffer size limitation not shown, so memory does not blow out.

The code as written produces an access violation occasionally when using the
Seek procedure below.  The crash appears to always be in the memory manager
code and either in a memory allocation routine or in a memory freeing
routine.

The Seek procedure is executed from a 3rd thread.
 
No crash occurs when placing the same critical section around the read and
write code but I don't understand why it's required.  The read and write
routines should be accessing different memory buffers at all times, and this
seems to be the case when no seek is done.  It all works fine.

A read cannot occur until FBufferSize is incremented above zero meaning at
least one block has been created.  So again, I can't understand why it's
crashing.

I'm trying to avoid placing the same critical section around the read and
write routines to allow them to work freely and as fast as possible,
particularly when initially buffering.

Can anyone see anything obvious?


type
  PBufRec = ^TBufRec;
  TBufRec = Record
    Buffer: PByte;
    BufSize: Integer;
    NextRec: PBufRec;
  end;

var
  FReadBuf,FWriteBuf: PBufRef;
  FBufferSize: Integer;

// Initialize
  New(FReadBuf);
  FReadBuf.Buffer := nil;
  FReadBuf.NextRec := nil;
  FWriteBuf := FReadBuf;


procedure WriteBuf(Buf: Pointer; Len: Integer);
begin
  EnterCriticalSection(CSWrite);
  if CanInput then
  begin
    New(NewBuf);
    NewBuf.Buffer := nil;
    NewBuf.NextRec := nil;
    GetMem(FWriteBuf.Buffer,Len);
    Move(Buf^,FWriteBuf.Buffer^,Len);
    FWriteBuf.NextRec := NewBuf;
    FWriteBuf.BufSize := Len;
    FWriteBuf := NewBuf;
    InterlockedExchangeAdd(@FBufferSize,Len);
  End;
  LeaveCriticalSection(CSWrite);
end;

Function ReadBuf(Buf: Pointer; Len: Integer): Integer;
Var
  TempBuf: pByte;
begin
  EnterCriticalSection(CSRead);
  if not CanInput or (FBufferSize = 0) then Result := 0
  else begin
    if Len < FReadBuf.BufSize then
    begin
      Result := Len;
      Move(FReadBuf.Buffer^,Buf^,Result);
      TempBuf := FReadBuf.Buffer;
      Inc(TempBuf,Result);
      Move(TempBuf^,FReadBuf.Buffer^,FReadBuf.BufSize-Result);
      Dec(FReadBuf.BufSize,Result);
    end
    else begin
      Result := FReadBuf.BufSize;
      Move(FReadBuf.Buffer^,Buf^,Result);
      FreeMem(FReadBuf.Buffer);
      FReadBuf.Buffer := nil;
      NextBuf := FReadBuf.NextRec;
      Dispose(FReadBuf);
      FReadBuf := NextBuf;
    end;
    InterlockedExchangeAdd(@FBufferSize,-Result);
  end;
  LeaveCriticalSection(CSRead);
end;

procedure Seek(Pos: Integer);
begin
  CanInput := False;
  EnterCriticalSection(CSWrite);
  EnterCriticalSection(CSRead);
  FAudio.Seek(Pos); // not shown
  DeleteBuffers; // below
  New(FReadBuf);
  FReadBuf.Buffer := nil;
  FReadBuf.NextRec := nil;
  FWriteBuf := FReadBuf;
  LeaveCriticalSection(CSRead);
  LeaveCriticalSection(CSWrite);
  CanInput := True;
end;

procedure DeleteBuffers;
var
  NextBuf: PBufRec;
begin
  while (FReadBuf <> nil) and (FReadBuf.Buffer <> nil) do
  begin
    NextBuf := FReadBuf.NextRec;
    FreeMem(FReadBuf.Buffer);
    Dispose(FReadBuf);
    FReadBuf := NextBuf;
  end;
  if FReadBuf <> nil then
  begin
    Dispose(FReadBuf);
    FReadBuf := nil;
  end;
  FBufferSize := 0;
end;


Thanks,
Ross.

_______________________________________________
Delphi mailing list -> [email protected]
http://www.elists.org/mailman/listinfo/delphi

Reply via email to