/*
* $Id$
*
* Copyright (C) 2003-2015 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.vm.scheduler;
import org.jnode.system.resource.IRQHandler;
import org.jnode.system.resource.ResourceOwner;
import org.jnode.annotation.KernelSpace;
import org.jnode.annotation.Uninterruptible;
/**
* Thread for IRQ handler.
*
* @author epr
*/
final class IRQThread extends Thread implements SystemThread {
private final IRQManager mgr;
private final int irq;
private boolean stop;
private int irqCount;
private int handledIrqCount;
/**
* Is this a shared IRQ?
*/
private final boolean shared;
private IRQAction actions;
private final VmThread vmThread;
private final Object ACTION_LOCK = new Object();
public IRQThread(IRQManager mgr, int irq, ResourceOwner owner,
IRQHandler handler, boolean shared, VmProcessor processor) {
super("IRQ-" + irq);
this.mgr = mgr;
this.irq = irq;
this.setPriority(Thread.MAX_PRIORITY);
this.stop = false;
this.irqCount = 0;
this.handledIrqCount = 0;
this.shared = shared;
this.actions = new IRQAction(owner, handler);
this.vmThread = ThreadHelper.getVmThreadKS(this);
this.vmThread.setRequiredProcessor(processor);
}
/**
* Add an IRQ handler
*
* @param owner
* @param handler
*/
public final void addHandler(ResourceOwner owner, IRQHandler handler) {
synchronized (ACTION_LOCK) {
final IRQAction a = new IRQAction(owner, handler);
if (actions == null) {
actions = a;
} else {
actions.add(a);
}
}
}
/**
* Remove a given handler
*
* @param handler
*/
public final void remove(IRQHandler handler) {
synchronized (ACTION_LOCK) {
if (this.actions != null) {
this.actions = this.actions.remove(handler);
}
}
}
public final boolean isEmpty() {
return (this.actions == null);
}
/**
* Stop this IRQ thread.
*/
final void stopThread() {
this.stop = true;
interrupt();
vmThread.unsecureResume();
}
/**
* Continue to run until i'm stopped.
*
* @see java.lang.Runnable#run()
*/
public final void run() {
while (!stop) {
doHandle();
}
}
/**
* Wait to be notified or then handle exactly 1 interrupt.
*/
private final void doHandle() {
if (irqCount == handledIrqCount) {
vmThread.unsecureSuspend();
// try {
// wait();
// } catch (InterruptedException ex) {
// // Ignore
// }
// Also set by signalIRQ, but just in case...
} else {
try {
try {
/*
* if (irq == 10) { Screen.debug(" <handle IRQ 10/> ");
*/
IRQAction action = this.actions;
while (action != null) {
try {
if (action.isEnabled()) {
action.getHandler().handleInterrupt(irq);
}
} catch (Throwable ex) {
action.incErrorCount();
ex.printStackTrace();
}
action = action.getNext();
}
handledIrqCount++;
} finally {
mgr.eoi(irq);
}
} catch (Throwable ex) {
ex.printStackTrace();
}
}
}
/**
* Tell this thread how many IRQ's have been received, so this thread can
* start the IRQ handler if needed.
*
* @param count
* @param current
* @throws org.vmmagic.pragma.UninterruptiblePragma
*/
@KernelSpace
@Uninterruptible
final void signalIRQ(int count, VmThread current) {
this.irqCount = count;
if ((irqCount != handledIrqCount) && (current != vmThread)) {
vmThread.unsecureResume();
}
}
/**
* @return int
*/
public final int getIrq() {
return irq;
}
/**
* Convert to a String representation.
*
* @return String
* @see java.lang.Object#toString()
*/
public String toString() {
final StringBuilder b = new StringBuilder();
if (shared) {
b.append("shared ");
}
IRQAction a = this.actions;
boolean first = true;
while (a != null) {
if (!first) {
b.append(", ");
}
first = false;
b.append(a);
a = a.getNext();
}
return b.toString();
}
static class IRQAction {
private final ResourceOwner owner;
private final IRQHandler handler;
private IRQAction next;
private int errorCount;
public IRQAction(ResourceOwner owner, IRQHandler handler) {
this.owner = owner;
this.handler = handler;
}
public final IRQHandler getHandler() {
return handler;
}
public final IRQAction getNext() {
return next;
}
public final void add(IRQAction action) {
IRQAction p = this;
while (p.next != null) {
p = p.next;
}
p.next = action;
}
public final IRQAction remove(IRQHandler handler) {
if (this.handler == handler) {
return this.next;
} else if (this.next != null) {
this.next = this.next.remove(handler);
return this;
} else {
return this;
}
}
public final void incErrorCount() {
errorCount++;
}
public final boolean isEnabled() {
return errorCount < 10;
}
public final String toString() {
return owner.getShortDescription()
+ ((errorCount > 0) ? " errors " + errorCount : "");
}
}
/**
* @return Returns the shared.
*/
public final boolean isShared() {
return this.shared;
}
}