If a worker was not given a task, it could happen to ignore the exit signal (for example if it's set before the worker started, causing it to clear the notifier boolean and test it again before checking m_running)
steal() can incorrectly return nullptr even if the list is not empty in case of concurrent access, but push and pop are not threadsafe so we use a spinlock to prevent concurrent uses