diff options
author | Kevin (Kun) "Kassimo" Qian <kevinkassimo@gmail.com> | 2019-02-18 15:26:41 -0800 |
---|---|---|
committer | Ryan Dahl <ry@tinyclouds.org> | 2019-02-18 18:26:41 -0500 |
commit | 077af20ceb0869ddff9e08f5db1055138450fe2e (patch) | |
tree | 6bc51ad2112d7828b3dbc641050f1b098d836254 /src | |
parent | 97e29e3dd068c938ec5f8346183f8f523dea23c0 (diff) |
Add `seek` and implement `Seeker` on `File` (#1797)
This patch contains a special hack that circumvents the current tokio
seek problem.
tokio `seek` is implemented to take ownership of the original File and
emit a new one in its future, which conflicts with the design of
ResourceTable.
To avoid the problem, the current hack makes the FsFile resource
an Option which we could `take` the value ownership out of it. We then
convert the tokio File into a Rust std File, perform the seek, and then
put it back into the resource.
This might be able to drop this hack after
https://github.com/tokio-rs/tokio/pull/785 lands.
Diffstat (limited to 'src')
-rw-r--r-- | src/msg.fbs | 8 | ||||
-rw-r--r-- | src/ops.rs | 23 | ||||
-rw-r--r-- | src/resources.rs | 61 |
3 files changed, 91 insertions, 1 deletions
diff --git a/src/msg.fbs b/src/msg.fbs index fcf29ad58..9776bb893 100644 --- a/src/msg.fbs +++ b/src/msg.fbs @@ -65,6 +65,7 @@ union Any { NowRes, IsTTY, IsTTYRes, + Seek, } enum ErrorKind: byte { @@ -117,6 +118,7 @@ enum ErrorKind: byte { // custom errors InvalidUri, + InvalidSeekMode, } table Cwd {} @@ -496,4 +498,10 @@ table IsTTYRes { stderr: bool; } +table Seek { + rid: uint32; + offset: int; + whence: uint; +} + root_type Base; diff --git a/src/ops.rs b/src/ops.rs index a415d7100..c8a005601 100644 --- a/src/ops.rs +++ b/src/ops.rs @@ -126,6 +126,7 @@ pub fn dispatch( msg::Any::WriteFile => op_write_file, msg::Any::Now => op_now, msg::Any::IsTTY => op_is_tty, + msg::Any::Seek => op_seek, _ => panic!(format!( "Unhandled message {}", msg::enum_name_any(inner_type) @@ -868,6 +869,28 @@ fn op_write( } } +fn op_seek( + _state: &Arc<IsolateState>, + base: &msg::Base<'_>, + data: libdeno::deno_buf, +) -> Box<Op> { + assert_eq!(data.len(), 0); + let _cmd_id = base.cmd_id(); + let inner = base.inner_as_seek().unwrap(); + let rid = inner.rid(); + let offset = inner.offset(); + let whence = inner.whence(); + + match resources::lookup(rid) { + None => odd_future(errors::bad_resource()), + Some(resource) => { + let op = resources::seek(resource, offset, whence) + .and_then(move |_| Ok(empty_buf())); + Box::new(op) + } + } +} + fn op_remove( state: &Arc<IsolateState>, base: &msg::Base<'_>, diff --git a/src/resources.rs b/src/resources.rs index 6a15e378c..2d617265e 100644 --- a/src/resources.rs +++ b/src/resources.rs @@ -30,7 +30,7 @@ use futures::Stream; use hyper; use std; use std::collections::HashMap; -use std::io::{Error, Read, Write}; +use std::io::{Error, Read, Seek, SeekFrom, Write}; use std::net::{Shutdown, SocketAddr}; use std::process::ExitStatus; use std::sync::atomic::AtomicUsize; @@ -565,3 +565,62 @@ pub fn eager_accept(resource: Resource) -> EagerAccept { }, } } + +// TODO(kevinkassimo): revamp this after the following lands: +// https://github.com/tokio-rs/tokio/pull/785 +pub fn seek( + resource: Resource, + offset: i32, + whence: u32, +) -> Box<dyn Future<Item = (), Error = DenoError> + Send> { + let mut table = RESOURCE_TABLE.lock().unwrap(); + // We take ownership of File here. + // It is put back below while still holding the lock. + let maybe_repr = table.remove(&resource.rid); + match maybe_repr { + None => panic!("bad rid"), + Some(Repr::FsFile(f)) => { + let seek_from = match whence { + 0 => SeekFrom::Start(offset as u64), + 1 => SeekFrom::Current(offset as i64), + 2 => SeekFrom::End(offset as i64), + _ => { + return Box::new(futures::future::err(errors::new( + errors::ErrorKind::InvalidSeekMode, + format!("Invalid seek mode: {}", whence), + ))); + } + }; + // Trait Clone not implemented on tokio::fs::File, + // so convert to std File first. + let std_file = f.into_std(); + // Create a copy and immediately put back. + // We don't want to block other resource ops. + // try_clone() would yield a copy containing the same + // underlying fd, so operations on the copy would also + // affect the one in resource table, and we don't need + // to write back. + let maybe_std_file_copy = std_file.try_clone(); + // Insert the entry back with the same rid. + table.insert( + resource.rid, + Repr::FsFile(tokio_fs::File::from_std(std_file)), + ); + if maybe_std_file_copy.is_err() { + return Box::new(futures::future::err(DenoError::from( + maybe_std_file_copy.unwrap_err(), + ))); + } + let mut std_file_copy = maybe_std_file_copy.unwrap(); + return Box::new(futures::future::lazy(move || { + let result = std_file_copy + .seek(seek_from) + .map(|_| { + return (); + }).map_err(DenoError::from); + futures::future::result(result) + })); + } + _ => panic!("cannot seek"), + } +} |