成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

deno原理篇-通信實現(xiàn)

ChristmasBoy / 3292人閱讀

摘要:理解基礎篇原理篇一啟動加載通信方式執(zhí)行代碼和相似,包含同步和異步的方式,異步方式通過的實現(xiàn)。同時在異步通信完成后,會創(chuàng)建一個對象,將作為,作為,加入中。

理解deno-基礎篇
deno-原理篇一啟動加載

通信方式

deno執(zhí)行代碼和node相似,包含同步和異步的方式, 異步方式通過async的實現(xiàn)。

Typescript/Javascript調(diào)用rust

在上一節(jié)中講到deno的啟動時會初始化v8 isolate實例,在初始化的過程中,會將c++的函數(shù)綁定到v8 isolate的實例上,在v8執(zhí)行Javascript代碼時,可以像調(diào)用Javascript函數(shù)一樣調(diào)用這些綁定的函數(shù)。具體的綁定實現(xiàn)如下:

void InitializeContext(v8::Isolate* isolate, v8::Local 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中可以通過如下代碼實現(xiàn)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;

在執(zhí)行Typescript代碼時,只需要引入libdeno,就直接調(diào)用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];
}

調(diào)用libdeno.send方法可以將數(shù)據(jù)傳給c++,然后通過c++去調(diào)用rust代碼實現(xiàn)具體的工程操作。

Typescript層同步異步實現(xiàn)
同步

在Typescript中只需要設置sendInternal方法的sync參數(shù)為true即可,在rust中會根據(jù)sync參數(shù)去判斷是執(zhí)行同步或者異步操作,如果sync為true,libdeono.send方法會返回執(zhí)行的結(jié)果,rust和typescript之間傳遞數(shù)據(jù)需要將數(shù)據(jù)序列化,這里序列化操作使用的是flatbuffer庫。

const [cmdId, resBuf] = sendInternal(builder, innerType, inner, data, true);
異步實現(xiàn)

同理,實現(xiàn)異步方式,只需要設置sync參數(shù)為false即可,但是異步操作和同步相比,多了回掉方法,在執(zhí)行異步通信時,libdeno.send方法會返回一個唯一的cmdId標志這次調(diào)用操作。同時在異步通信完成后,會創(chuàng)建一個promise對象,將cmdId作為key,promise作為value,加入map中。代碼如下:

const [cmdId, resBuf] = sendInternal(builder, innerType, inner, data, false);
  util.assert(resBuf == null);
  const promise = util.createResolvable();
  promiseTable.set(cmdId, promise);
  return promise;
rust實現(xiàn)同步和異步

當在Typescript中調(diào)用libdeno.send方法時,調(diào)用了C++文件binding.cc中的Send方法,該方法是在deno初始化時綁定到v8 isolate上去的。在Send方法中去調(diào)用了ops.rs文件中的dispatch方法,該方法實現(xiàn)了消息到函數(shù)的映射。每個類型的消息對應了一種函數(shù),例如讀文件消息對應了讀文件的函數(shù)。

pub fn dispatch(
  isolate: &Isolate,
  control: libdeno::deno_buf,
  data: libdeno::deno_buf,
) -> (bool, Box) {
  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 = 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)
  };

  // ...省略多余的代碼
}

在每個類型的函數(shù)中會根據(jù)在Typescript中調(diào)用libdeo.send方法時傳入的sync參數(shù)值去判斷同步執(zhí)行還是異步執(zhí)行。

let (is_sync, op) = dispatch(isolate, control_buf, zero_copy_buf);
同步執(zhí)行

在執(zhí)行dispatch方法后,會返回is_sync的變量,如果is_sync為true,表示該方法是同步執(zhí)行的,op表示返回的結(jié)果。rust代碼會調(diào)用c++文件api.cc中的deno_respond方法,將執(zhí)行結(jié)果同步回去,deno_respond方法中根據(jù)current_args_的值去判斷是否為同步消息,如果current_args_存在值,則直接返回結(jié)果。

異步執(zhí)行

在deno中,執(zhí)行異步操作是通過rust的Tokio模塊來實現(xiàn)的,在調(diào)用dispatch方法后,如果是異步操作,is_sync的值為false,op不再是執(zhí)行結(jié)果,而是一個執(zhí)行函數(shù)。通過tokio模塊派生一個線程程異步去執(zhí)行該函數(shù)。

    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初始化時,會創(chuàng)建一個管道,代碼如下:

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

管道可以實現(xiàn)不同線程之間的通信,由于異步操作是創(chuàng)建了一個新的線程去執(zhí)行的,所以子線程無法直接和主線程之間通信,需要通過管道的機制去實現(xiàn)。在異步代碼執(zhí)行完成后,調(diào)用tx.send方法將執(zhí)行結(jié)果加入管道里面,event loop會每次從管道里面去讀取結(jié)果返回回去。

Event Loop

由于異步操作依賴事件循環(huán),所以先解釋一下deno中的事件循環(huán),其實事件循環(huán)很簡單,就是一段循環(huán)執(zhí)行的代碼,當達到條件后,事件循環(huán)會結(jié)束執(zhí)行,deno中主要的事件循環(huán)代碼實現(xiàn)如下:

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方法用來判斷是否所有的異步操作都執(zhí)行完畢,當所有的異步操作都執(zhí)行完畢后,停止事件循環(huán),is_idle方法代碼如下:

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

當產(chǎn)生一次異步方法調(diào)用時,會調(diào)用下面的方法,使ntasks內(nèi)部的值加1,

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

在event loop循環(huán)中,每次從管道中去取值,這里event loop充消費者,執(zhí)行異步方法的子線程充當生產(chǎn)者。如果在一次事件循環(huán)中,獲取到了一次執(zhí)行結(jié)果,那么會調(diào)用ntasks_decrement方法,使ntasks內(nèi)部的值減1,當ntasks的值為0的時候,事件循環(huán)會退出執(zhí)行。在每次循環(huán)中,將管道中取得的值作為參數(shù),調(diào)用complete_op方法,將結(jié)果返回回去。

rust中將異步操作結(jié)果返回回去

在初始化v8實例時,綁定的c++方法中有一個Recv方法,該方法的作用時暴露一個Typescript的函數(shù)給rust,在deno的io.ts文件的start方法中執(zhí)行l(wèi)ibdeno.recv(handleAsyncMsgFromRust),將handleAsyncMsgFromRust函數(shù)通過c++方法暴露給rust。具體實現(xiàn)如下:

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;
}

當異步操作執(zhí)行完成后,可以在rust中直接調(diào)用handleAsyncMsgFromRust方法,將結(jié)果返回給Typescript。先看一下handleAsyncMsgFromRust方法的實現(xiàn)細節(jié):

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方法的實現(xiàn)中可以知道,首先通過flatbuffer反序列化返回的結(jié)果,然后獲取返回結(jié)果的cmdId,根據(jù)cmdId獲取之前創(chuàng)建的promise對象,然后調(diào)用promise.resolve方法觸發(fā)promise.then中的代碼執(zhí)行。

結(jié)尾

~下節(jié)講一下deno中import的實現(xiàn)~

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/109084.html

相關(guān)文章

  • 理解deno-基礎

    摘要:介紹是一個基于和的的安全運行時。文件中主要是的代碼,是功能的具體實現(xiàn)。圖來自于官網(wǎng),圖的架構(gòu)圖預告接下來還會有兩篇文章分析的內(nèi)部原理 deno介紹 deno是一個基于v8、rust和Tokio的Javascript/Typescript的安全運行時。它在內(nèi)部嵌入了一個typescript的編譯器??梢詫ypescript編譯成js然后運行在v8上,并通過c++ libdeno實現(xiàn)js...

    heartFollower 評論0 收藏0
  • 從源碼一步步學習,Ryan Dahl的Deno實現(xiàn)原理

    摘要:之父在中的設計錯誤演講中表示不允許將任意本地函數(shù)綁定至當中。所有系統(tǒng)調(diào)用都將通過消息傳遞完成序列化。兩項原生函數(shù)與。這既簡化了設計流程,又使得系統(tǒng)更易于審計。 Node之父ry:在Node中的設計錯誤演講中表示: 不允許將任意本地函數(shù)綁定至 V8 當中。 所有系統(tǒng)調(diào)用都將通過消息傳遞完成(protobuf 序列化)。 兩項原生函數(shù):send 與 recv。 這既簡化了設計流程,又使得...

    goji 評論0 收藏0
  • Deno 兼容瀏覽器具體指的是什么?

    摘要:里面有一句描述,可以看到的目標是兼容瀏覽器。那么這里的兼容瀏覽器到底如何是什么意思呢我簡單談談我的理解吧。很多人還有誤解以為兼容瀏覽器指的是會提供類似里的寫法。 Deno 里面有一句描述:Aims to be browser compatible,可以看到 Deno 的目標是兼容瀏覽器。那么這里的兼容瀏覽器到底如何是什么意思呢? 我簡單談談我的理解吧。 首先這里的兼容性肯定不是 Den...

    Yangyang 評論0 收藏0
  • Deno 并不是下一代 Node.js

    摘要:長文預警字,圖。開發(fā)并不是因為,也不是為了取代。不知道從官方介紹來看,可以認為它是下一代是如何腦補出來的。只是一個原型或?qū)嶒炐援a(chǎn)品。所以,不是要取代,也不是下一代,也不是要放棄重建生態(tài)。的目前是要擁抱瀏覽器生態(tài)。 這幾天前端圈最火的事件莫過于 ry(Ryan Dahl) 的新項目 deno 了,很多 IT 新聞和媒體都用了標題:下一代 Node.js。這周末讀了一遍 deno 的源碼,...

    mmy123456 評論0 收藏0
  • 我為 VS Code 開發(fā)了一個 Deno 插件

    摘要:自發(fā)布以來就備受關(guān)注,也有很多媒體和開發(fā)者稱為下一代。所以在寫這個插件之前,我又為寫了一個插件。插件提供了開箱即用的支持,開發(fā)者不需要任何配置,但是有一個前提是開發(fā)者需要使用內(nèi)置的。 這幾天為 Deno 開發(fā)了一個 VS Code 插件:Deno support for VSCode,GitHub 地址:https://github.com/justjavac/...。 自 Deno ...

    YanceyOfficial 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<