001    /*
002    Galois, a framework to exploit amorphous data-parallelism in irregular
003    programs.
004    
005    Copyright (C) 2010, The University of Texas at Austin. All rights reserved.
006    UNIVERSITY EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES CONCERNING THIS SOFTWARE
007    AND DOCUMENTATION, INCLUDING ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR ANY
008    PARTICULAR PURPOSE, NON-INFRINGEMENT AND WARRANTIES OF PERFORMANCE, AND ANY
009    WARRANTY THAT MIGHT OTHERWISE ARISE FROM COURSE OF DEALING OR USAGE OF TRADE.
010    NO WARRANTY IS EITHER EXPRESS OR IMPLIED WITH RESPECT TO THE USE OF THE
011    SOFTWARE OR DOCUMENTATION. Under no circumstances shall University be liable
012    for incidental, special, indirect, direct or consequential damages or loss of
013    profits, interruption of business, or related expenses which may arise from use
014    of Software or Documentation, including but not limited to those resulting from
015    defects in Software and/or Documentation, or loss or inaccuracy of data of any
016    kind.
017    
018    
019     */
020    
021    package galois.runtime;
022    
023    import galois.objects.Mappable;
024    import galois.objects.MethodFlag;
025    import galois.runtime.wl.OrderableWorklist;
026    import galois.runtime.wl.ParameterOrderedWorklist;
027    import galois.runtime.wl.ParameterUnorderedWorklist;
028    import galois.runtime.wl.ParameterWorklist;
029    import galois.runtime.wl.Priority;
030    import galois.runtime.wl.Worklist;
031    import galois.runtime.wl.Priority.Rule;
032    
033    import java.io.FileInputStream;
034    import java.io.PrintStream;
035    import java.util.ArrayDeque;
036    import java.util.ArrayList;
037    import java.util.Arrays;
038    import java.util.HashMap;
039    import java.util.List;
040    import java.util.Map;
041    import java.util.Properties;
042    import java.util.concurrent.Callable;
043    import java.util.concurrent.ExecutionException;
044    import java.util.concurrent.atomic.AtomicInteger;
045    import java.util.concurrent.locks.Condition;
046    import java.util.concurrent.locks.ReentrantLock;
047    import java.util.logging.Level;
048    import java.util.logging.Logger;
049    
050    import util.Launcher;
051    import util.Reflection;
052    import util.RuntimeStatistics;
053    import util.Sampler;
054    import util.StackSampler;
055    import util.Statistics;
056    import util.SystemProperties;
057    import util.fn.Lambda2Void;
058    import util.fn.Lambda3Void;
059    import util.fn.LambdaVoid;
060    
061    /**
062     * Provides methods to access Galois runtime from application code.
063     *
064     */
065    public final class GaloisRuntime {
066      private static Logger logger = Logger.getLogger("galois.runtime.GaloisRuntime");
067    
068      private static final int ITERATION_MULTIPLIER = SystemProperties.getIntProperty("iterationMultiplier", 1);
069      private static GaloisRuntime instance = null;
070    
071      private boolean invalid;
072      private final boolean useParameter;
073      private final boolean useSerial;
074      private final ReplayFeature.Type replayType;
075      private final int maxThreads;
076    
077      private final int maxIterations;
078      private final boolean moreStats;
079      private final boolean ignoreUserFlags;
080    
081      private final ThreadSuspender threadSuspender;
082      private final ArrayDeque<ExecutorFrame> stack;
083      private final Executor root;
084      private ExecutorFrame current;
085      private static byte currentMask;
086    
087      private GaloisRuntime(int numThreads, boolean useParameter, boolean useSerial, ReplayFeature.Type replayType,
088          boolean moreStats, boolean ignoreUserFlags) {
089        this.maxIterations = ITERATION_MULTIPLIER * numThreads;
090        this.maxThreads = useParameter ? 1 : numThreads;
091        this.useParameter = useParameter;
092        this.useSerial = useSerial;
093        this.replayType = replayType;
094        this.moreStats = moreStats;
095        this.ignoreUserFlags = ignoreUserFlags;
096    
097        threadSuspender = new ThreadSuspender(maxThreads);
098        stack = new ArrayDeque<ExecutorFrame>();
099        root = new DummyExecutor();
100        current = new ExecutorFrame(new ThreadPool(numThreads), root, MethodFlag.NONE);
101        currentMask = current.mask;
102      }
103    
104      /**
105       * Called by the testing framework to reset the runtime.
106       */
107      private static void initialize(int numThreads, boolean useParameter, boolean useSerial,
108          ReplayFeature.Type replayType, boolean moreStats, boolean ignoreUserFlags) {
109        if (instance != null) {
110          instance.invalidate();
111        }
112    
113        instance = new GaloisRuntime(numThreads, useParameter, useSerial, replayType, moreStats, ignoreUserFlags);
114      }
115    
116      /**
117       * Returns the current instance of the runtime.
118       * 
119       * @return  a reference to the current runtime
120       */
121      public static GaloisRuntime getRuntime() {
122        if (instance == null) {
123          // Use default serial Runtime
124          initialize(1, false, true, ReplayFeature.Type.NO, false, false);
125        }
126        return instance;
127      }
128    
129      /**
130       * Creates an unordered Galois iterator that concurrently applies a function over all elements
131       * in some initial collection. Additional elements may be added during iteration.
132       * 
133       * @param <T>       type of elements to iterate over
134       * @param initial   initial elements to iterate over
135       * @param body      function to apply
136       * @param priority  specification of the order elements are processed
137       * @throws ExecutionException  if there is an uncaught exception during execution
138       * @see #foreach(Mappable, LambdaVoid)
139       * @see #foreach(Mappable, Lambda2Void, galois.runtime.wl.Priority.Rule)
140       */
141      public static <T> void foreach(Iterable<T> initial, Lambda2Void<T, ForeachContext<T>> body, Rule priority)
142          throws ExecutionException {
143        getRuntime().runBody(initial, body, priority);
144      }
145    
146      /**
147       * Creates an unordered Galois iterator that concurrently applies a function over all elements
148       * in some initial collection. Additional elements may be added during iteration.
149       * 
150       * @param <T>       type of elements to iterate over
151       * @param initial   initial elements to iterate over
152       * @param body      function to apply
153       * @param priority  specification of the order elements are processed
154       * @throws ExecutionException  if there is an uncaught exception during execution
155       * @see #foreach(Mappable, LambdaVoid)
156       * @see #foreach(Iterable, Lambda2Void, galois.runtime.wl.Priority.Rule)
157       */
158      public static <T> void foreach(Mappable<T> initial, Lambda2Void<T, ForeachContext<T>> body, Rule priority)
159          throws ExecutionException {
160        getRuntime().runBody(initial, body, priority);
161      }
162    
163      /**
164       * Creates an ordered Galois iterator that concurrently applies a function over all elements
165       * in some initial collection. Additional elements may be added during iteration. Elements
166       * are processed strictly according to some order.
167       * 
168       * @param <T>       type of elements to iterate over
169       * @param initial   initial elements to iterate over
170       * @param body      function to apply
171       * @param priority  specification of the order elements are processed
172       * @throws ExecutionException  if there is an uncaught exception during execution
173       * @see #foreachOrdered(Mappable, Lambda2Void, galois.runtime.wl.Priority.Rule)
174       */
175      public static <T> void foreachOrdered(Iterable<T> initial, Lambda2Void<T, ForeachContext<T>> body, Rule priority)
176          throws ExecutionException {
177        getRuntime().runOrderedBody(initial, body, priority);
178      }
179    
180      /**
181       * Creates an ordered Galois iterator that concurrently applies a function over all elements
182       * in some initial collection. Additional elements may be added during iteration. Elements
183       * are processed strictly according to some order.
184       * 
185       * @param <T>       type of elements to iterate over
186       * @param initial   initial elements to iterate over
187       * @param body      function to apply
188       * @param priority  specification of the order elements are processed
189       * @throws ExecutionException  if there is an uncaught exception during execution
190       * @see #foreachOrdered(Iterable, Lambda2Void, galois.runtime.wl.Priority.Rule)
191       */
192      public static <T> void foreachOrdered(Mappable<T> initial, Lambda2Void<T, ForeachContext<T>> body, Rule priority)
193          throws ExecutionException {
194        getRuntime().runOrderedBody(initial, body, priority);
195      }
196    
197      /**
198       * Creates an unordered Galois iterator that concurrently applies a function over all elements
199       * in some initial collection. In contrast to
200       * {@link #foreach(Mappable, Lambda2Void, galois.runtime.wl.Priority.Rule)},
201       * no additional elements may be added during iteration and the particular order
202       * elements are processed in is dictated by the particular {@link Mappable} instance.
203       * 
204       * @param <T>       type of elements to iterate over
205       * @param initial   initial elements to iterate over
206       * @param body      function to apply
207       * @throws ExecutionException  if there is an uncaught exception during execution
208       * @see #foreach(Mappable, Lambda2Void, Object)
209       * @see #foreach(Mappable, Lambda3Void, Object, Object)
210       */
211      public static <T> void foreach(Mappable<T> initial, LambdaVoid<T> body) throws ExecutionException {
212        getRuntime().runBody(initial, body);
213      }
214    
215      /**
216       * Creates an unordered Galois iterator that concurrently applies a function over all elements
217       * in some initial collection. In contrast to
218       * {@link #foreach(Mappable, Lambda2Void,galois.runtime.wl.Priority.Rule)},
219       * no additional elements may be added during iteration and the particular order
220       * elements are processed in is dictated by the particular {@link Mappable} instance.
221       * 
222       * @param <T>       type of elements to iterate over
223       * @param initial   initial elements to iterate over
224       * @param body      function to apply
225       * @param arg1      additional argument to function
226       * @throws ExecutionException  if there is an uncaught exception during execution
227       * @see #foreach(Mappable, LambdaVoid)
228       * @see #foreach(Mappable, Lambda3Void, Object, Object)
229       */
230      public static <T, A1> void foreach(Mappable<T> initial, Lambda2Void<T, A1> body, A1 arg1) throws ExecutionException {
231        getRuntime().runBody(initial, body, arg1);
232      }
233    
234      /**
235       * Creates an unordered Galois iterator that concurrently applies a function over all elements
236       * in some initial collection. In contrast to 
237       * {@link #foreach(Mappable, Lambda2Void, galois.runtime.wl.Priority.Rule)},
238       * no additional elements may be added during iteration and the particular order
239       * elements are processed in is dictated by the particular {@link Mappable} instance.
240       * 
241       * @param <T>       type of elements to iterate over
242       * @param initial   initial elements to iterate over
243       * @param body      function to apply
244       * @param arg1      additional argument to function
245       * @param arg2      additional argument to function
246       * @throws ExecutionException  if there is an uncaught exception during execution
247       * @see #foreach(Mappable, LambdaVoid)
248       * @see #foreach(Mappable, Lambda2Void, Object)
249       */
250      public static <T, A1, A2> void foreach(Mappable<T> initial, Lambda3Void<T, A1, A2> body, A1 arg1, A2 arg2)
251          throws ExecutionException {
252        getRuntime().runBody(initial, body, arg1, arg2);
253      }
254    
255      private void invalidate() {
256        assert stack.isEmpty();
257        current.pool.shutdown();
258        invalid = true;
259      }
260    
261      private void checkValidity() {
262        assert !invalid;
263      }
264    
265      public void onCommit(Iteration it, Callback action) {
266        checkValidity();
267        current.executor.onCommit(it, action);
268      }
269    
270      public void onUndo(Iteration it, Callback action) {
271        checkValidity();
272        current.executor.onUndo(it, action);
273      }
274    
275      public void onRelease(Iteration it, ReleaseCallback action) {
276        checkValidity();
277        current.executor.onRelease(it, action);
278      }
279    
280      public static boolean needMethodFlag(byte flags, byte option) {
281        // Since this is called very often, skip the validity check
282        // checkValidity();
283    
284        // Apparently the following check even when converted to using static
285        // fields is slow
286        // if (useParameter)
287        //   flags = MethodFlag.ALL;
288    
289        return ((flags & currentMask) & option) != 0;
290      }
291    
292      <T> void callAll(List<? extends Callable<T>> callables) throws InterruptedException, ExecutionException {
293        checkValidity();
294        current.pool.callAll(callables);
295      }
296    
297      /**
298       * Gets the maximum number of threads that can be used by the Runtime.
299       *
300       * @return maximum number of threads available
301       */
302      public int getMaxThreads() {
303        checkValidity();
304        return maxThreads;
305      }
306    
307      public int getMaxIterations() {
308        checkValidity();
309        return maxIterations;
310      }
311    
312      /**
313       * Signals that conflict has been detected by the user/library code.
314       *
315       * @param it          the current iteration
316       * @param conflicter  the iteration that is in conflict with the current iteration
317       */
318      public void raiseConflict(Iteration it, Iteration conflicter) {
319        checkValidity();
320        current.executor.arbitrate(it, conflicter);
321      }
322    
323      private void push(ExecutorFrame frame) {
324        stack.push(current);
325        current = frame;
326        currentMask = current.mask;
327      }
328    
329      private void pop() {
330        current = stack.pop();
331        currentMask = current.mask;
332      }
333    
334      void replaceWithRootContextAndCall(final Callback callback) throws ExecutionException {
335        ExecutorFrame oldFrame = current;
336    
337        pop();
338        try {
339          pushContextAndCall(root, new Callable<IterationStatistics>() {
340            @Override
341            public IterationStatistics call() throws Exception {
342              callback.call();
343              return null;
344            }
345          });
346        } finally {
347          push(oldFrame);
348        }
349      }
350    
351      private IterationStatistics __stackSamplerRecordMe(Callable<IterationStatistics> callback) throws Exception {
352        return callback.call();
353      }
354    
355      private IterationStatistics pushContextAndCall(Executor executor, Callable<IterationStatistics> callback)
356          throws ExecutionException {
357        boolean suspended = false;
358        if (!current.executor.isSerial()) {
359          try {
360            // TODO(ddn): Can lead to deadlock (infinite livelock) when using mappable
361            // iterators because one thread sleeps holding its locks and the other threads
362            // keep executing but they can never suspend their executor
363            if (current.executor instanceof MappableExecutor)
364              throw new Error("Not yet supported");
365            threadSuspender.suspend(current.executor);
366            suspended = true;
367          } catch (InterruptedException e) {
368            throw new ExecutionException(e);
369          }
370        }
371    
372        boolean isSerial = executor.isSerial();
373        byte mask = isSerial ? MethodFlag.NONE : MethodFlag.ALL;
374        // Try to reuse thread pool if possible
375        ThreadPool pool;
376        if (suspended) {
377          pool = new ThreadPool(maxThreads);
378        } else {
379          pool = current.pool;
380        }
381    
382        push(new ExecutorFrame(pool, executor, mask));
383        try {
384          if (isSerial)
385            return __stackSamplerRecordMe(callback);
386          else
387            return callback.call();
388        } catch (Exception e) {
389          throw new ExecutionException(e);
390        } finally {
391          // Shutdown newly created thread pools
392          if (pool != null && suspended)
393            pool.shutdown();
394          pop();
395        }
396      }
397    
398      private <T> void initializeWorklist(final Worklist<T> wl, Iterable<T> initial, Mappable<T> mappable)
399          throws ExecutionException {
400        final ForeachContext<T> ctx = new SimpleContext<T>(maxThreads);
401    
402        if (initial != null) {
403          for (T item : initial) {
404            wl.addInitial(item, ctx);
405          }
406        } else {
407          mappable.map(new LambdaVoid<T>() {
408            @Override
409            public void call(T item) {
410              wl.addInitial(item, ctx);
411            }
412          });
413        }
414        wl.finishAddInitial();
415      }
416    
417      private <T, S> void initializeWorklist(final ParameterWorklist<T, S> wl, Iterable<T> initial, Mappable<T> mappable)
418          throws ExecutionException {
419        if (initial != null) {
420          for (T item : initial) {
421            wl.add(item);
422          }
423        } else {
424          mappable.map(new LambdaVoid<T>() {
425            @Override
426            public void call(T item) {
427              wl.add(item);
428            }
429          });
430        }
431      }
432    
433      private <T> void runBody(Mappable<T> mappable, Lambda2Void<T, ForeachContext<T>> body, Rule priority)
434          throws ExecutionException {
435        checkValidity();
436        runBody(null, mappable, body, ExecutorType.UNORDERED, priority);
437      }
438    
439      private <T> void runBody(Iterable<T> initial, Lambda2Void<T, ForeachContext<T>> body, Rule priority)
440          throws ExecutionException {
441        checkValidity();
442        runBody(initial, null, body, ExecutorType.UNORDERED, priority);
443      }
444    
445      private <T> void runOrderedBody(Mappable<T> mappable, Lambda2Void<T, ForeachContext<T>> body, Rule priority)
446          throws ExecutionException {
447        checkValidity();
448        runBody(null, mappable, body, ExecutorType.ORDERED, priority);
449      }
450    
451      private <T> void runOrderedBody(Iterable<T> initial, Lambda2Void<T, ForeachContext<T>> body, Rule priority)
452          throws ExecutionException {
453        checkValidity();
454        runBody(initial, null, body, ExecutorType.ORDERED, priority);
455      }
456    
457      @SuppressWarnings("unchecked")
458      private <T> void runBody(Iterable<T> initial, Mappable<T> mappable, final Lambda2Void<T, ForeachContext<T>> body,
459          ExecutorType type, Rule priority) throws ExecutionException {
460    
461        IterationStatistics stats = null;
462        if (replayType == ReplayFeature.Type.PLAYBACK) {
463          final Worklist<T> wl = Priority.makeSerial(priority);
464          final PlaybackReplayFeature<T> feature = (PlaybackReplayFeature<T>) Features.getReplayFeature();
465          initializeWorklist(wl, initial, mappable);
466          stats = pushContextAndCall(feature, new Callable<IterationStatistics>() {
467            @Override
468            public IterationStatistics call() throws Exception {
469              return feature.call(body, wl);
470            }
471          });
472        } else if (useSerial) {
473          final Worklist<T> wl = Priority.makeSerial(priority);
474          final SerialExecutor<T> ex = new SerialExecutor<T>();
475          initializeWorklist(wl, initial, mappable);
476          stats = pushContextAndCall(ex, new Callable<IterationStatistics>() {
477            @Override
478            public IterationStatistics call() throws Exception {
479              return ex.call(body, wl);
480            }
481          });
482        } else if (useParameter) {
483          if (type == ExecutorType.ORDERED) {
484            final ParameterOrderedWorklist<T> wl = Priority.makeParameterOrdered(priority);
485            final ParameterOrderedExecutor<T> ex = new ParameterOrderedExecutor<T>(wl);
486            initializeWorklist(wl, initial, mappable);
487            stats = pushContextAndCall(ex, new Callable<IterationStatistics>() {
488              @Override
489              public IterationStatistics call() throws Exception {
490                return ex.call(body);
491              }
492            });
493          } else {
494            assert type == ExecutorType.BAREBONES || type == ExecutorType.UNORDERED;
495            final ParameterUnorderedWorklist<T> wl = Priority.makeParameterUnordered();
496            final AbstractParameterExecutor<T, T> ex = new ParameterUnorderedExecutor<T>(wl);
497            initializeWorklist(wl, initial, mappable);
498            stats = pushContextAndCall(ex, new Callable<IterationStatistics>() {
499              @Override
500              public IterationStatistics call() throws Exception {
501                return ex.call(body);
502              }
503            });
504          }
505        } else {
506          if (type == ExecutorType.ORDERED) {
507            final OrderableWorklist<T> wl = Priority.makeOrdered(priority);
508            final OrderedExecutor<T> ex = new OrderedExecutor<T>(wl);
509            initializeWorklist(wl, initial, mappable);
510            stats = pushContextAndCall(ex, new Callable<IterationStatistics>() {
511              @Override
512              public IterationStatistics call() throws Exception {
513                return ex.call(body, wl);
514              }
515            });
516          } else if (type == ExecutorType.BAREBONES) {
517            final Worklist<T> wl = Priority.makeUnordered(priority);
518            final BarebonesExecutor<T> ex = new BarebonesExecutor<T>();
519            initializeWorklist(wl, initial, mappable);
520            stats = pushContextAndCall(ex, new Callable<IterationStatistics>() {
521              @Override
522              public IterationStatistics call() throws Exception {
523                return ex.call(body, wl);
524              }
525            });
526          } else {
527            assert type == ExecutorType.UNORDERED;
528            final Worklist<T> wl = Priority.makeUnordered(priority);
529            final UnorderedExecutor<T> ex = new UnorderedExecutor<T>();
530            initializeWorklist(wl, initial, mappable);
531            stats = pushContextAndCall(ex, new Callable<IterationStatistics>() {
532              @Override
533              public IterationStatistics call() throws Exception {
534                return ex.call(body, wl);
535              }
536            });
537          }
538        }
539        if (stats == null)
540          throw new Error("unknown executor");
541    
542        Launcher.getLauncher().addStats(stats);
543        Features.getReplayFeature().onFinish();
544      }
545    
546      @SuppressWarnings("unchecked")
547      private <T> void runBody(final Mappable<T> mappable, final Object body, final MappableType type, final Object... args)
548          throws ExecutionException {
549        IterationStatistics stats = null;
550    
551        if (replayType == ReplayFeature.Type.PLAYBACK) {
552          final PlaybackReplayFeature<T> feature = (PlaybackReplayFeature<T>) Features.getReplayFeature();
553          stats = pushContextAndCall(feature, new Callable<IterationStatistics>() {
554            @Override
555            public IterationStatistics call() throws Exception {
556              return feature.call(mappable, body, type, args);
557            }
558          });
559        } else if (useSerial) {
560          final SerialMappableExecutor<T> ex = new SerialMappableExecutor<T>(mappable);
561          stats = pushContextAndCall(ex, new Callable<IterationStatistics>() {
562            @Override
563            public IterationStatistics call() throws Exception {
564              return ex.call(body, type, args);
565            }
566          });
567        } else if (useParameter) {
568          ParameterUnorderedWorklist<T> wl = Priority.makeParameterUnordered();
569          final ParameterUnorderedExecutor<T> ex = new ParameterUnorderedExecutor<T>(wl);
570    
571          initializeWorklist(wl, null, mappable);
572          stats = pushContextAndCall(ex, new Callable<IterationStatistics>() {
573            @Override
574            public IterationStatistics call() throws Exception {
575              return ex.call(body, type, args);
576            }
577          });
578        } else {
579          final MappableExecutor<T> ex = new MappableExecutor<T>(mappable);
580          stats = pushContextAndCall(ex, new Callable<IterationStatistics>() {
581            @Override
582            public IterationStatistics call() throws Exception {
583              return ex.call(body, type, args);
584            }
585          });
586        }
587        if (stats == null)
588          throw new Error("unknown executor");
589    
590        Launcher.getLauncher().addStats(stats);
591        Features.getReplayFeature().onFinish();
592      }
593    
594      private <T> void runBody(Mappable<T> mappable, LambdaVoid<T> body) throws ExecutionException {
595        checkValidity();
596        runBody(mappable, body, MappableType.TYPE_0);
597      }
598    
599      private <T, A1> void runBody(Mappable<T> mappable, Lambda2Void<T, A1> body, A1 arg1) throws ExecutionException {
600        checkValidity();
601        runBody(mappable, body, MappableType.TYPE_1, arg1);
602      }
603    
604      private <T, A1, A2> void runBody(Mappable<T> mappable, Lambda3Void<T, A1, A2> body, A1 arg1, A2 arg2)
605          throws ExecutionException {
606        checkValidity();
607        runBody(mappable, body, MappableType.TYPE_2, arg1, arg2);
608      }
609    
610      public boolean useParameter() {
611        checkValidity();
612        return useParameter;
613      }
614    
615      public boolean useSerial() {
616        checkValidity();
617        return useSerial;
618      }
619    
620      public boolean ignoreUserFlags() {
621        checkValidity();
622        return ignoreUserFlags;
623      }
624    
625      public boolean inRoot() {
626        checkValidity();
627        return current.executor == root;
628      }
629    
630      public boolean moreStats() {
631        checkValidity();
632        return moreStats;
633      }
634    
635      private static enum ExecutorType {
636        BAREBONES, UNORDERED, ORDERED;
637      }
638    
639      private static class ExecutorFrame {
640        final ThreadPool pool;
641        final Executor executor;
642        final byte mask;
643    
644        public ExecutorFrame(ThreadPool pool, Executor executor, byte mask) {
645          this.pool = pool;
646          this.executor = executor;
647          this.mask = mask;
648        }
649      }
650    
651      private static class ThreadSuspender implements Callback {
652        private final ReentrantLock lock;
653        private final Condition cond;
654        private final int numThreads;
655    
656        private int numSuspended;
657        private boolean once;
658    
659        public ThreadSuspender(int numThreads) {
660          this.numThreads = numThreads;
661          lock = new ReentrantLock();
662          cond = lock.newCondition();
663        }
664    
665        private void abort() {
666          // TODO(ddn): What to do for barebones executors?
667          IterationAbortException.throwException();
668        }
669    
670        private void commit() {
671          Iteration it = Iteration.getCurrentIteration();
672          if (it != null)
673            it.performCommit(true);
674        }
675    
676        private void reset() {
677          once = false;
678          numSuspended = 0;
679        }
680    
681        public void suspend(Executor executor) throws InterruptedException {
682          lock.lock();
683          try {
684            if (once) {
685              abort();
686              return;
687            } else {
688              once = true;
689            }
690    
691            executor.suspend(this);
692    
693            while (numSuspended < numThreads - 1) {
694              cond.await();
695            }
696    
697            executor.suspendDone();
698            reset();
699            commit();
700          } finally {
701            lock.unlock();
702          }
703        }
704    
705        @Override
706        public void call() {
707          lock.lock();
708          try {
709            numSuspended++;
710    
711            if (numSuspended == numThreads - 1) {
712              cond.signal();
713            }
714          } finally {
715            lock.unlock();
716          }
717        }
718      }
719    
720      private static class DummyExecutor implements Executor {
721        @Override
722        public void arbitrate(Iteration current, Iteration conflicter) throws IterationAbortException {
723        }
724    
725        @Override
726        public void onCommit(Iteration it, Callback action) {
727        }
728    
729        @Override
730        public void onRelease(Iteration it, ReleaseCallback action) {
731        }
732    
733        @Override
734        public void onUndo(Iteration it, Callback action) {
735        }
736    
737        public boolean isSerial() {
738          return true;
739        }
740    
741        @Override
742        public void suspend(Callback listener) {
743          throw new UnsupportedOperationException();
744        }
745    
746        @Override
747        public void suspendDone() {
748          throw new UnsupportedOperationException();
749        }
750      }
751    
752      private static class SimpleContext<T> extends AbstractExecutorContext<T> {
753        private final int maxThreads;
754        private final AtomicInteger current = new AtomicInteger();
755    
756        public SimpleContext(int maxThreads) {
757          this.maxThreads = maxThreads;
758        }
759    
760        @Override
761        public int getThreadId() {
762          return current.getAndIncrement() % maxThreads;
763        }
764    
765        @Override
766        public int getIterationId() {
767          throw new UnsupportedOperationException("Not supported yet.");
768        }
769      }
770    
771      private static void dumpStatistics(List<Statistics> stats, PrintStream summaryOut, PrintStream fullOut) {
772        if (stats.isEmpty())
773          return;
774    
775        fullOut.println("====== Individual Statistics ======");
776        for (Statistics stat : stats) {
777          stat.dumpFull(fullOut);
778        }
779    
780        List<Statistics> merged = new ArrayList<Statistics>();
781        Map<Class<? extends Statistics>, Statistics> reps = new HashMap<Class<? extends Statistics>, Statistics>();
782    
783        for (Statistics stat : stats) {
784          Class<? extends Statistics> key = stat.getClass();
785          Statistics rep = reps.get(key);
786    
787          if (rep == null) {
788            reps.put(key, stat);
789            merged.add(stat);
790          } else {
791            rep.merge(stat);
792          }
793        }
794    
795        summaryOut.println("==== Summary Statistics ====");
796        fullOut.println("====== Merged Statistics ======");
797        for (Statistics stat : merged) {
798          stat.dumpSummary(summaryOut);
799          stat.dumpFull(fullOut);
800        }
801      }
802    
803      private static void usage() {
804        System.err.println("java -cp ... galois.runtime.GaloisRuntime [options] <main class> <args>*");
805        System.err.println(" -r <num runs>      : number of runs to use");
806        System.err.println(" -t <num threads>   : number of threads to use");
807        System.err.println(" -f <property file> : property file to read arguments from");
808        System.err.println(" -p                 : use ParaMeter");
809        System.err.println(" -s                 : use serial data structures and executor");
810        System.err.println(" -dr                : record execution for deterministic replay");
811        System.err.println(" -dp                : playback execution from deterministic replay");
812        System.err.println(" -g                 : enable additional statistics.");
813        System.err.println("                      Currently: stack profiling, processor utilization");
814        System.err.println(" --help             : print help");
815      }
816    
817      /**
818       * @param args
819       * @throws Exception 
820       */
821      public static void main(String[] args) throws Exception {
822        String main = null;
823        String[] mainArgs = new String[0];
824        boolean useParameter = false;
825        boolean useSerial = false;
826        boolean moreStats = false;
827        boolean ignoreUserFlags = false;
828        ReplayFeature.Type replayType = ReplayFeature.Type.NO;
829        int samplerInterval = 0;
830        int numThreads = 1;
831        int numRuns = 1;
832    
833        for (int i = 0; i < args.length; i++) {
834          String arg = args[i];
835          if (arg.equals("-r")) {
836            numRuns = Integer.parseInt(args[++i]);
837          } else if (arg.equals("-t")) {
838            numThreads = Integer.parseInt(args[++i]);
839          } else if (arg.equals("-p")) {
840            useParameter = true;
841          } else if (arg.equals("-f")) {
842            Properties p = new Properties(System.getProperties());
843            p.load(new FileInputStream(args[++i]));
844            System.setProperties(p);
845          } else if (arg.equals("--dr")) {
846            replayType = ReplayFeature.Type.RECORD;
847          } else if (arg.equals("-s")) {
848            useSerial = true;
849          } else if (arg.equals("--dp")) {
850            replayType = ReplayFeature.Type.PLAYBACK;
851            useSerial = true;
852          } else if (arg.equals("-g")) {
853            samplerInterval = 100;
854            moreStats = true;
855          } else if (arg.equals("-i")) {
856            ignoreUserFlags = true;
857          } else if (arg.equals("--help")) {
858            usage();
859            System.exit(1);
860          } else {
861            main = arg;
862            mainArgs = Arrays.asList(args).subList(i + 1, args.length).toArray(new String[args.length - i - 1]);
863            break;
864          }
865        }
866    
867        if (main == null) {
868          usage();
869          System.exit(1);
870        }
871    
872        if (useParameter) {
873          samplerInterval = 0;
874          numThreads = 1;
875          numRuns = 1;
876        }
877    
878        String defaultArgs = System.getProperty("args");
879        if (defaultArgs != null) {
880          if (mainArgs.length != 0) {
881            System.err.println("'args' property and commandline args both given");
882            System.exit(1);
883          }
884          mainArgs = defaultArgs.split("\\s+");
885        }
886    
887        // Run 
888        RuntimeStatistics stats = new RuntimeStatistics();
889        Launcher launcher = Launcher.getLauncher();
890    
891        for (int i = 0; i < numRuns; i++) {
892          if (i != 0) {
893            launcher.reset();
894          }
895    
896          initialize(numThreads, useParameter, useSerial, replayType, moreStats, ignoreUserFlags);
897          Features.initialize(getRuntime().getMaxIterations(), replayType);
898    
899          Sampler sampler = StackSampler.start(samplerInterval);
900          launcher.startTiming();
901          try {
902            Reflection.invokeStaticMethod(main, "main", new Object[] { mainArgs });
903            launcher.stopTiming();
904            launcher.addStats(sampler.stop());
905            long timeWithoutGc = launcher.elapsedTime(true);
906            long timeWithGc = launcher.elapsedTime(false);
907            if (logger.isLoggable(Level.INFO)) {
908              logger.info("Runtime (ms): " + timeWithGc + " (without GC: " + timeWithoutGc + ")");
909            }
910            stats.putStats(timeWithoutGc, timeWithGc);
911          } catch (Exception e) {
912            throw e;
913          }
914        }
915    
916        launcher.addStats(stats);
917        PrintStream out = new PrintStream("stats.txt");
918        dumpStatistics(launcher.getStatistics(), System.out, out);
919        out.close();
920      }
921    }