Search code examples
gotimedaemongoroutine

Daemon executes only one time a goroutine


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...