etcd/mod/lock/acquire_handler.go
2013-11-27 16:59:05 -07:00

76 lines
1.8 KiB
Go

package lock
import (
"net/http"
"path"
"strconv"
"time"
"github.com/gorilla/mux"
)
// acquireHandler attempts to acquire a lock on the given key.
func (h *handler) acquireHandler(w http.ResponseWriter, req *http.Request) {
h.client.SyncCluster()
vars := mux.Vars(req)
keypath := path.Join(prefix, vars["key"])
ttl, err := strconv.Atoi(req.FormValue("ttl"))
if err != nil {
http.Error(w, "invalid ttl: " + err.Error(), http.StatusInternalServerError)
return
}
// Create an incrementing id for the lock.
resp, err := h.client.AddChild(keypath, "-", uint64(ttl))
if err != nil {
http.Error(w, "add lock index error: " + err.Error(), http.StatusInternalServerError)
return
}
// Keep updating TTL to make sure lock request is not expired before acquisition.
stopChan := make(chan bool)
defer close(stopChan)
go func(k string) {
stopped := false
for {
select {
case <-time.After(time.Duration(ttl / 2) * time.Second):
case <-stopChan:
stopped = true
}
h.client.Update(k, "-", uint64(ttl))
if stopped {
break
}
}
}(resp.Key)
// Extract the lock index.
index, _ := strconv.Atoi(path.Base(resp.Key))
for {
// Read all indices.
resp, err = h.client.GetAll(keypath, true)
if err != nil {
http.Error(w, "lock children lookup error: " + err.Error(), http.StatusInternalServerError)
return
}
indices := extractResponseIndices(resp)
waitIndex := resp.ModifiedIndex
prevIndex := findPrevIndex(indices, index)
// If there is no previous index then we have the lock.
if prevIndex == 0 {
break
}
// Otherwise watch previous index until it's gone.
_, err = h.client.Watch(path.Join(keypath, strconv.Itoa(prevIndex)), waitIndex, nil, nil)
if err != nil {
http.Error(w, "lock watch error: " + err.Error(), http.StatusInternalServerError)
return
}
}
}