0

I made TDataset descendant asynchronous, instead of sleep or ProcessMessages in main thread it works by events from network thread. So when Recordset is ready it calls

procedure TMySqlQuery.OrdinalOnDataReady(Sender: TObject);
begin
  AddToLog('OrdinalOnDataReady');
  FDataAvailable := true; // used in IsCursorOpen
  inherited Open;
  if Assigned(FParentOnDataReady) then
      FParentOnDataReady(self);
end;

It works, but sometime I have issues with GetRecord by Open called from this thread and DBGrid's GetFieldData called from main thread by DBGrid's DrawCells from Form's ProcessMessages. By logging both functions I see

[17:10:39] RecordToBuffer row 0
[17:10:39] len = 17 buf : 
00 E0 10 C3 00 0A 00 00 00 F0 10 C3 00 0B 00 00   |  .ïœ.ï“.....ÿ€.ï“....
00   |  .
[17:10:40] RecordToBuffer row 1
[17:10:40] len = 17 buf : 
00 00 FF C3 00 25 00 00 00 10 11 C3 00 0B 00 00   |  ..ÿï“.%.....ï“....
00   |  .
[17:10:40] ActiveBuffer
[17:10:40] len = 17 buf : 
00 E0 10 C3 00 0A 00 00 00 F0 10 C3 00 0B 00 00   |  .ïœ.ï“.....ÿ€.ï“....
00   |  .
...
more ActiveBuffer
...
[17:10:40] ActiveBuffer
[17:10:40] len = 17 buf : 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   |  ................
00   |  .
[17:10:40] len = 8 buf : 
00 00 00 00 00 00 00 00   |  ........

and on break by assert when ActiveBuffer column data is nil I can see that DBGrid attempts to read row higher than GetRecord readed into own internal FBuffers. For example if assertion fired at GetFieldData row 3 - FBuffers filled up to row 2 from total 36 rows available in Recordset. When I debugging step by step GetRecord with F8 there is no errors utnil I press F9 and get assertion at another record.

I'm not quite understand how exactly DBGrid works with TDataset (even stack trace is huge), but can this thread races be solved?

user2091150
  • 978
  • 12
  • 25
  • Accessing TDataSet from two threads simultaneusly is not allowed. TDataSet isn't thread safe. – Anders E. Andersen Jan 11 '14 at 19:19
  • 1
    Joanna Carter had a good set of articles about TDataSet internals. Maybe you still can find them somewhere.... For what I remember from my Delphi 5 experience with TDbf, db-aware controls only care about number of buffers, so if you, for example, reported you have 10 for buffers count - the control is free to read them all. If your buffers are not ready - then do not report you have ten buffers. – Arioch 'The Jan 11 '14 at 19:24
  • @Arioch'The - and where is Joannas article? – Branko Jan 11 '14 at 19:49
  • 2
    @Branko i'd quote myself: Maybe you still can find them somewhere... They were on her site, Joanna Carter Consulting, before she got fed up by EMBT attitudes – Arioch 'The Jan 11 '14 at 19:51
  • There is message at http://stackoverflow.com/questions/78475/in-delphi-is-tdataset-thread-safe about it is possible (second from bottom), without details with words "2) Everything done to that TDataSet instance must be done in context of the thread where it was created. That's not easy if you wanted to place e.g. a db grid on top of it." Actually I got problem with DBGrid and was wondering about solution. – user2091150 Jan 11 '14 at 22:49
  • `TDataset` is **not** threadsafe and therefore everything related/connected (Connection, Datasource) to this has to be done in the **same** thread context. It is allowed to use a dataset inside a thread, but it is not allowed to use this from a different thread context. All components on a form are created in the **main thread** context, so you are not allowed to use them in a different thread context. You may dislike it, but you must respect it – Sir Rufo Jan 12 '14 at 10:32

1 Answers1

0

Solution was quite simple: since data in TDataset's FBuffers (from Data.DB) filled with 0 if not initialized it is possible to find filled ActiveBuffer by GetRecord or not by adding yet another marker byte into record and assigning not 0 in GetRecord. So if DBGrid attempts to read uninitialized data I checking marker in GetFieldData, if 0 result false and exit. Since DBGrid extracting data to same cells more than once I still have it filled with proper data. It is workaround but it works.

user2091150
  • 978
  • 12
  • 25