/*
* Copyright 2007 Xu, Chuan <xuchuan@gmail.com>
*
* This file is part of ZOJ.
*
* ZOJ is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either revision 3 of the License, or (at your option) any later revision.
*
* ZOJ is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with ZOJ. if not, see
* <http://www.gnu.org/licenses/>.
*/
/**
*
*/
package cn.edu.zju.acm.onlinejudge.judgeservice;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import cn.edu.zju.acm.onlinejudge.bean.Submission;
import cn.edu.zju.acm.onlinejudge.judgeservice.submissionfilter.SubmissionFilter;
import cn.edu.zju.acm.onlinejudge.persistence.PersistenceException;
class SubmissionQueue {
private Candidate head = new Candidate();
private Candidate tail = this.head;
private Map<SubmissionFilter, WeakReference<SubmissionQueueReaderImpl>> readerMap =
new HashMap<SubmissionFilter, WeakReference<SubmissionQueueReaderImpl>>();
private ReferenceQueue<SubmissionQueueReaderImpl> readerReferenceQueue =
new ReferenceQueue<SubmissionQueueReaderImpl>();
public synchronized void push(Submission submission, int priority) {
Candidate candidate = new Candidate();
candidate.prev = this.tail;
this.tail.submission = submission;
this.tail.priority = priority;
this.tail.next = candidate;
this.tail = candidate;
this.notifyAll();
}
public SubmissionQueueReader getReader(SubmissionFilter submissionFilter) {
synchronized (this.readerMap) {
this.clearReferences();
WeakReference<SubmissionQueueReaderImpl> readerReference = this.readerMap.get(submissionFilter);
SubmissionQueueReaderImpl reader = null;
if (readerReference != null) {
reader = readerReference.get();
}
if (reader == null) {
reader = this.new SubmissionQueueReaderImpl(submissionFilter);
readerReference =
new SubmissionQueue.ReaderReference(reader, this.readerReferenceQueue, submissionFilter);
this.readerMap.put(submissionFilter, readerReference);
}
return reader;
}
}
private void clearReferences() {
for (;;) {
ReaderReference readerReference = (ReaderReference) this.readerReferenceQueue.poll();
if (readerReference == null) {
break;
}
this.readerMap.remove(readerReference.submissionFilter);
}
}
private static class ReaderReference extends WeakReference<SubmissionQueueReaderImpl> {
SubmissionFilter submissionFilter;
public ReaderReference(SubmissionQueueReaderImpl referent,
ReferenceQueue<? super SubmissionQueueReaderImpl> queue,
SubmissionFilter submissionFilter) {
super(referent, queue);
this.submissionFilter = submissionFilter;
}
}
private class Candidate {
Candidate prev = null;
Candidate next = null;
Submission submission = null;
int priority;
public Submission tryClaim() {
if (this.submission == null) {
return null;
}
Submission ret = this.submission;
synchronized (this) {
if (this.submission == null) {
return null;
}
this.submission = null;
}
synchronized (SubmissionQueue.this) {
if (this.prev != null) {
this.prev.next = this.next;
}
this.next.prev = this.prev;
if (SubmissionQueue.this.head == this) {
SubmissionQueue.this.head = this.next;
}
// IMPORTANT: Set prev to null to make garbage collection possible. Don't set next to null so that any
// candidate reference can travel to the main queue by following this link. This is important to support
// multi-threaded queue readers.
this.prev = null;
}
return ret;
}
}
private class SubmissionQueueReaderImpl implements SubmissionQueueReader {
private SubmissionFilter submissionFilter;
int referenceCount = 0;
private Candidate head = SubmissionQueue.this.head;
private List<LinkedList<Candidate>> candidatesLists = new ArrayList<LinkedList<Candidate>>();
private int size = 0;
private SubmissionQueueReaderImpl(SubmissionFilter submissionFilter) {
this.submissionFilter = submissionFilter;
for (int i = Priority.MIN; i <= Priority.MAX; ++i) {
this.candidatesLists.add(new LinkedList<Candidate>());
}
}
public synchronized Submission poll(JudgeClientJudgeThread judgeThread) throws InterruptedException,
PersistenceException {
for (;;) {
while (this.head.next != null) {
this.add(this.head);
this.head = this.head.next;
}
if (this.size == 0) {
synchronized (SubmissionQueue.this) {
while (this.head.next != null) {
this.add(this.head);
this.head = this.head.next;
}
if (this.size == 0) {
SubmissionQueue.this.wait();
continue;
}
}
}
for (int i = this.candidatesLists.size() - 1; i >= 0; --i) {
LinkedList<Candidate> candidatesList = this.candidatesLists.get(i);
while (candidatesList.size() > 0) {
Candidate candidate = candidatesList.removeFirst();
--this.size;
Submission ret = candidate.tryClaim();
if (ret != null) {
return ret;
}
}
}
}
}
private void add(Candidate candidate) throws PersistenceException {
Submission submission = candidate.submission;
if (submission == null) {
return;
}
int priority = candidate.priority;
priority += this.submissionFilter.filter(submission, priority);
priority -= Priority.MIN;
if (priority < 0) {
return;
}
if (priority >= this.candidatesLists.size()) {
priority = this.candidatesLists.size() - 1;
}
this.candidatesLists.get(priority).add(candidate);
++this.size;
}
}
}