def keepalive_socket_options(timeout: int, idle: int, cnt: int = 3) -> Iterator[Tuple[int, int, int]]:
    """Get all keepalive related options to be set in a socket.

    :param timeout: value for ``TCP_USER_TIMEOUT``.
    :param idle: value for ``TCP_KEEPIDLE``.
    :param cnt: value for ``TCP_KEEPCNT``.

    :yields: all keepalive related socket options to be set. The first item in the tuple is the protocol, the second
        item is the option, and the third item is the value to be used. The return values depend on the platform:

            * ``Windows``:
                * ``SO_KEEPALIVE``.
            * ``Linux``:
                * ``SO_KEEPALIVE``;
                * ``TCP_USER_TIMEOUT``;
                * ``TCP_KEEPIDLE``;
                * ``TCP_KEEPINTVL``;
                * ``TCP_KEEPCNT``.
            * ``MacOS``:
                * ``SO_KEEPALIVE``;
                * ``TCP_KEEPIDLE``;
                * ``TCP_KEEPINTVL``;
                * ``TCP_KEEPCNT``.
    """
    yield (socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)

    if not (sys.platform.startswith('linux') or sys.platform.startswith('darwin')):
        return

    TCP_USER_TIMEOUT = getattr(socket, 'TCP_USER_TIMEOUT', None)
    if TCP_USER_TIMEOUT is not None:
        yield (socket.SOL_TCP, TCP_USER_TIMEOUT, apply_keepalive_limit('TCP_USER_TIMEOUT', int(timeout * 1000)))
    # The socket constants from MacOS netinet/tcp.h are not exported by python's
    # socket module, therefore we are using 0x10, 0x101, 0x102 constants.
    TCP_KEEPIDLE = getattr(socket, 'TCP_KEEPIDLE', 0x10 if sys.platform.startswith('darwin') else None)
    if TCP_KEEPIDLE is not None:
        idle = apply_keepalive_limit('TCP_KEEPIDLE', idle)
        yield (socket.IPPROTO_TCP, TCP_KEEPIDLE, idle)
    TCP_KEEPINTVL = getattr(socket, 'TCP_KEEPINTVL', 0x101 if sys.platform.startswith('darwin') else None)
    if TCP_KEEPINTVL is not None:
        intvl = keepalive_intvl(timeout, idle, cnt)
        yield (socket.IPPROTO_TCP, TCP_KEEPINTVL, intvl)
    TCP_KEEPCNT = getattr(socket, 'TCP_KEEPCNT', 0x102 if sys.platform.startswith('darwin') else None)
    if TCP_KEEPCNT is not None:
        cnt = apply_keepalive_limit('TCP_KEEPCNT', cnt)
        yield (socket.IPPROTO_TCP, TCP_KEEPCNT, cnt)
Get all keepalive related options to be set in a socket.

    :param timeout: value for ``TCP_USER_TIMEOUT``.
    :param idle: value for ``TCP_KEEPIDLE``.
    :param cnt: value for ``TCP_KEEPCNT``.

    :yields: all keepalive related socket options to be set. The first item in the tuple is the protocol, the second
        item is the option, and the third item is the value to be used. The return values depend on the platform:

            * ``Windows``:
                * ``SO_KEEPALIVE``.
            * ``Linux``:
                * ``SO_KEEPALIVE``;
                * ``TCP_USER_TIMEOUT``;
                * ``TCP_KEEPIDLE``;
                * ``TCP_KEEPINTVL``;
                * ``TCP_KEEPCNT``.
            * ``MacOS``:
                * ``SO_KEEPALIVE``;
                * ``TCP_KEEPIDLE``;
                * ``TCP_KEEPINTVL``;
                * ``TCP_KEEPCNT``.
    
patroni/utils.py