Icon Multi-Threaded Applications

Introduction
DBISAM is internally structured to be thread-safe and usable within a multi-threaded application provided that you follow the rules that are outlined below.

Unique Sessions
DBISAM requires that you use a unique TDBISAMSession component for every thread that must perform any database access at all. Each of these TDBISAMSession components must also contain a SessionName property that is unique among all TDBISAMSession components in the application. Doing this allows DBISAM to treat each thread as a separate and distinct "user" and will isolate transactions and other internal structures accordingly. You may use the AutoSessionName property of the TDBISAMSession component to allow DBISAM to automatically name each session so that is unique or you may use code similar to the following:

var
   LastSessionValue: Integer;
   SessionNameSection: TRTLCriticalSection;

{ Assume that the following code is being executed
  within a thread }

function UpdateAccounts: Boolean;
var
   LocalSession: TDBISAMSession;   
   LocalDatabase: TDBISAMDatabase;
   LocalQuery:   TDBISAMQuery;
begin
   Result:=False;
   LocalSession:=GetNewSession;
   try
      LocalDatabase:=TDBISAMDatabase.Create(nil);
      try
         with LocalDatabase do
            begin
            { Be sure to assign the same session name
              as the TDBISAMSession component }
            SessionName:=LocalSession.SessionName;
            DatabaseName:='Accounts';
            Directory:='g:\accountdb';
            Connected:=True;
            end;
         LocalQuery:=TDBISAMQuery.Create(nil);
         try
            with LocalQuery do
               begin
               { Be sure to assign the same session and
                 database name as the TDBISAMDatabase
                 component }
               SessionName:=LocalSession.SessionName;
               DatabaseName:=LocalDatabase.DatabaseName;
               SQL.Clear;
               SQL.Add('UPDATE accounts SET PastDue=True');
               SQL.Add('WHERE DueDate < CURRENT_DATE'));
               Prepare;
               try
                  { Start the transaction and execute the query }
                  LocalDatabase.StartTransaction;
                  try
                     ExecSQL;
                     LocalDatabase.Commit;
                     Result:=True;
                  except
                     LocalDatabase.Rollback;
                  end;
               finally
                  UnPrepare;
               end;
               end;
         finally
            LocalQuery.Free;
         end;
      finally
         LocalDatabase.Free;
      end;
   finally
      LocalSession.Free;
   end;
end;

function GetNewSession: TDBISAMSession;
begin
   EnterCriticalSection(SessionNameSection);
   try
      LastSessionValue:=LastSessionValue+1;
      Result:=TDBISAMSession.Create(nil);
      with Result do
         SessionName:='AccountSession'+IntToStr(LastSessionValue);
   finally
      LeaveCriticalSection(SessionNameSection);
   end;
end;

{ initialization in application }
   LastSessionValue:=0;
   InitializeCriticalSection(SessionNameSection);
{ finalization in application }
   DeleteCriticalSection(SessionNameSection);

The AutoSessionName property is, by default, set to False so you must specifically turn it on if you want this functionality. You may also use the thread ID of the currently thread to uniquely name a session since the thread ID is guaranteed to be unique within the context of a process.

Unique Databases
Another requirement is that all TDBISAMDatabase components must also be unique and have their SessionName properties referring to the unique SessionName property of the TDBISAMSession component defined in the manner discussed above.

Unique Tables and Queries
The final requirement is that all TDBISAMTable and TDBISAMQuery components must also be unique and have their SessionName properties referring to the unique SessionName property of the TDBISAMSession component defined in the manner discussed above. Also, if a TDBISAMTable or TDBISAMQuery component refers to a TDBISAMDatabase component's DatabaseName property via its own DatabaseName property, then the TDBISAMDatabase referred to must be defined in the manner discussed above.

ISAPI Applications
ISAPI applications created using the Borland WebBroker components or a similar technology are implicitly multi-threaded. Because of this, you should ensure that your ISAPI application is thread-safe according to these rules for multi-threading when using DBISAM. Also, if you have simply dropped a TDBISAMSession component on the WebModule of a WebBroker ISAPI application, you must set its AutoSessionName property to True before dropping any other DBISAM components on the form so that DBISAM will automatically give the TDBISAMSession component a unique SessionName property and propogate this name to all of the other DBISAM components.

Further Considerations
There are some other things to keep in mind when writing a multi-threaded database application with DBISAM, especially if the activity will be heavy and there will be many threads actively running. Be prepared to handle any errors in a manner that allows the thread to terminate gracefully and properly free any TDBISAMSssion, TDBISAMDatabase, TDBISAMTable, or TDBISAMQuery components that it has created. Otherwise you may run into a situation where memory is being consumed at an alarming rate. Finally, writing multi-threaded applications, especially with database access, is not a task for the beginning developer so please be sure that you are well-versed in using threads and how they work before jumping into writing a multi-threaded application with DBISAM.
Image