https://go.googlesource.com/proposal/+/master/design/12416-cgo-pointers.md

https://github.com/golang/go/issues/12416

Proposal: Rules for passing pointers between Go and C

Author: Ian Lance Taylor Last updated: October, 2015

Discussion at https://golang.org/issue/12416.

Abstract

List specific rules for when it is safe to pass pointers between Go and C using cgo.

Background

Go programmers need to know the rules for how to use cgo safely to share memory between Go and C. When using cgo, there is memory allocated by Go and memory allocated by C. For this discussion, we define a Go pointer to be a pointer to Go memory, and a C pointer to be a pointer to C memory. The rules that need to be defined are when and how Go code can use C pointers and C code can use Go pointers.

Note that for this discussion a Go pointer may be any pointer type, including a pointer to a type defined in C. Note that some Go values contain Go pointers implicitly, such as strings, slices, maps, channels, and function values.

It is a generally accepted (but not actually documented) rule that Go code can use C pointers, and they will work as well or as poorly as C code holding C pointers. So the only question is this: when can C code use Go pointers?

The de-facto rule for 1.4 is: you can pass any Go pointer to C. C code may use it freely. If C code stores the Go pointer in C memory then there must be a live copy of the pointer in Go as well. You can allocate Go memory in C code by calling the Go function _cgo_allocate.

The de-facto rule for 1.5 adds restrictions. You can still pass any Go pointer to C. However, C code may not store a Go pointer in Go memory (C code can still store a Go pointer in C memory, with the same restrictions as in 1.4). The _cgo_allocate function has been removed.

We do not want to document the 1.5 de-facto restrictions as the permanent rules because they are somewhat confusing, they limit future garbage collection choices, and in particular they prohibit any future development of a moving garbage collector.

Proposal

I propose that we permit Go code to pass Go pointers to C code, while preserving the following invariant:

  • The Go garbage collector must be aware of the location of all Go pointers, except for a known set of pointers that are temporarily visible to C code. The pointers visible to C code exist in an area that the garbage collector can not see, and the garbage collector may not modify or release them.

It is impossible to break this invariant in Go code that does not import “unsafe” and does not call C.

I propose the following rules for passing pointers between Go and C, while preserving this invariant:

  1. Go code may pass a Go pointer to C provided that the Go memory to which it points does not contain any Go pointers.
  • The C code must not store any Go pointers in Go memory, even temporarily.
  • When passing a pointer to a field in a struct, the Go memory in question is the memory occupied by the field, not the entire struct.
  • When passing a pointer to an element in an array or slice, the Go memory in question is the entire array or the entire backing array of the slice.
  • Passing a Go pointer to C code means that that Go pointer is visible to C code; passing one Go pointer does not cause any other Go pointers to become visible.
  • The maximum number of Go pointers that can become visible to C code in a single function call is the number of arguments to the function.
  1. C code may not keep a copy of a Go pointer after the call returns.
  • A Go pointer passed as an argument to C code is only visible to C code for the duration of the function call.
  1. A Go function called by C code may not return a Go pointer.
  • A Go function called by C code may take C pointers as arguments, and it may store non-pointer or C pointer data through those pointers, but it may not store a Go pointer in memory pointed to by a C pointer.
  • A Go function called by C code may take a Go pointer as an argument, but it must preserve the property that the Go memory to which it points does not contain any Go pointers.
  • C code calling a Go function can not cause any additional Go pointers to become visible to C code.
  1. Go code may not store a Go pointer in C memory.
  • C code may store a Go pointer in C memory subject to rule 2: it must stop storing the pointer before it returns to Go.

The purpose of these four rules is to preserve the above invariant and to limit the number of Go pointers visible to C code at any one time.

Examples

Go code can pass the address of an element of a byte slice to C, and C code can use pointer arithmetic to access all the data in the slice, and change it (the C code is of course responsible for doing its own bounds checking).

Go code can pass a Go string to C. With the current Go compilers it will look like a two element struct.

Go code can pass the address of a struct to C, and C code can use the data or change it. Go code can pass the address of a struct that has pointer fields, but those pointers must be nil or must be C pointers.

Go code can pass a non-nested Go func value into C, and the C code may call a Go function passing the func value as an argument, but it must not save the func value in C memory between calls, and it must not call the func value directly.

A Go function called by C code may not return a string.

Consequences

This proposal restricts the Go garbage collector: any Go pointer passed to C code must be pinned for the duration of the C call. By definition, since that memory block may not contain any Go pointers, this will only pin a single block of memory.

Because C code can call back into Go code, and that Go code may need to copy the stack, we can never pass a Go stack pointer into C code. Any pointer passed into C code must be treated by the compiler as escaping, even though the above rules mean that we know it will not escape. This is an additional cost to the already high cost of calling C code.

Although these rules are written in terms of cgo, they also apply to SWIG, which uses cgo internally.

Similar rules may apply to the syscall package. Individual functions in the syscall package will have to declare what Go pointers are permitted. This particularly applies to Windows.

That completes the rules for sharing memory and the implementation restrictions on Go code.

Support

We turn now to helping programmers use these rules correctly. There is little we can do on the C side. Programmers will have to learn that C code may not store Go pointers in Go memory, and may not keep copies of Go pointers after the function returns.

We can help programmers on the Go side, by implementing restrictions within the cgo program. Let us assume that the C code and any unsafe Go code behaves perfectly. We want to have a way to test that the Go code never breaks the invariant.

We propose an expensive dynamic check that may be enabled upon request, similar to the race detector. The dynamic checker will be turned on via a new option to go build: -checkcgo. The dynamic checker will have the following effects:

  • We will turn on the write barrier at all times. Whenever a pointer is written to memory, we will check whether the pointer is a Go pointer. If it is, we will check whether we are writing it to Go memory (including the heap, the stack, global variables). If we are not, we will report an error.

  • We will change cgo to add code to check any pointer value passed to a C function. If the value points to memory containing a Go pointer, we will report an error.

  • We will change cgo to add the same check to any pointer value passed to an exported Go function, except that the check will be done on function return rather than function entry.

  • We will change cgo to check that any pointer returned by an exported Go function is not a Go pointer.

These rules taken together preserve the invariant. It will be impossible to write a Go pointer to non-Go memory. When passing a Go pointer to C, only that Go pointer will be made visible to C. The cgo check ensures that no other pointers are exposed. Although the Go pointer may contain pointer to C memory, the write barrier ensures that that C memory can not contain any Go pointers. When C code calls a Go function, no additional Go pointers will become visible to C.

We propose that we enable the above changes, other than the write barrier, at all times. These checks are reasonably cheap.

These checks should detect all violations of the invariant on the Go side. It is still possible to violate the invariant on the C side. There is little we can do about this (in the long run we could imagine writing a Go specific memory sanitizer to catch errors.)

A particular unsafe area is C code that wants to hold on to Go func and pointer values for future callbacks from C to Go. This works today but is not permitted by the invariant. It is hard to detect. One safe approach is: Go code that wants to preserve funcs/pointers stores them into a map indexed by an int. Go code calls the C code, passing the int, which the C code may store freely. When the C code wants to call into Go, it passes the int to a Go function that looks in the map and makes the call. An explicit call is required to release the value from the map if it is no longer needed, but that was already true before.

Rationale

The garbage collector has more flexibility when it has complete control over all Go pointers. We want to preserve that flexibility as much as possible.

One simple rule would be to always prohibit passing Go pointers to C. Unfortunately that breaks existing packages, like github.com/gonum/blas, that pass slices of floats from Go to C for efficiency. It also breaks the standard library, which passes the address of a C.struct_addrinfo to C.getaddrinfo. It would be possible to require all such code to change to allocate their memory in C rather than Go, but it would make cgo considerably harder to use.

This proposal is an attempt at the next simplest rule. We permit passing Go pointers to C, but we limit their number, and require that the garbage collector be aware of exactly which pointers have been passed. If a later garbage collector implements moving pointers, cgo will introduce temporary pins for the duration of the C call.

Rules are necessary, but it's always useful to enforce the rules. We can not enforce the rules in C code, but we can attempt to do so in Go code.

If we adopt these rules, we can not change them later, except to loosen them. We can, however, change the enforcement mechanism, if we think of better approaches.

Compatibility

This rules are intended to extend the Go 1 compatibility guidelines to the cgo interface.

Implementation

The implementation of the rules requires adding documentation to the cgo command.

The implementation of the enforcement mechanism requires changes to the cgo tool and the go tool.

The goal is to get agreement on this proposal and to complete the work before the 1.6 freeze date.

Open issues

Can and should we provide library support for certain operations, like passing a token for a Go value through C to Go functions called from C?

Should there be a way for C code to allocate Go memory, where of course the Go memory may not contain any Go pointers?

golang 与 c语言 之间传递指针的规则提案的更多相关文章

  1. 两个应用之间传递广播的规则 Broadcast

    sendBroadcast(new Intent(Config.ACTION_PRINT),”com.qf.permission.print”);先判断应用有没有对应的权限 再去判断有没有对应的act ...

  2. C语言结构体指针的引用问题

    在写栈的一个应用时遇见这样的一个问题 SqStack s; s->base = (int*)malloc(sizeof(int)*10); 通过这样一个代码引用的时候,会导致程序出现异常 经过一 ...

  3. 使用DLL进行不同语言之间的调用(转)

    源:使用DLL进行不同语言之间的调用 __declspec(dllexport) 是告诉编译器用来导出函数的,在代码中不另作说明了. extern "C" 的意思就是用C的方式来导 ...

  4. Golang 汇编asm语言基础学习

    Golang 汇编asm语言基础学习 一.CPU 基础知识 cpu 内部结构 cpu 内部主要是由寄存器.控制器.运算器和时钟四个部分组成. 寄存器:用来暂时存放指令.数据等对象.它是一个更快的内存. ...

  5. (转)如何在JavaScript与ActiveX之间传递数据2

    本文研究如何在JS等脚本语言与ActiveX控件之间通信,如何传递各种类型的参数,以及COM的IDispatch接口.使用类似的方法,可以推广到其他所有脚本型语言,如LUA,AutoCad等.本文将研 ...

  6. 由链表初始化看C语言的二级指针

    先来看C语言创建链表.插入节点和遍历链表的一段代码: #include <stdio.h> #include <stdlib.h> typedef int ElemType; ...

  7. Android 笔记-Fragment 与 Activity之间传递数据

    Fragment 与 Activity之间传递数据有两种方法.一种是使用setArgument,一种是使用接口回调.以下先学习第一种方法. (1)使用setArgument方法: 为了便于理解,我在这 ...

  8. 用WM_COPYDATA消息来实现两个进程之间传递数据

    文着重讲述了如果用WM_COPYDATA消息来实现两个进程之间传递数据. 进程之间通讯的几种方法:在Windows程序中,各个进程之间常常需要交换数据,进行数据通讯.常用的方法有   1.使用内存映射 ...

  9. 【MVC架构】——怎样利用Json在View和Controller之间传递数据

    在MVC架构中,尽管非常多东西和三层非常相似,可是也有非常大的差别.就比方传递数据.在三层架构中,传递数据就仅仅要一层返回,另外一层用同样类型的变量来接收即可了.在MVC中,事实上原理是一样的,Con ...

随机推荐

  1. redis aof和rdb区别

    转自https://blog.csdn.net/m0_38110132/article/details/76906422 1.前言 最近在项目中使用到Redis做缓存,方便多个业务进程之间共享数据.由 ...

  2. linux几个命令

    ll |wc -l 统计目录下多少文件 df -h 磁盘统计大小 du -sh 该目录占磁盘总大小 du -sh * 该目录下每个目录占用磁盘大小

  3. java基础---->序列化框架arvo的使用

    这里面我们介绍一下序列化框架arvo的使用,在kafka中使用的就是这个. arvo的使用 一.需要通过插件生成Model类方式 一.生成我们的数据模型User.java 我们在resources里面 ...

  4. nuxt项目中vue报错The client-side rendered virtual ...

    报错: 翻译过来是: [Vue警告]:客户端呈现的虚拟DOM树与服务器呈现的内容不匹配.这可能是由不正确的HTML标记引起的,例如在其中嵌套块级元素或丢失.Bailing水化和执行完整的客户端渲染. ...

  5. javascript基础学习系列-原型链模式

    1.demo代码如下: 2.画图如下: 3.规则: 1)每一个函数数据类型(普通函数/类)都有一个天生自带的属性:prototype(原型),并且这个属性是一个对象数据类型的值 2)并且prototy ...

  6. 快速构建springmvc+spring+swagger2环境

    快速构建springmvc+spring+swagger2环境 开发工具:Intellij idea               jdk: 1.8 开发步骤: 1.创建maven工程,如图建立工程结构 ...

  7. WIN7/XP用注册表关联指定后缀名和打开程序(手动【图文】和C编程两种实现)

    前言: 本文是基本原理介绍和手动的操作.程序实现该功能在http://blog.csdn.net/arvon2012/article/details/7839556,同时里面有完整代码的下载. 今天在 ...

  8. IIS8.5 Error Code 0x8007007e HTTP 错误 500.19的解决方法

    window server 2012R2 IIS8.5 引用:https://www.52jbj.com/yunying/340443.html HTTP 错误 500.19 - Internal S ...

  9. lombok安装方法

    一.介绍 lombok网址:https://projectlombok.org/download.html lombok能够在编译源码的时候自动生成getter和setter方法.即它最终能够达到的效 ...

  10. 被Entity Framework Core的细节改进震撼了一下

    今天用 SQL Server Profiler 查看 Entity Framework Core 生成的 SQL 语句时,突然发现一个细节改进,并且被它震撼了一下: exec sp_executesq ...