Took a short nap and came back to a flood of bug reports

Well... I guess that's what happens when a project starts gaining momentum - more people use it, more edge cases get exercised, and the bugs start coming out of hiding.
I'd still prefer if bug reports were opened as GitHub issues whenever possible. It's much easier for me to keep track of them, prioritize them, and make sure nothing gets lost. But it's all good - keep them coming

Btw. I tried the installer on a PC with a 4k display too, with the Windows font scaling set to 150%. It makes the text a bit too big, but it still works.
Will look into it later.
I tried the future
procedure DownloadBigFile(
OnStartEvent, OnProcessEvent, OnDoneEvent: TNotifyEvent);
begin
if Assigned(OnStartEvent) then OnStartEvent(nil);
Sleep(1000);
if Assigned(OnProcessEvent) then OnProcessEvent(nil);
Sleep(1000);
if Assigned(OnDoneEvent) then OnDoneEvent(nil);
end;
procedure tform1.OnStart(sender: tobject);
begin
memo1.Append('Start.');
end;
procedure tform1.OnDone(sender: tobject);
begin
memo1.Append('Done!');
end;
procedure tform1.OnProcess(sender: tobject);
begin
memo1.Append('Downloading...');
end;
procedure tform1.btndownloadclick(sender: tobject);
begin
async DownloadBigFile(@OnStart, @OnProcess, @OnDone);
memo1.Append('Return to main loop');
end;
I expected to get:
Return to main loop
Start.
Downloading...
Done!
Actually:
Return to main loop
Done!
Downloading...
Start.
Did something go wrong, or is there something I don't understand?
Real bug, thanks for the report. Fixed. Two separate things going on in your code though.
1) Compiler bug.
async F(a, b, c) handed the arguments to the worker in reverse, so
DownloadBigFile(@OnStart, @OnProcess, @OnDone) actually ran as
DownloadBigFile(@OnDone, @OnProcess, @OnStart). Any call form with two or more arguments hit it. Arguments of different types got caught by the type checker, but three
TNotifyEvent parameters swap silently, which is why it looked like the callbacks fired backwards.
2) Not a bug, just threading - and it would still bite you after the fix. The routine runs on a worker thread, and so does everything it calls, including your three callbacks.
memo1.Append is a cross-thread LCL call, which is never safe no matter how it looks.
Good timing though: I just added a
sync keyword for exactly this. It's the mirror of
async - it hands a statement (or a begin..end block) to the main thread and waits for it to run there. So the fix is one word per callback:
procedure tform1.OnStart(sender: tobject);
begin
sync memo1.Append('Start.');
end;
Same for
OnProcess and
OnDone. A single statement needs no
begin..end. Your
btndownloadclick doesn't await the future, so it returns straight to the message loop, which pumps the queue and runs the sync bodies - nothing else to do on your side. Output comes out in order:
Return to main loop
Start.
Downloading...
Done!
sync lowers to
TThread.Synchronize under the hood, so it needs the
Classes unit (an LCL form already has it). One rule to remember: don't await a future on the main thread while its worker is sitting in sync - each would wait for the other. Fire-and-forget like yours is fine.
It seems like packed records in an union doesn't work correctly?
Fixed both: syntax highlighting and the "Identifier not found" error (the workaround was to use
Self, BTW).