/*
* Copyright 2010-2011 Research In Motion Limited.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package blackberry.core.threading;
import blackberry.core.threading.InterruptibleThread.Interruption;
final class DispatcherImpl implements Runnable {
private static final int MAX_QUEUE_SIZE = 256;
private static final int DISPATCHER_TIMEOUT = 120000;
private static String NAME = "WebWorksDispatcher";
private DispatchableEvent _priority;
private DispatchableEvent _timed;
private DispatchableEvent _regular;
private DispatchableEvent _current;
// Number of events waiting (includes all three lists)
private int _queueSize;
private InterruptibleThread _thread;
private int _threadPriority;
// Enforce singleton
private DispatcherImpl() {
_threadPriority = Thread.NORM_PRIORITY;
}
private static class SingletonHolder {
private static final DispatcherImpl INSTANCE = new DispatcherImpl();
}
static final DispatcherImpl getInstance() {
// Use 'Initialization on Demand Holder' idiom
return SingletonHolder.INSTANCE;
}
synchronized void setPriority( int value ) {
synchronized( this ) {
_threadPriority = value;
if( _thread != null ) {
_thread.setPriority( _threadPriority );
}
}
}
synchronized boolean dispatch( final DispatchableEvent event ) {
DispatchableEvent newHead = dispatchCommon( _regular, event, -1 );
if( newHead == null ) {
return false;
}
_regular = newHead;
return true;
}
synchronized boolean dispatchHead( final DispatchableEvent event ) {
DispatchableEvent newHead = dispatchCommon( _priority, event, -1 );
if( newHead == null ) {
return false;
}
_priority = newHead;
return true;
}
synchronized boolean dispatchAt( final DispatchableEvent event, long dispatchTime ) {
DispatchableEvent newHead = dispatchCommon( _timed, event, dispatchTime );
if( newHead == null ) {
return false;
}
_timed = newHead;
return true;
}
private DispatchableEvent dispatchCommon( DispatchableEvent head,
DispatchableEvent event, long dispatchTime ) {
event.setDispatchTime( dispatchTime );
// Check to see if it can be merged with another event in that list
for ( DispatchableEvent iter = head; iter != null; iter = iter.next() ) {
if( iter == event || iter.merge( event ) ) {
return head;
}
}
// Check against max queue size
if( _queueSize >= MAX_QUEUE_SIZE ) {
return null;
}
// Restart the thread if it's gone dead from idleness
if( _thread == null ) {
_thread = new InterruptibleThread( this, NAME );
_thread.setPriority( _threadPriority );
_thread.start();
}
if( head == null || head.getDispatchTime() > dispatchTime ) {
head = event.prependTo( head );
} else {
for ( DispatchableEvent iter = head; iter != null; iter = iter.next() ) {
if( event.appendTo( iter ) ) {
break;
}
}
}
_queueSize++;
this.notifyAll();
return head;
}
/**
* Remove all DispatchableEvent(s) from the queue.
*/
synchronized void clear( final Object context ) {
_priority = clear( _priority, context );
_timed = clear( _timed, context );
_regular = clear( _regular, context );
if( _current != null && _current.hasContext( context ) && _thread != null ) {
try {
_thread.interrupt();
} catch ( IllegalThreadStateException itse ) {
// The thread died. Set it to null so it will get re-created and recover.
_thread = null;
}
}
}
private DispatchableEvent clear( DispatchableEvent head, final Object context ) {
while ( head != null && head.hasContext( context ) ) {
_queueSize--;
head = head.next();
}
if( head == null ) {
return head;
}
DispatchableEvent iter = head;
DispatchableEvent next = iter.next();
while ( next != null ) {
if( next.hasContext( context ) ) {
_queueSize--;
iter.removeNext();
} else {
iter = next;
}
next = iter.next();
}
return head;
}
public void run() {
while ( true ) {
synchronized( this ) {
while ( _queueSize == 0 ) {
try {
wait( DISPATCHER_TIMEOUT );
// Are we terminating?
if( _queueSize == 0 ) {
_thread = null;
return;
}
} catch ( InterruptedException ie ) {
// If it was triggered by a call to interrupt(), then
// clear the interrupted flag since we want to keep going
_thread.reset();
}
}
// Process queues and get "_current" event
long waitTime = 0;
if( _priority != null ) {
_current = _priority;
_priority = _priority.next();
} else {
if( _timed != null ) {
waitTime = _timed.getDispatchTime() - System.currentTimeMillis();
if( waitTime <= 0 ) {
_current = _timed;
_timed = _timed.next();
}
}
if( _current == null && _regular != null ) {
_current = _regular;
_regular = _regular.next();
}
}
if( _current == null ) {
// No events found. Wait for the first timed event.
try {
wait( waitTime );
} catch ( InterruptedException ie ) {
// If it was triggered by a call to interrupt(), then
// clear the interrupted flag since we want to keep going
}
continue; // Loop back to the top to find an event again
} else {
_queueSize--;
}
}
// Process "_current" event
try {
_current.dispatch();
} catch( Interruption in ) {
// Clear the interrupted flag
_thread.reset();
} catch( Error e ) {
e.printStackTrace();
} catch( Throwable t ) {
t.printStackTrace();
}
synchronized( this ) {
// In case clear() was called between dispatch() and this
_thread.reset();
_current = null;
}
}
}
}