Daemon executes only one time a goroutine

Issue

I try to add the necessary code to execute my app like a daemon. I used the next project:

  • github.com/sevlyar/go-daemon

I rewrote the sample go code that it’s done:
https://github.com/sevlyar/go-daemon/blob/master/sample/sample.go

package main

import (
    "bufio"
    "flag"
    "fmt"
    "io/ioutil"
    "os"
    "syscall"
    "time"

    "github.com/sevlyar/go-daemon"
)

var (
    signal = flag.String("s", "", `sdaemon -s ...
        quit -- graceful shutdown`)
)

var (
    stop = make(chan struct{})
    done = make(chan struct{})
)

func main() {
    flag.Parse()
    daemon.AddCommand(daemon.StringFlag(signal, "quit"), syscall.SIGQUIT, TermHandler)

    cntxt := &daemon.Context{
        PidFileName: "/var/run/sdaemon.pid",
        PidFilePerm: 0644,
        WorkDir:     "./",
        Umask:       027,
        Args:        []string{"[sdaemon]"},
    }
    if len(daemon.ActiveFlags()) > 0 {
        d, _ := cntxt.Search()
        daemon.SendCommands(d)
        return
    }
    d, err := cntxt.Reborn()
    if d != nil {
        return
    }
    if err != nil {
        os.Exit(1)
    }
    defer cntxt.Release()

    // Start daemon
    go Worker()

    err = daemon.ServeSignals()
    if err != nil {
        fmt.Printf("STOPPED!\n")
        return
    }
}

func Worker() {
    for {
        go Writer()
        if _, ok := <-stop; ok {
            break
        }
    }
    done <- struct{}{}
}

func TermHandler(sig os.Signal) error {
    stop <- struct{}{}
    if sig == syscall.SIGQUIT {
        <-done
    }
    return daemon.ErrStop
}

I have added a function Writer() that read a file, keep the text like a string and create a new file with this string.

func Writer() error {
    time.Sleep(time.Minute)

    f, _ := ioutil.ReadFile("$HOME/test")
    contents := string(f)

    fileHandle, _ := os.Create("$HOME/stest")
    writer := bufio.NewWriter(fileHandle)
    defer fileHandle.Close()
    fmt.Fprintln(writer, contents)
    writer.Flush()

    return nil
}

I don’t handle so good the channels in golang and I don’t know why the infinite loop for in Worker() function is executed only once…

Can you help me please?

Solution

The problem is in the Worker function, when you try to check if you have any data in the done channel. The receivecall will block until there is a value to be read, so that call will block until you send a signal to the process.

The second value returned from the receive operator, ok, does not indicate whether a value was succesfully read or not. It just indicates if the channel was closed when trying to receive a value (if so the zero value is returned, see the specification).

To check if there is a value in the channel you need to use a select statement, like this:

select {
        case v, ok <- stop:
            // We could read a value from the channel
        default:
            // No value could be read, but we didn't block
}

So your Worker function should look something like:

func Worker() {
    for {
        time.Sleep(time.Minute)
        select {
        case <- stop:
            // Got a stop signal, stopping
            done <- struct{}{}
            return
        default:
            // No stop signal, continuing loop
        }

        go Writer()
    }
}

Note that I have moved the Sleep from the Writer function to the Worker, otherwise you would end up with thousands of concurrent Writer go-routines…

Answered By – johlo

Answer Checked By – Senaida (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.