<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://asyncband.apache.org/blog/</id>
    <title>Apache Asyncband (Incubating) Blog</title>
    <updated>2026-10-15T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://asyncband.apache.org/blog/"/>
    <subtitle>Apache Asyncband (Incubating) Blog</subtitle>
    <icon>https://asyncband.apache.org/favicon.svg</icon>
    <entry>
        <title type="html"><![CDATA[Moving from tokio::sync to Asyncband]]></title>
        <id>https://asyncband.apache.org/blog/2026/10/15/tokio-field-map/</id>
        <link href="https://asyncband.apache.org/blog/2026/10/15/tokio-field-map/"/>
        <updated>2026-10-15T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[A guide to corresponding synchronization APIs and the differences in initialization, notifications, permit accounting, and channel behavior.]]></summary>
        <content type="html"><![CDATA[<p>Asyncband is a collection of asynchronous synchronization primitives. Several have counterparts in <code>tokio::sync</code>, with different API choices; others provide additional forms of coordination. For a project already using Tokio, the practical questions are which operations have a corresponding API and which assumptions need to change.</p>
<p>Runtime independence is shared by both libraries. Tokio documents its <a href="https://docs.rs/tokio/1.53.3/tokio/sync/index.html#runtime-compatibility" target="_blank" rel="noopener noreferrer" class="">synchronization primitives as runtime agnostic</a>, with an exception for methods such as <code>send_timeout</code> that use its timer. Asyncband can also run on a Tokio executor. The choice of synchronization API does not by itself require a change of runtime.</p>
<p><em>This guide compares Asyncband 0.7.3 with Tokio 1.53.3. Links point to those versions.</em></p>
<h2 class="anchor anchorTargetStickyNavbar_zq6S" id="modules-and-features">Modules and features<a href="https://asyncband.apache.org/blog/2026/10/15/tokio-field-map/#modules-and-features" class="hash-link" aria-label="Direct link to Modules and features" title="Direct link to Modules and features" translate="no">​</a></h2>
<p>Asyncband has no default features. Enable the primitives your code uses:</p>
<div class="language-toml codeBlockContainer_lkHg theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_KnrP"><pre tabindex="0" class="prism-code language-toml codeBlock_FDzL thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_pYqn"><div class="token-line" style="color:#393A34"><span class="token key property" style="color:#36acaa">asyncband</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">version</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"0.7.3"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">features</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"mutex"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"semaphore"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"once-cell"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>Types live in their respective modules. For example, <code>Mutex</code> is imported from <code>asyncband::mutex</code>, while <code>OnceCell</code> lives in <code>asyncband::once</code> and requires the <code>once-cell</code> feature. The <code>once</code> feature enables the separate <code>Once</code> primitive.</p>
<p>The following table is a starting point for locating APIs. Paths in the second column are relative to <code>asyncband</code>.</p>

































































<table><thead><tr><th>Tokio API</th><th>Asyncband API</th><th>Check when porting</th></tr></thead><tbody><tr><td><code>sync::Mutex</code></td><td><a href="https://docs.rs/asyncband/0.7.3/asyncband/mutex/struct.Mutex.html" target="_blank" rel="noopener noreferrer" class=""><code>mutex::Mutex</code></a></td><td><code>try_lock</code> returns <code>Option</code>, rather than <code>Result</code>.</td></tr><tr><td><code>sync::RwLock</code></td><td><a href="https://docs.rs/asyncband/0.7.3/asyncband/rwlock/struct.RwLock.html" target="_blank" rel="noopener noreferrer" class=""><code>rwlock::RwLock</code></a></td><td><code>try_read</code> and <code>try_write</code> return <code>Option</code>.</td></tr><tr><td><code>sync::Semaphore</code></td><td><a href="https://docs.rs/asyncband/0.7.3/asyncband/semaphore/struct.Semaphore.html" target="_blank" rel="noopener noreferrer" class=""><code>semaphore::Semaphore</code></a></td><td><code>acquire(n)</code> takes a permit count and returns a guard directly.</td></tr><tr><td><code>sync::Notify</code></td><td><a href="https://docs.rs/asyncband/0.7.3/asyncband/event/index.html" target="_blank" rel="noopener noreferrer" class=""><code>event</code></a></td><td>Choose an event by its retained state; there is no direct replacement for every <code>Notify</code> operation.</td></tr><tr><td><code>sync::Barrier</code></td><td><a href="https://docs.rs/asyncband/0.7.3/asyncband/barrier/struct.Barrier.html" target="_blank" rel="noopener noreferrer" class=""><code>barrier::Barrier</code></a></td><td>The participant count is <code>u32</code>, rather than <code>usize</code>.</td></tr><tr><td><code>sync::OnceCell</code></td><td><a href="https://docs.rs/asyncband/0.7.3/asyncband/once/struct.OnceCell.html" target="_blank" rel="noopener noreferrer" class=""><code>once::OnceCell</code></a></td><td>Initialization is retryable; <code>set</code> is asynchronous.</td></tr><tr><td><code>sync::SetOnce</code></td><td>Related API: <a href="https://docs.rs/asyncband/0.7.3/asyncband/completion/index.html" target="_blank" rel="noopener noreferrer" class=""><code>completion</code></a></td><td>Publishing and observing use separate handles; abandonment is reported.</td></tr><tr><td><code>sync::oneshot</code></td><td><a href="https://docs.rs/asyncband/0.7.3/asyncband/oneshot/index.html" target="_blank" rel="noopener noreferrer" class=""><code>oneshot</code></a></td><td>Retain the receive future when selecting repeatedly.</td></tr><tr><td><code>sync::mpsc</code></td><td><a href="https://docs.rs/asyncband/0.7.3/asyncband/mpsc/index.html" target="_blank" rel="noopener noreferrer" class=""><code>mpsc</code></a></td><td>Constructors are <code>bounded</code> and <code>unbounded</code>; <code>recv</code> returns <code>Result</code>.</td></tr><tr><td><code>sync::broadcast</code></td><td><a href="https://docs.rs/asyncband/0.7.3/asyncband/broadcast/mpmc/index.html" target="_blank" rel="noopener noreferrer" class=""><code>broadcast::mpmc</code></a></td><td>Bounded sends wait for slow receivers instead of evicting unread messages.</td></tr><tr><td><code>sync::watch</code></td><td><a href="https://docs.rs/asyncband/0.7.3/asyncband/watch/index.html" target="_blank" rel="noopener noreferrer" class=""><code>watch</code></a></td><td>Values are read by cloning, rather than through a borrow guard.</td></tr></tbody></table>
<p>Both libraries use guards to release locks and semaphore permits. Their queued lock and semaphore acquisitions preserve queue order; cancelling an acquisition gives up its place without leaking permits. This does not undo changes made by application code while holding a guard. Both barriers are reusable and designate one leader per generation, but <code>wait</code> is not cancel-safe: if another branch wins and you still intend to wait, retain the same pending future instead of registering another arrival.</p>
<h2 class="anchor anchorTargetStickyNavbar_zq6S" id="initialization-and-externally-supplied-values">Initialization and externally supplied values<a href="https://asyncband.apache.org/blog/2026/10/15/tokio-field-map/#initialization-and-externally-supplied-values" class="hash-link" aria-label="Direct link to Initialization and externally supplied values" title="Direct link to Initialization and externally supplied values" translate="no">​</a></h2>
<p>Both libraries provide <code>OnceCell::get_or_init</code> and <code>get_or_try_init</code>. A failed, panicked, or cancelled initialization attempt leaves the cell available for another attempt. There are still call-site differences. Tokio's <a href="https://docs.rs/tokio/1.53.3/tokio/sync/struct.OnceCell.html#method.set" target="_blank" rel="noopener noreferrer" class=""><code>OnceCell::set</code></a> is synchronous and can report that initialization is already in progress. Asyncband's <a href="https://docs.rs/asyncband/0.7.3/asyncband/once/struct.OnceCell.html#method.set" target="_blank" rel="noopener noreferrer" class=""><code>set</code></a> is awaited: it waits for an ongoing attempt, then either stores the supplied value or returns it if the cell has already been initialized.</p>
<p>Tokio also provides <a href="https://docs.rs/tokio/1.53.3/tokio/sync/struct.SetOnce.html" target="_blank" rel="noopener noreferrer" class=""><code>SetOnce</code></a>. One caller supplies a value with <code>set</code>, and other callers can <code>wait</code> for it without supplying an initializer. Asyncband's <code>OnceCell</code> does not have that waiting API. Its <a href="https://docs.rs/asyncband/0.7.3/asyncband/completion/index.html" target="_blank" rel="noopener noreferrer" class=""><code>completion</code></a> module serves a related use case: a single-use <code>Completer</code> publishes a value to cloneable <code>Completion</code> observers. Observers receive an <code>Abandoned</code> error if the completer is dropped without publishing. That separation of capabilities and the abandonment result are part of the migration decision; it is not a type rename.</p>
<h2 class="anchor anchorTargetStickyNavbar_zq6S" id="notifications-and-shutdown">Notifications and shutdown<a href="https://asyncband.apache.org/blog/2026/10/15/tokio-field-map/#notifications-and-shutdown" class="hash-link" aria-label="Direct link to Notifications and shutdown" title="Direct link to Notifications and shutdown" translate="no">​</a></h2>
<p>Tokio's <a href="https://docs.rs/tokio/1.53.3/tokio/sync/struct.Notify.html" target="_blank" rel="noopener noreferrer" class=""><code>Notify</code></a> supports several notification operations. Choose an Asyncband event based on the state that the application needs to retain:</p>
<ul>
<li class=""><code>notify_one()</code> wakes one waiter or stores one permit for a later waiter. <code>AutoResetEvent::set()</code> has the corresponding one-signal behavior. Multiple calls with no waiting consumer coalesce into one stored signal.</li>
<li class=""><code>notify_waiters()</code> notifies existing <code>Notified</code> futures, including those created but not yet polled. It does not retain a notification for futures created afterward.</li>
<li class=""><code>ManualResetEvent::set()</code> releases current waiters and keeps the event set until <code>reset()</code>. Later waits also pass while it remains set. It suits a persistent readiness condition, but a <code>set()</code>/<code>reset()</code> pair is not a direct replacement for <code>notify_waiters()</code>.</li>
</ul>
<p>For shutdown, retain the shutdown state explicitly. A bare <code>notify_waiters()</code> call made before a task creates its wait future cannot tell that task to stop later. Asyncband's <a href="https://docs.rs/asyncband/0.7.3/asyncband/shutdown/index.html" target="_blank" rel="noopener noreferrer" class=""><code>shutdown</code></a> module combines a persistent request with completion tracking: <code>request_shutdown()</code> records the request for current and future observers, and awaiting the coordinator waits for every <code>ShutdownGuard</code> to be dropped. Workers still decide how to finish their work and release their guards.</p>
<h2 class="anchor anchorTargetStickyNavbar_zq6S" id="semaphore-counts-and-closure">Semaphore counts and closure<a href="https://asyncband.apache.org/blog/2026/10/15/tokio-field-map/#semaphore-counts-and-closure" class="hash-link" aria-label="Direct link to Semaphore counts and closure" title="Direct link to Semaphore counts and closure" translate="no">​</a></h2>
<p>Tokio's <code>acquire().await</code> acquires one permit and returns a <code>Result</code>. Asyncband spells the operation <code>acquire(1).await</code> and returns the guard directly, so remove error handling for acquisition when changing that call. Its non-waiting <code>try_acquire(n)</code> returns <code>Option</code>.</p>
<p>This difference follows from the absence of a closed state. Tokio's <a href="https://docs.rs/tokio/1.53.3/tokio/sync/struct.Semaphore.html#method.close" target="_blank" rel="noopener noreferrer" class=""><code>Semaphore::close</code></a> makes pending and future acquisitions fail. Asyncband has no corresponding operation. Code that uses closure to stop admission must keep a separate shutdown signal and select between it and acquisition. An acquisition that can never be satisfied still needs to be rejected or given a deadline by the caller.</p>
<p>Permit counts also differ. Tokio accepts <code>usize</code> in <code>new</code> and <code>add_permits</code>, but <code>u32</code> in <code>acquire_many</code>; its <code>MAX_PERMITS</code> is <code>usize::MAX &gt;&gt; 3</code> because the representation reserves bits. Asyncband uses <code>usize</code> counts throughout and does not reserve count bits. <code>acquire(n)</code> handles both single- and multi-permit requests, and <code>release(n)</code> adds capacity.</p>
<p>Both libraries let a permit guard <code>forget</code> its permits so that dropping it does not return them. When adjusting capacity without first acquiring a guard, distinguish these operations:</p>
<ul>
<li class="">Tokio's <code>forget_permits(n)</code> and Asyncband's <a href="https://docs.rs/asyncband/0.7.3/asyncband/semaphore/struct.Semaphore.html#method.drain_permits" target="_blank" rel="noopener noreferrer" class=""><code>drain_permits(n)</code></a> remove up to <code>n</code> currently available permits and report the amount removed.</li>
<li class="">Asyncband's <a href="https://docs.rs/asyncband/0.7.3/asyncband/semaphore/struct.Semaphore.html#method.reduce_permits" target="_blank" rel="noopener noreferrer" class=""><code>reduce_permits(n)</code></a> reduces the logical balance by exactly <code>n</code>. It leaves existing guards valid; if too few permits are available, future returns repay the deficit before ordinary queued acquisitions proceed.</li>
</ul>
<p>For example, reducing a fully occupied eight-slot budget by three eventually leaves five slots. Draining three at that moment removes zero. Forgetting an acquired guard requires obtaining those permits first, which may itself wait.</p>
<h2 class="anchor anchorTargetStickyNavbar_zq6S" id="channel-behavior">Channel behavior<a href="https://asyncband.apache.org/blog/2026/10/15/tokio-field-map/#channel-behavior" class="hash-link" aria-label="Direct link to Channel behavior" title="Direct link to Channel behavior" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_zq6S" id="mpsc-constructors-and-receive-results">MPSC constructors and receive results<a href="https://asyncband.apache.org/blog/2026/10/15/tokio-field-map/#mpsc-constructors-and-receive-results" class="hash-link" aria-label="Direct link to MPSC constructors and receive results" title="Direct link to MPSC constructors and receive results" translate="no">​</a></h3>
<p>Tokio's <code>mpsc::channel(capacity)</code> becomes Asyncband's <code>mpsc::bounded(capacity)</code>, and <code>unbounded_channel()</code> becomes <code>unbounded()</code>. Both bounded variants apply backpressure when full. Their receive results differ: Tokio uses <code>Option&lt;T&gt;</code>, with <code>None</code> after the channel drains and closes; Asyncband uses <code>Result&lt;T, RecvError&gt;</code>, reporting disconnection after buffered values drain. Update receive loops and their end-of-stream handling along with the imports.</p>
<h3 class="anchor anchorTargetStickyNavbar_zq6S" id="broadcast-retention">Broadcast retention<a href="https://asyncband.apache.org/blog/2026/10/15/tokio-field-map/#broadcast-retention" class="hash-link" aria-label="Direct link to Broadcast retention" title="Direct link to Broadcast retention" translate="no">​</a></h3>
<p>Tokio's <a href="https://docs.rs/tokio/1.53.3/tokio/sync/broadcast/index.html" target="_blank" rel="noopener noreferrer" class=""><code>broadcast</code></a> lets a producer send without waiting for buffer capacity. When its bounded buffer fills, old values are evicted and affected receivers observe <code>RecvError::Lagged</code>.</p>
<p>Asyncband's <a href="https://docs.rs/asyncband/0.7.3/asyncband/broadcast/mpmc/fn.bounded.html" target="_blank" rel="noopener noreferrer" class=""><code>broadcast::mpmc::bounded</code></a> retains messages until active receivers have read them or dropped their subscriptions. A slow receiver therefore makes <code>send(value).await</code> wait once the shared backlog fills. There is no lag error, but one receiver that stops draining can stall every producer. Reading a message releases that receiver's claim on buffer capacity; processing it afterward does not hold the slot.</p>
<p>The send result also changes: Asyncband's bounded <code>send</code> returns <code>()</code>, rather than Tokio's result containing a receiver count. With no active receivers, an Asyncband broadcast send retains nothing. The <a href="https://github.com/apache/asyncband/issues/167" target="_blank" rel="noopener noreferrer" class="">channel design</a> and <a href="https://github.com/apache/asyncband/issues/213" target="_blank" rel="noopener noreferrer" class="">bounded broadcast discussion</a> describe these retention choices. Before porting, decide whether slow consumers should cause missed messages or producer backpressure.</p>
<h3 class="anchor anchorTargetStickyNavbar_zq6S" id="watch-reads">Watch reads<a href="https://asyncband.apache.org/blog/2026/10/15/tokio-field-map/#watch-reads" class="hash-link" aria-label="Direct link to Watch reads" title="Direct link to Watch reads" translate="no">​</a></h3>
<p>Both watch channels retain the latest value and may coalesce intermediate updates. Tokio exposes <code>borrow()</code> and <code>borrow_and_update()</code> guards. Asyncband's <code>get()</code> clones the current value without marking it observed; <code>recv().await</code> waits for an update, clones the latest value, and marks that version observed together. Use <code>recv</code> when the consumer needs the updated value, and consider an <code>Arc&lt;T&gt;</code> payload when cloning the underlying value would be expensive.</p>
<h3 class="anchor anchorTargetStickyNavbar_zq6S" id="oneshot-selection">Oneshot selection<a href="https://asyncband.apache.org/blog/2026/10/15/tokio-field-map/#oneshot-selection" class="hash-link" aria-label="Direct link to Oneshot selection" title="Direct link to Oneshot selection" translate="no">​</a></h3>
<p>Both oneshot channels provide a synchronous <code>send</code> and let the receiver await one value. Tokio's receiver implements <code>Future</code>, so it can be borrowed as <code>&amp;mut receiver</code> in a <code>select!</code>. Asyncband's receiver implements <code>IntoFuture</code>. Call <code>receiver.into_future()</code> once and retain the returned <code>Recv</code> future, then select on <code>&amp;mut recv</code> when the receive should survive another branch winning. Dropping the Asyncband receiver or its receive future disconnects the channel and discards any unread message.</p>
<h2 class="anchor anchorTargetStickyNavbar_zq6S" id="additional-coordination-apis">Additional coordination APIs<a href="https://asyncband.apache.org/blog/2026/10/15/tokio-field-map/#additional-coordination-apis" class="hash-link" aria-label="Direct link to Additional coordination APIs" title="Direct link to Additional coordination APIs" translate="no">​</a></h2>
<p>Asyncband also provides dedicated APIs for the following tasks:</p>













































<table><thead><tr><th>Asyncband API</th><th>Use</th></tr></thead><tbody><tr><td><a href="https://docs.rs/asyncband/0.7.3/asyncband/latch/struct.Latch.html" target="_blank" rel="noopener noreferrer" class=""><code>Latch</code></a></td><td>Wait for a one-shot countdown to reach zero.</td></tr><tr><td><a href="https://docs.rs/asyncband/0.7.3/asyncband/waitgroup/struct.WaitGroup.html" target="_blank" rel="noopener noreferrer" class=""><code>WaitGroup</code></a></td><td>Wait for a dynamically registered set of workers to finish.</td></tr><tr><td><a href="https://docs.rs/asyncband/0.7.3/asyncband/phaser/struct.Phaser.html" target="_blank" rel="noopener noreferrer" class=""><code>Phaser</code></a></td><td>Coordinate multiple phases with a changing participant set.</td></tr><tr><td><a href="https://docs.rs/asyncband/0.7.3/asyncband/condvar/struct.Condvar.html" target="_blank" rel="noopener noreferrer" class=""><code>Condvar</code></a></td><td>Wait for a predicate protected by an Asyncband mutex.</td></tr><tr><td><a href="https://docs.rs/asyncband/0.7.3/asyncband/once/struct.Once.html" target="_blank" rel="noopener noreferrer" class=""><code>Once</code></a></td><td>Complete one successful initialization without storing its result.</td></tr><tr><td><a href="https://docs.rs/asyncband/0.7.3/asyncband/once/struct.LazyCell.html" target="_blank" rel="noopener noreferrer" class=""><code>LazyCell</code></a></td><td>Own an initializer and preserve its in-progress future across caller cancellation.</td></tr><tr><td><a href="https://docs.rs/asyncband/0.7.3/asyncband/once/struct.OnceMap.html" target="_blank" rel="noopener noreferrer" class=""><code>OnceMap</code></a></td><td>Initialize and retain values independently by key.</td></tr><tr><td><a href="https://docs.rs/asyncband/0.7.3/asyncband/singleflight/struct.Group.html" target="_blank" rel="noopener noreferrer" class=""><code>singleflight::Group</code></a></td><td>Share work among overlapping calls for a key without retaining a cache afterward.</td></tr><tr><td><a href="https://docs.rs/asyncband/0.7.3/asyncband/pool/index.html" target="_blank" rel="noopener noreferrer" class=""><code>pool</code></a></td><td>Reuse objects through bounded or unbounded pools.</td></tr></tbody></table>]]></content>
        <author>
            <name>Nanami</name>
            <uri>https://github.com/nanami-0713</uri>
        </author>
        <author>
            <name>tison</name>
            <uri>https://github.com/tisonkun</uri>
        </author>
        <category label="migration" term="migration"/>
        <category label="tokio" term="tokio"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Using Semaphores to Control In-Flight Work]]></title>
        <id>https://asyncband.apache.org/blog/2026/10/14/choosing-the-right-primitive/</id>
        <link href="https://asyncband.apache.org/blog/2026/10/14/choosing-the-right-primitive/"/>
        <updated>2026-10-14T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Use Asyncband's Semaphore to share concurrency limits, keep permits with the resources they account for, and reserve capacity for work of different sizes.]]></summary>
        <content type="html"><![CDATA[<p>An asynchronous program can start work faster than the resources behind it can handle. Downloads compete for connections and bandwidth. Background jobs compete for CPU time. Buffered data occupies memory until its consumer finishes with it. A semaphore gives these operations a shared budget: work acquires permits before consuming capacity and returns them when that capacity becomes available again.</p>
<p>The useful questions are <strong>what one permit represents, which operations share the budget, and how long each operation holds its permits</strong>. This post works through those choices with Asyncband's <a href="https://docs.rs/asyncband/0.7.3/asyncband/semaphore/struct.Semaphore.html" target="_blank" rel="noopener noreferrer" class=""><code>Semaphore</code></a>, including how Apache OpenDAL and prek use it.</p>
<p><em>The Asyncband APIs below are from version 0.7.3, with the <code>semaphore</code> feature enabled. The HTTP example uses reqwest; the semaphore itself is runtime-independent.</em></p>
<h2 class="anchor anchorTargetStickyNavbar_zq6S" id="share-a-budget-across-operations">Share a budget across operations<a href="https://asyncband.apache.org/blog/2026/10/14/choosing-the-right-primitive/#share-a-budget-across-operations" class="hash-link" aria-label="Direct link to Share a budget across operations" title="Direct link to Share a budget across operations" translate="no">​</a></h2>
<p>Suppose a client should perform at most eight downloads at once. Create one <code>Semaphore::new(8)</code> and share it across the calls that consume that budget. Each download acquires one permit. When all eight are held, another acquisition waits asynchronously until capacity becomes available.</p>
<div class="language-rust codeBlockContainer_lkHg theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_KnrP"><pre tabindex="0" class="prism-code language-rust codeBlock_FDzL thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_pYqn"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">use</span><span class="token plain"> </span><span class="token namespace" style="opacity:0.7">asyncband</span><span class="token namespace punctuation" style="opacity:0.7;color:#393A34">::</span><span class="token namespace" style="opacity:0.7">semaphore</span><span class="token namespace punctuation" style="opacity:0.7;color:#393A34">::</span><span class="token class-name">Semaphore</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">fn</span><span class="token plain"> </span><span class="token function-definition function" style="color:#d73a49">download</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    client</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;</span><span class="token namespace" style="opacity:0.7">reqwest</span><span class="token namespace punctuation" style="opacity:0.7;color:#393A34">::</span><span class="token class-name">Client</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    slots</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;</span><span class="token class-name">Semaphore</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    url</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;</span><span class="token keyword" style="color:#00009f">str</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">-&gt;</span><span class="token plain"> </span><span class="token class-name">Result</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token class-name">Vec</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token keyword" style="color:#00009f">u8</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token namespace" style="opacity:0.7">reqwest</span><span class="token namespace punctuation" style="opacity:0.7;color:#393A34">::</span><span class="token class-name">Error</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> _permit </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> slots</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">acquire</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token keyword" style="color:#00009f">await</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> response </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> client</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">url</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">send</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token keyword" style="color:#00009f">await</span><span class="token operator" style="color:#393A34">?</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">error_for_status</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token operator" style="color:#393A34">?</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token class-name">Ok</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">response</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">bytes</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token keyword" style="color:#00009f">await</span><span class="token operator" style="color:#393A34">?</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">to_vec</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p><code>acquire</code> returns a guard. Keeping that guard in <code>_permit</code> holds the slot through the download, including reading the response body. Returning from the function drops the guard and gives the slot back, including on an error. There is no separate success or error branch that needs to release it manually.</p>
<p>The semaphore must live outside the individual calls. Creating a new semaphore inside <code>download</code> would give every call its own eight-slot budget. An <code>Arc&lt;Semaphore&gt;</code> lets independently owned tasks share the same budget; each task can borrow it for <code>acquire</code> or use <code>acquire_owned</code> when the permit itself must own a reference to the semaphore.</p>
<p>This is useful when work comes from several places. Apache OpenDAL's <a href="https://github.com/apache/opendal/blob/ee8be2353d6af6152f6293cb8ac0b4d3d7a234d7/core/layers/concurrent-limit/src/lib.rs" target="_blank" rel="noopener noreferrer" class=""><code>ConcurrentLimitLayer</code></a> lets multiple operators share an Asyncband semaphore. The hook runner <a href="https://github.com/j178/prek/blob/735c452028a698ce99ae5821dda9baedd0e07037/crates/prek/src/cli/run/run.rs" target="_blank" rel="noopener noreferrer" class="">prek</a> similarly passes one semaphore through project runs to limit concurrent hook execution. The scope of the shared semaphore determines the scope of the limit.</p>
<p>Eight slots limit simultaneous downloads. The number completed per second still depends on their duration. A policy expressed in requests per second needs a time-based rate limiter.</p>
<h2 class="anchor anchorTargetStickyNavbar_zq6S" id="keep-the-permit-with-the-resource">Keep the permit with the resource<a href="https://asyncband.apache.org/blog/2026/10/14/choosing-the-right-primitive/#keep-the-permit-with-the-resource" class="hash-link" aria-label="Direct link to Keep the permit with the resource" title="Direct link to Keep the permit with the resource" translate="no">​</a></h2>
<p>In the example above, the download ends when the response body has been read. A streaming API has a different lifetime: it can return a response while the caller is still receiving its body. Releasing a permit when the response is returned would leave that later transfer outside the budget.</p>
<p>OpenDAL handles this at its <a href="https://github.com/apache/opendal/blob/ee8be2353d6af6152f6293cb8ac0b4d3d7a234d7/core/layers/concurrent-limit/src/lib.rs#L191" target="_blank" rel="noopener noreferrer" class="">HTTP transport boundary</a>. When an HTTP concurrency limit is configured, it acquires a permit before sending the request and moves the permit into a wrapper around the response body stream. The stream holds the permit until it is dropped. If sending the request fails, the local permit is dropped instead.</p>
<p>That ownership pattern also applies to a returned buffer or a queued job: put the permit in the object that owns the charged resource. If ownership moves to another task, move the permit with it. The function that acquired the permit may return long before the resource is released.</p>
<p>The download example's returned <code>Vec&lt;u8&gt;</code> illustrates the boundary. Its slot covers the transfer, but the caller can retain the downloaded bytes afterward. Limiting transfers alone does not bound retained memory.</p>
<h2 class="anchor anchorTargetStickyNavbar_zq6S" id="charge-for-the-size-of-the-work">Charge for the size of the work<a href="https://asyncband.apache.org/blog/2026/10/14/choosing-the-right-primitive/#charge-for-the-size-of-the-work" class="hash-link" aria-label="Direct link to Charge for the size of the work" title="Direct link to Charge for the size of the work" translate="no">​</a></h2>
<p>A permit can represent a byte of capacity instead of one operation. This matters when a small buffer and a large buffer should consume different amounts of a shared budget.</p>
<p>Here is a standalone example of a fixed payload budget. It reserves capacity before allocating a buffer and keeps the reservation with the returned value:</p>
<div class="language-rust codeBlockContainer_lkHg theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_KnrP"><pre tabindex="0" class="prism-code language-rust codeBlock_FDzL thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_pYqn"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">use</span><span class="token plain"> </span><span class="token namespace" style="opacity:0.7">std</span><span class="token namespace punctuation" style="opacity:0.7;color:#393A34">::</span><span class="token namespace" style="opacity:0.7">sync</span><span class="token namespace punctuation" style="opacity:0.7;color:#393A34">::</span><span class="token class-name">Arc</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">use</span><span class="token plain"> </span><span class="token namespace" style="opacity:0.7">asyncband</span><span class="token namespace punctuation" style="opacity:0.7;color:#393A34">::</span><span class="token namespace" style="opacity:0.7">semaphore</span><span class="token namespace punctuation" style="opacity:0.7;color:#393A34">::</span><span class="token punctuation" style="color:#393A34">{</span><span class="token class-name">OwnedSemaphorePermit</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token class-name">Semaphore</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">struct</span><span class="token plain"> </span><span class="token type-definition class-name">BufferBudget</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    capacity</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">usize</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    available</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token class-name">Arc</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token class-name">Semaphore</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">struct</span><span class="token plain"> </span><span class="token type-definition class-name">BudgetedBuffer</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    data</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token class-name">Vec</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token keyword" style="color:#00009f">u8</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    _permit</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token class-name">OwnedSemaphorePermit</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">impl</span><span class="token plain"> </span><span class="token class-name">BufferBudget</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">fn</span><span class="token plain"> </span><span class="token function-definition function" style="color:#d73a49">new</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">capacity</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">usize</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">-&gt;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">Self</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">Self</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            capacity</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            available</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token class-name">Arc</span><span class="token punctuation" style="color:#393A34">::</span><span class="token function" style="color:#d73a49">new</span><span class="token punctuation" style="color:#393A34">(</span><span class="token class-name">Semaphore</span><span class="token punctuation" style="color:#393A34">::</span><span class="token function" style="color:#d73a49">new</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">capacity</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">fn</span><span class="token plain"> </span><span class="token function-definition function" style="color:#d73a49">allocate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">&amp;</span><span class="token keyword" style="color:#00009f">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> len</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">usize</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">-&gt;</span><span class="token plain"> </span><span class="token class-name">Option</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token class-name">BudgetedBuffer</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> len </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">capacity </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token class-name">None</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> permit </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">available</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">clone</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">acquire_owned</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">len</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token keyword" style="color:#00009f">await</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token class-name">Some</span><span class="token punctuation" style="color:#393A34">(</span><span class="token class-name">BudgetedBuffer</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            data</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token macro property" style="color:#36acaa">vec!</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> len</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            _permit</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> permit</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>With <code>BufferBudget::new(8 * 1024 * 1024)</code>, the budget covers eight MiB of payload. A 64 KiB allocation charges 64 KiB; a two MiB allocation charges two MiB. The buffer and its reservation can move together between tasks. Dropping the buffer frees its data and returns the permits.</p>
<p>Rejecting a request larger than the entire fixed budget is necessary: returning every outstanding permit could still never satisfy it. Reserve the full amount in one acquisition, too. Acquiring a large reservation incrementally can leave several callers each holding some capacity while all wait for the rest.</p>
<p>This budget counts the payload bytes in these buffers. Allocator overhead, other allocations, and extra copies require their own accounting. Growing or copying a buffer must reserve the additional capacity before doing so.</p>
<h2 class="anchor anchorTargetStickyNavbar_zq6S" id="decide-what-happens-when-capacity-runs-out">Decide what happens when capacity runs out<a href="https://asyncband.apache.org/blog/2026/10/14/choosing-the-right-primitive/#decide-what-happens-when-capacity-runs-out" class="hash-link" aria-label="Direct link to Decide what happens when capacity runs out" title="Direct link to Decide what happens when capacity runs out" translate="no">​</a></h2>
<p>Waiting is one admission policy. <code>acquire(n).await</code> waits for capacity; <code>try_acquire(n)</code> returns <code>None</code> when it cannot acquire the requested permits immediately. A caller can use the latter to reject or defer work instead of joining the wait queue. A deadline around <code>acquire</code> gives waiting a time limit.</p>
<p>The placement of the acquisition matters. If a program allocates a large payload and only then waits for its permit, that payload already consumes memory outside the budget. Likewise, spawning one task per incoming job and acquiring inside each task limits admitted work while allowing an arbitrarily large population of waiting tasks. Apply admission before creating the resource whose growth you intend to bound, or bound the waiting population separately.</p>
<p>Asyncband assigns permits to queued acquisitions in order. A large acquisition at the front can therefore hold back smaller ones behind it. If a budget has three units left and the first waiter needs five, a later one-unit waiter waits too. This preserves ordering, with a latency cost when request sizes vary substantially.</p>
<p>Dropping a pending acquisition removes it from the queue without leaking permits; retrying joins at the back. After acquisition completes, dropping the permit guard returns its permits. This accounting does not undo application side effects or stop work that has been handed to another task; the task doing that work must retain the permit for its actual lifetime.</p>
<h2 class="anchor anchorTargetStickyNavbar_zq6S" id="adjust-a-running-budget">Adjust a running budget<a href="https://asyncband.apache.org/blog/2026/10/14/choosing-the-right-primitive/#adjust-a-running-budget" class="hash-link" aria-label="Direct link to Adjust a running budget" title="Direct link to Adjust a running budget" translate="no">​</a></h2>
<p>Some applications change their concurrency budget while work is already running. Asyncband provides two operations for this: <code>release(n)</code> adds capacity, and <a href="https://docs.rs/asyncband/0.7.3/asyncband/semaphore/struct.Semaphore.html#method.reduce_permits" target="_blank" rel="noopener noreferrer" class=""><code>reduce_permits(n)</code></a> removes exactly <code>n</code> permits from the logical balance. A reduction does not revoke permits that callers already hold. If insufficient permits are currently available, later returns repay the deficit before ordinary queued acquisitions proceed.</p>
<p>For example, suppose an eight-slot budget is fully occupied and its controller reduces it by three. The eight operations can finish normally. As their guards are dropped, three returned permits retire the deficit; after all eight return, five slots remain available. The controller should serialize budget changes so that each adjustment is applied once.</p>
<p><a href="https://docs.rs/asyncband/0.7.3/asyncband/semaphore/struct.Semaphore.html#method.drain_permits" target="_blank" rel="noopener noreferrer" class=""><code>drain_permits(n)</code></a> only removes permits that are available at the time and returns the number removed. Draining three from that fully occupied budget removes zero, so it cannot promise the same capacity reduction. Also, returning an ordinary guard already restores its permits: an additional <code>release</code> would add capacity a second time.</p>
<p>For a fixed limit, acquiring and dropping guards is enough. Add weighted reservations or capacity adjustments when the resource being controlled calls for them, and keep the permit's lifetime aligned with the work it accounts for.</p>]]></content>
        <author>
            <name>Nanami</name>
            <uri>https://github.com/nanami-0713</uri>
        </author>
        <author>
            <name>tison</name>
            <uri>https://github.com/tisonkun</uri>
        </author>
        <category label="semaphore" term="semaphore"/>
    </entry>
</feed>