1# Phony
2
3[![Go Report Card](https://goreportcard.com/badge/github.com/Arceliar/phony)](https://goreportcard.com/report/github.com/Arceliar/phony)
4[![GoDoc](https://godoc.org/github.com/Arceliar/phony?status.svg)](https://godoc.org/github.com/Arceliar/phony)
5
6Phony is a [Pony](https://ponylang.io/)-inspired proof-of-concept implementation of shared-memory actor-model concurrency in the Go programming language. `Actor`s automatically manage goroutines and use asynchronous causal messaging (with backpressure) for communcation. This makes it easy to write programs that are free from deadlocks, goroutine leaks, and many of the `for` loops over `select` statements that show up in boilerplate code. The down side is that the code needs to be written in an asynchronous style, which is not idiomatic to Go, so it can take some getting used to.
7
8## Benchmarks
9
10```
11goos: linux
12goarch: amd64
13pkg: github.com/Arceliar/phony
14BenchmarkLoopActor-4                	15617646	        71.1 ns/op	      16 B/op	       1 allocs/op
15BenchmarkLoopChannel-4              	14870767	        73.0 ns/op	       0 B/op	       0 allocs/op
16BenchmarkSendActor-4                	 3268095	       377 ns/op	      32 B/op	       2 allocs/op
17BenchmarkSendChannel-4              	 2598151	       442 ns/op	       0 B/op	       0 allocs/op
18BenchmarkRequestResponseActor-4     	 2256913	       527 ns/op	      48 B/op	       3 allocs/op
19BenchmarkRequestResponseChannel-4   	 1257068	       869 ns/op	       0 B/op	       0 allocs/op
20BenchmarkBlock-4                    	  780747	      1586 ns/op	     144 B/op	       3 allocs/op
21PASS
22ok  	github.com/Arceliar/phony	12.677s
23```
24
25These are microbenchmarks, but they seem to indicate that `Actor` messaging and goroutine+channel operations have comparable cost. I suspect that the difference is negligible in most applications.
26
27## Implementation Details
28
29The code base is short, under 100 source lines of code as of writing, so reading the code is probably the best way to see *what* it does, but that doesn't necessarily explain *why* certain design decisions were made. To elaborate on a few things:
30
31- Phony only depends on packages from the standard library:
32    - `runtime` for some scheduler manipulation (through `Goexit()` and `Gosched()`).
33    - `sync/atomic` to implement the `Inbox`'s message queues.
34    - `unsafe` to use `atomic`'s `unsafe.Pointer` operations, which the paranoid should audit themselves for correctness.
35
36- Attempts were make to make embedding and composition work:
37    - `Actor` is an `interface` satisfied by the `Inbox` `struct`.
38    - The zero value of an `Inbox` is a fully initialized and ready-to-use `Actor`
39    - This means any `struct` that anonymously embeds an `Inbox` is an `Actor`
40    - `struct`s that don't want to export the `Actor` interface can embed it as a field instead.
41
42- `Inbox` was implemented with scalability in mind:
43    - The `Inbox` is basically an atomically updated single-consumer multiple-producer linked list.
44    - Pushing a message is wait-free -- no locks, spinlocks, or `CompareAndSwap` loops.
45    - Popping messages is wait-free in the normal case, with a busy loop (`LoadPointer`) if popping the last message lost a race with a push.
46    - When backpressure is required, it's implemented by sending two extra messages (one to the receiver of the original message, and one to the sender).
47
48- The implementation aims to be as lightweight as reasonably possible:
49    - On `x86_64`, an empty `Inbox` is 24 bytes, and messages overhead is 16 bytes, or half that on `x86`.
50    - An `Actor` with an empty `Inbox` has no goroutine.
51    - An `Actor` that has stopped due to backpressure also has no goroutine.
52    - This means that idle `Actor`s can be collected as garbage when they're no longer reachable, just like any other `struct`.
53
54