doulu7174 2018-02-13 14:57
浏览 65

使用Slack Bot处理多个数据争用

I'm working on a slack bot as my first golang project and while the functionality of this specific bot command works great, it can randomly panic and throw an error.

I was able to determine that I have data races going on, specifically with my two go routines. However I am having difficulty determining how to patch them, or whether doing a mutex lock is a proper way to handle shared variables.

I'm trying to figure out if the issue is my result variable, which is split into channels that are each handled by the go routine, or if it is my buffer variable used by the os.exec package to run a command that both stdout and stderr modify.

Here is the code, and data race examples below.

package reboot

import (

    "bytes"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "os/exec"
    "regexp"
)

// to handle json keys
type rebootObject struct {
    Name    string
    Command string
}

var (
    fileNotFound = "config file not found"
    cmdNotFound  = "Error finding command argument"
)

// *bot.Cmd is the first arg to be passed to the bot
// bot.CmdResultV3 wants message from go routine and done = true
func Reboot(command *bot.Cmd) (result bot.CmdResultV3, err error) {
    result = bot.CmdResultV3{Message: make(chan string), Done: make(chan bool, 1)}

    // load json config file with names/commands
    filePath := "./plugins/reboot/config.json"
    file, err1 := ioutil.ReadFile(filePath)
    if err1 != nil {
        bot.Reply(command.Channel, fileNotFound, command.User)
    }

    var scriptParse []rebootObject
    //userinput := "box4535345346" // faking user input
    userinput := command.Args[0] // real one
    err2 := json.Unmarshal(file, &scriptParse)
    if err2 != nil {
        fmt.Println("error:", err2)
        bot.Reply(command.Channel, fileNotFound, command.User)
    }
    //strip numbers off input to match json key
    reg, err := regexp.Compile("[^a-zA-Z]+")
    if err != nil {
        log.Fatal(err)
    }
    // loop through json file to find the match of user input to json name key
    cmdFound := false
    for k := range scriptParse {
        newinput := reg.ReplaceAllString(userinput, "")
        // keep running for loop until names do match
        if scriptParse[k].Name != newinput {
            continue
        }
        cmdFound = true

        cmd := exec.Command("/bin/bash", "-c", scriptParse[k].Command)
        var b bytes.Buffer
        cmd.Stdout = &b
        cmd.Stderr = &b
        err = cmd.Start()
        if err != nil {
            return
        }
        done := false
        go func() {
            cmd.Wait()
            done = true
            result.Done <- true
        }()
        go func() {
            for {
                line, _ := b.ReadString('
')
                if line != "" {
                    result.Message <- line
                }
                if done {
                    close(result.Message)
                    break
                }

            }
        }()
    }
    if cmdFound == false {
        result.Done <- true
        bot.Reply(command.Channel, cmdNotFound, command.User)
    }
    return result, nil
}

Data races:

==================
WARNING: DATA RACE
Read at 0x00c420582558 by goroutine 37:
  bytes.(*Buffer).readSlice()
      /usr/local/go/src/bytes/buffer.go:421 +0x48
  bytes.(*Buffer).ReadString()
      /usr/local/go/src/bytes/buffer.go:440 +0x45
  github.com/owner/gobot/plugins/reboot.Reboot.func2()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:79 +0x41

Previous write at 0x00c420582558 by goroutine 35:
  bytes.(*Buffer).ReadFrom()
      /usr/local/go/src/bytes/buffer.go:92 +0x608
  io.copyBuffer()
      /usr/local/go/src/io/io.go:386 +0x348
  io.Copy()
      /usr/local/go/src/io/io.go:362 +0x7e
  os/exec.(*Cmd).writerDescriptor.func1()
      /usr/local/go/src/os/exec/exec.go:264 +0x68
  os/exec.(*Cmd).Start.func1()
      /usr/local/go/src/os/exec/exec.go:380 +0x34

Goroutine 37 (running) created at:
  github.com/owner/gobot/plugins/reboot.Reboot()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:77 +0x885
  github.com/owner/gobot/bot.(*Bot).handleCmd()
      /Users/macowner/go/src/github.com/owner/gobot/bot/cmd.go:240 +0x13b
  github.com/owner/gobot/bot.(*Bot).MessageReceived()
      /Users/macowner/go/src/github.com/owner/gobot/bot/bot.go:101 +0x5a8
  github.com/owner/gobot/bot.Run()
      /Users/macowner/go/src/github.com/owner/gobot/bot/reply.go:142 +0xdb3
  main.main()
      /Users/macowner/go/src/github.com/owner/gobot/main.go:46 +0x9b0

Goroutine 35 (running) created at:
  os/exec.(*Cmd).Start()
      /usr/local/go/src/os/exec/exec.go:379 +0xa6b
  github.com/owner/gobot/plugins/reboot.Reboot()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:67 +0x7c8
  github.com/owner/gobot/bot.(*Bot).handleCmd()
      /Users/macowner/go/src/github.com/owner/gobot/bot/cmd.go:240 +0x13b
  github.com/owner/gobot/bot.(*Bot).MessageReceived()
      /Users/macowner/go/src/github.com/owner/gobot/bot/bot.go:101 +0x5a8
  github.com/owner/gobot/bot.Run()
      /Users/macowner/go/src/github.com/owner/gobot/bot/reply.go:142 +0xdb3
  main.main()
      /Users/macowner/go/src/github.com/owner/gobot/main.go:46 +0x9b0
==================
==================
WARNING: DATA RACE
Read at 0x00c420582540 by goroutine 37:
  bytes.(*Buffer).readSlice()
      /usr/local/go/src/bytes/buffer.go:421 +0x62
  bytes.(*Buffer).ReadString()
      /usr/local/go/src/bytes/buffer.go:440 +0x45
  github.com/owner/gobot/plugins/reboot.Reboot.func2()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:79 +0x41

Previous write at 0x00c420582540 by goroutine 35:
  bytes.(*Buffer).ReadFrom()
      /usr/local/go/src/bytes/buffer.go:91 +0x5d0
  io.copyBuffer()
      /usr/local/go/src/io/io.go:386 +0x348
  io.Copy()
      /usr/local/go/src/io/io.go:362 +0x7e
  os/exec.(*Cmd).writerDescriptor.func1()
      /usr/local/go/src/os/exec/exec.go:264 +0x68
  os/exec.(*Cmd).Start.func1()
      /usr/local/go/src/os/exec/exec.go:380 +0x34

Goroutine 37 (running) created at:
  github.com/owner/gobot/plugins/reboot.Reboot()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:77 +0x885
  github.com/owner/gobot/bot.(*Bot).handleCmd()
      /Users/macowner/go/src/github.com/owner/gobot/bot/cmd.go:240 +0x13b
  github.com/owner/gobot/bot.(*Bot).MessageReceived()
      /Users/macowner/go/src/github.com/owner/gobot/bot/bot.go:101 +0x5a8
  github.com/owner/gobot/bot.Run()
      /Users/macowner/go/src/github.com/owner/gobot/bot/reply.go:142 +0xdb3
  main.main()
      /Users/macowner/go/src/github.com/owner/gobot/main.go:46 +0x9b0

Goroutine 35 (running) created at:
  os/exec.(*Cmd).Start()
      /usr/local/go/src/os/exec/exec.go:379 +0xa6b
  github.com/owner/gobot/plugins/reboot.Reboot()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:67 +0x7c8
  github.com/owner/gobot/bot.(*Bot).handleCmd()
      /Users/macowner/go/src/github.com/owner/gobot/bot/cmd.go:240 +0x13b
  github.com/owner/gobot/bot.(*Bot).MessageReceived()
      /Users/macowner/go/src/github.com/owner/gobot/bot/bot.go:101 +0x5a8
  github.com/owner/gobot/bot.Run()
      /Users/macowner/go/src/github.com/owner/gobot/bot/reply.go:142 +0xdb3
  main.main()
      /Users/macowner/go/src/github.com/owner/gobot/main.go:46 +0x9b0
==================
==================
WARNING: DATA RACE
Write at 0x00c420582560 by goroutine 37:
  bytes.(*Buffer).readSlice()
      /usr/local/go/src/bytes/buffer.go:429 +0x186
  bytes.(*Buffer).ReadString()
      /usr/local/go/src/bytes/buffer.go:440 +0x45
  github.com/owner/gobot/plugins/reboot.Reboot.func2()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:79 +0x41

Previous write at 0x00c420582560 by goroutine 35:
  bytes.(*Buffer).ReadFrom()
      /usr/local/go/src/bytes/buffer.go:191 +0x5f
  io.copyBuffer()
      /usr/local/go/src/io/io.go:386 +0x348
  io.Copy()
      /usr/local/go/src/io/io.go:362 +0x7e
  os/exec.(*Cmd).writerDescriptor.func1()
      /usr/local/go/src/os/exec/exec.go:264 +0x68
  os/exec.(*Cmd).Start.func1()
      /usr/local/go/src/os/exec/exec.go:380 +0x34

Goroutine 37 (running) created at:
  github.com/owner/gobot/plugins/reboot.Reboot()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:77 +0x885
  github.com/owner/gobot/bot.(*Bot).handleCmd()
      /Users/macowner/go/src/github.com/owner/gobot/bot/cmd.go:240 +0x13b
  github.com/owner/gobot/bot.(*Bot).MessageReceived()
      /Users/macowner/go/src/github.com/owner/gobot/bot/bot.go:101 +0x5a8
  github.com/owner/gobot/bot.Run()
      /Users/macowner/go/src/github.com/owner/gobot/bot/reply.go:142 +0xdb3
  main.main()
      /Users/macowner/go/src/github.com/owner/gobot/main.go:46 +0x9b0

Goroutine 35 (running) created at:
  os/exec.(*Cmd).Start()
      /usr/local/go/src/os/exec/exec.go:379 +0xa6b
  github.com/owner/gobot/plugins/reboot.Reboot()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:67 +0x7c8
  github.com/owner/gobot/bot.(*Bot).handleCmd()
      /Users/macowner/go/src/github.com/owner/gobot/bot/cmd.go:240 +0x13b
  github.com/owner/gobot/bot.(*Bot).MessageReceived()
      /Users/macowner/go/src/github.com/owner/gobot/bot/bot.go:101 +0x5a8
  github.com/owner/gobot/bot.Run()
      /Users/macowner/go/src/github.com/owner/gobot/bot/reply.go:142 +0xdb3
  main.main()
      /Users/macowner/go/src/github.com/owner/gobot/main.go:46 +0x9b0
==================
==================
WARNING: DATA RACE
Read at 0x00c42014cc00 by goroutine 37:
  runtime.slicebytetostring()
      /usr/local/go/src/runtime/string.go:72 +0x0
  bytes.(*Buffer).ReadString()
      /usr/local/go/src/bytes/buffer.go:441 +0x84
  github.com/owner/gobot/plugins/reboot.Reboot.func2()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:79 +0x41

Previous write at 0x00c42014cc00 by goroutine 35:
  internal/race.WriteRange()
      /usr/local/go/src/internal/race/race.go:49 +0x42
  syscall.Read()
      /usr/local/go/src/syscall/syscall_unix.go:165 +0x9a
  internal/poll.(*FD).Read()
      /usr/local/go/src/internal/poll/fd_unix.go:122 +0x1a0
  os.(*File).read()
      /usr/local/go/src/os/file_unix.go:216 +0x70
  os.(*File).Read()
      /usr/local/go/src/os/file.go:103 +0x8e
  bytes.(*Buffer).ReadFrom()
      /usr/local/go/src/bytes/buffer.go:209 +0x1dd
  io.copyBuffer()
      /usr/local/go/src/io/io.go:386 +0x348
  io.Copy()
      /usr/local/go/src/io/io.go:362 +0x7e
  os/exec.(*Cmd).writerDescriptor.func1()
      /usr/local/go/src/os/exec/exec.go:264 +0x68
  os/exec.(*Cmd).Start.func1()
      /usr/local/go/src/os/exec/exec.go:380 +0x34

Goroutine 37 (running) created at:
  github.com/owner/gobot/plugins/reboot.Reboot()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:77 +0x885
  github.com/owner/gobot/bot.(*Bot).handleCmd()
      /Users/macowner/go/src/github.com/owner/gobot/bot/cmd.go:240 +0x13b
  github.com/owner/gobot/bot.(*Bot).MessageReceived()
      /Users/macowner/go/src/github.com/owner/gobot/bot/bot.go:101 +0x5a8
  github.com/owner/gobot/bot.Run()
      /Users/macowner/go/src/github.com/owner/gobot/bot/reply.go:142 +0xdb3
  main.main()
      /Users/macowner/go/src/github.com/owner/gobot/main.go:46 +0x9b0

Goroutine 35 (running) created at:
  os/exec.(*Cmd).Start()
      /usr/local/go/src/os/exec/exec.go:379 +0xa6b
  github.com/owner/gobot/plugins/reboot.Reboot()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:67 +0x7c8
  github.com/owner/gobot/bot.(*Bot).handleCmd()
      /Users/macowner/go/src/github.com/owner/gobot/bot/cmd.go:240 +0x13b
  github.com/owner/gobot/bot.(*Bot).MessageReceived()
      /Users/macowner/go/src/github.com/owner/gobot/bot/bot.go:101 +0x5a8
  github.com/owner/gobot/bot.Run()
      /Users/macowner/go/src/github.com/owner/gobot/bot/reply.go:142 +0xdb3
  main.main()
      /Users/macowner/go/src/github.com/owner/gobot/main.go:46 +0x9b0
==================
==================
WARNING: DATA RACE
Read at 0x00c4202d2600 by goroutine 37:
  runtime.slicebytetostring()
      /usr/local/go/src/runtime/string.go:72 +0x0
  bytes.(*Buffer).ReadString()
      /usr/local/go/src/bytes/buffer.go:441 +0x84
  github.com/owner/gobot/plugins/reboot.Reboot.func2()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:79 +0x41

Previous write at 0x00c4202d2600 by goroutine 35:
  runtime.slicecopy()
      /usr/local/go/src/runtime/slice.go:160 +0x0
  bytes.(*Buffer).ReadFrom()
      /usr/local/go/src/bytes/buffer.go:205 +0x4b2
  io.copyBuffer()
      /usr/local/go/src/io/io.go:386 +0x348
  io.Copy()
      /usr/local/go/src/io/io.go:362 +0x7e
  os/exec.(*Cmd).writerDescriptor.func1()
      /usr/local/go/src/os/exec/exec.go:264 +0x68
  os/exec.(*Cmd).Start.func1()
      /usr/local/go/src/os/exec/exec.go:380 +0x34

Goroutine 37 (running) created at:
  github.com/owner/gobot/plugins/reboot.Reboot()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:77 +0x885
  github.com/owner/gobot/bot.(*Bot).handleCmd()
      /Users/macowner/go/src/github.com/owner/gobot/bot/cmd.go:240 +0x13b
  github.com/owner/gobot/bot.(*Bot).MessageReceived()
      /Users/macowner/go/src/github.com/owner/gobot/bot/bot.go:101 +0x5a8
  github.com/owner/gobot/bot.Run()
      /Users/macowner/go/src/github.com/owner/gobot/bot/reply.go:142 +0xdb3
  main.main()
      /Users/macowner/go/src/github.com/owner/gobot/main.go:46 +0x9b0

Goroutine 35 (running) created at:
  os/exec.(*Cmd).Start()
      /usr/local/go/src/os/exec/exec.go:379 +0xa6b
  github.com/owner/gobot/plugins/reboot.Reboot()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:67 +0x7c8
  github.com/owner/gobot/bot.(*Bot).handleCmd()
      /Users/macowner/go/src/github.com/owner/gobot/bot/cmd.go:240 +0x13b
  github.com/owner/gobot/bot.(*Bot).MessageReceived()
      /Users/macowner/go/src/github.com/owner/gobot/bot/bot.go:101 +0x5a8
  github.com/owner/gobot/bot.Run()
      /Users/macowner/go/src/github.com/owner/gobot/bot/reply.go:142 +0xdb3
  main.main()
      /Users/macowner/go/src/github.com/owner/gobot/main.go:46 +0x9b0
==================
==================
WARNING: DATA RACE
Write at 0x00c4202008c8 by goroutine 36:
  github.com/owner/gobot/plugins/reboot.Reboot.func1()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:74 +0x4d

Previous read at 0x00c4202008c8 by goroutine 37:
  github.com/owner/gobot/plugins/reboot.Reboot.func2()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:83 +0x61

Goroutine 36 (running) created at:
  github.com/owner/gobot/plugins/reboot.Reboot()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:72 +0x846
  github.com/owner/gobot/bot.(*Bot).handleCmd()
      /Users/macowner/go/src/github.com/owner/gobot/bot/cmd.go:240 +0x13b
  github.com/owner/gobot/bot.(*Bot).MessageReceived()
      /Users/macowner/go/src/github.com/owner/gobot/bot/bot.go:101 +0x5a8
  github.com/owner/gobot/bot.Run()
      /Users/macowner/go/src/github.com/owner/gobot/bot/reply.go:142 +0xdb3
  main.main()
      /Users/macowner/go/src/github.com/owner/gobot/main.go:46 +0x9b0

Goroutine 37 (running) created at:
  github.com/owner/gobot/plugins/reboot.Reboot()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:77 +0x885
  github.com/owner/gobot/bot.(*Bot).handleCmd()
      /Users/macowner/go/src/github.com/owner/gobot/bot/cmd.go:240 +0x13b
  github.com/owner/gobot/bot.(*Bot).MessageReceived()
      /Users/macowner/go/src/github.com/owner/gobot/bot/bot.go:101 +0x5a8
  github.com/owner/gobot/bot.Run()
      /Users/macowner/go/src/github.com/owner/gobot/bot/reply.go:142 +0xdb3
  main.main()
      /Users/macowner/go/src/github.com/owner/gobot/main.go:46 +0x9b0
==================
  • 写回答

2条回答 默认 最新

  • dongpo7467 2018-02-13 17:23
    关注

    The first goroutine, starting with cmd.Wait(), is writing to the buffer b while the other goroutine, starting with line, _ := b.ReadString(' ') is reading from it. That's why datarace happens. Modify the code in such a way that the result of execution is read only after completion of execution.

    评论

报告相同问题?

悬赏问题

  • ¥20 模型在y分布之外的数据上预测能力不好如何解决
  • ¥15 processing提取音乐节奏
  • ¥15 gg加速器加速游戏时,提示不是x86架构
  • ¥15 python按要求编写程序
  • ¥15 Python输入字符串转化为列表排序具体见图,严格按照输入
  • ¥20 XP系统在重新启动后进不去桌面,一直黑屏。
  • ¥15 opencv图像处理,需要四个处理结果图
  • ¥15 无线移动边缘计算系统中的系统模型
  • ¥15 深度学习中的画图问题
  • ¥15 java报错:使用mybatis plus查询一个只返回一条数据的sql,却报错返回了1000多条