From 94db0e8919511ab2750e10aedfb1f70b1a9061bd Mon Sep 17 00:00:00 2001 From: Stefan Plantikow Date: Wed, 16 Nov 2011 23:14:18 +0100 Subject: [PATCH 1/4] Added cross-platform fsync api to io; win32 impl needs to be refined No tests, need mktmpfile first --- src/lib/io.rs | 88 ++++++++++++++++++++++++++++++++++++++++++++- src/lib/linux_os.rs | 25 +++++++++---- src/lib/macos_os.rs | 45 ++++++++++++++++------- src/lib/win32_os.rs | 19 ++++++---- 4 files changed, 151 insertions(+), 26 deletions(-) diff --git a/src/lib/io.rs b/src/lib/io.rs index 99ae9bb670cf4..ebb2afa73f074 100644 --- a/src/lib/io.rs +++ b/src/lib/io.rs @@ -25,6 +25,8 @@ type buf_reader = fn eof() -> bool; fn seek(int, seek_style); fn tell() -> uint; + // Needed on readers in case one needs to flush metadata changes (atime) + fn fsync(level: fsync::level) -> int; }; @@ -58,7 +60,9 @@ fn convert_whence(whence: seek_style) -> i32 { }; } -resource FILE_res(f: os::libc::FILE) { os::libc::fclose(f); } +resource FILE_res(f: os::libc::FILE) { + os::libc::fclose(f); +} obj FILE_buf_reader(f: os::libc::FILE, res: option::t<@FILE_res>) { fn read(len: uint) -> [u8] unsafe { @@ -76,6 +80,9 @@ obj FILE_buf_reader(f: os::libc::FILE, res: option::t<@FILE_res>) { assert (os::libc::fseek(f, offset, convert_whence(whence)) == 0i32); } fn tell() -> uint { ret os::libc::ftell(f) as uint; } + fn fsync(level: fsync::level) -> int { + ret os::fsync_fd(os::libc::fileno(f), level) as int; + } } @@ -219,6 +226,7 @@ obj byte_buf_reader(bbuf: byte_buf) { bbuf.pos = seek_in_buf(offset, pos, len, whence); } fn tell() -> uint { ret bbuf.pos; } + fn fsync(_level: fsync::level) -> int { ret 0; } } fn new_byte_buf_reader(buf: [u8]) -> buf_reader { @@ -242,6 +250,7 @@ type buf_writer = fn write([u8]); fn seek(int, seek_style); fn tell() -> uint; + fn fsync(level: fsync::level) -> int; }; obj FILE_writer(f: os::libc::FILE, res: option::t<@FILE_res>) { @@ -255,6 +264,9 @@ obj FILE_writer(f: os::libc::FILE, res: option::t<@FILE_res>) { assert (os::libc::fseek(f, offset, convert_whence(whence)) == 0i32); } fn tell() -> uint { ret os::libc::ftell(f) as uint; } + fn fsync(level: fsync::level) -> int { + ret os::fsync_fd(os::libc::fileno(f), level) as int; + } } resource fd_res(fd: fd_t) { os::libc::close(fd); } @@ -283,6 +295,10 @@ obj fd_buf_writer(fd: fd_t, res: option::t<@fd_res>) { log_err "need 64-bit native calls for tell, sorry"; fail; } + + fn fsync(level: fsync::level) -> int { + ret os::fsync_fd(fd, level) as int; + } } fn file_buf_writer(path: str, @@ -433,6 +449,7 @@ obj byte_buf_writer(buf: mutable_byte_buf) { buf.pos = seek_in_buf(offset, pos, len, whence); } fn tell() -> uint { ret buf.pos; } + fn fsync(_level: fsync::level) -> int { ret 0; } } fn string_writer() -> str_writer { @@ -477,6 +494,75 @@ fn read_whole_file(file: str) -> result::t<[u8], str> { }) } +// fsync related + +mod fsync { + + tag level { + // whatever fsync does on that platform + fsync; + + // fdatasync on linux, similiar or more on other platforms + fdatasync; + + // full fsync + // + // You must additionally sync the parent directory as well! + fullfsync; + } + + + // Resource of artifacts that need to fsync on destruction + resource res(arg: arg) { + alt arg.opt_level { + option::none::. { } + option::some::(level) { + // fail hard if not succesful + assert(arg.fsync_fn(arg.val, level) != -1); + } + } + } + + type arg = { + val: t, + opt_level: option::t, + fsync_fn: fn(t, level) -> int + }; + + // fsync file after executing blk + // FIXME find better way to create resources within lifetime of outer res + fn FILE_res_sync(&&file: FILE_res, opt_level: option::t, + blk: block(&&res)) { + blk(res({ + val: *file, opt_level: opt_level, + fsync_fn: fn(&&file: os::libc::FILE, l: level) -> int { + ret os::fsync_fd(os::libc::fileno(file), l) as int; + } + })); + } + + // fsync fd after executing blk + fn fd_res_sync(&&fd: fd_res, opt_level: option::t, + blk: block(&&res)) { + blk(res({ + val: *fd, opt_level: opt_level, + fsync_fn: fn(&&fd: fd_t, l: level) -> int { + ret os::fsync_fd(fd, l) as int; + } + })); + } + + // Type of objects that may want to fsync + type t = obj { fn fsync(l: level) -> int; }; + + // Call o.fsync after executing blk + fn obj_sync(&&o: t, opt_level: option::t, blk: block(&&res)) { + blk(res({ + val: o, opt_level: opt_level, + fsync_fn: fn(&&o: t, l: level) -> int { ret o.fsync(l); } + })); + } +} // diff --git a/src/lib/linux_os.rs b/src/lib/linux_os.rs index de655b5c12ce3..fcaa8695151b9 100644 --- a/src/lib/linux_os.rs +++ b/src/lib/linux_os.rs @@ -18,6 +18,7 @@ export exec_suffix; export target_os; export dylib_filename; export get_exe_path; +export fsync_fd; // FIXME Somehow merge stuff duplicated here and macosx_os.rs. Made difficult // by https://github.com/graydon/rust/issues#issue/268 @@ -35,11 +36,14 @@ native mod libc { fn fopen(path: str::sbuf, mode: str::sbuf) -> FILE; fn fdopen(fd: fd_t, mode: str::sbuf) -> FILE; fn fclose(f: FILE); - fn fgetc(f: FILE) -> c_int; - fn ungetc(c: c_int, f: FILE); - fn feof(f: FILE) -> c_int; - fn fseek(f: FILE, offset: long, whence: c_int) -> c_int; - fn ftell(f: FILE) -> long; + fn fsync(fd: fd_t) -> c_int; + fn fdatasync(fd: fd_t) -> c_int; + fn fileno(f: FILE) -> fd_t; + fn fgetc(f: FILE) -> int; + fn ungetc(c: int, f: FILE); + fn feof(f: FILE) -> int; + fn fseek(f: FILE, offset: int, whence: int) -> int; + fn ftell(f: FILE) -> int; type dir; fn opendir(d: str::sbuf) -> dir; fn closedir(d: dir) -> c_int; @@ -88,9 +92,16 @@ fn fclose(file: libc::FILE) { libc::fclose(file) } +fn fsync_fd(fd: fd_t, level: io::fsync::level) -> c_int { + alt level { + io::fsync::fsync. | io::fsync::fullfsync. { ret libc::fsync(fd); } + io::fsync::fdatasync. { ret libc::fdatasync(fd); } + } +} + fn waitpid(pid: pid_t) -> i32 { - let status = 0i32; - assert (os::libc::waitpid(pid, status, 0i32) != -1i32); + let status = 0u32; + assert (os::libc::waitpid(pid, status, 0i32) != -1); ret status; } diff --git a/src/lib/macos_os.rs b/src/lib/macos_os.rs index 2bd5eab93ba5c..11c8466239b98 100644 --- a/src/lib/macos_os.rs +++ b/src/lib/macos_os.rs @@ -12,6 +12,7 @@ export exec_suffix; export target_os; export dylib_filename; export get_exe_path; +export fsync_fd; // FIXME Refactor into unix_os module or some such. Doesn't // seem to work right now. @@ -28,6 +29,8 @@ native mod libc { type FILE; fn fopen(path: str::sbuf, mode: str::sbuf) -> FILE; fn fdopen(fd: fd_t, mode: str::sbuf) -> FILE; + fn fsync(fd: fd_t) -> c_int; + fn fileno(f: FILE) -> fd_t; fn fclose(f: FILE); fn fgetc(f: FILE) -> c_int; fn ungetc(c: c_int, f: FILE); @@ -46,21 +49,26 @@ native mod libc { fn waitpid(pid: pid_t, &status: c_int, options: c_int) -> c_int; fn mkdir(s: str::sbuf, mode: c_int) -> c_int; fn rmdir(s: str::sbuf) -> c_int; + + // FIXME: Needs varags + fn fcntl(fd: fd_t, cmd: c_int) -> c_int; } mod libc_constants { - const O_RDONLY: c_int = 0i32; - const O_WRONLY: c_int = 1i32; - const O_RDWR: c_int = 2i32; - const O_APPEND: c_int = 8i32; - const O_CREAT: c_int = 512i32; - const O_EXCL: c_int = 2048i32; - const O_TRUNC: c_int = 1024i32; - const O_TEXT: c_int = 0i32; // nonexistent in darwin libc - const O_BINARY: c_int = 0i32; // nonexistent in darwin libc - - const S_IRUSR: unsigned = 256u32; - const S_IWUSR: unsigned = 128u32; + const O_RDONLY: c_int = 0i32; + const O_WRONLY: c_int = 1i32; + const O_RDWR: c_int = 2i32; + const O_APPEND: c_int = 8i32; + const O_CREAT: c_int = 512i32; + const O_EXCL: c_int = 2048i32; + const O_TRUNC: c_int = 1024i32; + const O_TEXT: c_int = 0i32; // nonexistent in darwin libc + const O_BINARY: c_int = 0i32; // nonexistent in darwin libc + + const S_IRUSR: unsigned = 256u32; + const S_IWUSR: unsigned = 128u32; + + const F_FULLFSYNC: c_int = 51i32; } fn pipe() -> {in: fd_t, out: fd_t} { @@ -87,6 +95,19 @@ fn waitpid(pid: pid_t) -> i32 { ret status; } +fn fsync_fd(fd: fd_t, level: io::fsync::level) -> c_int { + alt level { + io::fsync::fsync. { ret libc::fsync(fd); } + _ { + // According to man fnctl, the ok retval is only specified to be !=-1 + if (libc::fcntl(libc_constants::F_FULLFSYNC, fd) == -1 as c_int) + { ret -1 as c_int; } + else + { ret 0 as c_int; } + } + } +} + #[abi = "cdecl"] native mod rustrt { fn rust_getcwd() -> str; diff --git a/src/lib/win32_os.rs b/src/lib/win32_os.rs index 4b5635020d961..aae6b893e605d 100644 --- a/src/lib/win32_os.rs +++ b/src/lib/win32_os.rs @@ -15,12 +15,14 @@ native mod libc { fn fopen(path: str::sbuf, mode: str::sbuf) -> FILE; fn _fdopen(fd: fd_t, mode: str::sbuf) -> FILE; fn fclose(f: FILE); - fn fgetc(f: FILE) -> c_int; - fn ungetc(c: c_int, f: FILE); - fn feof(f: FILE) -> c_int; - fn fseek(f: FILE, offset: long, whence: c_int) -> c_int; - fn ftell(f: FILE) -> long; - fn _pipe(fds: *mutable fd_t, size: unsigned, mode: c_int) -> c_int; + fn fsync(fd: fd_t) -> c_int; + fn fileno(f: FILE) -> fd_t; + fn fgetc(f: FILE) -> int; + fn ungetc(c: int, f: FILE); + fn feof(f: FILE) -> int; + fn fseek(f: FILE, offset: int, whence: int) -> int; + fn ftell(f: FILE) -> int; + fn _pipe(fds: *mutable int, size: uint, mode: int) -> int; } mod libc_constants { @@ -92,6 +94,11 @@ fn fclose(file: libc::FILE) { libc::fclose(file) } +fn fsync_fd(fd: fd_t, level: io::fsync::level) -> c_int { + // FIXME do something more apropriate + ret libc::fsync(fd); +} + #[abi = "cdecl"] native mod rustrt { fn rust_process_wait(handle: c_int) -> c_int; From c56f131eb82dd6bbffbe7682639d7a8a2dd3f53a Mon Sep 17 00:00:00 2001 From: Stefan Plantikow Date: Thu, 17 Nov 2011 06:19:02 +0100 Subject: [PATCH 2/4] Fixed whitespace and discovered make check --- src/lib/io.rs | 53 +++++++++++++++++++++++---------------------- src/lib/macos_os.rs | 4 ++-- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/lib/io.rs b/src/lib/io.rs index ebb2afa73f074..77dee97ec34b1 100644 --- a/src/lib/io.rs +++ b/src/lib/io.rs @@ -25,7 +25,8 @@ type buf_reader = fn eof() -> bool; fn seek(int, seek_style); fn tell() -> uint; - // Needed on readers in case one needs to flush metadata changes (atime) + // Needed on readers in case one needs to flush metadata + // changes (atime) fn fsync(level: fsync::level) -> int; }; @@ -61,7 +62,7 @@ fn convert_whence(whence: seek_style) -> i32 { } resource FILE_res(f: os::libc::FILE) { - os::libc::fclose(f); + os::libc::fclose(f); } obj FILE_buf_reader(f: os::libc::FILE, res: option::t<@FILE_res>) { @@ -80,8 +81,8 @@ obj FILE_buf_reader(f: os::libc::FILE, res: option::t<@FILE_res>) { assert (os::libc::fseek(f, offset, convert_whence(whence)) == 0i32); } fn tell() -> uint { ret os::libc::ftell(f) as uint; } - fn fsync(level: fsync::level) -> int { - ret os::fsync_fd(os::libc::fileno(f), level) as int; + fn fsync(level: fsync::level) -> int { + ret os::fsync_fd(os::libc::fileno(f), level) as int; } } @@ -264,8 +265,8 @@ obj FILE_writer(f: os::libc::FILE, res: option::t<@FILE_res>) { assert (os::libc::fseek(f, offset, convert_whence(whence)) == 0i32); } fn tell() -> uint { ret os::libc::ftell(f) as uint; } - fn fsync(level: fsync::level) -> int { - ret os::fsync_fd(os::libc::fileno(f), level) as int; + fn fsync(level: fsync::level) -> int { + ret os::fsync_fd(os::libc::fileno(f), level) as int; } } @@ -296,8 +297,8 @@ obj fd_buf_writer(fd: fd_t, res: option::t<@fd_res>) { fail; } - fn fsync(level: fsync::level) -> int { - ret os::fsync_fd(fd, level) as int; + fn fsync(level: fsync::level) -> int { + ret os::fsync_fd(fd, level) as int; } } @@ -498,42 +499,42 @@ fn read_whole_file(file: str) -> result::t<[u8], str> { mod fsync { - tag level { + tag level { // whatever fsync does on that platform fsync; // fdatasync on linux, similiar or more on other platforms - fdatasync; - + fdatasync; + // full fsync // // You must additionally sync the parent directory as well! - fullfsync; + fullfsync; } - + // Resource of artifacts that need to fsync on destruction resource res(arg: arg) { alt arg.opt_level { option::none::. { } - option::some::(level) { + option::some::(level) { // fail hard if not succesful assert(arg.fsync_fn(arg.val, level) != -1); } } } - type arg = { - val: t, - opt_level: option::t, - fsync_fn: fn(t, level) -> int + type arg = { + val: t, + opt_level: option::t, + fsync_fn: fn(t, level) -> int }; // fsync file after executing blk // FIXME find better way to create resources within lifetime of outer res - fn FILE_res_sync(&&file: FILE_res, opt_level: option::t, - blk: block(&&res)) { - blk(res({ + fn FILE_res_sync(&&file: FILE_res, opt_level: option::t, + blk: block(&&res)) { + blk(res({ val: *file, opt_level: opt_level, fsync_fn: fn(&&file: os::libc::FILE, l: level) -> int { ret os::fsync_fd(os::libc::fileno(file), l) as int; @@ -542,11 +543,11 @@ mod fsync { } // fsync fd after executing blk - fn fd_res_sync(&&fd: fd_res, opt_level: option::t, - blk: block(&&res)) { - blk(res({ + fn fd_res_sync(&&fd: fd_res, opt_level: option::t, + blk: block(&&res)) { + blk(res({ val: *fd, opt_level: opt_level, - fsync_fn: fn(&&fd: fd_t, l: level) -> int { + fsync_fn: fn(&&fd: fd_t, l: level) -> int { ret os::fsync_fd(fd, l) as int; } })); @@ -555,7 +556,7 @@ mod fsync { // Type of objects that may want to fsync type t = obj { fn fsync(l: level) -> int; }; - // Call o.fsync after executing blk + // Call o.fsync after executing blk fn obj_sync(&&o: t, opt_level: option::t, blk: block(&&res)) { blk(res({ val: o, opt_level: opt_level, diff --git a/src/lib/macos_os.rs b/src/lib/macos_os.rs index 11c8466239b98..3e6681cf21dd2 100644 --- a/src/lib/macos_os.rs +++ b/src/lib/macos_os.rs @@ -100,9 +100,9 @@ fn fsync_fd(fd: fd_t, level: io::fsync::level) -> c_int { io::fsync::fsync. { ret libc::fsync(fd); } _ { // According to man fnctl, the ok retval is only specified to be !=-1 - if (libc::fcntl(libc_constants::F_FULLFSYNC, fd) == -1 as c_int) + if (libc::fcntl(libc_constants::F_FULLFSYNC, fd) == -1 as c_int) { ret -1 as c_int; } - else + else { ret 0 as c_int; } } } From 37e56e2b134e3118a7b4d9fc912f9a14449f91e2 Mon Sep 17 00:00:00 2001 From: Stefan Plantikow Date: Thu, 17 Nov 2011 06:40:13 +0100 Subject: [PATCH 3/4] Fixed merge mistake in io and *os --- src/lib/linux_os.rs | 14 +++++++------- src/lib/win32_os.rs | 12 ++++++------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/lib/linux_os.rs b/src/lib/linux_os.rs index fcaa8695151b9..fa229bd1eb200 100644 --- a/src/lib/linux_os.rs +++ b/src/lib/linux_os.rs @@ -39,11 +39,11 @@ native mod libc { fn fsync(fd: fd_t) -> c_int; fn fdatasync(fd: fd_t) -> c_int; fn fileno(f: FILE) -> fd_t; - fn fgetc(f: FILE) -> int; - fn ungetc(c: int, f: FILE); - fn feof(f: FILE) -> int; - fn fseek(f: FILE, offset: int, whence: int) -> int; - fn ftell(f: FILE) -> int; + fn fgetc(f: FILE) -> c_int; + fn ungetc(c: c_int, f: FILE); + fn feof(f: FILE) -> c_int; + fn fseek(f: FILE, offset: long, whence: c_int) -> c_int; + fn ftell(f: FILE) -> long; type dir; fn opendir(d: str::sbuf) -> dir; fn closedir(d: dir) -> c_int; @@ -100,8 +100,8 @@ fn fsync_fd(fd: fd_t, level: io::fsync::level) -> c_int { } fn waitpid(pid: pid_t) -> i32 { - let status = 0u32; - assert (os::libc::waitpid(pid, status, 0i32) != -1); + let status = 0i32; + assert (os::libc::waitpid(pid, status, 0i32) != -1i32); ret status; } diff --git a/src/lib/win32_os.rs b/src/lib/win32_os.rs index aae6b893e605d..9450e3d226b46 100644 --- a/src/lib/win32_os.rs +++ b/src/lib/win32_os.rs @@ -17,12 +17,12 @@ native mod libc { fn fclose(f: FILE); fn fsync(fd: fd_t) -> c_int; fn fileno(f: FILE) -> fd_t; - fn fgetc(f: FILE) -> int; - fn ungetc(c: int, f: FILE); - fn feof(f: FILE) -> int; - fn fseek(f: FILE, offset: int, whence: int) -> int; - fn ftell(f: FILE) -> int; - fn _pipe(fds: *mutable int, size: uint, mode: int) -> int; + fn fgetc(f: FILE) -> c_int; + fn ungetc(c: c_int, f: FILE); + fn feof(f: FILE) -> c_int; + fn fseek(f: FILE, offset: long, whence: c_int) -> c_int; + fn ftell(f: FILE) -> long; + fn _pipe(fds: *mutable fd_t, size: uint, mode: c_int) -> c_int; } mod libc_constants { From 4c64104318ff77adbc81f51ac1402a15b207aebd Mon Sep 17 00:00:00 2001 From: Stefan Plantikow Date: Thu, 17 Nov 2011 12:32:02 +0100 Subject: [PATCH 4/4] Fixed small merge mistake in win32 --- src/lib/io.rs | 5 +++++ src/lib/linux_os.rs | 1 + src/lib/macos_os.rs | 1 + src/lib/win32_os.rs | 3 ++- 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/lib/io.rs b/src/lib/io.rs index 77dee97ec34b1..910ec8bb9ea3b 100644 --- a/src/lib/io.rs +++ b/src/lib/io.rs @@ -251,6 +251,7 @@ type buf_writer = fn write([u8]); fn seek(int, seek_style); fn tell() -> uint; + fn flush() -> int; fn fsync(level: fsync::level) -> int; }; @@ -265,6 +266,7 @@ obj FILE_writer(f: os::libc::FILE, res: option::t<@FILE_res>) { assert (os::libc::fseek(f, offset, convert_whence(whence)) == 0i32); } fn tell() -> uint { ret os::libc::ftell(f) as uint; } + fn flush() -> int { ret os::libc::fflush(f) as int; } fn fsync(level: fsync::level) -> int { ret os::fsync_fd(os::libc::fileno(f), level) as int; } @@ -297,6 +299,8 @@ obj fd_buf_writer(fd: fd_t, res: option::t<@fd_res>) { fail; } + fn flush() -> int { ret 0; } + fn fsync(level: fsync::level) -> int { ret os::fsync_fd(fd, level) as int; } @@ -450,6 +454,7 @@ obj byte_buf_writer(buf: mutable_byte_buf) { buf.pos = seek_in_buf(offset, pos, len, whence); } fn tell() -> uint { ret buf.pos; } + fn flush() -> int { ret 0; } fn fsync(_level: fsync::level) -> int { ret 0; } } diff --git a/src/lib/linux_os.rs b/src/lib/linux_os.rs index fa229bd1eb200..625f60b49eddd 100644 --- a/src/lib/linux_os.rs +++ b/src/lib/linux_os.rs @@ -36,6 +36,7 @@ native mod libc { fn fopen(path: str::sbuf, mode: str::sbuf) -> FILE; fn fdopen(fd: fd_t, mode: str::sbuf) -> FILE; fn fclose(f: FILE); + fn fflush(f: FILE) -> c_int; fn fsync(fd: fd_t) -> c_int; fn fdatasync(fd: fd_t) -> c_int; fn fileno(f: FILE) -> fd_t; diff --git a/src/lib/macos_os.rs b/src/lib/macos_os.rs index 3e6681cf21dd2..50c291df1d3ae 100644 --- a/src/lib/macos_os.rs +++ b/src/lib/macos_os.rs @@ -29,6 +29,7 @@ native mod libc { type FILE; fn fopen(path: str::sbuf, mode: str::sbuf) -> FILE; fn fdopen(fd: fd_t, mode: str::sbuf) -> FILE; + fn fflush(f: FILE) -> c_int; fn fsync(fd: fd_t) -> c_int; fn fileno(f: FILE) -> fd_t; fn fclose(f: FILE); diff --git a/src/lib/win32_os.rs b/src/lib/win32_os.rs index 9450e3d226b46..fc1bcddd6caf8 100644 --- a/src/lib/win32_os.rs +++ b/src/lib/win32_os.rs @@ -15,6 +15,7 @@ native mod libc { fn fopen(path: str::sbuf, mode: str::sbuf) -> FILE; fn _fdopen(fd: fd_t, mode: str::sbuf) -> FILE; fn fclose(f: FILE); + fn fflush(f: FILE) -> c_int; fn fsync(fd: fd_t) -> c_int; fn fileno(f: FILE) -> fd_t; fn fgetc(f: FILE) -> c_int; @@ -22,7 +23,7 @@ native mod libc { fn feof(f: FILE) -> c_int; fn fseek(f: FILE, offset: long, whence: c_int) -> c_int; fn ftell(f: FILE) -> long; - fn _pipe(fds: *mutable fd_t, size: uint, mode: c_int) -> c_int; + fn _pipe(fds: *mutable fd_t, size: unsigned, mode: c_int) -> c_int; } mod libc_constants {