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