use channels to wait for end