UAsyncIo.pas
上传用户:hylc_2004
上传日期:2014-01-23
资源大小:46800k
文件大小:23k
源码类别:

Delphi控件源码

开发平台:

Delphi

  1. //------------------------------------------------------------------------------
  2. // File: UAsyncIO.pas
  3. // Original files: asyncio.h, asyncio.c
  4. //
  5. // Desc: Base library with I/O functionality.
  6. //
  7. // Portions created by Microsoft are
  8. // Copyright (c) 2000-2002  Microsoft Corporation.  All rights reserved.
  9. //------------------------------------------------------------------------------
  10. unit UAsyncIo;
  11. interface
  12. uses
  13.   Windows, Contnrs, BaseClass, DirectShow9, DSUtil;
  14. type
  15.   //
  16.   // definition of CAsyncFile object that performs file access. It provides
  17.   // asynchronous, unbuffered, aligned reads from a file, using a worker thread
  18.   // on win95 and potentially overlapped i/o if available.
  19.   // !!! Need to use real overlapped i/o if available
  20.   // currently only uses worker thread, not overlapped i/o
  21.   TBCAsyncIo = class;
  22.   TBCAsyncStream = class;
  23.   LONGLONG = Int64;
  24.   PLONGLONG = ^LONGLONG;
  25.   //
  26.   //  Model the stream we read from based on a file-like interface
  27.   //
  28.   TBCAsyncStream = class
  29.   public
  30.     function SetPointer(APos: LONGLONG): HResult; virtual; abstract;
  31.     function Read(ABuffer: PByte; ABytesToRead: DWord;
  32.       AAlign: Boolean; out ABytesRead: DWord): HResult; virtual; abstract;
  33.     function Size(out AAvailable: LONGLONG): LONGLONG; overload; virtual; abstract;
  34.     function Size: LONGLONG; overload; virtual;
  35.     function Alignment: DWord; virtual; abstract;
  36.     procedure Lock; virtual; abstract;
  37.     procedure Unlock; virtual; abstract;
  38.     //procedure SetStopHandle(hevStop: THandle); virtual
  39.   end;
  40.   // represents a single request and performs the i/o. Can be called on either
  41.   // worker thread or app thread, but must hold pcsFile across file accesses.
  42.   // (ie across SetFilePointer/ReadFile pairs)
  43.   TBCAsyncRequest = class
  44.   private
  45.     FIO: TBCAsyncIo;
  46.     FStream: TBCAsyncStream;
  47.     FPos: LONGLONG;
  48.     FAligned: Boolean;
  49.     FLength: Integer;
  50.     FBuffer: PByte;
  51.     FContext: Pointer;
  52.     FUser: DWord;
  53.     Fhr: HResult;
  54.   public
  55.     // init the params for this request. Issue the i/o
  56.     // if overlapped i/o is possible.
  57.     function Request(AIO: TBCAsyncIo; AStream: TBCAsyncStream;
  58.       APos: LONGLONG; ALength: Integer; AAligned: Boolean;
  59.       ABuffer: PByte;
  60.       // filter's context
  61.       AContext: Pointer;
  62.       // downstream filter's context
  63.       AUser: DWord): HResult;
  64.     // issue the i/o if not overlapped, and block until i/o complete.
  65.     // returns error code of file i/o
  66.     function Complete: HResult;
  67.     // cancels the i/o. blocks until i/o is no longer pending
  68.     function Cancel: HResult;
  69.     // accessor functions
  70.     function GetContext: Pointer;
  71.     function GetUser: DWord;
  72.     function GetHResult: HResult;
  73.     // we set FLength to the actual length
  74.     function GetActualLength: Integer;
  75.     function GetStart: LONGLONG;
  76.   end;
  77.   TBCRequestList = class(TObjectList);
  78.   // this class needs a worker thread, but the ones defined in classesbase
  79.   // are not suitable (they assume you have one message sent or posted per
  80.   // request, whereas here for efficiency we want just to set an event when
  81.   // there is work on the queue).
  82.   //
  83.   // we create CAsyncRequest objects and queue them on m_listWork. The worker
  84.   // thread pulls them off, completes them and puts them on m_listDone.
  85.   // The events m_evWork and m_evDone are set when the corresponding lists are
  86.   // not empty.
  87.   //
  88.   // Synchronous requests are done on the caller thread. These should be
  89.   // synchronised by the caller, but to make sure we hold m_csFile across
  90.   // the SetFilePointer/ReadFile code.
  91.   //
  92.   // Flush by calling BeginFlush. This rejects all further requests (by
  93.   // setting m_bFlushing within m_csLists), cancels all requests and moves them
  94.   // to the done list, and sets m_evDone to ensure that no WaitForNext operations
  95.   // will block. Call EndFlush to cancel this state.
  96.   //
  97.   // we support unaligned calls to SyncRead. This is done by opening the file
  98.   // twice if we are using unbuffered i/o (m_dwAlign > 1).
  99.   // !!!fix this to buffer on top of existing file handle?
  100.   TBCAsyncIo = class
  101.   private
  102.     FReader: TBCCritSec;
  103.     FStream: TBCAsyncStream;
  104.     // locks access to the list and events
  105.     FCSLists: TBCCritSec;
  106.     // true if between BeginFlush/EndFlush
  107.     FFlushing: Boolean;
  108.     FListWork: TBCRequestList;
  109.     FListDone: TBCRequestList;
  110.     // set when list is not empty
  111.     FOnWork: TBCAMEvent;
  112.     FOnDone: TBCAMEvent;
  113.     // for correct flush behaviour: all protected by m_csLists
  114.     // nr of items not on listDone or listWork
  115.     FItemsOut: Integer;
  116.     // TRUE if someone waiting for m_evAllDone
  117.     FWaiting: Boolean;
  118.     // signal when FItemsOut goes to 0 if FWaiting
  119.     FOnAllDone: TBCAMEvent;
  120.     // set when thread should exit
  121.     FOnStop: TBCAMEvent;
  122.     FThread: THandle;
  123.     FThreadProc: TThreadProc;
  124.     function Size: LONGLONG;
  125.     // start the thread
  126.     function StartThread: HResult;
  127.     // stop the thread and close the handle
  128.     function CloseThread: HResult;
  129.     // manage the list of requests. hold m_csLists and ensure
  130.     // that the (manual reset) event hevList is set when things on
  131.     // the list but reset when the list is empty.
  132.     // returns null if list empty
  133.     function GetWorkItem: TBCAsyncRequest;
  134.     // get an item from the done list
  135.     function GetDoneItem: TBCAsyncRequest;
  136.     // put an item on the work list
  137.     function PutWorkItem(ARequest: TBCAsyncRequest): HResult;
  138.     // put an item on the done list
  139.     function PutDoneItem(ARequest: TBCAsyncRequest): HResult;
  140.     // called on thread to process any active requests
  141.     // ??? void ProcessRequests(void);
  142.     procedure ProcessRequests;
  143.     // initial static thread proc calls ThreadProc with DWORD
  144.     // param as this
  145.     function InitialThreadProc(pv: Pointer): DWord; stdcall;
  146.     function ThreadProc: DWord; virtual;
  147.   public
  148.     constructor Create(AStream: TBCAsyncStream);
  149.     destructor Destroy; override;
  150.     // open the file
  151.     function Open(AName: PChar): HResult; virtual;
  152.     // ready for async activity - call this before
  153.     // calling Request
  154.     function AsyncActive: HResult;
  155.     // call this when no more async activity will happen before
  156.     // the next AsyncActive call
  157.     function AsyncInactive: HResult;
  158.     // queue a requested read. must be aligned.
  159.     function Request(APos: LONGLONG; ALength: Integer;
  160.       AAligned: Boolean; ABuffer: PByte;
  161.       AContext: Pointer; AUser: DWord): HResult;
  162.     // wait for the next read to complete
  163.     function WaitForNext(ATimeout: DWord; AContext: PPointer;
  164.       out AUser: DWord; out AActual: Integer): HResult;
  165.     // perform a read of an already aligned buffer
  166.     function SyncReadAligned(APos: LONGLONG; ALength: Integer;
  167.       ABuffer: PByte; out AActual: Integer; AContext: Pointer): HResult;
  168.     // perform a synchronous read. will be buffered
  169.     // if not aligned.
  170.     function SyncRead(APos: LONGLONG; ALength: Integer;
  171.       ABuffer: PByte): HResult;
  172.     // return length
  173.     function Length(out ATotal: LONGLONG;
  174.       out AAvailable: LONGLONG): HResult;
  175.     // all Reader positions, read lengths and memory locations must
  176.     // be aligned to this.
  177.     function Alignment(out Al: Integer): HResult; overload;
  178.     function BeginFlush: HResult;
  179.     function EndFlush: HResult;
  180.     function Alignment: Integer; overload;
  181.     function IsAligned(Al: Integer): Boolean; overload;
  182.     function IsAligned(Al: LONGLONG): Boolean; overload;
  183.     //  Accessor
  184.     function StopEvent: THandle;
  185.   end;
  186. implementation
  187. // --- TBCAsyncStream ---
  188. function TBCAsyncStream.Size: LONGLONG;
  189. var
  190.   Available: LONGLONG;
  191. begin
  192.   Result := Size(Available);
  193. end;
  194. // --- TBCAsyncRequest ---
  195. function TBCAsyncRequest.Request(AIO: TBCAsyncIo; AStream: TBCAsyncStream;
  196.   APos: LONGLONG; ALength: Integer; AAligned: Boolean;
  197.   ABuffer: PByte; AContext: Pointer; AUser: DWord): HResult;
  198. begin
  199.   FIo     := AIo;
  200.   FStream := AStream;
  201.   FPos    := APos;
  202.   FLength := ALength;
  203.   FAligned:= AAligned;
  204.   FBuffer := ABuffer;
  205.   FContext:= AContext;
  206.   FUser   := AUser;
  207.   Fhr     := VFW_E_TIMEOUT;   // not done yet
  208.   Result  := S_OK;
  209. end;
  210. function TBCAsyncRequest.Complete: HResult;
  211. var
  212.   Actual: DWord;
  213.   Sample: IMediaSample;
  214. begin
  215.   FStream.Lock;
  216.   try
  217.     Fhr := FStream.SetPointer(FPos);
  218.     if (S_OK = Fhr) then
  219.     begin
  220.       Fhr := FStream.Read(FBuffer, FLength, FAligned, Actual);
  221.       if (Fhr = OLE_S_FIRST) then
  222.       begin
  223.         if Assigned(FContext) then
  224.         begin
  225.           Sample := IMediaSample(FContext);
  226.           Sample.SetDiscontinuity(True);
  227.           Fhr := S_OK;
  228.         end;
  229.       end;
  230.       if (Failed(Fhr)) then
  231.       else
  232.         if (Actual <> DWord(FLength)) then
  233.         begin
  234.           // tell caller size changed - probably because of EOF
  235.           FLength := Integer(Actual);
  236.           Fhr := S_FALSE;
  237.         end
  238.         else
  239.           Fhr := S_OK;
  240.     end;
  241.   finally
  242.     FStream.Unlock;
  243.     Result := Fhr;
  244.   end;
  245. end;
  246. function TBCAsyncRequest.Cancel: HResult;
  247. begin
  248.   Result := S_OK;
  249. end;
  250. function TBCAsyncRequest.GetContext: Pointer;
  251. begin
  252.   Result := FContext;
  253. end;
  254. function TBCAsyncRequest.GetUser: DWord;
  255. begin
  256.   Result := FUser;
  257. end;
  258. function TBCAsyncRequest.GetHResult: HResult;
  259. begin
  260.   Result := Fhr;
  261. end;
  262. function TBCAsyncRequest.GetActualLength: Integer;
  263. begin
  264.   Result := FLength;
  265. end;
  266. function TBCAsyncRequest.GetStart: LONGLONG;
  267. begin
  268.   Result := FPos;
  269. end;
  270. // --- TBCAsyncIo ---
  271. constructor TBCAsyncIo.Create(AStream: TBCAsyncStream);
  272. begin
  273.   FReader := TBCCritSec.Create;
  274.   FStream := AStream;
  275.   FCSLists := TBCCritSec.Create;
  276.   FFlushing := False;
  277.   FListWork := TBCRequestList.Create;
  278.   FListWork.OwnsObjects := False;
  279.   FListDone := TBCRequestList.Create;
  280.   FListDone.OwnsObjects := False;
  281.   FOnWork := TBCAMEvent.Create(True);
  282.   FOnDone := TBCAMEvent.Create(True);
  283.   FOnAllDone := TBCAMEvent.Create(True);
  284.   FOnStop := TBCAMEvent.Create(True);
  285.   FItemsOut := 0;
  286.   FWaiting := False;
  287.   // set when thread should exit
  288.   FThread := 0;
  289.   FThreadProc := nil;
  290. end;
  291. destructor TBCAsyncIo.Destroy;
  292. begin
  293.   // move everything to the done list
  294.   BeginFlush();
  295.   // shutdown worker thread
  296.   CloseThread();
  297.   // empty the done list
  298.   FListDone.Clear;
  299.   FListDone.Free;
  300.   FListDone := nil;
  301. end;
  302. function TBCAsyncIo.Open(AName: PChar): HResult;
  303. begin
  304.   Result := NOERROR;
  305. end;
  306. function TBCAsyncIo.Size: LONGLONG;
  307. begin
  308.   Assert(Assigned(FStream));
  309.   Result := FStream.Size;
  310. end;
  311. function TBCAsyncIo.StartThread: HResult;
  312. var
  313.   dwThreadID, dwErr: DWord;
  314. begin
  315.   if (FThread <> 0) then
  316.   begin
  317.     Result := S_OK;
  318.     Exit;
  319.   end;
  320.   // clear the stop event before starting
  321.   FOnStop.Reset;
  322.   FThread := CreateThread(nil, 0, @TBCAsyncIo.InitialThreadProc,
  323.     Self, 0, dwThreadID);
  324.   if (FThread = 0) then
  325.   begin
  326.     dwErr := GetLastError;
  327.     Result := HResultFromWin32(dwErr);
  328.     Exit;
  329.   end;
  330.   Result := S_OK;
  331. end;
  332. function TBCAsyncIo.CloseThread: HResult;
  333. begin
  334.   // signal the thread-exit object
  335.   FOnStop.SetEv;
  336.   if (FThread <> 0) then
  337.   begin
  338.     WaitForSingleObject(FThread, INFINITE);
  339.     CloseHandle(FThread);
  340.     FThread := 0;
  341.   end;
  342.   Result := S_OK;
  343. end;
  344. function TBCAsyncIo.GetWorkItem: TBCAsyncRequest;
  345. var
  346.   Req: TBCAsyncRequest;
  347. begin
  348.   Result := nil;
  349.   Req := nil;
  350.   FCSLists.Lock;
  351.   with FListWork do
  352.   try
  353.     if (Count <> 0) then
  354.     begin
  355.       Req := TBCAsyncRequest(Items[0]);
  356.       Delete(0);
  357.     end;
  358.     // force event set correctly
  359.     if (Count = 0) then
  360.       FOnWork.Reset;
  361.     Result := Req;
  362.   finally
  363.     FCSLists.UnLock;
  364.   end;
  365. end;
  366. function TBCAsyncIo.GetDoneItem: TBCAsyncRequest;
  367. var
  368.   Req: TBCAsyncRequest;
  369. begin
  370.   Result := nil;
  371.   Req := nil;
  372.   FCSLists.Lock;
  373.   with FListDone do
  374.   try
  375.     if (Count <> 0) then
  376.     begin
  377.       Req := TBCAsyncRequest(Items[0]);
  378.       Delete(0);
  379.     end;
  380.     Result := Req;
  381.     // force event set correctly if list now empty
  382.     // or we're in the final stages of flushing
  383.     // Note that during flushing the way it's supposed to work is that
  384.     // everything is shoved on the Done list then the application is
  385.     // supposed to pull until it gets nothing more
  386.     //
  387.     // Thus we should not set m_evDone unconditionally until everything
  388.     // has moved to the done list which means we must wait until
  389.     // cItemsOut is 0 (which is guaranteed by m_bWaiting being TRUE).
  390.     if (Count = 0) and ((Not FFlushing) or FWaiting) then
  391.       FOnDone.Reset;
  392.   finally
  393.     FCSLists.UnLock;
  394.   end;
  395. end;
  396. function TBCAsyncIo.PutWorkItem(ARequest: TBCAsyncRequest): HResult;
  397. begin
  398.   FCSLists.Lock;
  399.   try
  400.     if (FFlushing) then
  401.       Result := VFW_E_WRONG_STATE
  402.     else
  403.       try
  404.         FListWork.Add(ARequest);
  405.         // event should now be in a set state - force this
  406.         FOnWork.SetEv;
  407.         // start the thread now if not already started
  408.         Result := StartThread;
  409.       except
  410.         Result := E_OUTOFMEMORY;
  411.       end;
  412.   finally
  413.     FCSLists.UnLock;
  414.   end;
  415. end;
  416. function TBCAsyncIo.PutDoneItem(ARequest: TBCAsyncRequest): HResult;
  417. begin
  418.   // put an item on the done list - ok to do this when
  419.   // flushing
  420.   Assert(FCSLists.CritCheckIn);
  421.   try
  422.     FListDone.Add(ARequest);
  423.     // event should now be in a set state - force this
  424.     FOnDone.SetEv;
  425.     Result := S_OK;
  426.   except
  427.     Result := E_OUTOFMEMORY;
  428.   end;
  429. end;
  430. procedure TBCAsyncIo.ProcessRequests;
  431. var
  432.   Req: TBCAsyncRequest;
  433. begin
  434.   // lock to get the item and increment the outstanding count
  435.   repeat
  436.     FCSLists.Lock;
  437.     try
  438.       Req := GetWorkItem;
  439.       if (Req = nil) then
  440.         // done
  441.         Exit;
  442.       // one more item not on the done or work list
  443.       Inc(FItemsOut);
  444.     finally
  445.       FCSLists.UnLock;
  446.     end;
  447.     Req.Complete;
  448.     // regain critsec to replace on done list
  449.     FCSLists.Lock;
  450.     try
  451.       PutDoneItem(Req);
  452.       Dec(FItemsOut);
  453.       if (FItemsOut = 0) then
  454.         if (FWaiting) then
  455.           FOnAllDone.SetEv;
  456.     finally
  457.       FCSLists.UnLock;
  458.     end;
  459.   until False;
  460. end;
  461. function TBCAsyncIo.InitialThreadProc(pv: Pointer): DWord;
  462. begin
  463.   Result := ThreadProc;
  464. end;
  465. function TBCAsyncIo.ThreadProc: DWord;
  466. const
  467.   EvCount = 2;
  468. var
  469.   Events: array[0..EvCount - 1] of THandle;
  470. begin
  471.   // the thread proc - assumes that DWORD thread param is the
  472.   // Self pointer
  473.   Events[0] := FOnStop.Handle;
  474.   Events[1] := FOnWork.Handle;
  475.   repeat
  476.     case WaitForMultipleObjects(2, @Events, False, Infinite) of
  477.     WAIT_OBJECT_0+1:
  478.       // requests need processing
  479.       ProcessRequests;
  480.     else
  481.       begin
  482.         // any error or stop event - we should exit
  483.         Result := 0;
  484.         Exit;
  485.       end;
  486.     end;
  487.   until False;
  488. end;
  489. function TBCAsyncIo.AsyncActive: HResult;
  490. begin
  491.   Result := StartThread;
  492. end;
  493. function TBCAsyncIo.AsyncInactive: HResult;
  494. begin
  495.   Result := CloseThread;
  496. end;
  497. function TBCAsyncIo.Request(APos: LONGLONG; ALength: Integer;
  498.   AAligned: Boolean; ABuffer: PByte;
  499.   AContext: Pointer; AUser: DWord): HResult;
  500. var
  501.   Request: TBCAsyncRequest;
  502. begin
  503.   if AAligned then
  504.   begin
  505.     if (Not IsAligned(APos)) or (Not IsAligned(ALength)) or
  506.       (Not IsAligned(Integer(ABuffer))) then
  507.     begin
  508.       Result := VFW_E_BADALIGN;
  509.       Exit;
  510.     end;
  511.   end;
  512.   try
  513.     Request := TBCAsyncRequest.Create;
  514.   except
  515.     Result := E_OUTOFMEMORY;
  516.     Exit;
  517.   end;
  518.   Result := Request.Request(Self, FStream, APos, ALength,
  519.     AAligned, ABuffer, AContext, AUser);
  520.   if (Succeeded(Result)) then
  521.   begin
  522.     // might fail if flushing
  523.     Result := PutWorkItem(Request);
  524.   end;
  525.   if Failed(Result) then
  526.     Request.Free;
  527. end;
  528. function TBCAsyncIo.WaitForNext(ATimeout: DWord; AContext: PPointer;
  529.   out AUser: DWord; out AActual: Integer): HResult;
  530. var
  531.   Request: TBCAsyncRequest;
  532.   hr: HResult;
  533. begin
  534.   if (AContext = nil) then
  535.   begin
  536.     Result := E_POINTER;
  537.     Exit;
  538.   end;
  539.   // some errors find a sample, others don't. Ensure that
  540.   // *ppContext is NULL if no sample found
  541.   AContext^ := nil;
  542.   // wait until the event is set, but since we are not
  543.   // holding the critsec when waiting, we may need to re-wait
  544.   repeat
  545.     if (Not FOnDone.Wait(ATimeout)) then
  546.     begin
  547.       // timeout occurred
  548.       Result := VFW_E_TIMEOUT;
  549.       Exit;
  550.     end;
  551.     // get next event from list
  552.     Request := GetDoneItem;
  553.     if Assigned(Request) then
  554.     begin
  555.       // found a completed request
  556.       // check if ok
  557.       hr := Request.GetHResult;
  558.       if (hr = S_FALSE) then
  559.       begin
  560.         // this means the actual length was less than
  561.         // requested - may be ok if he aligned the end of file
  562.         if ((Request.GetActualLength +
  563.           Request.GetStart) = Size) then
  564.           hr := S_OK
  565.         else
  566.           // it was an actual read error
  567.           hr := E_FAIL;
  568.      end;
  569.       // return actual bytes read
  570.       AActual := Request.GetActualLength;
  571.       // return his context
  572.       AContext^ := Request.GetContext;
  573.       AUser := Request.GetUser;
  574.       Request.Free;
  575.       Result := hr;
  576.       Exit;
  577.     end
  578.     else
  579.       try
  580.         //  Hold the critical section while checking the list state
  581.         FCSLists.Lock;
  582.         if (FFlushing and (Not FWaiting)) then
  583.         begin
  584.           // can't block as we are between BeginFlush and EndFlush
  585.           // but note that if m_bWaiting is set, then there are some
  586.           // items not yet complete that we should block for.
  587.           Result := VFW_E_WRONG_STATE;
  588.           Exit;
  589.         end;
  590.       finally
  591.         FCSLists.UnLock;
  592.       end;
  593.     // done item was grabbed between completion and
  594.     // us locking m_csLists.
  595.   until False;
  596. end;
  597. function TBCAsyncIo.SyncReadAligned(APos: LONGLONG; ALength: Integer;
  598.   ABuffer: PByte; out AActual: Integer; AContext: Pointer): HResult;
  599. var
  600.   Request: TBCAsyncRequest;
  601. begin
  602.   if (Not IsAligned(APos)) or (Not IsAligned(ALength)) or
  603.     (Not IsAligned(Integer(ABuffer))) then
  604.   begin
  605.     Result := VFW_E_BADALIGN;
  606.     Exit;
  607.   end;
  608.   try
  609.     Request := TBCAsyncRequest.Create;
  610.   except
  611.     Result := E_OUTOFMEMORY;
  612.     Exit;
  613.   end;
  614.   Result := Request.Request(Self, FStream, APos, ALength,
  615.     True, ABuffer, AContext, 0);
  616.   if Failed(Result) then
  617.     Exit;
  618.   Result := Request.Complete;
  619.   // return actual data length
  620.   AActual := Request.GetActualLength;
  621.   Request.Free;
  622. end;
  623. function TBCAsyncIo.SyncRead(APos: LONGLONG; ALength: Integer;
  624.   ABuffer: PByte): HResult;
  625. var
  626.   Unused: Integer;
  627.   Req: TBCAsyncRequest;
  628. begin
  629.   // perform a synchronous read request on this thread.
  630.   // may not be aligned - so we will have to buffer.
  631.   if (IsAligned(APos) and IsAligned(ALength) and IsAligned(Integer(ABuffer))) then
  632.   begin
  633.     Result := SyncReadAligned(APos, ALength, ABuffer, Unused, nil);
  634.     Exit;
  635.   end;
  636.   // not aligned with requirements - use buffered file handle.
  637.   //!!! might want to fix this to buffer the data ourselves?
  638.   Req := TBCAsyncRequest.Create;
  639.   Result := Req.Request(Self, FStream, APos, ALength, False, ABuffer, nil, 0);
  640.   if Failed(Result) then
  641.     Exit;
  642.   Result := Req.Complete;
  643.   Req.Free;
  644. end;
  645. function TBCAsyncIo.Length(out ATotal: LONGLONG;
  646.   out AAvailable: LONGLONG): HResult;
  647. begin
  648.   ATotal := FStream.Size(AAvailable);
  649.   Result := S_OK;
  650. end;
  651. function TBCAsyncIo.Alignment(out Al: Integer): HResult;
  652. begin
  653.   Al := Alignment;
  654.   Result := S_OK;
  655. end;
  656. // cancel all items on the worklist onto the done list
  657. // and refuse further requests or further WaitForNext calls
  658. // until the end flush
  659. //
  660. // WaitForNext must return with NULL only if there are no successful requests.
  661. // So Flush does the following:
  662. // 1. set m_bFlushing ensures no more requests succeed
  663. // 2. move all items from work list to the done list.
  664. // 3. If there are any outstanding requests, then we need to release the
  665. //    critsec to allow them to complete. The m_bWaiting as well as ensuring
  666. //    that we are signalled when they are all done is also used to indicate
  667. //    to WaitForNext that it should continue to block.
  668. // 4. Once all outstanding requests are complete, we force m_evDone set and
  669. //    m_bFlushing set and m_bWaiting false. This ensures that WaitForNext will
  670. //    not block when the done list is empty.
  671. function TBCAsyncIo.BeginFlush: HResult;
  672. var
  673.   Req: TBCAsyncRequest;
  674. begin
  675.   // hold the lock while emptying the work list
  676.   FCSLists.Lock;
  677.   try
  678.     // prevent further requests being queued.
  679.     // Also WaitForNext will refuse to block if this is set
  680.     // unless m_bWaiting is also set which it will be when we release
  681.     // the critsec if there are any outstanding).
  682.     FFlushing := True;
  683.     repeat
  684.       Req := GetWorkItem;
  685.       if Not Assigned(Req) then
  686.         Break;
  687.       Req.Cancel;
  688.       PutDoneItem(Req);
  689.     until False;
  690.     // now wait for any outstanding requests to complete
  691.     if (FItemsOut > 0) then
  692.       begin
  693.           // can be only one person waiting
  694.           Assert(Not FWaiting);
  695.           // this tells the completion routine that we need to be
  696.           // signalled via m_evAllDone when all outstanding items are
  697.           // done. It also tells WaitForNext to continue blocking.
  698.           FWaiting := True;
  699.       end
  700.     else
  701.       begin
  702.         // all done
  703.         // force m_evDone set so that even if list is empty,
  704.         // WaitForNext will not block
  705.         // don't do this until we are sure that all
  706.         // requests are on the done list.
  707.         FOnDone.SetEv;
  708.         Result := S_OK;
  709.         Exit;
  710.       end;
  711.   finally
  712.     FCSLists.UnLock;
  713.   end;
  714.   Assert(FWaiting);
  715.   // wait without holding critsec
  716.   repeat
  717.     FOnAllDone.Wait;
  718.     // hold critsec to check
  719.     FCSLists.Lock;
  720.     try
  721.       if (FItemsOut = 0) then
  722.       begin
  723.         // now we are sure that all outstanding requests are on
  724.         // the done list and no more will be accepted
  725.         FWaiting := False;
  726.         // force m_evDone set so that even if list is empty,
  727.         // WaitForNext will not block
  728.         // don't do this until we are sure that all
  729.         // requests are on the done list.
  730.         FOnDone.SetEv;
  731.         Result := S_OK;
  732.         Exit;
  733.       end;
  734.     finally
  735.       FCSLists.UnLock;
  736.     end;
  737.   until False;
  738. end;
  739. function TBCAsyncIo.EndFlush: HResult;
  740. begin
  741.   FCSLists.Lock;
  742.   try
  743.     FFlushing := False;
  744.     Assert(Not FWaiting);
  745.     // m_evDone might have been set by BeginFlush - ensure it is
  746.     // set IFF m_listDone is non-empty
  747.     if (FListDone.Count > 0) then
  748.       FOnDone.SetEv
  749.     else
  750.       FOnDone.Reset;
  751.     Result := S_OK;
  752.   finally
  753.     FCSLists.UnLock;
  754.   end;
  755. end;
  756. function TBCAsyncIo.Alignment: Integer;
  757. begin
  758.   Result := FStream.Alignment;
  759. end;
  760. function TBCAsyncIo.IsAligned(Al: Integer): Boolean;
  761. begin
  762.   Result := ((Al and (Alignment - 1)) = 0);
  763. end;
  764. function TBCAsyncIo.IsAligned(Al: LONGLONG): Boolean;
  765. begin
  766.   Result := IsAligned(Integer(Al and $FFFFFFFF));
  767. end;
  768. function TBCAsyncIo.StopEvent: THandle;
  769. begin
  770.   Result := FOnDone.Handle;
  771. end;
  772. end.