From: Jérémie Galarneau Date: Tue, 25 Feb 2014 21:32:05 +0000 (-0500) Subject: Fix: Unsynchronized access in LTTngTCPSessiondClient X-Git-Tag: v2.4.0~9 X-Git-Url: https://git.lttng.org./?a=commitdiff_plain;h=e614d916bcf4093bbf98d302b9e37ed990dc7304;p=lttng-ust.git Fix: Unsynchronized access in LTTngTCPSessiondClient enabledEventList is shared between the LTTngThread and eventTimer threads but is not synchronized. This patch changes enabledEventList's type from an ArrayList to a synchronized HashSet. Signed-off-by: Jérémie Galarneau Signed-off-by: Mathieu Desnoyers --- diff --git a/liblttng-ust-jul/org/lttng/ust/jul/LTTngTCPSessiondClient.java b/liblttng-ust-jul/org/lttng/ust/jul/LTTngTCPSessiondClient.java index 25d1cb2e..35f768f3 100644 --- a/liblttng-ust-jul/org/lttng/ust/jul/LTTngTCPSessiondClient.java +++ b/liblttng-ust-jul/org/lttng/ust/jul/LTTngTCPSessiondClient.java @@ -30,10 +30,14 @@ import java.net.*; import java.lang.management.ManagementFactory; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; import java.util.List; +import java.util.Set; import java.util.Timer; import java.util.TimerTask; import java.util.logging.Logger; +import java.util.Collections; class USTRegisterMsg { public static int pid; @@ -57,7 +61,8 @@ public class LTTngTCPSessiondClient { private Semaphore registerSem; private Timer eventTimer; - private List enabledEventList = new ArrayList(); + private Set enabledEventSet = + Collections.synchronizedSet(new HashSet()); /* * Map of Logger objects that have been enabled. They are indexed by name. */ @@ -82,65 +87,81 @@ public class LTTngTCPSessiondClient { this.eventTimer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { - /* - * We have to make a copy here since it is possible that the - * enabled event list is changed during an iteration on it. - */ - List tmpList = new ArrayList(enabledEventList); - - LTTngSessiondCmd2_4.sessiond_enable_handler enableCmd = new - LTTngSessiondCmd2_4.sessiond_enable_handler(); - for (LTTngEvent event: tmpList) { - int ret; - Logger logger; - + synchronized (enabledEventSet) { + LTTngSessiondCmd2_4.sessiond_enable_handler enableCmd = new + LTTngSessiondCmd2_4.sessiond_enable_handler(); /* - * Check if this Logger name has been enabled already. Note - * that in the case of "*", it's never added in that hash - * table thus the enable command does a lookup for each - * logger name in that hash table for the * case in order - * to make sure we don't enable twice the same logger - * because JUL apparently accepts that the *same* - * LogHandler can be added twice on a Logger object... - * don't ask... + * Modifying events in a Set will raise a + * ConcurrentModificationException. Thus, we remove an event + * and add its modified version to modifiedEvents when a + * modification is necessary. */ - logger = enabledLoggers.get(event.name); - if (logger != null) { - continue; - } + Set modifiedEvents = new HashSet(); + Iterator it = enabledEventSet.iterator(); - /* - * Set to one means that the enable all event has been seen - * thus event from that point on must use loglevel for all - * events. Else the object has its own loglevel. - */ - if (handler.logLevelUseAll == 1) { - event.logLevel.level = handler.logLevelAll; - event.logLevel.type = handler.logLevelTypeAll; - } + while (it.hasNext()) { + int ret; + Logger logger; + LTTngEvent event = it.next(); - /* - * The all event is a special case since we have to iterate - * over every Logger to see which one was not enabled. - */ - if (event.name.equals("*")) { - enableCmd.name = event.name; - enableCmd.lttngLogLevel = event.logLevel.level; - enableCmd.lttngLogLevelType = event.logLevel.type; /* - * The return value is irrelevant since the * event is - * always kept in the list. + * Check if this Logger name has been enabled already. Note + * that in the case of "*", it's never added in that hash + * table thus the enable command does a lookup for each + * logger name in that hash table for the * case in order + * to make sure we don't enable twice the same logger + * because JUL apparently accepts that the *same* + * LogHandler can be added twice on a Logger object... + * don't ask... */ - enableCmd.execute(handler, enabledLoggers); - continue; - } + logger = enabledLoggers.get(event.name); + if (logger != null) { + continue; + } - ret = enableCmd.enableLogger(handler, event, enabledLoggers); - if (ret == 1) { - /* Enabled so remove the event from the list. */ - enabledEventList.remove(event); + /* + * Set to one means that the enable all event has been seen + * thus event from that point on must use loglevel for all + * events. Else the object has its own loglevel. + */ + if (handler.logLevelUseAll == 1) { + it.remove(); + event.logLevel.level = handler.logLevelAll; + event.logLevel.type = handler.logLevelTypeAll; + modifiedEvents.add(event); + } + + /* + * The all event is a special case since we have to iterate + * over every Logger to see which one was not enabled. + */ + if (event.name.equals("*")) { + enableCmd.name = event.name; + enableCmd.lttngLogLevel = event.logLevel.level; + enableCmd.lttngLogLevelType = event.logLevel.type; + /* + * The return value is irrelevant since the * event is + * always kept in the set. + */ + enableCmd.execute(handler, enabledLoggers); + continue; + } + + ret = enableCmd.enableLogger(handler, event, enabledLoggers); + if (ret == 1) { + /* Enabled so remove the event from the set. */ + if (!modifiedEvents.remove(event)) { + /* + * event can only be present in one of + * the sets. + */ + it.remove(); + } + } } + enabledEventSet.addAll(modifiedEvents); } + } }, this.timerDelay, this.timerDelay); @@ -274,12 +295,10 @@ public class LTTngTCPSessiondClient { event = enableCmd.execute(this.handler, this.enabledLoggers); if (event != null) { /* - * Add the event to the list so it can be enabled if + * Add the event to the set so it can be enabled if * the logger appears at some point in time. */ - if (enabledEventList.contains(event) == false) { - enabledEventList.add(event); - } + enabledEventSet.add(event); } data = enableCmd.getBytes(); break;