framontb
framontb

Reputation: 2077

Frozen script at unix socket read with EAGAIN (Resource temporarily unavailable) error

I have this Go connection snippet in a script:

    d := net.Dialer{Timeout: time.Second * 5}
    c, err := d.Dial("unix", CONFIG.ServiceSocket.Path)
    if err != nil {
        handleFatalError(err, errorString)
    }
    defer c.Close()

    // send message
    message := fmt.Sprintf("{\"action\":\"%s\"}", *Arguments.Action)
    _, err = c.Write([]byte(message))
    if err != nil {
        var errorString = fmt.Sprintf("Write error: %s", err)
        handleFatalError(err, errorString)
    }

    // read response
    buf := make([]byte, CONFIG.ServiceSocket.Buffer)
    n, err := c.Read(buf[:])
    if err != nil {
        var errorString = fmt.Sprintf("Read error: %s", err)
        handleFatalError(err, errorString)
    }

But the script gets frozen with the following sequence:

socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3
connect(3, {sa_family=AF_UNIX, sun_path="/var/run/dhcomms/dispatcher.sock"}, 35) = 0
epoll_ctl(4, EPOLL_CTL_ADD, 3, {EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLET, {u32=74057432, u64=139805554509528}}) = 0
getsockname(3, {sa_family=AF_UNIX}, [112->2]) = 0
getpeername(3, {sa_family=AF_UNIX, sun_path="/var/run/dhcomms/dispatcher.sock"}, [112->35]) = 0
write(3, "{\"action\":\"status\"}", 19) = 19
read(3, 0xc000100000, 1048576)          = -1 EAGAIN (Resource temporarily unavailable)
futex(0x6aafd8, FUTEX_WAKE_PRIVATE, 1)  = 1
futex(0x6aaed8, FUTEX_WAKE_PRIVATE, 1)  = 1
epoll_pwait(4, [{EPOLLOUT, {u32=74057432, u64=139805554509528}}], 128, 0, NULL, 0) = 1
epoll_pwait(4, 

I would like to capture this EAGAIN error and close the program in an orderly manner instead.

How should I achieve that ?

EDIT [19/6/23]: Added write/read code

Upvotes: 0

Views: 226

Answers (1)

framontb
framontb

Reputation: 2077

I explain here what I finally did, but I don't like it very much because it seems to me a kind of artificial way to solve the issue. The program get blocked reading the socket:

n, err = c.Read(buf[:])

So I wrap it inside a go routine with a channel an apply a timeout to the whole thing:

    var TIMEOUT_READ_SOCKET_UNIX time.Duration = 5
    buf := make([]byte, CONFIG.ServiceSocket.Buffer)
    c1 := make(chan string, 1)
    var n int
    go func() {
        n, err = c.Read(buf[:])
        c1 <- ""
    }()

    select {
    case res := <-c1:
        fmt.Println(res)
        break
    case <-time.After(TIMEOUT_READ_SOCKET_UNIX * time.Second):
        var errorString = "Timeout reading unix socket"
        err := fmt.Errorf("Timeout = %d seconds", TIMEOUT_READ_SOCKET_UNIX)
        handleFatalError(err, errorString)
    }

I didn't find the way to apply a timeout to the unix socket when the EAGAIN error happens. I bet this should be the right way. If someone knows how to do that, please...

NOTE: I used go-version 1.20 and I'm newby in Go.

Upvotes: -1

Related Questions