Is it safe to call Exit on try except?
Or should I call raise instead?
I tried both example below and at raise example, the trace went through Delphi's internal library code. While exit just exit the procedure and nothing more.
I read that it's better to preserve application stack or queue or something like that. Will calling exit will break that stack?
Example 1 (raise)
SDDatabase1.StartTransaction;
Try
SDQuery1.ApplyUpdates;
SDDatabase1.Commit;
SDQuery1.CommitUpdates;
Except
SDDatabase1.Rollback;
SDQuery1.RollbackUpdates;
raise;
End;
..............//other codes I don't want to execute
Example 2 (exit)
SDDatabase1.StartTransaction;
Try
SDQuery1.ApplyUpdates;
SDDatabase1.Commit;
SDQuery1.CommitUpdates;
Except
SDDatabase1.Rollback;
SDQuery1.RollbackUpdates;
MessageDlg('Save Failed because: '+E.Message, mtError, [mbOK], 0);
exit;
end;
..............//other codes I don't want to execute
It's rare that alternate options (A vs. B) can be objectively evaluated as one is "always better" than the other. This is why it's important to properly understand the differences and implications of each.
When examined in isolation of a single method both your examples skip the code after the end of the except block. However, one leaves in an exception-state and the other doesn't. This has implications not in the method you write, but in the callers (direct and indirect) of your method.
procedur Caller1;
begin
//...[A]
Caller2;
//...[B]
end;
procedure Caller2;
begin
//...[C]
CallDatabaseMethod; {Will raise; or Exit; based on example chosen}
//...[D]
end;
The key distinction between your 2 examples is:
Example 1 is able to report the failure state up the call-stack.
Example 2 hides this information because the exception handler swallows the exception.
Example 1 would also skip [B] and [D] code. However, Example 2 would execute [B] and [D] code. When you understand this difference, you are then empowered to decide whether [B] and [D] should be executed.
However, I suspect that more often than not, the fact that CallDatabaseMethod failed to do everything correctly suggests that [B] and [D] should not be called. E.g. Suppose the database method updates customer account data and [B] and [D] perform action related to sending the latest statement. You probably don't want to send a statement when the update failed!
That said, if your method can be considered as "successfully completed" in spite of the exception, then by all means swallowing the exception is perfectly acceptable. E.g. Suppose you have a method to "Add a Row" and its post condition is simply that the row must exist in the database. Then if your database return a PK violation, obviously the row does exist. In this case it makes perfect sense to swallow the exception.
You can of course tweak the implementation of example 2 so as to not hide the error.
If your method is written as a function that returns success or failure state as a result, then callers can use this to resolve the aforementioned problems. E.g.
function Caller1: Boolean;
begin
Result := Caller2;
{Caller can decide to skip/ignore/do something different}
if Result then ...
end;
function Caller2: Boolean;
begin
Result := CallDatabaseMethod;
{Caller can decide to skip/ignore/do something different}
if Result then ...
end;
function CallDatabaseMethod: Boolean;
begin
Result := True;
//...
try
//...
except
on E: ExceptionType do
begin
//...
Result := False;
end;
end;
//...
end;
This is the same way the Windows API works. It does have its pros and cons:
Use return codes means caller has to remember to check for errors. (A common source of WinAPI questions on this site involve programmers failing to check for error return by API functions.)
So it's clearly an advantage that the exception model cannot be "ignored" by callers - they will surface eventually, even if it involves crashing the application.
But conversely again there's the disadvantage that the code to ignore exceptions that have been forced upon you is messier.
It's also important to caution against getting into a situation where large amounts of code run inside except blocks1.
The other disadvantage of structure exception handling is that it does have a significant performance overhead, so ideally you don't want to raise and handle them too often.
I recommend that the best approach is to determine which kinds of errors can be considered "normal" and ensure this is processed using explicit error results rather than exceptions. Certainly instances of 1 above are prime candidates.
Finally David has already flagged concerns with your message dialog in example 2. So this comment is on the assumption that this code always runs in a user context.
I understand the urge to show the message immediately. You have context that is lost by the time the exception propagates to the application level handler. One option to consider is using Abort which simply raises an EAbort exception.
try
//...
except
on E: ExceptionType do
begin
MessageDlg(...);
Abort;
end;
end;
The default application exception should ignore this exception and not display a message. If you have your own handler, you should similarly check the exception class before displaying any message.
As a side note I'd like to consider a particular sentence in the question.
I read that it's better to preserve application stack or queue or something like that.
Obviously if you're not sure about what you read, it's difficult explain it to you. You might already have a clearer picture based on earlier parts of my answer.
However, it might be referring to a different problem with another approach to exception handling. Raising a new exception. (You avoid this problem with raise; because it re-raises the original exception in original context.) This is the sort of thing is done to provide "more meaningful error message - similar to your example 2.
try
except
raise EOtherError.Create('My Message');
end;
The problem with the above is that when this exception eventually propagates to the application handler you've lost the original class; original exception address; and the original message. This approach often results in a clearer error to the user: e.g. "Unable to open file filename" but hides information that might have been useful in troubleshooting. E.g. Was it a disk error, was file not file, was it access permission error.
So: an important thing to think about whenever handling errors (no matter what approach you use) is: will there be enough information to resolve an error should it occur?
Both are safe in principle, but it's impossible to recommend one or other approaches. It depends on your design intentions. You have to decide which is appropriate given the intended usage of the code.
If you handle the exception in this code, and leave the function with exit, then the execution returns to the calling function, and it has no inkling of whether or not the function succeeded or failed. This may be problematic.
If you re-raise the exception, execution will move to the next suitable exception handler up the call stack, passing through any finally blocks along the way.
So, behaviour will differ, and it's down to you to decide which you want.
It is a common beginner's mistake to attempt to handle exceptions further down the call stack than is ideal. For example, suppose you want your code to be used in both a GUI app and a non-visual app. Your use of MessageDlg would not be appropriate in a non-visual app.
In a GUI app it is common for most actions to be in response to user input, for instance a button press. Exceptions typically should result in the entire operation being aborted. In which case you should not attempt to handle exceptions at all. Let them pass to the application level exception handler.
Finally, your code treats all exceptions in the same way. This is often ill-advised. For instance, an access violation should surely be treated differently from a database error.
Related
Now I make TCP server with asyncio.
I want added exception error handling in my code. (like below)
try:
data = await reader.read(SERVER_IO_BUFFER_SIZE)
except SomeError:
#error handle
So, I look asyncio official document.
but I can't find any of information about Errors that may occur.
(link: https://docs.python.org/3/library/asyncio-stream.html#asyncio.StreamReader.read)
How can I get infomation about Errors that may occur?
The exact errors that may occur will depend on the type of the stream behind the StreamReader. An implementation that talks to a socket will raise IOError, while an implementation that reads data from a database might raise some database-specific errors.
If you are dealing with the network, e.g. through asyncio.open_connection or asyncio.start_server, you can expect instances of IOError and its subclasses. In other words, use except IOError as e.
Also, if the coroutine is cancelled, you can get asyncio.CancelledError at any await. You probably don't want to handle that exception - just let it propagate, and be sure to use the appropriate finally clauses or with context managers to ensure cleanup. (This last part is a good idea regardless of CancelledError.)
We have 2 applications, the first one is VCL project, the other is a windows service.
In the VCL project we do:
try
except
on E: Exception do
// do something with E.Message
end
but in the windows service (which uses several threads) we use:
try
except
// do something with Exception(ExceptObject).Message
end
The information I got from my coworkers is that "We must use ExceptObject in threads and E: Exception in applications that use GUI". But I couldn't find anything regarding this.
I found an example here http://edn.embarcadero.com/article/10452 where it uses an instance variable to store the exception and it uses the ExceptObject, but gives no explanation why.
Is this ExceptObject even thread-safe (since it comes from the unit 'System')?
So what is the right way to handle exceptions in Delphi and why is there more than one way to do it?
There is no right way for exception handling. There is just one way. What might confuse you could be dealing with the exception object which is created, and which causes an exception to raise, but whose lifetime is the most important for you here.
In general, there's only two ways of dealing with those exception objects. Either you let them alive beyond the exception block scope and release them by yourself or let them free by the RTL when the exception block ends.
But to answer what I guess you've asked. Exception class isn't thread safe. And, your coworkers were wrong as no one is forced to use specific exception handling in threads. These rules are the same for all threads created by the process, no matter what. Just, those exception objects can be unstable within exception blocks:
1. Get the current exception object from the ExceptObject
The ExceptObject returns the current exception object. In practice, it may cause this; if you store such object reference into a variable inside an exception handler block and another exception will get raised within such block, that stored instance may become invalid. Which is quite unsafe.
But it doesn't mean you could not take a reference of such object and pass it to another thread by using some synchronization mechanisms (since it's not a thread safe class) and work with it there. You just need to take care that no other exception will be raised because that would invalidate the previously stored object so as you must take care of staying inside the exception handler from the caller's point of view and you must use a kind of thread synchronization mechanism.
So actually working with the exception object acquired from an on expression can be more stable than using ExceptObject. But the same rules applies here as well; you'd need to synchronize the object instance from the on expression with another thread (since it's not a thread safe class), but in such case, object acquired from the on expression won't get changed unlike the ExceptObject one can be within a certain exception block.
2. Retain exception object by using AcquireExceptionObject
The AcquireExceptionObject function allows you to keep the exception object alive even out of the exception block.
For an exception handling when speaking about thread synchronization, I'd suggest you using the AcquireExceptionObject function which makes the exception object free to consume, even after the exception block ends. For you that brings the only responsability, free such acquired object by calling the ReleaseExceptionObject procedure or raising the exception by this object again.
Victoria is absolutely correct.
Personally, I have a strong preference for this idiom:
try
...
except
// IO error
On E : EInOutError do
ShowMessage('IO error : '+E.Message);
// Division by zero
On E : EDivByZero do
ShowMessage('Div by zero error : '+E.Message);
// Catch other errors
else
ShowMessage('Unknown error');
end;
To elaborate:
Victoria said "There is no right way for exception handling. There is just one way." That's absolutely correct.
The advice you got about "use one syntax for threads, and the other for GUIs" is simply wrong. There is no "different syntax" for "threads" vs. "GUI". That's nonsense :(
I prefer using on : MyExceptionType in an exception block.
I also prefer to differentiate different exception types, whenever/wherever possible.
The example you cited, http://edn.embarcadero.com/article/10452, deals with how to avoid a possible access violation if you don't handle the exception within that particular thread. Saving the exception instance in a member variable helps mitigate this problem.
The following link might help clarify:
http://www.delphibasics.co.uk/Article.asp?Name=Exceptions
I am working on small monitoring application which will have some threads for communication with some devices via SNMP, TCP, ICMP, other threads have to perform some calculations.
All this result I have to output in GUI (some Forms or TabSheets).
I am thinking about next possibilities:
use Synchronize from every worker thread:
use shared buffer and windows messaging mechanism. Thread will put message in shared buffer (queue) and will notify GUI with windows message.
use separate thread which will listen for Synchronization primitives (Events, Semaphores, etc) and use again Synchronize, but only from GUI-dedicated thread only, or Critical Section on GUI to display message.
UPDATE: (Proposed by one workmate) use shared buffer and TTimer in main form which will check periodically (100-1000 ms) shared buffer and consuming, instead of windows messaging. (Does it have some benefit over messaging?)
Other?
Dear experts, please explain what is the best practice or what are the advantages and disadvantages of exposed alternatives.
UPDATE:
As idea:
//shared buffer + send message variant
LogEvent global function will be called from everywhere (from worker threads too):
procedure LogEvent(S: String);
var
liEvent: IEventMsg;
begin
liEvent := TEventMsg.Create; //Interfaced object
with liEvent do
begin
Severity := llDebug;
EventType := 'General';
Source := 'Application';
Description := S;
end;
MainForm.AddEvent(liEvent); //Invoke main form directly
end;
In Main Form, where Events ListView and shared section (fEventList: TTInterfaceList which is already thread-safe) we'll be:
procedure TMainForm.AddEvent(aEvt: IEventMsg);
begin
fEventList.Add(aEvt);
PostMessage(Self.Handle, WM_EVENT_ADDED, 0, 0);
end;
Message handler:
procedure WMEventAdded(var Message: TMessage); message WM_EVENT_ADDED;
...
procedure TMainForm.WMEventAdded(var Message: TMessage);
var
liEvt: IEventMsg;
ListItem: TListItem;
begin
fEventList.Lock;
try
while fEventList.Count > 0 do
begin
liEvt := IEventMsg(fEventList.First);
fEventList.Delete(0);
with lvEvents do //TListView
begin
ListItem := Items.Add;
ListItem.Caption := SeverityNames[liEvt.Severity];
ListItem.SubItems.Add(DateTimeToStr(now));
ListItem.SubItems.Add(liEvt.EventType);
ListItem.SubItems.Add(liEvt.Source);
ListItem.SubItems.Add(liEvt.Description);
end;
end;
finally
fEventList.UnLock;
end;
end;
Is there something bad? Main Form is allocated ONCE on application startup and Destroyed on application exit.
Use Synchronize from every worker thread
This would probably be the simplest approach to implement, but as others have indicated will lead to your IO threads being blocked. This may/may not be a problem in your particular application.
However it should be noted that there are other reasons to avoid blocking. Blocking can make performance profiling a little trickier because it effectively pushes up the time spent in routines that are "hurrying up and waiting".
Use shared buffer and windows messaging mechanism
This is a good approach with a few special considerations.
If your data is extremely small, PostMessage can pack it all into the parameters of the message making it ideal.
However, since you make mention of a shared buffer, it seems you might have a bit more data. This is where you have to be a little careful. Using a "shared buffer" in the intuitive sense can expose you to race conditions (but I'll cover this in more detail later).
The better approach is to create a message object and pass ownership of the object to the GUI.
Create a new object containing all the details required for the GUI to update.
Pass the reference to this object via the additional parameters in PostMessage.
When the GUI finishes processing the message it is responsible for destroying it.
This neatly avoids the race conditions.
WARNING: You need to be certain the GUI gets all your messages, otherwise you will have memory leaks. You must check the return value of PostMessage to confirm it was actually sent, and you may as well destroy the object if not sent.
This approach works quite well if the data can be sent in light-weight objects.
Use separate thread ...
Using any kind of separate intermediate thread still requires similar considerations for getting the relevant data to the new thread - which then still has to be passed to the GUI in some way. This would probably only make sense if your application needs to perform aggreagation and time-consuming calculations before updating the GUI. In the same way that you don't want to block your IO threads, you don't want to block your GUI thread.
Use shared buffer and TTimer in main form
I mentioned earlier that the "intuitive idea" of a shared buffer, meaning: "different threads reading and writing at the same time"; exposes you to the risk of race conditions. If in the middle of a write operation you start reading data, then you risk reading data in an inconsistent state. These problems can be a nightmare to debug.
In order to avoid these race conditions you need to fall back on other synchronisation tools such as locks to protect the shared data. Locks of course bring us back to the blocking issues, albeit in a slightly better form. This is because you can control the granularity of the protection desired.
This does have some benefits over messaging:
If your data structures are large and complex, your messages might be inefficient.
You won't need to define a rigorous messaging protocol to cover all update scenarios.
The messaging approach can lead to a duplication of data within the system because the GUI keeps its own copy of the data to avoid race conditions.
There is a way to improve the idea of shared data, only if applicable: Some situations give you the option of using immutable data structures. That is: data structures that do not change after they've been created. (NOTE: The message objects mentioned earlier should be immutable.) The benefit of this is that you can safely read the data (from any number of threads) without any synchronisation primitives - provided you can guarantee the data doesn't change.
The best approach is to use a GDI custom message and just call PostMessage() to notify the GUI.
type
TMyForm = class(TForm)
.
.
.
private
procedure OnMyMessage(var Msg: TMessage); message WM_MY_MESSAGE;
procedure OnAnoMessage(var Msg: TMessage); message WM_ANO_MESSAGE;
.
.
PostMessage(self.Handle,WM_MY_MESSAGE,0,0);
See this great article for full explanation.
This is a lighter/faster approach to rely on the OS internal features.
I'm currently trying to wrap my mind around the correct way to use exceptions in Haskell. How exceptions work is straight-forward enough; I'm trying to get a clear picture of the correct way to interpret them.
The basic position is that, in a well-designed application, exceptions shouldn't escape to the top-level. Any exception that does is clearly one which the designer did not anticipate - i.e., a program bug (e.g., divide by zero), rather than an unusual run-time occurrence (e.g., file not found).
To that end, I wrote a simple top-level exception handler that catches all exceptions and prints a message to stderr saying "this is a bug" (before rethrowing the exception to terminate the program).
However, suppose the user presses Ctrl+C. This causes an exception to be thrown. Clearly this is not any kind of program bug. However, failing to anticipate and react to a user abort such as this could be considered a bug. So perhaps the program should catch this and handle it appropriately, doing any necessary cleanup before exiting.
The thing is, though... The code that handles this is going to catch the exception, release any resources or whatever, and then rethrow the exception! So if the exception makes it to the top-level, that doesn't necessarily mean it was unhandled. It just means we wanted to exit quickly.
So, my question: Should exceptions be used for flow-control in this manner? Should every function that explicitly catches UserInterrupt use explicit flow-control constructs to exit manually rather than rethrow the exception? (But then how does the caller know to also exit?) Is it OK for UserInterrupt to reach the top-level? But in that case, is it OK for ThreadKilled too, by the same argument?
In short, should the interrupt handler make a special case for UserInterrupt (and possibly ThreadKilled)? What about a HeapOverflow or StackOverflow? Is that a bug? Or is that "circumstance beyond the program's control"?
Cleaning up in the presence of exceptions
However, failing to anticipate and react to a user abort such as this could be considered a bug. So perhaps the program should catch this and handle it appropriately, doing any necessary cleanup before exiting.
In some sense you are right — the programmer should anticipate exceptions. But not by catching them. Instead, you should use exception-safe functions, such as bracket.
For example:
import Control.Exception
data Resource
acquireResource :: IO Resource
releaseResource :: Resource -> IO ()
workWithResource = bracket acquireResource releaseResource $ \resource -> ...
This way the resources will be cleaned up regardless of whether the program will be aborted by Ctrl+C.
Should exceptions reach top level?
Now, I'd like to address another statement of yours:
The basic position is that, in a well-designed application, exceptions shouldn't escape to the top-level.
I would argue that, in a well-designed application, exceptions are a perfectly fine way to abort. If there are any problems with this, then you're doing something wrong (e.g. want to execute a cleanup action at the end of main — but that should be done in bracket!).
Here's what I often do in my programs:
Define a data type that represents any possible error — anything that might go wrong. Some of them often wrap other exceptions.
data ProgramError
= InputFileNotFound FilePath IOException
| ParseError FilePath String
| ...
Define how to print errors in a user-friendly way:
instance Show ProgramError where
show (InputFileNotFound path e) = printf "File '%s' could not be read: %s" path (show e)
...
Declare the type as an exception:
instance Exception ProgramError
Throw these exceptions in the program whenever I feel like it.
Should I catch exceptions?
Exceptions that you anticipate must be caught and wrapped (e.g. in InputFileNotFound) to give them more context. What about the exceptions that you don't anticipate?
I can see some value in printing "it's a bug" to the users, so that they report the problem back to you. If you do this, you should anticipate UserInterrupt — it's not a bug, as you say. How you should treat ThreadKilled depends on your application — literally, whether you anticipate it!
This, however, is orthogonal to the "good design" and depends more on what kind of users you're targeting, what you expect of them and what they expect of your program.
The response may range from just printing the exception to a dialog that says "we're very sorry, would you like to submit a report to the developers?".
Should exceptions be used for flow-control in this manner?
Yes. I highly recommend you read Breaking from a loop, which shows how Either and EitherT at their core at nothing more than abstractions for exiting from a code block early. Exceptions are just a special case of this behavior where you exit because of an error, but there is no reason why that should be the only case in which you exit prematurely.
A certain form in our application displays a graphical view of a model. The user can, amongst loads of other stuff, initiate a transformation of the model that can take quite some time. This transformation sometimes proceeds without any user interaction, at other times frequent user input is necessary. While it lasts the UI should be disabled (just showing a progress dialog) unless user input is needed.
Possible Approaches:
Ignore the issue, just put the transformation code in a procedure and call that. Bad because the app seems hung in cases where the transformation needs some time but requires no user input.
Sprinkle the code with callbacks: This is obtrusive - you’d have to put a lot of these calls in the transformation code - as well as unpredictable - you could never be sure that you’d found the right spots.
Sprinkle the code with Application.ProcessMessages: Same problems as with callbacks. Additionally you get all the issues with ProcessMessages.
Use a thread: This relieves us from the “obtrusive and unpredictable” part of 2. and 3. However it is a lot of work because of the “marshalling” that is needed for the user input - call Synchronize, put any needed parameters in tailor-made records etc. It’s also a nightmare to debug and prone to errors.
//EDIT: Our current solution is a thread. However it's a pain in the a** because of the user input. And there can be a lot of input code in a lot of routines. This gives me a feeling that a thread is not the right solution.
I'm going to embarass myself and post an outline of the unholy mix of GUI and work code that I've produced:
type
// Helper type to get the parameters into the Synchronize'd routine:
PGetSomeUserInputInfo = ^TGetSomeUserInputInfo;
TGetSomeUserInputInfo = record
FMyModelForm: TMyModelForm;
FModel: TMyModel;
// lots of in- and output parameters
FResult: Boolean;
end;
{ TMyThread }
function TMyThread.GetSomeUserInput(AMyModelForm: TMyModelForm;
AModel: TMyModel; (* the same parameters as in TGetSomeUserInputInfo *)): Boolean;
var
GSUII: TGetSomeUserInputInfo;
begin
GSUII.FMyModelForm := AMyModelForm;
GSUII.FModel := AModel;
// Set the input parameters in GSUII
FpCallbackParams := #GSUII; // FpCallbackParams is a Pointer field in TMyThread
Synchronize(DelegateGetSomeUserInput);
// Read the output parameters from GSUII
Result := GSUII.FResult;
end;
procedure TMyThread.DelegateGetSomeUserInput;
begin
with PGetSomeUserInputInfo(FpCallbackParams)^ do
FResult := FMyModelForm.DoGetSomeUserInput(FModel, (* the params go here *));
end;
{ TMyModelForm }
function TMyModelForm.DoGetSomeUserInput(Sender: TMyModel; (* and here *)): Boolean;
begin
// Show the dialog
end;
function TMyModelForm.GetSomeUserInput(Sender: TMyModel; (* the params again *)): Boolean;
begin
// The input can be necessary in different situations - some within a thread, some not.
if Assigned(FMyThread) then
Result := FMyThread.GetSomeUserInput(Self, Sender, (* the params *))
else
Result := DoGetSomeUserInput(Sender, (* the params *));
end;
Do you have any comments?
I think as long as your long-running transformations require user interaction, you're not going to be truly happy with any answer you get. So let's back up for a moment: Why do you need to interrupt the transformation with requests for more information? Are these really questions you couldn't have anticipated before starting the transformation? Surely the users aren't too happy about the interruptions, either, right? They can't just set the transformation going and then go get a cup of coffee; they need to sit and watch the progress bar in case there's an issue. Ugh.
Maybe the issues the transformation encounters are things that could be "saved up" until the end. Does the transformation need to know the answers immediately, or could it finish everything else, and then just do some "fix-ups" afterward?
Definitely go for a threaded option (even after your edit, saying you find it complex). The solution that duffymo suggests is, in my opinion, very poor UI design (even though it's not explicitly about the appearance, it is about how the user interfaces with your application). Programs that do this are annoying, because you have no idea how long the task will take, when it will complete, etc. The only way this approach could be made better would be by stamping the results with the generation date/time, but even then you require the user to remember when they started the process.
Take the time/effort and make the application useful, informative and less frustrating for your end user.
For an optimal solution you will have to analyse your code anyway, and find all the places to check whether the user wants to cancel the long-running operation. This is true both for a simple procedure and a threaded solution - you want the action to finish after a few tenths of a second to have your program appear responsive to the user.
Now what I would do first is to create an interface (or abstract base class) with methods like:
IModelTransformationGUIAdapter = interface
function isCanceled: boolean;
procedure setProgress(AStep: integer; AProgress, AProgressMax: integer);
procedure getUserInput1(...);
....
end;
and change the procedure to have a parameter of this interface or class:
procedure MyTransformation(AGuiAdapter: IModelTransformationGUIAdapter);
Now you are prepared to implement things in a background thread or directly in the main GUI thread, the transformation code itself will not need to be changed, once you have added code to update the progress and check for a cancel request. You only implement the interface in different ways.
I would definitely go without a worker thread, especially if you want to disable the GUI anyway. To make use of multiple processor cores you can always find parts of the transformation process that are relatively separated and process them in their own worker threads. This will give you much better throughput than a single worker thread, and it is easy to accomplish using AsyncCalls. Just start as many of them in parallel as you have processor cores.
Edit:
IMO this answer by Rob Kennedy is the most insightful yet, as it does not focus on the details of the implementation, but on the best experience for the user. This is surely the thing your program should be optimised for.
If there really is no way to either get all information before the transformation is started, or to run it and patch some things up later, then you still have the opportunity to make the computer do more work so that the user has a better experience. I see from your various comments that the transformation process has a lot of points where the execution branches depending on user input. One example that comes to mind is a point where the user has to choose between two alternatives (like horizontal or vertical direction) - you could simply use AsyncCalls to initiate both transformations, and there are chances that the moment the user has chosen his alternative both results are already calculated, so you can simply present the next input dialog. This would better utilise multi-core machines. Maybe an idea to follow up on.
TThread is perfect and easy to use.
Develope and debug your slow function.
if it is ready, put the call into the tthread execute method.
Use the onThreadTerminate Event to find out the end of you function.
for user feedback use syncronize!
I think your folly is thinking of the transformation as a single task. If user input is required as part of the calculation and the input asked for depends on the caclulation up to that point, then I would refactor the single task into a number of tasks.
You can then run a task, ask for user input, run the next task, ask for more input, run the next task, etc.
If you model the process as a workflow, it should become clear what tasks, decisions and user input is required.
I would run each task in a background thread to keep the user interface interactive, but without all the marshaling issues.
Process asynchronously by sending a message to a queue and have the listener do the processing. The controller sends an ACK message to the user that says "We've received your request for processing. Please check back later for results." Give the user a mailbox or link to check back and see how things are progressing.
While I don't completely understand what your trying to do, what I can offer is my view on a possible solution. My understanding is that you have a series of n things to do, and along the way decisions on one could cause one or more different things to be added to the "transformation". If this is the case, then I would attempt to separate (as much as possible) the GUI and decisions from the actual work that needs to be done. When the user kicks off the "transformation" I would (not in a thread yet) loop through each of the necessary decisions but not performing any work...just asking the questions required to do the work and then pushing the step along with the parameters into a list.
When the last question is done, spawn your thread passing it the list of steps to run along with the parameters. The advantage of this method is you can show a progress bar of 1 of n items to give the user an idea of how long it might take when they come back after getting their coffee.
I'd certainly go with threads. Working out how a thread will interact with the user is often difficult, but the solution that has worked well for me is to not have the thread interact with the user, but have the user side GUI interact with the thread. This solves the problem of updating the GUI using synchronize, and gives the user more responsive activity.
So, to do this, I use various variables in the thread, accessed by Get/Set routines that use critical sections, to contain status information. For starters, I'd have a "Cancelled" property for the GUI to set to ask the thread to stop please. Then a "Status"property that indicates if the thread is waiting, busy or complete. You might have a "human readable" status to indicate what is happening, or a percentage complete.
To read all this information, just use a timer on the form and update. I tend to have a "statusChanged" property too, which is set if one of the other items needs refreshing, which stops too much reading going on.
This has worked well for me in various apps, including one which displays the status of up to 8 threads in a list box with progress bars.
If you decide going with Threads, which I also find somewhat complex the way they are implemented in Delphi, I would recommend the OmniThreadLibrary by Primož Gabrijelčič or Gabr as he is known here at Stack Overflow.
It is the simplest to use threading library I know of. Gabr writes great stuff.
If you can split your transformation code into little chunks, then you can run that code when the processor is idle. Just create an event handler, hook it up to the Application.OnIdle event. As long as you make sure that each chunk of code is fairly short (the amount of time you want the application to be unresponsive...say 1/2 a second. The important thing is to set the done flag to false at the end of your handler :
procedure TMyForm .IdleEventHandler(Sender: TObject;
var Done: Boolean);
begin
{Do a small bit of work here}
Done := false;
end;
So for example if you have a loop, instead of using a for loop, use a while loop, make sure the scope of the loop variable is at the form level. Set it to zero before setting the onIdle event, then for example perform 10 loops per onidle hit until you hit the end of the loop.
Count := 0;
Application.OnIdle := IdleEventHandler;
...
...
procedure TMyForm .IdleEventHandler(Sender: TObject;
var Done: Boolean);
var
LocalCount : Integer;
begin
LocalCount := 0;
while (Count < MaxCount) and (Count < 10) do
begin
{Do a small bit of work here}
Inc(Count);
Inc(LocalCount);
end;
Done := false;
end;