Trigger a channel inside the loop where the the channel is consumed - go

How to rigger a channel inside the loop where the same channel is consumed. Below is a sample code that does not work. How is this achievable?
https://go.dev/play/p/o5ZhNfw4IFu
package main
import (
"context"
"fmt"
"time"
)
func main() {
ch1 := make(chan struct{})
ch2 := make(chan struct{})
defer close(ch1)
defer close(ch2)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
defer cancel()
go func() {
time.Sleep(time.Second * 1)
ch1 <- struct{}{}
}()
loop:
for {
select {
case <-ctx.Done():
fmt.Println("timeout")
break loop
case <-ch1:
fmt.Println("ch1")
ch2 <- struct{}{} // This here does not work!
case <-ch2:
fmt.Println("ch2")
}
}
}

1. send data to ch2 inside goroutine
package main
import (
"context"
"fmt"
"time"
)
func main() {
ch1 := make(chan struct{})
ch2 := make(chan struct{})
defer close(ch1)
defer close(ch2)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
defer cancel()
go func() {
time.Sleep(time.Second * 1)
ch1 <- struct{}{}
}()
loop:
for {
select {
case <-ctx.Done():
fmt.Println("timeout")
break loop
case <-ch1:
fmt.Println("ch1")
go func() {
ch2 <- struct{}{}
}()
case <-ch2:
fmt.Println("ch2")
}
}
}
or
2. make ch2 buffered
package main
import (
"context"
"fmt"
"time"
)
func main() {
ch1 := make(chan struct{})
ch2 := make(chan struct{}, 1)
defer close(ch1)
defer close(ch2)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
defer cancel()
go func() {
time.Sleep(time.Second * 1)
ch1 <- struct{}{}
}()
loop:
for {
select {
case <-ctx.Done():
fmt.Println("timeout")
break loop
case <-ch1:
fmt.Println("ch1")
ch2 <- struct{}{}
case <-ch2:
fmt.Println("ch2")
}
}
}

Related

getting channel value from a webscoket client

I'm running a websocket client and want to pass the response(s) from the client to a channel that i can work with in my main file. Currently, the channel just returns a nil value once and then nothing else. I seem to have an issue when passing a value to the channel. Any help? Here is what I've done so far
package main
import (
"context"
"fmt"
"kraken_client/stored_data"
"kraken_client/ws_client"
"os"
"os/signal"
"strings"
"sync"
"syscall"
)
func main() {
// check if in production or testing mode & find base curency
var testing bool = true
args := os.Args
isTesting(args, &testing, &stored_data.Base_currency)
// go routine handler
comms := make(chan os.Signal, 1)
signal.Notify(comms, os.Interrupt, syscall.SIGTERM)
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
var wg sync.WaitGroup
// set ohlc interval and pairs
OHLCinterval := 5
pairs := []string{"BTC/" + stored_data.Base_currency, "EOS/" + stored_data.Base_currency}
// create ws connections
pubSocket, err := ws_client.ConnectToServer("public", testing)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// listen to websocket connections
ch := make(chan interface{})
wg.Add(1)
go pubSocket.PubListen(ctx, &wg, ch, testing)
// subscribe to a stream
pubSocket.SubscribeToOHLC(pairs, OHLCinterval)
go func() {
for c := range ch {
fmt.Println(c)
}
}()
<-comms
cancel()
wg.Wait()
defer close(ch)
}
Here is how the PubListen function works
func (socket *Socket) PubListen(ctx context.Context, wg *sync.WaitGroup, ch chan interface{}, testing bool) {
defer wg.Done()
defer socket.Close()
var res interface{}
socket.OnTextMessage = func(message string, socket Socket) {
//log.Println(message)
res = pubJsonDecoder(message, testing) // this function decodes the message and returns an interface
log.Println(res) // this is printing the correctly decoded value.
}
ch <- res
log.Println(res) // does not print a value
log.Println(ch) // does not print a value
<-ctx.Done()
log.Println("closing public socket")
return
}
What am I doing wrong?
The code in the question executes the statement ch <- res once from PubListen before the res is set by the OnTextMessage function.
To send a value to ch on each message, move the line ch <- res to the OnTextMessage function. That function is called once for each message.
func (socket *Socket) PubListen(ctx context.Context, wg *sync.WaitGroup, ch chan interface{}, testing bool) {
defer wg.Done()
defer socket.Close()
socket.OnTextMessage = func(message string, socket Socket) {
res := pubJsonDecoder(message, testing)
ch <- res
log.Println(res)
}
<-ctx.Done()
log.Println("closing public socket")
return
}

Golang: Cannot send error to channel in recover()

I try to send an error in the channel on recovery
Why this error is not sent to the channel?
package main
import (
"fmt"
"sync"
"errors"
)
func main() {
var wg sync.WaitGroup
wg.Add(1)
batchErrChan := make(chan error)
go func(errchan chan error) {
defer func() {
if r := recover(); r != nil {
errchan <- errors.New("recover err")
}
close(batchErrChan)
wg.Done()
}()
panic("ddd")
}(batchErrChan)
go func() {
for _ = range batchErrChan {
fmt.Println("err in range")
}
}()
wg.Wait()
}
https://play.golang.org/p/0ytunuYDWZU
I expect "err in range" to be printed, but it is not. Why?
Your program ends before the goroutine gets a chance to print the message. Try waiting to it:
...
done:=make(chan struct{})
go func() {
for _ = range batchErrChan {
fmt.Println("err in range")
}
close(done)
}()
wg.Wait()
<-done
}

Simplest concurrent loop with bounded concurrency

I'm looking for the simplest code for looping over a dataset in parallel. The requirements are that the number of goroutines is fixed and that they can return an error code. The following is a quick attempt which doesn't work, since the loops will deadlock as both goroutines are blocking on the error channel
package main
import (
"fmt"
"sync"
)
func worker(wg *sync.WaitGroup, intChan chan int, errChan chan error) {
defer wg.Done()
for i := range intChan {
fmt.Printf("Got %d\n", i)
errChan <- nil
}
}
func main() {
ints := []int{1,2,3,4,5,6,7,8,9,10}
intChan := make(chan int)
errChan := make(chan error)
wg := new(sync.WaitGroup)
for i := 0; i < 2; i++ {
wg.Add(1)
go worker(wg, intChan, errChan)
}
for i := range ints {
intChan <- i
}
for range ints {
err := <- errChan
fmt.Printf("Error: %v\n", err)
}
close(intChan)
wg.Wait()
}
What is the simplest pattern for doing this?
Listen for errors in a goroutine:
go func() {
for err:=range errChan {
// Deal with err
}
}()
for i := 0; i < 2; i++ {
wg.Add(1)
go worker(wg, intChan, errChan)
}
for i := range ints {
intChan <- i
}
close(errChan) // Error listener goroutine terminates after this

goroutine didn't respect `ctx.done()` or quit properly

I am trying to achieve quit gracefully when user press Ctrl-C. I am trying the code in Make Ctrl+C cancel the context.Context.
package main
import (
"context"
"fmt"
"os"
"os/signal"
"time"
)
func main() {
ctx := context.Background()
// trap Ctrl+C and call cancel on the context
ctx, cancel := context.WithCancel(ctx)
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
defer func() {
signal.Stop(c)
cancel()
fmt.Println("Cleaned up")
}()
go func() {
select {
case <-c:
fmt.Println("Got interrupt signal")
cancel()
case <-ctx.Done():
}
fmt.Println("Stopped monitoring")
}()
select {
case <-ctx.Done():
fmt.Println("notified to quit")
case <-time.NewTimer(time.Second * 2).C:
fmt.Println("done something")
}
}
It works well as expected when user press Ctrl-c, it console out the following:
Got interrupt signal
Stopped monitoring
notified to quit
Cleaned up
However, if it quit normally, It doesn't work as expected as below:
done something
Cleaned up
I mean it should print out Stopped monitoring, but not. In defer cleanup function, it called cancel() which should trigger the select in monitoring goroutine to quit, but not.
How to solve the issue?
Thanks #Zan Lynx, I worked out the below solution.
package main
import (
"context"
"fmt"
"os"
"os/signal"
"time"
)
func main() {
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
terminated := monitor(ctx, cancel)
defer func() {
cancel()
fmt.Println("Cleaned up")
<-terminated // wait for the monior goroutine quit
}()
select {
case <-ctx.Done():
fmt.Println("notified to quit")
case <-time.NewTimer(time.Second * 1).C:
fmt.Println("done something")
}
}
func monitor(ctx context.Context, cancel context.CancelFunc) <-chan interface{} {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
terminated := make(chan interface{})
go func() {
defer close(terminated)
defer fmt.Println("Stopped monitoring1")
defer signal.Stop(c)
select {
case <-c:
fmt.Println("Got interrupt singnal")
cancel()
case <-ctx.Done():
}
}()
return terminated
}

Parent-child context cancelling order in Go

I want to know if there are any guarantees regarding the return order upon Context cancellation in golang.
I want to create a context with cancellation and once all the listeners are done with processing catching and reacting to "<-ctx.Done()" from this context, I want to call os.Exit safely.
A concrete example to explain the idea of what I want is following. I want to catch a signal, trigger all cancellations, and then call os.Exit().
I create a context and listen for a signal:
ctx, cancel := context.WithCancel(context.Background())
go func() {
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt)
defer signal.Stop(c)
select {
case <-c:
cancel()
}
}()
In other places I "sign up" for this request several times:
res := NewRes()
go func() {
<-ctx.Done():
res.Close()
}()
But then I want to call os.Exit at the point when all the listeners are done.
For that I plan to create either parent or child context like this:
parent, pCancel := context.WithCancel(context.Background())
child, _ := context.WithCancel(parent)
go func() {
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt)
defer signal.Stop(c)
select {
case <-c:
pCancel()
case <-child.Done():
os.Exit(0)
}
}()
Unfortunately, I did not find the documentation describing the order how context are canceled, so I cannot come up with the correct solution for now.
You have to wait all routines before exiting. Calling pCancel() doesn't mean everything will stop. I recommend to do in routine all jobs, but on the main thread to wait for os.Interrupt signal.
Check example below
package main
import (
"context"
"fmt"
"os"
"os/signal"
"sync"
"time"
)
func main() {
parent, pCancel := context.WithCancel(context.Background())
child, _ := context.WithCancel(parent)
wg := &sync.WaitGroup{}
for i := 0; i < 10; i++ {
go work(wg, child)
}
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt)
defer signal.Stop(c)
select {
case <-c:
pCancel()
fmt.Println("Waiting everyone to finish...")
wg.Wait()
fmt.Println("Exiting")
os.Exit(0)
}
}
func work(wg *sync.WaitGroup, ctx context.Context) {
done := false
wg.Add(1)
for !done {
fmt.Println("Doing something...")
time.Sleep(time.Second)
select {
case <-ctx.Done():
fmt.Println("Done")
done = true
default:
}
}
wg.Done()
}
Although, It's recommended to use principle "Share Memory By Communicating".
Here is another example without using WaitGroup.
package main
import (
"context"
"fmt"
"os"
"os/signal"
"time"
)
func main() {
parent, pCancel := context.WithCancel(context.Background())
child, _ := context.WithCancel(parent)
done := make(chan struct{})
jobsCount := 10
for i := 0; i < jobsCount; i++ {
go work(child, done)
}
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt)
defer signal.Stop(c)
select {
case <-c:
pCancel()
fmt.Println("Waiting everyone to finish...")
for i := 0; i < jobsCount; i++ {
<-done
}
fmt.Println("Exiting")
os.Exit(0)
}
}
func work(ctx context.Context, doneChan chan struct{}) {
done := false
for !done {
fmt.Println("Doing something...")
time.Sleep(time.Second)
select {
case <-ctx.Done():
fmt.Println("Done")
done = true
default:
}
}
doneChan <- struct{}{}
}

Resources