nswrap/examples/memory/main.go
2019-06-11 12:38:22 -04:00

199 lines
4.9 KiB
Go

package main
import "C"
import (
"fmt"
"runtime"
"time"
"git.wow.st/gmp/nswrap/examples/memory/ns"
)
func dealloc() {
//[super dealloc] is called for you automatically, so no Supermethods
//struct is provided here.
fmt.Println("--dealloc called")
}
func release(super ns.MyClassSupermethods) {
fmt.Println("--release called")
super.Release() // comment out for leak
}
//Basic memory allocation test using a manual Autorelease pool. Also utilizes
//a custom object that overrides dealloc and release methods. Make sure you
//call [super release] or you will have a leak (see above). [super dealloc] is
//called for youautomatically as it is basically always required.
func memtest1() {
//because time.Sleep() is called within each loop, it is necessary
//to lock this goroutine to a thread. Otherwise, Sleep can return
//and continue execution on a different thread, causing the risk that
//the next call to NSAutoreleasePool.Drain() seg faults because it is
//not in the same thread where the NSAutoreleasePool was allocated.
runtime.LockOSThread()
fmt.Println("memtest1: started")
for {
pool := ns.NSAutoreleasePoolAlloc().Init()
o1 := ns.MyClassAlloc()
//If autorelease: true is set in nswrap.yaml, the manual calls to
//autorelease are not necessary.
o1.Autorelease()
o1.DeallocCallback(dealloc)
o1.ReleaseCallback(release)
o2 := ns.NSObjectAlloc()
o2.Autorelease()
o3 := ns.NSMutableArrayAlloc()
o3.Autorelease()
o4 := ns.NSStringAlloc()
o4.Autorelease()
_ = o1
_ = o2
_ = o3
_ = o4
pool.Drain()
time.Sleep(time.Second / 2)
}
fmt.Println("memtest1: done")
}
//Test the ns.Autoreleasepool() function. Also confirm that we do not need
//to release or manually autorelease objects returned by constructor methods
//(i.e. not created with *Alloc()).
func memtest2() {
runtime.LockOSThread()
fmt.Println("memtest2: started")
i := 0
for {
ns.Autoreleasepool(func() {
o1 := ns.NSObjectAlloc()
o1.Autorelease()
//Does not leak strings within an autorelease pool
s1 := ns.NSStringWithGoString(fmt.Sprintf("string-%d", i))
_ = s1
//o1.Retain() // uncomment for leak
})
time.Sleep(time.Second / 3)
}
fmt.Println("memtest2: done")
}
//Test nested Autoreleasepool invocations -- confirms that objects in the
//outer pool are not deallocated by the inner pool.
func memtest3() {
runtime.LockOSThread() // comment out for crash
fmt.Println("memtest3: started")
i := 0
for {
ns.Autoreleasepool(func() {
arr := ns.NSMutableArrayAlloc().Init()
arr.Autorelease()
arr.AddObject(ns.NSStringWithGoString(fmt.Sprintf("my string %d",i)))
s1 := ns.NSStringWithGoString(fmt.Sprintf("my other string %d",i))
_ = s1
i++
for {
ns.Autoreleasepool(func() {
str := arr.ObjectAtIndex(0).NSString()
fmt.Println(str) // does not leak in an autorelease pool
time.Sleep(time.Second / 2)
})
}
time.Sleep(time.Second)
})
}
fmt.Println("memtest3: done")
}
//Test of manual memory management. Lets run multiple goroutines here to
//confirm we can use multiple threads if autorelease pools are not in play.
func memtest4() {
go memtest4a()
go memtest4a()
go memtest4a()
go memtest4b()
go memtest4b()
go memtest4b()
go memtest4c()
go memtest4c()
go memtest4c()
// Exactly one goroutine (locked to an OS thread) can use an
// autorelease pool (?)
go memtest1()
go memtest2()
}
func memtest4a() {
for {
o1 := ns.NSObjectAlloc()
o1.Init()
time.Sleep(time.Second / 50)
o1.Release()
}
}
func memtest4b() {
i := 0
for {
o1 := ns.NSObjectAlloc() // need to Release
//s1 := ns.NSStringWithGoString(fmt.Sprintf("a string %d",i)) // uncomment for leak
s1 := ns.NSStringWithGoString("a string")
i++
arr := ns.NSArrayAlloc().InitWithObjects(s1) // need to Release arr
s2 := arr.ObjectAtIndex(0).NSString()
//If you try to convert an NSString to UTF8String, CString (*Char),
//or GoString, Objective-C runtime will leak an
//NSTaggedPointerCStringContainer. Don't know why or how to fix it.
//There would be no leak if we were using an autorelease pool.
//u := str.UTF8String() // uncomment for leak
//fmt.Println(s1) // uncomment for leak
time.Sleep(time.Second / 50)
o1.Release()
arr.Release()
//s1.Release() // does not work
_ = o1
_ = s2
}
}
func memtest4c() {
for {
o1 := ns.NSArrayAlloc()
o2 := ns.NSStringAlloc()
time.Sleep(time.Second / 50)
o1.Release()
o2.Release()
}
}
func fin(o *ns.MyClass) {
o.Release()
}
func releaseX(x int) func (ns.MyClassSupermethods) {
return func(super ns.MyClassSupermethods) {
fmt.Printf("--release %d\n", x)
super.Release() // comment out for leak
}
}
func main() {
//Uncomment more than one test at a time for a crash.
//Note: You may not run autorelease pools from multiple goroutines.
//Within an autorelease pool, do not do anything that can result in a
//switch to a different thread.
//go memtest1()
//go memtest2()
//go memtest3()
go memtest4()
select {}
}