Created
September 18, 2015 00:17
-
-
Save magical/9c6a1cc08556a7f50a76 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// https://blog.quickmediasolutions.com/2015/09/13/non-blocking-channels-in-go.html | |
// ...without using reflect | |
package util | |
import ( | |
"container/list" | |
) | |
// Channel that does not block when items are sent. To use the struct, simply | |
// send on the Send channel and receive on the Recv channel. Items will be | |
// stored internally until they are received. Closing the Send channel will | |
// cause the Recv channel to be also be closed once all items are received. | |
type NonBlockingChan struct { | |
Send chan<- interface{} | |
Recv <-chan interface{} | |
} | |
// Create a new non-blocking channel. | |
func NewNonBlockingChan() *NonBlockingChan { | |
// Create the two channels that will be used for sending and receiving | |
var ( | |
send = make(chan interface{}) | |
recv = make(chan interface{}) | |
) | |
// Assign the channels to the public members of the struct (which limits | |
// each of their direction) | |
n := &NonBlockingChan{ | |
Send: send, | |
Recv: recv, | |
} | |
// Start a goroutine to perform the sending and receiving | |
go func() { | |
// Create the list that will temporarily hold items for receiving | |
items := list.New() | |
for { | |
// Close the Recv channel and quit if the Send channel was closed | |
// and there are no more items left in the list to send | |
if send == nil && items.Len() == 0 { | |
close(recv) | |
break | |
} | |
// If the list contains at least one item, add a select case to | |
// send the first item from the list on the Recv channel | |
var recvCase chan interface{} | |
var recvItem interface{} | |
if items.Len() > 0 { | |
recvCase = recv | |
recvItem = items.Front().Value | |
} | |
select { | |
case v, ok := <-send: | |
// If an item was received, add it to the list - otherwise, | |
// remove this case from the select statement by setting send to nil | |
if ok { | |
items.PushBack(v) | |
} else { | |
send = nil | |
} | |
case recvCase <- recvItem: | |
// The first item was sent, so it can be removed from the list | |
items.Remove(items.Front()) | |
} | |
} | |
}() | |
return n | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment