in-flight-limit: Add missing task notification. (#85)

Previously, there was no notification when capacity is made available by
requests completing. This patch fixes the bug.

This also switches the tests to use `MockTask` from tokio-test.
This commit is contained in:
Carl Lerche 2018-06-11 15:29:34 -07:00 committed by GitHub
parent 679dcbe327
commit 20fb04e3e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 50 additions and 25 deletions

View File

@ -9,4 +9,5 @@ futures = "0.1"
tower-service = { version = "0.1", path = "../tower-service" } tower-service = { version = "0.1", path = "../tower-service" }
[dev-dependencies] [dev-dependencies]
tokio-test = { git = "https://github.com/carllerche/tokio-test" }
tower-mock = { version = "0.1", path = "../tower-mock" } tower-mock = { version = "0.1", path = "../tower-mock" }

View File

@ -218,7 +218,14 @@ impl Shared {
/// request has completed OR the service that made the reservation has /// request has completed OR the service that made the reservation has
/// dropped. /// dropped.
pub fn release(&self) { pub fn release(&self) {
self.curr.fetch_sub(1, SeqCst); let prev = self.curr.fetch_sub(1, SeqCst);
// Cannot go above the max number of in-flight
debug_assert!(prev <= self.max);
if prev == self.max {
self.task.notify();
}
} }
} }

View File

@ -1,4 +1,5 @@
extern crate futures; extern crate futures;
extern crate tokio_test;
extern crate tower_mock; extern crate tower_mock;
extern crate tower_in_flight_limit; extern crate tower_in_flight_limit;
extern crate tower_service; extern crate tower_service;
@ -7,9 +8,12 @@ use tower_in_flight_limit::InFlightLimit;
use tower_service::Service; use tower_service::Service;
use futures::future::{Future, poll_fn}; use futures::future::{Future, poll_fn};
use tokio_test::MockTask;
#[test] #[test]
fn basic_service_limit_functionality_with_poll_ready() { fn basic_service_limit_functionality_with_poll_ready() {
let mut task = MockTask::new();
let (mut service, mut handle) = let (mut service, mut handle) =
new_service(2); new_service(2);
@ -19,10 +23,12 @@ fn basic_service_limit_functionality_with_poll_ready() {
poll_fn(|| service.poll_ready()).wait().unwrap(); poll_fn(|| service.poll_ready()).wait().unwrap();
let r2 = service.call("hello 2"); let r2 = service.call("hello 2");
with_task(|| { task.enter(|| {
assert!(service.poll_ready().unwrap().is_not_ready()); assert!(service.poll_ready().unwrap().is_not_ready());
}); });
assert!(!task.is_notified());
// The request gets passed through // The request gets passed through
let request = handle.next_request().unwrap(); let request = handle.next_request().unwrap();
assert_eq!(*request, "hello 1"); assert_eq!(*request, "hello 1");
@ -34,17 +40,21 @@ fn basic_service_limit_functionality_with_poll_ready() {
request.respond("world 2"); request.respond("world 2");
// There are no more requests // There are no more requests
with_task(|| { task.enter(|| {
assert!(handle.poll_request().unwrap().is_not_ready()); assert!(handle.poll_request().unwrap().is_not_ready());
}); });
assert_eq!(r1.wait().unwrap(), "world 1"); assert_eq!(r1.wait().unwrap(), "world 1");
assert!(task.is_notified());
// Another request can be sent // Another request can be sent
poll_fn(|| service.poll_ready()).wait().unwrap(); task.enter(|| {
assert!(service.poll_ready().unwrap().is_ready());
});
let r3 = service.call("hello 3"); let r3 = service.call("hello 3");
with_task(|| { task.enter(|| {
assert!(service.poll_ready().unwrap().is_not_ready()); assert!(service.poll_ready().unwrap().is_not_ready());
}); });
@ -60,6 +70,8 @@ fn basic_service_limit_functionality_with_poll_ready() {
#[test] #[test]
fn basic_service_limit_functionality_without_poll_ready() { fn basic_service_limit_functionality_without_poll_ready() {
let mut task = MockTask::new();
let (mut service, mut handle) = let (mut service, mut handle) =
new_service(2); new_service(2);
@ -79,7 +91,7 @@ fn basic_service_limit_functionality_without_poll_ready() {
request.respond("world 2"); request.respond("world 2");
// There are no more requests // There are no more requests
with_task(|| { task.enter(|| {
assert!(handle.poll_request().unwrap().is_not_ready()); assert!(handle.poll_request().unwrap().is_not_ready());
}); });
@ -103,17 +115,19 @@ fn basic_service_limit_functionality_without_poll_ready() {
#[test] #[test]
fn request_without_capacity() { fn request_without_capacity() {
let mut task = MockTask::new();
let (mut service, mut handle) = let (mut service, mut handle) =
new_service(0); new_service(0);
with_task(|| { task.enter(|| {
assert!(service.poll_ready().unwrap().is_not_ready()); assert!(service.poll_ready().unwrap().is_not_ready());
}); });
let response = service.call("hello"); let response = service.call("hello");
// There are no more requests // There are no more requests
with_task(|| { task.enter(|| {
assert!(handle.poll_request().unwrap().is_not_ready()); assert!(handle.poll_request().unwrap().is_not_ready());
}); });
@ -122,18 +136,20 @@ fn request_without_capacity() {
#[test] #[test]
fn reserve_capacity_without_sending_request() { fn reserve_capacity_without_sending_request() {
let mut task = MockTask::new();
let (mut s1, mut handle) = let (mut s1, mut handle) =
new_service(1); new_service(1);
let mut s2 = s1.clone(); let mut s2 = s1.clone();
// Reserve capacity in s1 // Reserve capacity in s1
with_task(|| { task.enter(|| {
assert!(s1.poll_ready().unwrap().is_ready()); assert!(s1.poll_ready().unwrap().is_ready());
}); });
// Service 2 cannot get capacity // Service 2 cannot get capacity
with_task(|| { task.enter(|| {
assert!(s2.poll_ready().unwrap().is_not_ready()); assert!(s2.poll_ready().unwrap().is_not_ready());
}); });
@ -142,50 +158,54 @@ fn reserve_capacity_without_sending_request() {
let request = handle.next_request().unwrap(); let request = handle.next_request().unwrap();
request.respond("world"); request.respond("world");
with_task(|| { task.enter(|| {
assert!(s2.poll_ready().unwrap().is_not_ready()); assert!(s2.poll_ready().unwrap().is_not_ready());
}); });
r1.wait().unwrap(); r1.wait().unwrap();
with_task(|| { task.enter(|| {
assert!(s2.poll_ready().unwrap().is_ready()); assert!(s2.poll_ready().unwrap().is_ready());
}); });
} }
#[test] #[test]
fn service_drop_frees_capacity() { fn service_drop_frees_capacity() {
let mut task = MockTask::new();
let (mut s1, _handle) = let (mut s1, _handle) =
new_service(1); new_service(1);
let mut s2 = s1.clone(); let mut s2 = s1.clone();
// Reserve capacity in s1 // Reserve capacity in s1
with_task(|| { task.enter(|| {
assert!(s1.poll_ready().unwrap().is_ready()); assert!(s1.poll_ready().unwrap().is_ready());
}); });
// Service 2 cannot get capacity // Service 2 cannot get capacity
with_task(|| { task.enter(|| {
assert!(s2.poll_ready().unwrap().is_not_ready()); assert!(s2.poll_ready().unwrap().is_not_ready());
}); });
drop(s1); drop(s1);
with_task(|| { task.enter(|| {
assert!(s2.poll_ready().unwrap().is_ready()); assert!(s2.poll_ready().unwrap().is_ready());
}); });
} }
#[test] #[test]
fn response_error_releases_capacity() { fn response_error_releases_capacity() {
let mut task = MockTask::new();
let (mut s1, mut handle) = let (mut s1, mut handle) =
new_service(1); new_service(1);
let mut s2 = s1.clone(); let mut s2 = s1.clone();
// Reserve capacity in s1 // Reserve capacity in s1
with_task(|| { task.enter(|| {
assert!(s1.poll_ready().unwrap().is_ready()); assert!(s1.poll_ready().unwrap().is_ready());
}); });
@ -196,33 +216,35 @@ fn response_error_releases_capacity() {
r1.wait().unwrap_err(); r1.wait().unwrap_err();
with_task(|| { task.enter(|| {
assert!(s2.poll_ready().unwrap().is_ready()); assert!(s2.poll_ready().unwrap().is_ready());
}); });
} }
#[test] #[test]
fn response_future_drop_releases_capacity() { fn response_future_drop_releases_capacity() {
let mut task = MockTask::new();
let (mut s1, _handle) = let (mut s1, _handle) =
new_service(1); new_service(1);
let mut s2 = s1.clone(); let mut s2 = s1.clone();
// Reserve capacity in s1 // Reserve capacity in s1
with_task(|| { task.enter(|| {
assert!(s1.poll_ready().unwrap().is_ready()); assert!(s1.poll_ready().unwrap().is_ready());
}); });
// s1 sends the request, then s2 is able to get capacity // s1 sends the request, then s2 is able to get capacity
let r1 = s1.call("hello"); let r1 = s1.call("hello");
with_task(|| { task.enter(|| {
assert!(s2.poll_ready().unwrap().is_not_ready()); assert!(s2.poll_ready().unwrap().is_not_ready());
}); });
drop(r1); drop(r1);
with_task(|| { task.enter(|| {
assert!(s2.poll_ready().unwrap().is_ready()); assert!(s2.poll_ready().unwrap().is_ready());
}); });
} }
@ -235,8 +257,3 @@ fn new_service(max: usize) -> (InFlightLimit<Mock>, Handle) {
let service = InFlightLimit::new(service, max); let service = InFlightLimit::new(service, max);
(service, handle) (service, handle)
} }
fn with_task<F: FnOnce() -> U, U>(f: F) -> U {
use futures::future::{Future, lazy};
lazy(|| Ok::<_, ()>(f())).wait().unwrap()
}