From 919848f0fe043e87d0514cb8a73e8d60b8b8e69d Mon Sep 17 00:00:00 2001 From: levlam Date: Sun, 11 Oct 2020 21:28:33 +0300 Subject: [PATCH] Use ClientManager for Java example interface implementation. GitOrigin-RevId: 4280b6407a1c1a18bf2a6e952f6761847b69cb83 --- example/java/org/drinkless/tdlib/Client.java | 209 +++++++------------ example/java/td_jni.cpp | 45 ++-- 2 files changed, 98 insertions(+), 156 deletions(-) diff --git a/example/java/org/drinkless/tdlib/Client.java b/example/java/org/drinkless/tdlib/Client.java index 986d5b3c7..d95196f17 100644 --- a/example/java/org/drinkless/tdlib/Client.java +++ b/example/java/org/drinkless/tdlib/Client.java @@ -8,14 +8,11 @@ package org.drinkless.tdlib; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; /** * Main class for interaction with the TDLib. */ -public final class Client implements Runnable { +public final class Client { /** * Interface for handler for results of queries to TDLib and incoming updates from TDLib. */ @@ -55,25 +52,11 @@ public final class Client implements Runnable { * @throws NullPointerException if query is null. */ public void send(TdApi.Function query, ResultHandler resultHandler, ExceptionHandler exceptionHandler) { - if (query == null) { - throw new NullPointerException("query is null"); - } - - readLock.lock(); - try { - if (isClientDestroyed) { - if (resultHandler != null) { - handleResult(new TdApi.Error(500, "Client is closed"), resultHandler, exceptionHandler); - } - return; - } - - long queryId = currentQueryId.incrementAndGet(); + long queryId = currentQueryId.incrementAndGet(); + if (resultHandler != null) { handlers.put(queryId, new Handler(resultHandler, exceptionHandler)); - nativeClientSend(nativeClientId, queryId, query); - } finally { - readLock.unlock(); } + nativeClientSend(nativeClientId, queryId, query); } /** @@ -97,22 +80,9 @@ public final class Client implements Runnable { * @throws NullPointerException if query is null. */ public static TdApi.Object execute(TdApi.Function query) { - if (query == null) { - throw new NullPointerException("query is null"); - } return nativeClientExecute(query); } - /** - * Overridden method from Runnable, do not call it directly. - */ - @Override - public void run() { - while (!stopFlag) { - receiveQueries(300.0 /*seconds*/); - } - } - /** * Creates new Client. * @@ -123,54 +93,79 @@ public final class Client implements Runnable { */ public static Client create(ResultHandler updateHandler, ExceptionHandler updateExceptionHandler, ExceptionHandler defaultExceptionHandler) { Client client = new Client(updateHandler, updateExceptionHandler, defaultExceptionHandler); - new Thread(client, "TDLib thread").start(); + synchronized (responseReceiver) { + if (!responseReceiver.isRun) { + responseReceiver.isRun = true; + + Thread receiverThread = new Thread(responseReceiver, "TDLib thread"); + receiverThread.setDaemon(true); + receiverThread.start(); + } + } return client; } - /** - * Closes Client. - */ - public void close() { - writeLock.lock(); - try { - if (isClientDestroyed) { - return; + private static class ResponseReceiver implements Runnable { + public boolean isRun = false; + + @Override + public void run() { + while (true) { + int resultN = nativeClientReceive(clientIds, eventIds, events, 100000.0 /*seconds*/); + for (int i = 0; i < resultN; i++) { + processResult(clientIds[i], eventIds[i], events[i]); + events[i] = null; + } } - if (!stopFlag) { - send(new TdApi.Close(), null); - } - isClientDestroyed = true; - while (!stopFlag) { - Thread.yield(); - } - while (!handlers.isEmpty()) { - receiveQueries(300.0); - } - updateHandlers.remove(nativeClientId); - defaultExceptionHandlers.remove(nativeClientId); - destroyNativeClient(nativeClientId); - } finally { - writeLock.unlock(); } + + private void processResult(int clientId, long id, TdApi.Object object) { + boolean isClosed = false; + if (id == 0 && object instanceof TdApi.UpdateAuthorizationState) { + TdApi.AuthorizationState authorizationState = ((TdApi.UpdateAuthorizationState) object).authorizationState; + if (authorizationState instanceof TdApi.AuthorizationStateClosed) { + isClosed = true; + } + } + + Handler handler = id == 0 ? updateHandlers.get(clientId) : handlers.remove(id); + if (handler != null) { + try { + handler.resultHandler.onResult(object); + } catch (Throwable cause) { + ExceptionHandler exceptionHandler = handler.exceptionHandler; + if (exceptionHandler == null) { + exceptionHandler = defaultExceptionHandlers.get(clientId); + } + if (exceptionHandler != null) { + try { + exceptionHandler.onException(cause); + } catch (Throwable ignored) { + } + } + } + } + + if (isClosed) { + updateHandlers.remove(clientId); // there will be no more updates + defaultExceptionHandlers.remove(clientId); // ignore further exceptions + } + } + + private static final int MAX_EVENTS = 1000; + private final int[] clientIds = new int[MAX_EVENTS]; + private final long[] eventIds = new long[MAX_EVENTS]; + private final TdApi.Object[] events = new TdApi.Object[MAX_EVENTS]; } - private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); - private final Lock readLock = readWriteLock.readLock(); - private final Lock writeLock = readWriteLock.writeLock(); + private final int nativeClientId; - private volatile boolean stopFlag = false; - private volatile boolean isClientDestroyed = false; - private final long nativeClientId; + private static final ConcurrentHashMap defaultExceptionHandlers = new ConcurrentHashMap(); + private static final ConcurrentHashMap updateHandlers = new ConcurrentHashMap(); + private static final ConcurrentHashMap handlers = new ConcurrentHashMap(); + private static final AtomicLong currentQueryId = new AtomicLong(); - private static final ConcurrentHashMap defaultExceptionHandlers = new ConcurrentHashMap(); - private static final ConcurrentHashMap updateHandlers = new ConcurrentHashMap(); - - private final ConcurrentHashMap handlers = new ConcurrentHashMap(); - private final AtomicLong currentQueryId = new AtomicLong(); - - private static final int MAX_EVENTS = 1000; - private final long[] eventIds = new long[MAX_EVENTS]; - private final TdApi.Object[] events = new TdApi.Object[MAX_EVENTS]; + private static final ResponseReceiver responseReceiver = new ResponseReceiver(); private static class Handler { final ResultHandler resultHandler; @@ -184,76 +179,24 @@ public final class Client implements Runnable { private Client(ResultHandler updateHandler, ExceptionHandler updateExceptionHandler, ExceptionHandler defaultExceptionHandler) { nativeClientId = createNativeClient(); - updateHandlers.put(nativeClientId, new Handler(updateHandler, updateExceptionHandler)); + if (updateHandler != null) { + updateHandlers.put(nativeClientId, new Handler(updateHandler, updateExceptionHandler)); + } if (defaultExceptionHandler != null) { - defaultExceptionHandlers.put(nativeClientId, defaultExceptionHandler); + defaultExceptionHandlers.put(nativeClientId, defaultExceptionHandler); } } @Override protected void finalize() throws Throwable { - try { - close(); - } finally { - super.finalize(); - } + send(new TdApi.Close(), null, null); } - private void processResult(long id, TdApi.Object object) { - if (object instanceof TdApi.UpdateAuthorizationState) { - if (((TdApi.UpdateAuthorizationState) object).authorizationState instanceof TdApi.AuthorizationStateClosed) { - stopFlag = true; - } - } - Handler handler; - if (id == 0) { - // update handler stays forever - handler = updateHandlers.get(nativeClientId); - } else { - handler = handlers.remove(id); - } - if (handler == null) { - return; - } + private static native int createNativeClient(); - handleResult(object, handler.resultHandler, handler.exceptionHandler); - } + private static native void nativeClientSend(int nativeClientId, long eventId, TdApi.Function function); - private void handleResult(TdApi.Object object, ResultHandler resultHandler, ExceptionHandler exceptionHandler) { - if (resultHandler == null) { - return; - } - - try { - resultHandler.onResult(object); - } catch (Throwable cause) { - if (exceptionHandler == null) { - exceptionHandler = defaultExceptionHandlers.get(nativeClientId); - } - if (exceptionHandler != null) { - try { - exceptionHandler.onException(cause); - } catch (Throwable ignored) { - } - } - } - } - - private void receiveQueries(double timeout) { - int resultN = nativeClientReceive(nativeClientId, eventIds, events, timeout); - for (int i = 0; i < resultN; i++) { - processResult(eventIds[i], events[i]); - events[i] = null; - } - } - - private static native long createNativeClient(); - - private static native void nativeClientSend(long nativeClientId, long eventId, TdApi.Function function); - - private static native int nativeClientReceive(long nativeClientId, long[] eventIds, TdApi.Object[] events, double timeout); + private static native int nativeClientReceive(int[] clientIds, long[] eventIds, TdApi.Object[] events, double timeout); private static native TdApi.Object nativeClientExecute(TdApi.Function function); - - private static native void destroyNativeClient(long nativeClientId); } diff --git a/example/java/td_jni.cpp b/example/java/td_jni.cpp index 6571c8c69..e0106e258 100644 --- a/example/java/td_jni.cpp +++ b/example/java/td_jni.cpp @@ -26,31 +26,35 @@ static td::td_api::object_ptr fetch_function(JNIEnv *env, return result; } -static td::Client *get_client(jlong client_id) { - return reinterpret_cast(static_cast(client_id)); +static td::ClientManager *get_manager() { + return td::ClientManager::get_manager_singleton(); } -static jlong Client_createNativeClient(JNIEnv *env, jclass clazz) { - return static_cast(reinterpret_cast(new td::Client())); +static jint Client_createNativeClient(JNIEnv *env, jclass clazz) { + return static_cast(get_manager()->create_client()); } -static void Client_nativeClientSend(JNIEnv *env, jclass clazz, jlong client_id, jlong id, jobject function) { - get_client(client_id)->send({static_cast(id), fetch_function(env, function)}); +static void Client_nativeClientSend(JNIEnv *env, jclass clazz, jint client_id, jlong id, jobject function) { + get_manager()->send(static_cast(client_id), static_cast(id), + fetch_function(env, function)); } -static jint Client_nativeClientReceive(JNIEnv *env, jclass clazz, jlong client_id, jlongArray ids, jobjectArray events, - jdouble timeout) { - auto client = get_client(client_id); - jsize events_size = env->GetArrayLength(ids); // ids and events size must be of equal size +static jint Client_nativeClientReceive(JNIEnv *env, jclass clazz, jintArray client_ids, jlongArray ids, + jobjectArray events, jdouble timeout) { + jsize events_size = env->GetArrayLength(ids); // client_ids, ids and events must be of equal size if (events_size == 0) { return 0; } jsize result_size = 0; - auto response = client->receive(timeout); + auto *manager = get_manager(); + auto response = manager->receive(timeout); while (response.object) { - jlong result_id = static_cast(response.id); - env->SetLongArrayRegion(ids, result_size, 1, &result_id); + jint client_id = static_cast(response.client_id); + env->SetIntArrayRegion(client_ids, result_size, 1, &client_id); + + jlong request_id = static_cast(response.request_id); + env->SetLongArrayRegion(ids, result_size, 1, &request_id); jobject object; response.object->store(env, object); @@ -62,21 +66,17 @@ static jint Client_nativeClientReceive(JNIEnv *env, jclass clazz, jlong client_i break; } - response = client->receive(0); + response = manager->receive(0); } return result_size; } static jobject Client_nativeClientExecute(JNIEnv *env, jclass clazz, jobject function) { jobject result; - td::Client::execute({0, fetch_function(env, function)}).object->store(env, result); + td::ClientManager::execute(fetch_function(env, function))->store(env, result); return result; } -static void Client_destroyNativeClient(JNIEnv *env, jclass clazz, jlong client_id) { - delete get_client(client_id); -} - static void Log_setVerbosityLevel(JNIEnv *env, jclass clazz, jint new_log_verbosity_level) { td::Log::set_verbosity_level(static_cast(new_log_verbosity_level)); } @@ -136,11 +136,10 @@ static jint register_native(JavaVM *vm) { #define TD_OBJECT "L" PACKAGE_NAME "/TdApi$Object;" #define TD_FUNCTION "L" PACKAGE_NAME "/TdApi$Function;" - register_method(client_class, "createNativeClient", "()J", Client_createNativeClient); - register_method(client_class, "nativeClientSend", "(JJ" TD_FUNCTION ")V", Client_nativeClientSend); - register_method(client_class, "nativeClientReceive", "(J[J[" TD_OBJECT "D)I", Client_nativeClientReceive); + register_method(client_class, "createNativeClient", "()I", Client_createNativeClient); + register_method(client_class, "nativeClientSend", "(IJ" TD_FUNCTION ")V", Client_nativeClientSend); + register_method(client_class, "nativeClientReceive", "([I[J[" TD_OBJECT "D)I", Client_nativeClientReceive); register_method(client_class, "nativeClientExecute", "(" TD_FUNCTION ")" TD_OBJECT, Client_nativeClientExecute); - register_method(client_class, "destroyNativeClient", "(J)V", Client_destroyNativeClient); register_method(log_class, "setVerbosityLevel", "(I)V", Log_setVerbosityLevel); register_method(log_class, "setFilePath", "(Ljava/lang/String;)Z", Log_setFilePath);