On all non-trivial OS, process termination begins when a thread of the process requests termination, or another process with the appropriate permissions and privileges requests termination.
One of the first steps in process termination is to remove execution from all the threads, no matter what state they happen to be in. This is essential to prevent the threads allocating any further resouces to the process and to prevent them from actively using the rsources that are already allocated. Threads that are not currently running have their state changed to 'never run again'. Threads that are running on cores are stopped by a hardware-interrupt of the cores running them. Once all the threads are stopped, the kernel can start releasing resources like fd's, memory-segments etc.
A out-of-process temination cannot be allowed to run any kind of 'onTermination' code that belongs to the process. In such code, even a simple infinite loop would prevent process termination and tie up a core indefinitely. Other possibilities for malicious action are obviously possible.
This can result in problems with resources that are shared with other processes or systems. One way of getting round this is to design your app so that any 'left over' connections, partially-written files, shared memory stuff, whatever, are cleaned up on process startup, instead of process shutdown.
Systems should be designed so that they do not absolutey depend upon 'clean' shutdowns and can recover successfully from events like critical segfaults, out-of-process terminations, power failures, coffee-spillage and frothy beer-cans.
Your app-lifetime threads will be terminated upon an out-of-process termination. You can detach them so that they will be terminated upon an in-process shutdown, (and you should design to make that termination as safe as you can without relying on any 'onTermination' signals/handlers/whatever, since exploding beer cans do happen).