Each atom is a thread. Hydrogen threads call hydrogen(release_hydrogen) and oxygen threads call oxygen(release_oxygen), once each, on a shared H2O object:
class H2O:
def __init__(self): ...
def hydrogen(self, release_hydrogen) -> None: ... # calls release_hydrogen() once
def oxygen(self, release_oxygen) -> None: ... # calls release_oxygen() once
Atoms arrive in any order and must leave in molecules: if you cut the sequence of releases into consecutive groups of three, every group has exactly two H and one O (in any order inside the group). A thread that can't form a molecule yet has to wait. The tests always supply exactly twice as many hydrogens as oxygens, and every thread must eventually return.
Example: atoms arriving as O O H H H H might leave as H O H H H O (groups HOH, HHO), but never as O O H ....
Use threading primitives. No sleeping or busy-waiting.
Hint: An atom's place in the current molecule may only be given back after it has been released. What goes wrong if it is given back earlier?
Show hint
Limit how many atoms of each kind can be "in the current molecule" at once (two H, one O), and make the three members of a molecule wait for each other before anyone releases.