public void setTaskSchedulerForTag(Object tag, Class<? extends TaskScheduler> scheduler) {
   synchronized (schedulerByTag) {
     TaskScheduler old = getTaskSchedulerForTag(tag);
     if (old != null) {
       if (scheduler.isAssignableFrom(old.getClass())) {
         /* already have such an instance */
         return;
       }
       // might support multiple in future...
       throw new IllegalStateException(
           "Not allowed to set multiple TaskSchedulers on ExecutionManager tag (tag "
               + tag
               + ", has "
               + old
               + ", setting new "
               + scheduler
               + ")");
     }
     try {
       TaskScheduler schedulerI = scheduler.newInstance();
       // allow scheduler to have a nice name, for logging etc
       if (schedulerI instanceof CanSetName) ((CanSetName) schedulerI).setName("" + tag);
       setTaskSchedulerForTag(tag, schedulerI);
     } catch (InstantiationException e) {
       throw Throwables.propagate(e);
     } catch (IllegalAccessException e) {
       throw Throwables.propagate(e);
     }
   }
 }
  /**
   * This implementation handles the following flags, in addition to those described in the {@link
   * SubscriptionManager} interface:
   *
   * <ul>
   *   <li>subscriberExecutionManagerTag - a tag to pass to execution manager (without setting any
   *       execution semantics / TaskPreprocessor); if not supplied and there is a subscriber, this
   *       will be inferred from the subscriber and set up with SingleThreadedScheduler (supply this
   *       flag with value null to prevent any task preprocessor from being set)
   *   <li>eventFilter - a Predicate&lt;SensorEvent&gt; instance to filter what events are delivered
   * </ul>
   *
   * @see SubscriptionManager#subscribe(Map, Entity, Sensor, SensorEventListener)
   */
  public synchronized <T> SubscriptionHandle subscribe(
      Map<String, Object> flags,
      Entity producer,
      Sensor<T> sensor,
      SensorEventListener<? super T> listener) {
    Subscription s = new Subscription(producer, sensor, listener);
    s.subscriber = flags.containsKey("subscriber") ? flags.remove("subscriber") : listener;
    if (flags.containsKey("subscriberExecutionManagerTag")) {
      s.subscriberExecutionManagerTag = flags.remove("subscriberExecutionManagerTag");
      s.subscriberExecutionManagerTagSupplied = true;
    } else {
      s.subscriberExecutionManagerTag =
          s.subscriber instanceof Entity
              ? "subscription-delivery-entity-"
                  + ((Entity) s.subscriber).getId()
                  + "["
                  + s.subscriber
                  + "]"
              : s.subscriber instanceof String
                  ? "subscription-delivery-string[" + s.subscriber + "]"
                  : s != null ? "subscription-delivery-object[" + s.subscriber + "]" : null;
      s.subscriberExecutionManagerTagSupplied = false;
    }
    s.eventFilter = (Predicate) flags.remove("eventFilter");
    s.flags = flags;

    if (LOG.isDebugEnabled())
      LOG.debug(
          "Creating subscription {} for {} on {} {} in {}",
          new Object[] {s, s.subscriber, producer, sensor, this});
    allSubscriptions.put(s.id, s);
    LanguageUtils.addToMapOfSets(
        subscriptionsByToken, makeEntitySensorToken(s.producer, s.sensor), s);
    if (s.subscriber != null) {
      LanguageUtils.addToMapOfSets(subscriptionsBySubscriber, s.subscriber, s);
    }
    if (!s.subscriberExecutionManagerTagSupplied && s.subscriberExecutionManagerTag != null) {
      ((BasicExecutionManager) em)
          .setTaskSchedulerForTag(s.subscriberExecutionManagerTag, SingleThreadedScheduler.class);
    }
    return s;
  }
  /**
   * Unsubscribe the given subscription id.
   *
   * @see #subscribe(Map, Entity, Sensor, SensorEventListener)
   */
  public synchronized boolean unsubscribe(SubscriptionHandle sh) {
    if (!(sh instanceof Subscription))
      throw new IllegalArgumentException(
          "Only subscription handles of type Subscription supported: sh="
              + sh
              + "; type="
              + (sh != null ? sh.getClass().getCanonicalName() : null));
    Subscription s = (Subscription) sh;
    boolean b1 = allSubscriptions.remove(s.id) != null;
    boolean b2 =
        LanguageUtils.removeFromMapOfCollections(
            subscriptionsByToken, makeEntitySensorToken(s.producer, s.sensor), s);
    assert b1 == b2;
    if (s.subscriber != null) {
      boolean b3 =
          LanguageUtils.removeFromMapOfCollections(subscriptionsBySubscriber, s.subscriber, s);
      assert b3 == b2;
    }

    // TODO Requires code review: why did we previously do exactly same check twice in a row (with
    // no synchronization in between)?
    if ((subscriptionsBySubscriber.size() == 0
            || !groovyTruth(subscriptionsBySubscriber.get(s.subscriber)))
        && !s.subscriberExecutionManagerTagSupplied
        && s.subscriberExecutionManagerTag != null) {
      // if subscriber has gone away forget about his task; but check in synch block to ensure
      // setTaskPreprocessor call above will win in any race
      if ((subscriptionsBySubscriber.size() == 0
          || !groovyTruth(subscriptionsBySubscriber.get(s.subscriber))))
        ((BasicExecutionManager) em).clearTaskPreprocessorForTag(s.subscriberExecutionManagerTag);
    }

    // FIXME ALEX - this seems wrong
    ((BasicExecutionManager) em)
        .setTaskSchedulerForTag(s.subscriberExecutionManagerTag, SingleThreadedScheduler.class);
    return b1;
  }
  @Test(groups = {"Integration", "Acceptance"})
  public void testExecuteWithSingleThreadedScheduler() throws Exception {
    double minRatePerSec = 1000 * PERFORMANCE_EXPECTATION;

    executionManager.setTaskSchedulerForTag("singlethreaded", SingleThreadedScheduler.class);

    final AtomicInteger concurrentCallCount = new AtomicInteger();
    final AtomicInteger submitCount = new AtomicInteger();
    final AtomicInteger counter = new AtomicInteger();
    final CountDownLatch completionLatch = new CountDownLatch(1);
    final List<Exception> exceptions = Lists.newCopyOnWriteArrayList();

    final Runnable work =
        new Runnable() {
          public void run() {
            int numConcurrentCalls = concurrentCallCount.incrementAndGet();
            try {
              if (numConcurrentCalls > 1)
                throw new IllegalStateException("numConcurrentCalls=" + numConcurrentCalls);
              int val = counter.incrementAndGet();
              if (val >= numIterations) completionLatch.countDown();
            } catch (Exception e) {
              exceptions.add(e);
              LOG.warn("Exception in runnable of testExecuteWithSingleThreadedScheduler", e);
              throw Exceptions.propagate(e);
            } finally {
              concurrentCallCount.decrementAndGet();
            }
          }
        };

    measureAndAssert(
        "testExecuteWithSingleThreadedScheduler",
        numIterations,
        minRatePerSec,
        new Runnable() {
          public void run() {
            while (submitCount.get() > counter.get() + 5000) {
              LOG.info(
                  "delaying because "
                      + submitCount.get()
                      + " submitted and only "
                      + counter.get()
                      + " run");
              Time.sleep(500);
            }
            executionManager.submit(
                MutableMap.of("tags", ImmutableList.of("singlethreaded")), work);
            submitCount.incrementAndGet();
          }
        },
        new Runnable() {
          public void run() {
            try {
              completionLatch.await(LONG_TIMEOUT_MS, TimeUnit.MILLISECONDS);
            } catch (InterruptedException e) {
              throw Exceptions.propagate(e);
            }
            assertTrue(completionLatch.getCount() <= 0);
          }
        });

    if (exceptions.size() > 0) throw exceptions.get(0);
  }