There are several things here.
Lets us start with the main question how to get something back from the gui thread. It is correct to submit a suspending function to a gui execution context. And then retrieve the result.
Here you use
getResult which means that your worker thread will block and wait for the result.
If
myLongRunning was also suspending then you could have
awaited the result instead:
Code: Select all
class predicates
myLongRunning : (myForm MyForm, integer Cnt) suspending.
clauses
myLongRunning(MyForm, Cnt) :-
if Cnt mod 10000000 = 0 then
RctFuture = pfc\asynchronous\future::submit({ = MyForm:getOuterRect() }, MyForm:executionContext),
Rct = RctFuture:futureExtension::await(),
stdIO::write(Rct, "\n")
end if,
Cnt1 = if Cnt = upperBound(integer) then 0 else Cnt + 1 end if,
myLongRunning(MyForm, Cnt1).
In vip 11
await is stowed away/hidden in
futureExtension so you will either have to qualify explicitly (like I have done above) or open
futureExtension (which is preferably, I think).
In future versions this will be structured a bit differently, but the idea is the same.
When you use
await the execution will suspend until the result is ready instead of blocking. If the gui request is a simple as here there is not much gained in suspending instead of blocking (actually it may be worse) but we are talking principles here.
Next issue is about gui execution contexts. An execution context is very simple, it only have a
complete predicate:
Code: Select all
predicates
complete : (runnable Action).
This predicate must execute the Action in the execution context. For a threadpool execution context the Action is put in a queue and eventually a thread in the pool will dequeue it and run it.
A gui execution context will simply use
postAction so the Action is executed by the gui thread.
However
postAction can only be used on a window that actually exist, and anything that is posted to a window is lost if the window is destroyed before the post event comes through to the window.
In some contexts it may be good if the Action simply disappear when the window is destroyed. But I think it can easily be very complex. The task window is long living and I think it is safest to use that. I didn't want to pollute everything with asynchronous stuff, so windows don't have a executionContext property. But it is very simple to create one from any window using this code (which I think we will add somewhere in the async department):
Code: Select all
class predicates
executionContext : (window Src [this]) -> pfc\asynchronous\executionContext ExecutionContext.
clauses
executionContext(Src) =
implement : pfc\asynchronous\executionContext
complete(Action) :-
Src:postAction(Action).
end implement.
Using this code will control very precisely which window that it the context (and I think you should use the task window).
The final thing is that in this particular example you use a form, and the code will break if the form is closed. I am aware that the example is illustrative, but it also illustrates that you need to consider lifetime a lot. Both lifetime of windows but also of your background tasks.