2013-09-04 22:02:17 -04:00
|
|
|
/*
|
|
|
|
Copyright (C) 2013 Aaron Lindsay <aaron@aclindsay.com>
|
|
|
|
*/
|
|
|
|
|
2013-08-13 21:51:19 -04:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"asink"
|
|
|
|
)
|
|
|
|
|
|
|
|
type pathMapRequest struct {
|
|
|
|
path string
|
|
|
|
local bool /*is this event local (true) or remote(false)?*/
|
|
|
|
responseChan chan *asink.Event
|
|
|
|
}
|
|
|
|
|
|
|
|
type pathMapValue struct {
|
|
|
|
latestEvent *asink.Event
|
|
|
|
locked bool
|
|
|
|
localWaiters []chan *asink.Event
|
|
|
|
remoteWaiters []chan *asink.Event
|
|
|
|
}
|
|
|
|
|
|
|
|
var pathLockerChan = make(chan *pathMapRequest)
|
|
|
|
var pathUnlockerChan = make(chan *asink.Event)
|
|
|
|
|
|
|
|
func PathLocker(db *AsinkDB) {
|
|
|
|
var event *asink.Event
|
|
|
|
var request *pathMapRequest
|
|
|
|
var v *pathMapValue
|
|
|
|
var ok bool
|
|
|
|
var c chan *asink.Event
|
|
|
|
m := make(map[string]*pathMapValue)
|
|
|
|
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case event = <-pathUnlockerChan:
|
|
|
|
if v, ok = m[event.Path]; ok != false {
|
2013-08-14 07:26:52 -04:00
|
|
|
//only update status in data structures if the event hasn't been discarded
|
2013-08-23 00:09:03 -04:00
|
|
|
if event.LocalStatus&asink.DISCARDED == 0 {
|
2013-08-14 07:26:52 -04:00
|
|
|
if v.latestEvent == nil || !v.latestEvent.IsSameEvent(event) {
|
|
|
|
err := db.DatabaseAddEvent(event)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
//TODO batch database writes instead of doing one at a time
|
2013-08-13 21:51:19 -04:00
|
|
|
}
|
2013-08-14 07:26:52 -04:00
|
|
|
v.latestEvent = event
|
2013-08-13 21:51:19 -04:00
|
|
|
}
|
|
|
|
if len(v.localWaiters) > 0 {
|
|
|
|
c = v.localWaiters[0]
|
|
|
|
v.localWaiters = v.localWaiters[1:]
|
2013-08-14 07:26:52 -04:00
|
|
|
c <- v.latestEvent
|
2013-08-13 21:51:19 -04:00
|
|
|
} else if len(v.remoteWaiters) > 0 {
|
|
|
|
c = v.remoteWaiters[0]
|
|
|
|
v.remoteWaiters = v.remoteWaiters[1:]
|
2013-08-14 07:26:52 -04:00
|
|
|
c <- v.latestEvent
|
2013-08-13 21:51:19 -04:00
|
|
|
} else {
|
|
|
|
v.locked = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case request = <-pathLockerChan:
|
|
|
|
v, ok = m[request.path]
|
|
|
|
//allocate pathMapValue object if it doesn't exist
|
|
|
|
if !ok {
|
|
|
|
v = new(pathMapValue)
|
|
|
|
m[request.path] = v
|
|
|
|
}
|
|
|
|
if v.locked {
|
|
|
|
if request.local {
|
|
|
|
v.localWaiters = append(v.localWaiters, request.responseChan)
|
|
|
|
} else {
|
|
|
|
v.remoteWaiters = append(v.remoteWaiters, request.responseChan)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
v.locked = true
|
|
|
|
event, err := db.DatabaseLatestEventForPath(request.path)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
request.responseChan <- event
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//locks the path to ensure nothing else inside asink is mucking with that file.
|
|
|
|
//'local' determines the precedence of the lock - all local lock requesters will
|
|
|
|
//be served before any remote requesters.
|
|
|
|
//The previous event for this path is returned, nil is returned if no previous event exists
|
|
|
|
func LockPath(path string, local bool) (currentEvent *asink.Event) {
|
|
|
|
c := make(chan *asink.Event)
|
|
|
|
pathLockerChan <- &pathMapRequest{path, local, c}
|
|
|
|
return <-c
|
|
|
|
}
|
|
|
|
|
|
|
|
//unlocks the path, storing the updated event back to the database
|
|
|
|
func UnlockPath(event *asink.Event) {
|
|
|
|
pathUnlockerChan <- event
|
|
|
|
}
|