golang-ui / nuklear

This project provides Go bindings for nuklear.h — a small ANSI C GUI library.

Home Page:https://github.com/vurtun/nuklear

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Signal arrived during external code execution

jkvatne opened this issue · comments

This issue is similar to #30, but seems not to have with fonts to do. It fails either after some time, or while dragging the window (holding the mouse button down for >10 sec). This is on an Windows 10 PC.

Sometimes it fails only in the Goland debugger. Failures are either inside NkConvert() or inside NkLabel() and are typicaly "Exception 0xc0000005 0x8 0x0 0x0, PC=0x0, signal arrived during external code execution"

To provoke the error I usually have to add heavy network traffic over tcpip, and I have to use pictures. It improves if I move as much as possible of network handling etc outside the nk.NkPlatformNewFrame() and nk.NkEnd(ctx) pair.

The picture routines comes from the example in https://github.com/xlab/libvpx-go/blob/master/cmd/webm-player/view.go as I have not found any other information on how to include drawings/pictures in nuklear.

I have included an example that crashes consitently in my Goland debugger (but not when run as an exe) Debugging this kind of problems is extremely difficult, and if I can not get the Nukelar framework to work reliably, I have to drop it and find something else (which seems to be impossible).

proofofprinciple.zip

To force an error as fast as possible, add the following line to the init() function or start of main():
debug.SetGCPercent(0)
Setting -1 to diable Garbage Collection, no errors will happen. It seems some pointers in addition to the fonts, are used in nk, but not protected from Garbage Collection.

I have been able to find a dirty fix that removes the problem. In etc.go line 223 I modify the Handle() function as follows:

const FontListSize = 3
var FontList [FontListSize]*_Ctype_struct_nk_user_font
var p int

func (f Font) Handle() *UserFont {
	OldFont := &f.handle
	FontList[p] = OldFont;
	p++
	if p>=FontListSize {
		p=0
	}
	return NewUserFontRef(unsafe.Pointer(&f.handle))
}

This will make permanent references to the font handles and keep the GC away. Note that the fontlist must be at least 3 elements long when I use two different fonts in my main program. Changing fonts is done with calls like this:

nk.NkStyleSetFont(ctx, bigFont.Handle())

Tests are done with debug.SetGCPercent(0) to provoke early failures. Note that there are heavy tcp trafic using a goroutine and a thread-safe queue, and continous redrawing of a plotted graph. All of this gives quite much garbage collection, as the tcp routine allocates new buffers at least 100 times pr second.

But I am not able to find the underlying problem. It seems obvious that there are some memory beeing allocated by go, and passed to the C code, without keeping any go references, so it will be garbage collected. But where?

But I am not able to find the underlying problem. It seems obvious that there are some memory beeing allocated by go, and passed to the C code, without keeping any go references, so it will be garbage collected. But where?

I will try to help as soon as I have some time, thank you for this initial research. There is a plenty of generated runtime.Finalizer code that calls C.free on unused objects, I think I will modify c-for-go so it will issue warning about freeing objects, so we could se what's being freed prematurely.

A better solution to this problem has been found, that does not require modification of etc.go. You must just store the font handles in individual variables, so they are not garbage collected. (Test with debug.SetGCPercent(0))

var (
	BigFont     *nk.Font
	SmallFont   *nk.Font
	BigFontHandle     *nk.UserFont
	SmallFontHandle   *nk.UserFont
)

func setup() {
	nk.NkFontStashBegin(&atlas)
	w.BigFont = nk.NkFontAtlasAddFromBytes(atlas, MustAsset("assets/FreeSans.ttf"), 16 nil)
	w.SmallFont = nk.NkFontAtlasAddFromBytes(atlas, MustAsset("assets/FreeSans.ttf"), 12, nil)
	nk.NkFontStashEnd()
	w.BigFontHandle = w.BigFont.Handle()
	w.SmallFontHandle= w.SmallFont.Handle()
}

Later, when drawing the form, you can use NkStylePushFont

	nk.NkStylePushFont(winInfo.Ctx, winInfo.BigFontHandle)
	nk.NkLabel(..)
	nk.NkStylePopFont(winInfo.Ctx)

or simply set the font

		nk.NkStyleSetFont(winInfo.Ctx, winInfo.BigFontHandle)