当前位置: 首页 > news >正文

处理由引用计数引起的泄漏

网游服务器的逻辑一般来说比较复杂,而且在很多情况下还使用了多线程,因此使用基于引用计数的智能指针能很大程度的减少内存泄漏和对象失效等问提.

但是基于引用计数的指针在很多情况下也会产生另一种情况的泄漏,例如:网游中有一个代表角色的类型character,角色的对象在网游中可以说是最常见的对象

之一了,与几乎所有的游戏逻辑都有关系,因此,一个角色对象可能会存在于很多容器,或被其它的对象所持有,为了保证角色对象的有效性,常常会使用基

于引用计数的智能指针来存放角色对象。问题由此产生了,例如当一个角色离开地图,我们就需要把所有指向这个角色的智能指针清0,稍有不甚,漏掉一个都

会导致资源无法释放,而且要找到具体还被哪些地方持有是相当麻烦的事情.

在我们项目中,处理这种情况的做法是,不使用智能指针来存放对象,而是采用了另外一种叫做ident的对象.对象被创建之后,只由一个容器持有其原始对象,

其它的所有外部引用持有的都是那个由原始对象产生的ident对像.当逻辑需要使用原始对象时,通过ident转换成原始对象,如果原始对象依旧有效则返回原始

对象,否则,返回NULL。首先,原始对象是基于引用计数的对象,其中有一个64位的identity成员,其高32位是一个全局计数器的值,低32位是一个时间戳.对象被创建的时候,

identity被正确的初始化,被销毁时将identity置0,这样,两个对象identity拥有相同值的概率是相当低的.

然后看下ident对象,只有一备份的identity和一个指向原始对象的指针,通过make_ident函数,可以通过一个原始对象的指针产生一个ident对象.然后,

可以通过cast_2_refbase将一个ident对象转换回原始指针,如果转换成功,原始对象的引用加1,防止对象正在使用的时候被其它线程释放掉.只要在

使用完毕后调用ref_decrease清理即可.

refbase.h

 

#ifndef _REFBASE_H
#define _REFBASE_H

#include <stdint.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include "atomic.h"

extern atomic_32_t global_counter;

struct refbase
{
        atomic_32_t refcount;
        atomic_64_t identity;
        atomic_32_t flag;
        void (*destroyer)(void*);
};

void ref_init(struct refbase *r,void (*destroyer)(void*),int32_t initcount);

static inline atomic_32_t ref_increase(struct refbase *r)
{
    return ATOMIC_INCREASE(&r->refcount);
}

static inline atomic_32_t ref_decrease(struct refbase *r)
{
    atomic_32_t count;
    if((count = ATOMIC_DECREASE(&r->refcount)) == 0){
                r->identity = 0;
                _FENCE;
        int32_t c = 0;
        for(;;){
            if(COMPARE_AND_SWAP(&r->flag,0,1))
                break;
            if(c < 4000){
                ++c;
                __asm__("pause");
            }else{
                struct timespec ts = { 0, 500000 };
                nanosleep(&ts, NULL);
            }
        }
                r->destroyer(r);
        }
    return count;
}

typedef struct ident
{
        uint64_t identity;
        struct refbase *ptr;
}ident;

static inline ident make_ident(struct refbase *ptr)
{
        ident _ident = {ptr->identity,ptr};
        return _ident;
}

static inline ident make_empty_ident()
{
    ident _ident = {0,NULL};
        return _ident;
}

static inline struct refbase *cast_2_refbase(ident _ident)
{
    while(_ident.identity == _ident.ptr->identity)
        {
                if(COMPARE_AND_SWAP(&_ident.ptr->flag,0,1))
                {
            struct refbase *ptr = NULL;
            if(_ident.identity == _ident.ptr->identity &&
               ref_increase(_ident.ptr) > 0)
                    ptr = _ident.ptr;
            _FENCE;
            _ident.ptr->flag = 0;
            return ptr;
                }
        }
    return NULL;
}

static inline int32_t is_vaild_ident(ident _ident)
{
        if(!_ident.ptr || !_ident.identity) return 0;
        return 1;
}

#define TO_IDENT(OTHER_IDENT) (*(ident*)&OTHER_IDENT)

#endif

 

refbase.c

#include "refbase.h"
#include "SysTime.h"

atomic_32_t global_counter = 0;

void ref_init(struct refbase *r,void (*destroyer)(void*),int32_t initcount)
{
    r->destroyer = destroyer;
    r->identity = ATOMIC_INCREASE(&global_counter);
    r->identity <<= 32;
    r->identity += GetSystemMs();
    r->refcount = initcount;
}

 

大致处理逻辑如下:

ident _ident;
struct ref_base *atker = cast_2_refbase(_ident);
if(atker)
{
    //对象依然有效,执行某些逻辑 ......
    ref_decrease(&atker);//不再使用了,减少计数  
}
else
{
       //原对象已经失效
}

相关文章:

  • javascript操作JSON
  • 五大内存分区,堆与栈的区别(转)
  • media query
  • 电脑维修常见软件工具
  • 在ArcMap中将 DEM 显示为晕渲地貌效果
  • [程序猿感悟] 风雨20年:我所积累的20条编程经验
  • virtualbox centos安装增强工具
  • 单例模式(Singleton)
  • 桌面3D----埃舍尔多面体
  • hadoop的使用
  • apache所有模块详解
  • 【HeadFirst 设计模式学习笔记】5 单例模式
  • BlackHole开发日记-2012-12-16
  • flex 监听浏览器关闭或刷新
  • linux setenv 用法
  • 2017 年终总结 —— 在路上
  • HTML5新特性总结
  • Laravel 中的一个后期静态绑定
  • Python实现BT种子转化为磁力链接【实战】
  • Rancher-k8s加速安装文档
  • Swoft 源码剖析 - 代码自动更新机制
  • use Google search engine
  • 基于遗传算法的优化问题求解
  • 坑!为什么View.startAnimation不起作用?
  • 什么软件可以提取视频中的音频制作成手机铃声
  • 它承受着该等级不该有的简单, leetcode 564 寻找最近的回文数
  • 微信开源mars源码分析1—上层samples分析
  • 小程序、APP Store 需要的 SSL 证书是个什么东西?
  • 学习JavaScript数据结构与算法 — 树
  • 【运维趟坑回忆录】vpc迁移 - 吃螃蟹之路
  • 树莓派用上kodexplorer也能玩成私有网盘
  • ​软考-高级-系统架构设计师教程(清华第2版)【第12章 信息系统架构设计理论与实践(P420~465)-思维导图】​
  • #!/usr/bin/python与#!/usr/bin/env python的区别
  • (03)光刻——半导体电路的绘制
  • (1)(1.13) SiK无线电高级配置(五)
  • (11)工业界推荐系统-小红书推荐场景及内部实践【粗排三塔模型】
  • (2)MFC+openGL单文档框架glFrame
  • (6)STL算法之转换
  • (cos^2 X)的定积分,求积分 ∫sin^2(x) dx
  • (java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序】超详细~~
  • (LeetCode C++)盛最多水的容器
  • (附源码)计算机毕业设计SSM疫情居家隔离服务系统
  • (六)什么是Vite——热更新时vite、webpack做了什么
  • (三分钟)速览传统边缘检测算子
  • (转) Android中ViewStub组件使用
  • (转)nsfocus-绿盟科技笔试题目
  • (转)总结使用Unity 3D优化游戏运行性能的经验
  • ./mysql.server: 没有那个文件或目录_Linux下安装MySQL出现“ls: /var/lib/mysql/*.pid: 没有那个文件或目录”...
  • .NET 6 Mysql Canal (CDC 增量同步,捕获变更数据) 案例版
  • .NET使用HttpClient以multipart/form-data形式post上传文件及其相关参数
  • @Bean, @Component, @Configuration简析
  • @Transactional 详解
  • @WebServiceClient注解,wsdlLocation 可配置
  • @取消转义
  • [ vulhub漏洞复现篇 ] GhostScript 沙箱绕过(任意命令执行)漏洞CVE-2019-6116