• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

examples/counter/H06-Oct-2019-

.gitignoreH A D06-Oct-2019192

LICENSEH A D06-Oct-20191 KiB

README.mdH A D06-Oct-20194.2 KiB

actor.goH A D06-Oct-20194.7 KiB

actor_mobile.goH A D06-Oct-2019138

actor_other.goH A D06-Oct-2019141

actor_test.goH A D06-Oct-20193.6 KiB

doc.goH A D06-Oct-2019706

go.modH A D06-Oct-201942

README.md

1# Phony
2
3[![Go Report Card](https://goreportcard.com/badge/github.com/Arceliar/phony)](https://goreportcard.com/report/github.com/Arceliar/phony)
4
5[godoc](https://godoc.org/github.com/Arceliar/phony)
6
7Phony is a *very* minimal actor model library for Go, inspired by the causal messaging system in the [Pony](https://ponylang.io/) programming language. This was written in a weekend as an exercise/test, to demonstrate how easily the Actor model can be implemented in Go, rather than as something intended for real-world use. Note that these are Actors running in the local process (as in Pony), not in other processes or on other machines (as in [Erlang](https://www.erlang.org/)).
8
9Phony was written in response to a few places where, in my opinion, idiomatic Go leaves a lot to be desired:
10
111. Cyclic networks of goroutines that communicate over channels can deadlock, so you end up needing to either drop messages or write some manual buffering or scheduling logic (which is often error prone). Or you can rewrite your code to have no cycles, but sometimes the problem at hand is best modeled with the cycles. I don't really like any of these options. Go makes concurrency and communication *easy*, but combining them isn't *safe*.
122. Goroutines that wait for work from a channel can leak if not signaled to shut down properly, and that shutdown mechanism needs to be manually implemented in most cases. Sometimes it's as easy as ranging over a channel and defering a close, other times it can be a lot more complicated. It's annoying that Go is garbage collected, but it's killer features (goroutines and channels) still need manual management to avoid leaks.
133. I'm tired of writing infinite for loops over select statements. The code is not reusable and resists composition. Lets say I have some type which normally has a worker goroutine associated with it, sitting in a for loop over a select statement. If I want to embed that type in a new struct, which includes any additional channels that must be selected on, I need to rewrite the entire select loop. There's no mechanism to say "and also add this one behavior" without enumerating the full list of behaviors I want from my worker. This is depressing in light of how nicely things behave when a struct anonymously embeds a type, where fields and functions compose beautifully.
14
15## Features
16
171. Small implementation, only around 100 lines of code, excluding tests and examples. It depends only on a couple of commonly used standard library packages.
182. `Actor`s are extremely lightweight. On `x86_64`, an actor only takes up 16 bytes for their `Inbox` plus 32 bytes per message. While not running, an `Actor` has no associated goroutines, and it can be garbage collected just like any other object when it is no longer needed, even for cycles of `Actor`s.
193. Asynchronous message passing between `Actor`s. Unlike networks go goroutines communicating over channels, sending messages between `Actor`s cannot deadlock.
204. Unbounded Inbox sizes are kept small in practice through backpressure and scheduling. `Actor`s that send to an overworked recipient will pause at a safe point in the future, and wait until signaled that the recipient has caught up. A paused `Actor` also has no associated goroutine or stack.
21
22## Benchmarks
23
24```
25goos: linux
26goarch: amd64
27pkg: github.com/Arceliar/phony
28BenchmarkBlock-4             	 1000000	      1369 ns/op	     160 B/op	       3 allocs/op
29BenchmarkAct-4               	 5608544	       228 ns/op	      64 B/op	       2 allocs/op
30BenchmarkActFromNil-4        	11455034	        97.3 ns/op	      32 B/op	       1 allocs/op
31BenchmarkActFromMany-4       	 5889196	       204 ns/op	      64 B/op	       2 allocs/op
32BenchmarkActFromManyNil-4    	 5770095	       211 ns/op	      64 B/op	       2 allocs/op
33BenchmarkPingPong-4          	 1766218	       652 ns/op	      32 B/op	       1 allocs/op
34BenchmarkChannelMany-4       	 2354626	       500 ns/op	       0 B/op	       0 allocs/op
35BenchmarkChannel-4           	 1345572	       896 ns/op	       0 B/op	       0 allocs/op
36BenchmarkBufferedChannel-4   	15126925	        70.7 ns/op	       0 B/op	       0 allocs/op
37PASS
38ok  	github.com/Arceliar/phony	13.738s
39```
40
41If you're here then presumably you can read Go, so I'd recommend just checking the code to see exactly what the benchmarks are testing.
42