From 87f223e2f079adf0cc270fe8cf31f1c8c93e4e06 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Fri, 2 Aug 2013 19:22:06 +0200 Subject: io: fix file:close() and file:__gc() blocking --- lem/io/file.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++--------- test/fclose.lua | 52 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 11 deletions(-) create mode 100755 test/fclose.lua diff --git a/lem/io/file.c b/lem/io/file.c index c9f675a..b7ad297 100644 --- a/lem/io/file.c +++ b/lem/io/file.c @@ -45,6 +45,11 @@ struct file { struct lem_inputbuf buf; }; +struct file_gc { + struct lem_async a; + int fd; +}; + static struct file * file_new(lua_State *T, int fd, int mt) { @@ -63,14 +68,38 @@ file_new(lua_State *T, int fd, int mt) return f; } +/* + * file:__gc() metamethod + */ +static void +file_gc_work(struct lem_async *a) +{ + struct file_gc *gc = (struct file_gc *)a; + + close(gc->fd); +} + +static void +file_gc_reap(struct lem_async *a) +{ + struct file_gc *gc = (struct file_gc *)a; + + free(gc); +} + static int file_gc(lua_State *T) { struct file *f = lua_touserdata(T, 1); lem_debug("collecting %p, fd = %d", f, f->fd); - if (f->fd >= 0) - (void)close(f->fd); + if (f->fd >= 0) { + struct file_gc *gc = lem_xmalloc(sizeof(struct file_gc)); + + gc->fd = f->fd; + f->fd = -1; + lem_async_do(&gc->a, NULL, file_gc_work, file_gc_reap); + } return 0; } @@ -86,11 +115,41 @@ file_closed(lua_State *T) return 1; } +/* + * file:close() method + */ +static void +file_close_work(struct lem_async *a) +{ + struct file *f = (struct file *)a; + + if (close(f->fd)) + f->ret = errno; + else + f->ret = 0; +} + +static void +file_close_reap(struct lem_async *a) +{ + struct file *f = (struct file *)a; + lua_State *T = f->a.T; + + f->a.T = NULL; + f->fd = -1; + if (f->ret) { + lem_queue(T, io_strerror(T, f->ret)); + return; + } + + lua_pushboolean(T, 1); + lem_queue(T, 1); +} + static int file_close(lua_State *T) { struct file *f; - int ret; luaL_checktype(T, 1, LUA_TUSERDATA); f = lua_touserdata(T, 1); @@ -99,14 +158,9 @@ file_close(lua_State *T) if (f->a.T != NULL) return io_busy(T); - lem_debug("collecting %d", f->fd); - ret = close(f->fd); - f->fd = -1; - if (ret) - return io_strerror(T, errno); - - lua_pushboolean(T, 1); - return 1; + lem_async_do(&f->a, T, file_close_work, file_close_reap); + lua_settop(T, 1); + return lua_yield(T, 1); } /* diff --git a/test/fclose.lua b/test/fclose.lua new file mode 100755 index 0000000..417decd --- /dev/null +++ b/test/fclose.lua @@ -0,0 +1,52 @@ +#!bin/lem +-- +-- This file is part of LEM, a Lua Event Machine. +-- Copyright 2013 Ico Doornekamp +-- Copyright 2013 Emil Renner Berthing +-- +-- LEM is free software: you can redistribute it and/or modify it +-- under the terms of the GNU Lesser General Public License as +-- published by the Free Software Foundation, either version 3 of +-- the License, or (at your option) any later version. +-- +-- LEM is distributed in the hope that it will be useful, but +-- WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU Lesser General Public License for more details. +-- +-- You should have received a copy of the GNU Lesser General Public +-- License along with LEM. If not, see . +-- + +package.path = '?.lua' +package.cpath = '?.so' + +local utils = require 'lem.utils' +local io = require 'lem.io' + +local done = false + +utils.spawn(function() + local s, now, format = utils.newsleeper(), utils.now, string.format + local t1, t2 = now(), 0 + repeat + s:sleep(0.1) + t2 = now() + print(format("Tick after %uus", (t2 - t1)*1000000)) + t1 = t2 + until done +end) + +local file = assert(io.open('file.txt', 'w')) +local b = string.rep("a", 1024*1024) +for i = 1, 150 do + file:write(b) +end +print("Closing file") +file:close() +print("Write done") + +utils.newsleeper():sleep(1) +done = true + +-- vim: set ts=2 sw=2 noet: -- cgit v1.2.1