Assumindo que o Stretch Raspbian usa systemd como o Debian Stretch regular faz por padrão, /etc/rc.local é iniciado por /lib/systemd/system/rc-local.service :
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
# This unit gets pulled automatically into multi-user.target by
# systemd-rc-local-generator if /etc/rc.local is executable.
[Unit]
Description=/etc/rc.local Compatibility
ConditionFileIsExecutable=/etc/rc.local
After=network.target
[Service]
Type=forking
ExecStart=/etc/rc.local start
TimeoutSec=0
RemainAfterExit=yes
GuessMainPID=no
Como ele especifica Type=forking , TimeoutSec=0 e RemainAfterExit=yes , eu entendo que o systemd basicamente inicia e não importa se sai ou não. Isso explica por que o sistema pode concluir a inicialização com êxito, mesmo se /etc/rc.local ainda estiver em execução.
Seu script rc.local primeiro executa startsignal.py em segundo plano (= com & ): isso significa que apenas uma falha ao iniciar o script causaria um erro no script rc.local nesse ponto. Se startsignal.py for iniciado com êxito, mas retornar um erro, rc.local teria que usar wait <process or job ID> para ler o erro recebido do processo startsignal.py . Mas o seu processo aparentemente não se importa em verificar isso.
Então, seu rc.local começa com fan.py . Como ele é iniciado sem & , o shell iniciará outro processo para executar fan.py e aguardar a saída ... mas como fan.py tem um loop infinito, ele não será encerrado até que o sistema esteja sendo encerrado , fan.py tem um erro ou o processo executando fan.py é eliminado. O touch /home/pi/thisisrun será executado somente após a saída de fan.py .
Acho que faria mais sentido começar startsignal.py sem o & e fan.py com , em vez de vice-versa.