Мне не нравится 3й вариант, так как он не решает задачу целиком. Порт, который слушать - это больше конфигурационный параметр, значит надо задумываться уже, ничего ли страшного не случится при смене порта и рестарте (обычно не должно, но раз на раз не приходится). А что, если демону и вовсе не нужно слушать какой-либо порт? Не универсальненько.
Увы, кросс-платформенного решения "из коробки" нет, да. Я когда стряпал
свою поделку, то меня интересовали только *nix-like платформы, мне хватило старого хорошего PID-файла с syscall.Flock. То, что видел в других решениях, более кросс-платформенных, - люди заморачивались на platform specific код, для Windows они использовали регистрацию процесса в виде сервиса. Обернуть это дело в отдельный пакет с единым интерфейсом и platform specific компиляцией совсем не сложно в случае с Go. Для работы с сервисами Windows есть замечательный, пусть и не входящий в стандартную либу, но все же официальный пакет
golang.org/x/sys/windows/svc, и костылить на чистых syscall'ах даже не нужно.
Также загляните в
этот тред, там как раз про решение для Windows в виде semaphore/mutex аналогично тому, что указал
Владимир Мартьянов в комментарии к Вашему вопросу и тому, что Вы указали 2м пунктом.