I have a type with two methods; Add
& Close
. The Add
method is accessed concurrently and it should check if Close
was ever called.
type foo struct {
closed bool
}
func (f *foo) Close() error {
f.closed = true
...
}
func (f *foo) Add(...) error {
if f.closed {
return ErrClosed
}
...
}
This is a race condition right?
So would using an atomic store/load in this situation make sense?
type foo struct {
closed int32
}
func (f *foo) Close() error {
atomic.StoreInt32(&f.closed, 1)
...
}
func (f *foo) Add(...) error {
if atomic.LoadInt32(&f.closed) == 1 {
return ErrClosed
}
...
}
Or would a channel be a more idiomatic way to do this:
type foo struct {
closed chan struct{}
}
func NewFoo() *foo {
return &foo{make(chan struct{})}
}
func (f *foo) isClosed() bool {
select {
case <-f.closed:
return true
default:
}
return false
}
func (f *foo) Close() error {
close(f.closed)
...
}
func (f *foo) Add(...) error {
if f.isClosed() {
return ErrClosed
}
...
}
EDIT:
Thank you for the comments, I ended up with this.
type foo struct {
closed bool
closeLock sync.RWMutex
}
func (f *foo) Close() error {
f.closeLock.Lock()
defer f.closeLock.Unlock()
f.closed = true
...
}
func (f *foo) Add(...) error {
f.closeLock.RLock()
defer f.closeLock.RUnlock()
if f.closed {
return ErrClosed
}
...
}