exactly -
The problem is that there are three logical states that need three different actions
1. if nothing done yet: start async computation
2. if async computation in progress: wait for it
3. return result as computaton done
Atomicity in the sense that no other Python code can run during a setdefault, is not relevant.
Waiting for something that is running in another loop actually isn't easy either; I'm not sure that Python really allows it - won't you get an error:
The future belongs to a different loop than the one specified as the loop argument