编写安全代码:避免奇怪的逻辑引发的bug
2016-01-21 09:31
211 查看
作者:gfree.wind@gmail.com
博客:blog.focus-linux.net linuxfocus.blog.chinaunix.net
本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
======================================================================================================
本文来自今天修改的一个Bug。Bug的产生完全是由于一段不合理或者奇怪的逻辑引发的。代码大致如下:
void reset_res(res_config, new_res)
{
old_res = res_config->res;
res_config->res
= new_res;
barrier();
free_res(old_res);
}
int modify_config()
{
new_res_config = duplicate(old_res_config);
new_res = new_res();
reset_res(new_res_config, new_res)
config->res_config
= new_res_config;
barrier();
free_res_config(old_res_config);
}
其中barrier可以保证所有的工作线程完成了一个循环。在reset_res中的barrier后,就保证了没有任何线程引用old_res这一地址,这样后面就可以安全的调用free_res释放old_res指向的资源了。而modify_config中的barrier同样保证了barrier后,没有任何线程再引用old_res_config了。
看上去代码写得很严谨,甚至使用了barrier保证了资源的安全释放。这样保证了不使用任何锁的条件下,完成了配置的变化。看上去很美很不错,然而却由于modify_config的不合理甚至有些奇怪的逻辑,导致了bug的产生。
下面说一下bug是如何发生的:
1. modify_config用于不阻塞工作线程的条件下,修改配置;
2. 为了不阻塞工作线程,利用替换指针和barrier来完成配置的修改;
3. new_res_config = duplicate(old_res_config)复制旧的配置;
4. 配置不同,需要新的资源new_res,然后调用reset_res完成res的替换和释放;
5. config->res_config = new_res_config; barrier()完成配置的更新;
Bug就出现在第三步。调用duplicate后,new_res_config和res_config指向了同一份资源,然后在reset_res中,这份资源被释放,而new_res_config指向了新的资源。这时bug出现了。因为目前的应用的配置仍然是res_config,其仍然指向旧资源,然而旧资源已经被释放。
那么如何修改这个Bug呢?将第5步放到reset_res之前,代码示意如下:
config->res_config
= new_res_config;
barrier();
reset_res(new_res_config, new_res)
虽然这样更正了bug。但是我觉得这样的逻辑看起来很怪。为啥?因为在更新了新的配置后,却又调用了reset_res去更新资源。这相当于new_res_config在还未准备完毕后,就更新了配置,这不免有些奇怪。
正常的逻辑应该是什么样子呢?
new_res_config = duplicate(old_config)只复制配置,不复制资源指针或者进行深拷贝;
reset_res(new_res_config, new_res) 获得新的资源赋给new_res_config;这里甚至都不需要barrier
config->res_config = new_res_config;
barrier()
其实这个bug的产生完全是由于之前错误的逻辑引起的,或者说是不好的编码风格。根源出自于duplicate函数,它只是一个memcpy,对于资源如指针来说,仅仅是一个浅拷贝。
博客:blog.focus-linux.net linuxfocus.blog.chinaunix.net
本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
======================================================================================================
本文来自今天修改的一个Bug。Bug的产生完全是由于一段不合理或者奇怪的逻辑引发的。代码大致如下:
void reset_res(res_config, new_res)
{
old_res = res_config->res;
res_config->res
= new_res;
barrier();
free_res(old_res);
}
int modify_config()
{
new_res_config = duplicate(old_res_config);
new_res = new_res();
reset_res(new_res_config, new_res)
config->res_config
= new_res_config;
barrier();
free_res_config(old_res_config);
}
其中barrier可以保证所有的工作线程完成了一个循环。在reset_res中的barrier后,就保证了没有任何线程引用old_res这一地址,这样后面就可以安全的调用free_res释放old_res指向的资源了。而modify_config中的barrier同样保证了barrier后,没有任何线程再引用old_res_config了。
看上去代码写得很严谨,甚至使用了barrier保证了资源的安全释放。这样保证了不使用任何锁的条件下,完成了配置的变化。看上去很美很不错,然而却由于modify_config的不合理甚至有些奇怪的逻辑,导致了bug的产生。
下面说一下bug是如何发生的:
1. modify_config用于不阻塞工作线程的条件下,修改配置;
2. 为了不阻塞工作线程,利用替换指针和barrier来完成配置的修改;
3. new_res_config = duplicate(old_res_config)复制旧的配置;
4. 配置不同,需要新的资源new_res,然后调用reset_res完成res的替换和释放;
5. config->res_config = new_res_config; barrier()完成配置的更新;
Bug就出现在第三步。调用duplicate后,new_res_config和res_config指向了同一份资源,然后在reset_res中,这份资源被释放,而new_res_config指向了新的资源。这时bug出现了。因为目前的应用的配置仍然是res_config,其仍然指向旧资源,然而旧资源已经被释放。
那么如何修改这个Bug呢?将第5步放到reset_res之前,代码示意如下:
config->res_config
= new_res_config;
barrier();
reset_res(new_res_config, new_res)
虽然这样更正了bug。但是我觉得这样的逻辑看起来很怪。为啥?因为在更新了新的配置后,却又调用了reset_res去更新资源。这相当于new_res_config在还未准备完毕后,就更新了配置,这不免有些奇怪。
正常的逻辑应该是什么样子呢?
new_res_config = duplicate(old_config)只复制配置,不复制资源指针或者进行深拷贝;
reset_res(new_res_config, new_res) 获得新的资源赋给new_res_config;这里甚至都不需要barrier
config->res_config = new_res_config;
barrier()
其实这个bug的产生完全是由于之前错误的逻辑引起的,或者说是不好的编码风格。根源出自于duplicate函数,它只是一个memcpy,对于资源如指针来说,仅仅是一个浅拷贝。
相关文章推荐
- java中的断言
- C语言排序算法
- 用C语言实现大小端判断
- 浅谈C# 多态的魅力(虚方法,抽象,接口实现)
- 第一节程序大赛代码和心得(三):复杂问题平静心态,步步为营
- 关于C++中的'extern "C"'
- Qt学习2
- (Qt学习1)Qt环境搭建
- C++中cin/cout高级格式化操作
- UBOOT-2010.06 移植 支持 下载yaffs2文件系统 (九)
- Java String类
- 彻底解决兼容问题Windows_7安装_Visual_C++_6.0(VC6)
- Qt学习之路(4):初探信号槽
- 编写安全代码:小心使用浮点数
- 一些经典排序算法的实现(C++实现)
- Java笔记16:多线程共享数据
- C语言复杂指针说明
- 关于C语言指针的用法
- 面试中经常碰到的C语言算法
- 使用Eclipse开发Android报错:Project has no target set. Edit the project properties to set one.