Skip to main content

netget/llm/
rate_limiter.rs

1//! LLM rate limiter with concurrency control and token-based throttling
2//!
3//! This module provides a rate limiter that controls:
4//! 1. **Concurrency**: Maximum number of concurrent LLM requests
5//! 2. **Token usage**: Maximum tokens per time window (for API usage control)
6//!
7//! # Concurrency is a queue, not a filter
8//!
9//! `max_concurrent` bounds how many LLM calls run *at once*; it does not decide
10//! which requests are worth serving. A request that arrives while the limit is
11//! saturated **waits for a permit** — including network-sourced ones. This is
12//! what `--llm-max-concurrent 1` ("sequential processing") has always claimed to
13//! mean, and it is now what it does.
14//!
15//! Network requests used to `try_acquire` and fail instantly instead, which in
16//! the shipped default configuration (`max_concurrent: 1`) meant that a second
17//! simultaneous connection had its LLM call refused, its handler returned `Err`,
18//! and most protocols write nothing on that path — so the peer hung until its own
19//! timeout with nothing on the wire. Two concurrent `curl`s were enough.
20//!
21//! The wait is bounded in **two** dimensions, because an unbounded queue of
22//! network requests is its own denial of service (a slow backend plus a burst of
23//! connections becomes unbounded memory, and every peer waits forever):
24//!
25//! - [`RateLimiterConfig::max_queued`] caps how many network requests may be
26//!   waiting at once. Past that the limiter fails fast, so the overload is
27//!   reported at the edge instead of being absorbed into RAM.
28//! - [`RateLimiterConfig::queue_timeout_secs`] caps how long any one of them
29//!   waits. It defaults to one backend request timeout: with a healthy backend a
30//!   permit frees within at most one in-flight call, so exceeding it means the
31//!   queue ahead is genuinely deeper than the system can drain.
32//!
33//! Both failures surface as a typed [`RateLimitError`], so a protocol can tell
34//! "the system is overloaded" apart from "the model said something unusable" and
35//! answer the peer accordingly (HTTP does: 503 rather than 500). Use
36//! [`is_overload_error`] to test an [`anyhow::Error`] for it.
37//!
38//! Token-limit exhaustion is different in kind and still discards network
39//! requests: it is a *budget* that will not free up on the timescale of a
40//! request/response exchange, so waiting for it would only convert a fast refusal
41//! into a slow one.
42
43use crate::utils::clock::Instant;
44use anyhow::{Context, Result};
45use std::collections::VecDeque;
46use std::future::Future;
47use std::pin::Pin;
48use std::sync::atomic::{AtomicUsize, Ordering};
49use std::sync::Arc;
50use std::task::{Context as TaskContext, Poll, Waker};
51use std::time::Duration;
52use tokio::sync::{Mutex, RwLock};
53use tracing::{debug, info, warn};
54
55/// Default bound on how long a network-sourced request waits for a concurrency
56/// permit.
57///
58/// Deliberately equal to [`crate::llm::ollama_client::DEFAULT_REQUEST_TIMEOUT`]:
59/// the request holding the permit cannot itself run longer than that, so a
60/// shorter wait would reject requests that were about to be served, and a longer
61/// one would keep queueing behind a backlog the backend is not draining.
62///
63/// *Derived* from it rather than restated, because the equality is load-bearing and a
64/// hand-copied `120` silently stopped being equal the moment the request timeout moved.
65pub const DEFAULT_QUEUE_TIMEOUT_SECS: u64 =
66    crate::llm::ollama_client::DEFAULT_REQUEST_TIMEOUT.as_secs();
67
68/// Default bound on how many network-sourced requests may wait for a permit at
69/// once. Beyond this the limiter fails fast rather than growing the queue.
70pub const DEFAULT_MAX_QUEUED: usize = 128;
71
72/// Source of the LLM request
73#[derive(Debug, Clone, Copy, PartialEq, Eq)]
74pub enum RequestSource {
75    /// User input (waits for capacity, unbounded)
76    User,
77    /// Network event (waits for a concurrency permit within the configured
78    /// bounds; discarded outright only when the token budget is exhausted)
79    Network,
80}
81
82/// Why the rate limiter refused a request.
83///
84/// Carried as the source of the [`anyhow::Error`] returned by
85/// [`RateLimiter::acquire_permit`] so callers can distinguish an overloaded
86/// system from a failed LLM call. See [`is_overload_error`].
87#[derive(Debug, Clone, Copy, PartialEq, Eq)]
88pub enum RateLimitError {
89    /// Waited the full [`RateLimiterConfig::queue_timeout_secs`] without a
90    /// concurrency permit becoming available.
91    QueueTimeout {
92        /// How long the request waited, in seconds.
93        waited_secs: u64,
94    },
95    /// Too many network requests were already waiting for a permit.
96    QueueFull {
97        /// The configured [`RateLimiterConfig::max_queued`].
98        max_queued: usize,
99    },
100    /// The token budget for the current window is exhausted.
101    TokenLimit {
102        /// The configured token limit.
103        limit: u64,
104        /// The window the limit applies to, in seconds.
105        window_secs: u64,
106    },
107}
108
109impl std::fmt::Display for RateLimitError {
110    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111        match self {
112            Self::QueueTimeout { waited_secs } => write!(
113                f,
114                "LLM backend overloaded: waited {}s for a concurrency permit without one becoming available",
115                waited_secs
116            ),
117            Self::QueueFull { max_queued } => write!(
118                f,
119                "LLM backend overloaded: {} requests already queued for a concurrency permit",
120                max_queued
121            ),
122            Self::TokenLimit { limit, window_secs } => write!(
123                f,
124                "LLM token budget exhausted: {} tokens per {}s window",
125                limit, window_secs
126            ),
127        }
128    }
129}
130
131impl std::error::Error for RateLimitError {}
132
133/// Whether `err` (or anything in its context chain) is a rate-limiter refusal.
134///
135/// Protocols use this to answer a peer with an overload status instead of a
136/// generic failure — the distinction matters because an overload is transient
137/// and retryable while a malformed model answer is not.
138pub fn is_overload_error(err: &anyhow::Error) -> bool {
139    err.downcast_ref::<RateLimitError>().is_some()
140}
141
142/// Token usage record with timestamp
143#[derive(Debug, Clone)]
144struct TokenUsage {
145    timestamp: Instant,
146    input_tokens: u64,
147    output_tokens: u64,
148}
149
150/// Rate limiter configuration
151#[derive(Debug, Clone)]
152pub struct RateLimiterConfig {
153    /// Maximum concurrent LLM requests (default: 1)
154    pub max_concurrent: usize,
155
156    /// Maximum tokens per time window (None = unlimited)
157    /// Default: None for local Ollama, recommended: 10000 for cloud APIs
158    pub token_limit: Option<u64>,
159
160    /// Time window for token limiting in seconds (default: 60)
161    pub token_window_secs: u64,
162
163    /// How long a network-sourced request waits for a concurrency permit before
164    /// giving up (default: [`DEFAULT_QUEUE_TIMEOUT_SECS`]). `0` means wait
165    /// forever — only sane when the peer has its own timeout and you would
166    /// rather it drove the deadline.
167    pub queue_timeout_secs: u64,
168
169    /// How many network-sourced requests may wait for a concurrency permit at
170    /// once (default: [`DEFAULT_MAX_QUEUED`]). `0` means unbounded, which trades
171    /// the memory bound away; prefer a large number to zero.
172    pub max_queued: usize,
173}
174
175impl Default for RateLimiterConfig {
176    fn default() -> Self {
177        Self {
178            max_concurrent: 1,
179            token_limit: None,
180            token_window_secs: 60,
181            queue_timeout_secs: DEFAULT_QUEUE_TIMEOUT_SECS,
182            max_queued: DEFAULT_MAX_QUEUED,
183        }
184    }
185}
186
187/// LLM rate limiter with concurrency and token-based throttling
188#[derive(Clone)]
189pub struct RateLimiter {
190    /// One resizable gate shared by old permits and new waiters.
191    semaphore: Arc<ConcurrencyGate>,
192
193    /// Configuration (can be updated at runtime)
194    config: Arc<RwLock<RateLimiterConfig>>,
195
196    /// Token usage history (protected by mutex for interior mutability)
197    token_usage: Arc<Mutex<Vec<TokenUsage>>>,
198
199    /// Network-sourced requests currently waiting for a concurrency permit.
200    /// Kept outside `stats` so the queue-depth check on the hot path never
201    /// contends with the stats mutex.
202    queued_network: Arc<AtomicUsize>,
203    waiting_users: Arc<AtomicUsize>,
204
205    /// Statistics
206    stats: Arc<Mutex<RateLimiterStats>>,
207}
208
209/// Decrements the queue-depth counter however the wait ends — permit acquired,
210/// timed out, or task cancelled mid-`await`. A plain `fetch_sub` after the await
211/// would leak the slot on cancellation, and a leaked slot is permanent: the
212/// limiter would refuse ever more requests as `QueueFull` with nothing actually
213/// queued.
214struct QueueSlot(Arc<AtomicUsize>);
215
216impl Drop for QueueSlot {
217    fn drop(&mut self) {
218        self.0.fetch_sub(1, Ordering::AcqRel);
219    }
220}
221
222/// Rate limiter statistics
223#[derive(Debug, Default, Clone)]
224pub struct RateLimiterStats {
225    /// Total requests attempted
226    pub total_requests: u64,
227
228    /// Requests completed
229    pub requests_completed: u64,
230
231    /// Requests refused outright (token budget exhausted, or the wait queue was
232    /// already full)
233    pub requests_discarded: u64,
234
235    /// Requests waiting (user inputs that are queued)
236    pub requests_waiting: u64,
237
238    /// Network requests that had to wait for a concurrency permit rather than
239    /// getting one immediately
240    pub requests_queued: u64,
241
242    /// Network requests that gave up after waiting the full queue timeout
243    pub requests_queue_timed_out: u64,
244
245    /// Network requests waiting for a concurrency permit right now
246    pub currently_queued: u64,
247
248    /// Total input tokens processed
249    pub total_input_tokens: u64,
250
251    /// Total output tokens processed
252    pub total_output_tokens: u64,
253
254    /// Current tokens in window
255    pub current_window_tokens: u64,
256}
257
258impl RateLimiter {
259    /// Create a new rate limiter with the given configuration
260    pub fn new(config: RateLimiterConfig) -> Self {
261        let max_concurrent = config.max_concurrent;
262
263        Self {
264            semaphore: Arc::new(ConcurrencyGate::new(max_concurrent)),
265            config: Arc::new(RwLock::new(config)),
266            token_usage: Arc::new(Mutex::new(Vec::new())),
267            queued_network: Arc::new(AtomicUsize::new(0)),
268            waiting_users: Arc::new(AtomicUsize::new(0)),
269            stats: Arc::new(Mutex::new(RateLimiterStats::default())),
270        }
271    }
272
273    /// Update the rate limiter configuration at runtime
274    pub async fn update_config(&self, config: RateLimiterConfig) -> Result<()> {
275        let mut current_config = self.config.write().await;
276
277        // Existing requests count against the new limit. Shrinking never revokes a
278        // running request; new admission waits for the excess requests to finish.
279        self.semaphore.resize(config.max_concurrent);
280
281        *current_config = config;
282        info!(
283            "Rate limiter config updated: max_concurrent={}, token_limit={:?}, window={}s, queue_timeout={}s, max_queued={}",
284            current_config.max_concurrent,
285            current_config.token_limit,
286            current_config.token_window_secs,
287            current_config.queue_timeout_secs,
288            current_config.max_queued
289        );
290
291        Ok(())
292    }
293
294    /// Get current configuration
295    pub async fn get_config(&self) -> RateLimiterConfig {
296        self.config.read().await.clone()
297    }
298
299    /// Get current statistics
300    pub async fn get_stats(&self) -> RateLimiterStats {
301        // Update current window tokens before returning stats
302        let window = Duration::from_secs(self.config.read().await.token_window_secs);
303
304        // Snapshot usage before locking statistics. Never nest the config and stats
305        // guards: a pending config writer can otherwise deadlock concurrent readers.
306        // Compare ages rather than subtracting an arbitrary user-supplied window from
307        // Instant, which panics when the duration cannot be represented by the clock.
308        let token_usage = self.token_usage.lock().await;
309        let window_tokens: u64 = token_usage
310            .iter()
311            .filter(|usage| usage.timestamp.elapsed() <= window)
312            .fold(0u64, |total, usage| {
313                total.saturating_add(usage.input_tokens.saturating_add(usage.output_tokens))
314            });
315        drop(token_usage);
316
317        let mut stats = self.stats.lock().await;
318        stats.current_window_tokens = window_tokens;
319        stats.currently_queued = self.queued_network.load(Ordering::Acquire) as u64;
320        stats.requests_waiting = self.waiting_users.load(Ordering::Acquire) as u64;
321        stats.clone()
322    }
323
324    /// Drop token-usage records that have fallen out of the current window.
325    ///
326    /// Called unconditionally rather than only on the token-limited path: the
327    /// history is appended to on *every* LLM call, so skipping the prune when
328    /// `token_limit` is `None` — the default, and therefore what every long-lived
329    /// `netget --mcp` runs — grew the vector for the life of the process and made
330    /// `get_stats()` scan all of it.
331    async fn prune_token_usage(&self, window_secs: u64) {
332        let window = Duration::from_secs(window_secs);
333        self.token_usage
334            .lock()
335            .await
336            .retain(|usage| usage.timestamp.elapsed() <= window);
337    }
338
339    /// Check if we have token capacity available
340    async fn check_token_capacity(&self) -> Result<bool> {
341        let config = self.config.read().await.clone();
342        let window_secs = config.token_window_secs;
343
344        // Prune before the early return, not after it.
345        self.prune_token_usage(window_secs).await;
346
347        // If no token limit, always have capacity
348        let Some(token_limit) = config.token_limit else {
349            return Ok(true);
350        };
351
352        let token_usage = self.token_usage.lock().await;
353
354        // Calculate total tokens in current window
355        let window_tokens: u64 = token_usage.iter().fold(0u64, |total, usage| {
356            total.saturating_add(usage.input_tokens.saturating_add(usage.output_tokens))
357        });
358
359        debug!(
360            "Token usage check: {}/{} tokens in {}s window",
361            window_tokens, token_limit, config.token_window_secs
362        );
363
364        Ok(window_tokens < token_limit)
365    }
366
367    /// Record token usage after a successful LLM call
368    pub async fn record_token_usage(&self, input_tokens: u64, output_tokens: u64) {
369        let usage = TokenUsage {
370            timestamp: Instant::now(),
371            input_tokens,
372            output_tokens,
373        };
374
375        // Add to history, then drop anything that has aged out of the window.
376        // Pruning here (and not only in `check_token_capacity`) is what actually
377        // bounds the vector: with the default `token_limit: None` the capacity
378        // check returns before it ever needs the history.
379        let window_secs = self.config.read().await.token_window_secs;
380        self.token_usage.lock().await.push(usage);
381        self.prune_token_usage(window_secs).await;
382
383        // Update stats
384        let mut stats = self.stats.lock().await;
385        stats.total_input_tokens = stats.total_input_tokens.saturating_add(input_tokens);
386        stats.total_output_tokens = stats.total_output_tokens.saturating_add(output_tokens);
387        stats.requests_completed += 1;
388
389        debug!(
390            "Recorded token usage: input={}, output={}, total_requests={}",
391            input_tokens, output_tokens, stats.requests_completed
392        );
393    }
394
395    /// Number of token-usage records currently retained.
396    ///
397    /// Exposed so tests can pin the pruning behaviour; the history is otherwise
398    /// an implementation detail.
399    pub async fn token_history_len(&self) -> usize {
400        self.token_usage.lock().await.len()
401    }
402
403    /// Enforce the token budget both before queueing and after a permit frees.
404    async fn wait_for_token_capacity(&self, source: RequestSource) -> Result<()> {
405        // Check token capacity
406        let has_token_capacity = self.check_token_capacity().await?;
407
408        if !has_token_capacity {
409            match source {
410                RequestSource::User => {
411                    // For user requests, wait for token capacity
412                    info!("Token limit reached, waiting for capacity (user request)");
413
414                    // Poll until we have capacity (with exponential backoff)
415                    let mut delay = Duration::from_millis(100);
416                    while !self.check_token_capacity().await? {
417                        tokio::time::sleep(delay).await;
418                        delay = std::cmp::min(delay * 2, Duration::from_secs(5));
419                    }
420
421                    info!("Token capacity available, proceeding with user request");
422                }
423                RequestSource::Network => {
424                    // Token exhaustion is a budget, not congestion: it will not
425                    // clear on the timescale of one request, so waiting would
426                    // only turn a fast refusal into a slow one.
427                    self.stats.lock().await.requests_discarded += 1;
428                    let config = self.config.read().await.clone();
429                    warn!(
430                        "Discarding network event: Token limit ({}/{} tokens in {}s window)",
431                        self.token_usage
432                            .lock()
433                            .await
434                            .iter()
435                            .fold(0u64, |total, usage| {
436                                total.saturating_add(
437                                    usage.input_tokens.saturating_add(usage.output_tokens),
438                                )
439                            }),
440                        config.token_limit.unwrap_or(0),
441                        config.token_window_secs
442                    );
443
444                    return Err(anyhow::Error::new(RateLimitError::TokenLimit {
445                        limit: config.token_limit.unwrap_or(0),
446                        window_secs: config.token_window_secs,
447                    }));
448                }
449            }
450        }
451
452        Ok(())
453    }
454
455    /// Acquire a permit for an LLM request
456    ///
457    /// - For `RequestSource::User`: waits until capacity is available, without a
458    ///   deadline. A human sitting at the TUI would rather wait than be refused.
459    /// - For `RequestSource::Network`: waits for a concurrency permit within the
460    ///   configured queue bounds, so `max_concurrent: 1` serializes requests
461    ///   instead of dropping them. Refused with a [`RateLimitError`] when the
462    ///   queue is full, the wait times out, or the token budget is exhausted.
463    ///
464    /// Returns a permit guard that must be held for the duration of the LLM call.
465    /// The permit is automatically released when the guard is dropped.
466    pub async fn acquire_permit(&self, source: RequestSource) -> Result<RateLimiterPermit> {
467        // Update stats
468        {
469            let mut stats = self.stats.lock().await;
470            stats.total_requests += 1;
471        }
472
473        let _user_wait = (source == RequestSource::User).then(|| {
474            self.waiting_users.fetch_add(1, Ordering::AcqRel);
475            QueueSlot(Arc::clone(&self.waiting_users))
476        });
477        debug!("Acquiring rate limiter permit for {:?} request", source);
478
479        self.wait_for_token_capacity(source).await?;
480
481        // Queue policy is snapshotted for this wait; concurrency admission always
482        // uses the live gate so reconfiguration cannot create overlapping pools.
483        let semaphore = Arc::clone(&self.semaphore);
484        let (queue_timeout_secs, max_queued, max_concurrent) = {
485            let config = self.config.read().await;
486            (
487                config.queue_timeout_secs,
488                config.max_queued,
489                config.max_concurrent,
490            )
491        };
492
493        // Acquire concurrency permit
494        let permit = match source {
495            RequestSource::User => {
496                // For user requests, wait for permit
497                debug!("Waiting for concurrency permit (user request)");
498                semaphore
499                    .acquire_owned()
500                    .await
501                    .context("Failed to acquire semaphore permit")?
502            }
503            RequestSource::Network => {
504                // Fast path: capacity available right now, nothing to queue.
505                match Arc::clone(&semaphore).try_acquire_owned() {
506                    Ok(permit) => permit,
507                    Err(_) => {
508                        // Saturated. Queue, within bounds.
509                        let depth = self.queued_network.fetch_add(1, Ordering::AcqRel) + 1;
510                        let _slot = QueueSlot(Arc::clone(&self.queued_network));
511
512                        if max_queued > 0 && depth > max_queued {
513                            let mut stats = self.stats.lock().await;
514                            stats.requests_discarded += 1;
515                            warn!(
516                                "Refusing network event: {} requests already waiting for one of {} concurrency permits",
517                                max_queued, max_concurrent
518                            );
519                            return Err(anyhow::Error::new(RateLimitError::QueueFull {
520                                max_queued,
521                            }));
522                        }
523
524                        {
525                            let mut stats = self.stats.lock().await;
526                            stats.requests_queued += 1;
527                        }
528                        debug!(
529                            "Network event queued for a concurrency permit ({} waiting, max_concurrent={})",
530                            depth, max_concurrent
531                        );
532
533                        let acquire = semaphore.acquire_owned();
534                        let acquired = if queue_timeout_secs == 0 {
535                            acquire.await.map(Some).map_err(anyhow::Error::from)
536                        } else {
537                            match tokio::time::timeout(
538                                Duration::from_secs(queue_timeout_secs),
539                                acquire,
540                            )
541                            .await
542                            {
543                                Ok(result) => result.map(Some).map_err(anyhow::Error::from),
544                                Err(_elapsed) => Ok(None),
545                            }
546                        };
547
548                        match acquired.context("Failed to acquire semaphore permit")? {
549                            Some(permit) => permit,
550                            None => {
551                                let mut stats = self.stats.lock().await;
552                                stats.requests_queue_timed_out += 1;
553                                warn!(
554                                    "Refusing network event: waited {}s for one of {} concurrency permits",
555                                    queue_timeout_secs, max_concurrent
556                                );
557                                return Err(anyhow::Error::new(RateLimitError::QueueTimeout {
558                                    waited_secs: queue_timeout_secs,
559                                }));
560                            }
561                        }
562                    }
563                }
564            }
565        };
566
567        // An earlier request may have spent the remaining budget while this one
568        // waited for its concurrency permit. Its pre-queue check is no longer enough.
569        self.wait_for_token_capacity(source).await?;
570
571        debug!("Rate limiter permit acquired for {:?} request", source);
572
573        Ok(RateLimiterPermit {
574            _permit: permit,
575            rate_limiter: self.clone(),
576        })
577    }
578}
579
580/// RAII guard for a rate limiter permit
581///
582/// Automatically releases the concurrency permit when dropped.
583pub struct RateLimiterPermit {
584    _permit: ConcurrencyPermit,
585    rate_limiter: RateLimiter,
586}
587
588impl RateLimiterPermit {
589    /// Record token usage for this request
590    pub async fn record_usage(&self, input_tokens: u64, output_tokens: u64) {
591        self.rate_limiter
592            .record_token_usage(input_tokens, output_tokens)
593            .await;
594    }
595}
596
597// A FIFO gate keeps resize, release and cancellation synchronous. Awaiting tasks
598// hold no lock; dropping a queued future removes its slot and wakes its successor.
599struct ConcurrencyGate {
600    state: std::sync::Mutex<ConcurrencyState>,
601}
602struct ConcurrencyState {
603    limit: usize,
604    active: usize,
605    next_id: u64,
606    queue: VecDeque<(u64, Waker)>,
607}
608impl ConcurrencyGate {
609    fn new(limit: usize) -> Self {
610        Self {
611            state: std::sync::Mutex::new(ConcurrencyState {
612                limit,
613                active: 0,
614                next_id: 0,
615                queue: VecDeque::new(),
616            }),
617        }
618    }
619    fn lock(&self) -> std::sync::MutexGuard<'_, ConcurrencyState> {
620        self.state.lock().unwrap_or_else(|e| e.into_inner())
621    }
622    fn resize(&self, limit: usize) {
623        let wake = {
624            let mut state = self.lock();
625            state.limit = limit;
626            state.queue.front().map(|(_, w)| w.clone())
627        };
628        if let Some(w) = wake {
629            w.wake();
630        }
631    }
632    fn try_acquire_owned(self: Arc<Self>) -> Result<ConcurrencyPermit, ()> {
633        {
634            let mut state = self.lock();
635            if state.active >= state.limit || !state.queue.is_empty() {
636                return Err(());
637            }
638            state.active += 1;
639        }
640        Ok(ConcurrencyPermit(self))
641    }
642    fn acquire_owned(self: Arc<Self>) -> ConcurrencyWait {
643        ConcurrencyWait {
644            gate: self,
645            id: None,
646        }
647    }
648}
649struct ConcurrencyWait {
650    gate: Arc<ConcurrencyGate>,
651    id: Option<u64>,
652}
653impl Future for ConcurrencyWait {
654    type Output = Result<ConcurrencyPermit>;
655    fn poll(self: Pin<&mut Self>, cx: &mut TaskContext<'_>) -> Poll<Self::Output> {
656        let this = self.get_mut();
657        let gate = Arc::clone(&this.gate);
658        let mut state = gate.lock();
659        let first = this.id.map_or(state.queue.is_empty(), |id| {
660            state.queue.front().is_some_and(|(front, _)| *front == id)
661        });
662        if first && state.active < state.limit {
663            if this.id.take().is_some() {
664                state.queue.pop_front();
665            }
666            state.active += 1;
667            let wake = if state.active < state.limit {
668                state.queue.front().map(|(_, w)| w.clone())
669            } else {
670                None
671            };
672            drop(state);
673            if let Some(w) = wake {
674                w.wake();
675            }
676            return Poll::Ready(Ok(ConcurrencyPermit(gate)));
677        }
678        if let Some(id) = this.id {
679            if let Some((_, w)) = state.queue.iter_mut().find(|(entry, _)| *entry == id) {
680                w.clone_from(cx.waker());
681            }
682        } else {
683            let id = state.next_id;
684            state.next_id = state.next_id.wrapping_add(1);
685            state.queue.push_back((id, cx.waker().clone()));
686            this.id = Some(id);
687        }
688        Poll::Pending
689    }
690}
691impl Drop for ConcurrencyWait {
692    fn drop(&mut self) {
693        if let Some(id) = self.id {
694            let wake = {
695                let mut state = self.gate.lock();
696                state.queue.retain(|(entry, _)| *entry != id);
697                state.queue.front().map(|(_, w)| w.clone())
698            };
699            if let Some(w) = wake {
700                w.wake();
701            }
702        }
703    }
704}
705struct ConcurrencyPermit(Arc<ConcurrencyGate>);
706impl Drop for ConcurrencyPermit {
707    fn drop(&mut self) {
708        let wake = {
709            let mut state = self.0.lock();
710            state.active -= 1;
711            state.queue.front().map(|(_, w)| w.clone())
712        };
713        if let Some(w) = wake {
714            w.wake();
715        }
716    }
717}