Просмотр исходного кода

Manage memory returned by Start in PsiphonTunnel

mirokuratczyk 7 лет назад
Родитель
Сommit
5451fac5f7
2 измененных файлов с 35 добавлено и 6 удалено
  1. 33 4
      ClientLibrary/PsiphonTunnel.go
  2. 2 2
      ClientLibrary/example/main.c

+ 33 - 4
ClientLibrary/PsiphonTunnel.go

@@ -1,5 +1,6 @@
 package main
 
+// #include <stdlib.h>
 import "C"
 
 import (
@@ -9,6 +10,7 @@ import (
 	"fmt"
 	"sync"
 	"time"
+	"unsafe"
 
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
@@ -46,7 +48,17 @@ type psiphonTunnel struct {
 
 var tunnel psiphonTunnel
 
+// Memory managed by PsiphonTunnel which is allocated in Start and freed in Stop
+var managedStartResult *C.char
+
 //export Start
+//
+// ******************************* WARNING ********************************
+// The underlying memory referenced by the return value of Start is managed
+// by PsiphonTunnel and attempting to free it explicitly will cause the
+// program to crash. This memory is freed once Stop is called.
+// ************************************************************************
+//
 // Start starts the controller and returns once either of the following has occured: an active tunnel has been
 // established, the timeout has elapsed before an active tunnel could be established or an error has occured.
 //
@@ -213,9 +225,13 @@ func Start(configJSON, embeddedServerEntryList, networkID string, timeout int64)
 		tunnel.stopController()
 	}
 
+	// Free previous result
+	freeManagedStartResult()
+
 	// Return result
+	managedStartResult = marshalStartResult(result)
 
-	return marshalstartResult(result)
+	return managedStartResult
 }
 
 //export Stop
@@ -224,9 +240,12 @@ func Start(configJSON, embeddedServerEntryList, networkID string, timeout int64)
 // Stop should always be called after a successful call to Start to ensure the
 // controller is not left running.
 func Stop() {
+	freeManagedStartResult()
+
 	if tunnel.stopController != nil {
 		tunnel.stopController()
 	}
+
 	tunnel.controllerWaitGroup.Wait()
 }
 
@@ -236,9 +255,9 @@ func secondsBeforeNow(startTime time.Time) float64 {
 	return delta.Seconds()
 }
 
-// marshalstartResult serializes a startResult object as a JSON string in the form
+// marshalStartResult serializes a startResult object as a JSON string in the form
 // of a null-terminated buffer of C chars.
-func marshalstartResult(result startResult) *C.char {
+func marshalStartResult(result startResult) *C.char {
 	resultJSON, err := json.Marshal(result)
 	if err != nil {
 		return C.CString(fmt.Sprintf("{\"result_code\":%d, \"error\": \"%s\"}", startResultCodeOtherError, err.Error()))
@@ -261,7 +280,17 @@ func startErrorJson(err error) *C.char {
 	result.Code = startResultCodeOtherError
 	result.ErrorString = err.Error()
 
-	return marshalstartResult(result)
+	return marshalStartResult(result)
+}
+
+// freeManagedStartResult frees the memory on the heap pointed to by managedStartResult.
+func freeManagedStartResult() {
+	if managedStartResult != nil {
+		managedMemory := unsafe.Pointer(managedStartResult)
+		if managedMemory != nil {
+			C.free(managedMemory)
+		}
+	}
 }
 
 // main is a stub required by cgo.

+ 2 - 2
ClientLibrary/example/main.c

@@ -62,7 +62,7 @@ int main(int argc, char *argv[]) {
     // print results
     printf("Result: %s\n", result);
 
-    // cleanup
-    free(result);
+    // The underlying memory of `result` is managed by PsiphonTunnel and will
+    // have been freed in Stop.
 }