I would suggest a two-part approach, the first of which should probably be a habitual approach for Smart Contracts.
Try to structure the Smart Contracts with single-purpose, small, atomic transactions that do one update at a time. Loopy logic (for ...) and recursion, where needed, should usually pushed to clients. For example, refund all participants would be implemented with a function to refund one participant; the loopy part can be pushed to a client.
Doing so, IMHO, alleviates many cases where a race condition would be a concern.
If a race condition remains a concern, then a Mutex-inspired arrangement could be put in place. There are quite a few hits for that in google. There might be a flag to indicate an upgrade is in progress and it would prevent entry into functions until this process is complete and the "lock" is released.
This all depends, of course, on a deep understanding of the use-case. If possible, I would be trying to avoid the need for second option by careful observation of the first option.
Just some thoughts. Hope it helps.