可扩充类型(handle,struct)
扩充类型object
,domain
,coroutine
,timer
,socket
,share_value
,sync_object
等类型都是扩充自handle,
而扩充类型vector2
,vector3
,vector4
,matrix4
等类型都是扩充自struct。
如果需要扩充自己的类型,则应继承自handle或struct。
handle和struct差别在于:
- 如果希望可以跨域访问,则应继承handle;若只在域内访问 则继承struct。
- handle通过HandlePtr访问,赋值时只copy id。struct每次在赋 值时会直接copy整个结构。
handle
handle翻译为“句柄”,也就是“把手”的意思,可以理解为用来方便控制某个东西的东西。
Handle用于管理某个Resource
- 通过引用计数确保Resource指针有效
- 通过Handle上的Lock确保Resource相关操作的线程安全
- HandlePtr 内部用一个64bit的整形表示,包含类型,索引分页,索引偏移,版本 信息
- 创建handle的时候,引用计数初始值为1
Handle的组织结构
字段 | 大小 | 描述 |
---|---|---|
m_state | 1Byte | Handle的状态,有UNUSED,OPENED,CLOSED,这个只是Handle状态,Resource自己应该也有状态 |
m_ref | 4Byte | Handle的引用计数,创建时初始值为1 |
m_handle_id | 8Byte | Handle对应的ID,64 位非负整形,包含Handle的类型/索引/版本信息 |
m_resource | 8Byte | Handle对应的资源指针 |
m_lock | 不定 | 不同的Lock实现大小可能不同,用来实现资源的锁定和解锁,确保共享数据线程安全 |
m_entry | 不定 | 额外的自定义信息,如Object就会额外存放domain,看确定是否在当前域,这样可以免去引用计数和锁 |
Handle状态说明
- handle有四个内置状态:INVALID, INITIALIZING, USING, CLOSED
- INVALID为初始状态,此时的handle还没被分配,不可使用
- 创建一个新的句柄后,句柄就处于INITIALIZING状态,句柄初始化中,ref必定大于0,此时不能被close
- 对句柄进行Open操作后,句柄就处于USING状态,句柄可以被正常使用,ref必定大于0,此时可以被close
- 关闭句柄后(调用句柄的close)句柄处于CLOSED状态,此后所有cast_opened_handle将失败,当ref变为0时,清除并回收句柄
Handle生命周期
- 分配阶段 new出一个资源后,通过allocate_handle分配一个资源对应的handle
- 初始化段 得到handle后(此时不需要通过dummy锁住),进行初始化操作
- 使用阶段 通过handle的open方法 ,将handle和某个资源绑定,这个open是指handle的open并不一定是资源真正的open,资源本身如果需要单独的打开流程需要自己处理(一般应在此open操作之前完成),自身的状态也需要自己处理,不应该依赖handle的状态
- 关闭阶段 通过handle的close方法,主动减少一次引用计数,并将状态设为CLOSED,close必须在lock范围内调用,此后所有cast_opened_handle操作将会失败。close时会调用close_resource函数。等到handle的引用计数减为0,则调用资源的free_resource函数,并回收句柄。close方法仅能在USING状态下使用 注意:资源的free_resource函数中不能有lock/unlock操作,否则引用计数改变会导致再次关闭
Handle操作流程
- 当我们需要在多线程环境下对某个资源进行操作的时候,需要使用Handle来保证线程安全
- 先通过Handle::cast_opened_handle(handle_id)在栈上获取Handle的dummy对象
- 刚获取的时候,只是引用计数+1了,而并未进行lock
- 我们可以对资源进行lock和unlock操作来确保某段读写资源的代码不会重入
- 在中间过程中产生的任何返回或异常会导致栈回溯,dummy对象被自动销毁,自动进行unref和unlock
- 如果需要在退出函数前就把引用计数归还,可以手动调用dummy的unref减引用计数,此后dummy将失效
- 正常情况下,应该让dummy自动销毁,在析构中unref
- 资源的打开要自己实现一些逻辑,涉及到ref的加减,要格外小心
- 资源关闭可以使用通用的关闭方法Handle::close_handle_resource,如果特殊需求也可以自己实现
- Handle::take_raw_handle(id) 可以获取到Handle对象的引用,一般情况不建议使用,一旦过程中返回或抛异常,可能会导致没有正确unlock或者unref
用Handle机制进行Object管理
- get_entry_by_id获取object相关信息可以通过get_entry来实现,不锁、不改变引用计数,只查看entry数据
- object的Lock对象中封装了它对应的域
- invoke操作的时候可能不能走通用的lock/unlock,对效率要求较高,且需求不满足,还是维持以前的逻辑
- 对object进行一些其他操作时,比如某个外部函数要取object获取一些信息
- object的lock/unlock操作,会切域:
- lock : leave_domain -> push call context -> push domain context
- unlock : pop domain context -> pop call context -> check_gc
用Handle机制进行Domain管理
- 我们要确保domain的安全,确保可以正常删除domain
- 为每个Domain分配一个HandlePtr, create_domain时引用计数+1
- push domain context的时候需要引用计数+1
- pop domain context的时候引用计数-1
- delete_domain时引用计数-1
- Domain的 lock/unlock操作:
- lock : enter_domain
- unlock : leave_domain