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.

Reply via email to