|
|
|
/*
|
|
|
|
* Minio Cloud Storage, (C) 2015 Minio, Inc.
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package tasker
|
|
|
|
|
|
|
|
import (
|
|
|
|
"container/list"
|
|
|
|
"sync"
|
|
|
|
)
|
|
|
|
|
|
|
|
// TaskCtl (Task Controller) is a framework to create and manage
|
|
|
|
// tasks.
|
|
|
|
type TaskCtl struct {
|
|
|
|
mutex *sync.Mutex // Lock
|
|
|
|
// List of tasks managed by this task controller.
|
|
|
|
tasks *list.List
|
|
|
|
}
|
|
|
|
|
|
|
|
// New creates a new TaskCtl to create and control a collection of tasks.
|
|
|
|
// Single application can create multiple task controllers to manage different set of tasks separately.
|
|
|
|
func New(name string) *TaskCtl {
|
|
|
|
return &TaskCtl{
|
|
|
|
mutex: &sync.Mutex{},
|
|
|
|
tasks: list.New(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewTask creates a new task structure and returns a handle to it. Only the task controller
|
|
|
|
// has access to the task structure. The caller routine only receives a handle to its task structure.
|
|
|
|
// Task handle is like a reference to task self. Caller is expected to listen for commands from
|
|
|
|
// the task controller and comply with it co-operatively.
|
|
|
|
func (tc *TaskCtl) NewTask(name string) Handle {
|
|
|
|
tc.mutex.Lock()
|
|
|
|
defer tc.mutex.Unlock()
|
|
|
|
|
|
|
|
// Create a new task.
|
|
|
|
tsk := newTask(name)
|
|
|
|
|
|
|
|
// Register this task in the TaskCtl's tasklist and save the reference.
|
|
|
|
tsk.this = tc.tasks.PushBack(tsk)
|
|
|
|
|
|
|
|
// Free task from the tasklist upon close call.
|
|
|
|
go func() {
|
|
|
|
// Release the tasks resources upon return of this function.
|
|
|
|
defer tsk.close()
|
|
|
|
|
|
|
|
// Will be notified here upon task's end of life.
|
|
|
|
this := <-tsk.closeCh
|
|
|
|
|
|
|
|
tc.mutex.Lock()
|
|
|
|
defer tc.mutex.Unlock()
|
|
|
|
|
|
|
|
// Release the task structure from the task list.
|
|
|
|
tc.tasks.Remove(this)
|
|
|
|
}()
|
|
|
|
|
|
|
|
// Return a handle to this task.
|
|
|
|
return tsk.getHandle()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Shutdown ends all tasks, including the suspended ones.
|
|
|
|
func (tc *TaskCtl) Shutdown() {
|
|
|
|
tc.mutex.Lock()
|
|
|
|
defer tc.mutex.Unlock()
|
|
|
|
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
|
|
|
|
// End all tasks.
|
|
|
|
for e := tc.tasks.Front(); e != nil; e = e.Next() {
|
|
|
|
wg.Add(1)
|
|
|
|
thisTask := e.Value.(task) // Make a local copy for go routine.
|
|
|
|
// End tasks in background. Flow of events from here is as follows: thisTask.handle.Close() -> tc.NewTask() -> this.task.close().
|
|
|
|
go func() {
|
|
|
|
thisTask.getHandle().Close()
|
|
|
|
wg.Done()
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
|
|
|
wg.Wait() // Wait for all tasks to end gracefully.
|
|
|
|
|
|
|
|
// Reset the task pool.
|
|
|
|
tc.tasks = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Suspend puts all tasks to sleep.
|
|
|
|
func (tc *TaskCtl) Suspend() bool {
|
|
|
|
tc.mutex.Lock()
|
|
|
|
defer tc.mutex.Unlock()
|
|
|
|
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
|
|
|
|
// If any one of the task fails to suspend, this flag will set to false.
|
|
|
|
statusAll := make([]status, tc.tasks.Len())
|
|
|
|
|
|
|
|
// Suspend all tasks.
|
|
|
|
i := 0
|
|
|
|
for e := tc.tasks.Front(); e != nil; e = e.Next() {
|
|
|
|
wg.Add(1)
|
|
|
|
locTask := e.Value.(task) // Make a local copy for go routine.
|
|
|
|
locI := i // local i
|
|
|
|
// Suspend a task in background.
|
|
|
|
go func(locI int) {
|
|
|
|
defer wg.Done()
|
|
|
|
statusAll[locI] = locTask.command(CmdSignalSuspend)
|
|
|
|
}(locI)
|
|
|
|
i++
|
|
|
|
}
|
|
|
|
|
|
|
|
wg.Wait() // Wait for all tasks to suspend gracefully.
|
|
|
|
|
|
|
|
for _, st := range statusAll {
|
|
|
|
if st.code != statusDone {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// Resume wakes up all suspended task from sleep.
|
|
|
|
func (tc *TaskCtl) Resume() bool {
|
|
|
|
tc.mutex.Lock()
|
|
|
|
defer tc.mutex.Unlock()
|
|
|
|
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
|
|
|
|
// If any one of the task fails to suspend, this flag will set to false.
|
|
|
|
statusAll := make([]status, tc.tasks.Len())
|
|
|
|
|
|
|
|
i := 0
|
|
|
|
// Resume all suspended tasks.
|
|
|
|
for e := tc.tasks.Front(); e != nil; e = e.Next() {
|
|
|
|
wg.Add(1)
|
|
|
|
locTask := e.Value.(task) // Make a local copy for go routine.
|
|
|
|
locI := i // local i
|
|
|
|
// Resume a task in background.
|
|
|
|
go func(locI int) {
|
|
|
|
defer wg.Done()
|
|
|
|
statusAll[locI] = locTask.command(CmdSignalResume)
|
|
|
|
}(locI)
|
|
|
|
i++
|
|
|
|
}
|
|
|
|
wg.Wait() // Wait for all tasks to resume.
|
|
|
|
|
|
|
|
for _, st := range statusAll {
|
|
|
|
if st.code != statusDone {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
|
|
|
|
}
|