johnny-morrice / godelbrot

Unix-style Mandelbrot set renderer in Golang
MIT License
8 stars 1 forks source link

Plugins #13

Open johnny-morrice opened 8 years ago

johnny-morrice commented 8 years ago

While godelbrot is flexible in terms of its internal engineering, for true flexibility fractal render algorithms ought to be external code that implement a plugin interface.

A likely plugin provider is

https://github.com/hashicorp/go-plugin

We assume in any case where the user is getting bored, the complexity of rendering a fractal dwarfs the time to copy output over the loopback interface. :)

johnny-morrice commented 8 years ago

Plugins ought not just to run once, but to persist for multiple renderings. This allows:

johnny-morrice commented 8 years ago

Have looked at all the Go plugin alternatives. They are all based on net/rpc.

This makes me sad because I don't want to use an RPC format. It has been really great so far to be able to dump process input, inspect it, use it as a config file, whatever. That's just not something I'd be able to do with RPC.

It seems using RPC based plugins really appeals to people who don't want to use pipes. For example, their app runs in a datacenter. I am targeting desktops, perhaps single servers.

For this reason, I think I will roll an ultra-minimal plugin interface based on JSON and process pipes. I am surprised something like this doesn't already exist in Go!

Hoping to get an implementation in less than 200SLOC!

README.md of planned package is below

Pjays

Functional plugins with JSON and process pipes.

Motivation

RPC is silly.

Go to bed.

Usage

// Plugin author defines her plugin
me := pjays.Plugin{
    InType: "",
    OutType: 0,
    Input: os.Stdin,
    Output: os.Stdout,
}

// Plugin spawns its goroutines
me.Go()

// Plugin author performs plugin logic
s := <- me.Rch
me.Wch <- len(string(s))

// Application author defines plugin reference.
plugin := pjays.PluginHandle{
    Bin: "plugin-count", 
    InType: "",
    OutType: 0, 
    Input: pipeIn,
    Output: pipeOut,
}

// Spawn goroutines
plugin.Go()

// Plugin author communicates with plugin 
plugin.Wch <- "hello"
cnt := <- plugin.Rch

fmt.Println(cnt) // 5

Credits

Johnny Morrice http://jmorrice.teoma.io http://github.com/johnny-morrice

johnny-morrice commented 8 years ago

Working on this today, I realised that shelling out has many of the same problems as RPC. Even though it's a lot simpler.

The core issue is that serializing/deserializing over process/language/codebase boundaries is difficult.

Let's face it: fractal fans have already written all kinds of toy renderers. They aren't going to rearchitect their systems to match Godelbrot's expectations. They aren't going to learn Golang. They don't want to pollute their $PATH or their network with wrappers/shims/DCOM style RPC bollocks.

NEW PLAN

Godelbrot will embed Lua.

A plugin is a Lua function that receives an object representation of Godelbrot's json config format.

Plugin discovery is Lua namespace.

The function writes the fractal render image to stdout and any warnings or errors to stderr.

The Lua function must return an error code.

Plugin authors are free to shell out from Lua to invoke their program, or a shim that calls their service, or or or or....