Link to home
Start Free TrialLog in
Avatar of chrb
chrb

asked on

Hand out TTables

Hi,

I am going to write a ISAPI extension which is performing some database actions. The problem is that I dosn't want to create a new TTable for each request since that would slow down the requests to much. And one TTable would be too few since some request could be lengthy. What I was wondering was how to make it like each request asks for a TTable, and wait if it isn't some free. If there is an free the request will get it.
Is it something I have to remeber when doing this?
Hope somebody understand what I ment.

Thanks

With best regards
Christian Bøhn
Avatar of inter
inter
Flag of Türkiye image

Hi,

If I can understand, Say we have a N resources (TTables on our form-or you may prefer to create them dynamically-) For being clear lets say we have 4 tables for performing DB operations. Lets call these as Table1, Table2, Table3, Table4.  We do the following:

1 - On creation of the application store them in a list and mark their states:(lets say it is var TableList : TStringList;
..
  TableList := TStringList.Create;
  TableList.AddObject('Idle', Table1);
  TableList.AddObject('Idle', Table2);
  TableList.AddObject('Idle', Table3);
  TableList.AddObject('Idle', Table4);
..

2 - When a request is made check for them and return one of the idle tables:
function Form1.GetIdleTable:TTable;
var
  i : integer;
begin
  Result := nil; // assume not found
  for i:=0 to TableList.Count-1 do
    if TableList[i] = 'Idle' then
    begin
      Result := TTable(TableList.Objects[i]);
      TableList[i] := 'Busy';
      Break;
    end;
end;
3 - Write down a method so that sub tasks can release the table when finished their tasks
procedure Form1.ReleaseTable(T : TTable);
var
  i : integer;
begin
  for i:=0 to TableList.Count-1 do
    if TableList.Objects = Pointer(T)  then
    begin
      TableList[i] := 'Idle';
      Break;
    end;
end;
4 - On caller of the method in step 3, wait until an idle table returns
...
  MyTable := GetIdleTable;
  while MyTable = nil do MyTable := GetIdleTable;
  // now use MyTable for the querry
...
5 - After using a the table a process should release it as follows:
...
   // Assume we have done with MyTable obtained in step 4
  ReleaseTable(MyTable);
...

Is this ok?
Regards
Igor
Avatar of chrb
chrb

ASKER

I will take a look at it, but as far as I see it isn't thread safe. I forgot to meantion that this was going to be access by a lot of threads. But it shouldn't be hard to add some critical sections?

Chr
Hi,

You are surely right. Here is a critical section I wrote before. If you can, the threads and criticals section objects be in the same unit: so in your thread unit declare
...
implementation
Uses
   Sync;
var
  MyCs : TCriticalSection;
...
initialization
  MyCs := TCriticalSection.Create;
finalization
  MyCs.Free;
end.

THEN IN YOUR THREADS CODE DO:

procedure ThreadMain...
begin
...
   if NeedsProcessing then
   begin
     MyCs.Lock;
     MyTable := GetIdleTable;
     MyCs.UnLock;
   end;
...
   if FinishedProcessing then
   begin
     MyCs.Lock;
     ReleaseTable(MyTable);
     MyCs.Unlock;
  end;
..
end;

THIS IS THE CODE FOR SYNC IF YOU THERE RESPOND PLEASE

unit Synch;

interface
uses
  Windows;

type
  TCriticalSection = class(TObject)
  private
    GlobalCriticalSection : TRTLCriticalSection;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Lock;
    procedure UnLock;
  end;

  // By now only named mutexes are supported
  TMutex = class(TObject)
  private
    HMutex : THandle;    // This one is mutex handle
    HandleValid : boolean;
    FName  : string;     // This one is mutex name
  public
    constructor Create(AName : string);
    destructor Destroy; override;
    procedure Lock;
    procedure UnLock;
  end;

implementation
uses
  SysUtils;
constructor TCriticalSection.Create;
begin
  InitializeCriticalSection(GlobalCriticalSection);
end;

destructor TCriticalSection.Destroy;
begin
  DeleteCriticalSection(GlobalCriticalSection);
  inherited Destroy;
end;

procedure TCriticalSection.Lock;
begin
  EnterCriticalSection(GlobalCriticalSection);
end;

procedure TCriticalSection.UnLock;
begin
  LeaveCriticalSection(GlobalCriticalSection);
end;

constructor TMutex.Create(AName : string);
var
  Str : array[0..255] of char;
begin
  FName := AName;
  HandleValid := false;
  StrPCopy(Str, FName);
  HMutex := CreateMutex(
            nil,     // security NONE
            false,   // no initial owner
            Str);  // mutex name
  if (HMutex = NULL) then
  begin
  end;
end;

destructor TMutex.Destroy;
begin
  // Do nothing since mutexes automatically deleted
  inherited Destroy;
end;

procedure TMutex.Lock;
var
  dwWaitResult : DWORD;
begin
// wait for ovnership time out 0 by now
   dwWaitResult := WaitForSingleObject(HMutex, INFINITE);
   case dwWaitResult of
     WAIT_OBJECT_0  : HandleValid := true;//OK
     WAIT_TIMEOUT: ;
     WAIT_ABANDONED: ;
   end;
end;

procedure TMutex.UnLock;
begin
  if HandleValid then
  begin
    ReleaseMutex(HMutex);
    HandleValid := false;
  end;
end;

end.



Sorry, I think you create a thread when one request comes and needs processing. Them complete thread can be:

procedure TMyThread.Execute;
var
  Done : boolean;
  NeedsProcessing : boolean;
  FinishedProcessing : boolean;
begin
  Done := false;
  NeedsProcessing := true;
  FinishedProcessing := false;
  while not Done do
  begin
    if NeedsProcessing then
    begin
      MyCs.Lock;
      MyTable := GetIdleTable;
      if MyTable <> nil then
      begin
        // Process the table here
        // Signal For release
        NeedsProcessing := False;
        FinisgedProcessing := True; // querry to release table
      end;  
      MyCs.UnLock;
    end;
    if FinishedProcessing then
    begin
      MyCs.Lock;
      ReleaseTable(MyTable);
      MyCs.Unlock;
      Done := true; //terminate thread
    end;
  end; //while not done
end;


Please notify me if you can I should leave in 20 mins
Igor
Or in simplified form:(actually there is a bug in prev version:

procedure TMyThread.Execute;
var
  Done : boolean;
  NeedsProcessing : boolean;
begin
  Done := false;
  NeedsProcessing := true;
  while not Done do
  begin
    if NeedsProcessing then
    begin
      MyCs.Lock;
      MyTable := GetIdleTable;
      MyCs.UnLock; //unlock so other can acquire it
      if MyTable <> nil then // if nil wait for next pass
      begin
        // Process the table here
........................  
        // Signal For release
        NeedsProcessing := False;
        MyCs.Lock;
        ReleaseTable(MyTable);
        MyCs.Unlock;
        Done := true; //terminate thread
      end; // if table...
    end; // if needs...
  end; //while not done...
end;

Regards,
Igor
Avatar of chrb

ASKER

I am going to access it from ISAPI extension. But will the database actions take place in the thread called from, or in the thread created ? And I heard something about database sessions when using it in theads? I will try you code. Thanks!

Chr
Hi,

I really do not program with ISAPI. But done something very much like above with RPC. So the code above is a bit elaborated pseudo-code, there may be some bugs but the idea is there. By now thats all I can.

Have a nice day
Igor
Avatar of chrb

ASKER

Anyway you have given me a very good push. So send a message as anwear.
ASKER CERTIFIED SOLUTION
Avatar of inter
inter
Flag of Türkiye image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial