title: Avoiding race conditions while sending to channel?
body: <p>go version go1.11.2 darwin/amd64</p>
  
  <p>I have the following code sample, created for SO demo purposes:</p>
  
  <pre><code>package main
  
  import (
      ...
  )
  
  type T struct {
      ctx context.Context
      ch1 chan string
  }
  
  func New(ctx context.Context) *T {
      t := &amp;T{ctx: ctx}
      go t.run(2)
      return t
  
  }
  
  func (t *T) run(workers int) {
      t.ch1 = make(chan string)
      done := make(chan struct{})
  
      go func() {
          &lt;-t.ctx.Done()
          close(done)
          close(t.ch1)
      }()
  
      for i := 0; i &lt; workers; i++ {
          go func() {
              for {
                  select {
                  case &lt;-done:
                      return
                  case m, ok := &lt;-t.ch1:
                      if ok {
                          t.process(done, m)
                      }
                  }
              }
          }()
      }
  }
  
  func (t *T) process(done &lt;-chan struct{}, s string) {
      select {
      case &lt;-done:
          return
      default:
          log.Printf("processing %s", s)
          time.Sleep(time.Millisecond * 200)
      }
  }
  
  func (t *T) Read() &lt;-chan string {
      return t.ch1
  }
  
  func (t *T) Write(s string) error {
      select {
      case &lt;-t.ctx.Done():
          return errors.New("consumer is closed today")
      case t.ch1 &lt;- s:
          return nil
      }
  }
  
  func main() {
      ctx, cancel := context.WithCancel(context.Background())
      t := New(ctx)
  
      go func() {
          for m := range t.Read() {
              log.Printf("got %s", m)
          }
          &lt;-ctx.Done()
      }()
  
      for i := 0; i &lt; 10; i++ {
          t.Write(strconv.Itoa(i))
      }
      cancel()
  }
  </code></pre>
  
  <p>When I build and run this with the race detector, it throws the following data race:</p>
  
  <pre><code>go build -race ./test/ &amp;&amp; ./test
  ==================
  WARNING: DATA RACE
  Read at 0x00c0000b6030 by goroutine 7:
    main.main.func1()
        /redacted/test/app.go:60 +0x42
  
  Previous write at 0x00c0000b6030 by goroutine 6:
    main.(*T).run()
        /redacted/test/app.go:24 +0x6a
  
  Goroutine 7 (running) created at:
    main.main()
        /redacted/test/app.go:76 +0xbc
  
  Goroutine 6 (running) created at:
    main.New()
        /redacted/test/app.go:18 +0xcd
    main.main()
        /redacted/test/app.go:74 +0x86
  ==================
  ==================
  WARNING: DATA RACE
  Read at 0x00c0000b6030 by main goroutine:
    main.(*T).Write()
        /redacted/test/app.go:67 +0x8a
    main.main()
        /redacted/test/app.go:84 +0xdc
  
  Previous write at 0x00c0000b6030 by goroutine 6:
    main.(*T).run()
        /redacted/test/app.go:24 +0x6a
  
  Goroutine 6 (running) created at:
    main.New()
        /redacted/test/app.go:18 +0xcd
    main.main()
        /redacted/test/app.go:74 +0x86
  ==================
  2019/01/20 10:48:51 got 0
  2019/01/20 10:48:51 got 3
  2019/01/20 10:48:51 processing 1
  2019/01/20 10:48:51 processing 2
  2019/01/20 10:48:51 got 4
  2019/01/20 10:48:51 got 5
  2019/01/20 10:48:51 got 6
  2019/01/20 10:48:51 got 7
  2019/01/20 10:48:51 got 8
  2019/01/20 10:48:51 got 9
  Found 2 data race(s)
  </code></pre>
  
  <p>The problem I'm getting is that I can't seem to find a way to have a user input something in to a channel, without exposing any channel for writes, without a race. How would this be possible? Is there a better pattern for this that I am missing?</p>