'Wait until the thread terminates.
This blocks the calling thread until the thread whose join() method is
called terminates -- either normally or through an unhandled exception
or until the optional timeout occurs.
When the timeout argument is present and not None, it should be a
floating point number specifying a timeout for the operation in seconds
(or fractions thereof). As join() always returns None, you must call
isAlive() after join() to decide whether a timeout happened -- if the
thread is still alive, the join() call timed out.
When the timeout argument is not present or None, the operation will
block until the thread terminates.
A thread can be join()ed many times.
join() raises a RuntimeError if an attempt is made to join the current
thread as that would cause a deadlock. It is also an error to join() a
thread before it has been started and attempts to do so raises the same
exception.'

def join(self, timeout=None):

    if (not self.__initialized):
        raise RuntimeError('Thread.__init__()    not    called')
    if (not self.__started.is_set()):
        raise RuntimeError('cannot    join    thread    before    it    is    started')
    if (self is current_thread()):
        raise RuntimeError('cannot    join    current    thread')
    if __debug__:
        if (not self.__stopped):
            self._note('%s.join():    waiting    until    thread    stops', self)
    self.__block.acquire()
    try:
        if (timeout is None):
            while (not self.__stopped):
                self.__block.wait()
            if __debug__:
                self._note('%s.join():    thread    stopped', self)
        else:
            deadline = (_time() + timeout)
            while (not self.__stopped):
                delay = (deadline - _time())
                if (delay <= 0):
                    if __debug__:
                        self._note('%s.join():    timed    out', self)
                    break
                self.__block.wait(delay)
            else:
                if __debug__:
                    self._note('%s.join():    thread    stopped', self)
    finally:
        self.__block.release()
