pkaramol
pkaramol

Reputation: 19332

Synchronising writes to io.MultiWriter in golang

I am working on an app that should be able to write to both a bytes.Buffer as well to os.Stdout / os.Stderr.

Therefore I am creating an

w := io.MultiWriter(myBuf, os.Stdout)

The writes will be from multiple goroutines.

To make at least my bytes.Buffer thread safe, I am wrapping it

type Buffer struct {
    buffer bytes.Buffer
    mutex  sync.Mutex
}

func (s *Buffer) Write(p []byte) (n int, err error) {
    s.mutex.Lock()
    defer s.mutex.Unlock()
    return s.buffer.Write(p)
}

func (s *Buffer) String() string {
    s.mutex.Lock()
    defer s.mutex.Unlock()
    return s.buffer.String()
}

How can I achieve the same result with the standard error / output writes?

I thought about using log but it does not implement the io.Writer interface.

Upvotes: 0

Views: 1162

Answers (2)

erik258
erik258

Reputation: 16305

How can I achieve the same result with the standard error / output writes?

With a mutex, like you said.

I thought about using log but it does not implement the io.Writer interface.

Interesting idea, since it locks itself, but you can do this with plain old os.Stdout and os.Stderr, both of which implement io.Writer:

package main

import (
    "fmt"
    "io"
    "os"
    "sync"
)

type LockedWriter struct {
    w io.Writer
    l sync.Mutex
}

func (lw *LockedWriter) Write(p []byte) (n int, err error) {
    lw.l.Lock()
    defer lw.l.Unlock()
    return lw.w.Write(p)
}

func main() {
    var wg sync.WaitGroup
    var w = &LockedWriter{
        w: io.MultiWriter(os.Stdout, os.Stderr),
    }
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(i int) {
            for j := 0; j < i; j++ {
                fmt.Fprintf(w, "I am goroutine %d (%d/%d)\n", i, j, i)
            }
            wg.Done()
        }(i)
    }
    wg.Wait()
}

In this particular case, I couldn't reproduce any interpolated writes, but I think if the messages were long enough or the goroutines were doing more operations, I would.

Upvotes: 2

Kelsnare
Kelsnare

Reputation: 705

Will this help:

type StdoutType {
    stdout *File
    mutex sync.Mutex
}

func NewStdoutType() *StdoutType {
    return &StdoutType{
        stdout: os.Stdout,
    }
}

func (s *StdoutType) Write(p []byte) (n int, err error) {
    s.mutex.Lock()
    defer s.mutex.Unlock()
    return s.stdout.Write(p)
}

StdoutType is now io.Writer compatible

Upvotes: 0

Related Questions