Skip to content

Commit c3c6e81

Browse files
committed
Refactor queue related logic.
Signed-off-by: Eric Wang <[email protected]>
1 parent a25cfa7 commit c3c6e81

File tree

3 files changed

+101
-96
lines changed

3 files changed

+101
-96
lines changed

lib/commands/submission.js

+38-51
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ var sprintf = require('sprintf-js').sprintf;
66
var h = require('../helper');
77
var chalk = require('../chalk');
88
var log = require('../log');
9-
var queue = require('../queue');
9+
var Queue = require('../queue');
1010
var core = require('../core');
1111
var session = require('../session');
1212

@@ -41,67 +41,62 @@ var cmd = {
4141
}
4242
};
4343

44-
function onTaskDone(e, msg, problem, cb) {
45-
// NOTE: msg color means different purpose:
46-
// - red: error
47-
// - green: accepted, fresh download
48-
// - yellow: not ac-ed, fresh download
49-
// - white: existed already, skip download
50-
log.printf('[%3d] %-60s %s', problem.id, problem.name,
51-
(e ? chalk.red('ERROR: ' + (e.msg || e)) : msg));
52-
if (cb) cb(e);
53-
}
54-
55-
function onTaskRun(argv, problem, cb) {
56-
var done = _.partial(onTaskDone, _, _, problem, cb);
44+
function doTask(problem, queue, cb) {
45+
var argv = queue.ctx.argv;
46+
47+
function onTaskDone(e, msg) {
48+
// NOTE: msg color means different purpose:
49+
// - red: error
50+
// - green: accepted, fresh download
51+
// - yellow: not ac-ed, fresh download
52+
// - white: existed already, skip download
53+
log.printf('[%3d] %-60s %s', problem.id, problem.name,
54+
(e ? chalk.red('ERROR: ' + (e.msg || e)) : msg));
55+
if (cb) cb(e);
56+
}
5757

5858
if (argv.extra) {
5959
// have to get problem details, e.g. problem description.
6060
core.getProblem(problem.id, function(e, problem) {
6161
if (e) return done(e);
62-
63-
exportSubmission(argv, problem, done);
62+
exportSubmission(problem, argv, onTaskDone);
6463
});
6564
} else {
66-
exportSubmission(argv, problem, done);
65+
exportSubmission(problem, argv, onTaskDone);
6766
}
6867
}
6968

70-
function exportSubmission(argv, problem, cb) {
69+
function exportSubmission(problem, argv, cb) {
7170
core.getSubmissions(problem, function(e, submissions) {
7271
if (e) return cb(e);
73-
if (submissions.length === 0) return cb('no submissions?');
72+
if (submissions.length === 0)
73+
return cb('No submissions?');
7474

7575
// get obj list contain required filetype
76-
var submissionInTargetType = _.filter(submissions, function(x) {
76+
submissions = _.filter(submissions, function(x) {
7777
return argv.lang === 'all' || argv.lang === x.lang;
7878
});
79-
if (submissionInTargetType.length === 0) {
80-
return cb('No previous submission in required language.');
81-
}
82-
var submission = _.find(submissionInTargetType, function(x) {
83-
return x.status_display === 'Accepted';
84-
});
85-
86-
var submissionState = submission === undefined ? 'notac' : 'ac';
79+
if (submissions.length === 0)
80+
return cb('No submissions in required language.');
8781

8882
// if no accepted, use the latest non-accepted one
89-
submission = submission || submissionInTargetType[0];
90-
91-
h.mkdir(argv.outdir);
83+
var submission = _.find(submissions, function(x) {
84+
return x.status_display === 'Accepted';
85+
}) || submissions[0];
86+
submission.ac = (submission.status_display === 'Accepted');
9287

93-
var filename = sprintf('%s/%d.%s.%s.%s%s',
88+
var f = sprintf('%s/%d.%s.%s.%s%s',
9489
argv.outdir,
9590
problem.id,
9691
problem.slug,
9792
submission.id,
98-
submissionState,
93+
submission.ac ? 'ac' : 'notac',
9994
h.langToExt(submission.lang));
10095

96+
h.mkdir(argv.outdir);
10197
// skip the existing cached submissions
102-
if (fs.existsSync(filename)) {
103-
return cb(null, chalk.underline(filename));
104-
}
98+
if (fs.existsSync(f))
99+
return cb(null, chalk.underline(f));
105100

106101
core.getSubmission(submission, function(e, submission) {
107102
if (e) return cb(e);
@@ -111,29 +106,22 @@ function exportSubmission(argv, problem, cb) {
111106
code: submission.code,
112107
tpl: argv.extra ? 'detailed' : 'codeonly'
113108
};
114-
fs.writeFileSync(filename, core.exportProblem(problem, opts));
115-
116-
if (submission.status_display === 'Accepted')
117-
cb(null, chalk.green.underline(filename));
118-
else
119-
cb(null, chalk.yellow.underline(filename));
109+
fs.writeFileSync(f, core.exportProblem(problem, opts));
110+
cb(null, submission.ac ? chalk.green.underline(f)
111+
: chalk.yellow.underline(f));
120112
});
121113
});
122114
}
123115

124116
cmd.handler = function(argv) {
125117
session.argv = argv;
126-
var doTask = _.partial(onTaskRun, argv, _, _);
118+
var q = new Queue(null, {argv: argv}, doTask);
127119

128120
if (argv.all) {
129121
core.getProblems(function(e, problems) {
130122
if (e) return log.fail(e);
131-
132-
problems = problems.filter(function(q) {
133-
return q.state === 'ac' || q.state === 'notac';
134-
});
135-
136-
queue.run(problems, doTask);
123+
problems = problems.filter(function(q) { return q.state === 'ac' || q.state === 'notac'; });
124+
q.addTasks(problems).run();
137125
});
138126
return;
139127
}
@@ -143,8 +131,7 @@ cmd.handler = function(argv) {
143131

144132
core.getProblem(argv.keyword, function(e, problem) {
145133
if (e) return log.fail(e);
146-
147-
queue.run([problem], doTask);
134+
q.addTask(problem).run();
148135
});
149136
};
150137

lib/plugins/leetcode.js

+27-21
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ var config = require('../config');
99
var h = require('../helper');
1010
var log = require('../log');
1111
var Plugin = require('../plugin');
12-
var queue = require('../queue');
12+
var Queue = require('../queue');
1313
var session = require('../session');
1414

1515
var plugin = new Plugin(10, 'leetcode', '',
@@ -52,20 +52,21 @@ function checkError(e, resp, expectedStatus) {
5252
plugin.getProblems = function(cb) {
5353
log.debug('running leetcode.getProblems');
5454
var problems = [];
55-
var doTask = function(category, taskDone) {
55+
var getCategory = function(category, queue, cb) {
5656
plugin.getCategoryProblems(category, function(e, _problems) {
5757
if (e) {
5858
log.debug(category + ': failed to getProblems: ' + e.msg);
5959
} else {
6060
log.debug(category + ': getProblems got ' + _problems.length + ' problems');
6161
problems = problems.concat(_problems);
6262
}
63-
return taskDone(e);
63+
return cb(e);
6464
});
6565
};
6666

6767
spin = h.spin('Downloading problems');
68-
queue.run(config.sys.categories, doTask, function(e) {
68+
var q = new Queue(config.sys.categories, {}, getCategory);
69+
q.run(null, function(e) {
6970
spin.stop();
7071
return cb(e, problems);
7172
});
@@ -144,7 +145,7 @@ plugin.getProblem = function(problem, cb) {
144145
operationName: 'getQuestionDetail'
145146
};
146147

147-
spin = h.spin('Downloading ' + problem.slug);
148+
var spin = h.spin('Downloading ' + problem.slug);
148149
request.post(opts, function(e, resp, body) {
149150
spin.stop();
150151
e = checkError(e, resp, 200);
@@ -181,7 +182,7 @@ function runCode(opts, problem, cb) {
181182
typed_code: h.getFileData(problem.file)
182183
});
183184

184-
spin = h.spin('Sending code to judge');
185+
var spin = h.spin('Sending code to judge');
185186
request(opts, function(e, resp, body) {
186187
spin.stop();
187188
e = checkError(e, resp, 200);
@@ -209,13 +210,12 @@ function runCode(opts, problem, cb) {
209210
});
210211
}
211212

212-
function verifyResult(opts, jobs, results, cb) {
213-
if (jobs.length === 0) return cb(null, results);
214-
213+
function verifyResult(task, queue, cb) {
214+
var opts = queue.ctx.opts;
215215
opts.method = 'GET';
216-
opts.url = config.sys.urls.verify.replace('$id', jobs[0].id);
216+
opts.url = config.sys.urls.verify.replace('$id', task.id);
217217

218-
spin = h.spin('Waiting for judge result');
218+
var spin = h.spin('Waiting for judge result');
219219
request(opts, function(e, resp, body) {
220220
spin.stop();
221221
e = checkError(e, resp, 200);
@@ -224,12 +224,12 @@ function verifyResult(opts, jobs, results, cb) {
224224
var result = JSON.parse(body);
225225
if (result.state === 'SUCCESS') {
226226
result = formatResult(result);
227-
_.extendOwn(result, jobs[0]);
228-
results.push(result);
229-
jobs.shift();
227+
_.extendOwn(result, task);
228+
queue.ctx.results.push(result);
229+
} else {
230+
queue.addTask(task);
230231
}
231-
232-
setImmediate(verifyResult, opts, jobs, results, cb);
232+
return cb();
233233
});
234234
}
235235

@@ -274,11 +274,14 @@ plugin.testProblem = function(problem, cb) {
274274
runCode(opts, problem, function(e, task) {
275275
if (e) return cb(e);
276276

277-
var jobs = [
277+
var tasks = [
278278
{type: 'Actual', id: task.interpret_id},
279279
{type: 'Expected', id: task.interpret_expected_id}
280280
];
281-
verifyResult(opts, jobs, [], cb);
281+
var q = new Queue(tasks, {opts: opts, results: []}, verifyResult);
282+
q.run(null, function(e, ctx) {
283+
return cb(e, ctx.results);
284+
});
282285
});
283286
};
284287

@@ -290,8 +293,11 @@ plugin.submitProblem = function(problem, cb) {
290293
runCode(opts, problem, function(e, task) {
291294
if (e) return cb(e);
292295

293-
var jobs = [{type: 'Actual', id: task.submission_id}];
294-
verifyResult(opts, jobs, [], cb);
296+
var tasks = [{type: 'Actual', id: task.submission_id}];
297+
var q = new Queue(tasks, {opts: opts, results: []}, verifyResult);
298+
q.run(null, function(e, ctx) {
299+
return cb(e, ctx.results);
300+
});
295301
});
296302
};
297303

@@ -376,7 +382,7 @@ plugin.getFavorites = function(cb) {
376382

377383
plugin.signin = function(user, cb) {
378384
log.debug('running leetcode.signin');
379-
spin = h.spin('Signing in leetcode.com');
385+
var spin = h.spin('Signing in leetcode.com');
380386
request(config.sys.urls.login, function(e, resp, body) {
381387
spin.stop();
382388
e = checkError(e, resp, 200);

lib/queue.js

+36-24
Original file line numberDiff line numberDiff line change
@@ -2,37 +2,49 @@ var _ = require('underscore');
22

33
var config = require('./config');
44

5-
var queue = {};
5+
function Queue(tasks, ctx, onTask) {
6+
this.tasks = _.clone(tasks) || [];
7+
this.ctx = ctx || {};
8+
this.onTask = onTask;
9+
this.error = null;
10+
}
11+
12+
Queue.prototype.addTask = function(task) {
13+
this.tasks.push(task);
14+
return this;
15+
};
16+
17+
Queue.prototype.addTasks = function(tasks) {
18+
this.tasks = this.tasks.concat(tasks);
19+
return this;
20+
};
21+
22+
Queue.prototype.run = function(concurrency, onDone) {
23+
this.concurrency = concurrency || config.network.concurrency || 1;
24+
this.onDone = onDone;
625

7-
function workerRun(ctx) {
26+
var self = this;
27+
for (var i = 0; i < this.concurrency; ++i) {
28+
setImmediate(function() { self.workerRun(); });
29+
}
30+
};
31+
32+
Queue.prototype.workerRun = function() {
833
// no more tasks, quit now
9-
if (ctx.tasks.length === 0) {
10-
if (--ctx.workers === 0 && ctx.cb)
11-
ctx.cb(ctx.error);
34+
if (this.tasks.length === 0) {
35+
if (--this.concurrency === 0 && this.onDone)
36+
this.onDone(this.error, this.ctx);
1237
return;
1338
}
1439

15-
var task = ctx.tasks.shift();
16-
ctx.doTask(task, function(e) {
17-
if (e) ctx.error = e;
40+
var task = this.tasks.shift();
41+
var self = this;
42+
this.onTask(task, self, function(e) {
43+
if (e) self.error = e;
1844

1945
// TODO: could retry failed task here.
20-
setImmediate(workerRun, ctx);
46+
setImmediate(function() { self.workerRun(); });
2147
});
22-
}
23-
24-
queue.run = function(tasks, doTask, cb) {
25-
var ctx = {
26-
tasks: _.clone(tasks),
27-
doTask: doTask,
28-
cb: cb,
29-
workers: config.network.concurrency || 1,
30-
error: null
31-
};
32-
33-
for (var i = 0; i < ctx.workers; ++i) {
34-
setImmediate(workerRun, ctx);
35-
}
3648
};
3749

38-
module.exports = queue;
50+
module.exports = Queue;

0 commit comments

Comments
 (0)