eval: Make writefile() able to disable fsync()

This commit is contained in:
ZyX
2017-04-02 22:11:35 +03:00
parent ddfa0359c6
commit b10880dadc
5 changed files with 66 additions and 33 deletions

View File

@@ -7915,6 +7915,11 @@ writefile({list}, {fname} [, {flags}])
appended to the file: > appended to the file: >
:call writefile(["foo"], "event.log", "a") :call writefile(["foo"], "event.log", "a")
:call writefile(["bar"], "event.log", "a") :call writefile(["bar"], "event.log", "a")
<
When {flags} contains "S" fsync() call is not used. This means
that writefile() will finish faster, but writes may be left in
OS buffers and not yet written to disk. Such changes will
disappear if system crashes before OS does writing.
All NL characters are replaced with a NUL character. All NL characters are replaced with a NUL character.
Inserting CR characters needs to be done before passing {list} Inserting CR characters needs to be done before passing {list}

View File

@@ -17421,6 +17421,7 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
bool binary = false; bool binary = false;
bool append = false; bool append = false;
bool do_fsync = true;
if (argvars[2].v_type != VAR_UNKNOWN) { if (argvars[2].v_type != VAR_UNKNOWN) {
const char *const flags = tv_get_string_chk(&argvars[2]); const char *const flags = tv_get_string_chk(&argvars[2]);
if (flags == NULL) { if (flags == NULL) {
@@ -17432,6 +17433,9 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (strchr(flags, 'a')) { if (strchr(flags, 'a')) {
append = true; append = true;
} }
if (strchr(flags, 'S')) {
do_fsync = false;
}
} }
char buf[NUMBUFLEN]; char buf[NUMBUFLEN];
@@ -17453,7 +17457,7 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (write_list(&fp, argvars[0].vval.v_list, binary)) { if (write_list(&fp, argvars[0].vval.v_list, binary)) {
rettv->vval.v_number = 0; rettv->vval.v_number = 0;
} }
if ((error = file_close(&fp)) != 0) { if ((error = file_close(&fp, do_fsync)) != 0) {
emsgf(_("E80: Error when closing file %s: %s"), emsgf(_("E80: Error when closing file %s: %s"),
fname, os_strerror(error)); fname, os_strerror(error));
} }

View File

@@ -113,27 +113,31 @@ FileDescriptor *file_open_new(int *const error, const char *const fname,
/// Close file and free its buffer /// Close file and free its buffer
/// ///
/// @param[in,out] fp File to close. /// @param[in,out] fp File to close.
/// @param[in] do_fsync If true, use fsync() to write changes to disk.
/// ///
/// @return 0 or error code. /// @return 0 or error code.
int file_close(FileDescriptor *const fp) FUNC_ATTR_NONNULL_ALL int file_close(FileDescriptor *const fp, const bool do_fsync)
FUNC_ATTR_NONNULL_ALL
{ {
const int error = file_fsync(fp); const int flush_error = (do_fsync ? file_fsync(fp) : file_flush(fp));
const int error2 = os_close(fp->fd); const int close_error = os_close(fp->fd);
rbuffer_free(fp->rv); rbuffer_free(fp->rv);
if (error2 != 0) { if (close_error != 0) {
return error2; return close_error;
} }
return error; return flush_error;
} }
/// Close and free file obtained using file_open_new() /// Close and free file obtained using file_open_new()
/// ///
/// @param[in,out] fp File to close. /// @param[in,out] fp File to close.
/// @param[in] do_fsync If true, use fsync() to write changes to disk.
/// ///
/// @return 0 or error code. /// @return 0 or error code.
int file_free(FileDescriptor *const fp) FUNC_ATTR_NONNULL_ALL int file_free(FileDescriptor *const fp, const bool do_fsync)
FUNC_ATTR_NONNULL_ALL
{ {
const int ret = file_close(fp); const int ret = file_close(fp, do_fsync);
xfree(fp); xfree(fp);
return ret; return ret;
} }

View File

@@ -811,7 +811,7 @@ static int open_shada_file_for_reading(const char *const fname,
/// Wrapper for closing file descriptors /// Wrapper for closing file descriptors
static void close_file(void *cookie) static void close_file(void *cookie)
{ {
const int error = file_free(cookie); const int error = file_free(cookie, true);
if (error != 0) { if (error != 0) {
emsgf(_(SERR "System error while closing ShaDa file: %s"), emsgf(_(SERR "System error while closing ShaDa file: %s"),
os_strerror(error)); os_strerror(error));

View File

@@ -98,7 +98,7 @@ describe('file_open', function()
eq(0, err) eq(0, err)
local attrs = lfs.attributes(filec) local attrs = lfs.attributes(filec)
eq('rwx------', attrs.permissions) eq('rwx------', attrs.permissions)
eq(0, m.file_close(fp)) eq(0, m.file_close(fp, false))
end) end)
itp('can create a rw------- file with kFileCreate', function() itp('can create a rw------- file with kFileCreate', function()
@@ -106,7 +106,7 @@ describe('file_open', function()
eq(0, err) eq(0, err)
local attrs = lfs.attributes(filec) local attrs = lfs.attributes(filec)
eq('rw-------', attrs.permissions) eq('rw-------', attrs.permissions)
eq(0, m.file_close(fp)) eq(0, m.file_close(fp, false))
end) end)
itp('can create a rwx------ file with kFileCreateOnly', function() itp('can create a rwx------ file with kFileCreateOnly', function()
@@ -114,7 +114,7 @@ describe('file_open', function()
eq(0, err) eq(0, err)
local attrs = lfs.attributes(filec) local attrs = lfs.attributes(filec)
eq('rwx------', attrs.permissions) eq('rwx------', attrs.permissions)
eq(0, m.file_close(fp)) eq(0, m.file_close(fp, false))
end) end)
itp('can create a rw------- file with kFileCreateOnly', function() itp('can create a rw------- file with kFileCreateOnly', function()
@@ -122,7 +122,7 @@ describe('file_open', function()
eq(0, err) eq(0, err)
local attrs = lfs.attributes(filec) local attrs = lfs.attributes(filec)
eq('rw-------', attrs.permissions) eq('rw-------', attrs.permissions)
eq(0, m.file_close(fp)) eq(0, m.file_close(fp, false))
end) end)
itp('fails to open an existing file with kFileCreateOnly', function() itp('fails to open an existing file with kFileCreateOnly', function()
@@ -141,35 +141,35 @@ describe('file_open', function()
local err, fp = file_open(file1, m.kFileCreate, 384) local err, fp = file_open(file1, m.kFileCreate, 384)
eq(0, err) eq(0, err)
eq(true, fp.wr) eq(true, fp.wr)
eq(0, m.file_close(fp)) eq(0, m.file_close(fp, false))
end) end)
itp('can open an existing file read-only with zero', function() itp('can open an existing file read-only with zero', function()
local err, fp = file_open(file1, 0, 384) local err, fp = file_open(file1, 0, 384)
eq(0, err) eq(0, err)
eq(false, fp.wr) eq(false, fp.wr)
eq(0, m.file_close(fp)) eq(0, m.file_close(fp, false))
end) end)
itp('can open an existing file read-only with kFileReadOnly', function() itp('can open an existing file read-only with kFileReadOnly', function()
local err, fp = file_open(file1, m.kFileReadOnly, 384) local err, fp = file_open(file1, m.kFileReadOnly, 384)
eq(0, err) eq(0, err)
eq(false, fp.wr) eq(false, fp.wr)
eq(0, m.file_close(fp)) eq(0, m.file_close(fp, false))
end) end)
itp('can open an existing file read-only with kFileNoSymlink', function() itp('can open an existing file read-only with kFileNoSymlink', function()
local err, fp = file_open(file1, m.kFileNoSymlink, 384) local err, fp = file_open(file1, m.kFileNoSymlink, 384)
eq(0, err) eq(0, err)
eq(false, fp.wr) eq(false, fp.wr)
eq(0, m.file_close(fp)) eq(0, m.file_close(fp, false))
end) end)
itp('can truncate an existing file with kFileTruncate', function() itp('can truncate an existing file with kFileTruncate', function()
local err, fp = file_open(file1, m.kFileTruncate, 384) local err, fp = file_open(file1, m.kFileTruncate, 384)
eq(0, err) eq(0, err)
eq(true, fp.wr) eq(true, fp.wr)
eq(0, m.file_close(fp)) eq(0, m.file_close(fp, false))
local attrs = lfs.attributes(file1) local attrs = lfs.attributes(file1)
eq(0, attrs.size) eq(0, attrs.size)
end) end)
@@ -178,7 +178,7 @@ describe('file_open', function()
local err, fp = file_open(file1, m.kFileWriteOnly, 384) local err, fp = file_open(file1, m.kFileWriteOnly, 384)
eq(0, err) eq(0, err)
eq(true, fp.wr) eq(true, fp.wr)
eq(0, m.file_close(fp)) eq(0, m.file_close(fp, false))
local attrs = lfs.attributes(file1) local attrs = lfs.attributes(file1)
eq(4096, attrs.size) eq(4096, attrs.size)
end) end)
@@ -195,7 +195,7 @@ describe('file_open', function()
local err, fp = file_open(linkf, m.kFileTruncate, 384) local err, fp = file_open(linkf, m.kFileTruncate, 384)
eq(0, err) eq(0, err)
eq(true, fp.wr) eq(true, fp.wr)
eq(0, m.file_close(fp)) eq(0, m.file_close(fp, false))
local attrs = lfs.attributes(file1) local attrs = lfs.attributes(file1)
eq(0, attrs.size) eq(0, attrs.size)
end) end)
@@ -221,7 +221,7 @@ describe('file_open_new', function()
local err, fp = file_open_new(file1, 0, 384) local err, fp = file_open_new(file1, 0, 384)
eq(0, err) eq(0, err)
eq(false, fp.wr) eq(false, fp.wr)
eq(0, m.file_free(fp)) eq(0, m.file_free(fp, false))
end) end)
itp('fails to open an existing file with kFileCreateOnly', function() itp('fails to open an existing file with kFileCreateOnly', function()
@@ -231,7 +231,27 @@ describe('file_open_new', function()
end) end)
end) end)
-- file_close is called above, so it is not tested directly describe('file_close', function()
itp('can flush writes to disk also with true argument', function()
local err, fp = file_open(filec, m.kFileCreateOnly, 384)
local wsize = file_write(fp, 'test')
eq(4, wsize)
eq(0, lfs.attributes(filec).size)
eq(0, m.file_close(fp, true))
eq(wsize, lfs.attributes(filec).size)
end)
end)
describe('file_free', function()
itp('can flush writes to disk also with true argument', function()
local err, fp = file_open_new(filec, m.kFileCreateOnly, 384)
local wsize = file_write(fp, 'test')
eq(4, wsize)
eq(0, lfs.attributes(filec).size)
eq(0, m.file_free(fp, true))
eq(wsize, lfs.attributes(filec).size)
end)
end)
describe('file_fsync', function() describe('file_fsync', function()
itp('can flush writes to disk', function() itp('can flush writes to disk', function()
@@ -244,7 +264,7 @@ describe('file_fsync', function()
eq(0, lfs.attributes(filec).size) eq(0, lfs.attributes(filec).size)
eq(0, file_fsync(fp)) eq(0, file_fsync(fp))
eq(wsize, lfs.attributes(filec).size) eq(wsize, lfs.attributes(filec).size)
eq(0, m.file_close(fp)) eq(0, m.file_close(fp, false))
end) end)
end) end)
@@ -259,7 +279,7 @@ describe('file_flush', function()
eq(0, lfs.attributes(filec).size) eq(0, lfs.attributes(filec).size)
eq(0, file_flush(fp)) eq(0, file_flush(fp))
eq(wsize, lfs.attributes(filec).size) eq(wsize, lfs.attributes(filec).size)
eq(0, m.file_close(fp)) eq(0, m.file_close(fp, false))
end) end)
end) end)
@@ -281,7 +301,7 @@ describe('file_read', function()
eq({exp_err, exp_s}, {file_read(fp, size)}) eq({exp_err, exp_s}, {file_read(fp, size)})
shift = shift + size shift = shift + size
end end
eq(0, m.file_close(fp)) eq(0, m.file_close(fp, false))
end) end)
itp('can read the whole file at once', function() itp('can read the whole file at once', function()
@@ -290,7 +310,7 @@ describe('file_read', function()
eq(false, fp.wr) eq(false, fp.wr)
eq({#fcontents, fcontents}, {file_read(fp, #fcontents)}) eq({#fcontents, fcontents}, {file_read(fp, #fcontents)})
eq({0, ('\0'):rep(#fcontents)}, {file_read(fp, #fcontents)}) eq({0, ('\0'):rep(#fcontents)}, {file_read(fp, #fcontents)})
eq(0, m.file_close(fp)) eq(0, m.file_close(fp, false))
end) end)
itp('can read more then 1024 bytes after reading a small chunk', function() itp('can read more then 1024 bytes after reading a small chunk', function()
@@ -300,7 +320,7 @@ describe('file_read', function()
eq({5, fcontents:sub(1, 5)}, {file_read(fp, 5)}) eq({5, fcontents:sub(1, 5)}, {file_read(fp, 5)})
eq({#fcontents - 5, fcontents:sub(6) .. (('\0'):rep(5))}, eq({#fcontents - 5, fcontents:sub(6) .. (('\0'):rep(5))},
{file_read(fp, #fcontents)}) {file_read(fp, #fcontents)})
eq(0, m.file_close(fp)) eq(0, m.file_close(fp, false))
end) end)
itp('can read file by 768-byte-chunks', function() itp('can read file by 768-byte-chunks', function()
@@ -320,7 +340,7 @@ describe('file_read', function()
eq({exp_err, exp_s}, {file_read(fp, size)}) eq({exp_err, exp_s}, {file_read(fp, size)})
shift = shift + size shift = shift + size
end end
eq(0, m.file_close(fp)) eq(0, m.file_close(fp, false))
end) end)
end) end)
@@ -331,7 +351,7 @@ describe('file_write', function()
eq(true, fp.wr) eq(true, fp.wr)
local wr = file_write(fp, fcontents) local wr = file_write(fp, fcontents)
eq(#fcontents, wr) eq(#fcontents, wr)
eq(0, m.file_close(fp)) eq(0, m.file_close(fp, false))
eq(wr, lfs.attributes(filec).size) eq(wr, lfs.attributes(filec).size)
eq(fcontents, io.open(filec):read('*a')) eq(fcontents, io.open(filec):read('*a'))
end) end)
@@ -348,7 +368,7 @@ describe('file_write', function()
eq(wr, #s) eq(wr, #s)
shift = shift + size shift = shift + size
end end
eq(0, m.file_close(fp)) eq(0, m.file_close(fp, false))
eq(#fcontents, lfs.attributes(filec).size) eq(#fcontents, lfs.attributes(filec).size)
eq(fcontents, io.open(filec):read('*a')) eq(fcontents, io.open(filec):read('*a'))
end) end)
@@ -365,7 +385,7 @@ describe('file_write', function()
eq(wr, #s) eq(wr, #s)
shift = shift + size shift = shift + size
end end
eq(0, m.file_close(fp)) eq(0, m.file_close(fp, false))
eq(#fcontents, lfs.attributes(filec).size) eq(#fcontents, lfs.attributes(filec).size)
eq(fcontents, io.open(filec):read('*a')) eq(fcontents, io.open(filec):read('*a'))
end) end)
@@ -380,6 +400,6 @@ describe('file_skip', function()
local rd, s = file_read(fp, 3) local rd, s = file_read(fp, 3)
eq(3, rd) eq(3, rd)
eq(fcontents:sub(4, 6), s) eq(fcontents:sub(4, 6), s)
eq(0, m.file_close(fp)) eq(0, m.file_close(fp, false))
end) end)
end) end)