/*
* Copyright 2003-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jetbrains.mps.classloading;
import jetbrains.mps.module.ReloadableModule;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
/**
* @author apyshkin
* @since 31/12/16
*/
final class ModuleClassLoaderThreadSafetyTaskGenerator extends TaskGenerator {
private static final Logger LOG = LogManager.getLogger(ModuleClassLoaderThreadSafetyTaskGenerator.class);
private static final int nThreads = 4;
private static final long TIMEOUT_LOADING = 200;
private final CyclicBarrier barrier = new CyclicBarrier(2);
private final Deque<ModuleClassLoader> myQueue = new LinkedBlockingDeque<>();
@NotNull
private Callable<Object> classloaderCreator(FakeReloadableModule s1) {
return () -> {
try {
LOG.info("Creating classloader");
createCL(s1, FIRST.class, myQueue);
barrier.await();
} catch (VirtualMachineError e) {
throw e;
} catch (Throwable e) {
LOG.error("", e);
onError();
}
return null;
};
}
@NotNull
private Callable<Object> loadingTask() {
return () -> {
try {
barrier.await();
ModuleClassLoader cl = myQueue.pollFirst();
LOG.info("Loading class from classloader " + cl);
ExecutorService service = Executors.newFixedThreadPool(nThreads);
List<Callable<Object>> tasks = new ArrayList<>(nThreads);
for (int i = 0; i < nThreads; ++i) {
int threadNumber = i;
tasks.add(() -> {
LOG.info(String.format("%d-th thread loaded %s", threadNumber, cl.loadClass(FIRST.class.getName())));
return null;
});
}
service.invokeAll(tasks, TIMEOUT_LOADING, TimeUnit.MILLISECONDS);
service.shutdownNow();
} catch (VirtualMachineError e) {
throw e;
} catch (Throwable e) {
LOG.error("", e);
onError();
}
return null;
};
}
private ModuleClassLoader createCL(ReloadableModule module, Class<?> aClass, Deque<ModuleClassLoader> toAdd) {
ModuleClassLoader cl = new ModuleClassLoader(new ModuleClassLoaderSupport(module,
Collections::emptyList,
new FakeClassPathItem(aClass)));
toAdd.add(cl);
return cl;
}
@NotNull
@Override
public Collection<Callable<Object>> createTasks() {
FakeReloadableModule s1 = new FakeReloadableModule("FIRST");
Collection<Callable<Object>> taskList = new ArrayList<>(nThreads);
taskList.add(classloaderCreator(s1));
taskList.add(loadingTask());
return taskList;
}
// test data
private static final class FIRST {}
}