gc.lua 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624
  1. -- $Id: gc.lua,v 1.72 2016/11/07 13:11:28 roberto Exp $
  2. -- See Copyright Notice in file all.lua
  3. print('testing garbage collection')
  4. local debug = require"debug"
  5. collectgarbage()
  6. assert(collectgarbage("isrunning"))
  7. local function gcinfo () return collectgarbage"count" * 1024 end
  8. -- test weird parameters
  9. do
  10. -- save original parameters
  11. local a = collectgarbage("setpause", 200)
  12. local b = collectgarbage("setstepmul", 200)
  13. local t = {0, 2, 10, 90, 500, 5000, 30000, 0x7ffffffe}
  14. for i = 1, #t do
  15. local p = t[i]
  16. for j = 1, #t do
  17. local m = t[j]
  18. collectgarbage("setpause", p)
  19. collectgarbage("setstepmul", m)
  20. collectgarbage("step", 0)
  21. collectgarbage("step", 10000)
  22. end
  23. end
  24. -- restore original parameters
  25. collectgarbage("setpause", a)
  26. collectgarbage("setstepmul", b)
  27. collectgarbage()
  28. end
  29. _G["while"] = 234
  30. limit = 5000
  31. local function GC1 ()
  32. local u
  33. local b -- must be declared after 'u' (to be above it in the stack)
  34. local finish = false
  35. u = setmetatable({}, {__gc = function () finish = true end})
  36. b = {34}
  37. repeat u = {} until finish
  38. assert(b[1] == 34) -- 'u' was collected, but 'b' was not
  39. finish = false; local i = 1
  40. u = setmetatable({}, {__gc = function () finish = true end})
  41. repeat i = i + 1; u = tostring(i) .. tostring(i) until finish
  42. assert(b[1] == 34) -- 'u' was collected, but 'b' was not
  43. finish = false
  44. u = setmetatable({}, {__gc = function () finish = true end})
  45. repeat local i; u = function () return i end until finish
  46. assert(b[1] == 34) -- 'u' was collected, but 'b' was not
  47. end
  48. local function GC2 ()
  49. local u
  50. local finish = false
  51. u = {setmetatable({}, {__gc = function () finish = true end})}
  52. b = {34}
  53. repeat u = {{}} until finish
  54. assert(b[1] == 34) -- 'u' was collected, but 'b' was not
  55. finish = false; local i = 1
  56. u = {setmetatable({}, {__gc = function () finish = true end})}
  57. repeat i = i + 1; u = {tostring(i) .. tostring(i)} until finish
  58. assert(b[1] == 34) -- 'u' was collected, but 'b' was not
  59. finish = false
  60. u = {setmetatable({}, {__gc = function () finish = true end})}
  61. repeat local i; u = {function () return i end} until finish
  62. assert(b[1] == 34) -- 'u' was collected, but 'b' was not
  63. end
  64. local function GC() GC1(); GC2() end
  65. contCreate = 0
  66. print('tables')
  67. while contCreate <= limit do
  68. local a = {}; a = nil
  69. contCreate = contCreate+1
  70. end
  71. a = "a"
  72. contCreate = 0
  73. print('strings')
  74. while contCreate <= limit do
  75. a = contCreate .. "b";
  76. a = string.gsub(a, '(%d%d*)', string.upper)
  77. a = "a"
  78. contCreate = contCreate+1
  79. end
  80. contCreate = 0
  81. a = {}
  82. print('functions')
  83. function a:test ()
  84. while contCreate <= limit do
  85. load(string.format("function temp(a) return 'a%d' end", contCreate), "")()
  86. assert(temp() == string.format('a%d', contCreate))
  87. contCreate = contCreate+1
  88. end
  89. end
  90. a:test()
  91. -- collection of functions without locals, globals, etc.
  92. do local f = function () end end
  93. print("functions with errors")
  94. prog = [[
  95. do
  96. a = 10;
  97. function foo(x,y)
  98. a = sin(a+0.456-0.23e-12);
  99. return function (z) return sin(%x+z) end
  100. end
  101. local x = function (w) a=a+w; end
  102. end
  103. ]]
  104. do
  105. local step = 1
  106. if _soft then step = 13 end
  107. for i=1, string.len(prog), step do
  108. for j=i, string.len(prog), step do
  109. pcall(load(string.sub(prog, i, j), ""))
  110. end
  111. end
  112. end
  113. foo = nil
  114. print('long strings')
  115. x = "01234567890123456789012345678901234567890123456789012345678901234567890123456789"
  116. assert(string.len(x)==80)
  117. s = ''
  118. n = 0
  119. k = math.min(300, (math.maxinteger // 80) // 2)
  120. while n < k do s = s..x; n=n+1; j=tostring(n) end
  121. assert(string.len(s) == k*80)
  122. s = string.sub(s, 1, 10000)
  123. s, i = string.gsub(s, '(%d%d%d%d)', '')
  124. assert(i==10000 // 4)
  125. s = nil
  126. x = nil
  127. assert(_G["while"] == 234)
  128. print("steps")
  129. print("steps (2)")
  130. local function dosteps (siz)
  131. assert(not collectgarbage("isrunning"))
  132. collectgarbage()
  133. assert(not collectgarbage("isrunning"))
  134. local a = {}
  135. for i=1,100 do a[i] = {{}}; local b = {} end
  136. local x = gcinfo()
  137. local i = 0
  138. repeat -- do steps until it completes a collection cycle
  139. i = i+1
  140. until collectgarbage("step", siz)
  141. assert(gcinfo() < x)
  142. return i
  143. end
  144. collectgarbage"stop"
  145. if not _port then
  146. -- test the "size" of basic GC steps (whatever they mean...)
  147. assert(dosteps(0) > 10)
  148. assert(dosteps(10) < dosteps(2))
  149. end
  150. -- collector should do a full collection with so many steps
  151. assert(dosteps(20000) == 1)
  152. assert(collectgarbage("step", 20000) == true)
  153. assert(collectgarbage("step", 20000) == true)
  154. assert(not collectgarbage("isrunning"))
  155. collectgarbage"restart"
  156. assert(collectgarbage("isrunning"))
  157. if not _port then
  158. -- test the pace of the collector
  159. collectgarbage(); collectgarbage()
  160. local x = gcinfo()
  161. collectgarbage"stop"
  162. assert(not collectgarbage("isrunning"))
  163. repeat
  164. local a = {}
  165. until gcinfo() > 3 * x
  166. collectgarbage"restart"
  167. assert(collectgarbage("isrunning"))
  168. repeat
  169. local a = {}
  170. until gcinfo() <= x * 2
  171. end
  172. print("clearing tables")
  173. lim = 15
  174. a = {}
  175. -- fill a with `collectable' indices
  176. for i=1,lim do a[{}] = i end
  177. b = {}
  178. for k,v in pairs(a) do b[k]=v end
  179. -- remove all indices and collect them
  180. for n in pairs(b) do
  181. a[n] = nil
  182. assert(type(n) == 'table' and next(n) == nil)
  183. collectgarbage()
  184. end
  185. b = nil
  186. collectgarbage()
  187. for n in pairs(a) do error'cannot be here' end
  188. for i=1,lim do a[i] = i end
  189. for i=1,lim do assert(a[i] == i) end
  190. print('weak tables')
  191. a = {}; setmetatable(a, {__mode = 'k'});
  192. -- fill a with some `collectable' indices
  193. for i=1,lim do a[{}] = i end
  194. -- and some non-collectable ones
  195. for i=1,lim do a[i] = i end
  196. for i=1,lim do local s=string.rep('@', i); a[s] = s..'#' end
  197. collectgarbage()
  198. local i = 0
  199. for k,v in pairs(a) do assert(k==v or k..'#'==v); i=i+1 end
  200. assert(i == 2*lim)
  201. a = {}; setmetatable(a, {__mode = 'v'});
  202. a[1] = string.rep('b', 21)
  203. collectgarbage()
  204. assert(a[1]) -- strings are *values*
  205. a[1] = nil
  206. -- fill a with some `collectable' values (in both parts of the table)
  207. for i=1,lim do a[i] = {} end
  208. for i=1,lim do a[i..'x'] = {} end
  209. -- and some non-collectable ones
  210. for i=1,lim do local t={}; a[t]=t end
  211. for i=1,lim do a[i+lim]=i..'x' end
  212. collectgarbage()
  213. local i = 0
  214. for k,v in pairs(a) do assert(k==v or k-lim..'x' == v); i=i+1 end
  215. assert(i == 2*lim)
  216. a = {}; setmetatable(a, {__mode = 'vk'});
  217. local x, y, z = {}, {}, {}
  218. -- keep only some items
  219. a[1], a[2], a[3] = x, y, z
  220. a[string.rep('$', 11)] = string.rep('$', 11)
  221. -- fill a with some `collectable' values
  222. for i=4,lim do a[i] = {} end
  223. for i=1,lim do a[{}] = i end
  224. for i=1,lim do local t={}; a[t]=t end
  225. collectgarbage()
  226. assert(next(a) ~= nil)
  227. local i = 0
  228. for k,v in pairs(a) do
  229. assert((k == 1 and v == x) or
  230. (k == 2 and v == y) or
  231. (k == 3 and v == z) or k==v);
  232. i = i+1
  233. end
  234. assert(i == 4)
  235. x,y,z=nil
  236. collectgarbage()
  237. assert(next(a) == string.rep('$', 11))
  238. -- 'bug' in 5.1
  239. a = {}
  240. local t = {x = 10}
  241. local C = setmetatable({key = t}, {__mode = 'v'})
  242. local C1 = setmetatable({[t] = 1}, {__mode = 'k'})
  243. a.x = t -- this should not prevent 't' from being removed from
  244. -- weak table 'C' by the time 'a' is finalized
  245. setmetatable(a, {__gc = function (u)
  246. assert(C.key == nil)
  247. assert(type(next(C1)) == 'table')
  248. end})
  249. a, t = nil
  250. collectgarbage()
  251. collectgarbage()
  252. assert(next(C) == nil and next(C1) == nil)
  253. C, C1 = nil
  254. -- ephemerons
  255. local mt = {__mode = 'k'}
  256. a = {{10},{20},{30},{40}}; setmetatable(a, mt)
  257. x = nil
  258. for i = 1, 100 do local n = {}; a[n] = {k = {x}}; x = n end
  259. GC()
  260. local n = x
  261. local i = 0
  262. while n do n = a[n].k[1]; i = i + 1 end
  263. assert(i == 100)
  264. x = nil
  265. GC()
  266. for i = 1, 4 do assert(a[i][1] == i * 10); a[i] = nil end
  267. assert(next(a) == nil)
  268. local K = {}
  269. a[K] = {}
  270. for i=1,10 do a[K][i] = {}; a[a[K][i]] = setmetatable({}, mt) end
  271. x = nil
  272. local k = 1
  273. for j = 1,100 do
  274. local n = {}; local nk = k%10 + 1
  275. a[a[K][nk]][n] = {x, k = k}; x = n; k = nk
  276. end
  277. GC()
  278. local n = x
  279. local i = 0
  280. while n do local t = a[a[K][k]][n]; n = t[1]; k = t.k; i = i + 1 end
  281. assert(i == 100)
  282. K = nil
  283. GC()
  284. -- assert(next(a) == nil)
  285. -- testing errors during GC
  286. do
  287. collectgarbage("stop") -- stop collection
  288. local u = {}
  289. local s = {}; setmetatable(s, {__mode = 'k'})
  290. setmetatable(u, {__gc = function (o)
  291. local i = s[o]
  292. s[i] = true
  293. assert(not s[i - 1]) -- check proper finalization order
  294. if i == 8 then error("here") end -- error during GC
  295. end})
  296. for i = 6, 10 do
  297. local n = setmetatable({}, getmetatable(u))
  298. s[n] = i
  299. end
  300. assert(not pcall(collectgarbage))
  301. for i = 8, 10 do assert(s[i]) end
  302. for i = 1, 5 do
  303. local n = setmetatable({}, getmetatable(u))
  304. s[n] = i
  305. end
  306. collectgarbage()
  307. for i = 1, 10 do assert(s[i]) end
  308. getmetatable(u).__gc = false
  309. -- __gc errors with non-string messages
  310. setmetatable({}, {__gc = function () error{} end})
  311. local a, b = pcall(collectgarbage)
  312. assert(not a and type(b) == "string" and string.find(b, "error in __gc"))
  313. end
  314. print '+'
  315. -- testing userdata
  316. if T==nil then
  317. (Message or print)('\n >>> testC not active: skipping userdata GC tests <<<\n')
  318. else
  319. local function newproxy(u)
  320. return debug.setmetatable(T.newuserdata(0), debug.getmetatable(u))
  321. end
  322. collectgarbage("stop") -- stop collection
  323. local u = newproxy(nil)
  324. debug.setmetatable(u, {__gc = true})
  325. local s = 0
  326. local a = {[u] = 0}; setmetatable(a, {__mode = 'vk'})
  327. for i=1,10 do a[newproxy(u)] = i end
  328. for k in pairs(a) do assert(getmetatable(k) == getmetatable(u)) end
  329. local a1 = {}; for k,v in pairs(a) do a1[k] = v end
  330. for k,v in pairs(a1) do a[v] = k end
  331. for i =1,10 do assert(a[i]) end
  332. getmetatable(u).a = a1
  333. getmetatable(u).u = u
  334. do
  335. local u = u
  336. getmetatable(u).__gc = function (o)
  337. assert(a[o] == 10-s)
  338. assert(a[10-s] == nil) -- udata already removed from weak table
  339. assert(getmetatable(o) == getmetatable(u))
  340. assert(getmetatable(o).a[o] == 10-s)
  341. s=s+1
  342. end
  343. end
  344. a1, u = nil
  345. assert(next(a) ~= nil)
  346. collectgarbage()
  347. assert(s==11)
  348. collectgarbage()
  349. assert(next(a) == nil) -- finalized keys are removed in two cycles
  350. end
  351. -- __gc x weak tables
  352. local u = setmetatable({}, {__gc = true})
  353. -- __gc metamethod should be collected before running
  354. setmetatable(getmetatable(u), {__mode = "v"})
  355. getmetatable(u).__gc = function (o) os.exit(1) end -- cannot happen
  356. u = nil
  357. collectgarbage()
  358. local u = setmetatable({}, {__gc = true})
  359. local m = getmetatable(u)
  360. m.x = {[{0}] = 1; [0] = {1}}; setmetatable(m.x, {__mode = "kv"});
  361. m.__gc = function (o)
  362. assert(next(getmetatable(o).x) == nil)
  363. m = 10
  364. end
  365. u, m = nil
  366. collectgarbage()
  367. assert(m==10)
  368. -- errors during collection
  369. u = setmetatable({}, {__gc = function () error "!!!" end})
  370. u = nil
  371. assert(not pcall(collectgarbage))
  372. if not _soft then
  373. print("deep structures")
  374. local a = {}
  375. for i = 1,200000 do
  376. a = {next = a}
  377. end
  378. collectgarbage()
  379. end
  380. -- create many threads with self-references and open upvalues
  381. print("self-referenced threads")
  382. local thread_id = 0
  383. local threads = {}
  384. local function fn (thread)
  385. local x = {}
  386. threads[thread_id] = function()
  387. thread = x
  388. end
  389. coroutine.yield()
  390. end
  391. while thread_id < 1000 do
  392. local thread = coroutine.create(fn)
  393. coroutine.resume(thread, thread)
  394. thread_id = thread_id + 1
  395. end
  396. -- Create a closure (function inside 'f') with an upvalue ('param') that
  397. -- points (through a table) to the closure itself and to the thread
  398. -- ('co' and the initial value of 'param') where closure is running.
  399. -- Then, assert that table (and therefore everything else) will be
  400. -- collected.
  401. do
  402. local collected = false -- to detect collection
  403. collectgarbage(); collectgarbage("stop")
  404. do
  405. local function f (param)
  406. ;(function ()
  407. assert(type(f) == 'function' and type(param) == 'thread')
  408. param = {param, f}
  409. setmetatable(param, {__gc = function () collected = true end})
  410. coroutine.yield(100)
  411. end)()
  412. end
  413. local co = coroutine.create(f)
  414. assert(coroutine.resume(co, co))
  415. end
  416. -- Now, thread and closure are not reacheable any more;
  417. -- two collections are needed to break cycle
  418. collectgarbage()
  419. assert(not collected)
  420. collectgarbage()
  421. assert(collected)
  422. collectgarbage("restart")
  423. end
  424. do
  425. collectgarbage()
  426. collectgarbage"stop"
  427. local x = gcinfo()
  428. repeat
  429. for i=1,1000 do _ENV.a = {} end
  430. collectgarbage("step", 0) -- steps should not unblock the collector
  431. until gcinfo() > 2 * x
  432. collectgarbage"restart"
  433. end
  434. if T then -- tests for weird cases collecting upvalues
  435. local function foo ()
  436. local a = {x = 20}
  437. coroutine.yield(function () return a.x end) -- will run collector
  438. assert(a.x == 20) -- 'a' is 'ok'
  439. a = {x = 30} -- create a new object
  440. assert(T.gccolor(a) == "white") -- of course it is new...
  441. coroutine.yield(100) -- 'a' is still local to this thread
  442. end
  443. local t = setmetatable({}, {__mode = "kv"})
  444. collectgarbage(); collectgarbage('stop')
  445. -- create coroutine in a weak table, so it will never be marked
  446. t.co = coroutine.wrap(foo)
  447. local f = t.co() -- create function to access local 'a'
  448. T.gcstate("atomic") -- ensure all objects are traversed
  449. assert(T.gcstate() == "atomic")
  450. assert(t.co() == 100) -- resume coroutine, creating new table for 'a'
  451. assert(T.gccolor(t.co) == "white") -- thread was not traversed
  452. T.gcstate("pause") -- collect thread, but should mark 'a' before that
  453. assert(t.co == nil and f() == 30) -- ensure correct access to 'a'
  454. collectgarbage("restart")
  455. -- test barrier in sweep phase (advance cleaning of upvalue to white)
  456. local u = T.newuserdata(0) -- create a userdata
  457. collectgarbage()
  458. collectgarbage"stop"
  459. T.gcstate"atomic"
  460. T.gcstate"sweepallgc"
  461. local x = {}
  462. assert(T.gccolor(u) == "black") -- upvalue is "old" (black)
  463. assert(T.gccolor(x) == "white") -- table is "new" (white)
  464. debug.setuservalue(u, x) -- trigger barrier
  465. assert(T.gccolor(u) == "white") -- upvalue changed to white
  466. collectgarbage"restart"
  467. print"+"
  468. end
  469. if T then
  470. local debug = require "debug"
  471. collectgarbage("stop")
  472. local x = T.newuserdata(0)
  473. local y = T.newuserdata(0)
  474. debug.setmetatable(y, {__gc = true}) -- bless the new udata before...
  475. debug.setmetatable(x, {__gc = true}) -- ...the old one
  476. assert(T.gccolor(y) == "white")
  477. T.checkmemory()
  478. collectgarbage("restart")
  479. end
  480. if T then
  481. print("emergency collections")
  482. collectgarbage()
  483. collectgarbage()
  484. T.totalmem(T.totalmem() + 200)
  485. for i=1,200 do local a = {} end
  486. T.totalmem(0)
  487. collectgarbage()
  488. local t = T.totalmem("table")
  489. local a = {{}, {}, {}} -- create 4 new tables
  490. assert(T.totalmem("table") == t + 4)
  491. t = T.totalmem("function")
  492. a = function () end -- create 1 new closure
  493. assert(T.totalmem("function") == t + 1)
  494. t = T.totalmem("thread")
  495. a = coroutine.create(function () end) -- create 1 new coroutine
  496. assert(T.totalmem("thread") == t + 1)
  497. end
  498. -- create an object to be collected when state is closed
  499. do
  500. local setmetatable,assert,type,print,getmetatable =
  501. setmetatable,assert,type,print,getmetatable
  502. local tt = {}
  503. tt.__gc = function (o)
  504. assert(getmetatable(o) == tt)
  505. -- create new objects during GC
  506. local a = 'xuxu'..(10+3)..'joao', {}
  507. ___Glob = o -- ressurect object!
  508. setmetatable({}, tt) -- creates a new one with same metatable
  509. print(">>> closing state " .. "<<<\n")
  510. end
  511. local u = setmetatable({}, tt)
  512. ___Glob = {u} -- avoid object being collected before program end
  513. end
  514. -- create several objects to raise errors when collected while closing state
  515. do
  516. local mt = {__gc = function (o) return o + 1 end}
  517. for i = 1,10 do
  518. -- create object and preserve it until the end
  519. table.insert(___Glob, setmetatable({}, mt))
  520. end
  521. end
  522. -- just to make sure
  523. assert(collectgarbage'isrunning')
  524. print('OK')