Repository: go
Issue: x/exp/shiny: driver.Main not returning (and also can be made to panic unexpectedly)
Labels: NeedsInvestigation

Please answer these questions before submitting your issue. Thanks!
See below.

### What version of Go are you using (`go version`)?
go version go1.9 darwin/amd64

### Does this issue reproduce with the latest release?
Yes.

### What operating system and processor architecture are you using (`go env`)?
GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/william"
GORACE=""
GOROOT="/usr/local/go"
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GCCGO="gccgo"
CC="clang"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/60/zldw5qqn401b20vjmzxx1_140000gn/T/go-build661525149=/tmp/go-build -gno-record-gcc-switches -fno-common"
CXX="clang++"
CGO_ENABLED="1"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"

### What did you do?
Below are simplified fully runnable programs that illustrate the issue.  I'm trying to add cleanup code after the function passed to driver.Main returns.  The documentation for driver.Main sates that "It returns when f returns."  However, when returning from the passed-in function (closure), the program exits.  In lieu of cleanup code, I'm using print statements in the example code below.  Although I could achieve what I need using a defer call in the passed-in function (closure), there still seems to be a bug (or at any rate, a documentation bug) worth reporting.

For additional information, see this thread on go-nuts between Dave MacFarlane (who encouraged me to file this report) and me: https://groups.google.com/forum/#!topic/golang-nuts/DmEV4K3xIZY

If possible, provide a recipe for reproducing the error.
A complete runnable program is good.
A link on play.golang.org is best.
Here is the first illustration program (since this is a shiny based program, using the go playground won't work, I don't think): 

package main

import (
	"fmt"

	"golang.org/x/exp/shiny/driver"
	"golang.org/x/exp/shiny/screen"
)

func main() {
	fmt.Println("Starting.")
	driver.Main(func(s screen.Screen) {
		fmt.Println("I'm printed; you can see me.")
		return
	})
	fmt.Println("I'm not printed; alas, I can't be seen.")
}


### What did you expect to see?
All three print statements being called.

### What did you see instead?
Only the first two were called.

After adding code to create a window and running it, it panics.  That is, this code (note there's also one more print statement than the code above):

package main

import (
	"fmt"
	"log"

	"golang.org/x/exp/shiny/driver"
	"golang.org/x/exp/shiny/screen"
)

func main() {
	fmt.Println("Starting.")
	driver.Main(func(s screen.Screen) {
		fmt.Println("I'm printed; you can see me.")
		w, err := s.NewWindow(&screen.NewWindowOptions{Title: "I Don't Return"})
		if err != nil {
			log.Fatal(err)
		}
		defer w.Release()
		fmt.Println("You can see me too.")
		return
	})
	fmt.Println("I'm not printed; alas, I can't be seen.")
}

produces this:

william@hardy% go run main.go
Starting.
I'm printed; you can see me.
You can see me too.
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x10 pc=0x40bab37]

goroutine 1 [running, locked to thread]:
golang.org/x/exp/shiny/driver/gldriver.preparedOpenGL(0x4555610, 0x463cd10, 0x1)
	/Users/william/src/golang.org/x/exp/shiny/driver/gldriver/cocoa.go:91 +0xb7
golang.org/x/exp/shiny/driver/gldriver._cgoexpwrap_78313b9f6607_preparedOpenGL(0x4555610, 0x463cd10, 0x1)
	golang.org/x/exp/shiny/driver/gldriver/_obj/_cgo_gotypes.go:345 +0x3f
golang.org/x/exp/shiny/driver/gldriver._Cfunc_startDriver()
	golang.org/x/exp/shiny/driver/gldriver/_obj/_cgo_gotypes.go:305 +0x41
golang.org/x/exp/shiny/driver/gldriver.main(0x4108530, 0x0, 0xc420053f10)
	/Users/william/src/golang.org/x/exp/shiny/driver/gldriver/cocoa.go:107 +0x51
golang.org/x/exp/shiny/driver/gldriver.Main(0x4108530)
	/Users/william/src/golang.org/x/exp/shiny/driver/gldriver/gldriver.go:26 +0x2f
golang.org/x/exp/shiny/driver.main(0x4108530)
	/Users/william/src/golang.org/x/exp/shiny/driver/driver_darwin.go:15 +0x2b
golang.org/x/exp/shiny/driver.Main(0x4108530)
	/Users/william/src/golang.org/x/exp/shiny/driver/driver.go:24 +0x2b
main.main()
	/Users/william/src/play/shiny/main.go:15 +0x7b
exit status 2
william@hardy% 

I don't see any documentation to think that this should be expected.  It may have something to do with lifecycle events not being called.  I also had a very similar panic when calling Send on the window's Event.Deque from another goroutine (one that was started from within the closure passed to driver.Main).  I figured that that goroutine was calling Send too soon, i.e., before certain lifecycle events were placed on the EventDeque, but I'm only guessing.

The panic doesn't occur if I add code to process the events from the window's EventDeque, but the original issue of driver.Main not returning still persists.  That is, this code:

package main

import (
	"fmt"
	"image"
	"log"

	"golang.org/x/exp/shiny/driver"
	"golang.org/x/exp/shiny/screen"
	"golang.org/x/mobile/event/key"
	"golang.org/x/mobile/event/lifecycle"
	"golang.org/x/mobile/event/paint"
	"golang.org/x/mobile/event/size"
)

func main() {
	fmt.Println("Starting.")
	driver.Main(func(s screen.Screen) {
		fmt.Println("I'm printed; you can see me.")
		w, err := s.NewWindow(&screen.NewWindowOptions{Title: "I Don't Return"})
		if err != nil {
			log.Fatal(err)
		}
		defer w.Release()

		var b screen.Buffer
		defer func() {
			if b != nil {
				b.Release()
			}
		}()

		fmt.Println("You can see me too.")
		for {
			switch e := w.NextEvent().(type) {
			case lifecycle.Event:
				if e.To == lifecycle.StageDead {
					return
				}
			case key.Event:
				if e.Direction == key.DirPress && e.Modifiers == key.ModMeta {
					switch e.Code {
					case key.CodeQ:
						return
					}
				}
			case paint.Event:
				w.Upload(image.Point{}, b, b.Bounds())
				w.Publish()
			case size.Event:
				if b != nil {
					b.Release()
				}
				b, err = s.NewBuffer(e.Size())
				if err != nil {
					log.Fatal(err)
				}
			case error:
				log.Print(e)
			}
		}
	})
	fmt.Println("I'm not printed; alas, I can't be seen.")
}

produces this:

william@hardy% go run main.go
Starting.
I'm printed; you can see me.
You can see me too.

and I just get my prompt back after either choosing the Quit menu option on the top menu bar or hitting Command-Q.  That is, the last print statement never gets called.

I understand that shiny is experimental and also that development has somewhat paused recently.  However, I still think it's worthwhile submitting this report.

Please let me know if I can be of any help.

Thanks,
William