Mega patch
This is still a dirty prototype, so please excuse the mess. Please excuse the fact that this is a mega patch. Once things settle down this won't happen any more. Some of the changes squashed into here include: * Merge vertex loop with type loop (The file watcher seems to cache events anyways) * Improve pgraph library * Add indegree, outdegree, and topological sort with tests * Add reverse function for vertex list * Tons of additional cleanup! Amazingly, on my first successful compile, this seemed to run! A special thanks to Ira Cooper who helped me talk through some of the algorithmic decisions and for his help in finding better ones!
This commit is contained in:
223
types.go
223
types.go
@@ -18,56 +18,231 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"code.google.com/p/go-uuid/uuid"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Type interface {
|
||||
//Name() string
|
||||
Watch(*Vertex)
|
||||
Init()
|
||||
GetName() string // can't be named "Name()" because of struct field
|
||||
Watch()
|
||||
StateOK() bool // TODO: can we rename this to something better?
|
||||
Apply() bool
|
||||
Exit() bool
|
||||
SetVertex(*Vertex)
|
||||
Compare(Type) bool
|
||||
SendEvent(eventName, bool)
|
||||
GetTimestamp() int64
|
||||
UpdateTimestamp() int64
|
||||
//Process()
|
||||
}
|
||||
|
||||
type BaseType struct {
|
||||
Name string `yaml:"name"`
|
||||
timestamp int64 // last updated timestamp ?
|
||||
events chan Event
|
||||
vertex *Vertex
|
||||
}
|
||||
|
||||
type NoopType struct {
|
||||
uuid string
|
||||
Type string // always "noop"
|
||||
Name string // name variable
|
||||
Events chan string // FIXME: eventually a struct for the event?
|
||||
BaseType `yaml:",inline"`
|
||||
Comment string `yaml:"comment"` // extra field for example purposes
|
||||
}
|
||||
|
||||
func NewNoopType(name string) *NoopType {
|
||||
// FIXME: we could get rid of this New constructor and use raw object creation with a required Init()
|
||||
return &NoopType{
|
||||
uuid: uuid.New(),
|
||||
Type: "noop",
|
||||
Name: name,
|
||||
Events: make(chan string, 1), // XXX: chan size?
|
||||
BaseType: BaseType{
|
||||
Name: name,
|
||||
events: make(chan Event), // unbuffered chan size to avoid stale events
|
||||
vertex: nil,
|
||||
},
|
||||
Comment: "",
|
||||
}
|
||||
}
|
||||
|
||||
func (obj NoopType) Watch(v *Vertex) {
|
||||
select {
|
||||
case exit := <-obj.Events:
|
||||
if exit == "exit" {
|
||||
// initialize structures like channels if created without New constructor
|
||||
func (obj *BaseType) Init() {
|
||||
obj.events = make(chan Event)
|
||||
}
|
||||
|
||||
// this method gets used by all the types, if we have one of (obj NoopType) it would get overridden in that case!
|
||||
func (obj *BaseType) GetName() string {
|
||||
return obj.Name
|
||||
}
|
||||
|
||||
func (obj *BaseType) GetVertex() *Vertex {
|
||||
return obj.vertex
|
||||
}
|
||||
|
||||
func (obj *BaseType) SetVertex(v *Vertex) {
|
||||
obj.vertex = v
|
||||
}
|
||||
|
||||
// get timestamp of a vertex
|
||||
func (obj *BaseType) GetTimestamp() int64 {
|
||||
return obj.timestamp
|
||||
}
|
||||
|
||||
// update timestamp of a vertex
|
||||
func (obj *BaseType) UpdateTimestamp() int64 {
|
||||
obj.timestamp = time.Now().UnixNano() // update
|
||||
return obj.timestamp
|
||||
}
|
||||
|
||||
// can this element run right now?
|
||||
func (obj *BaseType) OKTimestamp() bool {
|
||||
v := obj.GetVertex()
|
||||
g := v.GetGraph()
|
||||
// these are all the vertices pointing TO v, eg: ??? -> v
|
||||
for _, n := range g.IncomingGraphEdges(v) {
|
||||
// if the vertex has a greater timestamp than any pre-req (n)
|
||||
// then we can't run right now...
|
||||
if obj.GetTimestamp() > n.Type.GetTimestamp() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (obj *BaseType) Poke() bool { // XXX: how can this ever fail and return false? eg: when is a poke not possible and should be rescheduled?
|
||||
v := obj.GetVertex()
|
||||
g := v.GetGraph()
|
||||
// these are all the vertices pointing AWAY FROM v, eg: v -> ???
|
||||
for _, n := range g.OutgoingGraphEdges(v) {
|
||||
n.SendEvent(eventPoke, false) // XXX: should this be sync or not? XXX: try it as async for now, but switch to sync and see if we deadlock -- maybe it's possible, i don't know for sure yet
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// push an event into the message queue for a particular type vertex
|
||||
func (obj *BaseType) SendEvent(event eventName, sync bool) {
|
||||
if !sync {
|
||||
obj.events <- Event{event, nil, ""}
|
||||
return
|
||||
}
|
||||
|
||||
resp := make(chan bool)
|
||||
obj.events <- Event{event, resp, ""}
|
||||
for {
|
||||
value := <-resp
|
||||
// wait until true value
|
||||
if value {
|
||||
return
|
||||
} else {
|
||||
log.Fatal("Unknown event: %v\n", exit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (obj NoopType) Exit() bool {
|
||||
obj.Events <- "exit"
|
||||
return true
|
||||
// process events when a select gets one
|
||||
// this handles the pause code too!
|
||||
func (obj *BaseType) ReadEvent(event *Event) bool {
|
||||
|
||||
event.ACK()
|
||||
switch event.Name {
|
||||
case eventStart:
|
||||
return true
|
||||
|
||||
case eventPoke:
|
||||
return true
|
||||
|
||||
case eventExit:
|
||||
return false
|
||||
|
||||
case eventPause:
|
||||
// wait for next event to continue
|
||||
select {
|
||||
case e := <-obj.events:
|
||||
e.ACK()
|
||||
if e.Name == eventExit {
|
||||
return false
|
||||
} else if e.Name == eventContinue {
|
||||
return true
|
||||
} else {
|
||||
log.Fatal("Unknown event: ", e)
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
log.Fatal("Unknown event: ", event)
|
||||
}
|
||||
return false // required to keep the stupid go compiler happy
|
||||
}
|
||||
|
||||
func (obj NoopType) StateOK() bool {
|
||||
// XXX: rename this function
|
||||
func (obj *BaseType) Process(typ Type) {
|
||||
var ok bool
|
||||
|
||||
ok = true
|
||||
// is it okay to run dependency wise right now?
|
||||
// if not, that's okay because when the dependency runs, it will poke
|
||||
// us back and we will run if needed then!
|
||||
if obj.OKTimestamp() {
|
||||
// XXX XXX: why does this have to be typ instead of just obj! "obj.StateOK undefined (type *BaseType has no field or method StateOK)"
|
||||
|
||||
if !typ.StateOK() { // TODO: can we rename this to something better?
|
||||
// throw an error if apply fails...
|
||||
// if this fails, don't UpdateTimestamp()
|
||||
if !typ.Apply() { // check for error
|
||||
ok = false
|
||||
}
|
||||
}
|
||||
|
||||
if ok {
|
||||
// if poke fails, don't update timestamp
|
||||
// since we didn't propagate the pokes!
|
||||
if obj.Poke() {
|
||||
obj.UpdateTimestamp() // this was touched...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (obj *NoopType) Watch() {
|
||||
//vertex := obj.vertex // stored with SetVertex
|
||||
var send = false // send event?
|
||||
for {
|
||||
|
||||
select {
|
||||
case event := <-obj.events:
|
||||
|
||||
if ok := obj.ReadEvent(&event); !ok {
|
||||
return // exit
|
||||
}
|
||||
send = true
|
||||
}
|
||||
|
||||
// do all our event sending all together to avoid duplicate msgs
|
||||
if send {
|
||||
send = false
|
||||
|
||||
obj.Process(obj) // XXX: rename this function
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (obj *NoopType) StateOK() bool {
|
||||
return true // never needs updating
|
||||
}
|
||||
|
||||
func (obj NoopType) Apply() bool {
|
||||
fmt.Printf("Apply->%v[%v]\n", obj.Type, obj.Name)
|
||||
func (obj *NoopType) Apply() bool {
|
||||
fmt.Printf("Apply->Noop[%v]\n", obj.Name)
|
||||
return true
|
||||
}
|
||||
|
||||
func (obj *NoopType) Compare(typ Type) bool {
|
||||
switch typ.(type) {
|
||||
// we can only compare NoopType to others of the same type
|
||||
case *NoopType:
|
||||
return obj.compare(typ.(*NoopType))
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (obj *NoopType) compare(typ *NoopType) bool {
|
||||
if obj.Name != typ.Name {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user