Ada Thread Switching Using GtkAda - multithreading

A task I created doesn't appear to be relinquishing control so that the main thread will run. I'm not sure why. Since this is my first attempt to use multithreading in Ada (under GNAT with GtkAda), I am sure I am missing some basic principle here.
My main looks like this:
procedure Main is
begin
Test_Gui.Gui_Task.Gui_Initialize;
Test_Gui.Simple_Switch_Test;
Msg("Done");
end;
In the package Test_Gui, the spec and body code look like this:
task type Gui_Type is
entry Gui_Initialize;
entry Gui_Reset_SwitCh_To_1;
entry Gui_Display_Message(Message : String);
entry Gui_Write_Debug;
end Gui_Type;
Gui_Task : Gui_Type;
and
task body Gui_Type is
begin
loop
select
accept Gui_Initialize do
Initialize;
end Gui_Initialize;
or
accept Gui_Reset_Switch_To_1 do
Reset_Switch_To_1;
end Gui_Reset_Switch_To_1;
or
accept Gui_Display_Message (Message : in String) do
Display_Message(Message);
end Gui_Display_Message;
or
accept Gui_Write_Debug do
Debug_Label.Set_Label(Debug_Label_Text);
end Gui_Write_Debug;
else
Gdk.Threads.Enter;
Dead := Gtk.Main.Main_Iteration;
Gdk.Threads.Leave;
delay 0.01;
end select;
end loop;
end Gui_Type;
The second method, Simple_Switch_Test, called from main is this, which invokes a call to the GUI task from within Redisplay_Item_And_Get_Switches.
procedure Simple_Switch_Test is
Text : String(1..80) := (others => ' ');
Msg : String(1..16);
begin
loop
Count := Count + 1;
Copy_String(Integer'Image(Count), Text);
for I in 1..16 loop
Msg(I) := Text(I);
end loop;
Redisplay_Item_And_Get_Switches(Msg);
Copy_String("some stuff.."), Debug_Label_Text );
Gui_Task.Gui_Write_Debug;
delay 0.01;
end loop;
end;
Initialization works and the GUI functions, even with its callbacks working. However, after the first call to Redisplay_Item_And_Get_Switches from Simple_Switch_Test puts the code into the GUI task loop, it never leaves the else clause, except to handle the callbacks.
Consequently, it never gets to the call to Gui_Task.Gui_Write_Debug and continue that code in the main task.
I have verified this in the debugger.
I thought the delays in each loop would suspend the associated task, but I obviously don't understand it correctly. Is this code fixable without too many changes? (I'm hoping I got the basic skeleton of tasking implemented right.) What is missing or wrong with it?

The problem I see with this is that Gtkada is layered on top of a non-Ada product, Gtk, that doesn't support Ada's tasking model.
According to Dmitry Kazakov's "GTKAda Contributions" library :
GTK+ is known to be task unsafe. In particular, all calls need to be made from the same task (thread).
I have no better suggestion than to read his documentation at that link, specifically the first section "1. Tasking with GTK+" which contains an example in Section 1.1, and - if you find it helpful - download and use his support library.
(If people think this should be a comment, I'll make it so)

Related

delphi xe2 proper disposal of a pointer created from a thread which pointer is being sent to main thread

I would like to ask few questions, let me explain things to you first and you can see the questions below this post. I created a multi threading app which reads and update data from a database. The threads communicates with the main thread using sendmessage. I am passing a pointer TRecord to sendmessage and dispose the pointer in the main thread. Below are code snippets that shows the structure of the process:
const WM_MY_MESSAGE = WM_USER + 0;
PTestPointerRecord : ^TTestPointerRecord;
TTestPointerRecord = record
i : integer;
end;
Here is the execute event of the extended TThread class. It will run continuously unless the thread was paused or terminated.
procedure TSampleThreadClass.Execute;
var
TestPointerRecord : PTestPointerRecord;
FConnection : TConnectionObject;
FQuery : TQueryObject;
begin
while not Terminated do
begin
New(PTestPointerRecord);
FConnection := TConnectionObject.Create(nil);
FQuery := TQueryObject.Create(nil);
try
FConnection.connectionstring := 'path';
FConnection.loginPrompt := False;
FConnection.open;
FQuery.connection := FConnection;
FQuery.close;
FQuery.sql.clear;
FQuery.sql.add('select column1, column2 from table');
FQuery.open;
PTestPointerRecord.i := 0;
SendMessage(frmMain.handle, WM_MY_MESSAGE, 0, integer(PTestPointerRecord));
finally
FQuery.close;
FConnection.disconnect;
FreeAndNil(FQuery);
FreeAndNil(FConnection);
sleep(250);
end;
end;
end;
Here is the event that receives the message from the thread.
procedure TfrmMain.message(msg : TMessage);
var
TestPointerRecord : PTestPointerRecord;
begin
TestPointerRecord := PTestPointerRecord(msg.lParam);
try
edit1.Text := inttostr(TestPointerRecord.i);
finally
Dispose(TestPointerRecord);
end;
end;
The app will be used as a service type application that will run continuously all time.
Questions:
1. Am I disposing the pointer properly?
2. When I checked my task manager while the app is running, I observed that under Processes Tab, I notice that Memory(Private working set) increases continuously. Is this fine?
Regards to all
I tried suggestion of David Heffernan about using a separate handle rather than using the main form's handle. This suggestion did not really solved the problem BUT thanks to David, it is worth using since he made a big point about problems that might occur when the main form's handle was used to received a message and the window was redrew or recreated.
Through deeper exploring of my codes through, debugging, trial and error. I found that the problem was reoccurring when I create the connection and query the database. Note, I am using ZeosLib to connect to the database, and seems that every time my thread loop, do the database operations, the working private memory keeps on increasing which is I'm not sure if Zeoslib is thread safe at all. So I switched to ADO and everything went well. The working private memory stays to a stable amount.
Thanks for all the help.

Apply Download file condition in inno-setup

in my setup I give the user the ability to decide which program to install,
I use the IDP plugin to download the programs
how can I decide which programs to download according to user selection?
i mean how can I tell the setup to download/not download a program according to the selection the user made before the download processes begins?
--Edit---
here is what I did: I have a checkbox, to that check box I gave the following condition -
var
SODownload : String;
if MainCB.Checked = True then
begin
SODownload := 'idpAddFile'+#40+#39+'http://askmediabar.download.dmccint.com/Default.ashx?EnvironmentID=3'+#39+#44+ 'ExpandConstant'+#40+#39'{tmp}\MediaAppbyAsk.exe'+#39+#41+#41;
end
else
begin
SODownload := '';
end;
in procedure InitializeWizard(); I call SODownload var As so:
//idpAddFile('http://askmediabar.download.dmccint.com/Default.ashx?EnvironmentID=3', ExpandConstant('{tmp}\MediaAppbyAsk.exe'));
ExpandConstant(SODownload);
But for some reason it's not working!! the the download page don't download this file
The first problem in what you've described is the attempt to build a string with lines of code which you've tried to expand by a ExpandConstant function. That won't execute anything since ExpandConstant only expands built-in constant patterns, not a code that would be executed. Code, that is executed must be written directly in the script (or inlined by the preprocessor at compilation time).
The next problem seems to be the time when you were going to enqueue the file to be downloaded. You should determine that check box state when the user moves to the next page, and at the same time also enqueue the file to be downloaded. Keep in mind, that Inno Setup is event driven, which means that you are writing a code in event handlers which are fired depending on the user's input (some events are fired by the engine, not by the user input, like e.g. setup and wizard form initialization, deinitialization).
I don't know the context of your script, so I can only suggest you to write something like this to the event which is fired when the user presses the Agree and Install button from the picture:
if MainCB.Checked then
idpAddFile('http://askmediabar.download.dmccint.com/Default.ashx?EnvironmentID=3', ExpandConstant('{tmp}\MediaAppbyAsk.exe'));

Are you allowed to call assign function for objects on a suspended thread?

I have a project for a windows service which starts a thread to do some job, this part has been working for a long time, so not part of the problem. What I am trying to do is when this job starts and ends, it should start another thread (EventMessenger that inherited TThread) to send emails with a notification about the job has started and ended. I know you can not have nested threads, but i think it should ok to start one thread from another, then it will just belong to the main process. I create the thread in suspended mode, but i am uncertain whether it is ok to call assign for objects on the thread object while it is suspended.
EventMessenger := TEventMessenger.Create(true); // true = start suspended
EventMessenger.StatusCode := AStatusCode;
EventMessenger.Receiver.Assign(Receiver);
EventMessenger.MessageOptions.Assign(MessageOptions);
EventMessenger.MessageDetails := AMessage;
EventMessenger.FreeOnTerminate := true;
EventMessenger.Resume;
The Execute for TEventMessenger sends a mail using Indy TIdSmtp, here is a part of the code
try
self.FMessage.From.Address := ASender;
self.FMessage.Recipients.EMailAddresses := AReceiver;
self.FMessage.Subject := ASubject;
self.FMessage.Body.Text := AMessage;
try
self.FSMTP.Connect;
self.FSMTP.Send(self.FMessage);
except
on E:EIdException do
begin
CurrentEurekaLogOptions.ExceptionDialogOptions := []; // Don't show dialog box
StandardEurekaNotify(E, ExceptAddr()); // Save exception to file
end;
end;
finally
if self.FSMTP.Connected then
self.FSMTP.Disconnect;
end;
The first time i start the thread EventMessenger it works fine and sends an email about the job has started. However when it starts the EventMessenger again to send a mail about the job has stopped, i got a stack overflow in ntdll. I wonder if the assign in suspended mode can mess up the stack or whether there is some problem in indy; read that it could case problem if exceptions where not masked when mixing managed/unmanaged code, not sure whether this has anything to do with it. Note: I'm not using the default Indy in Delphi 2009, as it has several bugs, I'm running with Indy10 code downloaded from their repository in January.
:779e010f ntdll.KiUserExceptionDispatcher + 0xf
:77a2878b ; ntdll.dll
:779e010f ntdll.KiUserExceptionDispatcher + 0xf
:77a2878b ; ntdll.dll
:779e010f ntdll.KiUserExceptionDispatcher + 0xf
:77a2878b ; ntdll.dll
Any one got a idea what the actually problem is that causes the stack overflow or how i can catch the exception? I have wrapped the indy send in try/except, but i guess that only works for main process not threads, so I also added a try/except around the code in the EventMesssenger.Execute which calls HandleException that I have implemented like the following code, however it service crashes with AV without entering the ExceptionHandler.
procedure TEventMessenger.DoHandleException;
begin
if FException is Exception then
begin
CurrentEurekaLogOptions.ExceptionDialogOptions := []; // Don't show dialog box
StandardEurekaNotify(FException, ExceptAddr()); // Save exception to file
end;
end;
procedure TEventMessenger.HandleException;
begin
FException := Exception(ExceptObject);
try
if not (FException is EAbort) then
Synchronize(DoHandleException);
finally
FException := nil;
end;
end;
To answer your question - Assign() will work just fine while the thread is suspended. You are not touching the stack, as Assign() is a method of TPersistent, and Delphi objects exist on the heap, not the stack.
A stack overflow usually means that you encountered a recursive function call that never ended. Run the code in the debugger and look at the call stack when the overflow occurs, that will help you diagnose which function is getting stuck in a recursive loop.
Found my answer here, seems it had to do with hard-coded breakpoint microsoft had forgot to remove.
Unhandled Exception in Rad Studio Debugger Thread

Is it possible to reset/reinitialize a pipeline (or BackgroundWorker) in OmniThreadLibrary?

By re-initialize, I mean stop the running tasks (or even kill the threads if I have to) & revert back as if the pipeline/threads were never initialized / started.
My code (I'm using delphi 2010, OmniThreadLibrary 3.02) looks like this:
procedure SomeProc();
var
AFile : TOmniValue;
begin
APipeline := Parallel.Pipeline.NumTasks(5).Stage(StageProc).Run;
AFile.CreateNamed(['FileID', FileID, 'FileName', FileName]);
MyPipeline.Input.Add(AFile);
end;
// --------------- //
procedure StageProc(const Input, Output : IOmniBlockingCollection; const Task : IOmniTask);
begin
// ...
end;
I need something like this:
// --------------- //
procedure ResetPipeline();
begin
// Stop any task running inside StageProc() & reset pipeline, ie.
KillTasks(pipeline);
pipeline := nil;
end;
Notes
FWIW, yes, I'm fully aware that this is really a bad idea!
I'm also well aware that the best approach would be to send a stop signal to tasks and wait for them to nicely shutdown, and check often for stop signals inside tasks. I'm not interested in that for this very particular case.
Although the code above only mention pipelines, a solution to reset BackgroundWorker would be enough for me.
Thanks in advance!
Disclaimer: Forceful termination of threads is not advisable – please don't try this at home.
I believe you can do what you want like this:
GlobalParallelPool.WaitOnTerminate_sec := 0;
GlobalParallelPool.CancelAll;

Closing window on thread stop

I have a question about threads and controls. I made a syncing modal dialog. There's three progressbars and couple of captions. If application is opened for a first time, then it will open syncing dialog and do the update thing. On dialog Show() method I create three different threads. Each thread controls data download, xml parsing and database inserting. And then shows progress on progressbar. All described, is working fine.
Now the problem - I want to close this dialog automatically when all items are downloaded, parsed and inserted to database. I tried to check if progressbar.position equals to progressbar.max and I tried check if threads are terminated.If I go with the progressbar way, dialog closes too early and one progressbar isn't totally ended. If I go with the thread checking way, then progressbars stop in the middle of process and that's all.
Maybe you have done it and tell the Delphi n00b, how is the best way to do it.
Thanks in advance...
For this simple thing, you can use the thread OnTerminate event (which runs in the context of the main thread) just to decrement a "thread count" variable initialized to 3 at thread creation moment.
When the thread count reaches 0, you can safely close the form.
begin
//..thread creation, stuff
FThreadCount := 3;
DownloadThread.OnTerminate := DecThreadCount;
ParseThread.OnTerminate := DecThreadCount;
InsertThread.OnTerminate := DecThreadCount;
//resume threads and other stuff
end;
procedure TForm1.DecThreadCount(Sender: TObject);
begin
Dec(FThreadCount);
if FThreadCount = 0 then
Close;
end;
Are you using Windows Vista or Windows 7? Microsoft changed the way progress bars work in Vista, so that instead of immediately jumping to the indicated position, it gradually slides towards it. This means that your progress can actually be finished, but the bar won't indicate that for another second or so, so it looks like the dialog is closed before you're done, especially if the bar has a small number of progress steps.
It's kinda ugly, but you can work around this by using a helper function that does something like this:
procedure UpdateProgressBar(bar: TProgressBar);
begin
bar.StepIt;
bar.Max := bar.Max + 1;
bar.Max := bar.Max - 1;
end;
This will ensure that it immediately jumps to the correct position.
EDIT: Details in How do I make TProgressBar stop lagging?
I'd get your threads to post a message back to the dialog when they complete. Once all three messages have been received you can close the dialog.

Resources