W1036

W1036 — A drop calls send or recv, which wait with no time limit of their own, outside a within block. A drop runs with cancellation off, so when the other end never acts, the wait holds up the drop until the limit of the drop: its type's wait_limit, 10 seconds by default. Then the wait gives up, as in a cancelled block. A wait_limit that calls them gets the warning too, because it runs before the drop and should only return a number.

What the compiler reports

Warning[W1036] at {{.*}}a_drop_that_waits_with_no_time_limit.vx:[[@LINE+15]]:{{[0-9]+}}: 'send' waits with no time limit of its own in a 'drop'. A drop runs with cancellation off, so if the other end of the channel never acts, this wait holds up the drop until the limit of the drop: its type's 'wait_limit', 10 seconds by default. Use 'send_for' with a time limit, or put the wait in a 'within' block
Warning[W1036] at {{.*}}a_drop_that_waits_with_no_time_limit.vx:[[@LINE+15]]:{{[0-9]+}}: 'recv' waits with no time limit of its own in a 'drop'. {{.*}} Use 'recv_for' with a time limit
Warning[W1036] at {{.*}}a_drop_that_waits_with_no_time_limit.vx:[[@LINE+26]]:{{[0-9]+}}: 'recv' waits in 'wait_limit'. 'wait_limit' runs before the drop, to say how long its waits can take, so it should return a number and not wait

The fragments the test suite holds the compiler to. An ellipsis marks text the test does not constrain; the emitted message also carries a source location.

A program that triggers it

//
// Part of the Vx Project, under the Apache License v2.0 with LLVM Exceptions.
// See LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// A `drop` runs with cancellation off, so a `send` or `recv` in it can wait until the limit of the
// drop. The compiler warns about each one (W1036). `send_for` and `recv_for` have a time limit, and
// the same `send` in a normal method gets no warning. A wait in a `within` block has the block's
// limit, so it gets no warning. `wait_limit` should only return a number, so a wait in it gets
// the warning too.
//

import std::sync::channel;

struct SlotGuard {
  slot : i64,
  to_pool : Sender<i64>,
  from_pool : Receiver<i64>,
}

impl Drop for SlotGuard {
  fn drop(self : &mut SlotGuard) -> void {
    // Each of these two calls waits with no time limit.
    let _sent = self.to_pool.send(self.slot);
    let _got = self.from_pool.recv();
    // These two calls have a time limit.
    let _sent_for = self.to_pool.send_for(self.slot, 0.1);
    let _got_for = self.from_pool.recv_for(0.1);
    // So does this one: the limit of its block.
    within(0.1) {
      let _got_within = self.from_pool.recv();
    }
  }

  fn wait_limit(self : &SlotGuard) -> f64 {
    // This call waits before the drop starts.
    let _got = self.from_pool.recv();
    return 0.5;
  }
}

impl SlotGuard {
  fn give_back(self : &SlotGuard) -> void {
    // Not a drop, so a cancel can stop this wait.
    let _sent = self.to_pool.send(self.slot);
  }
}

fn main() -> i32 {
  let (tx, rx) = channel<i64>(4);
  let g = SlotGuard {
    slot: 1, to_pool: tx, from_pool: rx
  };
  g.give_back();
  return 0;
}

From tests/frontend/pass/a_drop_that_waits_with_no_time_limit.vx, which asserts this diagnostic on every commit.

Related