Discovery: Improve the lifecycle management of the join control thread in zen discovery.

This PR also includes:
* Better exception handling in UnicastZenPing#ping
* In the join thread that runs the innerJoinCluster loop, remember the last known exception and throw that when assertions are enabled. We loop until inner join has successfully completed and if continues exceptions are thrown we should fail the test, because the exception shouldn't occur in production (at least not too often).
since finalizePingCycle will call listener.onPing, I think we shouldn't re-throw the exception but rather let finalizePingCycle do it's thing here:

```
logger.warn("[{}] failed to send pings", t, id);
finalizePingCycle(id, listener);
```

at a future change we will do the same with UnicastZenPing