Hello,
I am using Lazarus 1.8.4 and fpc 3.0.4 both installed using official DEB packages from lazarus web site. Target platform is Linux amd64.
I have a thread that I make a connection to a PostgreSQL database using zeoslib components. My main application is also making a connection to very same database. I have defined "-dUseCThreads" in my custom options and necessary thread units are in use by final executable.
My thread, I am getting Access violation that I failed to find the reason. Thread code is nothing too big so I am posting it all below:
unit uUserClean;
{$mode objfpc}{$H+}
interface
uses
ZConnection,
ZDataset,
Classes,
SysUtils;
type
TUserClean = class(TThread)
protected
FDB: TZConnection;
FqryUsers: TZReadOnlyQuery;
FqryDropUser: TZQuery;
procedure Execute; override;
procedure Log(const Value: string);
public
LogDir: string;
constructor Create(); reintroduce; overload;
destructor Destroy(); override;
end;
implementation
{ TUserClean }
constructor TUserClean.Create();
begin
inherited Create(True);
FreeOnTerminate := True;
FDB := TZConnection.Create(nil);
FDB.Protocol := 'postgresql';
FDB.Database := 'pars';
FDB.HostName := 'localhost';
FDB.User := 'parsuser';
FDB.Password := 'passwordforpars';
FqryUsers := TZReadOnlyQuery.Create(nil);
FqryUsers.Connection := FDB;
FqryUsers.SQL.Text := 'select usename from pg_user where usename like ''pars.%'' and valuntil < current_timestamp';
FqryDropUser := TZQuery.Create(nil);
FqryDropUser.Connection := FDB;
FqryDropUser.SQL.Text := 'DROP USER :username';
end;
destructor TUserClean.Destroy();
begin
Log('TUserClean.Execute(): Thread finishing...');
FqryUsers.Free();
FqryDropUser.Free();
FDB.Free();
inherited;
end;
procedure TUserClean.Log(const Value: string);
var
F: TextFile;
Prefix: string;
FileName: string;
begin
Prefix := FormatDateTime('yyyy-mm-dd hh:nn:ss.zzz ', Now());
FileName := LogDir + FormatDateTime('yyyy-mm', Now()) + '_thread.log';
{$I-}
AssignFile(F, FileName);
if FileExists(FileName) then
Append(F)
else
ReWrite(F);
if IOResult <> 0 then Exit();
try
WriteLn(F, Prefix + Value);
finally
CloseFile(F);
end;
end;
procedure TUserClean.Execute();
var
I: Integer;
begin
Log('TUserClean.Execute(): Running execute...');
while not Terminated do
begin
Log('Looping...');
try
while not FDB.Connected do
begin
Log('Connecting database');
try
FDB.Connect();
except
on E: Exception do
begin
Log('Error: TUserClean.Create(): ' + E.Message);
Exit();
end;
end;
for I := 0 to 100 do
begin
if Terminated then Exit();
Sleep(100);
end;
Log('Retrying database connection');
end;
if Terminated then Exit();
if FqryUsers.Active then FqryUsers.Refresh() else FqryUsers.Open();
if FqryUsers.RecordCount > 0 then
begin
Log('There are ' + FormatFloat('#,##0', FqryUsers.RecordCount) + ' users that needs to be removed from system.');
FqryUsers.First();
while not FqryUsers.Eof do
begin
Log('Removing user: ' + FqryUsers.Fields[0].AsString);
FqryDropUser.ParamByName('username').AsString := FqryUsers.Fields[0].AsString;
try
FqryDropUser.ExecSQL();
except
on E: Exception do
begin
Log('Remove failed: ' + E.Message);
end;
end;
if Terminated then Exit();
FqryUsers.Next();
end;
end;
Log('Sleeping until next check');
for I := 0 to 100 do
begin
if Terminated then Exit();
Sleep(100);
end;
except
on E: Exception do
begin
Log('Error: ' + E.Message);
end;
end;
end;
Log('TUserClean.Execute(): Terminated');
end;
end.
Here is how I call it:
Log('Starting user cleanup thread'); // Main application log routine. Writing in a different file than the thread log
CleanupThread := TUserClean.Create(True);
Log('Adjusting user cleanup thread parameters');
CleanupThread.LogDir := LogDir;
CleanupThread.Start();
And, here is my thread log file contents:
ek@greenkey:~/pars/logs$ head 2018-08_thread.log
==> 2018-08_thread.log <==
2018-08-03 12:03:19.878 TUserClean.Execute(): Running execute...
2018-08-03 12:03:19.879 Looping...
2018-08-03 12:03:19.879 Error: Access violation
2018-08-03 12:03:19.879 Looping...
2018-08-03 12:03:19.879 Error: Access violation
2018-08-03 12:03:19.879 Looping...
2018-08-03 12:03:19.879 Error: Access violation
2018-08-03 12:03:19.879 Looping...
2018-08-03 12:03:19.879 Error: Access violation
2018-08-03 12:03:19.879 Looping...
Looking at below lines in Execute block
Log('Looping...');
try
while not FDB.Connected do
begin
Log('Connecting database');
By looking at error in log file, I would say "FDB" is not created. Otherwise log should contain "Connecting database" text in it.
I could not see what is my mistake here. "FDB" class is created when thread is created. It is a private variable so it should be used in all class' procedures/units.
I appreciate any help.
Thanks & regards,
Ertan