private LocalInstance<AGGREGATOR, SAMPLE> getOrCreateLocal() { LocalInstance<AGGREGATOR, SAMPLE> current = local.get(); if (current == null) { current = new LocalInstance<>(updater); local.set(current); } return current; }
private void put(LocalInstance<AGGREGATOR, SAMPLE> q) { // Has to set registered before adding to the list. Otherwise, the // instance might be removed from the list, set as unregistered, and // then the local thread might happily remove that information. The Java // memory model is a guarantuee for the minimum amount of visibility, // not a definition of the actual amount. q.setRegistered(true); synchronized (directoryLock) { directory.add(q); } }
/** * Get a view of the current data. This requires this ThreadLocalDirectory * to have been instantiated with an updater implementing ObservableUpdater. * * @return a list of a copy of the current data in all producer threads * @throws IllegalStateException * if the updater does not implement {@link ObservableUpdater} */ public List<AGGREGATOR> view() { if (observableUpdater == null) { throw new IllegalStateException("Does not use observable updaters."); } List<LocalInstance<AGGREGATOR, SAMPLE>> current; List<AGGREGATOR> view; synchronized (directoryLock) { current = new ArrayList<>( directory); } view = new ArrayList<>(current.size()); for (LocalInstance<AGGREGATOR, SAMPLE> x : current) { view.add(x.copyCurrent(observableUpdater)); } return view; }
/** * Fetch the current set of sampled data, and reset state of all thread * local instances. The producer threads will not alter data in the list * returned from this method. * * @return a list of data from all producer threads */ public List<AGGREGATOR> fetch() { List<AGGREGATOR> contained; List<LocalInstance<AGGREGATOR, SAMPLE>> previous; int previousIntervalSize; synchronized (directoryLock) { previousIntervalSize = directory.size(); previous = directory; directory = new ArrayList<>( previousIntervalSize); } contained = new ArrayList<>(previousIntervalSize); // Yes, this is an inconsistence about when the registered state is // reset and when the thread local is removed from the list. // LocalInstance.isRegistered tells whether the data is available to // some consumer, not whether the LocalInstance is a member of the // directory. for (LocalInstance<AGGREGATOR, SAMPLE> x : previous) { contained.add(x.getAndReset(updater)); } return contained; }
/** * Update a value with a given thread local instance. * * <p> * If a producer thread is to insert a series of data, it is desirable to * limit the number of memory transactions to the theoretical minimum. Since * reading a thread local is the memory equivalence of reading a volatile, * it is then useful to avoid re-reading the running threads' input * instance. For this scenario, fetch the running thread's instance with * getLocalInstance(), and then insert the produced data with the multiple * calls necessary to update(SAMPLE, LocalInstance<AGGREGATOR, SAMPLE>). * </p> * * @param x * the data to insert * @param localInstance * the local data insertion instance */ public void update(SAMPLE x, LocalInstance<AGGREGATOR, SAMPLE> localInstance) { boolean isRegistered; isRegistered = localInstance.update(x, updater); if (!isRegistered) { put(localInstance); } }
AGGREGATOR getAndReset(Updater<AGGREGATOR, SAMPLE> updater) { AGGREGATOR previous; synchronized (lock) { previous = current; current = updater.createGenerationInstance(previous); setRegistered(false); } return previous; }