diff --git a/models/contest.js b/models/contest.js index e59eb01..8bcfda3 100644 --- a/models/contest.js +++ b/models/contest.js @@ -120,21 +120,20 @@ class Contest extends Model { let problems = await this.getProblems(); if (!problems.includes(judge_state.problem_id)) throw new ErrorMessage('当前比赛中无此题目。'); - await syzoj.utils.lock('Contest::newSubmission', 'create_player', judge_state.user_id); - - let player = await ContestPlayer.findInContest({ - contest_id: this.id, - user_id: judge_state.user_id - }); - - if (!player) { - player = await ContestPlayer.create({ + let player; + await syzoj.utils.lock(['Contest::newSubmission', 'create_player', judge_state.user_id], async () => { + player = await ContestPlayer.findInContest({ contest_id: this.id, user_id: judge_state.user_id }); - } - syzoj.utils.unlock('Contest::newSubmission', 'create_player', judge_state.user_id); + if (!player) { + player = await ContestPlayer.create({ + contest_id: this.id, + user_id: judge_state.user_id + }); + } + }); await player.updateScore(judge_state); await player.save(); diff --git a/models/contest_player.js b/models/contest_player.js index c64659a..09480f9 100644 --- a/models/contest_player.js +++ b/models/contest_player.js @@ -69,100 +69,98 @@ class ContestPlayer extends Model { } async updateScore(judge_state) { - await syzoj.utils.lock('ContestPlayer::updateScore', this.id); + await syzoj.utils.lock(['ContestPlayer::updateScore', this.id], async () => { + await this.loadRelationships(); + if (this.contest.type === 'ioi') { + if (!judge_state.pending) { + if (!this.score_details[judge_state.problem_id]) { + this.score_details[judge_state.problem_id] = { + score: judge_state.score, + judge_id: judge_state.id, + submissions: {} + }; + } - await this.loadRelationships(); - if (this.contest.type === 'ioi') { - if (!judge_state.pending) { - if (!this.score_details[judge_state.problem_id]) { - this.score_details[judge_state.problem_id] = { - score: judge_state.score, + this.score_details[judge_state.problem_id].submissions[judge_state.id] = { judge_id: judge_state.id, - submissions: {} + score: judge_state.score, + time: judge_state.submit_time }; - } - - this.score_details[judge_state.problem_id].submissions[judge_state.id] = { - judge_id: judge_state.id, - score: judge_state.score, - time: judge_state.submit_time - }; - let arr = Object.values(this.score_details[judge_state.problem_id].submissions); - arr.sort((a, b) => a.time - b.time); + let arr = Object.values(this.score_details[judge_state.problem_id].submissions); + arr.sort((a, b) => a.time - b.time); - let maxScoreSubmission = null; - for (let x of arr) { - if (!maxScoreSubmission || x.score >= maxScoreSubmission.score && maxScoreSubmission.score < 100) { - maxScoreSubmission = x; + let maxScoreSubmission = null; + for (let x of arr) { + if (!maxScoreSubmission || x.score >= maxScoreSubmission.score && maxScoreSubmission.score < 100) { + maxScoreSubmission = x; + } } - } - this.score_details[judge_state.problem_id].judge_id = maxScoreSubmission.judge_id; - this.score_details[judge_state.problem_id].score = maxScoreSubmission.score; - this.score_details[judge_state.problem_id].time = maxScoreSubmission.time; + this.score_details[judge_state.problem_id].judge_id = maxScoreSubmission.judge_id; + this.score_details[judge_state.problem_id].score = maxScoreSubmission.score; + this.score_details[judge_state.problem_id].time = maxScoreSubmission.time; + this.score = 0; + for (let x in this.score_details) { + this.score += this.score_details[x].score; + } + } + } else if (this.contest.type === 'noi') { + this.score_details[judge_state.problem_id] = { + score: judge_state.score, + judge_id: judge_state.id + }; this.score = 0; for (let x in this.score_details) { this.score += this.score_details[x].score; } - } - } else if (this.contest.type === 'noi') { - this.score_details[judge_state.problem_id] = { - score: judge_state.score, - judge_id: judge_state.id - }; - this.score = 0; - for (let x in this.score_details) { - this.score += this.score_details[x].score; - } - } else if (this.contest.type === 'acm') { - if (!judge_state.pending) { - if (!this.score_details[judge_state.problem_id]) { - this.score_details[judge_state.problem_id] = { - accepted: false, - unacceptedCount: 0, - acceptedTime: 0, - judge_id: 0, - submissions: {} - }; - } + } else if (this.contest.type === 'acm') { + if (!judge_state.pending) { + if (!this.score_details[judge_state.problem_id]) { + this.score_details[judge_state.problem_id] = { + accepted: false, + unacceptedCount: 0, + acceptedTime: 0, + judge_id: 0, + submissions: {} + }; + } - this.score_details[judge_state.problem_id].submissions[judge_state.id] = { - judge_id: judge_state.id, - accepted: judge_state.status === 'Accepted', - compiled: judge_state.status !== 'Compile Error', - time: judge_state.submit_time - }; + this.score_details[judge_state.problem_id].submissions[judge_state.id] = { + judge_id: judge_state.id, + accepted: judge_state.status === 'Accepted', + compiled: judge_state.status !== 'Compile Error', + time: judge_state.submit_time + }; - let arr = Object.values(this.score_details[judge_state.problem_id].submissions); - arr.sort((a, b) => a.time - b.time); - - this.score_details[judge_state.problem_id].unacceptedCount = 0; - this.score_details[judge_state.problem_id].judge_id = 0; - for (let x of arr) { - if (x.accepted) { - this.score_details[judge_state.problem_id].accepted = true; - this.score_details[judge_state.problem_id].acceptedTime = x.time; - this.score_details[judge_state.problem_id].judge_id = x.judge_id; - break; - } else if (x.compiled) { - this.score_details[judge_state.problem_id].unacceptedCount++; + let arr = Object.values(this.score_details[judge_state.problem_id].submissions); + arr.sort((a, b) => a.time - b.time); + + this.score_details[judge_state.problem_id].unacceptedCount = 0; + this.score_details[judge_state.problem_id].judge_id = 0; + for (let x of arr) { + if (x.accepted) { + this.score_details[judge_state.problem_id].accepted = true; + this.score_details[judge_state.problem_id].acceptedTime = x.time; + this.score_details[judge_state.problem_id].judge_id = x.judge_id; + break; + } else if (x.compiled) { + this.score_details[judge_state.problem_id].unacceptedCount++; + } } - } - if (!this.score_details[judge_state.problem_id].accepted) { - this.score_details[judge_state.problem_id].judge_id = arr[arr.length - 1].judge_id; - } + if (!this.score_details[judge_state.problem_id].accepted) { + this.score_details[judge_state.problem_id].judge_id = arr[arr.length - 1].judge_id; + } - this.score = 0; - for (let x in this.score_details) { - if (this.score_details[x].accepted) this.score++; + this.score = 0; + for (let x in this.score_details) { + if (this.score_details[x].accepted) this.score++; + } } } - } - - syzoj.utils.unlock('ContestPlayer::updateScore', this.id); + }); } getModel() { return model; } diff --git a/models/judge_state.js b/models/judge_state.js index 28f6cc1..546123d 100644 --- a/models/judge_state.js +++ b/models/judge_state.js @@ -191,43 +191,41 @@ class JudgeState extends Model { } async rejudge() { - await syzoj.utils.lock('JudgeState::rejudge', this.id); + await syzoj.utils.lock(['JudgeState::rejudge', this.id], async () => { + await this.loadRelationships(); - await this.loadRelationships(); - - let oldStatus = this.status; + let oldStatus = this.status; - this.status = 'Waiting'; - this.score = 0; - this.total_time = 0; - this.max_memory = 0; - this.pending = true; - this.result = { status: "Waiting", total_time: 0, max_memory: 0, score: 0, case_num: 0, compiler_output: "", pending: true }; - await this.save(); - - let WaitingJudge = syzoj.model('waiting_judge'); - let waiting_judge = await WaitingJudge.create({ - judge_id: this.id - }); + this.status = 'Waiting'; + this.score = 0; + this.total_time = 0; + this.max_memory = 0; + this.pending = true; + this.result = { status: "Waiting", total_time: 0, max_memory: 0, score: 0, case_num: 0, compiler_output: "", pending: true }; + await this.save(); - await waiting_judge.save(); + let WaitingJudge = syzoj.model('waiting_judge'); + let waiting_judge = await WaitingJudge.create({ + judge_id: this.id + }); - if (oldStatus === 'Accepted') { - await this.user.refreshSubmitInfo(); - await this.user.save(); - } + await waiting_judge.save(); - if (this.type === 0 || this.type === 2) { if (oldStatus === 'Accepted') { - this.problem.ac_num--; - await this.problem.save(); + await this.user.refreshSubmitInfo(); + await this.user.save(); } - } else if (this.type === 1) { - let contest = await Contest.fromID(this.type_info); - await contest.newSubmission(this); - } - syzoj.utils.unlock('JudgeState::rejudge', this.id); + if (this.type === 0 || this.type === 2) { + if (oldStatus === 'Accepted') { + this.problem.ac_num--; + await this.problem.save(); + } + } else if (this.type === 1) { + let contest = await Contest.fromID(this.type_info); + await contest.newSubmission(this); + } + }); } getModel() { return model; } diff --git a/models/user.js b/models/user.js index 537900d..8d9926a 100644 --- a/models/user.js +++ b/models/user.js @@ -88,34 +88,32 @@ class User extends Model { } async refreshSubmitInfo() { - await syzoj.utils.lock('User::refreshSubmitInfo', this.id); + await syzoj.utils.lock(['User::refreshSubmitInfo', this.id], async () => { + let JudgeState = syzoj.model('judge_state'); + let all = await JudgeState.model.findAll({ + attributes: ['problem_id'], + where: { + user_id: this.id, + status: 'Accepted', + type: { + $ne: 1 // Not a contest submission + } + } + }); - let JudgeState = syzoj.model('judge_state'); - let all = await JudgeState.model.findAll({ - attributes: ['problem_id'], - where: { + let s = new Set(); + all.forEach(x => s.add(parseInt(x.get('problem_id')))); + this.ac_num = s.size; + + let cnt = await JudgeState.count({ user_id: this.id, - status: 'Accepted', type: { $ne: 1 // Not a contest submission } - } - }); - - let s = new Set(); - all.forEach(x => s.add(parseInt(x.get('problem_id')))); - this.ac_num = s.size; + }); - let cnt = await JudgeState.count({ - user_id: this.id, - type: { - $ne: 1 // Not a contest submission - } + this.submit_num = cnt; }); - - this.submit_num = cnt; - - syzoj.utils.unlock('User::refreshSubmitInfo', this.id); } async getACProblems() { diff --git a/modules/api.js b/modules/api.js index 4bf4643..186f074 100644 --- a/modules/api.js +++ b/modules/api.js @@ -95,35 +95,36 @@ app.get('/api/waiting_judge', async (req, res) => { try { if (req.query.session_id !== syzoj.config.judge_token) return res.status(404).send({ err: 'Permission denied' }); - await syzoj.utils.lock('/api/waiting_judge'); + let judge_state; + await syzoj.utils.lock('/api/waiting_judge', async () => { + let waiting_judge = await WaitingJudge.findOne(); + if (!waiting_judge) { + return; + } + + judge_state = await waiting_judge.getJudgeState(); + await judge_state.loadRelationships(); + await judge_state.problem.loadRelationships(); + await waiting_judge.destroy(); + }); - let waiting_judge = await WaitingJudge.findOne(); - if (!waiting_judge) { - syzoj.utils.unlock('/api/waiting_judge'); - return res.send({ have_task: 0 }); + if (judge_state) { + res.send({ + have_task: 1, + judge_id: judge_state.id, + code: judge_state.code, + language: judge_state.language, + testdata: judge_state.problem.testdata ? judge_state.problem.testdata.md5 : '', + time_limit: judge_state.problem.time_limit, + memory_limit: judge_state.problem.memory_limit, + file_io: judge_state.problem.file_io, + file_io_input_name: judge_state.problem.file_io_input_name, + file_io_output_name: judge_state.problem.file_io_output_name + }); + } else { + res.send({ have_task: 0 }); } - - let judge_state = await waiting_judge.getJudgeState(); - await judge_state.loadRelationships(); - await judge_state.problem.loadRelationships(); - await waiting_judge.destroy(); - - syzoj.utils.unlock('/api/waiting_judge'); - - res.send({ - have_task: 1, - judge_id: judge_state.id, - code: judge_state.code, - language: judge_state.language, - testdata: judge_state.problem.testdata ? judge_state.problem.testdata.md5 : '', - time_limit: judge_state.problem.time_limit, - memory_limit: judge_state.problem.memory_limit, - file_io: judge_state.problem.file_io, - file_io_input_name: judge_state.problem.file_io_input_name, - file_io_output_name: judge_state.problem.file_io_output_name - }); } catch (e) { - syzoj.log(e); res.status(500).send(e); } }); diff --git a/utility.js b/utility.js index e0fde00..bf987ab 100644 --- a/utility.js +++ b/utility.js @@ -74,30 +74,6 @@ function highlightPygmentize(code, lang, cb) { renderer.config.highlight = highlightPygmentize; -let locks = {}; -let lock = function () { - let s = JSON.stringify(Array.from(arguments)); - return new Promise((resolve, reject) => { - if (!locks[s]) { - locks[s] = { - lock: new AsyncLock(), - done: null - }; - } - - locks[s].lock.acquire('', done => { - locks[s].done = done; - resolve(); - }); - }); -}; - -let unlock = function () { - let s = JSON.stringify(Array.from(arguments)); - locks[s].done(); - locks[s].done = null; -} - module.exports = { resolvePath(s) { let a = Array.from(arguments); @@ -346,6 +322,10 @@ module.exports = { isValidUsername(s) { return /^[a-zA-Z0-9\-\_]+$/.test(s); }, - lock: lock, - unlock: unlock + locks: [], + lock(key, cb) { + let s = JSON.stringify(key); + if (!this.locks[s]) this.locks[s] = new AsyncLock(); + return this.locks[s].acquire(s, cb); + } };