Simplifying synchronization logic

... by using only one lock. The performance gain from using multiple
is likely negligible and not worth the additional complexity in the
code.
Add retry logic for adding executors

If a request to add new executors is not fully granted, it is
retried until all pending executors have registered. Further,
there is a max limit in the number of retry attempts. This
retry timer is canceled if there are no more pending executors
or the original timer is canceled.

Similar logic for retrying removal of executors is forthcoming.
Add retry logic for removing executors

For the most part, this is very similar to the retry logic for
adding executors. There are two main differences: (1) each executor
is removed individually, so we have to keep track of a set of IDs
to remove rather than just a raw number, and (2) we avoid keeping
a retry timer for each executor by aggregating all retries into one.
However, because executors are removed individually, we need to
either start the retry timer on the first remove, or refresh the
retry timer every time. Here we take the latter approach to avoid
retrying new removals too eagerly (before the timeout expires).
Simplify initialization code
Simplify code to access the scheduler backend

Instead of doing a match every time the backend is used, store it
in a lazy val once and for all.
Wording changes in comments and log messages
Start the retry timer for adding executors at the right time

We should retry an add only if an attempt was actually made, not
when after it has failed.