Skip Navigation
Jump
Nagle's algorithm - Wikipedia
  • I specifically mentioned HTTP/2 because it should have been easy for everyone to both test and find the relevant info.

    But anyway, here is a short explanation, and the curl-library thread where the issue was first encountered.

    You should also find plenty of blog posts where "unexplainable delay"/"unexplainable slowness"/"something is stuck" is in the premise, and then after a lot of story development and "suspense", the big reveal comes that it was Nagle's fault.

    As with many things TCP. A technique that may have been useful once, ends up proving to be counterproductive when used with modern protocols, workflows, and networks.

    3
  • Jump
    Nagle's algorithm - Wikipedia
  • I already mentioned why! It's common pitfall. For example, try a large HTTP/2 transfer over a socket where TCP_NODELAY is not set (or rather, explicitly unset), and see how the transfer rate would be limited because of it.

    3
  • Jump
    Nagle's algorithm - Wikipedia
  • A reminder that TCP_NODELAY should be set by default, and you should remember to set it if it's not. Many protocols end up being ping-pong ones. This includes HTTP/2 for example.

    4
  • Jump
    Australia's equivalent climates
  • Never set foot in AU.
    I was under the impression that Tasmania doesn't get that cold.
    Also, apparently some would rather describe Perth as Mediterranean-SouthAfrican, rather than Mediterranean-Californian 😉

    6
  • Jump
    Async Rust can be a pleasure to work with (without `Send + Sync + 'static`)
  • but futures only execute when polled.

    The most interesting part here is the polling only has to take place on the scope itself. That was actually what I wanted to check, but got distracted because all spawns are awaited in the scope in moro's README example.

    async fn slp() {
        tokio::time::sleep(std::time::Duration::from_millis(1)).await
    }
    
    async fn _main() {
        let result_fut = moro::async_scope!(|scope| {
            dbg!("d1");
            scope.spawn(async { 
                dbg!("f1a");
                slp().await;
                slp().await;
                slp().await;
                dbg!("f1b");
            });
            dbg!("d2"); // 11
            scope.spawn(async {
                dbg!("f2a");
                slp().await;
                slp().await;
                dbg!("f2b");
            });
            dbg!("d3"); // 14
            scope.spawn(async {
                dbg!("f3a");
                slp().await;
                dbg!("f3b");
            });
            dbg!("d4");
            async { dbg!("b1"); } // never executes
        });
        slp().await;
        dbg!("o1");
        let _ = result_fut.await;
    }
    
    fn main() {
        let rt = tokio::runtime::Builder::new_multi_thread()
            .enable_all()
            .build()
            .unwrap();
        rt.block_on(_main())
    }
    
    [src/main.rs:32:5] "o1" = "o1"
    [src/main.rs:7:9] "d1" = "d1"
    [src/main.rs:15:9] "d2" = "d2"
    [src/main.rs:22:9] "d3" = "d3"
    [src/main.rs:28:9] "d4" = "d4"
    [src/main.rs:9:13] "f1a" = "f1a"
    [src/main.rs:17:13] "f2a" = "f2a"
    [src/main.rs:24:13] "f3a" = "f3a"
    [src/main.rs:26:13] "f3b" = "f3b"
    [src/main.rs:20:13] "f2b" = "f2b"
    [src/main.rs:13:13] "f1b" = "f1b"
    

    The non-awaited jobs are run concurrently as the moro docs say. But what if we immediately await f2?

    [src/main.rs:32:5] "o1" = "o1"
    [src/main.rs:7:9] "d1" = "d1"
    [src/main.rs:15:9] "d2" = "d2"
    [src/main.rs:9:13] "f1a" = "f1a"
    [src/main.rs:17:13] "f2a" = "f2a"
    [src/main.rs:20:13] "f2b" = "f2b"
    [src/main.rs:22:9] "d3" = "d3"
    [src/main.rs:28:9] "d4" = "d4"
    [src/main.rs:24:13] "f3a" = "f3a"
    [src/main.rs:13:13] "f1b" = "f1b"
    [src/main.rs:26:13] "f3b" = "f3b"
    

    f1 and f2 are run concurrently, f3 is run after f2 finishes, but doesn't have to wait for f1 to finish, which is maybe obvious, but... (see below).

    So two things here:

    1. Re-using the spawn terminology here irks me for some reason. I don't know what would be better though. Would defer_to_scope() be confusing if the job is awaited in the scope?
    2. Even if assumed obvious, a note about execution order when there is a mix of awaited and non-awaited jobs is worth adding to the documentation IMHO.
    2
  • Jump
    Async Rust can be a pleasure to work with (without `Send + Sync + 'static`)
  • I skimmed the latter parts of this post since I felt like I read it all before, but I think moro is new to me. I was intrigued to find out how scoped span exactly behaves.

    async fn slp() {
        tokio::time::sleep(std::time::Duration::from_millis(1)).await
    }
    
    async fn _main() {
        let value = 22;
        let result_fut = moro::async_scope!(|scope| {
            dbg!(); // line 8
            let future1 = scope.spawn(async {
                slp().await;
                dbg!(); // line 11
                let future2 = scope.spawn(async {
                    slp().await;
                    dbg!(); // line 14
                    value // access stack values that outlive scope
                });
                slp().await;
                dbg!(); // line 18
    
                let v = future2.await * 2;
                v
            });
    
            slp().await;
            dbg!(); // line 25
            let v = future1.await * 2;
            slp().await;
            dbg!(); // line 28
            v
        });
        slp().await;
        dbg!(); // line 32
        let result = result_fut.await;
        eprintln!("{result}"); // prints 88
    }
    
    fn main() {
        // same output with `new_current_thread()` of course
        let rt = tokio::runtime::Builder::new_multi_thread()
            .enable_all()
            .build()
            .unwrap();
        rt.block_on(_main())
    }
    

    This prints:

    [src/main.rs:32:5]
    [src/main.rs:8:9]
    [src/main.rs:25:9]
    [src/main.rs:11:13]
    [src/main.rs:18:13]
    [src/main.rs:14:17]
    [src/main.rs:28:9]
    88
    

    So scoped spawn doesn't really spawn tasks as one might mistakenly think!

    4
  • Jump
    Which protocol or open standard do you like or wish was more popular?
  • Because non-open ones are not available, even for a price. Unless you buy something bigger than the "standard" itself of course, like a company that is responsible for it or having access to it.

    There is also the process of standardization itself, with committees, working groups, public proposals, ..etc involved.

    Anyway, we can't backtrack on calling ISO standards and their likes "open" on the global level, hence my suggestion to use more precise language (“publicly available and sharable”) when talking about truly open standards.

    3
  • Jump
    Which protocol or open standard do you like or wish was more popular?
  • The term open-standard does not cut it. People should start using "publicly available and sharable" instead (maybe there is a better name for it).

    ISO standards for example are technically "open". But how relevant is that to a curious individual developer when anything you need to implement would require access to multiple "open" standards, each coming with a (monetary) price, with some extra shenanigans [archived] on top.

    IETF standards however are actually truly open, as in publicly available and sharable.

    16
  • Jump
    A post by Guido van Rossum removed for violating Python community guidelines
  • It implies that the value of their policy work is significantly below...

    It's always safe to assume that value to be negative unless proven otherwise actually.

    -1
  • Jump
    Debug-time enforcement of borrowing rules, and safety in general.
  • I think your second link isn’t what you intended?

    You scared me for a moment there. I don't know why you thought that.

    Needless to say, even with the first example, metavar expressions are not strictly needed here, as using a second pattern and recursing expansions would work.

    But I wanted to showcase the power of ${ignore}, as it can be cleaner and/or more powerful in some cases where extra patterns and recursing expansions can get messy and hard to track.

    2
  • Jump
    Debug-time enforcement of borrowing rules, and safety in general.
  • First of all, unsafe famously doesn't disable the borrow checker, which is something any Rustacean would know, so your intro is a bit weird in that regard.

    And if you neither like the borrow checker, nor like unsafe rust as is, then why are you forcing yourself to use Rust at all. If you're bored with C++, there are other of languages out there, a couple of which are even primarily developed by game developers, for game developers.

    The fact that you found a pattern that can be alternatively titled "A Generic Method For Introducing Heisenbugs In Rust", and you are somehow excited about it, indicates that you probably should stop this endeavor.

    Generally speaking, I think the Rust community would benefit from making an announcement a long the lines of "If you're a game developer, then we strongly advise you to become a Rustacean outside the field of game development first, before considering doing game development in Rust".

    13
  • github.com Release v0.3.0 · khonsulabs/cushy

    Breaking Changes This crate's MSRV is now 1.74.1, required by updating wgpu. wgpu has been updated to 0.20. winit has been updated to 0.30. All context types no longer accept a 'window life...

    1
    github.com Release 1.6.0 · slint-ui/slint

    Release announcement with the highlights: https://slint.dev/blog/slint-1.6-released Detailed list of changes: ChangeLog

    2