Add locks to prevent reading updates when destroying the client

This commit is contained in:
Andrea Cavalli 2020-09-07 15:21:57 +02:00
parent 4d909e986e
commit e92c04098f

View File

@ -22,6 +22,8 @@ public class Client extends NativeClient implements TelegramClient {
private ClientState state = ClientState.of(false, 0, false, false, false); private ClientState state = ClientState.of(false, 0, false, false, false);
private final ReentrantReadWriteLock stateLock = new ReentrantReadWriteLock(); private final ReentrantReadWriteLock stateLock = new ReentrantReadWriteLock();
private final ReentrantReadWriteLock updatesLock = new ReentrantReadWriteLock();
private final ReentrantReadWriteLock responsesLock = new ReentrantReadWriteLock();
/** /**
* Creates a new TDLib client. * Creates a new TDLib client.
@ -53,25 +55,35 @@ public class Client extends NativeClient implements TelegramClient {
@Override @Override
public List<Response> receive(double timeout, int eventsSize, boolean receiveResponses, boolean receiveUpdates) { public List<Response> receive(double timeout, int eventsSize, boolean receiveResponses, boolean receiveUpdates) {
long clientId; if (receiveResponses) responsesLock.readLock().lock();
stateLock.readLock().lock();
try { try {
if (!state.isInitialized()) { if (receiveUpdates) updatesLock.readLock().lock();
sleep(timeout); try {
return Collections.emptyList(); long clientId;
} stateLock.readLock().lock();
requireInitialized(); try {
if (!state.isReadyToReceive()) { if (!state.isInitialized()) {
sleep(timeout); sleep(timeout);
return Collections.emptyList(); return Collections.emptyList();
} }
requireReadyToReceive(); requireInitialized();
clientId = state.getClientId(); if (!state.isReadyToReceive()) {
} finally { sleep(timeout);
stateLock.readLock().unlock(); return Collections.emptyList();
} }
requireReadyToReceive();
clientId = state.getClientId();
} finally {
stateLock.readLock().unlock();
}
return Arrays.asList(this.internalReceive(clientId, timeout, eventsSize, receiveResponses, receiveUpdates)); return Arrays.asList(this.internalReceive(clientId, timeout, eventsSize, receiveResponses, receiveUpdates));
} finally {
if (receiveUpdates) updatesLock.readLock().unlock();
}
} finally {
if (receiveResponses) responsesLock.readLock().unlock();
}
} }
private void sleep(double timeout) { private void sleep(double timeout) {
@ -87,31 +99,41 @@ public class Client extends NativeClient implements TelegramClient {
@Override @Override
public Response receive(double timeout, boolean receiveResponses, boolean receiveUpdates) { public Response receive(double timeout, boolean receiveResponses, boolean receiveUpdates) {
long clientId; responsesLock.readLock().lock();
stateLock.readLock().lock();
try { try {
if (!state.isInitialized()) { updatesLock.readLock().lock();
sleep(timeout); try {
long clientId;
stateLock.readLock().lock();
try {
if (!state.isInitialized()) {
sleep(timeout);
return null;
}
requireInitialized();
if (!state.isReadyToReceive()) {
sleep(timeout);
return null;
}
requireReadyToReceive();
clientId = state.getClientId();
} finally {
stateLock.readLock().unlock();
}
Response[] responses = this.internalReceive(clientId, timeout, 1, receiveResponses, receiveUpdates);
if (responses.length > 0) {
return responses[0];
}
return null; return null;
} finally {
updatesLock.readLock().unlock();
} }
requireInitialized();
if (!state.isReadyToReceive()) {
sleep(timeout);
return null;
}
requireReadyToReceive();
clientId = state.getClientId();
} finally { } finally {
stateLock.readLock().unlock(); responsesLock.readLock().unlock();
} }
Response[] responses = this.internalReceive(clientId, timeout, 1, receiveResponses, receiveUpdates);
if (responses.length > 0) {
return responses[0];
}
return null;
} }
private Response[] internalReceive(long clientId, double timeout, int eventsSize, boolean receiveResponses, boolean receiveUpdates) { private Response[] internalReceive(long clientId, double timeout, int eventsSize, boolean receiveResponses, boolean receiveUpdates) {
@ -187,30 +209,50 @@ public class Client extends NativeClient implements TelegramClient {
@Override @Override
public void destroyClient() { public void destroyClient() {
stateLock.writeLock().lock(); responsesLock.writeLock().lock();
try { try {
if (state.isInitialized() && state.hasClientId()) { updatesLock.writeLock().lock();
if (state.isReadyToSend() || state.isReadyToReceive()) { try {
throw new IllegalStateException("You need to close the Client before destroying it!"); stateLock.writeLock().lock();
try {
if (state.isInitialized() && state.hasClientId()) {
if (state.isReadyToSend() || state.isReadyToReceive()) {
throw new IllegalStateException("You need to close the Client before destroying it!");
}
destroyNativeClient(this.state.getClientId());
state = ClientState.of(false, 0, false, false, false);
}
} finally {
stateLock.writeLock().unlock();
} }
destroyNativeClient(this.state.getClientId()); } finally {
state = ClientState.of(false, 0, false, false, false); updatesLock.writeLock().unlock();
} }
} finally { } finally {
stateLock.writeLock().unlock(); responsesLock.writeLock().unlock();
} }
} }
@Override @Override
public void initializeClient() { public void initializeClient() {
stateLock.writeLock().lock(); responsesLock.writeLock().lock();
try { try {
if (!state.isInitialized() && !state.hasClientId()) { updatesLock.writeLock().lock();
long clientId = createNativeClient(); try {
state = ClientState.of(true, clientId, true, true, false); stateLock.writeLock().lock();
try {
if (!state.isInitialized() && !state.hasClientId()) {
long clientId = createNativeClient();
state = ClientState.of(true, clientId, true, true, false);
}
} finally {
stateLock.writeLock().unlock();
}
} finally {
updatesLock.writeLock().unlock();
} }
} finally { } finally {
stateLock.writeLock().unlock(); responsesLock.writeLock().unlock();
} }
} }