swift/ref: Implement invite scan & neighborhood ad/scan

Not yet tested but API implemented, including changes to the API
in ref to use handle in a uintptr map instead of a separate scanId in
a global ref map -- it was an extra layer of indirection.

Also fixes potential leaks of handlers in Swift should an exception
occur during startScan calls, uses alias instead of email, and makes
sure unit tests don't use DISPATCH_TIME_FOREVER to prevent any
deadlocks from preventing unit tests from finishing.

JNI patches come from @razvanm via CL 23685.

MultiPart: 1/2
Change-Id: I10fd3799f0f1a2c64191021b8f8a399fbdfa071a
diff --git a/services/syncbase/bridge/cgo/impl.go b/services/syncbase/bridge/cgo/impl.go
index a0788e8..ce14e19 100644
--- a/services/syncbase/bridge/cgo/impl.go
+++ b/services/syncbase/bridge/cgo/impl.go
@@ -46,7 +46,7 @@
 	"v.io/v23/vom"
 	_ "v.io/x/ref/runtime/factories/roaming"
 	"v.io/x/ref/services/syncbase/bridge"
-	"v.io/x/ref/services/syncbase/bridge/cgo/refmap"
+	"v.io/x/ref/services/syncbase/bridge/cgo/ptrmap"
 	"v.io/x/ref/services/syncbase/discovery"
 	"v.io/x/ref/services/syncbase/syncbaselib"
 	"v.io/x/ref/test/testutil"
@@ -96,7 +96,7 @@
 	neighborhoodAdStatus *adStatus
 )
 
-var globalRefMap = refmap.NewRefMap()
+var globalPtrMap = ptrmap.New()
 
 // TODO(razvanm): Replace the function arguments with an options struct.
 //export v23_syncbase_Init
@@ -342,7 +342,7 @@
 }
 
 //export v23_syncbase_NeighborhoodNewScan
-func v23_syncbase_NeighborhoodNewScan(cbs C.v23_syncbase_NeighborhoodScanCallbacks, cUint64 *C.uint64_t, cErr *C.v23_syncbase_VError) {
+func v23_syncbase_NeighborhoodNewScan(cbs C.v23_syncbase_NeighborhoodScanCallbacks, cErr *C.v23_syncbase_VError) {
 	scanCtx, cancel := context.WithCancel(b.Ctx)
 	scanChan := make(chan discovery.AppPeer)
 	err := discovery.ListenForAppPeers(scanCtx, scanChan)
@@ -365,13 +365,15 @@
 	}()
 
 	// Remember the scan's cancellation information and return the scan's id.
-	x := C.uint64_t(globalRefMap.Add(cancel))
-	cUint64 = &x
+	if err := globalPtrMap.Set(uintptr(cbs.handle), cancel); err != nil {
+		cancel()
+		cErr.init(err)
+	}
 }
 
 //export v23_syncbase_NeighborhoodStopScan
-func v23_syncbase_NeighborhoodStopScan(cUint64 C.uint64_t) {
-	if cancel, ok := globalRefMap.Remove(uint64(cUint64)).(context.CancelFunc); ok && cancel != nil {
+func v23_syncbase_NeighborhoodStopScan(cHandle C.v23_syncbase_Handle) {
+	if cancel, ok := globalPtrMap.Remove(uintptr(cHandle)).(context.CancelFunc); ok && cancel != nil {
 		cancel()
 	}
 }
@@ -743,7 +745,7 @@
 // Syncgroup Invites
 
 //export v23_syncbase_DbSyncgroupInvitesNewScan
-func v23_syncbase_DbSyncgroupInvitesNewScan(cName C.v23_syncbase_String, cbs C.v23_syncbase_DbSyncgroupInvitesCallbacks, cUint64 *C.uint64_t, cErr *C.v23_syncbase_VError) {
+func v23_syncbase_DbSyncgroupInvitesNewScan(cName C.v23_syncbase_String, cbs C.v23_syncbase_DbSyncgroupInvitesCallbacks, cErr *C.v23_syncbase_VError) {
 	encodedId := cName.extract()
 	dbId, err := util.DecodeId(encodedId)
 	if err != nil {
@@ -772,14 +774,16 @@
 		}
 	}()
 
-	// Remember the scan's cancellation information and return the scan's id.
-	x := C.uint64_t(globalRefMap.Add(cancel))
-	cUint64 = &x
+	// Remember the scan's cancellation information.
+	if err := globalPtrMap.Set(uintptr(cbs.handle), cancel); err != nil {
+		cancel()
+		cErr.init(err)
+	}
 }
 
 //export v23_syncbase_DbSyncgroupInvitesStopScan
-func v23_syncbase_DbSyncgroupInvitesStopScan(cUint64 C.uint64_t) {
-	if cancel, ok := globalRefMap.Remove(uint64(cUint64)).(context.CancelFunc); ok && cancel != nil {
+func v23_syncbase_DbSyncgroupInvitesStopScan(cHandle C.v23_syncbase_Handle) {
+	if cancel, ok := globalPtrMap.Remove(uintptr(cHandle)).(context.CancelFunc); ok && cancel != nil {
 		cancel()
 	}
 }
diff --git a/services/syncbase/bridge/cgo/jni.go b/services/syncbase/bridge/cgo/jni.go
index 2aed5e7..66a1e69 100644
--- a/services/syncbase/bridge/cgo/jni.go
+++ b/services/syncbase/bridge/cgo/jni.go
@@ -407,7 +407,7 @@
 //export v23_syncbase_internal_onChange
 func v23_syncbase_internal_onChange(handle C.v23_syncbase_Handle, change C.v23_syncbase_WatchChange) {
 	id := uint64(uintptr(handle))
-	h := globalRefMap.Get(id).(*watchPatternsCallbacksHandle)
+	h := globalPtrMap.Get(uintptr(id)).(*watchPatternsCallbacksHandle)
 	env, free := getEnv()
 	obj := change.extractToJava(env)
 	arg := *(*C.jvalue)(unsafe.Pointer(&obj))
@@ -422,7 +422,7 @@
 //export v23_syncbase_internal_onError
 func v23_syncbase_internal_onError(handle C.v23_syncbase_Handle, error C.v23_syncbase_VError) {
 	id := uint64(uintptr(handle))
-	h := globalRefMap.Remove(id).(*watchPatternsCallbacksHandle)
+	h := globalPtrMap.Remove(uintptr(id)).(*watchPatternsCallbacksHandle)
 	env, free := getEnv()
 	obj := error.extractToJava(env)
 	arg := *(*C.jvalue)(unsafe.Pointer(&obj))
@@ -445,11 +445,16 @@
 	cName := newVStringFromJava(env, name)
 	cResumeMarker := newVBytesFromJava(env, resumeMaker)
 	cPatterns := newVCollectionRowPatternsFromJava(env, patterns)
+	ptr := uintptr(unsafe.Pointer(C.NewGlobalRef(env, callbacks)))
 	cbs := C.newVWatchPatternsCallbacks()
-	cbs.handle = C.v23_syncbase_Handle(uintptr(globalRefMap.Add(&watchPatternsCallbacksHandle{
-		obj:       uintptr(unsafe.Pointer(C.NewGlobalRef(env, callbacks))),
+	callbacksHandle := &watchPatternsCallbacksHandle{
+		obj:       ptr,
 		callbacks: newJWatchPatternsCallbacks(env, callbacks),
-	})))
+	}
+	if err := globalPtrMap.Set(ptr, callbacksHandle); err != nil {
+		panic(err)
+	}
+	cbs.handle = C.v23_syncbase_Handle(ptr)
 	var cErr C.v23_syncbase_VError
 	v23_syncbase_DbWatchPatterns(cName, cResumeMarker, cPatterns, cbs, &cErr)
 	maybeThrowException(env, &cErr)
@@ -458,7 +463,7 @@
 //export v23_syncbase_internal_onInvite
 func v23_syncbase_internal_onInvite(handle C.v23_syncbase_Handle, invite C.v23_syncbase_Invite) {
 	id := uint64(uintptr(handle))
-	h := globalRefMap.Get(id).(*inviteCallbacksHandle)
+	h := globalPtrMap.Get(uintptr(id)).(*inviteCallbacksHandle)
 	env, free := getEnv()
 	obj := invite.extractToJava(env)
 	arg := *(*C.jvalue)(unsafe.Pointer(&obj))
@@ -479,20 +484,25 @@
 func Java_io_v_syncbase_internal_Database_SyncgroupInvitesNewScan(env *C.JNIEnv, cls C.jclass, name C.jstring, callbacks C.jobject) C.jlong {
 	cName := newVStringFromJava(env, name)
 	cbs := C.newVSyncgroupInvitesCallbacks()
-	cbs.handle = C.v23_syncbase_Handle(uintptr(globalRefMap.Add(&inviteCallbacksHandle{
-		obj:       uintptr(unsafe.Pointer(C.NewGlobalRef(env, callbacks))),
+	scanId := uintptr(unsafe.Pointer(C.NewGlobalRef(env, callbacks)))
+	callbacksHandle := &inviteCallbacksHandle{
+		obj:       scanId,
 		callbacks: newJSyncgroupInvitesCallbacks(env, callbacks),
-	})))
+	}
+	if err := globalPtrMap.Set(scanId, callbacksHandle); err != nil {
+		panic(err)
+	}
+	cbs.handle = C.v23_syncbase_Handle(uintptr(scanId))
 	var cErr C.v23_syncbase_VError
-	var scanId C.uint64_t
-	v23_syncbase_DbSyncgroupInvitesNewScan(cName, cbs, &scanId, &cErr)
+	v23_syncbase_DbSyncgroupInvitesNewScan(cName, cbs, &cErr)
 	maybeThrowException(env, &cErr)
 	return C.jlong(scanId)
 }
 
 //export Java_io_v_syncbase_internal_Database_SyncgroupInvitesStopScan
 func Java_io_v_syncbase_internal_Database_SyncgroupInvitesStopScan(env *C.JNIEnv, cls C.jclass, scanId C.jlong) {
-	v23_syncbase_DbSyncgroupInvitesStopScan(C.uint64_t(scanId))
+	v23_syncbase_DbSyncgroupInvitesStopScan(C.v23_syncbase_Handle(scanId))
+	globalPtrMap.Remove(uintptr(scanId))
 }
 
 //export Java_io_v_syncbase_internal_Collection_GetPermissions
@@ -562,7 +572,7 @@
 //export v23_syncbase_internal_onKeyValue
 func v23_syncbase_internal_onKeyValue(handle C.v23_syncbase_Handle, keyValue C.v23_syncbase_KeyValue) {
 	id := uint64(uintptr(handle))
-	h := globalRefMap.Get(id).(*scanCallbacksHandle)
+	h := globalPtrMap.Get(uintptr(id)).(*scanCallbacksHandle)
 	env, free := getEnv()
 	obj := keyValue.extractToJava(env)
 	arg := *(*C.jvalue)(unsafe.Pointer(&obj))
@@ -577,7 +587,7 @@
 //export v23_syncbase_internal_onDone
 func v23_syncbase_internal_onDone(handle C.v23_syncbase_Handle, error C.v23_syncbase_VError) {
 	id := uint64(uintptr(handle))
-	h := globalRefMap.Get(id).(*scanCallbacksHandle)
+	h := globalPtrMap.Get(uintptr(id)).(*scanCallbacksHandle)
 	env, free := getEnv()
 	obj := error.extractToJava(env)
 	arg := *(*C.jvalue)(unsafe.Pointer(&obj))
@@ -588,7 +598,7 @@
 	}
 	C.DeleteGlobalRef(env, unsafe.Pointer(h.obj))
 	free()
-	globalRefMap.Remove(id)
+	globalPtrMap.Remove(uintptr(id))
 }
 
 type scanCallbacksHandle struct {
@@ -602,11 +612,16 @@
 	cHandle := newVStringFromJava(env, handle)
 	cStart := newVBytesFromJava(env, start)
 	cLimit := newVBytesFromJava(env, limit)
-	cbs := C.newVScanCallbacks()
-	cbs.handle = C.v23_syncbase_Handle(uintptr(globalRefMap.Add(&scanCallbacksHandle{
-		obj:       uintptr(unsafe.Pointer(C.NewGlobalRef(env, callbacks))),
+	ptr := uintptr(unsafe.Pointer(C.NewGlobalRef(env, callbacks)))
+	callbacksHandle := &scanCallbacksHandle{
+		obj:       ptr,
 		callbacks: newJScanCallbacks(env, callbacks),
-	})))
+	}
+	if err := globalPtrMap.Set(ptr, callbacksHandle); err != nil {
+		panic(err)
+	}
+	cbs := C.newVScanCallbacks()
+	cbs.handle = C.v23_syncbase_Handle(ptr)
 	var cErr C.v23_syncbase_VError
 	v23_syncbase_CollectionScan(cName, cHandle, cStart, cLimit, cbs, &cErr)
 	maybeThrowException(env, &cErr)
@@ -635,7 +650,7 @@
 //export v23_syncbase_internal_onPeer
 func v23_syncbase_internal_onPeer(handle C.v23_syncbase_Handle, peer C.v23_syncbase_AppPeer) {
 	id := uint64(uintptr(handle))
-	h := globalRefMap.Get(id).(*peerCallbacksHandle)
+	h := globalPtrMap.Get(uintptr(id)).(*peerCallbacksHandle)
 	env, free := getEnv()
 	obj := peer.extractToJava(env)
 	arg := *(*C.jvalue)(unsafe.Pointer(&obj))
@@ -655,20 +670,25 @@
 //export Java_io_v_syncbase_internal_Neighborhood_NewScan
 func Java_io_v_syncbase_internal_Neighborhood_NewScan(env *C.JNIEnv, cls C.jclass, callbacks C.jobject) C.jlong {
 	cbs := C.newVNeighborhoodScanCallbacks()
-	cbs.handle = C.v23_syncbase_Handle(uintptr(globalRefMap.Add(&peerCallbacksHandle{
-		obj:       uintptr(unsafe.Pointer(C.NewGlobalRef(env, callbacks))),
+	scanId := uintptr(unsafe.Pointer(C.NewGlobalRef(env, callbacks)))
+	callbacksHandle := &peerCallbacksHandle{
+		obj:       scanId,
 		callbacks: newJNeigbhorhoodScanCallbacks(env, callbacks),
-	})))
+	}
+	if err := globalPtrMap.Set(scanId, callbacksHandle); err != nil {
+		panic(err)
+	}
+	cbs.handle = C.v23_syncbase_Handle(uintptr(scanId))
 	var cErr C.v23_syncbase_VError
-	var scanId C.uint64_t
-	v23_syncbase_NeighborhoodNewScan(cbs, &scanId, &cErr)
+	v23_syncbase_NeighborhoodNewScan(cbs, &cErr)
 	maybeThrowException(env, &cErr)
 	return C.jlong(scanId)
 }
 
 //export Java_io_v_syncbase_internal_Neighborhood_StopScan
 func Java_io_v_syncbase_internal_Neighborhood_StopScan(env *C.JNIEnv, cls C.jclass, scanId C.jlong) {
-	v23_syncbase_NeighborhoodStopScan(C.uint64_t(scanId))
+	v23_syncbase_NeighborhoodStopScan(C.v23_syncbase_Handle(scanId))
+	globalPtrMap.Remove(uintptr(scanId))
 }
 
 //export Java_io_v_syncbase_internal_Row_Exists
diff --git a/services/syncbase/bridge/cgo/ptrmap/ptrmap.go b/services/syncbase/bridge/cgo/ptrmap/ptrmap.go
new file mode 100644
index 0000000..e250347
--- /dev/null
+++ b/services/syncbase/bridge/cgo/ptrmap/ptrmap.go
@@ -0,0 +1,47 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ptrmap
+
+import (
+	"fmt"
+	"sync"
+)
+
+func New() *ptrMap {
+	return &ptrMap{
+		refs: make(map[uintptr]interface{}),
+	}
+}
+
+type ptrMap struct {
+	refs map[uintptr]interface{}
+	lock sync.Mutex
+}
+
+func (r *ptrMap) Set(ptr uintptr, val interface{}) error {
+	r.lock.Lock()
+	defer r.lock.Unlock()
+	if _, ok := r.refs[ptr]; ok {
+		return fmt.Errorf("already have existing value at address %v", ptr)
+	}
+	r.refs[ptr] = val
+	return nil
+}
+
+func (r *ptrMap) Get(ptr uintptr) interface{} {
+	r.lock.Lock()
+	defer r.lock.Unlock()
+	return r.refs[ptr]
+}
+
+func (r *ptrMap) Remove(ptr uintptr) interface{} {
+	r.lock.Lock()
+	defer r.lock.Unlock()
+	val, ok := r.refs[ptr]
+	if ok {
+		delete(r.refs, ptr)
+	}
+	return val
+}
diff --git a/services/syncbase/bridge/cgo/refmap/refmap.go b/services/syncbase/bridge/cgo/refmap/refmap.go
deleted file mode 100644
index a3ab087..0000000
--- a/services/syncbase/bridge/cgo/refmap/refmap.go
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2016 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package refmap
-
-import (
-	"sync"
-)
-
-func NewRefMap() *refMap {
-	return &refMap{
-		refs: make(map[uint64]interface{}),
-	}
-}
-
-type refMap struct {
-	refs   map[uint64]interface{}
-	lastId uint64
-	lock   sync.Mutex
-}
-
-func (r *refMap) Add(val interface{}) uint64 {
-	r.lock.Lock()
-	defer r.lock.Unlock()
-	id := r.lastId
-	r.lastId++
-	r.refs[id] = val
-	return id
-}
-
-func (r *refMap) Get(id uint64) interface{} {
-	r.lock.Lock()
-	defer r.lock.Unlock()
-	if val, ok := r.refs[id]; ok {
-		return val
-	}
-	return nil
-}
-
-func (r *refMap) Remove(id uint64) interface{} {
-	r.lock.Lock()
-	defer r.lock.Unlock()
-	if val, ok := r.refs[id]; ok {
-		delete(r.refs, id)
-		return val
-	}
-	return nil
-}