The way this is done is that every developer must merge changes before they commit a file. I have never used Mercurial so I don't know the commands or the exact process, but in any serious config management tool you will be warned if you try to check in "over the top" of someone else. When you get this warning you should merge the other changes in to your checked out copy, check the build and tests etc and then check in.
This is a very common issue, and merging in this way can be a real PITA, but that's life. The alternative approach of locking all checked out files prevents this issue, but has the serious drawback of blocking everyone else while you work on a file, which in turn leads to people editing uncontrolled versions and then all hell breaks loose...
As far as I'm aware most of the big open source projects using DVCS use "pull requests" instead of pushes, i.e. a user requests that the project pulls from their branch, and the prject can choose to undertake these pull requests in any order, if at all. This eliminates the needs for the "push race", as you've named it.
In other companies I can't vouch for process, but where I work this isn't an issue.
See, when you're working on a case you're working on a branch of the entire repo, so your push requests go to a remote version of the main trunk. When you want to integrate your (finished) change into the trunk you load up the trunk, pull, merge, push.
Occasionally (very occasionally) two people will try and do this at the same time (usual due to some miscommunication). In this case whoever "loses" will just have to re-pull, merge, push. Since there's no 5pm rush to commit to a central repository the problem you've outlined isn't really there.
That's the beauty of DVCS: branching is painless, so everyone can work on their own branch.
EDIT
Oh, I just noticed your "In mercurial you don't branch..." comment: Yes, you do. You don't have to, but since it's so easy to and the benefits of doing so outweigh not doing so do greatly, you do tend to just branch repos a lot.
Best Answer
I handle it this way:
In my local repository, I commit every change, even if I'm experimenting. If I'm okay with the experiment, and it's tested, I push it to the remote repository. If not, it stays in my local repository (or go back to an older revision).
The idea is that the remote repository only contains working, tested versions of my projects.