What happens between return from main() and exit handlers

I have an exit handler installed (with atexit()) that has the purpose to
shut down a second thread that I spawned.
The second thread calls PtInit, creates a window, calls PtMainLoop.

If I call exit() from main() everything works fine.

If just exit main(), the second thread crashes (but only if I use a
continuous update of a PtRaw widget inside the window).

What is the conceptual difference of exiting main with return() and calling
exit() directly?