Performance improvements, better initialization
This commit is contained in:
parent
a6e075832f
commit
35bdc5653c
4
.gitignore
vendored
4
.gitignore
vendored
@ -95,3 +95,7 @@ parent/.classpath
|
||||
tdlight-java/target-snapshot/
|
||||
|
||||
tdlight-java-8/target-snapshot/
|
||||
/example-tdlight-session/data/db.sqlite
|
||||
/example-tdlight-session/data/db.sqlite-shm
|
||||
/example-tdlight-session/data/db.sqlite-wal
|
||||
/example-tdlight-session/data/td.binlog
|
||||
|
@ -6,11 +6,14 @@ import it.tdlight.jni.TdApi.Object;
|
||||
import it.tdlight.jni.TdApi.Update;
|
||||
import it.tdlight.util.CleanSupport;
|
||||
import it.tdlight.util.CleanSupport.CleanableSupport;
|
||||
import java.util.Map;
|
||||
import java.util.function.LongSupplier;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.slf4j.MarkerFactory;
|
||||
|
||||
class AutoCleaningTelegramClient implements TelegramClient {
|
||||
|
||||
private final TelegramClient client;
|
||||
private final InternalClient client;
|
||||
private volatile CleanableSupport cleanable;
|
||||
|
||||
AutoCleaningTelegramClient(InternalClientsState state) {
|
||||
@ -18,7 +21,12 @@ class AutoCleaningTelegramClient implements TelegramClient {
|
||||
}
|
||||
|
||||
public void onClientRegistered(int clientId, LongSupplier nextQueryIdSupplier) {
|
||||
Runnable shutDown = () -> NativeClientAccess.send(clientId, nextQueryIdSupplier.getAsLong(), new TdApi.Close());
|
||||
Runnable shutDown = () -> {
|
||||
Logger logger = LoggerFactory.getLogger(TelegramClient.class);
|
||||
logger.debug(MarkerFactory.getMarker("TG"), "The client is being shut down automatically");
|
||||
long reqId = nextQueryIdSupplier.getAsLong();
|
||||
NativeClientAccess.send(clientId, reqId, new TdApi.Close());
|
||||
};
|
||||
Thread shutdownHook = new Thread(shutDown);
|
||||
Runtime.getRuntime().addShutdownHook(shutdownHook);
|
||||
cleanable = CleanSupport.register(this, shutDown);
|
||||
|
@ -115,9 +115,9 @@ public class ClientFactory implements AutoCloseable {
|
||||
}
|
||||
|
||||
if (isClosed) {
|
||||
logger.trace("Removing Client {} from event handlers", clientId);
|
||||
logger.debug("Removing Client {} from event handlers", clientId);
|
||||
state.removeClientEventHandlers(clientId);
|
||||
logger.trace("Removed Client {} from event handlers", clientId);
|
||||
logger.debug("Removed Client {} from event handlers", clientId);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
package it.tdlight;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.LongSupplier;
|
||||
|
||||
public interface ClientRegistrationEventHandler {
|
||||
interface ClientRegistrationEventHandler {
|
||||
|
||||
void onClientRegistered(int clientId, LongSupplier nextQueryIdSupplier);
|
||||
}
|
||||
|
@ -42,38 +42,42 @@ public final class Init {
|
||||
*/
|
||||
public static void init() throws UnsupportedNativeLibraryException {
|
||||
if (!started) {
|
||||
boolean shouldStart = false;
|
||||
synchronized (Init.class) {
|
||||
if (!started) {
|
||||
Native.loadNativesInternal();
|
||||
ConstructorDetector.init();
|
||||
try {
|
||||
NativeClientAccess.execute(new SetLogVerbosityLevel(3));
|
||||
Log.setLogMessageHandler(3, (verbosityLevel, message) -> {
|
||||
switch (verbosityLevel) {
|
||||
case -1:
|
||||
case 0:
|
||||
case 1:
|
||||
LOG.error(message);
|
||||
break;
|
||||
case 2:
|
||||
LOG.warn(message);
|
||||
break;
|
||||
case 3:
|
||||
LOG.info(message);
|
||||
break;
|
||||
case 4:
|
||||
LOG.debug(message);
|
||||
break;
|
||||
default:
|
||||
LOG.trace(message);
|
||||
break;
|
||||
}
|
||||
});
|
||||
NativeClientAccess.execute(new SetLogStream(new LogStreamEmpty()));
|
||||
} catch (Throwable ex) {
|
||||
LOG.error("Can't set verbosity level on startup", ex);
|
||||
}
|
||||
started = true;
|
||||
shouldStart = true;
|
||||
}
|
||||
}
|
||||
if (shouldStart) {
|
||||
Native.loadNativesInternal();
|
||||
ConstructorDetector.init();
|
||||
try {
|
||||
NativeClientAccess.execute(new SetLogVerbosityLevel(3));
|
||||
Log.setLogMessageHandler(3, (verbosityLevel, message) -> {
|
||||
switch (verbosityLevel) {
|
||||
case -1:
|
||||
case 0:
|
||||
case 1:
|
||||
LOG.error(message);
|
||||
break;
|
||||
case 2:
|
||||
LOG.warn(message);
|
||||
break;
|
||||
case 3:
|
||||
LOG.info(message);
|
||||
break;
|
||||
case 4:
|
||||
LOG.debug(message);
|
||||
break;
|
||||
default:
|
||||
LOG.trace(message);
|
||||
break;
|
||||
}
|
||||
});
|
||||
NativeClientAccess.execute(new SetLogStream(new LogStreamEmpty()));
|
||||
} catch (Throwable ex) {
|
||||
LOG.error("Can't set verbosity level on startup", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,8 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.slf4j.Marker;
|
||||
@ -15,8 +17,8 @@ import org.slf4j.MarkerFactory;
|
||||
|
||||
final class InternalClient implements ClientEventsHandler, TelegramClient {
|
||||
|
||||
private static final Marker TG_MARKER = MarkerFactory.getMarker("TG");
|
||||
private static final Logger logger = LoggerFactory.getLogger(TelegramClient.class);
|
||||
static final Marker TG_MARKER = MarkerFactory.getMarker("TG");
|
||||
static final Logger logger = LoggerFactory.getLogger(TelegramClient.class);
|
||||
|
||||
private ClientRegistrationEventHandler clientRegistrationEventHandler;
|
||||
private final Map<Long, Handler<?>> handlers = new ConcurrentHashMap<>();
|
||||
@ -80,11 +82,11 @@ final class InternalClient implements ClientEventsHandler, TelegramClient {
|
||||
}
|
||||
|
||||
private void handleClose() {
|
||||
logger.trace(TG_MARKER, "Received close");
|
||||
logger.debug(TG_MARKER, "Received close");
|
||||
handlers.forEach((eventId, handler) ->
|
||||
handleResponse(eventId, new TdApi.Error(500, "Instance closed"), handler));
|
||||
handlers.clear();
|
||||
logger.info(TG_MARKER, "Client closed {}", clientId);
|
||||
logger.debug(TG_MARKER, "Client closed {}", clientId);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -98,7 +100,7 @@ final class InternalClient implements ClientEventsHandler, TelegramClient {
|
||||
handleException(handler.getExceptionHandler(), cause);
|
||||
}
|
||||
} else {
|
||||
logger.error(TG_MARKER, "Unknown event id \"{}\", the event has been dropped! {}", eventId, event);
|
||||
logger.trace(TG_MARKER, "Client {}, request event id is not registered \"{}\", the following response has been dropped. {}", clientId, eventId, event);
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,7 +108,7 @@ final class InternalClient implements ClientEventsHandler, TelegramClient {
|
||||
* Handles a response or an update
|
||||
*/
|
||||
private void handleEvent(long eventId, TdApi.Object event) {
|
||||
logger.trace(TG_MARKER, "Received response {}: {}", eventId, event);
|
||||
logger.trace(TG_MARKER, "Client {}, response received for request {}: {}", clientId, eventId, event);
|
||||
if (updatesHandler != null || updateHandler == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
@ -164,18 +166,23 @@ final class InternalClient implements ClientEventsHandler, TelegramClient {
|
||||
logger.info(TG_MARKER, "Registered new client {}", clientId);
|
||||
|
||||
// Send a dummy request to start TDLib
|
||||
this.send(new TdApi.GetOption("version"), (result) -> {}, ex -> {});
|
||||
logger.debug(TG_MARKER, "Sending dummy startup request as client {}", clientId);
|
||||
TdApi.Function<?> dummyRequest = new TdApi.GetOption("version");
|
||||
this.send(dummyRequest, null, null);
|
||||
// test Client.execute
|
||||
this.execute(new TdApi.GetTextEntities("@telegram /test_command https://telegram.org telegram.me @gif @test"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R extends TdApi.Object> void send(Function<R> query,
|
||||
ResultHandler<R> resultHandler,
|
||||
ExceptionHandler exceptionHandler) {
|
||||
logger.trace(TG_MARKER, "Trying to send {}", query);
|
||||
logger.trace(TG_MARKER, "Trying to send async request {}", query);
|
||||
|
||||
// Handle special requests
|
||||
TdApi.Object specialResult = tryHandleSpecial(query);
|
||||
if (specialResult != null) {
|
||||
logger.trace(TG_MARKER, "Handling special result for async request {}: {}", query, specialResult);
|
||||
if (resultHandler != null) {
|
||||
resultHandler.onResult(specialResult);
|
||||
}
|
||||
@ -191,11 +198,12 @@ final class InternalClient implements ClientEventsHandler, TelegramClient {
|
||||
|
||||
@Override
|
||||
public <R extends TdApi.Object> TdApi.Object execute(Function<R> query) {
|
||||
logger.trace(TG_MARKER, "Trying to execute {}", query);
|
||||
logger.trace(TG_MARKER, "Trying to execute sync request {}", query);
|
||||
|
||||
// Handle special requests
|
||||
TdApi.Object specialResult = tryHandleSpecial(query);
|
||||
if (specialResult != null) {
|
||||
logger.trace(TG_MARKER, "Handling special result for sync request {}: {}", query, specialResult);
|
||||
return specialResult;
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ package it.tdlight;
|
||||
|
||||
import io.atlassian.util.concurrent.CopyOnWriteMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
@ -13,11 +14,11 @@ public class InternalClientsState {
|
||||
static final int STATE_STOPPED = 4;
|
||||
private final AtomicInteger runState = new AtomicInteger();
|
||||
private final AtomicLong currentQueryId = new AtomicLong();
|
||||
private final Map<Integer, ClientEventsHandler> registeredClientEventHandlers = CopyOnWriteMap.newHashMap();
|
||||
private final Map<Integer, ClientEventsHandler> registeredClientEventHandlers = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
public long getNextQueryId() {
|
||||
return currentQueryId.updateAndGet(value -> (value >= Long.MAX_VALUE ? 0 : value) + 1);
|
||||
return currentQueryId.updateAndGet(value -> (value == Long.MAX_VALUE ? 0 : value) + 1);
|
||||
}
|
||||
|
||||
public void registerClient(int clientId, ClientEventsHandler internalClient) {
|
||||
|
@ -80,11 +80,11 @@ final class InternalReactiveClient implements ClientEventsHandler, ReactiveTeleg
|
||||
* This method will be called exactly once
|
||||
*/
|
||||
private void handleClose() {
|
||||
logger.trace(TG_MARKER, "Received close");
|
||||
logger.debug(TG_MARKER, "Received close");
|
||||
try {
|
||||
Runtime.getRuntime().removeShutdownHook(shutdownHook);
|
||||
} catch (IllegalStateException ignored) {
|
||||
logger.trace(TG_MARKER, "Can't remove shutdown hook because the JVM is already shutting down");
|
||||
logger.debug(TG_MARKER, "Can't remove shutdown hook because the JVM is already shutting down");
|
||||
}
|
||||
TdApi.Error instanceClosedError = new Error(500, "Instance closed");
|
||||
handlers.forEach((eventId, handler) -> this.handleResponse(eventId, instanceClosedError, handler));
|
||||
@ -186,7 +186,7 @@ final class InternalReactiveClient implements ClientEventsHandler, ReactiveTeleg
|
||||
subscriber.onNext(new TdApi.Ok());
|
||||
subscriber.onComplete();
|
||||
} else if (clientId == null) {
|
||||
logger.trace(TG_MARKER,
|
||||
logger.debug(TG_MARKER,
|
||||
"Can't send a request to TDLib before calling \"createAndRegisterClient\" function!"
|
||||
);
|
||||
subscriber.onError(new IllegalStateException(
|
||||
|
@ -15,9 +15,12 @@ import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
abstract class ResponseReceiver extends Thread implements AutoCloseable {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ResponseReceiver.class);
|
||||
private static final String FLAG_USE_OPTIMIZED_DISPATCHER = "tdlight.dispatcher.use_optimized_dispatcher";
|
||||
private static final boolean USE_OPTIMIZED_DISPATCHER
|
||||
= Boolean.parseBoolean(System.getProperty(FLAG_USE_OPTIMIZED_DISPATCHER, "true"));
|
||||
@ -59,6 +62,7 @@ abstract class ResponseReceiver extends Thread implements AutoCloseable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
LOG.debug("ResponseReceiver is now running");
|
||||
int[] sortIndex;
|
||||
final SimpleIntQueue closedClients = new SimpleIntQueue();
|
||||
try {
|
||||
@ -66,6 +70,7 @@ abstract class ResponseReceiver extends Thread implements AutoCloseable {
|
||||
while (!(interrupted = Thread.interrupted()) && registeredClients.length > 0) {
|
||||
// Timeout is expressed in seconds
|
||||
int resultsCount = receive(clientIds, eventIds, events, 2.0);
|
||||
LOG.trace("Received {} events", resultsCount);
|
||||
|
||||
if (resultsCount <= 0) {
|
||||
SpinWaitSupport.onSpinWait();
|
||||
@ -207,12 +212,15 @@ abstract class ResponseReceiver extends Thread implements AutoCloseable {
|
||||
}
|
||||
}
|
||||
|
||||
LOG.trace("ResponseReceiver will no longer process updates");
|
||||
|
||||
if (interrupted) {
|
||||
for (int clientId : registeredClients) {
|
||||
eventsHandler.handleClientEvents(clientId, true, clientEventIds, clientEvents, 0, 0);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
LOG.debug("ResponseReceiver stopped");
|
||||
this.closeWait.countDown();
|
||||
}
|
||||
}
|
||||
@ -266,6 +274,7 @@ abstract class ResponseReceiver extends Thread implements AutoCloseable {
|
||||
public void close() throws InterruptedException {
|
||||
this.closeWait.await();
|
||||
if (registeredClients.length == 0) {
|
||||
LOG.debug("Interrupting response receiver");
|
||||
ResponseReceiver.this.interrupt();
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ class SequentialRequestsExecutor implements Executor {
|
||||
|
||||
private final Executor executor = Executors.newSingleThreadExecutor(r -> {
|
||||
final Thread thread = new Thread(r);
|
||||
thread.setName("TDLight user input request");
|
||||
thread.setDaemon(true);
|
||||
return thread;
|
||||
});
|
||||
|
@ -7,7 +7,6 @@ import it.tdlight.Init;
|
||||
import it.tdlight.ResultHandler;
|
||||
import it.tdlight.TelegramClient;
|
||||
import it.tdlight.jni.TdApi.Update;
|
||||
import it.tdlight.util.MapUtils;
|
||||
import it.tdlight.util.UnsupportedNativeLibraryException;
|
||||
import it.tdlight.jni.TdApi;
|
||||
import it.tdlight.jni.TdApi.ChatListArchive;
|
||||
@ -17,7 +16,6 @@ import it.tdlight.jni.TdApi.User;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
@ -45,7 +43,7 @@ public final class SimpleTelegramClient implements Authenticable, MutableTelegra
|
||||
private final TelegramClient client;
|
||||
private ClientInteraction clientInteraction;
|
||||
private final TDLibSettings settings;
|
||||
private AuthenticationSupplier<?> authenticationData;
|
||||
private final AuthenticationSupplier<?> authenticationData;
|
||||
|
||||
private final CopyOnWriteMap<String, CopyOnWriteMap<CommandHandler, Void>> commandHandlers = CopyOnWriteMap.newHashMap();
|
||||
private final CopyOnWriteMap<ResultHandler<TdApi.Update>, Void> updateHandlers = CopyOnWriteMap.newHashMap();
|
||||
@ -64,7 +62,8 @@ public final class SimpleTelegramClient implements Authenticable, MutableTelegra
|
||||
Set<ResultHandler<Update>> updateHandlers,
|
||||
Set<ExceptionHandler> updateExceptionHandlers,
|
||||
Set<ExceptionHandler> defaultExceptionHandlers,
|
||||
ClientInteraction clientInteraction) {
|
||||
ClientInteraction clientInteraction,
|
||||
AuthenticationSupplier<?> authenticationData) {
|
||||
this.client = clientFactory.createClient();
|
||||
this.settings = Objects.requireNonNull(settings, "TDLight client settings are null");
|
||||
|
||||
@ -115,6 +114,9 @@ public final class SimpleTelegramClient implements Authenticable, MutableTelegra
|
||||
this.addUpdateHandler(TdApi.UpdateAuthorizationState.class,
|
||||
this.meGetter = new AuthorizationStateReadyGetMe(client, mainChatsLoader, archivedChatsLoader));
|
||||
this.addUpdateHandler(TdApi.UpdateNewMessage.class, new CommandsHandler(client, this.commandHandlers, this::getMe));
|
||||
this.authenticationData = authenticationData;
|
||||
createDirectories();
|
||||
client.initialize(this::handleUpdate, this::handleUpdateException, this::handleDefaultException);
|
||||
}
|
||||
|
||||
private void handleUpdate(TdApi.Object update) {
|
||||
@ -196,15 +198,6 @@ public final class SimpleTelegramClient implements Authenticable, MutableTelegra
|
||||
this.defaultExceptionHandlers.put(defaultExceptionHandlers, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the client
|
||||
*/
|
||||
public void start(AuthenticationSupplier<?> authenticationData) {
|
||||
this.authenticationData = authenticationData;
|
||||
createDirectories();
|
||||
client.initialize(this::handleUpdate, this::handleUpdateException, this::handleDefaultException);
|
||||
}
|
||||
|
||||
private void createDirectories() {
|
||||
try {
|
||||
if (Files.notExists(settings.getDatabaseDirectoryPath())) {
|
||||
|
@ -62,17 +62,16 @@ public final class SimpleTelegramClientBuilder implements MutableTelegramClient
|
||||
* Build and start the client
|
||||
* @return Telegram client
|
||||
*/
|
||||
public SimpleTelegramClient build(AuthenticationSupplier authenticationData) {
|
||||
SimpleTelegramClient client = new SimpleTelegramClient(clientManager,
|
||||
public SimpleTelegramClient build(AuthenticationSupplier<?> authenticationData) {
|
||||
return new SimpleTelegramClient(clientManager,
|
||||
clientSettings,
|
||||
commandHandlers,
|
||||
updateHandlers,
|
||||
updateExceptionHandlers,
|
||||
defaultExceptionHandlers,
|
||||
clientInteraction
|
||||
clientInteraction,
|
||||
authenticationData
|
||||
);
|
||||
client.start(authenticationData);
|
||||
return client;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ public final class Native {
|
||||
* Internal util
|
||||
*/
|
||||
public static void loadNativesInternal() throws UnsupportedNativeLibraryException {
|
||||
loadLibrary("tdlight");
|
||||
loadLibrary("tdjni");
|
||||
}
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(Native.class);
|
||||
|
Loading…
Reference in New Issue
Block a user