Skip to content

Instantly share code, notes, and snippets.

@adityagodbole
Last active August 29, 2015 14:15
Show Gist options
  • Save adityagodbole/2e2786b8b8b66e27f165 to your computer and use it in GitHub Desktop.
Save adityagodbole/2e2786b8b8b66e27f165 to your computer and use it in GitHub Desktop.
package main
import (
"errors"
"os"
"fmt"
"reflect"
"strconv"
)
// The interface of the type which embeds the original struct
// The way to interact with the new
type Agent interface {
Send(string, ...interface{}) (interface{}, error)
Stop()
Raw() interface{}
}
// Our default implementation
type atomicData struct {
d interface{}
com_ch chan msg
methods map[string]*reflect.Method
}
// Internal type for the command message
type msg struct {
Com string
Params []interface{}
RetCh chan resp
}
// Internal type for the response
type resp struct {
Res interface{}
Ok bool
}
func (a *atomicData) Raw() interface{} {
return a.d
}
func (a *atomicData) Stop() {
a.com_ch <- msg{Com: "AgentStop"}
}
func (a *atomicData) Send(com string, p ...interface{}) (interface{}, error) {
ch := make(chan resp)
a.com_ch <- msg{Com: com, RetCh: ch, Params: p}
res := <-ch
close(ch)
if res.Ok {
return res.Res, nil
}
return nil, errors.New("No response")
}
func (a *atomicData) init_chan() {
a.com_ch = make(chan msg)
}
// Memoised (per struct) method retrieval
func (a *atomicData) getMethod(name string) (*reflect.Method, bool) {
if a.methods[name] != nil {
return a.methods[name], true
}
blobType := reflect.TypeOf(a.d)
method, found := blobType.MethodByName(name)
a.methods[name] = &method
return &method, found
}
func call_op(a *atomicData , fname string, params []interface{}) (resp, error) {
d := a.d
method, found := a.getMethod(fname)
if !found {
return resp{}, errors.New(fmt.Sprintf("Method %d not found", fname))
}
fvalue := method.Func
if fvalue.Kind() != reflect.Func {
return resp{}, errors.New("Handler is not a function type")
}
methodType := method.Type
if methodType.NumIn() != len(params)+1 {
msg := fmt.Sprintf("Handler parameter arity error (%d instead of %d)", len(params),
methodType.NumIn())
return resp{}, errors.New(msg)
}
args := make([]reflect.Value, 8)
args[0] = reflect.ValueOf(d)
for i := 1; i < methodType.NumIn(); i++ {
arg := params[i-1]
args[i] = reflect.ValueOf(arg)
}
// call the function with the params
res := fvalue.Call(args[0:methodType.NumIn()])
resp := resp{}
if methodType.NumOut() > 0 {
resp.Ok = true
resp.Res = res[0].Interface()
}
return resp, nil
}
func Go(d interface{}) Agent {
a := &atomicData{d: d}
a.init_chan()
a.methods = make(map[string]*reflect.Method)
go func() {
for msg := range a.com_ch {
if msg.Com == "AgentStop" {
return
}
res, err := call_op(a, msg.Com, msg.Params)
if err != nil {
fmt.Println(err.Error())
}
if msg.RetCh != nil {
msg.RetCh <- res
}
}
}()
return a
}
///////////////////////////////////////////////////////////////////
// This is the code a user of the Agent library would have to write
///////////////////////////////////////////////////////////////////
type myStruct struct {
val int
at Agent
}
func (a *myStruct) Inc(n int) int {
a.val += n
return a.val
}
func (a myStruct) Val() int {
return a.val
}
func New(val int) Agent {
a := &myStruct{val: val}
return Go(a)
}
func main() {
arg, _ := strconv.ParseInt(os.Args[1], 10, 0)
n_mut := int(arg)
i := New(10)
defer i.Stop()
ch := make(chan bool)
mutator := func() {
i.Send("Inc", 1)
ch <- true
}
for i := 0; i < n_mut; i++ {
go mutator()
}
rcount := 0
for _ = range ch {
rcount++
if rcount >= n_mut {
fmt.Printf("%d", i.Raw().(*myStruct).Val())
break
}
}
}
package main
import (
"fmt"
"os"
"strconv"
)
type Blob interface {
IsBlob()
}
type AtomicData interface {
Send(string, interface{}, bool) interface{}
}
type atomicData struct {
d Blob
com_ch chan Msg
ops map[string]Op
}
type Op func(Blob, interface{}) interface{}
type Msg struct {
Com string
Params interface{}
RetCh chan interface{}
}
func (a *atomicData) Send(com string, p interface{}, hasResponse bool) interface{} {
var ch chan interface{}
if hasResponse {
ch = make(chan interface{})
} else {
ch = nil
}
a.com_ch <- Msg{Com: com, RetCh: ch, Params: p}
var res interface{}
if hasResponse {
res = <-ch
close(ch)
}
return res
}
func (a *atomicData) init_chan() {
a.com_ch = make(chan Msg)
}
func Go(d Blob, ops map[string]Op) AtomicData {
a := &atomicData{d: d}
a.init_chan()
go func() {
for msg := range a.com_ch {
if msg.Com == "AgentStop" {
return
}
fun := ops[msg.Com]
res := fun(a.d, msg.Params)
if msg.RetCh != nil && res != nil {
msg.RetCh <- res
}
}
}()
return a
}
///////////////////////////////////////////////////////////////////
// This is the code a user of the Agent library would have to write
///////////////////////////////////////////////////////////////////
type myStruct struct {
val int
at AtomicData
}
func (b *myStruct) IsBlob() {}
var ops = map[string]Op{
"inc": func(a Blob, params interface{}) interface{} {
var data int
if params != nil {
data = params.(int)
}
val := a.(*myStruct)
return val.inc(data)
},
}
func (a *myStruct) inc(n int) int {
a.val += n
return a.val
}
func (a myStruct) Val() int {
return a.val
}
func New(val int) *myStruct {
a := &myStruct{val: val}
a.at = Go(a, ops)
return a
}
func (a *myStruct) Delete() {
a.at.Send("AgentStop", nil, false)
}
func (a *myStruct) Inc(n int) int {
res := a.at.Send("inc", n, true)
return res.(int)
}
func main() {
arg, _ := strconv.ParseInt(os.Args[1], 10, 0)
n_mut := int(arg)
i := New(10)
defer i.Delete()
ch := make(chan bool)
mutator := func() {
i.Inc(1)
ch <- true
}
for i := 0; i < n_mut; i++ {
go mutator()
}
rcount := 0
for _ = range ch {
rcount++
if rcount >= n_mut {
fmt.Printf("%d", i.Val())
break
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment