I've built a few services in Delphi 7 and did not have this problem. Now that I started a new service app in XE2, it won't stop properly. I don't know if it's something I'm doing wrong or if it might be a bug in the XE2 services.
The execute procedure looks like this:
procedure TMySvc.ServiceExecute(Sender: TService);
begin
try
CoInitialize(nil);
Startup;
try
while not Terminated do begin
DoSomething; //Problem persists even when nothing's here
end;
finally
Cleanup;
CoUninitialize;
end;
except
on e: exception do begin
PostLog('EXCEPTION in Execute: '+e.Message);
end;
end;
end;
I never have an exception, as you can see I log any exception. PostLog saves to an INI file, which works fine. Now I do use ADO components, so I use CoInitialize() and CoUninitialize. It does connect to the DB and do its job properly. The problem only happens when I stop this service. Windows gives me the following message:
Then the service continues. I have to stop it a second time. The second time it does stop, but with the following message:
The log file indicates that the service did successfully free (OnDestroy event was logged) but it never successfully stopped (OnStop was never logged).
In my above code, I have two procedures Startup and Cleanup. These simply create/destroy and initialize/uninitialize my necessary things...
procedure TMySvc.Startup;
begin
FUpdateThread:= TMyUpdateThread.Create;
FUpdateThread.OnLog:= LogUpdate;
FUpdateThread.Resume;
end;
procedure TMySvc.Cleanup;
begin
FUpdateThread.Terminate;
end;
As you can see, I have a secondary thread running. This service actually has numerous threads running like this, and the main service thread is only logging the events from each thread. Each thread has different responsibilities. The threads are reporting properly, and they are also being terminated properly.
What could be causing this stop failure? If my posted code doesn't expose anything, then I can post more code later - just have to 'convert' it because of internal naming, etc.
EDIT
I just started NEW service project in Delphi XE2, and have the same issue. This is all my code below:
unit JDSvc;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes, JDSvcMgr;
type
TJDService = class(TService)
procedure ServiceExecute(Sender: TService);
private
FAfterInstall: TServiceEvent;
public
function GetServiceController: TServiceController; override;
end;
var
JDService: TJDService;
implementation
{$R *.DFM}
procedure ServiceController(CtrlCode: DWord); stdcall;
begin
JDService.Controller(CtrlCode);
end;
function TJDService.GetServiceController: TServiceController;
begin
Result := ServiceController;
end;
procedure TJDService.ServiceExecute(Sender: TService);
begin
while not Terminated do begin
end;
end;
end.
look at the source code for the Execute method:
procedure TServiceThread.Execute;
var
msg: TMsg;
Started: Boolean;
begin
PeekMessage(msg, 0, WM_USER, WM_USER, PM_NOREMOVE); { Create message queue }
try
// Allow initialization of the Application object after
// StartServiceCtrlDispatcher to prevent conflicts under
// Windows 2003 Server when registering a class object with OLE.
if Application.DelayInitialize then
Application.Initialize;
FService.Status := csStartPending;
Started := True;
if Assigned(FService.OnStart) then FService.OnStart(FService, Started);
if not Started then Exit;
try
FService.Status := csRunning;
if Assigned(FService.OnExecute) then
FService.OnExecute(FService)
else
ProcessRequests(True);
ProcessRequests(False);
except
on E: Exception do
FService.LogMessage(Format(SServiceFailed,[SExecute, E.Message]));
end;
except
on E: Exception do
FService.LogMessage(Format(SServiceFailed,[SStart, E.Message]));
end;
end;
as you can see if you don't assign a OnExecute method, Delphi will process SCM requests (Service Start, Stop, ...) until the service is stopped.
When you make an loop in the Service.Execute you must to process SCM requests yourself by calling ProcessRequests(False). A good habit is not to use Service.execute and start your workerthread in the Service.OnStart event and terminating/freeing it in the Service.OnStop event.
As told in the comments, another problem lies in the FUpdateThread.Terminate part.
David Heffernan was spot on with the Free/WaitFor comment.
Make sure you end your thread in correct fashion using synchronisation objects.
Related
I am reading "Delphi High performance" and there is something that I am missing. Given this code as test:
type TTest = class(TThread)
private
amemo: TMemo;
public
constructor Create(ss: boolean; memo: TMemo);
protected
procedure Execute; override;
end;
constructor TTest.Create(ss: boolean; memo: TMemo);
begin
inherited Create(ss);
FreeOnTerminate := true;
amemo := memo;
end;
procedure TTest.Execute;
var i: uint32;
begin
inherited;
i := 0;
while not Terminated do
begin
Inc(i);
Synchronize(procedure
begin amemo.Lines.Add(i.ToString) end);
Sleep(1000);
end;
end;
Very simply, this thread prints some numbers in a memo. I start the thread suspended and so I have to call this piece of code:
procedure TForm1.Button1Click(Sender: TObject);
begin
thread := TTest.Create(true, Memo1);
thread.Start;
end;
I have always stopped the thread calling thread.Terminate; but reading the book I see that Primoz stops a thread like this:
procedure TForm1.Button2Click(Sender: TObject);
begin
thread.Terminate;
thread.WaitFor; //he adds this method call
//FreeAndNil(thread)
//there is the above line as well in the code copied from the book but I have removed it since I have set FreeOnTerminate := true (so I dont have to worry about freeing the obj).
end;
At this point, if I run the code using only Terminate I have no problems. If I run the code with Terminate + WaitFor I get this error:
I have read more coding in delphi too and I see that Nick Hodges just makes a call to Terminate;. Is calling Terminate; enough to safey stop a thread? Note that I've set FreeOnTerminate := true so I don't care about the death of the object. Terminated should stop the execution (what is inside execute) and so it should be like this:
Call Terminated
Execute stops
Thread stops execution
Thread is now free (FreeOnTerminate := true)
Please tell me what I'm missing.
Note.
In the book the thread doesn't have FreeOnTerminate := true. So the thread needs to be freed manually; I guess that this is the reason why he calls
thread.Terminate;
thread.WaitFor;
FreeAndNil(thread)
I agree on Terminate (stop the thread= and FreeAndNil (free the object manually) but the WaitFor?
Please tell me what I'm missing.
The documentation for FreeOnTerminate explicitly says that you cannot use the Thread in any way after Terminate.
That includes your WaitFor call, which would work on a possibly already free'd object. This use-after-free can trigger the error above, among other even more "interesting" behaviours.
I wanted to realize a repetitive task in an OmniThreadLibrary worker task, that runs in another thread. The task should be executed every 3 seconds, for example.
Therefore I wrote a TOmniWorker descendant with an instance of TTimer as you can see below:
program Project14;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
Vcl.ExtCtrls,
Vcl.Forms,
OtlTaskControl;
type
TMyTimerWorker = class(TOmniWorker)
strict private
FTimer: TTimer;
procedure DoOnTimer(Sender: TObject);
protected
function Initialize: Boolean; override;
procedure Cleanup; override;
end;
{ TMyTimerWorker }
procedure TMyTimerWorker.Cleanup;
begin
FTimer.Free;
inherited;
end;
procedure TMyTimerWorker.DoOnTimer(Sender: TObject);
begin
Beep;
end;
function TMyTimerWorker.Initialize: Boolean;
begin
Result := inherited;
if not Result then exit;
FTimer := TTimer.Create(nil);
FTimer.OnTimer := DoOnTimer;
FTimer.Interval := 3000;
FTimer.Enabled := True; // note: this isn't necessary, but is added to avoid hints that 'Enabled' might be 'False'
end;
var
LTimerWorker: IOmniWorker;
begin
try
LTimerWorker := TMyTimerWorker.Create;
CreateTask(LTimerWorker).Unobserved.Run;
while True do
Application.ProcessMessages;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
I set breakpoints in Initialize and DoOnTimer. Former executes well but latter won't be called at all. BTW: Cleanup isn't called neither, so the task is still running.
What am I doing wrong? Is it impossible to use a TTimer in an OTL task? If yes, why?
UPDATE: I found a workaround for TTimer () but why does TTimer approach not work?
You TTimer-based code doesn't work because TTimer uses windows messages to trigger the timer event and windows messages are not processed in an OTL worker by default.
Call .MsgWait before .Run and internal worker loop will use MsgWaitForMultipleObjects instead of WaitForMultipleObjects which will allow for message processing.
Saying that, you really should not use TTimer in background tasks because - as others have said - TTimer is not threadsafe.
I created a new Windows Service project using wizard, putted some code, compiled it, run it with /INSTALL and then I tried to start it using net start myservice but I've got an service name not found error; then I went to the Control Panel in Services and when I try to start clicking the 'Start' link the dialog windows that shows up freezes at 50% of the progress bar indefinitely.
This is my first try to make a service to update the main system I am developing, and for a test I put a Timer to tell the time every one minute. Can anyone notice what is wrong and why it is behaving like that?
The DPR file with:
{...}
begin
if not Application.DelayInitialize or Application.Installing then
begin
Application.Initialize;
end;
Application.CreateForm(TZeusUpdateSevice, ZeusUpdateSevice);
Application.Run;
end.
and the PAS file with:
{...}
procedure ServiceController(CtrlCode: DWord); stdcall;
begin
ZeusUpdateSevice.Controller(CtrlCode);
end;
function TZeusUpdateSevice.GetServiceController: TServiceController;
begin
Result := ServiceController;
end;
procedure TZeusUpdateSevice.ServiceAfterInstall(Sender: TService);
var
regEdit : TRegistry;
begin
regEdit := TRegistry.Create(KEY_READ or KEY_WRITE);
try
regEdit.RootKey := HKEY_LOCAL_MACHINE;
if regEdit.OpenKey('\SYSTEM\CurrentControlSet\Services\' + Name,False) then
begin
regEdit.WriteString('Description','Mantém atualizados os arquivos e as credenciais da Plataforma Zeus.');
regEdit.CloseKey;
end;
finally
FreeAndNil(regEdit);
end;
end;
procedure TZeusUpdateSevice.ServiceStart(Sender: TService; var Started: Boolean);
begin
{ executa os processos solicitados pelo sistema }
Timer1.Enabled := True;
while not Terminated do ServiceThread.ProcessRequests(True);
Timer1.Enabled := False;
end;
procedure TZeusUpdateSevice.Timer1Timer(Sender: TObject);
begin
ShowMessage('Now, time is: ' + TimeToStr(Now));
end;
There are a couple of obvious problems:
You have an infinite loop in the OnStart event. This event allows you to perform one time actions when the service starts. That code belongs in OnExecute.
Services cannot show UI and so ShowMessage cannot work. You'll need to use a non-visual mechanism to give feedback.
Because your OnStart doesn't return, the SCM regards your service as not having started. So I guess that item 1 above is the explanation as to why your service won't start.
to write information on the processing state to the GUI inside a tcpserver.onexecute(..) function , i used the following command sequence
ExecuteDUMMYCommand(Global_Send_Record);
BitMap_PaintImageProcess;
TThread.Synchronize(nil, BitMap_PaintImageProcess);
The code is working well on some machines, but on a few it fails. The code execution stops atTThread.Synchronize command. I guess on these machines the function call is trapped inside a deadlock
Any chance to figure out the real problem behind ?
The procedure BitMap_PaintImageProcess , here I create a Bitmap and do a lot of painting stuff , but is seems that this code is never executed ?
I try to explain the very long code and reduce to the main points , the critical thread issues are hidden in processing the bitmap inside my Bitmapprocessingclass.
This class is accessed inside the GUIProcessing procedures of my ServerMainForm which also has the INDY TCP Server component.
{--------------- CLASS DEFINITION -----------------------------}
TBitMapProcessingClass = class()
FBitmap : TBitmap;
FList : TListOfSomething;
procedure ProcessTheBitmap(....);
......
(many many functions);
procedure Init;
procedure Free;
Procedure Create;
end;
TMainform = class(TForm)
MyServer : TIdTCPServer;
aBitMaoProcessingClass : TBitMaoProcessingClass;
procedure BitMap_PaintImageProcess;
procedure BitMap_ListProcess;
.....
end;
{------------------------- Implemantation ------------------------------}
procedure TMainform.IndyTCPServer.Onexecute()
begin
.......
ExecuteDUMMYCommand(Global_Send_Record);
BitMap_PaintImageProcess;
TThread.Synchronize(nil, BitMap_PaintImageProcess);
.......
end;
procedure TMainform.BitMap_PaintImageProcess;
begin
DoSomeServerVCLStuff(....);
aBitMapProcessingClass.ProcessTheBitmap;
DoSomeServerVCLStuff(....);
end;
Having no idea what BitMap_PaintImageProcess() does in fact, I have a few suppositions:
In the TThread.Synchronize call you try to read some data from the socket/idContext, but the data is not yet available. This will block the main thread until the data becomes available. But Indy's thread that is responsible for reading from the underlying socket buffer is currently blocked by your TThread.Synchronize call in the OnExecute event i.e. deadlock occurs;
In the TThread.Synchronize call you use Application.ProcessMessages (a common mistake);
In the OnExecute event you enter a critical section. Then during the TThread.Synchronize call you try to enter the same critical section again;
You modify the GUI in the onExecute event. Because onExecute is not thread safe, accessing VCL/FM from Indy's thread could lead to unpredictable results, including random deadlocks (hard to find).
I would suggest to use MadExcept / Eurekalog. They both have options to check if the main thread is "frozen". When that happens (during the deadlock) they will show you the current call stack. Having the call stack you can figure out which function is causing the deadlock.
Regarding the posted code:
procedure TMainform.IndyTCPServer.Onexecute()
begin
.......
ExecuteDUMMYCommand(Global_Send_Record);
BitMap_PaintImageProcess; //-> You do VCL stuff in the context of Indy's thread!
TThread.Synchronize(nil, BitMap_PaintImageProcess);
end;
In the BitMap_PaintImageProcess() you call DoSomeServerVCLStuff(....). Do not forget that OnExecute is fired from Indy's thread for the current context. I.e. you modify VCL from another thread (other from the Main Thread) which is not thread safe.
On your comment:
...but here my complex TBitmap processing Class must be alive the whole
time my Server is active...
If you have only one (global) instance for image processing, then what will happen if another client connects, while you are still processing the old connection (think Parallel :) )? Your image processing class should be instantiated separately for each new connection/context. For GUI updating you can use TIdNotify descendant.
A possible solution:
type
{ tIdNotify Stuff }
TVclProc= procedure(imgClass: tMyImageProcessingClass) of object;
tIdNotifyDescendant = (tIdNotify)
protected
fImgClass: tMyImageProcessingClass;
fProc: TVclProc;
procedure DoNotify; override;
public
class procedure updateVcl(imgClass: tMyImageProcessingClass; vclProc: TVclProc);
end;
procedure tIdNotifyDescendant.DoNotify;
begin
inherited DoNotify;
FProc(fImgClass);
end;
class procedure tIdNotifyDescendant.updateVcl(imgClass: tMyImageProcessingClass; vclProc: TVclProc);
begin
with Create do
begin
fImgClass := imgClass;
fProc := vclProc;
Notify;
end;
end;
{ Indy stuff & other logic }
procedure TForm1.IdTCPServer1Connect(AContext: TIdContext);
begin
// Create your instance when the client connects
AContext.Data := tMyImageProcessingClass.Create;
end;
procedure TForm1.IdTCPServer1Disconnect(AContext: TIdContext);
begin
// Do cleanup
if assinged(AContext.Data) then
(AContext.Data as tMyImageProcessingClass).Free // Casting just for clarity
end;
procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var
imgProcClass: tMyImageProcessingClass;
begin
imgProcClass := acontext.Data as tMyImageProcessingClass;
// Do image processing
// Notify GUI for the progress:
tIdNotifyDescendant.updateVcl(AContext.data as tMyImageProcessingClass);
end;
Tip: If you do JPEG processing and you use Draw() method have in mind this: TJPEGImage.Draw() is not thread safe
I added a few more details on my BitmapProcessingclass and the idea of a thread safe extension of the existing class...
I need the existing class u nchanged in others apps ... I need a extension inside my app with the indy server.
ONly one client my connect to one server, or he has to query the state of the server
type TBmpNotify = class(TIdNotify)
protected
FBMP: MyImageProcessingClass;
procedure DoNotify; override;
public
constructor Create(aBMP: MyImageProcessingClass);
function SetImageView(LL, UR: TPoint): Boolean;
procedure PaintBitMap;
function InitBitMap(x, y: Integer;
PixelFormat: TPixelFormat = pf24bit): Boolean;
destructor free;
end;
implementation
{ TBmpNotify }
constructor TBmpNotify.Create(aBMP: MyImageProcessingClass);
begin
// indise this class I also create
// class.TBitmap
// class.TList
// much more stuff ....
FBmp := MyImageProcessingClass.Create;
end;
procedure TBmpNotify.DoNotify;
begin
inherited;
end;
destructor TBmpNotify.free;
begin
FBmp.Free;
inherited;
end;
function TBmpNotify.InitBitMap(x, y: Integer;
PixelFormat: TPixelFormat): Boolean;
begin
// Write values to the List
// also modify TBitmap
// execution time of this function ~ 5 min
FBmp.InitBitMap(x,y,PixelFormat)
end;
procedure TBmpNotify.PaintBitMap;
begin
// much TBitmap, bitmap.canvas .... is used
// execution time of this function ~ 1 min
FBmp.PaintBitMap;
end;
function TBmpNotify.SetImageView(LL, UR: TPoint): Boolean;
begin
// this function takes about 1 min
FBmp.SetImageView(LL, UR);
end;
end.
For some specific needs i need to create procedure that waits for socket request (or answer) in dll:
TForm1 = class(TForm)
ServerSocket1: TServerSocket;
......
procedure MyWaitProc; stdcall;
begin
Go := false;
while not Go do
begin
// Wating...
// Application.ProcessMessages; // Works with this line
end;
end;
procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
begin
MessageBoxA(0, PAnsiChar('Received: '+Socket.ReceiveText), '', MB_OK);
Go := true;
end;
exports
MyWaitProc;
When I call Application.ProcessMessages everything works fine: application waits for request and then continues. But in my case calling Application.ProcessMessages causes to unlocking main form on host application (not dll's one). When I don't call Application.ProcessMessages application just hangs couse it cannot handle message...
So, how to create such a procedure that's wating for socket answer ?
Maybe there a way to wait for socket answer without using Application.ProcessMessages ?
EDIT
I also tried to use TIdTCPServer, for some reasons, the result is the same.
TForm1 = class(TForm)
IdTCPServer1: TIdTCPServer;
.....
procedure MyWaitProc; stdcall;
begin
Go := false;
while not Go do
begin
// Waiting ...
// Application.ProcessMessages;
end;
end;
procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var
s: string;
begin
s := AContext.Connection.Socket.ReadString(1);
AllText := AllText + s;
Go := True;
end;
TServerSocket runs in non-blocking mode by default, which depends on processing window messages. To remove that dependancy, you have to switch it to blocking mode instead.
TIdTCPServer runs in blocking mode exclusively, so no window messages. If you are having a problem with it, then you are misusing it. For example, in your TServerSocket code, you do not set Go = True until after a response has been received, but in your TServerSocket code you are setting Go = True before reading a response instead.
As an alternative, have a look at Indy's TIdSimpleServer component. TIdSimpleServer is synchronous and only accepts 1 connection at a time, whereas TIdTCPServer is asynchronous and accepts many connections at a time. For example:
TForm1 = class(TForm)
ServerSocket: TIdSimpleServer;
procedure MyWaitProc; stdcall;
var
s: String;
begin
ServerSocket.Listen;
s := ServerSocket.IOHandler.ReadLn;
ServerSocket.Disconnect;
MessageBox(0, PChar('Received: '+s), '', MB_OK);
end;
exports
MyWaitProc;
Rather than creating a loop that occasionally calls Application.ProcessMessages you can create a descendant of TThread and move the socket request to the TThread.Execute method. Use TThread.OnTerminate to notify your form(or any other class) when the thread has completed its work.
There is sample code which gives more details about how to use TThread.
There are several other 3rd party threading libraries that either provide more flexibility or are easier to use than TThread and I would highly recommend any of them over TThread if you are new to multi-threading.
Note: There are some serious side-effects to using Application.ProcessMessages. You are seeing one of them in your code with the dll unlocking the application's mainform. It breaks the single-threaded UI model the VCL is build upon. ProcessMessages has its place but using threads is more appropriate for the situation you're describing.
var Slowpoke: TMyLongRunningProcessThread;
procedure MyWaitProc(Completed:TNotifyEvent)
begin
Slowpoke := TMyLongRunningProcessThread.Create(True);
Slowpoke.FreeOnTerminate := True;
Slowpoke.OnTerminate := Completed;
Slowpoke.Resume;
end;
MyWaitProc returns immediately after starting the thread so the GUI is free to respond to user actions. When the thread terminates it calls the event handler pointed to by Completed.
Obviously if you need to retrieve data from the thread you'll want to either have the thread write to an accessible memory location before it Frees itself or remove the FreeOnTerminate so the data can be retreived from the thread through a property.