luci的context

来源:互联网 发布:js 在tbody中添加行 编辑:程序博客网 时间:2024/05/22 14:31

  最近有个需求是在luci的交互界面上增加本地ipk的安装功能,因此要去研究下luci。

  luci的httpdispatch函数解析了路径信息后调用dispatch来处理请求。代码如下:

local stat, err = util.coxpcall(function()dispatch(context.request)end, error500)


  luci的主体部分正是dispatch,dispatch中有个很重要的表context。很多重要的处理都跟他有关。所以先搞清楚这个数据的话,应该对理解dispatch这个函数有很大的帮组。

 1、 context

context = util.threadlocal()

function threadlocal(tbl)return setmetatable(tbl or {}, tl_meta)end
  从上面的代码可以得出的结论是context是一个表,而且它的元表是tl_meta。

2、 tl_meta

local tl_meta = {__mode = "k",__index = function(self, key)local t = rawget(self, coxpt[coroutine.running()] or coroutine.running() or 0)return t and t[key]end,__newindex = function(self, key, value)local c = coxpt[coroutine.running()] or coroutine.running() or 0local r = rawget(self, c)if not r thenrawset(self, c, { [key] = value })elser[key] = valueendend}

  __mode="k"表示tl_meta这个表的弱引用类型是是key的弱引用。这个字段涉及lua的垃圾回收机制,垃圾回收应该对运行的逻辑关系没什么关系,暂且不管。

  下面的__index用于查询,__newindex用于更新。也就是说,如果对一个表不存在的元素进行访问,就会执行__index里面的代码。如果对一个表不存在的元素进行赋值的时候,就会执行__newindex里面的代码。

2.1、__newindex

  因为对于一个新定义的表来说,首先操作的赋值操作,所以先分析__newindex。

local c = coxpt[coroutine.running()] or coroutine.running() or 0

coroutine.running()在lua5.2的手册里的定义是:返回当前运行的协程及一个布尔值,如果布尔值为 true 则表示当前运行的协程为主协
程。

所以这个c的值就是当前运行的协程号。实际代码中打印的结果是:thread: 0x9bd4678,thread类型的数据。

local r = rawget(self, c)if not r thenrawset(self, c, { [key] = value })elser[key] = valueend
 在第一次运行的时候,所以在self这个表中是nil,所以下面的if语句执行的就是:

rawset(self, c, { [key] = value })
  把self的c这个index下的值设置为一个表,表的内容就是调用者所给的名/值对。

  然后之后调用的时候,如果是同一个协程的话,就直接修改值,也就是if语句中的else部分的代码。

这么做的用意是给每个协程都提供一个位置,从而让每个协程都操作自己的空间,而不会影响别的协程的空间。

2.2、__index

  与__newindex类似的,先获取每个协程自己的空间的key-value,没有的话直接返回key-value


2.3 coxpt[coroutine.running()]

   前面的内容还没提到coxpt[coroutine.running()]的作用。在luci代码中对coxpt这个表的赋值操作只有一处:

function coxpcall(f, err, ...)local res, co = oldpcall(coroutine.create, f)if not res thenlocal params = {...}local newf = function() return f(unpack(params)) endco = coroutine.create(newf)endlocal c = coroutine.running()coxpt[co] = coxpt[c] or c or 0return performResume(err, co, ...)end
    oldpcall在程序中被赋值成pcall。lua5.2对pcall的解释是:

pcall在保护模式下用指定的参数调用函数 f。 这意味着在 f 中发生的任何错误都不会被传递 ,相反,pcall 捕获错误并返回一个状态码。它的第一个返回值是一个状态码(一个布尔值) ,如果为 true 则表示没有错误发生,调用成功。这种情况下,pcall 还会返回 f 函数的所有返回值,这些返回值在状态码之后被返回。如果发生了错误,pcall 返回  e false 及错识消息

  所以res,co的值分别对应的是pcall的状态码和coroutine.create()所返回的一个“thread”的对象。当oldpcall调用出错的情况下,函数进入if语句重新创建一个协程。所以这个coxpcall调用的一个目的是增加程序的健壮性。

  给返回当前的协程号返回给c,这里说明一点的是如果是同一个协程那么coroutine.running()和coroutine.create()返回的协程号是一样的(经过程序验证)。

  coxpt[co] = coxpt[c] or c or 0 这条表达式就把协程号赋值给coxpt[co]。再回到前面的计算索引的表达式:

   local c = coxpt[coroutine.running()] or coroutine.running() or 0   
  就可以知道只要在对contex操作之前先执行2.3的这个函数调用就能够直接查表获得当前的协程号了,coxpt的作用在于提高程序的运行效率。

  最后return performResume(err, co, ...) 这个函数调用的意义是进行错误的回溯及保证co这个协程处理完成进入状态“dead"


  总结:

  context是一个表,它的特殊之处在于它可以为不同的协程分配不同的索引,来实现协程之间的访问内存隔离。此外为了提高效率,context还有一个用来存放索引号的coxpt的表。

 



  

 


0 0
原创粉丝点击