To support dynamic changes, move endpoint activation from
the main thread into a dedicated thread that will also
process asynchronous events (to be added separately). The
main thread will still initialize the device and port
list, but activation code will be handled by the new thread,
so that endpoints can be deferred until their associated
ports are activated.
This also means that multicast joins are deferred, and
additional state checking is needed to prevent the main
thread (which processes client requests) from accessing
endpoints which are not yet fully active.