gc.lua 13 KB

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