def start_cmd_loop(self):
        """Starts the command line loop. This method is called automatically by
           the serve_forever method. The function call is idempotent so you can
           call the method before or after that without worrying or extra
           side-effect. An EOF terminates the loop but does not close the
           server. A `KeyboardInterrupt` terminates the server as well.
        """
        # thread-safe check if the loop is already running
        with self._cmd_lock:
            cmd_start = self._cmd_start
            self._cmd_start = True

        if cmd_start:
            return

        cmd_state = {
            'suggestions': [],
            'clean_up_lock': threading.Lock(),
            'clean': False,
            'line': '',
        }

        # setup internal commands (no replace)
        @self.cmd(argc=0, no_replace=True)
        def help(args):  # pylint: disable=unused-variable
            msg('available commands:')
            for key in self._cmd_methods.keys():
                msg('    {0}', key.replace('_', ' '))

        @self.cmd(argc=0, no_replace=True)
        def restart(args):  # pylint: disable=unused-variable
            global _do_restart
            _do_restart = True
            self.done = True

        @self.cmd(argc=0, no_replace=True)
        def quit(args):  # pylint: disable=unused-variable
            self.done = True

        # loading the history
        hfile = self.history_file
        try:
            readline.read_history_file(hfile)
        except IOError:
            pass

        # set up command completion
        def complete(text, state):
            if state == 0:
                origline = readline.get_line_buffer()
                line = origline.lstrip()
                stripped = len(origline) - len(line)
                begidx = readline.get_begidx() - stripped
                endidx = readline.get_endidx() - stripped
                prefix = line[:begidx].replace(' ', '_')

                def match_cmd(cmd):
                    return cmd.startswith(prefix) and \
                           cmd[begidx:].startswith(text)

                matches = filter(match_cmd, self._cmd_methods.keys())

                def _endidx(m):
                    eix = m.find('_', endidx)
                    return eix + 1 if eix >= 0 else len(m)

                candidates = [
                    m[begidx:_endidx(m)].replace('_', ' ') for m in matches
                ]
                rest_cmd = line[:begidx].split()
                args = []
                while rest_cmd:
                    cur_cmd = '_'.join(rest_cmd)
                    if cur_cmd in self._cmd_complete and \
                            self._cmd_complete[cur_cmd] is not None:
                        cc = self._cmd_complete[cur_cmd](args, text)
                        if cc is not None:
                            candidates.extend(cc)
                    args.insert(0, rest_cmd.pop())
                cmd_state['suggestions'] = sorted(set(candidates))
                cmd_state['line'] = line
            suggestions = cmd_state['suggestions']
            if len(suggestions) == 1 and text == suggestions[0]:
                probe_cmd = cmd_state['line'].replace(' ', '_')
                if probe_cmd in self._cmd_argc and \
                        self._cmd_argc[probe_cmd] != 0:
                    cmd_state['line'] = ""
                    return text + ' '
                return None
            if state < len(suggestions):
                return suggestions[state]
            return None

        old_completer = readline.get_completer()
        readline.set_completer(complete)
        # be mac compatible
        if readline.__doc__ is not None and 'libedit' in readline.__doc__:
            readline.parse_and_bind("bind ^I rl_complete")
        else:
            readline.parse_and_bind("tab: complete")

        # remember to clean up before exit -- the call must be idempotent!
        def clean_up():
            with cmd_state['clean_up_lock']:
                clean = cmd_state['clean']
                cmd_state['clean'] = True

            if clean:
                return

            readline.write_history_file(hfile)
            readline.set_completer(old_completer)
        atexit.register(clean_up)
        self._clean_up_call = clean_up

        def cmd_loop():
            close = False
            kill = True
            try:
                while not self.done and not close and not self.no_command_loop:
                    line = ""
                    try:
                        try:
                            line = input(self.prompt)
                        except IOError as e:
                            if e.errno == errno.EBADF:
                                close = True
                                kill = False
                            elif (e.errno == errno.EWOULDBLOCK or
                                  e.errno == errno.EAGAIN or
                                  e.errno == errno.EINTR):
                                continue
                            else:
                                raise e
                        self.handle_cmd(line)
                    except EOFError:
                        close = True
                        kill = False
                    except KeyboardInterrupt:
                        close = True
                    except Exception:
                        msg("{0}", traceback.format_exc())
                        msg("^ exception executing command {0} ^", line)
            finally:
                if kill:
                    self.done = True
                else:
                    msg("no command loop - use CTRL-C to terminate")
                    self.no_command_loop = True
                clean_up()

        if not self.no_command_loop:
            t = self._thread_factory(target=cmd_loop)
            t.daemon = True
            t.start()