Repository: denoland/deno
File: ext/node/ops/handle_wrap.rs (Rust)

Code before:
  ) -> Result<(), ResourceError> {
    // Call _onClose() on the JS handles. Not needed for Rust handles.
    let this = v8::Local::new(scope, this);
    let on_close_str = ON_CLOSE_STR.v8_string(scope).unwrap();
    let onclose = this.get(scope, on_close_str.into());

    if let Some(onclose) = onclose {
      let fn_: v8::Local<v8::Function> = onclose.try_into().unwrap();
      fn_.call(scope, this.into(), &[]);
    }

    state
      .borrow()
      .borrow::<deno_core::V8TaskSpawner>()
      .spawn(|scope| {
        // Workaround for https://github.com/denoland/deno/pull/24656
        //
        // We need to delay 'cb' at least 2 ticks to avoid "close" event happening before "error"
        // event in net.Socket.
        //
        // This is a temporary solution. We should support async close like `uv_close(handle, close_cb)`.

        if let Some(cb) = cb {
          let recv = v8::undefined(scope);
          cb.open(scope).call(scope, recv.into(), &[]);
        }
      });

    Ok(())
  }

  #[fast]
  fn has_ref(&self, state: &mut OpState) -> bool {
    if let Some(handle) = self.handle {
      return state.has_ref(handle);
    }

    true
  }

  #[fast]
  #[rename("r#ref")]
  fn ref_(&self, state: &mut OpState) {
    if let Some(handle) = self.handle {
      state.uv_ref(handle);
    }
  }

  #[fast]
  fn unref(&self, state: &mut OpState) {
    if let Some(handle) = self.handle {

Review comment (suggestion):
This is effectively Node's OnClose callback, add a comment to the original source code: https://github.com/nodejs/node/blob/038d82980ab26cd79abe4409adc2fecad94d7c93/src/handle_wrap.cc#L135-L157

Add the equivalent of the following
```
  CHECK_EQ(wrap->state_, kClosing);
  wrap->state_ = kClosed;
```

Code after:
    state: &mut OpState,
    #[smi] provider: i32,
    #[smi] handle: Option<ResourceId>,
  ) -> (AsyncWrap, HandleWrap) {
    (
      AsyncWrap::create(state, provider),
      HandleWrap::create(handle),
    )
  }

  // Ported from Node.js
  //
  // https://github.com/nodejs/node/blob/038d82980ab26cd79abe4409adc2fecad94d7c93/src/handle_wrap.cc#L65-L85
  #[reentrant]
  fn close(
    &self,
    op_state: Rc<RefCell<OpState>>,
    #[this] this: v8::Global<v8::Object>,
    scope: &mut v8::HandleScope,
    #[global] cb: Option<v8::Global<v8::Function>>,
  ) -> Result<(), ResourceError> {
    if self.state.get() != State::Initialized {
      return Ok(());
    }

    let state = self.state.clone();
    // This effectively mimicks Node's OnClose callback.
    //
    // https://github.com/nodejs/node/blob/038d82980ab26cd79abe4409adc2fecad94d7c93/src/handle_wrap.cc#L135-L157
    let on_close = move |scope: &mut v8::HandleScope| {
      assert!(state.get() == State::Closing);
      state.set(State::Closed);

      // Workaround for https://github.com/denoland/deno/pull/24656
      //
      // We need to delay 'cb' at least 2 ticks to avoid "close" event happening before "error"
      // event in net.Socket.
      //
      // This is a temporary solution. We should support async close like `uv_close`.
      if let Some(cb) = cb {
        let recv = v8::undefined(scope);
        cb.open(scope).call(scope, recv.into(), &[]);
      }
    };

    uv_close(scope, op_state, this, on_close);
    self.state.set(State::Closing);

    Ok(())
  }