gc.lua 17 KB

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