How to use context

Issue

For a personal project, I’m spawning an external process which hosts a REST API.
I only want to pass control back to the main thread once the external process has been initialized, which can be known by reading Stdout.

So I have created an io.Writer implementation which closes a channel once a certain criteria has been met.

type channelIOWriter struct {
    InitializedChannel chan bool
    isInitialized      bool
    buffer             string
}

func newChannelIOWriter() *channelIOWriter {
    retVal := new(channelIOWriter)
    retVal.InitializedChannel = make(chan bool)

    return retVal
}

func (writer *channelIOWriter) Write(data []byte) (int, error) {
    for _, b := range data {

        if b == '\n' {
            writer.buffer = *bytes.NewBuffer(make([]byte, 0))
            continue
        }

        writer.buffer.WriteByte(b)

        if strings.HasPrefix(writer.buffer.String(), "ChromeDriver was started successfully.") {
            if !writer.isInitialized {
                close(writer.InitializedChannel)
            }

            writer.isInitialized = true
        }
    }

    return len(data), nil
}

Next, I have the main function which, in a seperate goroutine, spawns the external process.
A wait is performed on the channel that’s suposed to be closed by the io.Writer implementation.

func main() {
    writer := newChannelIOWriter()

    go func() {
        cmd := exec.Command("chromedriver", "--port=9009")
        cmd.Stdout = writer
        cmd.Start()
    }()

    <-writer.InitializedChannel

    fmt.Println("Control is passed back to the MAIN thread.")
}

According to the comments on the question, it seems to be better to use a context.
Anyone who can explain on how to use that?

Solution

I stripped out all unnecessary detail and demo how context with deadline works

package main

import (
    "context"
    "fmt"
    "os/exec"
    "time"
)

type example struct {
    cancel context.CancelFunc
}

func (e *example) Write(data []byte) (int, error) {
    defer e.cancel()
    return len(data), nil
}
func main() {
    deadline := time.Now().Add(5 * time.Second)
    ctx, cancel := context.WithDeadline(context.Background(), deadline)
    writer := &example{
        cancel: cancel,
    }

    go func() {
        cmd := exec.Command("ls", ".")
        cmd.Stdout = writer
        _ = cmd.Start()
    }()

    <-ctx.Done()

    fmt.Println("Control is passed back to the MAIN thread.", ctx.Err())
}

Answered By – Hau Ma

Answer Checked By – Mildred Charles (GoLangFix Admin)

Leave a Reply

Your email address will not be published.