aren't we better off using a timer ?
Well, no, because a timer is not immediate, while the message loop is...
Technically a better solution is to create an event and poll the status in the loop running in a separate thread. The button can set the event as a request to end the loop immediately.
But Handoko's solution has the same effect.
One more solution is to test for a global flag from a thread and set that flag through one of the interlocked* functions or an atomic read/write (nativeint,native booleans are atomic on intel, amd and arm.)