I am writing a small Python script that makes heavy use of threads / multiple processes and I came across an interesting race condition that I cannot find an explanation for. I have a background process that plays a song that the user can skip or that can end on it's own, in both these cases I need to fire a callback to say it has ended.
Below is a psuedo code sample demonstrating the problem :
import threading, subprocess
def play_song(song_name, callback) :
stop_any_previous_songs()
play_song_in_subprocess_then_call(callback)
def main(self) :
while True :
music_stopped = threading.Event()
def play_callback() :
# say that the song stopped
music_stopped.set()
#Assume that the thread is actually a daemon
threading.Thread(target=play_song, \
args=(get_next_song(), play_callback)\
.start()
while not music_stopped.is_set() and user_not_interrupted() :
# Spin
pass
if music_stopped.is_set() :
# End of song reached, play the next song
continue
else :
# User input from some source
process_user_input()
The problem is that when starting a new song after a previous song had played the play_callback
would be called as a result of the stop_any_previous_songs()
call. When called the play_callback
would call set
on the music_stopped
Event for the next song.
So my question is when the play_callback
function is called is it merely going to the enclosing scope and looking for an object with the name `music_stopped'? Or is the underlying cause something else?