libmn: move to work-stealing distributed scheduler

Instead of eagerly distributing work across workers when
a thread is scheduled, each individual worker now steals
work from other workers when it's queue goes empty. This
reduces the amount of cross-worker communication /
synchronization / contention while still maintaining a good
balance of load.
libmn: fail if other queue is locked when trying to steal work

Since we're trying N*2 times to find another worker to steal from,
we can just try_lock() instead of waiting for the lock. This avoids
contention for another worker's queue.
libmn: don't use indirect function call for 'run_after_switch'

The indirect call to `run_after_switch()` was waiting 10s of
milliseconds in the primesieve example. This replaces it with
a simple switch dispatch through an enum (allowing the compiler
to inline the action).