add a version of ExecutorUtils.parallelWork that exposes exceptions

It is easy to accidently and silently drop exceptions when using
ExecutorUtils.parallelWork(). It returns a list of Futures wrapping
the result (value or exception) of each task.  When the underlying
task is used only for its side-effects, the caller often (incorrectly)
ignores the returned Futures and thus any wrapped exceptions.

Callers should always examine the returned Futures to rethrow/catch
any wrapped exceptions.  This commit adds a second parallel work
method, ExecutorUtils.parallelForEach(), that takes care of rethrowing
wrapped exceptions.
do not drop exceptions in worker threads when building graphs

Callers of ExecutorUtils.parallelWork() must examine each returned
Future for an exception or they exceptions will be silently
dropped. Many callers did not.  This commit fixes all such callers to
instead use ExecutorUtils.parallelForEach(), which rethrows an
exception on the calling thread automatically.
detect erroneous duplicate nodes in input when building graph