Jacques Le Roux wrote: > Hi Adam, > > I don't mind to revert this patch, but I think it's nice to have this > feature. > I suppose that what you call "multiple calls in series" are expressions > like (and yes it's not synchronized, my bad !) > >>> + for (Appender appender : appenders) { >>> + if (appender != null && >>> appender.getName().equals(appenderName)) { >>> + return appender; >>> + }
No, that's not what I mean. > Could we not replace Vector (bad I agree) by synchronizedMap and use > synchronization on the synchronized Map, like in > http://java.sun.com/j2se/1.4.2/docs/api/java/util/Collections.html#synchronizedList(java.util.List) Foo hasFoo() { synchronized (fooContainer) { } } void setFoo() { synchronized (fooContainer) { } } Foo getFoo() { if (!hasFoo()) setFoo(); return getFoo(); } This is the pattern that is in this patch, and it's a race condition. In addition, synchronizing on the internal Collection(the value in the map) isn't always worth it, if you are already inside a synchronized block on some other variable. > > > For the memorty leaks, I think removeAppenderFromThreadGroupMap would be > OK, isn'it ? > >> Integer.toString(), not "" + int. > is bad I agree > > Also here result is not genericized as it was "before" (but I guess the > patch was anterior too the genericization, and I did not spot that also, > maybe there are more I will be carefull on this aspect too) > >>> - Map<String, Object> result = >>> dispatcher.runSync(getServiceName(), getContext()); >>> + >>> + if (this.logLocation != null) { >>> + Debug >>> + .registerCurrentThreadGroupLogger(this.logLocation, >>> + appenderName); >>> + } >>> + Map result = dispatcher.runSync(getServiceName(), >>> getContext()); > > So, by usint also FlexibleLocation instead of File, don't you think we > could amend this "patch" instead of simply reverting it ? No, because there are issues with memory loss too, due to the reimplementation of what ThreadLocal actually accomplishes. What would be better, is to implement a singleton per-thread initialization and cleanup system. == void ThreadWorker.run() { try { doWork(); } finally { ThreadInit.runCleanup(); } } static <T> T ThreadInit.getInitObject(ThreadInit<T> init) { Map<Class<? extends ThreadInit>, InitData> initMap = tl.get(); if (initMap == null) { initMap = FastMap.newInstance(); tl.set(initMap); } InitData<T> initData = initMap.get(init.getClass()); if (initData != null) return initData.getValue(); T initValue = init.start(); initMap.put(init.getClass(), new InitData<T>(init, initValue)); return initValue; } static void ThreadInit.runCleanup() { Map<Class<? extends ThreadInit>, InitData<?>> initMap = tl.get(); if (initMap == null) return; for (InitData<?> initData: initMap.values()) { runCleanup(initData); } } static <T> void ThreadInit.runCleanup(InitData<T> initData) { initData.getInit().runCleanup(initData.getValue()); } == This allows any random code anywhere to have init code that runs *once* per thread invocation; it just requires that very early in the invocation chain, is a try/finally block, that runs the cleanup code. It's also possible to extend this slightly, so there is a concept of push/pop ThreadLocal state. You'll note there is *no* synchronization here. This is because the top-level variable that holds the object graph, is a ThreadLocal. Synchronization is only used for cross-thread control. ThreadLocal is local, and can't corrupt across threads.