nswrap/examples/memory/main.go

205 lines
5.1 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
i++
})
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))
fmt.Printf("%s\n",arr.ObjectAtIndex(0).NSString())
_ = s1
for x := 0; x < 3; x++ {
ns.Autoreleasepool(func() {
str := arr.ObjectAtIndex(0).NSString()
fmt.Printf("%d->%s\n",x,str) // does not leak in an autorelease pool
time.Sleep(time.Second / 5)
})
}
time.Sleep(time.Second/2)
i++
})
}
fmt.Println("memtest3: done")
}
//Test of manual memory management.
func memtest4() {
go memtest4a()
go memtest4a()
go memtest4a()
go memtest4b()
go memtest4b()
go memtest4b()
go memtest4c()
go memtest4c()
go memtest4c()
// running multiple separate threads with autorelease pools...
go memtest1()
go memtest2()
go memtest3()
go memtest1()
go memtest2()
go memtest3()
go memtest1()
go memtest2()
go memtest3()
}
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
// These object constructors will always leak. In the case of an
// immutable string, it is not an issue unless a large number of different
// strings are going to be made.
//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 creates an autoreleased
// NSTaggedPointerCStringContainer internally and there is no way to
// release it unless you called your method from within an autorelease
// pool. The following two calls cause a leak.
//u := s1.UTF8String() // uncomment for leak
//fmt.Println(s1) // uncomment for leak
time.Sleep(time.Second / 50)
o1.Release()
arr.Release()
//s1.Release() // does not prevent the leak caused by
// NSStringWithGoString: s1 is autoreleased by the Objective-C
// runtime, these methods will always leak without autorelease
// pools.
_ = o1
_ = s2
}
}
func memtest4c() {
for {
o1 := ns.NSArrayAlloc()
o2 := ns.NSStringAlloc()
time.Sleep(time.Second / 50)
o1.Release()
o2.Release()
}
}
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 {}
}