asynchronoustasktoit

Asynchronous data exchange between tasks


The documentation in the "Tasks in Toit" section indicates that the language has facilities for asynchronous data exchange between tasks. If I understand correctly, then two classes from the monitor package: Channel and Mailbox provide this opportunity. Unfortunately, I didn't find examples of using these classes, so I ask you to give at least the simplest example of the implementation of two tasks:

Thanks in advance, MK


Solution

  • Here's an example of the first part, using Channel. This class is useful if you have a stream of messages for another task.

    import monitor
    
    main:
      // A channel with a backlog of 5 items.  Once the reader is 5 items behind, the
      // writer will block when trying to send.  This helps avoid unbounded memory
      // use by the in-flight messages if messages are being generated faster than
      // they are being consumed.  Decreasing this will tend to reduce throughput,
      // increasing it will increase memory use.
      channel := monitor.Channel 5
    
      task:: message_generating_task channel
      task:: message_receiving_task channel
    
    /// Normally this could be looking at IO ports, GPIO pins, or perhaps a socket
    /// connection.  It could block for some unknown time while doing this.  In this
    /// case we just sleep a little to illustrate that the data arrives at random
    /// times.
    generate_message:
      milliseconds := random 1000
      sleep --ms=milliseconds
      // The message is just a string, but could be any object.
      return "Message creation took $(milliseconds)ms"
    
    message_generating_task channel/monitor.Channel:
      10.repeat:
        message := generate_message
        channel.send message
      channel.send null  // We are done.
    
    /// Normally this could be looking at IO ports, GPIO pins, or perhaps a socket
    /// connection.  It could block for some unknown time while doing this.  In this
    /// case we just sleep a little to illustrate that the data takes a random
    /// amount of time to process.
    process_message message:
      milliseconds := random 1000
      sleep --ms=milliseconds
      print message
    
    message_receiving_task channel/monitor.Channel:
      while message := channel.receive:
        process_message message
    

    Here is an example of using Mailbox. This class is useful if you have a task processing requests and giving responses to other tasks.

    import monitor
    
    main:
      mailbox := monitor.Mailbox
    
      task:: client_task 1 mailbox
      task:: client_task 2 mailbox
      task --background:: factorization_task mailbox
    
    /// Normally this could be looking at IO ports, GPIO pins, or perhaps a socket
    /// connection.  It could block for some unknown time while doing this.  For
    /// this example we just sleep a little to illustrate that the data arrives at
    /// random times.
    generate_huge_number:
      milliseconds := random 1000
      sleep --ms=milliseconds
      return (random 100) + 1  // Not actually so huge.
    
    client_task task_number mailbox/monitor.Mailbox:
      10.repeat:
        huge := generate_huge_number
        factor := mailbox.send huge  // Send number, wait for result.
        other_factor := huge / factor
        print "task $task_number: $factor * $other_factor == $huge"
    
    // Factorize a number using the quantum computing port.
    factorize_number number:
      // TODO: Use actual quantum computing instead of brute-force search.
      for i := number.sqrt.round; i > 1; i--:
        factor := number / i
        if factor * i == number:
          return factor
        // This will yield so the other tasks can run.  In a real application it
        // would be waiting on an IO pin connected to the quantum computing unit.
        sleep --ms=1
      return 1  // 1 is sort-of a factor of all integers.
    
    factorization_task mailbox/monitor.Mailbox:
      // Because this task was started as a background task (see 'main' function),
      // the program does not wait for it to exit so this loop does not need a real
      // exit condition.
      while number := mailbox.receive:
        result := factorize_number number
        mailbox.reply result