Mainthread: A simple Go library for running stuff on the main thread

Hello,

I’ve extracted functionality for running functions on the main operating system thread from an upcoming gamedev library Pixel I’m working on into a separate package:

I expect that this package might be useful for people dealing with windows and graphics in Go. The package is quite nicely designed, so it should be pleasant to use, surely more pleasant than implementing the functionality yourself.

Something from README

Operating systems often require, that code which deals with windows and graphics has to run on the
main thread. This is however somehow challenging in Go due to Go’s concurrent nature.

This package makes it easily possible.

All you need to do is put your main code into a separate function and call mainthread.Run from
your real main, like this:

package main

import (
	"fmt"

	"github.com/faiface/mainthread"
)

func run() {
	// now we can run stuff on the main thread like this
	mainthread.Call(func() {
		fmt.Println("printing from the main thread")
	})
	fmt.Println("printing from another thread")
}

func main() {
	mainthread.Run(run) // enables mainthread package and runs run in a separate goroutine
}

More functions

If you don’t wish to wait until a function finishes running on the main thread, use
mainthread.CallNonBlock:

mainthread.CallNonBlock(func() {
	fmt.Println("i'm in the main thread")
})
fmt.Println("but imma be likely printed first, cuz i don't wait")

If you want to get some value returned from the main thread, you can use mainthread.CallErr or
mainthread.CallVal:

err := mainthread.CallErr(func() error {
	return nil // i don't do nothing wrong
})
val := mainthread.CallVal(func() interface{} {
	return 42 // the meaning of life, universe and everything
})

If mainthread.CallErr or mainthread.CallVall aren’t sufficient for you, you can just assign
variables from within the main thread:

var x, y int
mainthread.Call(func() {
	x, y = 1, 2
})

However, be careful with mainthread.CallNonBlock when dealing with local variables.

1 Like

Why not use

package main

import "runtime"

func init() { runtime.LockOSThread() }

func main(){
	// do stuff
}

Your approach makes it necessary to call everything directly or indirectly from the main function and makes it impossible to call functions “designed to run on the main thread” from other goroutines.

Imagine you’re writing a Go library. You probably don’t want to prevent the users of your library from using goroutines. But then you have to be able to somehow “send” function execution to the main thread from other goroutines.

And that’s exactly what this library does.

For what it’s worth, I am familiar with the need for this kind of support, coming from experience with digital content creation platforms where there is a persistent python environment that can be used to extend the application. One may write tooling or widget plugins to customise workflows.

Autodesk Maya and Foundry Nuke provide apis similar to this library :

https://knowledge.autodesk.com/support/maya/learn-explore/caas/CloudHelp/cloudhelp/2015/ENU/Maya/files/Python-Python-and-threading-htm.html

https://www.thefoundry.co.uk/products/nuke/developers/63/pythondevguide/threading.html

They both happen to be applications built on Qt, and the need to run widget affecting code in the main thread means they needed to abstract away the Qt specifics and just give you a way to put commands into the main event loop.

Thanks for the insight from other areas!

You know, I mainly implemented this package to separate this functionality from the main Pixel library (upcoming) to remove some clutter and possibly save few other people’s time.

Another benefic could be, that if everyone uses a single library for running code on the main thread, than it might be possible to use multiple libraries, that all need to run code on the main thread, simultaneously.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.