Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strange behaviour of select (does not allow other goroutines to run)

Tags:

go

I'm trying to write SDL app in go using https://github.com/klkblake/Go-SDL.
I created timer to call draw function on it:

render_timer := time.NewTicker(time.Second / 60)

Somewhere in event loop:

for running == true {
    [...]
    [process sdl events]
    [...]
    select {
    case <-render_timer.C:
        call_my_draw_function()
    default:
        some_default_actions()
    }
    [...]
}

If I run program after compiling this code nothing is drawn on screen. But if I place just:

fmt.Println("default")

in the default branch of select -- the code begin to work as I want it to(draws something in window); and if i remove println it again don't draw anything.
What am I doing wrong? Why is there such behaviour of select ?


Hm... Simplest testcase is:

package main

import (
"fmt"
"time"
)

func main() {

    rt := time.NewTicker(time.Second / 60)
    for {
        select {
        case <-rt.C:
            fmt.Println("time")
        default:
        }
    time.Sleep(1) // without this line 'case <-rt.C' is never executed
    }
}
like image 580
Bad_ptr Avatar asked Dec 09 '22 20:12

Bad_ptr


2 Answers

As for your example, your loop is a busy loop, constantly hitting the default: case. The scheduler in go is co-operative, and since you're in a busy loop, the go routine running the Ticker will never be scheduled to run, and thus never send anything on the channel. This will be the case even if your default: case is not empty, but do pure computations - that never make any calls that invoke the schudler.

However, when you do something else that in some form invokes the go scheduler, e.g. doing I/O , the scheduler will give the Ticker a chance to run.

You could import the runtime package and do

default:
    runtime.Gosched()

to make the scheduler run, which will not starve the Ticker go routine.

I'm unsure how this leads to the problems you get when running SDL, as that would most likely involve I/O or something else that triggers the scheduler

like image 77
nos Avatar answered May 29 '23 09:05

nos


See golang: goroute with select doesn't stop unless I added a fmt.Print()

Long story short, because of the default case, and because your GOMAXPROCS is probably set to 1 (the default value), it never schedules the goroutine to do its work. There are many options to fix this, depending on your needs (a sleep in default, a time.After() channel in the select instead of the default, a call to runtime.Gosched(), etc.).

Adding the fmt.Print makes it work because - my guess, not sure - it involves io and internally causes a Gosched() (that or something like Phlip's answer in the related question, I just read it).

like image 41
mna Avatar answered May 29 '23 09:05

mna