Skip to content

Copying

Problem

You need to send mutable data from one actor to another while keeping a copy of it in your original actor.

Solution

use "collections"

actor Collector
  """
  Receives characters via it's `collect` behavior and stores them.
  Every 10 characters we receive results in the entire array being sent all to
  the receiver.
  """
  let _receiver: Receiver
  let _data: Array[U8] = Array[U8]

  new create(receiver: Receiver) =>
    _receiver = receiver

  be collect(char: U8) =>
    _data.push(char)

    if (_data.size() % 10) == 0 then
      let copy: Array[U8] iso = recover Array[U8] end

      for v in _data.values() do
        copy.push(v)
      end

      _receiver.receive(consume copy)
    end

actor Receiver
  """
  Receives an array of characters from a collector and prints them as a string
  to standard out.
  """
  let _out: OutStream

  new create(out: OutStream) =>
    _out = out

  be receive(data: Array[U8] iso) =>
    let s = String.from_array(consume data)
    _out.print(s)

The critical section from our example is:

      let copy: Array[U8] iso = recover Array[U8] end

      for v in _data.values() do
        copy.push(v)
      end

      _receiver.receive(consume copy)

Let’s walk through what we are doing. First, we create a new iso array that we will populate with the values from _data and then to our receiver. That our array is an iso is crucial. Because copy is isolated, the Pony compiler will make sure that we only ever have a single reference to it and can safely share the isolated copy.

      let copy: Array[U8] iso = recover Array[U8] end

Next, we copy all the value from our mutable ref array into our iso array:

      for v in _data.values() do
        copy.push(v)
      end

Finally, we consume our reference to the iso copy array and send it to our receiver:

      _receiver.receive(consume copy)

Discussion

The copying pattern goes hand in hand with the use of persistent data structures to share data between actors. Each method involves copying data. Which pattern should you pick? Whichever one will minimize the number of copies.

Persistent data structures involve copying on each update. The copying pattern involves a copy each time we send the data to another actor. As a general rule of thumb, you should figure out which you will do more: update or send. If you are updating more, use the copying pattern. If you are sending more, use persistent data structures.

In the end, that’s just a rule of thumb. Your best bet is to benchmark and pick the method that gives you the best performance for your use case. In our simple example, we are going to update our _data array 10 times for every one time we copy it to send. In this case, we are pretty sure that our rule of thumb would stand up to benchmarking.