Launch go routine in handler

I have an HTTP handler. I would like to run a go routine and then populate the response writer and return control to the browser but keep the go routine running. I am able to keep the go routine running using a chan but the page does not get delivered to the browser, the previous page is just suspended, waiting.

func realTimeHandler(writer http.ResponseWriter, request *http.Request) {
	fmt.Println("realTimeHandler")
	log.Println("realTimeHandler")
	p := data.DaemonPage{}
	initializeHandling(&p, request)
	initializeTrackSpecification(&p)
	p.TrackSpecification.Stock.Symbol = getStringValue(&p, "stockSymbol")
	p.RealTime = true

	ch := make(chan bool)

	go finnHubRealTime.Realtime(p.TrackSpecification.Stock.Symbol, ch)

	RenderPage(writer, &p)

	done := <-ch
	log.Println(done)
}

Without the chan lock, the new page is rendered but the function exits, killing the finnHubRealTime.Realtime function.

Are you sure the problem isn’t that main is exiting? Check this StackOverflow question out. I’m assuming you’re blocking main but just in case:

Also:

the previous page is just suspended, waiting.

That makes me wonder if finnHubRealTime.Realtime is stuck in an endless loop or something along those lines. Might help if you could show the implementation there.

I don’t think this is very elegant, but it is working, in main package, a global variable:

var realTimeGoRoutinesControllerChannel = make(chan *data.DaemonPage)

then, in main()

	go func() {
		for {
			p := <-realTimeGoRoutinesControllerChannel
			ch := make(chan bool)

			go finnHubRealTime.Realtime(p.TrackSpecification.Stock.Symbol, ch)

		}
	}()

then in the handler:

func realTimeHandler(writer http.ResponseWriter, request *http.Request) {
	fmt.Println("realTimeHandler")
	log.Println("realTimeHandler")
	p := data.DaemonPage{}

	initializeHandling(&p, request)
	initializeTrackSpecification(&p)
	p.TrackSpecification.Stock.Symbol = getStringValue(&p, "stockSymbol")
	p.RealTime = true

	realTimeGoRoutinesControllerChannel <- &p

	RenderPage(writer, &p)

}

Check out my solution below. Sorry, I made the previous reply yesterday but then got distracted and forgot to send it. It is working now. Now I’m working on controlling the finnHubRealTime.Realtime to start, stop, restart. Making progress but crashing on the channel.

in main, I launch monitorRealTiimeChannel() as a go routine. It waits until realTimeGoRoutinesControllerChannel gets set.

In the realTimeHandler(), I populate the realTimeGoRoutinesControllerChannel, which triggers launching finnHubRealTime.Realtime() and it sets the variable: realTimeIsRunning to true. This works fine. The web page is updating every few seconds with the latest stock price.

Now, on the web page I want to switch to a different stock. So again it calls realTimeHandler(). The handler triggers the monitor with:
realTimeGoRoutinesControllerChannel ← &p
The monitor checks realTimeIsRunning and if true, it sets the p.DoRealTime to false and puts p into realTimeGoRoutinesControllerChannel. In the RealTime() function there is an anonymous go function stopped waiting on realTimeGoRoutinesControllerChannel. It checks the value of p.DoRealTime. If false it terminates the web socket processing. It then sets the channel to p.
The monitor has been waiting on the channel and receives the input. It then puts p into the channel, but I think it crashes because nobody is waiting on it. I tested this with this function:

func sendToChannel() {
	ch := make(chan bool)
	ch <- true
}

This crashes.

Here is my monitor. I apologize for all of the logging but it helps me when dealing with concurrency.


func monitorRealTimeChannel() {
	for {
		log.Println("monitorRealTimeChannel is waiting")
		p := <-realTimeGoRoutinesControllerChannel

		log.Println("received realTimeGoRoutinesControllerChannel")
		if realTimeIsRunning {
			log.Println("realTimeIsRunning")
			realTimeIsRunning = false

			log.Println("tell realtime to stop - p.DoRealTime = false")
			p.DoRealTime = false // tell realtime to stop

			log.Printf("p.DoRealTime: %v", p.DoRealTime)
			log.Println("realTimeGoRoutinesControllerChannel <- p")
			realTimeGoRoutinesControllerChannel <- p

			//now wait until realtime has finished up
			log.Println("now wait until realtime has finished up")
			log.Println("p := <-realTimeGoRoutinesControllerChannel")
			p := <-realTimeGoRoutinesControllerChannel
			p.DoRealTime = true

			log.Printf("p.DoRealTime: %v", p.DoRealTime)

			// put p back into realTimeGoRoutinesControllerChannel
			log.Println("put p back into realTimeGoRoutinesControllerChannel ")
			log.Println("this crashes the app")
			log.Println("realTimeGoRoutinesControllerChannel <- p")

			// ah ha, the realTimeGoRoutinesControllerChannel has nobody waiting on it
			realTimeGoRoutinesControllerChannel <- p
		} else {
			//tell realtime to run with this p
			log.Println("realTimeIsRunning = false, so launch realtime")
			realTimeIsRunning = true
			go finnHubRealTime.Realtime(p.TrackSpecification.Stock.Symbol, realTimeGoRoutinesControllerChannel, &stock)

		}

	}
}

What I would like to do is have the monitor waiting on the channel again and then trigger it, but so far I haven’t figured out how to do that.

Then, it will be time to clean up the code as I’ve been doing a lot of trial and error to figure out web sockets, go routines, and channels.
more work …
OK, I guess I will need to study channels some more. The way I understand it, this line should cause a wait until the channel is populated:

p := <-realTimeGoRoutinesControllerChannel

But it didn’t wait and p is nil. This was the second time thru the loop after running realtime and then cancelling it.
I had disabled the code that sets p into the channel to see if I could at least stop the realtime. On this line, the channel is not nil but it allows receiving from it and populates p as nil.

OK, I have it working now. I had a defer close(ch) that I had forgotten about. But then I decided to use 2 channels instead of the 1 and then cut out a bunch of unnecessary code. Now it’s working. However, the markets are closed so I’m not getting much data.

If financial data is to be monitored continuously in real time, it would be more appropriate to do it with socket io. If you want to return the result of a long-running calculation in pieces, chunked response can be returned.

thanks for that. The service I am using, finnhub, supports web sockets, which I have never used before. The code that generates the real time quotes is this:

		w, _, err := websocket.DefaultDialer.Dial("wss://ws.finnhub.io?token=TOKEN"XXX, nil)
		if err != nil {
			panic(err)
		}
		defer w.Close()

		symbols := []string{stockSymbol}

		for _, s := range symbols {
			msg, _ := json.Marshal(map[string]interface{}{"type": "subscribe", "symbol": s})
			w.WriteMessage(websocket.TextMessage, msg)
		}

		var msg interface{}

		stock.LatestPrice = -1
		stock.Symbol = stockSymbol

		for doRealTime {
			err := w.ReadJSON(&msg)

I then just deal with the quotes which are in msg. Now, I’m attempting to connect to the trade API which, unfortunately, uses oauth 1.0 which is a pain and I haven’t used it in over 10 years. Most of the technology I’m using for this app I have never used before so I’m learning a lot.