gc.lua 17 KB

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