felix-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ma...@apache.org
Subject svn commit: r1568220 - /felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/impl/SerialExecutor.java
Date Fri, 14 Feb 2014 08:47:36 GMT
Author: marrs
Date: Fri Feb 14 08:47:35 2014
New Revision: 1568220

URL: http://svn.apache.org/r1568220
Log:
Updated the SerialExecutor after a suggestion from Pierre.

Modified:
    felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/impl/SerialExecutor.java

Modified: felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/impl/SerialExecutor.java
URL: http://svn.apache.org/viewvc/felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/impl/SerialExecutor.java?rev=1568220&r1=1568219&r2=1568220&view=diff
==============================================================================
--- felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/impl/SerialExecutor.java (original)
+++ felix/sandbox/marrs/dependencymanager-prototype/dm/src/dm/impl/SerialExecutor.java Fri
Feb 14 08:47:35 2014
@@ -1,24 +1,8 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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 dm.impl;
 
 import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.Executor;
 
 import org.osgi.service.log.LogService;
 
@@ -31,98 +15,82 @@ import org.osgi.service.log.LogService;
  * 
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
-public final class SerialExecutor {
-	private static final Runnable DUMMY_RUNNABLE = new Runnable() { public void run() {}; };
-    private final LinkedList m_workQueue = new LinkedList();
-    private Runnable m_active;
-    private volatile Thread m_runningThread;
+public class SerialExecutor implements Executor {
+    private final List<Runnable> m_queue = new LinkedList<Runnable>();
+    private Thread m_runningThread;
     private final Logger m_logger;
-    
-    /**
-     * Makes a new SerialExecutor
-     * @param logger the logger used to log possible errors thrown by submitted tasks.
-     */
+
     public SerialExecutor(Logger logger) {
         m_logger = logger;
     }
-    
-    /**
-     * Enqueue a new task for later execution. This method is
-     * thread-safe, so multiple threads can contribute tasks.
-     * 
-     * @param runnable the runnable containing the actual task
-     */
-    private synchronized void enqueue(final Runnable runnable) {
-    	m_workQueue.addLast(new Runnable() {
-			public void run() {
-				try {
-					runnable.run();
-				}
-				catch (Throwable t) {
-		            m_logger.log(LogService.LOG_ERROR, "got unexpected exception while executing
dependencymanager task:"
-		                + toString(), t);
-				}
-				finally {
-					scheduleNext();
-				}
-			}
-		});
-    }
-    
+
     /**
-     * Execute any pending tasks. This method is thread safe,
-     * so multiple threads can try to execute the pending
-     * tasks, but only the first will be used to actually do
-     * so. Other threads will return immediately.
+     * Execute a task. This method is thread safe, so multiple threads can try to execute
a task
+     * but only the first will be executed, other threads will return immediately, and the
+     * first thread will execute the tasks scheduled by the other threads.<p>
+     * <p>
+     * This method is reentrant: if the current thread is currently being executed by this
executor, then any
+     * subsequent tasks scheduled in this executor will executed immediately (inline execution).
      */
-    private void execute() {
-    	Runnable active;
-    	synchronized (this) {
-    		active = m_active;
-    		// for now just put some non-null value in there so we can never
-    		// get a race condition when two threads enter this section after
-    		// one another (causing sheduleNext() to be invoked twice below)
-    		m_active = DUMMY_RUNNABLE;
-    	}
-    	if (active == null) {
-    	    scheduleNext();
-    	}
+    public void execute(Runnable command) {
+        execute(command, true);
     }
     
     /**
-     * Execute a task. This method is thread safe,
-     * so multiple threads can try to execute a task
+     * Execute a task. This method is thread safe, so multiple threads can try to execute
a task
      * but only the first will be executed, other threads will return immediately, and the
      * first thread will execute the tasks scheduled by the other threads.
+     * 
+     * @param command the task to execute (possibly to be delayed and executed by another
single thread)
+     * @param reentrant true means that if the executor is already being executed on this
thread, then the 
+     * command is run immediately (inline execution). else, if reentrant == false, then the
task is enqueued 
+     * even if the executor is currently being executed by the current thread.
      */
-    public void execute(Runnable task) {
-    	boolean isExecutingOnThisThread;
-    	synchronized (this) {
-    		isExecutingOnThisThread = (Thread.currentThread() == (Thread) m_runningThread);
-    	}
-        if (isExecutingOnThisThread) {
-            task.run();
+    public void execute(Runnable command, boolean reentrant) {
+        Runnable next = null;
+        Thread currentThread = Thread.currentThread();
+        boolean isExecutingOnThisThread = false;
+
+        synchronized (this) {
+            if (reentrant && m_runningThread == currentThread) {
+                // we are already being executed by this executor, so we'll execute the command
directly (inline execution).
+                isExecutingOnThisThread = true;
+            }
+            else {
+                if (m_queue.isEmpty()) {
+                    // Nobody is currently using the executor, we'll execute the command,
but we have to store it
+                    // in the queue, so another thread won't execute concurrently. Also,
we mark the current executing
+                    // thread to our own current thread, in order to inline executions of
reentrant scheduled tasks, if any.
+                    m_runningThread = currentThread;
+                    next = command;
+                }
+                m_queue.add(command);
+            }
+        }
+
+        if (reentrant && isExecutingOnThisThread) {
+            runTask(command);
         }
         else {
-           enqueue(task);
-           execute();
+            while (next != null) {
+                runTask(next);
+                synchronized (this) {
+                    m_queue.remove(0); // The first element is the one we have just executed
+                    next = m_queue.isEmpty() ? null : (Runnable) m_queue.get(0);
+                    if (next == null) {
+                        m_runningThread = null;
+                    }
+                }
+            }
         }
     }
 
-    private void scheduleNext() {
-    	Runnable active;
-    	synchronized (this) {
-			if (!m_workQueue.isEmpty()) {
-			    m_runningThread = Thread.currentThread();
-				m_active = (Runnable) m_workQueue.removeFirst();
-			} else {
-	            m_runningThread = null;
-			    m_active = null;
-			}
-    		active = m_active;
-    	}
-    	if (active != null) {
-            active.run();
+    private void runTask(Runnable command) {
+        try {
+            command.run();
+        }
+        catch (Throwable t) {
+            m_logger.log(LogService.LOG_ERROR, "Error processing tasks", t);
         }
     }
-}
+}
\ No newline at end of file



Mime
View raw message