欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

luci的context

程序员文章站 2022-06-09 18:07:05
...

luci的context

  最近有个需求是在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 0
		local r = rawget(self, c)
		if not r then
			rawset(self, c, { [key] = value })
		else
			r[key] = value
		end
	end
}

  __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 then
			rawset(self, c, { [key] = value })
		else
			r[key] = value
		end
 在第一次运行的时候,所以在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 then
		local params = {...}
		local newf = function() return f(unpack(params)) end
		co = coroutine.create(newf)
	end
	local c = coroutine.running()
	coxpt[co] = coxpt[c] or c or 0

	return 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的表。

 



  

 


相关标签: Openwrt