d的分配器3
上一篇2
假定
隔离可应用来可变数据
或可变数据的指针
,如果数据是常或不变
的,则D的传递性
表明所有指针
都是只读
的.
我在写带借用和引用计数
的(Neat)
语言,这不是有效的借用
,基本上不想用默认可变
变量来借用
,因为就需要如下:
Vector!int vector;
vector ~= 3;
void evil() { vector = Vector!int.init; }
auto borrowed = vector[0];
func(borrowed);
void func(scope ref int value) {
//析构最后非借用的向量引用,怎么办
evil;
补充
:在rust
中,该成语
不成立,因为void evil()
已抓了向量
.但是嵌套
函数是D语言
的重要部分.因此不能把借用
硬塞到围绕垃集
设计的语言
上;需要在每一个
级别上都支持它(就像变量默认
为右值
一样).
隔离(isolated)
会很好,但是现在可用struct
来建模它,这样就可:
class Mallocator : IAllocator
{
import core.stdc.stdlib : free, malloc;
void* safeAllocate(size_t n) @trusted
{
return malloc(n);
}
void safeDeallocate(Isolated!(void*) ip) @trusted
{
ip.unwrap.free;
}
}
void main()
{
IAllocator a = new Mallocator;
scope m = a.safeAllocate(4);
auto ip = (() @trusted => assumeIsolated(a.safeAllocate(4)))();
a.safeDeallocate(ip.move);
assert(ip.unwrap == null);
}
工作代码:这里.
隔离
可进入std.typecons
.
隔离
是不够
的,还必须保证指针
是用'malloc'
分配的.
可在用malloc
分配的指针外加个包装器
.@安全 free(释放)
只接受它们,而不接受隔离
指针.
不充分的free
如果ptr
的值不等于
,先前由malloc(),calloc(),realloc()
或aligned_alloc()
返回的值,则该行为未定义
.
未定义行为
不是内存安全
的.
这太可怕了.还不如叫它DynamicArray
,这正是我建议
的,用数据结构
,而不是直接调用
分配器!
当然,可添加@require(AllocatorAware)
,然后就到此为止.至少会迫使人们审计
代码,并发现嘿,这不是我应直接用的,但仍允许传递分配器
.
这完全是题外话,显然大多数用户
不需要直接使用分配器API
.
但是,如果正在*实现*
像动态数组
等数据结构
,并且想支持用户提供的自定义分配器
,则数据结构可@safe
的唯一方法是分配器API
使用该包装器类型
来表示@safe
接口.
上例中,使用@system
可传入想要
的任意
指针,或提取
指针,就失去了给定结构
所提供的保护
.如果结构
很简单,可用@safe
很容易地完成,:/所以分配器
并不适合返回.所以你只能呆在审计
的区间
内.
没有吗?
数据结构
管理生命期
,它从语言中接管
来保证检查.
最后,数据结构和算法
都要审计.语言
应有生命期
管理.
这一点上,Dennis
做了些工作
来替换域推导
,它提供期望堵塞从数据结构
中借用内存的生命期跟踪
中的最后大漏洞的基础设施
😃 ,这里
可用DIP1035
的@系统
变量来确保不会干扰
包装器内部结构.
你没有抓住我原帖要点.
为了使数据结构
向用户提供保证
,分配器
必须依次向数据结构
提供某些保证
.如果数据结构
事先不知道用哪个
分配器(如如果它在调用通用RCAllocator.deallocate
函数),则必须在类型系统
中编码那些保证
,以便@safe
代码不会意外破坏
它们.
可惜,我确实完全掌握
了它.我认为不值得
.
沃尔特
已表明,他不想,要解决此事
需要的DFA
,因此解决
该问题只会浪费精力
.
据我所知,不能同时具备
以下三个条件:
1
.@安全
容器.
2
.用户提供分配器
.
3
.语言不变
.
考虑到Walter
和Atila
在内存安全的立场,(1)
是必须的,所以问题
是更喜欢(1)+(2)
(实现隔离
等)还是(1)+(3)
(禁止
用户定义自己的分配器),我也不确定哪个更好.
你一会儿说(1)+(3)
,我提出(1)+(2)
,你说(1)+(2)+(3)
是可能的.
如果同意我说的(1)+(2)+(3)
是不可能的,则我不反对你的其他主张
;另一方面,如果你相信(1)+(2)+(3)
是可能的,则想听听你的想法.
改变
语言来帮助解决
该问题,如果有
机会,不会不这样做;)特别是小改变,且有多个用例
时.
现在,1.
和2.
我想区分有效的@safe
和机器
可检查的@safe
.
我不相信会有完全
的机器检查
能力.这表明DFA
,人手不足以来设计和实现
它.DIP1000
是个很好的示例
,因为它不支持变量中间接
有多个
生命期.
有效@safe
表明尽量多的机器检查
代码,但是希望隔离
不安全部分
到库代码
中,在那里
审计来确保安全.
告诉人们不要用自定义
分配器.
是的,这是不言而喻的,即使有像隔离
限定符这种语言特性
来帮助,数据结构和分配器
仍需要在内部
使用@trusted
代码.
我无法理解.你是在建议(1)+(3),(2)+(3)
,还是两者的混合?比如,如果使用官方
分配器,容器
将是@safe
的,如果使用第三方
的分配器
,它将是@system
的?
如果人多
,则是1
和2
(但不是3
).否则,就只能暂时把目标从完美
变成足够好
.正如安德烈所说:完美
是好
的敌人,似乎有条相当不错的"好"
路.
使用什么分配器库
并不重要.它工作或不工作
.编译器不应关心
是谁的代码,只应该关心生命期模式
.
你建议目标在哪.
我建议,尽量多地用@safe
检查,但是让数据结构
尽量多地负责分配器
的生命期
保证.
完美方法:所有代码
都是机械检查
的.
好的:审计
库和底层代码,机械检查
库用户代码.
先有个好方法,再试找出是否可消除限制
,并在库代码
应用.
我是一半一半.像@localsafe
表明API
中的分配器都应该是@system
.因为该线程,我重新考虑我自己的分配器
.
但是就编译器硬编码进std.allocators
对比sidero.base.allocators
,是的,不应这样
,如果两个API
匹配,则编译器
应该在生命期
管理方面以相同
对待它们.
前面我提出了分配器
中要求1个@信任
的证书
函数.你同意原则上满足所有这些要求,但不会考虑它,因为向外人解释会很尴尬.你能解释一下吗?也许有更多的想法.
无论是语言原语
还是标准库
函数都给予
了保证,重要的是不涉足@系统
或@信任
,用户
可写什么.
标准库
可能有漏洞
,语言级
检查器也可能有漏洞
.
根本问题
是没有(除了约定
外)强制机制
来阻止分配器拥有@信任
并瞎搞
.特别是,在初始实现分配器
,库作者都必须(文档
?注解
?)“知道”@系统
是否符合@信任
证书.
手动验证@系统
代码是在D中要远离的(参见DIP1035
),采纳该机制
是个倒退
.
则,应该允许@safe
数据结构调用@system
分配器,并简单*假设*
分配器实现不会乱搞
吗?如果有人编写了自己的分配器
实现,且没有手动检查
他们的@system
代码是否提供了数据结构
所期望保证,可接受他们在@safe
代码中得到内存破坏吗?
问题是,如果编写@safe
数据结构
1
.需要分配器
为你提供某些保证
(即,只要满足前提条件A,B
和C
,调用deallocate
就是安全的).
2
.如果接受用户提供的任意分配器
,则无法判断
它们是否
提供了这些保证.
忘记分配器的作者.数据结构
作者应该做什么?
可能答案是,“数据结构
作者应该假设提供了保证
”.该答案是不可接受的,因为它允许在@safe
代码中内存破坏
.
或,“数据结构
作者应该在分配器
上找到某种"标志"
或"证书"
,以表明它提供了必要的保证
”.这是个更好的答案
,但并不理想
,因为它需要手动验证@trusted
代码及@system
代码,并且在数据结构
作者和分配器
作者之间还要划分验证
责任.
或,"数据结构
作者应该创建白名单
,列举他知道提供必要
保证的分配器,并且只信任
该名单上的分配器
."这与前面答案类似,但是验证的所有责任
都放在@trusted
代码的作者身上(即数据结构
作者),并且不依赖于分配器
作者来手动验证他们的@system
代码.
或,"数据结构
作者不应接受
用户的任意分配器
."这是限制性更强
的折衷
方案,但与前面
不同,它允许数据结构
作者确信他的@trusted
代码不会导致内存破坏
.
或,"数据结构
作者应该依靠分配器
作者来提供@safe
接口."这是最好的答案,但需要添加新的语言特性
,不行.
我想听到你的回答.你认为该数据结构
的作者应该怎么做?
除非Walter
改变他对DFA
的看法,或说让在dmd
上增加校对
引擎,否则没法.
现在有了@系统
变量,所以就像ntrel
和Dukc
已提出的那样,可有poorman
的typestate
和poorman
的move
语义.
为什么该机制不行呢?这不正是"@系统
"变量要解决的问题(非平凡内存安全不变量
)吗?
(用sumtype
,甚至可移动标志
到运行时
(以标志数量
呈指数级
膨胀为代价)来获得poorman
的依赖类型状态.)
import core.stdc.stdlib;
void main(){
import std.stdio;
void foo()@safe{
auto ptr0=fancyMalloc(16);
writeln(ptr0.borrow((scope ptr){
return cast(int)ptr;
}));
fancyFree(ptr0);
}
foo();
void bar()@safe{
auto ptr0=fancyMalloc(16);
auto ptr1=ptr0.withAliasing.leak;
// 好,泄漏是安全的
writeln(ptr1);
}
bar();
void baz()@safe{
auto ptr0=fancyMalloc(16);
auto ptr1=ptr0.withAliasing;
// fancyFree(ptr1); // 错误,未隔离
}
baz();
void qux()@safe{
auto ptr0=fancyMalloc(16);
auto ptr1=ptr0.withAliasing;
// auto ptr2=ptr1.unsafeAddFlags!(PointerFlags.isolated); // 错误,不安全
// fancyFree(ptr2); // 好
}
qux();
void flarp()@trusted{
auto ptr0=fancyMalloc(16);
auto ptr1=ptr0.withAliasing;
auto ptr2=ptr1.unsafeAddFlags!(PointerFlags.isolated); // 好,可检查它是否工作
fancyFree(ptr2); // (ok)
}
flarp();
void bongo()@safe{
auto ptr1=function()@trusted{
auto ptr0=malloc(16);
return ptr0.unsafeAddFlags!(PointerFlags.mallocd|PointerFlags.isolated);
// 可以看出它是`malloced`和`隔离`的
}();
fancyFree(ptr1); // ok
}
bongo();
}
enum PointerFlags{
none,
mallocd=1,
isolated=2,
}
struct Pointer(T,PointerFlags flags){
private @system T* ptr;
Pointer!(T,flags&~PointerFlags.isolated) withAliasing()@trusted{
auto result=ptr;
ptr=null;
return typeof(return)(result);
}
static if(!(flags&PointerFlags.isolated)){
T* leak()@trusted{
auto result=ptr;
ptr=null;
return result;
}
}
auto borrow(R)(scope R delegate(scope T*)@safe dg)@trusted{
scope local=ptr;
ptr=null;
scope(exit) ptr=local;
return dg(local);
}
auto borrow(R)(scope R delegate(scope T*)@system dg)@system{
scope local=ptr;
ptr=null;
scope(exit) ptr=local;
return dg(ptr);
}
}
Pointer!(T,flags) unsafeAddFlags(PointerFlags flags,T)(ref T* ptr)@system{
auto result=ptr;
ptr=null;
return typeof(return)(result);
}
Pointer!(T,newFlags|oldFlags) unsafeAddFlags(PointerFlags newFlags,T,PointerFlags oldFlags)(ref Pointer!(T,oldFlags) ptr)@system{
auto result=ptr.ptr;
ptr.ptr=null;
return unsafeAddFlags!(newFlags|oldFlags)(result);
}
Pointer!(void, PointerFlags.mallocd|PointerFlags.isolated) fancyMalloc(size_t size)@trusted{
return typeof(return)(malloc(size));
}
void fancyFree(ref Pointer!(void, PointerFlags.mallocd|PointerFlags.isolated) ptr)@trusted{
if(!ptr.ptr) return;
free(ptr.ptr);
ptr.ptr=null;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现