<div dir="ltr">Hello,<div><br></div><div>I would like to follow up on this one, because I also found myself creating a wrapper similar to this one, but for a different reason.</div><div>Specifically, when I want to limit concurrency in order to not exceed a rate limit somewhere.</div><div>Example:</div><div><div style="background-color:rgb(25,26,28);color:rgb(188,190,196)"><pre style="font-family:"JetBrains Mono",monospace;font-size:14.3pt">Stream<Article> <span style="color:rgb(86,168,245)">summarize</span>(Stream<Article> articles) {<br>    <span style="color:rgb(207,142,109)">try </span>(<span style="color:rgb(207,142,109)">var </span>scope = MyTaskScope.<span style="font-style:italic">withLimitedConcurrency</span>(<span style="color:rgb(42,172,184)">10</span>)) {<br>        <span style="color:rgb(207,142,109)">var </span>subtasks = articles.map(article -> <span style="color:rgb(199,125,187)">scope</span>.fork(() -> <span style="color:rgb(199,125,187)">llm</span>.summarize(<span style="color:rgb(199,125,187)">article</span>)));<br><br>        <span style="color:rgb(207,142,109)">return </span>subtasks.map(Subtask::get);<br>    }<br>}</pre></div></div><div>And `MyTaskScope` looks like this:</div><div><div style="background-color:rgb(25,26,28);color:rgb(188,190,196)"><pre style="font-family:"JetBrains Mono",monospace;font-size:14.3pt"><span style="color:rgb(207,142,109)">class </span>MyTaskScope<<span style="color:rgb(22,186,172)">T</span>> <span style="color:rgb(207,142,109)">implements </span>AutoCloseable {<br>    <span style="color:rgb(207,142,109)">private final </span>StructuredTaskScope<<span style="color:rgb(22,186,172)">T</span>, Void> <span style="color:rgb(199,125,187)">sts</span>;<br>    <span style="color:rgb(207,142,109)">private final </span>Semaphore <span style="color:rgb(199,125,187)">semaphore</span>;<br><br>    <span style="color:rgb(207,142,109)">private </span><span style="color:rgb(86,168,245)">MyTaskScope</span>(StructuredTaskScope<<span style="color:rgb(22,186,172)">T</span>, Void> sts, <span style="color:rgb(207,142,109)">int </span>n) {<br>        <span style="color:rgb(207,142,109)">this</span>.<span style="color:rgb(199,125,187)">sts </span>= sts;<br>        <span style="color:rgb(207,142,109)">this</span>.<span style="color:rgb(199,125,187)">semaphore </span>= <span style="color:rgb(207,142,109)">new </span>Semaphore(n, <span style="color:rgb(207,142,109)">true</span>);<br>    }<br><br>    <span style="color:rgb(207,142,109)">static </span><<span style="color:rgb(22,186,172)">T</span>> MyTaskScope<<span style="color:rgb(22,186,172)">T</span>> <span style="color:rgb(86,168,245)">withLimitedConcurrency</span>(<span style="color:rgb(207,142,109)">int </span>n) {<br>        <span style="color:rgb(207,142,109)">return new </span>MyTaskScope<<span style="color:rgb(22,186,172)">T</span>>(StructuredTaskScope.<span style="font-style:italic">open</span>(), n);<br>    }<br><br>    <<span style="color:rgb(22,186,172)">U </span><span style="color:rgb(207,142,109)">extends </span><span style="color:rgb(22,186,172)">T</span>> Subtask<<span style="color:rgb(22,186,172)">U</span>> <span style="color:rgb(86,168,245)">fork</span>(Callable<? <span style="color:rgb(207,142,109)">extends </span><span style="color:rgb(22,186,172)">U</span>> task) {<br>        <span style="color:rgb(207,142,109)">return </span><span style="color:rgb(199,125,187)">sts</span>.fork(() -> {<br>            <span style="color:rgb(207,142,109)">try </span>{<br>                <span style="color:rgb(199,125,187)">semaphore</span>.acquire();<br>                <span style="color:rgb(207,142,109)">return </span><span style="color:rgb(199,125,187)">task</span>.call();<br>            } <span style="color:rgb(207,142,109)">finally </span>{<br>                <span style="color:rgb(199,125,187)">semaphore</span>.release();<br>            }<br>        });<br>    }<br><br>    <span style="color:rgb(179,174,96)">@Override<br></span><span style="color:rgb(179,174,96)">    </span><span style="color:rgb(207,142,109)">public void </span><span style="color:rgb(86,168,245)">close</span>() {<br>        <span style="color:rgb(199,125,187)">sts</span>.close();<br>    }<br>}</pre></div></div><div>For this use case, it would make more sense to implement `StructuredTaskScope` instead of `AutoCloseable`, but it's sealed.</div><div><br></div><div>Kind regards,</div><div>Filip Egeric</div><div><br></div></div><br><div class="gmail_quote gmail_quote_container"><div dir="ltr" class="gmail_attr">On Mon, Oct 20, 2025 at 8:04 PM Josiah Noel <<a href="mailto:josiahnoel@gmail.com">josiahnoel@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div>Long time no see,</div><div><br></div><div>So I moved one of my applications to the new API, and I don't really know what else to say other than it works for me. The only thing I'm not a fan of is the interrupted exception when calling join. Because of the interrupted exception I find myself creating a wrapper class like this:</div><div><br><div style="background-color:rgb(28,31,34);padding:0px 0px 0px 2px"><div style="color:rgb(248,248,242);font-family:"Comic Mono";white-space:pre-wrap"><p style="margin:0px"><span style="color:rgb(241,41,113)">public</span> <span style="color:rgb(241,41,113)">class</span> <span style="color:rgb(86,216,240)">STSWrapper</span> <span style="color:rgb(241,41,113)">implements</span> <span style="color:rgb(209,151,217)">AutoCloseable</span> <span style="color:rgb(216,216,216)">{</span></p><p style="margin:0px"><br></p><p style="margin:0px">  <span style="color:rgb(241,41,113)">private</span> <span style="color:rgb(241,41,113)">final</span> <span style="color:rgb(209,151,217)">StructuredTaskScope</span><span style="color:rgb(216,216,216)"><</span><span style="color:rgb(191,164,164)">Object</span><span style="color:rgb(216,216,216)">,</span> <span style="color:rgb(191,164,164)">Void</span><span style="color:rgb(216,216,216)">></span> <span style="color:rgb(123,225,42)">scope</span> <span style="color:rgb(216,216,216)">=</span> <span style="text-decoration-line:underline;text-decoration-style:wavy;text-decoration-color:rgb(244,200,45)">StructuredTaskScope</span><span style="color:rgb(216,216,216)">.</span><span style="color:rgb(190,214,255);background-color:rgb(65,65,4)">open</span><span style="color:rgb(216,216,216)">();</span></p><p style="margin:0px"><br></p><p style="margin:0px">  <span style="color:rgb(241,41,113)">public</span> <span style="color:rgb(216,216,216)"><</span><span style="color:rgb(191,164,164)">T</span><span style="color:rgb(216,216,216)">></span> <span style="color:rgb(209,151,217)">Supplier</span><span style="color:rgb(216,216,216)"><</span><span style="color:rgb(191,164,164)">T</span><span style="color:rgb(216,216,216)">></span> <span style="color:rgb(123,225,42)">fork</span><span style="color:rgb(216,216,216)">(</span><span style="color:rgb(209,151,217)">Callable</span><span style="color:rgb(216,216,216)"><?</span> <span style="color:rgb(241,41,113)">extends</span> <span style="color:rgb(191,164,164)">T</span><span style="color:rgb(216,216,216)">></span> <span style="color:rgb(121,171,255)">task</span><span style="color:rgb(216,216,216)">)</span> <span style="color:rgb(216,216,216)">{</span></p><p style="margin:0px">    <span style="color:rgb(241,41,113)">return</span> <span style="color:rgb(123,225,42)">scope</span><span style="color:rgb(216,216,216)">.</span><span style="color:rgb(190,214,255)">fork</span><span style="color:rgb(216,216,216)">(</span><span style="color:rgb(121,171,255)">task</span><span style="color:rgb(216,216,216)">);</span></p><p style="margin:0px">  <span style="color:rgb(216,216,216)">}</span></p><p style="margin:0px"><br></p><p style="margin:0px">  <span style="color:rgb(241,41,113)">public</span> <span style="color:rgb(216,216,216)"><</span><span style="color:rgb(191,164,164)">T</span><span style="color:rgb(216,216,216)">></span> <span style="color:rgb(209,151,217)">Supplier</span><span style="color:rgb(216,216,216)"><</span><span style="color:rgb(191,164,164)">T</span><span style="color:rgb(216,216,216)">></span> <span style="color:rgb(123,225,42)">fork</span><span style="color:rgb(216,216,216)">(</span><span style="color:rgb(209,151,217)">Runnable</span> <span style="color:rgb(121,171,255)">task</span><span style="color:rgb(216,216,216)">)</span> <span style="color:rgb(216,216,216)">{</span></p><p style="margin:0px">    <span style="color:rgb(241,41,113)">return</span> <span style="color:rgb(123,225,42)">scope</span><span style="color:rgb(216,216,216)">.</span><span style="color:rgb(190,214,255)">fork</span><span style="color:rgb(216,216,216)">(</span><span style="color:rgb(121,171,255)">task</span><span style="color:rgb(216,216,216)">);</span></p><p style="margin:0px">  <span style="color:rgb(216,216,216)">}</span></p><p style="margin:0px"><br></p><p style="margin:0px">  <span style="color:rgb(241,41,113)">public</span> <span style="color:rgb(241,41,113)">void</span> <span style="color:rgb(123,225,42)">join</span><span style="color:rgb(216,216,216)">()</span> <span style="color:rgb(216,216,216)">{</span></p><p style="margin:0px">    <span style="color:rgb(241,41,113)">try</span> <span style="color:rgb(216,216,216)">{</span></p><p style="margin:0px">      <span style="color:rgb(123,225,42)">scope</span><span style="color:rgb(216,216,216)">.</span><span style="color:rgb(190,214,255)">join</span><span style="color:rgb(216,216,216)">();</span></p><p style="margin:0px">    <span style="color:rgb(216,216,216)">}</span> <span style="color:rgb(241,41,113)">catch</span> <span style="color:rgb(216,216,216)">(</span><span style="color:rgb(241,41,113)">final</span> <span style="color:rgb(86,216,240)">InterruptedException</span> <span style="color:rgb(241,41,113)">e</span><span style="color:rgb(216,216,216)">)</span> <span style="color:rgb(216,216,216)">{</span></p><p style="margin:0px">      <span style="color:rgb(86,216,240)">Thread</span><span style="color:rgb(216,216,216)">.</span><span style="color:rgb(190,214,255)">currentThread</span><span style="color:rgb(216,216,216)">().</span><span style="color:rgb(255,255,255)">interrupt</span><span style="color:rgb(216,216,216)">();</span></p><p style="margin:0px">      <span style="color:rgb(241,41,113)">throw</span> <span style="color:rgb(241,41,113)">new</span> <span style="color:rgb(255,255,255)">IllegalStateException</span><span style="color:rgb(216,216,216)">(</span><span style="color:rgb(231,248,242)">e</span><span style="color:rgb(216,216,216)">);</span></p><p style="margin:0px">    <span style="color:rgb(216,216,216)">}</span></p><p style="margin:0px">  <span style="color:rgb(216,216,216)">}</span></p><p style="margin:0px"><br></p><p style="margin:0px">  <span style="color:rgb(255,255,255)">@</span><span style="color:rgb(255,255,255)">Override</span></p><p style="margin:0px">  <span style="color:rgb(241,41,113)">public</span> <span style="color:rgb(241,41,113)">void</span> <span style="color:rgb(123,225,42)">close</span><span style="color:rgb(216,216,216)">()</span> <span style="color:rgb(216,216,216)">{</span></p><p style="margin:0px">    <span style="color:rgb(123,225,42)">scope</span><span style="color:rgb(216,216,216)">.</span><span style="color:rgb(190,214,255)">close</span><span style="color:rgb(216,216,216)">();</span></p><p style="margin:0px">  <span style="color:rgb(216,216,216)">}</span></p><p style="margin:0px"><span style="color:rgb(216,216,216)">}</span></p><p style="font-size:18pt;margin:0px"></p></div></div></div><div><br></div><div>I assume there are good reasons to not make it like CompletableFuture's unchecked join method, so if nothing can be done I'll just leave it at that.<br><br>The application itself is nothing but an orchestration api where we make downstream calls to other services to get and combine data. I didn't test anything other than the default joiner because if any of the calls fail we want to terminate the request immediately (they were also all different types). I didn't need to check the state of the individual tasks, so I used a supplier for the wrapper.</div><div><br>Most of the other services I work on follow similar requirements so I don't think I'll be able to test any other joiners other than the default on real services.<br><br></div><span class="gmail_signature_prefix">-- </span><br><div dir="ltr" class="gmail_signature"><div dir="ltr">Cheers, Josiah.</div></div><input name="virtru-metadata" type="hidden" value="{"email-policy":{"disableCopyPaste":false,"disablePrint":false,"disableForwarding":false,"enableNoauth":false,"expires":false,"sms":false,"expirationNum":1,"expirationUnit":"days","expirationDate":null,"isManaged":false},"attachments":{},"compose-id":"1","compose-window":{"secure":false}}"></div>
</div>
</div>
</blockquote></div>