2017-11-11 73 views
4

我目前正在圍繞C庫編寫一個Go包裝。該C庫使用不透明的結構指針來隱藏接口中的信息。但是,底層實現會在其中存儲size_t值。這會導致生成的程序出現運行時錯誤。 最小工作示例來重現問題是這樣的:如何解決CEG庫中使用不透明結構指針時cgo中的「寫入障礙中的錯誤指針」恐慌

main.go

package main 

/* 
#include "stddef.h" 
// Create an opaque type to hide the details of the underlying data structure. 
typedef struct HandlePrivate *Handle; 

// In reality, the implementation uses a type derived from size_t for the Handle. 
Handle getInvalidPointer() { 
    size_t actualHandle = 1; 
    return (Handle) actualHandle; 
} 
*/ 
import "C" 

// Create a temporary slice containing invalid pointers. 
// The idea is that the local variable slice can be garbage collected at the end of the function call. 
// When the slice is scanned for linked objects, the GC comes across the invalid pointers. 
func getTempSlice() { 
    slice := make([]C.Handle, 1000000) 
    for i, _ := range slice { 
     slice[i] = C.getInvalidPointer() 
    } 
} 

func main() { 
    getTempSlice() 
} 

運行這個程序會導致以下錯誤

runtime: writebarrierptr *0xc42006c000 = 0x1 
fatal error: bad pointer in write barrier 
[...stack trace omitted...] 

注意,錯誤消失時,GC通過設置環境變量GOGC=off來禁用。

我的問題是哪個是解決或解決此問題的最佳方法。該庫爲了隱藏信息而在指針中存儲整數值,這似乎混淆了GC。出於顯而易見的原因,我不想開始搞亂庫本身,而是在包裝圖層中吸收這種行爲。

我的環境是Ubuntu 16.04,帶有gcc 5.4.0和Go 1.9.2。

Documentation of cgo

回答

2

我能重現錯誤go1.8.5go1.9.2。我無法重現提示的錯誤:devel +f01b928 Sat Nov 11 06:17:48 2017 +0000(有效go1.10alpha)。


// Create a temporary slice containing invalid pointers. 
// The idea is that the local variable slice can be garbage collected at the end of the function call. 
// When the slice is scanned for linked objects, the GC comes across the invalid pointers. 

一去的口頭禪是不要忽略錯誤。但是,您似乎認爲GC將優雅地忽略錯誤。 GC應該大聲抱怨(go1.8.5go1.9.2)。最糟糕的是,由於發佈到發佈的行爲可能會有所不同,GC可能會忽略錯誤(go devel)。

Go編譯器看到一個指針,Go運行時GC期待一個有效的指針。

// go tool cgo 
// type _Ctype_Handle *_Ctype_struct_HandlePrivate 
// var handle _Ctype_Handle 
var handle C.Handle 
// main._Ctype_Handle <nil> 0x0 
fmt.Fprintf(os.Stderr, "%[1]T %[1]v %[1]p\n", handle) 

slice := make([]C.Handle, 1000000) 
for i, _ := range slice { 
    slice[i] = C.getInvalidPointer() 
} 

使用類型uintptr。例如,

package main 

import "unsafe" 

/* 
#include "stddef.h" 
// Create an opaque type to hide the details of the underlying data structure. 
typedef struct HandlePrivate *Handle; 

// In reality, the implementation uses a type derived from size_t for the Handle. 
Handle getInvalidPointer() { 
    size_t actualHandle = 1; 
    return (Handle) actualHandle; 
} 
*/ 
import "C" 

// Create a temporary slice of C pointers as Go integer type uintptr. 
func getTempSlice() { 
    slice := make([]uintptr, 1000000) 
    for i, _ := range slice { 
     slice[i] = uintptr(unsafe.Pointer(C.getInvalidPointer())) 
    } 
} 

func main() { 
    getTempSlice() 
} 
+0

我在使用OP代碼的「go version go1.9.2 linux/amd64」與我的拱門上有同樣的錯誤。錯誤:「運行時:writebarrierptr * 0xc42007a000 = 0x1 致命錯誤:寫入障礙中的指針錯誤」 – Stargateur