protected AddressSetView runAddressAnalysis(final Program program, final Set<Address> locations,
			final TaskMonitor monitor) throws CancelledException, InterruptedException, Exception {

		monitor.checkCancelled();

		final AddressSet analyzedSet = new AddressSet();
		if (locations.isEmpty()) {
			return analyzedSet;
		}

		GThreadPool pool = AutoAnalysisManager.getSharedAnalsysThreadPool();
		monitor.setMessage("Analyzing functions...");
		monitor.setMaximum(locations.size());

		QCallback<Address, AddressSetView> callback = new QCallback<Address, AddressSetView>() {
			@Override
			public AddressSetView process(Address loc, TaskMonitor taskMonitor) {
				synchronized (analyzedSet) {
					if (analyzedSet.contains(loc)) {
						taskMonitor.incrementProgress(1);
						return EMPTY_ADDRESS_SET;
					}
				}

				try {
					AddressSetView result = analyzeLocation(program, loc, null, taskMonitor);
					synchronized (analyzedSet) {
						analyzedSet.add(result);
					}

					taskMonitor.incrementProgress(1);
					return result;
				}
				catch (CancelledException e) {
					return null; // monitor was cancelled
				}
			}
		};

		// bound check thread limit
		if (maxThreadCount > pool.getMaxThreadCount() || maxThreadCount < 1) {
			maxThreadCount = 1;
		}

		// @formatter:off
		ConcurrentQ<Address, AddressSetView> queue = new ConcurrentQBuilder<Address, AddressSetView>()
			.setThreadPool(pool)
			.setMaxInProgress(maxThreadCount)
			.setMonitor(monitor)
			.build(callback);
		// @formatter:on

		queue.add(locations);

		queue.waitUntilDone();

		return analyzedSet;
	}
Run constant propagation starting at address in locations

@param program program
@param locations function entry points
@param monitor to cancel
@return set of addresses covered during constant analysis

@throws CancelledException
@throws InterruptedException
@throws Exception
Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/ConstantPropagationAnalyzer.java