While there is a limit on the number of address spaces that can be active at a time, there is no limit on the number of Exec's in a program. You must keep track of children in some way. That needs to be done in the stub function that is called by the newly forked thread. Join returns an error if the address space id requested is not a child of the current process. You can't do it in general in exit, because it is always possible that the parent will later join.
Exit is tricky.
If Y is still executing, then indicate that space X is done. If Y is done, don't do this. It is necessary to tell all of X's children that X is dead. In Exit, it is necessary to halt the machine if there are no other address spaces alive. So, you need to keep count of how many active address spaces there are.