Skip to content

comm::peek hangs #2841

Description

@jesse99

Was able to get comm::peek to hang with 8+ tasks. This was on Mac 10.7.4 using a rust pulled on July 8, 2012.

Here is a test case:

// rustc --test multiple_tasks.rs && ./multiple_tasks
use std;

enum control_event
{
    dup_event(str, comm::chan<str>),
    close_event,
}

fn silly(n: uint) -> comm::chan<control_event>
{
    do task::spawn_listener
    |control_port: comm::port<control_event>|
    {
        io::println(#fmt["started task %?", n]);
        loop
        {
            libc::funcs::posix88::unistd::sleep(1);

            if comm::peek(control_port)
            {
                alt comm::recv(control_port)
                {
                    dup_event(data, data_ch)
                    {
                        io::println(#fmt["task %? received %?", n, data]);
                        comm::send(data_ch, data + data);
                    }
                    close_event
                    {
                        io::println(#fmt["exiting task %?", n]);
                        break;
                    }
                }
            }
        }
    }
}

#[test]
fn blah()
{
    let po = comm::port();
    let ch = comm::chan(po);

    // With under 8 this test passes.
    // With 8 and above the test hangs (last thing printed is "started task 7").
    // If prints are added to the loop in silly you can see that the tasks are running.
    let silly_channels = do vec::from_fn(8) |n| {silly(n)};

    for vec::each(silly_channels)
    |sc|
    {
        comm::send(sc, dup_event("hey", ch));
        assert comm::recv(po) == "heyhey";
    }

    for vec::each(silly_channels)
    |sc|
    {
        comm::send(sc, close_event);
    }
}

Activity

  1. jesse99 commented on Jul 8, 2012

    @jesse99
    ContributorAuthor

    This seems to be even more serious than I thought: I rewrote my code to use select2 instead of peek and now the select2 is hanging...

  2. jesse99 commented on Jul 9, 2012

    @jesse99
    ContributorAuthor

    Was able to work around this using task::spawn_sched:

    // rustc --test multiple_tasks.rs && ./multiple_tasks
    use std;
    
    enum control_event
    {
        dup_event(str, comm::chan<str>),
        close_event,
    }
    
    // This works with single_threaded and with manual_threads(4|16).
    // Also tried thread_per_core and thread_per_task but they are not
    // implemented.
    fn silly2(n: uint) -> comm::chan<control_event>
    {
        let channel_port = comm::port();
        let channel_channel = comm::chan(channel_port);
    
        do task::spawn_sched(task::manual_threads(4))
        {
            let control_port: comm::port<control_event> = comm::port();    // this is bookkeeping that spawn_listener handles for us
            let control_channel = comm::chan(control_port);
            comm::send(channel_channel, control_channel);
    
            io::println(#fmt["started task %?", n]);
            loop
            {
                libc::funcs::posix88::unistd::sleep(1);
    
                if comm::peek(control_port)
                {
                    alt comm::recv(control_port)
                    {
                        dup_event(data, data_ch)
                        {
                            io::println(#fmt["task %? received %?", n, data]);
                            comm::send(data_ch, data + data);
                        }
                        close_event
                        {
                            io::println(#fmt["exiting task %?", n]);
                            break;
                        }
                    }
                }
            }
        };
    
        comm::recv(channel_port)
    }
    
    #[test]
    fn blah()
    {
        let po = comm::port();
        let ch = comm::chan(po);
    
        let silly_channels = do vec::from_fn(10) |n| {silly2(n)};
    
        for vec::each(silly_channels)
        |sc|
        {
            comm::send(sc, dup_event("hey", ch));
            assert comm::recv(po) == "heyhey";
        }
    
        for vec::each(silly_channels)
        |sc|
        {
            comm::send(sc, close_event);
        }
    }
    
  3. eholk commented on Jul 9, 2012

    @eholk
    Contributor

    I suspect the call to libc::funcs::posix88::unistd::sleep(1) might be part of the problem. sleep is outside of the realm of things the Rust scheduler is aware of, and so sleeping blocks a whole thread instead of allowing other tasks to run on that thread while the sleeping task is sleeping. I'd recommend using std::timer::sleep, which the Rust scheduler is aware of. That said, unistd::sleep should wake up eventually, so I'm surprised this is hanging rather than just taking a potentially very long time to finish.

    Also, recv blocks until data is available on a port, so sleeping in the loop isn't really necessary. It may be that you want to periodically perform some other processing though. If this is the case, I'd suggest using std::timer::recv_timeout.

  4. ghost assigned on Jul 12, 2012
  5. jesse99 commented on Jul 13, 2012

    @jesse99
    ContributorAuthor

    I don't think this behavior can be pinned on unistd::sleep (exclusively). Yesterday I saw the same symptom (task not starting up) and worked around it the same way (task::spawn_sched instead of task::spawn). There was a lot of code involved and a lot of tasks running around, but my code at least didn't call sleep.

    (The code above was from some sample code of mine which used sleep in a rather unrealistic manner to keep the sample simple. My real code is where I saw this new task hang).

  6. jesse99 commented on Jul 14, 2012

    @jesse99
    ContributorAuthor

    Found another place where tasks launched with spawn just hang: the test_server_client unit test in https://github.com/jdm/rust-socket on "Linux shell 3.2.0-24-generic #39-Ubuntu SMP Mon May 21 16:52:17 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux". Worked around again with spawn_sched.

    Note that those unit tests did work on my Mac which is where I do 99% of my rust work.

  7. tedhorst commented on Jul 18, 2012

    @tedhorst
    Contributor

    Adding task::yield to comm::peek fixes the original case. I'm not sure about the select2 case. See pull request #2947.

  8. tedhorst commented on Jul 18, 2012

    @tedhorst
    Contributor

    The select2 case works for me. As a side note, I think there should be library sleep function that calls task::yield.

  9. eholk commented on Jul 18, 2012

    @eholk
    Contributor

    Nice work, @tedhorst. We don't currently insert yield checks on loop backedges like we should (#524), which means to loop around sleep and peek never yields. Adding a call to task::yield in comm::peek seems like kind of a hack, but I think it's reasonable for now given that we don't do yield checks like we should.

  10. eholk commented on Jul 25, 2012

    @eholk
    Contributor

    This was fixed by #2947.

  11. added a commit that references this issue on Apr 28, 2023
  12. added a commit that references this issue on Apr 22, 2026
  13. added a commit that references this issue on Aug 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions