参数所有权与共享原子

概述

DIP25和DIP1000中提出的语言特征通过检测指针是否超出了函数域,大大提高了给函数传递引用和指针的内存安全.相应,如果函数不允许转义引用,则容器可以安全地传递内部引用给函数.

但,如果传递多个相同容器的引用给函数,则一个引用会渲染其他引用引用的空数据.该DIP旨在纠正该问题.这是在DIP 25和DIP 1000后的自然发展,是安全实施引用计数所必需的.这项提议是迈向博客文章"所有权和借款在D中"(中文在此)概述的万里长征的第一步.

理由

如果不能内存安全的访问容器有效载荷,容器是内存不安全的.如果容器无法直接引用其有效负载,则无法高效运行.让用户不做某些事情是不可靠的,也无法扩展.

问题最简单说明:

struct S {
     字节* ptr;
     ref byte get(){ return *ptr; }
}

void foo(ref S t,ref byte b){
    释放(t.ptr);    //释放b引用内存
    b = 4 ;//已坏的内存访问
}

空 测试(){
    s;
    s.ptr = 转换(字节*)malloc(1);
    foo(s,s.get());  //(*) 
}

使用域指针的相同问题:

struct S {
    字节* ptr;
    字节* get(){ return ptr; }
}

void foo(域 S*t,域 字节* pb){//两个参数指向相同内存
    释放(t.ptr);    //释放pb引用的内存
    *pb = 4 ;        //破坏的内存访问
}

空 测试(){
    s;
    s.ptr = 转换(字节*)malloc(1);
    foo(&s,s.get());  //(*) 
}

D当前不能防御这种问题,因此不能机械检查内存安全引用计数.(Timon Gehr首先指出这点.)

先前的工作

Rust这样避免问题的:

  1. 任何借不能超过所有者的域.

  2. 你可能有1到2种借,但不能2种一样.

  3. 同一资源的一/多个引用(&T),

  4. 只能一个可变引用(&mut T).
    引用地址

    扫描二维码关注公众号,回复: 9499858 查看本文章

描述

该方法在于:示例中,给函数foo传递了同一数据的两个可变引用(即不允许同一个资源的多个可变引用).即,只要同一数据有多个引用,且其中一个是可变的,则无论可变引用是不是,都能使数据空.因此,如果将多个同一数据的引用传递给一个函数时,它们必须都为常.

DIP25和DIP1000在编译器语义已收集的数据上,加额外检查,以通过函数调用和返回来跟踪生命期.
这是在上面两个dip上建立和测试的基础上构建的.
仅检查@安全代码.

句法

此DIP建议不更改语法.它在现有构造上加了其他语义检查.

局限

提交特征仅检查函数调用表达式.它不检查之间的状态.不检查非域针.尽管这是重要一步,但不是完整借贷/所有权机制.例如,可用临时项来取消检查域指针:

空 测试(){ 
    S s; 
    s.ptr = 转换(字节*)malloc(1);
    动 ps =&s; 
    foo(ps,s.get());  //(*) `ps,与s是相同针`
}

解决此问题只能使用"到达定义"的对编译器帮助很大的数据流分析.另外,引用不需要它,因为只初化它们一次且始终在域内.

重大变更和过时

这将破坏传递多个相同对象的可变引用给函数的现有代码,不知道这种模式有多普遍.可将代码标记为@信任,@系统来修复.

讨论区

一些评论者抱怨说,DIP缺乏细节,并且提供的示例来说明DIP解决的问题是不够的.特别是,@safe由于DIP声明提出的检查仅在@safe代码中执行,因此请求使用代码说明问题的示例.DIP作者回答说,"先前的工作"部分中包含额外的信息,并且提供的示例足以说明问题.

讨论区

社区评论对缺乏细节的一些批评被重复了,有人抗议没有对这些反馈做出修改.有人对"大局"计划(如作者在本博客文章中概述的)以及该建议如何适合其中提出了疑问.关于Rust实现所有权和借用以及讨论D是否适合他.


共享原子

概述

核心语言的操作符不支持读写类型为共享的数据.只能通过库中的函数调用来访问它们.

理由

使共享类型成为D中的一流类型是项有效的创新.对开发健壮的多线程应用程序区分共享和非共享数据的能力至关重要.但D缺少改变访问共享数据的语义的方法,
由于优化编译器的代码动作,使默认行为遭受数据竞争的既明显(速度慢)又隐藏(看不见)的影响.

禁止直接访问共享数据,要求用户正确使用core.atomic.

描述

现在要求程序员使用core.atomic等效函数来读写共享内存对象.直接读写共享内存对象时,编译器将报告错误.避免意外,无意地不使用原子访问.
允许初化共享数据.语法不变.

例子:


共享 int x;// 好,初化为0
++x;//错,读写共享x
共享 int* p = &x;// 好, 初化共享 p
共享 int* q = p; // 错, 读共享p//赋值
共享(int)* r = &x; // 好
共享(int)* s = r;  // 好
*s = 3; //错,写至共享 *s
int y = *s;//错,从共享读*s

局限性

该提议不保证代码无锁,也不能消除事务竞争时锁的需求.(事务是必须执行的一系列,完成事务前,其他线程不能改共享数据的操作.具有原子性)

它不禁止转换共享数据为非共享数据,然后通过核心语言运算符操作它,尽管仅允许@system和@trusted代码操作.

替代

用CPU支持的操作符对锁定操作提供有限支持.C++就这样.这有争议,因为有人认为这鼓励错误的编码做法.

重大变更和弃用

所有访问共享内存对象的代码都会中断.因而会有很长的淘汰周期.

core.atomic的代码没问题.

为了生成常规代码,锁保护代码需要通过转换()式转换(常)式来去掉类型头部的"共享".用户必须小心,不要让那些头未共享的内存位置的引用脱离锁定代码区域,但在本dip前保持原样.

讨论区

重大的,反复投诉集中在文本的矛盾部分,引起了审阅者的困惑.DIP作者解释说是编辑过程中发生的错误,下一个版本中修改.除要求作者包含其他语言的引用外,其余讨论都涉及相关主题(例如,共享的当前和预期行为).

讨论区

在本轮审核中,仅提供了两项可行反馈意见:可能破坏代码示例;并且以"原子读取执行获取操作…"开头的句子与该建议无关,应该删除.DIP作者对此进行了修改.

讨论区

该提议共享仍可与BetterC一起使用.

发布了377 篇原创文章 · 获赞 25 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/fqbqrr/article/details/104336348