I have a program with multiple multiple loops, each one ran in a Goroutine. I need to plug or unplug monitors while my program runs, so I have to restart the sdl to let it find my new monitors, I do it by sdl.quit() to quit the last sdl and sdl.init(sdl.InitEverything) to initialize it again.
My problem is that I have to handle the sdl events in a loop, If I don't it will become unresponsive, but this loop will block my Main code. I don't need to handle any events like mouse clicks, I just want to show some simple picture and manipulate them, is there any way to stop events or run the whole sdl in a goroutine? I tried but get weird results. This is my struct:
type SDLstruct{
window *sdl.Window
renderer *sdl.Renderer
texture *sdl.Texture
src, dst sdl.Rect
event sdl.Event
Close bool
winWidth, winHeight int32
}
This function starts a window and a renderer:
func (sdlVars *SDLstruct)StartWindow() (err error) {
sdlVars.winWidth, sdlVars.winHeight = 1920,1080
sdl.Init(sdl.INIT_VIDEO)
Num,err :=sdl.GetNumVideoDisplays()
if err!=nil {
return err
}
var rect sdl.Rect
rect,err = sdl.GetDisplayBounds(0)
if err!=nil {
return err
}
for i:=Num-1;i>=0;i--{
Mod , _:=sdl.GetDisplayMode(i,0)
if Mod.W ==info.winWidth && Mod.H==info.winHeight{
rect,err = sdl.GetDisplayBounds(i)
if err!=nil {
return err
}
break
}
}
sdlVars.window, err = sdl.CreateWindow(winTitle, rect.X, rect.Y,
rect.W, rect.H, sdl.WINDOW_SHOWN)
sdlVars.window.SetBordered(false)
sdlVars.window.SetFullscreen(1)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to create window: %s\n", err)
return err
}
sdlVars.renderer, err = sdl.CreateRenderer(sdlVars.window, -1, sdl.RENDERER_ACCELERATED)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to create renderer: %s\n", err)
return err
}
sdlVars.renderer.Clear()
sdlVars.renderer.SetDrawColor(0, 0, 0, 255)
sdlVars.renderer.FillRect(&sdl.Rect{0, 0, int32(info.winWidth), int32(info.winHeight)})
sdlVars.renderer.Present()
sdl.ShowCursor(0)
return nil
}
This function handle events:
func (sdlVars *SDLstruct)HandleEvents() {
for sdlVars.event = sdl.PollEvent(); sdlVars.event != nil; sdlVars.event = sdl.PollEvent() {
switch t := sdlVars.event.(type) {
case *sdl.QuitEvent:
sdlVars.Close = true
}
}
}
and this one closes everything:
func (sdlVars *SDLstruct)CloseSDL() (err error) {
err =sdlVars.renderer.Destroy()
if err!=nil{
return err
}
err = sdlVars.window.Destroy()
if err!=nil{
return err
}
sdl.Quit()
return nil
}
This is my Main function:
func main() {
var wg sync.WaitGroup
SdlVars:= new(SDLstruct)
wg.Add(1)
go SdlVars.StartSDL()
time.Sleep(time.Second*5)
SdlVars.Close = true
time.Sleep(time.Second*15)
}
In my Main function I tell it to start the sdl and after 5 seconds close everything and wait 15 seconds, but it doesn't close window.
Be careful as using SDL from multiple threads is not entirely trivial. Typically, your SDL loop must sit in the main thread. There is a good reason for that and it's because the event loop is part of the main thread and many of your objects are created and mutated on the main thread...or at least they should be.
When you introduce multiple threads weird undefined and dangerous behavior could happen. This is why a good rule of thumb is to use runtime.LockOSThread to ensure the Go runtime doesn't pin your main goroutine to other threads and keeps it on the main thread.
See this for more details: enter link description here
Related
I am trying to effectively use bindings auto-generated by go-ethereum, which I stored in the uniswap_core package.
It created many different Event structs and each also has it's own Iterator struct.
Here is an example of code the tool abigen generated for an Iterator struct:
// IUniswapV3FactoryPoolCreatedIterator is returned from FilterPoolCreated and is used to iterate over the raw logs and unpacked data for PoolCreated events raised by the IUniswapV3Factory contract.
type IUniswapV3FactoryPoolCreatedIterator struct {
Event *IUniswapV3FactoryPoolCreated // Event containing the contract specifics and raw log
contract *bind.BoundContract // Generic contract to use for unpacking event data
event string // Event name to use for unpacking event data
logs chan types.Log // Log channel receiving the found contract events
sub ethereum.Subscription // Subscription for errors, completion and termination
done bool // Whether the subscription completed delivering logs
fail error // Occurred error to stop iteration
}
// Next advances the iterator to the subsequent event, returning whether there
// are any more events found. In case of a retrieval or parsing error, false is
// returned and Error() can be queried for the exact failure.
func (it *IUniswapV3FactoryPoolCreatedIterator) Next() bool {
// If the iterator failed, stop iterating
if it.fail != nil {
return false
}
// If the iterator completed, deliver directly whatever's available
if it.done {
select {
case log := <-it.logs:
it.Event = new(IUniswapV3FactoryPoolCreated)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
default:
return false
}
}
// Iterator still in progress, wait for either a data or an error event
select {
case log := <-it.logs:
it.Event = new(IUniswapV3FactoryPoolCreated)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
case err := <-it.sub.Err():
it.done = true
it.fail = err
return it.Next()
}
}
// Error returns any retrieval or parsing error occurred during filtering.
func (it *IUniswapV3FactoryPoolCreatedIterator) Error() error {
return it.fail
}
// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *IUniswapV3FactoryPoolCreatedIterator) Close() error {
it.sub.Unsubscribe()
return nil
}
I want to create a general function that, given any EventIterator and an interface{} channel, will iterate though the events and send them back via the channel.
A solution I've come up with is simply creating a type switch statement to get the appropriate Iterator type. But then I have to repeat the exact same iteration code within each case block.
How do I avoid this repetition?
func (f *contract_factory) readEvent(it interface{}, c_event chan interface{}) {
//TODO figure how to avoid duplicating this shit
switch iter := it.(type){
case *uniswap_core.UniswapV3FactoryPoolCreatedIterator:
for {
if ! iter.Next() {
close(c_event)
if err := iter.Error(); err != nil {
f.logger.Error("failure while iterating events", zap.Error(err))
}
break
}
c_event <- iter.Event
}
iter.Close()
case *uniswap_core.UniswapV3FactoryOwnerChangedIterator:
for {
if ! iter.Next() {
close(c_event)
if err := iter.Error(); err != nil {
f.logger.Error("failure while iterating events", zap.Error(err))
}
break
}
c_event <- iter.Event
}
iter.Close()
default:
err = errors.New("unknown event iterator type")
f.logger.Error("unknown event iterator type", zap.String("iterator type", reflect.TypeOf(iter).String()))
close(c_event)
}
}
I have the following code in Go using the semaphore library just as an example:
package main
import (
"fmt"
"context"
"time"
"golang.org/x/sync/semaphore"
)
// This protects the lockedVar variable
var lock *semaphore.Weighted
// Only one go routine should be able to access this at once
var lockedVar string
func acquireLock() {
err := lock.Acquire(context.TODO(), 1)
if err != nil {
panic(err)
}
}
func releaseLock() {
lock.Release(1)
}
func useLockedVar() {
acquireLock()
fmt.Printf("lockedVar used: %s\n", lockedVar)
releaseLock()
}
func causeDeadLock() {
acquireLock()
// calling this from a function that's already
// locked the lockedVar should cause a deadlock.
useLockedVar()
releaseLock()
}
func main() {
lock = semaphore.NewWeighted(1)
lockedVar = "this is the locked var"
// this is only on a separate goroutine so that the standard
// go "deadlock" message doesn't print out.
go causeDeadLock()
// Keep the primary goroutine active.
for true {
time.Sleep(time.Second)
}
}
Is there a way to get the acquireLock() function call to print a message after a timeout indicating that there is a potential deadlock but without unblocking the call? I would want the deadlock to persist, but a log message to be written in the event that a timeout is reached. So a TryAcquire isn't exactly what I want.
An example of what I want in psuedo code:
afterFiveSeconds := func() {
fmt.Printf("there is a potential deadlock\n")
}
lock.Acquire(context.TODO(), 1, afterFiveSeconds)
The lock.Acquire call in this example would call the afterFiveSeconds callback if the Acquire call blocked for more than 5 seconds, but it would not unblock the caller. It would continue to block.
I think I've found a solution to my problem.
func acquireLock() {
timeoutChan := make(chan bool)
go func() {
select {
case <-time.After(time.Second * time.Duration(5)):
fmt.Printf("potential deadlock while acquiring semaphore\n")
case <-timeoutChan:
break
}
}()
err := lock.Acquire(context.TODO(), 1)
close(timeoutChan)
if err != nil {
panic(err)
}
}
I am trying to write a go app that will monitor the status of a server application I run in windows. The application will run for roughly 16 hours before throwing out the following error: (Small snippet)
fatal error: too many callback functions
goroutine 137 [running]:
runtime.throw(0xc4c0a1, 0x1b)
H:/Program Files/Go/src/runtime/panic.go:1117 +0x79 fp=0xc000639d30 sp=0xc000639d00 pc=0x899379
syscall.compileCallback(0xbd18a0, 0xc00041fce0, 0x1, 0x0)
H:/Program Files/Go/src/runtime/syscall_windows.go:201 +0x5e5 fp=0xc000639e28 sp=0xc000639d30 pc=0x8c90e5
syscall.NewCallback(...)
H:/Program Files/Go/src/syscall/syscall_windows.go:177
main.FindWindow(0xc47278, 0x13, 0xc000639f50, 0x2, 0x2)
I have two files. One is the file that is calling a bunch of Windows API stuff, and one is a goroutine that is being performed every 30 seconds to get an update.
I am fairly new to go, especially in windows related development, so I am struggling to find the issue and how to prevent it.
Here is the main file (test example).
func main() {
go updateServerStats()
select {}
}
func ServerStats() {
serverStatsTicker := time.NewTicker(30 * time.Second)
for range serverStatsTicker.C {
serverRunning, serverHung, err := ServerHangCheck()
if err != nil {
ErrorLogger.Println("Server Check Error: ", err)
}
if serverHung {
fmt.Println("Server is hung")
}
}
}
Here is the primary callback/windows file. Its very much still a very rough, very work in progress. Something I found and modified from go playground.
package main
import (
"fmt"
"syscall"
"unsafe"
)
var (
user32 = syscall.MustLoadDLL("user32.dll")
procEnumWindows = user32.MustFindProc("EnumWindows")
procGetWindowTextW = user32.MustFindProc("GetWindowTextW")
procIsHungAppWindow = user32.MustFindProc("IsHungAppWindow")
)
//EnumWindows iterates over each window to be used for callbacks
func EnumWindows(enumFunc uintptr, lparam uintptr) (err error) {
r1, _, e1 := syscall.Syscall(procEnumWindows.Addr(), 2, uintptr(enumFunc), uintptr(lparam), 0)
if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
//GetWindowText gets the description of the Window, which is the "Text" of the window.
func GetWindowText(hwnd syscall.Handle, str *uint16, maxCount int32) (len int32, err error) {
r0, _, e1 := syscall.Syscall(procGetWindowTextW.Addr(), 3, uintptr(hwnd), uintptr(unsafe.Pointer(str)), uintptr(maxCount))
len = int32(r0)
if len == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
//IsHungAppWindow uses the IsHungAppWindow to see if Windows has been getting responses.
func IsHungAppWindow(hwnd syscall.Handle) (ishung bool, err error) {
r2, _, err := syscall.Syscall(procIsHungAppWindow.Addr(), 2, uintptr(hwnd), 0, 0)
if r2 == 1{
return true, err
}
return false, err
}
//FindWindow uses EnumWindows with a callback to GetWindowText, and if matches given Title, checks if its hung, and returns state.
func FindWindow(title string) (bool ,bool, error) {
var hwnd syscall.Handle
var isHung bool = false
cb := syscall.NewCallback(func(h syscall.Handle, p uintptr) uintptr {
b := make([]uint16, 200)
_, err := GetWindowText(h, &b[0], int32(len(b)))
if err != nil {
// ignore the error
return 1 // continue enumeration
}
if syscall.UTF16ToString(b) == title {
// note the window
isHung, _ = IsHungAppWindow(h)
hwnd = h
return 0 // stop enumeration
}
return 1 // continue enumeration
})
EnumWindows(cb, 0)
if hwnd == 0 {
return false, false, fmt.Errorf("DCS Not Found")
}
if isHung == true {
return true, isHung, fmt.Errorf("DCS Is Running But Hung")
}
return true, isHung, nil
}
//ServerHangCheck checks the server to see if the window is hung or process is running.
func ServerHangCheck() (bool, bool, error) {
const title = "server_application"
running, hung, err := FindWindow(title)
return running, hung, err
}
Looking at syscall.NewCallback, we find that it's actually implemented via runtime/syscall_windows.go as the function compileCallback:
func NewCallback(fn interface{}) uintptr {
return compileCallback(fn, true)
}
Looking at the runtime/syscall_windows.go code we find that it has a fixed size table of all registered Go callbacks. This code varies a lot between Go releases so it's not too productive to delve any further here. However, there's one thing that is clear: the code checks to see if the callback function is already registered, and if so, re-uses it. So a single callback function occupies one table slot, but adding multiple functions will eventually use up all the table slots and result in the fatal error that you encountered.
You asked in a comment (the comments popped up while I was writing this):
Would I need to reuse it, or can I "close" the original? – Mallachar 7 mins ago
You cannot close out the original. There's an actual table of functions elsewhere in memory; a registered callback uses up a slot in this table, and slots are never released.
Per the advice of torek I got a work around.
I created a global variable
var callbacker uintptr
Then then I created a function which is called by init
func init() {
callbacker = syscall.NewCallback(CallBackCreator)
}
func CallBackCreator(h syscall.Handle, p uintptr) uintptr {
hwnd = 0
b := make([]uint16, 200)
_, err := GetWindowText(h, &b[0], int32(len(b)))
if err != nil {
// ignore the error
return 1 // continue enumeration
}
if syscall.UTF16ToString(b) == title {
// note the window
isHung, _ = IsHungAppWindow(h)
hwnd = h
return 0 // stop enumeration
}
return 1 // continue enumeration
}
which is now called by the function
EnumWindows(callbacker, 0)
Likely not the most "Go" appropriate way, but I am now making better progress.
Lets say I have the following function I'd like to write a test for:
func GetBootTime() (time.Time, error) {
currentTime := time.Now()
var info unix.Sysinfo_t
if err := unix.Sysinfo(&info); err != nil {
return time.Time{}, fmt.Errorf("error getting system uptime: %s", err)
}
return currentTime.Add(-time.Duration(info.Uptime) * time.Second).Truncate(time.Second), nil
}
How can I get unix.Sysinfo to return an error?
You cannot.
But you can "abstract it away" in one way or another.
For instance, you can have
var sysInfo := unix.SysInfo
func GetBootTime() (time.Time, error) {
currentTime := time.Now()
var info unix.Sysinfo_t
if err := sysInfo(&info); err != nil {
return time.Time{}, fmt.Errorf("error getting system uptime: %s", err)
}
return currentTime.Add(-time.Duration(info.Uptime) * time.Second).Truncate(time.Second), nil
}
…and then in your testing code have something like
sysInfo = func (_ *unix.Sysinfo_t) error {
return syscall.Errno(42)
}
before the actual test runs.
Note that this patching must be synchronized with other goroutines which may run testing code which ultimately calls into this function.
Note that there exist more hard-core appoaches which abstract away whole subsystems — such as github.com/spf13/afero which abstracts away all the filesystem operations available via the os and path/filepath packages and github.com/LopatkinEvgeniy/clock which abstracts away most functions from the time package.
With such an approach, you write all your code in a way so that it uses a single object implementing a particular interface, to carry out certain class of tasks, and at runtime, this object is either a "real" one or a "fake" one — when you do testing.
For example:
package package
// Dear user, CleanUp must only be used with defer: defer CleanUp()
func CleanUp() {
// some logic to check if call was deferred
// do tear down
}
And in userland code:
func main() {
package.CleanUp() // PANIC, CleanUp must be deferred!
}
But all should be fine if user runs:
func main() {
defer package.CleanUp() // good job, no panic
}
Things I already tried:
func DeferCleanUp() {
defer func() { /* do tear down */ }()
// But then I realized this was exactly the opposite of what I needed
// user doesn't need to call defer CleanUp anymore but...
}
// now if the APi is misused it can cause problems too:
defer DeferCleanUp() // a defer inception xD, question remains.
Alright, per OPs request and just for laughs, I'm posting this hacky approach to solving this by looking at the call stack and applying some heuristics.
DISCLAIMER: Do not use this in real code. I don't think checking deferred is even a good thing.
Also Note: this approach will only work if the executable and the source are on the same machine.
Link to gist: https://gist.github.com/dvirsky/dfdfd4066c70e8391dc5 (this doesn't work in the playground because you can't read the source file there)
package main
import(
"fmt"
"runtime"
"io/ioutil"
"bytes"
"strings"
)
func isDeferred() bool {
// Let's get the caller's name first
var caller string
if fn, _, _, ok := runtime.Caller(1); ok {
caller = function(fn)
} else {
panic("No caller")
}
// Let's peek 2 levels above this - the first level is this function,
// The second is CleanUp()
// The one we want is who called CleanUp()
if _, file, line, ok := runtime.Caller(2); ok {
// now we actually need to read the source file
// This should be cached of course to avoid terrible performance
// I copied this from runtime/debug, so it's a legitimate thing to do :)
data, err := ioutil.ReadFile(file)
if err != nil {
panic("Could not read file")
}
// now let's read the exact line of the caller
lines := bytes.Split(data, []byte{'\n'})
lineText := strings.TrimSpace(string(lines[line-1]))
fmt.Printf("Line text: '%s'\n", lineText)
// Now let's apply some ugly rules of thumb. This is the fragile part
// It can be improved with regex or actual AST parsing, but dude...
return lineText == "}" || // on simple defer this is what we get
!strings.Contains(lineText, caller) || // this handles the case of defer func() { CleanUp() }()
strings.Contains(lineText, "defer ")
} // not ok - means we were not clled from at least 3 levels deep
return false
}
func CleanUp() {
if !isDeferred() {
panic("Not Deferred!")
}
}
// This should not panic
func fine() {
defer CleanUp()
fmt.Println("Fine!")
}
// this should not panic as well
func alsoFine() {
defer func() { CleanUp() }()
fmt.Println("Also Fine!")
}
// this should panic
func notFine() {
CleanUp()
fmt.Println("Not Fine!")
}
// Taken from the std lib's runtime/debug:
// function returns, if possible, the name of the function containing the PC.
func function(pc uintptr) string {
fn := runtime.FuncForPC(pc)
if fn == nil {
return ""
}
name := fn.Name()
if lastslash := strings.LastIndex(name, "/"); lastslash >= 0 {
name = name[lastslash+1:]
}
if period := strings.Index(name, "."); period >= 0 {
name = name[period+1:]
}
name = strings.Replace(name, "·", ".", -1)
return name
}
func main(){
fine()
alsoFine()
notFine()
}