百分百源码网-让建站变得如此简单! 登录 注册 签到领金币!

主页 | 如何升级VIP | TAG标签

当前位置: 主页>网站教程>JS教程> deno通讯实现的办法(附代码)
分享文章到:

deno通讯实现的办法(附代码)

发布时间:09/01 来源:未知 浏览: 关键词:
本篇文章给大家带来的内容是关于deno通讯实现的办法(附代码),有必然的参照 价值,有需要的伴侣可以参照 一下,但愿对你有所帮忙。

通讯方式

deno施行代码和node类似,包括同步和异步的方式, 异步方式通过Promise.then实现。

Typescript/Javascript调取rust

在上一节中讲到deno的启动时会初始化v8 isolate实例,在初始化的历程中,会将c++的函数绑定到v8 isolate的实例上,在v8施行Javascript代码时,可以像调取Javascript函数一样调取这些绑定的函数。详细的绑定实现如下:

void InitializeContext(v8::Isolate* isolate, v8::Local<v8::Context> context) {
  v8::HandleScope handle_scope(isolate);
  v8::Context::Scope context_scope(context);

  auto global = context->Global();

  auto deno_val = v8::Object::New(isolate);
  CHECK(global->Set(context, deno::v8_str("libdeno"), deno_val).FromJust());

  auto print_tmpl = v8::FunctionTemplate::New(isolate, Print);
  auto print_val = print_tmpl->GetFunction(context).ToLocalChecked();
  CHECK(deno_val->Set(context, deno::v8_str("print"), print_val).FromJust());

  auto recv_tmpl = v8::FunctionTemplate::New(isolate, Recv);
  auto recv_val = recv_tmpl->GetFunction(context).ToLocalChecked();
  CHECK(deno_val->Set(context, deno::v8_str("recv"), recv_val).FromJust());

  auto send_tmpl = v8::FunctionTemplate::New(isolate, Send);
  auto send_val = send_tmpl->GetFunction(context).ToLocalChecked();
  CHECK(deno_val->Set(context, deno::v8_str("send"), send_val).FromJust());

  auto eval_context_tmpl = v8::FunctionTemplate::New(isolate, EvalContext);
  auto eval_context_val =
      eval_context_tmpl->GetFunction(context).ToLocalChecked();
  CHECK(deno_val->Set(context, deno::v8_str("evalContext"), eval_context_val)
            .FromJust());

  auto error_to_json_tmpl = v8::FunctionTemplate::New(isolate, ErrorToJSON);
  auto error_to_json_val =
      error_to_json_tmpl->GetFunction(context).ToLocalChecked();
  CHECK(deno_val->Set(context, deno::v8_str("errorToJSON"), error_to_json_val)
            .FromJust());

  CHECK(deno_val->SetAccessor(context, deno::v8_str("shared"), Shared)
            .FromJust());
}

在完成绑定之后,在Typescript中可以通过如下代码实现c++办法和Typescript办法的映射

libdeno.ts
interface Libdeno {
  recv(cb: MessageCallback): void;

  send(control: ArrayBufferView, data?: ArrayBufferView): null | Uint8Array;

  print(x: string, isErr?: boolean): void;

  shared: ArrayBuffer;

  /** Evaluate provided code in the current context.
   * It differs from eval(...) in that it does not create a new context.
   * Returns an array: [output, errInfo].
   * If an error occurs, `output` becomes null and `errInfo` is non-null.
   */
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  evalContext(code: string): [any, EvalErrorInfo | null];

  errorToJSON: (e: Error) => string;
}

export const libdeno = window.libdeno as Libdeno;

在施行Typescript代码时,只需要引入libdeno,就直接调取c++办法,例如:

import { libdeno } from "./libdeno";
function sendInternal(
  builder: flatbuffers.Builder,
  innerType: msg.Any,
  inner: flatbuffers.Offset,
  data: undefined | ArrayBufferView,
  sync = true
): [number, null | Uint8Array] {
  const cmdId = nextCmdId++;
  msg.Base.startBase(builder);
  msg.Base.addInner(builder, inner);
  msg.Base.addInnerType(builder, innerType);
  msg.Base.addSync(builder, sync);
  msg.Base.addCmdId(builder, cmdId);
  builder.finish(msg.Base.endBase(builder));
  const res = libdeno.send(builder.asUint8Array(), data);
  builder.inUse = false;
  return [cmdId, res];
}

调取libdeno.send办法可以将数据传给c++,然后通过c++去调取rust代码实现详细的工程操纵。

Typescript层同步异步实现

同步

在Typescript中只需要设定sendInternal办法的sync参数为true即可,在rust中会按照sync参数去推断是施行同步或者异步操纵,假如sync为true,libdeono.send办法会返回施行的结果,rust和typescript之间传递数据需要将数据序列化,这里序列化操纵使用的是flatbuffer库。

const [cmdId, resBuf] = sendInternal(builder, innerType, inner, data, true);
异步实现

同理,实现异步方式,只需要设定sync参数为false即可,但是异步操纵和同步比拟,多了回掉办法,在施行异步通讯时,libdeno.send办法会返回一个独一的cmdId标记这次调取操纵。同时在异步通讯完成后,会创立一个promise对象,将cmdId作为key,promise作为value,参加map中。代码如下:

const [cmdId, resBuf] = sendInternal(builder, innerType, inner, data, false);
  util.assert(resBuf == null);
  const promise = util.createResolvable<msg.Base>();
  promiseTable.set(cmdId, promise);
  return promise;

rust实现同步和异步

当在Typescript中调取libdeno.send办法时,调取了C++文件binding.cc中的Send办法,该办法是在deno初始化时绑定到v8 isolate上去的。在Send办法中去调取了ops.rs文件中的dispatch办法,该办法实现了新闻到函数的映射。每个类型的新闻对应了一种函数,例如读文件新闻对应了读文件的函数。

pub fn dispatch(
  isolate: &Isolate,
  control: libdeno::deno_buf,
  data: libdeno::deno_buf,
) -> (bool, Box<Op>) {
  let base = msg::get_root_as_base(&control);
  let is_sync = base.sync();
  let inner_type = base.inner_type();
  let cmd_id = base.cmd_id();

  let op: Box<Op> = if inner_type == msg::Any::SetTimeout {
    // SetTimeout is an exceptional op: the global timeout field is part of the
    // Isolate state (not the IsolateState state) and it must be updated on the
    // main thread.
    assert_eq!(is_sync, true);
    op_set_timeout(isolate, &base, data)
  } else {
    // Handle regular ops.
    let op_creator: OpCreator = match inner_type {
      msg::Any::Accept => op_accept,
      msg::Any::Chdir => op_chdir,
      msg::Any::Chmod => op_chmod,
      msg::Any::Close => op_close,
      msg::Any::FetchModuleMetaData => op_fetch_module_meta_data,
      msg::Any::CopyFile => op_copy_file,
      msg::Any::Cwd => op_cwd,
      msg::Any::Dial => op_dial,
      msg::Any::Environ => op_env,
      msg::Any::Exit => op_exit,
      msg::Any::Fetch => op_fetch,
      msg::Any::FormatError => op_format_error,
      msg::Any::Listen => op_listen,
      msg::Any::MakeTempDir => op_make_temp_dir,
      msg::Any::Metrics => op_metrics,
      msg::Any::Mkdir => op_mkdir,
      msg::Any::Open => op_open,
      msg::Any::ReadDir => op_read_dir,
      msg::Any::ReadFile => op_read_file,
      msg::Any::Readlink => op_read_link,
      msg::Any::Read => op_read,
      msg::Any::Remove => op_remove,
      msg::Any::Rename => op_rename,
      msg::Any::ReplReadline => op_repl_readline,
      msg::Any::ReplStart => op_repl_start,
      msg::Any::Resources => op_resources,
      msg::Any::Run => op_run,
      msg::Any::RunStatus => op_run_status,
      msg::Any::SetEnv => op_set_env,
      msg::Any::Shutdown => op_shutdown,
      msg::Any::Start => op_start,
      msg::Any::Stat => op_stat,
      msg::Any::Symlink => op_symlink,
      msg::Any::Truncate => op_truncate,
      msg::Any::WorkerGetMessage => op_worker_get_message,
      msg::Any::WorkerPostMessage => op_worker_post_message,
      msg::Any::Write => op_write,
      msg::Any::WriteFile => op_write_file,
      msg::Any::Now => op_now,
      msg::Any::IsTTY => op_is_tty,
      msg::Any::Seek => op_seek,
      msg::Any::Permissions => op_permissions,
      msg::Any::PermissionRevoke => op_revoke_permission,
      _ => panic!(format!(
        "Unhandled message {}",
        msg::enum_name_any(inner_type)
      )),
    };
    op_creator(&isolate, &base, data)
  };

  // ...省略余外的代码
}

在每个类型的函数中会按照在Typescript中调取libdeo.send办法时传入的sync参数值去推断同步施行还是异步施行。

let (is_sync, op) = dispatch(isolate, control_buf, zero_copy_buf);
同步施行

在施行dispatch办法后,会返回is_sync的变量,假如is_sync为true,表示该办法是同步施行的,op表示返回的结果。rust代码会调取c++文件api.cc中的deno_respond办法,将施行结果同步回去,deno_respond办法中按照current_args_的值去推断可否为同步新闻,假如current_args_存在值,则直接返回结果。

异步施行

在deno中,施行异步操纵是通过rust的Tokio模块来实现的,在调取dispatch办法后,假如是异步操纵,is_sync的值为false,op不再是施行结果,而是一个施行函数。通过tokio模块派生一个线程程异步去施行该函数。

    let task = op
      .and_then(move |buf| {
        let sender = tx; // tx is moved to new thread
        sender.send((zero_copy_id, buf)).expect("tx.send error");
        Ok(())
      }).map_err(|_| ());
    tokio::spawn(task);

在deno初始化时,会创立一个管道,代码如下:

let (tx, rx) = mpsc::channel::<(usize, Buf)>();

管道可以实现不一样线程之间的通讯,由于异步操纵是创立了一个新的线程去施行的,所以子线程没法直接和主线程之间通讯,需要通过管道的机制去实现。在异步代码施行完成后,调取tx.send办法将施行结果参加管道里面,event loop会每次从管道里面去读取结果返回回去。

Event Loop

由于异步操纵依靠事件轮回,所以先说明一下deno中的事件轮回,其实事件轮回很简便,就是一段轮回施行的代码,当到达前提后,事件轮回会完毕施行,deno中主要的事件轮回代码实现如下:

pub fn event_loop(&self) -> Result<(), JSError> {
    // Main thread event loop.
    while !self.is_idle() {
      match recv_deadline(&self.rx, self.get_timeout_due()) {
        Ok((zero_copy_id, buf)) => self.complete_op(zero_copy_id, buf),
        Err(mpsc::RecvTimeoutError::Timeout) => self.timeout(),
        Err(e) => panic!("recv_deadline() failed: {:?}", e),
      }
      self.check_promise_errors();
      if let Some(err) = self.last_exception() {
        return Err(err);
      }
    }
    // Check on done
    self.check_promise_errors();
    if let Some(err) = self.last_exception() {
      return Err(err);
    }
    Ok(())
  }

self.is_idle办法用来推断可否所有的异步操纵都施行完毕,当所有的异步操纵都施行完毕后,休止事件轮回,is_idle办法代码如下:

fn is_idle(&self) -> bool {
    self.ntasks.get() == 0 && self.get_timeout_due().is_none()
  }

当发生一次异步办法调取时,会调取下面的办法,使ntasks内部的值加1,

fn ntasks_increment(&self) {
    assert!(self.ntasks.get() >= 0);
    self.ntasks.set(self.ntasks.get() + 1);
  }

在event loop轮回中,每次从管道中去取值,这里event loop充消耗者,施行异步办法的子线程充当生产者。假如在一次事件轮回中,猎取到了一次施行结果,那么会调取ntasks_decrement办法,使ntasks内部的值减1,当ntasks的值为0的时候,事件轮回会退出施行。在每次轮回中,将管道中取得的值作为参数,调取complete_op办法,将结果返回回去。

rust中将异步操纵结果返回回去

在初始化v8实例时,绑定的c++办法中有一个Recv办法,该办法的作用时显露一个Typescript的函数给rust,在deno的io.ts文件的start办法中施行libdeno.recv(handleAsyncMsgFromRust),将handleAsyncMsgFromRust函数通过c++办法显露给rust。详细实现如下:

export function start(source?: string): msg.StartRes {
  libdeno.recv(handleAsyncMsgFromRust);

  // First we send an empty `Start` message to let the privileged side know we
  // are ready. The response should be a `StartRes` message containing the CLI
  // args and other info.
  const startResMsg = sendStart();

  util.setLogDebug(startResMsg.debugFlag(), source);

  setGlobals(startResMsg.pid(), startResMsg.noColor(), startResMsg.execPath()!);

  return startResMsg;
}

当异步操纵施行完成后,可以在rust中直接调取handleAsyncMsgFromRust办法,将结果返回给Typescript。先看一下handleAsyncMsgFromRust办法的实现细节:

export function handleAsyncMsgFromRust(ui8: Uint8Array): void {
  // If a the buffer is empty, recv() on the native side timed out and we
  // did not receive a message.
  if (ui8 && ui8.length) {
    const bb = new flatbuffers.ByteBuffer(ui8);
    const base = msg.Base.getRootAsBase(bb);
    const cmdId = base.cmdId();
    const promise = promiseTable.get(cmdId);
    util.assert(promise != null, `Expecting promise in table. ${cmdId}`);
    promiseTable.delete(cmdId);
    const err = errors.maybeError(base);
    if (err != null) {
      promise!.reject(err);
    } else {
      promise!.resolve(base);
    }
  }
  // Fire timers that have become runnable.
  fireTimers();
}

从代码handleAsyncMsgFromRust办法的实现中可以知道,第一通过flatbuffer反序列化返回的结果,然后猎取返回结果的cmdId,按照cmdId猎取此前创立的promise对象,然后调取promise.resolve办法触发promise.then中的代码施行。

以上就是deno通讯实现的办法(附代码)的具体内容,更多请关注百分百源码网其它相关文章!

打赏

打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

百分百源码网 建议打赏1~10元,土豪随意,感谢您的阅读!

共有151人阅读,期待你的评论!发表评论
昵称: 网址: 验证码: 点击我更换图片
最新评论

本文标签

广告赞助

能出一分力是一分吧!

订阅获得更多模板

本文标签

广告赞助

订阅获得更多模板