Hi All,

I'm looking for some advice on a pattern I've found myself trapped in. I 
don't think I like it.

For all examples, assume I have to meet the following interface (enforced 
externally):

type DoerCloser interface {
Do()
Close()
}


The code below is mostly snippets from: 

https://play.golang.org/p/RQp97u4ZeK

First, the part that seems standard (or is this my first incorrect 
assumption?):

I'm using a "closed" (or "closing") channel to tell all methods on a struct 
that the user has asked us to close, and then a "done" channel to allow the 
methods to alert that they're done.

func (t *doerCloser) do() {
t.doneDoing = make(chan struct{})
defer close(t.doneDoing)
for {
select {
case <-t.closed:
return
case <-time.After(time.Second):
fmt.Println("Doing a thing!")
}
}
}

func (t *doerCloser) Close() {
close(t.closed)
<-t.doneDoing
}

This works fine if the "New" function that creates the struct kicks off the 
action, and therefore the "doneDoing" channel is always created and in a 
good state.

func NewDoerCloser1() DoerCloser {
dc := &doerCloser{
closed: make(chan struct{}),
}
go dc.do()
return dc
}

User can do the following safely: 

dc := NewDoerCloser1()
defer dc.Close()


Where it gets tricky is if "Do" can't be kicked off right away. In my 
current real-world example I need to meet an external interface, and the 
interface dictates that "Do" kicks of the thing--in other examples perhaps 
you simply want the user to be able to explicitly start the doing.

// If "do" is never called, Close will "Block" because it's a nil channel
func NewDoerCloser2() DoerCloser {
return &doerCloser{
closed: make(chan struct{}),
}
}

If "do" is never called, then "Close" will block forever on:

<-t.doneDoing.

One solution would be to create "doneDoing" and then close it immediately. 
Seems crazy to me; however, It's fairly simple so perhaps it isn't crazy?

func NewDoerCloser3() DoerCloser {
doneDoing := make(chan struct{})
defer close(doneDoing)

return &doerCloser{
closed:    make(chan struct{}),
doneDoing: doneDoing,
}
}

Another solution would be to "kick off" the "Do" functionality, but have it 
wait to start processing until "Do" is explicitly called. This seems much 
more complex than the "create and close immediately" idea.

func NewDoerCloser() DoerCloser {
dc := &doerCloser{
closed:   make(chan struct{}),
doCalled: make(chan struct{}),
}

go dc.do()

return dc
}

func (t *doerCloser) do() {
t.doneDoing = make(chan struct{})
defer close(t.doneDoing)

select {
case <-t.closed:
return
case <-t.doCalled:
}

// Let's start doing!

for {
select {
case <-t.closed:
return
case <-time.After(time.Millisecond):
fmt.Println("Doing a thing!")
}
}
}


https://play.golang.org/p/qA3_oFF_EP

Any other options? Am I totally crazy? Is this overcomplicated and there is 
a much simpler solution I'm spacing on?

Thank's everyone!

Evan

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to