class ThreadPool {

	public <WORK_ITEM> void consumeWork(Iterator<WORK_ITEM> workItemIterator,
			WorkerItemConsumerFactory<WORK_ITEM> workItemConsumerFactory, int numberOfThreads) {
		// Wrapping the iterator to avoid concurrency issues as iterators aren't ThreadSafe 
		Iterator<WORK_ITEM> threadSafeIterator = new Iterator<WORK_ITEM>() {
			@Override
			public boolean hasNext() {
				throw new RuntimeException("This method shouldn't be called");
			}

			@Override
			public WORK_ITEM next() {
				synchronized (this) {
					return workItemIterator.next();
				}
			}
		};
		
		Integer autoNumberOfThreads = getAutoNumberOfThreads();
		if (autoNumberOfThreads != null) {
			if(!isReentrantThread()) {
				// Forcing the number of threads to the required autoNumberOfThreads for the 
				// first Artefact using the ThreadPool (Level = 1)
				numberOfThreads = autoNumberOfThreads;
			} else {
				// Avoid parallelism for the artefacts that are children of an artefact 
				// already using the ThreadPool (Level > 1)
				numberOfThreads = 1;
			}
		}
		
		final BatchContext batchContext = new BatchContext(executionContext, numberOfThreads>1);
		
		WorkerController<WORK_ITEM> workerController = new WorkerController<>(batchContext);
		Consumer<WORK_ITEM> workItemConsumer = workItemConsumerFactory.createWorkItemConsumer(workerController);
		
		if(numberOfThreads == 1) {
			// No parallelism, run the worker in the current thread
			createWorkerAndRun(batchContext, workItemConsumer, threadSafeIterator, 0);
		} else {
			ReportNode currentReportNode = executionContext.getCurrentReportNode();
			List<Future<?>> futures = new ArrayList<>();
			long parentThreadId = Thread.currentThread().getId();
			// Create one worker for each "thread"
			for (int i = 0; i < numberOfThreads; i++) {
				int workerId = i;
				futures.add(executorService.submit(() -> {
					executionContext.associateThread(parentThreadId, currentReportNode);
					createWorkerAndRun(batchContext, workItemConsumer, threadSafeIterator, workerId);
				}));
			}
			
			// Wait for the workers to complete
			for (Future<?> future : futures) {
				try {
					future.get();
				} catch (InterruptedException | ExecutionException e) {
					logger.error("Error while waiting for worker execution to terminate. Execution ID: "+executionContext.getExecutionId(), e);
				}
			}
		}
	}

	public  ThreadPool(ExecutionContext context);

}