working on tasks...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2020-11-06 05:17:39 +03:00
parent d5771925c4
commit 9831b863ea
2 changed files with 341 additions and 181 deletions

View File

@ -596,82 +596,6 @@ module.Introspection = ImageGridFeatures.Feature({
//---------------------------------------------------------------------
// System life-cycle...
// XXX docs...
//
// action: ['Path/To/Action',
// abortablePromise('abort-id', function(abort, ...args){
//
// abort.cleanup(function(reason, res){
// if(reason == 'done'){
// // ...
// }
// if(reason == 'aborted'){
// // ...
// }
// })
//
// return new Promise(function(resolve, reject){
// // ...
//
// if(abort.isAborted){
// // handle abort...
// }
//
// // ...
// }) })],
//
//
// NOTE: if the returned promise is not resolved .cleanup(..) will not
// be called even if the appropriate .abort(..) as called...
var abortablePromise =
module.abortablePromise =
function(title, func){
return Object.assign(
function(...args){
var that = this
var abort = object.mixinFlat(
this.abortable(title, function(){
that.clearAbortable(title, abort)
return abort }),
{
get isAborted(){
return !((that.__abortable || new Map())
.get(title) || new Set())
.has(this) },
__cleanup: null,
cleanup: function(func){
var args = [...arguments]
var reason = this.isAborted ?
'aborted'
: 'done'
typeof(func) == 'function' ?
// register handler...
(this.__cleanup = this.__cleanup
|| new Set()).add(func)
// call cleanup handlers...
: [...(this.__cleanup || [])]
.forEach(function(f){
f.call(that, reason, ...args) })
return this },
})
return func.call(this, abort, ...args)
.then(function(res){
abort.cleanup(res)()
return res })
.catch(function(res){
abort.cleanup(res)() }) },
{
toString: function(){
return `core.abortablePromise('${ title }', \n${ func.toString() })` },
}) }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// XXX should his have state???
// ...if so, should this be a toggler???
var LifeCycleActions = actions.Actions({
@ -1057,96 +981,6 @@ var LifeCycleActions = actions.Actions({
.start() }],
// Abortable...
//
// Format:
// Map({
// title: Set([ func, ... ]),
// ...
// })
//
__abortable: null,
abortable: ['- System/Register abort handler',
doc`Register abortable action
.abortable(title, func)
-> func
`,
function(title, callback){
// reserved titles...
if(title == 'all' || title == '*'){
throw new Error('.abortable(..): can not set reserved title: "'+ title +'".') }
var abortable = this.__abortable = this.__abortable || new Map()
var set = abortable.get(title) || new Set()
abortable.set(title, set)
set.add(callback)
return actions.ASIS(callback) }],
clearAbortable: ['- System/Clear abort handler(s)',
doc`Clear abort handler(s)
Clear abort handler...
.clearAbortable(title, callback)
Clear all abort handlers for title...
.clearAbortable(title)
.clearAbortable(title, 'all')
Clear all abort handlers...
.clearAbortable('all')
`,
function(title, callback){
callback = callback || '*'
// clear all...
if(title == '*' || title == 'all'){
delete this.__abortable }
var set = ((this.__abortable || new Map()).get(title) || new Set())
// clear specific handler...
callback != '*'
&& callback != 'all'
&& set.delete(callback)
// cleanup / clear title...
;(set.size == 0
|| callback == '*'
|| callback == 'all')
&& (this.__abortable || new Set()).delete(title)
// cleanup...
this.__abortable
&& this.__abortable.size == 0
&& (delete this.__abortable) }],
abort: ['- System/Run abort handler(s)',
doc`
.abort(title)
.abort([title, .. ])
.abort('all')
`,
function(title){
title = title == '*' || title == 'all' ?
[...(this.__abortable || new Map()).keys()]
: title instanceof Array ?
title
: [title]
this.__abortable
&& title
.forEach(function(title){
[...(this.__abortable || new Map()).get(title) || []]
.forEach(function(f){ f() })
this.__abortable
&& this.__abortable.delete(title) }.bind(this))
// cleanup...
this.__abortable
&& this.__abortable.size == 0
&& (delete this.__abortable) }],
})
var LifeCycle =
@ -2510,17 +2344,350 @@ module.Workspace = ImageGridFeatures.Feature({
//---------------------------------------------------------------------
// Tasks...
// XXX we need:
// - serialize/restore
//
// XXX should this be a separate module???
//var tasks = require('lib/tasks')
var tasks = require('lib/tasks')
var task =
module.tast =
function(func){
func.__task__ = true
return func }
//
// action: ['Path/To/Action',
// abortablePromise('abort-id', function(abort, ...args){
//
// abort.cleanup(function(reason, res){
// if(reason == 'done'){
// // ...
// }
// if(reason == 'aborted'){
// // ...
// }
// })
//
// return new Promise(function(resolve, reject){
// // ...
//
// if(abort.isAborted){
// // handle abort...
// }
//
// // ...
// }) })],
//
//
// NOTE: if the returned promise is not resolved .cleanup(..) will not
// be called even if the appropriate .abort(..) as called...
//
// XXX is the abort api an overkill??
// ...can this be solved/integrated with tasks???
// essentially this creates an abortable task, for full blown tasks
// it would be nice to also be able to:
// - pause/resume (abort is done)
// - serialize/restore
// - list/sort/prioritize
// - remote (peer/worker)
// XXX docs...
var abortablePromise =
module.abortablePromise =
function(title, func){
return Object.assign(
task(function(...args){
var that = this
var abort = object.mixinFlat(
this.abortable(title, function(){
that.clearAbortable(title, abort)
return abort }),
{
get isAborted(){
return !((that.__abortable || new Map())
.get(title) || new Set())
.has(this) },
__cleanup: null,
cleanup: function(func){
var args = [...arguments]
var reason = this.isAborted ?
'aborted'
: 'done'
typeof(func) == 'function' ?
// register handler...
(this.__cleanup = this.__cleanup
|| new Set()).add(func)
// call cleanup handlers...
: [...(this.__cleanup || [])]
.forEach(function(f){
f.call(that, reason, ...args) })
return this },
})
return func.call(this, abort, ...args)
.then(function(res){
abort.cleanup(res)()
return res })
.catch(function(res){
abort.cleanup(res)() }) }),
{
toString: function(){
return `core.abortablePromise('${ title }', \n${ func.toString() })` },
}) }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
var makeTaskAction =
function(name, state, callback){
return function(title, task='all'){
title = title == '*' || title == 'all' ?
[...(this.__running_tasks || new Map()).keys()]
: title instanceof Array ?
title
: [title]
this.__running_tasks
&& title
.forEach(function(title){
[...(this.__running_tasks || new Map()).get(title) || []]
.forEach(function(t){
;(task == 'all'
|| task == '*'
|| task === t
|| (task instanceof Array
&& task.includes(t)))
// call handler...
&& t[name]
&& t[name]() !== false
// state...
&& state
&& (t.state = state) })
callback
&& callback.call(this, title, task) }.bind(this))
// cleanup...
this.__running_tasks
&& this.__running_tasks.size == 0
&& (delete this.__running_tasks) } }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// XXX see if a protocol can be practical here to:
// - serialize/restore jobs
// - ...
var TaskActions = actions.Actions({
config: {
},
// Format:
// Map({
// title: Set([
// {
// state: ...,
// abort: func,
// pause: func,
// resume: func,
// ...
// },
// ...
// ]),
// ...
// })
//
__running_tasks: null,
// XXX should this .resume(..)???
// XXX might be a good idea to make this compatible with tasks.Queue(..)
// ...and return a queue if not task is given??
Task: ['- System/',
function(title, task){
// reserved titles...
if(title == 'all' || title == '*'){
throw new Error('.abortable(..): can not set reserved title: "'+ title +'".') }
var tasks = this.__running_tasks = this.__running_tasks || new Map()
var set = tasks.get(title) || new Set()
tasks.set(title, set)
set.add(task)
task.state = 'running'
return task }],
getTasks: ['- System/',
function(title='all', state='all'){
var normArg = function(arg){
return !arg ?
'all'
: arg == 'all' || arg == '*' ?
arg
: arg instanceof Array ?
new Set(arg)
: new Set([arg]) }
title = normArg(title)
state = normArg(state)
return this.__running_tasks ?
[...this.__running_tasks.entries()]
.reduce(function(res, [t, set]){
if(title != 'all'
&& title != '*'
&& !title.has(t)){
return res }
var l = [...set]
.filter(function(t){
return state == 'all'
|| state == '*'
|| state.has(t.state) })
l.length > 0
&& (res[t] = l)
return res }, {})
: {} }],
// XXX should this abort the cleared tasks???
// ...if not would be logical to rename this to ._clearTask(..)
clearTask: ['- System/',
function(title, task='all'){
// clear all...
if(title == '*' || title == 'all'){
delete this.__running_tasks }
var set = ((this.__running_tasks || new Map()).get(title) || new Set())
// clear specific handler...
task != '*'
&& task != 'all'
&& set.delete(task)
// cleanup / clear title...
;(set.size == 0
|| task == '*'
|| task == 'all')
&& (this.__running_tasks || new Set()).delete(title)
// cleanup...
this.__running_tasks
&& this.__running_tasks.size == 0
&& (delete this.__running_tasks) }],
// XXX cache???
get tasks(){
return this.actions.filter(function(action){
return !!this.getActionAttr(action, '__task__') }.bind(this)) },
get running(){
return this.getTasks('all', 'running') },
get paused(){
return this.getTasks('all', 'paused') },
pause: ['- System/',
makeTaskAction('pause', 'paused')],
resume: ['- System/',
makeTaskAction('resume', 'running')],
_abort: ['- System/',
makeTaskAction('abort', null,
function(title, task='all'){
this.__running_tasks
&& (task == 'all'
|| task == '*'
|| this.__running_tasks.get(title).size == 0)
&& this.__running_tasks.delete(title) })],
// XXX LEGACY -- remove after migrating sharp.js and abortablePromise(..)
//
// Abortable...
//
// Format:
// Map({
// title: Set([ func, ... ]),
// ...
// })
//
// XXX rename...
// XXX extend to support other task operations...
__abortable: null,
abortable: ['- System/Register abort handler',
doc`Register abortable action
.abortable(title, func)
-> func
`,
function(title, callback){
// reserved titles...
if(title == 'all' || title == '*'){
throw new Error('.abortable(..): can not set reserved title: "'+ title +'".') }
var abortable = this.__abortable = this.__abortable || new Map()
var set = abortable.get(title) || new Set()
abortable.set(title, set)
set.add(callback)
return actions.ASIS(callback) }],
clearAbortable: ['- System/Clear abort handler(s)',
doc`Clear abort handler(s)
Clear abort handler...
.clearAbortable(title, callback)
Clear all abort handlers for title...
.clearAbortable(title)
.clearAbortable(title, 'all')
Clear all abort handlers...
.clearAbortable('all')
`,
function(title, callback){
callback = callback || '*'
// clear all...
if(title == '*' || title == 'all'){
delete this.__abortable }
var set = ((this.__abortable || new Map()).get(title) || new Set())
// clear specific handler...
callback != '*'
&& callback != 'all'
&& set.delete(callback)
// cleanup / clear title...
;(set.size == 0
|| callback == '*'
|| callback == 'all')
&& (this.__abortable || new Set()).delete(title)
// cleanup...
this.__abortable
&& this.__abortable.size == 0
&& (delete this.__abortable) }],
abort: ['- System/Abort task(s)',
doc`
.abort(title)
.abort([title, .. ])
.abort('all')
`,
function(title, task='all'){
title = title == '*' || title == 'all' ?
[...(this.__abortable || new Map()).keys()]
: title instanceof Array ?
title
: [title]
this.__abortable
&& title
.forEach(function(title){
[...(this.__abortable || new Map()).get(title) || []]
.forEach(function(f){ f() })
this.__abortable
&& this.__abortable.delete(title) }.bind(this))
// cleanup...
this.__abortable
&& this.__abortable.size == 0
&& (delete this.__abortable) }],
/* XXX LEGACY...
get jobs(){
return this.__jobs },
@ -2536,8 +2703,7 @@ var TaskActions = actions.Actions({
return job
}],
// XXX stop
//*/
})
@ -2552,14 +2718,6 @@ module.Tasks = ImageGridFeatures.Feature({
actions: TaskActions,
handlers: [
['start',
function(){
// XXX prepare for recovery and recover...
}],
['stop',
function(){
// XXX stop tasks and prepare for recovery...
}],
],
})

View File

@ -658,7 +658,7 @@ var SharpActions = actions.Actions({
(logger || this.logger)
: false
logger = logger
&& logger.push('Caching image metadata', {onclose: abort})
&& logger.push('Caching image metadata', {onclose: abort, quiet: true})
logger && logger.emit('queued', images)
/*/ XXX set this to tmp for .location.load =='loadImages'
@ -766,6 +766,8 @@ var SharpActions = actions.Actions({
// shorthands...
// XXX do we need these???
abortMakeResizedImage: ['- Sharp/',
'abort: "makeResizedImage"'],
abortMakePreviews: ['- Sharp/',
'abort: "makePreviews"'],
abortCacheMetadata: ['- Sharp/',