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<SensorEvent> 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); }