Skip to content
Snippets Groups Projects
Commit d09e781e authored by Christian Pointner's avatar Christian Pointner
Browse files

session cleanup after cancel (still needs testing

parent 5619eabc
No related branches found
No related tags found
No related merge requests found
...@@ -27,9 +27,11 @@ package importer ...@@ -27,9 +27,11 @@ package importer
import ( import (
"io/ioutil" "io/ioutil"
"log" "log"
"net/http"
"os" "os"
"runtime" "runtime"
"sync" "sync"
"time"
) )
type ProgressCB func(step int, stepName string, current, total float64, userdata interface{}) bool type ProgressCB func(step int, stepName string, current, total float64, userdata interface{}) bool
...@@ -63,6 +65,7 @@ type ImportContext struct { ...@@ -63,6 +65,7 @@ type ImportContext struct {
ProgressCallBack ProgressCB ProgressCallBack ProgressCB
ProgressCallBackData interface{} ProgressCallBackData interface{}
Cancel <-chan bool Cancel <-chan bool
doneChan chan<- Result
// TODO: add source config // TODO: add source config
} }
...@@ -125,17 +128,30 @@ type Importer struct { ...@@ -125,17 +128,30 @@ type Importer struct {
} }
type workerRequest struct { type workerRequest struct {
ctx *ImportContext ctx *ImportContext
enquedChan chan<- bool enqueuedChan chan<- bool
} }
func (im *Importer) runWorker(idx int) { func (im *Importer) runWorker(idx int) {
defer im.dbgLog.Printf("importer: worker %d has stopped", idx) defer im.dbgLog.Printf("importer: worker %d has stopped", idx)
im.dbgLog.Printf("importer: worker %d is running", idx) im.dbgLog.Printf("importer: worker %d is running", idx)
select { for {
case req := <-im.workerChan: select {
im.dbgLog.Printf("importer: worker %d got request: %+v", idx, req.ctx) case req := <-im.workerChan:
if req.ctx.isCanceled() {
im.dbgLog.Printf("importer: worker %d got canceled request")
continue
}
req.enqueuedChan <- true
im.dbgLog.Printf("importer: worker %d got request: %+v", idx, req.ctx)
// TODO: check for potential race between cancel and enqueue
// TODO: actually do import
time.Sleep(60 * time.Second)
req.ctx.doneChan <- Result{ResponseCode: http.StatusOK, ErrorString: "import success"}
}
} }
} }
......
...@@ -33,8 +33,8 @@ import ( ...@@ -33,8 +33,8 @@ import (
type SessionState uint8 type SessionState uint8
const ( const (
StateNew SessionState = iota StateNew SessionState = iota
StatePending // TODO: use this when session backlog is implemented StatePending
StateRunning StateRunning
StateCanceled StateCanceled
StateDone StateDone
...@@ -111,7 +111,7 @@ type session struct { ...@@ -111,7 +111,7 @@ type session struct {
quit chan bool quit chan bool
timer *time.Timer timer *time.Timer
workerChan chan<- workerRequest workerChan chan<- workerRequest
enquedIntChan chan bool enqueuedIntChan chan bool
cancelIntChan chan bool cancelIntChan chan bool
progressRateLimit time.Duration progressRateLimit time.Duration
progressIntChan chan ProgressData progressIntChan chan ProgressData
...@@ -136,7 +136,8 @@ func (s *session) run(timeout time.Duration) { ...@@ -136,7 +136,8 @@ func (s *session) run(timeout time.Duration) {
s.ctx.ProgressCallBack = sessionProgressCallback s.ctx.ProgressCallBack = sessionProgressCallback
s.ctx.ProgressCallBackData = (chan<- ProgressData)(s.progressIntChan) s.ctx.ProgressCallBackData = (chan<- ProgressData)(s.progressIntChan)
s.ctx.Cancel = s.cancelIntChan s.ctx.Cancel = s.cancelIntChan
s.workerChan <- workerRequest{&(s.ctx), s.enquedIntChan} s.ctx.doneChan = s.doneIntChan
s.workerChan <- workerRequest{&(s.ctx), s.enqueuedIntChan}
s.state = StatePending s.state = StatePending
if timeout <= 0 || timeout > 3*time.Hour { if timeout <= 0 || timeout > 3*time.Hour {
s.ctx.infoLog.Printf("importer: requested session timeout (%v) is invalid or too high - setting it to 3h", timeout) s.ctx.infoLog.Printf("importer: requested session timeout (%v) is invalid or too high - setting it to 3h", timeout)
...@@ -146,11 +147,20 @@ func (s *session) run(timeout time.Duration) { ...@@ -146,11 +147,20 @@ func (s *session) run(timeout time.Duration) {
return return
} }
func (s *session) cancel() { func (s *session) cancel(reason string) {
s.ctx.dbgLog.Println("importer: canceling running import") if s.state == StatePending || s.state == StateRunning {
select { s.ctx.dbgLog.Printf("importer: canceling running import (%s)", reason)
case s.cancelIntChan <- true: select {
default: // session got canceled already?? case s.cancelIntChan <- true:
default: // session got canceled already??
}
}
if s.state == StatePending {
r := &Result{ResponseCode: http.StatusNoContent, ErrorString: reason}
s.callDoneHandler(r)
if s.removeFunc != nil {
s.removeFunc()
}
} }
s.state = StateCanceled s.state = StateCanceled
} }
...@@ -217,32 +227,20 @@ func (s *session) dispatchRequests() { ...@@ -217,32 +227,20 @@ func (s *session) dispatchRequests() {
for { for {
select { select {
case <-s.quit: case <-s.quit:
if s.state == StatePending || s.state == StateRunning { s.cancel("session canceled due to global shutdown")
s.cancel()
}
return return
case <-s.timer.C: case <-s.timer.C:
if s.state == StatePending || s.state == StateRunning { s.cancel("session timed out")
s.cancel()
}
s.state = StateTimeout
r := &Result{ResponseCode: http.StatusInternalServerError, ErrorString: "session timed out"}
s.callDoneHandler(r)
if s.removeFunc != nil {
s.removeFunc()
}
case t := <-s.runChan: case t := <-s.runChan:
if s.state == StateNew { if s.state == StateNew {
s.run(t) s.run(t)
} }
case <-s.enquedIntChan: case <-s.enqueuedIntChan:
if s.state == StatePending { if s.state == StatePending {
s.state = StateRunning s.state = StateRunning
} }
case <-s.cancelChan: case <-s.cancelChan:
if s.state == StatePending || s.state == StateRunning { s.cancel("session canceled by user")
s.cancel()
}
case req := <-s.addProgressChan: case req := <-s.addProgressChan:
req.response <- s.addProgressHandler(req.userdata, req.callback) req.response <- s.addProgressHandler(req.userdata, req.callback)
case req := <-s.addDoneChan: case req := <-s.addDoneChan:
...@@ -399,7 +397,7 @@ func newSession(ctx *ImportContext, workerChan chan<- workerRequest, removeFunc ...@@ -399,7 +397,7 @@ func newSession(ctx *ImportContext, workerChan chan<- workerRequest, removeFunc
s.done = make(chan bool) s.done = make(chan bool)
s.timer = time.NewTimer(60 * time.Second) s.timer = time.NewTimer(60 * time.Second)
s.workerChan = workerChan s.workerChan = workerChan
s.enquedIntChan = make(chan bool) s.enqueuedIntChan = make(chan bool)
s.cancelIntChan = make(chan bool, 1) s.cancelIntChan = make(chan bool, 1)
s.progressRateLimit = 100 * time.Millisecond // TODO: hardcoded value s.progressRateLimit = 100 * time.Millisecond // TODO: hardcoded value
s.progressIntChan = make(chan ProgressData, 10) s.progressIntChan = make(chan ProgressData, 10)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment