I am trying to understand why a synchronized
block is used in this method from the vertx handlebars library io.vertx.ext.web.templ.handlebars.impl.HandlebarsTemplateEngineImpl class:
@Override
public void render(Map<String, Object> context, String templateFile, Handler<AsyncResult<Buffer>> handler) {
try {
int idx = templateFile.lastIndexOf('/');
String prefix = "";
String basename = templateFile;
if (idx != -1) {
prefix = templateFile.substring(0, idx);
basename = templateFile.substring(idx + 1);
}
Template template = isCachingEnabled() ? cache.get(templateFile) : null;
if (template == null) {
synchronized (this) {
loader.setPrefix(prefix);
// Strip leading slash from Utils##normalizePath
template = handlebars.compile(basename);
if (isCachingEnabled()) {
cache.put(templateFile, template);
}
}
}
Context engineContext = Context.newBuilder(context).resolver(getResolvers()).build();
handler.handle(Future.succeededFuture(Buffer.buffer(template.apply(engineContext))));
} catch (Exception ex) {
handler.handle(Future.failedFuture(ex));
}
}
Please explain it to me like I'm an idiot!
First, synchronization questions are never "idiot" questions.
I spent some time looking at this code too, and still not 100% sure it's totally correct.
The main reason to have
synchronized
block here is to protect the following two methods from executing out of order:You see, Handlebars has a reference to loader:
Possible scenario without sync block would be