Correct a concurrency bug discovered by mibby. FIXES #39.

It's another lazy initialization problem. I only check a single 
field before initializing two related fields, which can cause 
problems when two threads execute handleLogin() concurrently. 

If thread A detects that PACKET_LOGIN_CLIENT is null, it updates both
it and LOGIN_GAME_PROFILE. However, thread B may only see the 
PACKET_LOGIN_CLIENT update, and still believe LOGIN_GAME_PROFILE is 
NULL. Hence why it causes a NullPointerException in issue #39.
This commit is contained in:
Kristian S. Stangeland 2014-02-03 01:38:52 +01:00
parent ea7b550bda
commit 9ef9475b61

View File

@ -320,15 +320,22 @@ class ChannelInjector extends ByteToMessageDecoder implements Injector {
* @param packet - the packet. * @param packet - the packet.
*/ */
protected void handleLogin(Class<?> packetClass, Object packet) { protected void handleLogin(Class<?> packetClass, Object packet) {
// Initialize packet class Class<?> loginClass = PACKET_LOGIN_CLIENT;
if (PACKET_LOGIN_CLIENT == null) { FieldAccessor loginClient = LOGIN_GAME_PROFILE;
PACKET_LOGIN_CLIENT = PacketType.Login.Client.START.getPacketClass();
LOGIN_GAME_PROFILE = Accessors.getFieldAccessor(PACKET_LOGIN_CLIENT, GameProfile.class, true); // Initialize packet class and login
if (loginClass == null) {
loginClass = PacketType.Login.Client.START.getPacketClass();
PACKET_LOGIN_CLIENT = loginClass;
}
if (loginClient == null) {
loginClient = Accessors.getFieldAccessor(PACKET_LOGIN_CLIENT, GameProfile.class, true);
LOGIN_GAME_PROFILE = loginClient;
} }
// See if we are dealing with the login packet // See if we are dealing with the login packet
if (PACKET_LOGIN_CLIENT.equals(packetClass)) { if (loginClass.equals(packetClass)) {
GameProfile profile = (GameProfile) LOGIN_GAME_PROFILE.get(packet); GameProfile profile = (GameProfile) loginClient.get(packet);
// Save the channel injector // Save the channel injector
factory.cacheInjector(profile.getName(), this); factory.cacheInjector(profile.getName(), this);