JNI库线程等待与Telegram JNI Lib同步调用getChatList()方法
Alright, let's tackle your two JNI synchronization problems—both are about bridging async operations to synchronous calls, which is a common pain point when working with native code and TDLib. Here's a step-by-step breakdown for each:
There are a few reliable ways to handle this, depending on whether you control the thread creation or are dealing with callback-based async tasks:
Option 1: Use pthread Native Sync (for threads you create)
If you spawn threads directly with pthread_create, the simplest approach is pthread_join to block until the thread finishes:
#include <pthread.h> void* worker_task(void* arg) { // Your thread's logic here (e.g., compute data, call APIs) return NULL; } JNIEXPORT void JNICALL Java_com_yourpackage_YourClass_waitForThread(JNIEnv* env, jobject thiz) { pthread_t worker_thread; pthread_create(&worker_thread, NULL, worker_task, NULL); // Block until the thread completes execution pthread_join(worker_thread, NULL); }
Option 2: Condition Variables (for callback-driven async tasks)
If you're dealing with async operations that notify completion via callbacks (like most SDKs), use a pthread condition variable + mutex to wait for the callback signal:
#include <pthread.h> // Global sync primitives (wrap in a struct for better encapsulation in production code) pthread_mutex_t task_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t task_cond = PTHREAD_COND_INITIALIZER; int task_is_completed = 0; // Callback triggered when the async task finishes void on_task_finished() { pthread_mutex_lock(&task_mutex); task_is_completed = 1; pthread_cond_signal(&task_cond); // Notify waiting thread pthread_mutex_unlock(&task_mutex); } JNIEXPORT void JNICALL Java_com_yourpackage_YourClass_waitForAsyncTask(JNIEnv* env, jobject thiz) { pthread_mutex_lock(&task_mutex); task_is_completed = 0; // Reset state start_async_task(); // Kick off the async operation that triggers on_task_finished() // Wait until the callback signals completion while (!task_is_completed) { pthread_cond_wait(&task_cond, &task_mutex); } pthread_mutex_unlock(&task_mutex); }
Option 3: Leverage Java's Synchronization Tools
If you have access to Java objects from JNI, using CountDownLatch is a clean way to align with Java's concurrency model:
JNIEXPORT void JNICALL Java_com_yourpackage_YourClass_waitWithLatch(JNIEnv* env, jobject thiz) { // Look up CountDownLatch class and constructor jclass latch_cls = (*env)->FindClass(env, "java/util/concurrent/CountDownLatch"); jmethodID latch_ctor = (*env)->GetMethodID(env, latch_cls, "<init>", "(I)V"); jobject latch = (*env)->NewObject(env, latch_cls, latch_ctor, 1); // Countdown from 1 // Pass the latch to your async task; task calls countDown() when done start_async_task_with_latch(latch); // Block until latch reaches 0 jmethodID await_method = (*env)->GetMethodID(env, latch_cls, "await", "()V"); (*env)->CallVoidMethod(env, latch, await_method); // Clean up local references (*env)->DeleteLocalRef(env, latch); (*env)->DeleteLocalRef(env, latch_cls); }
TDLib's API is inherently async, so your recursive callback approach needs a sync wrapper to wait until all chats are loaded. Here's how to refactor it:
Step 1: Set Up Sync Primitives
First, define thread-safe variables to track chat list progress:
#include <pthread.h> #include <td/telegram/Client.h> // Sync primitives pthread_mutex_t chat_list_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t chat_list_cond = PTHREAD_COND_INITIALIZER; int all_chats_loaded = 0; TdApi::Chats* full_chat_list = nullptr;
Step 2: Update TDLib Callback to Load All Chats
Modify your TDLib update handler to collect all chats and signal when done:
void handle_tdlib_update(TdApi::Object* update) { if (update->get_id() == TdApi::Chats::ID) { pthread_mutex_lock(&chat_list_mutex); TdApi::Chats* fetched_chats = static_cast<TdApi::Chats*>(update); // Initialize full chat list if first fetch if (!full_chat_list) { full_chat_list = new TdApi::Chats(); full_chat_list->totalCount = fetched_chats->totalCount; } // Merge fetched chat IDs into the full list full_chat_list->chatIds.insert( full_chat_list->chatIds.end(), fetched_chats->chatIds.begin(), fetched_chats->chatIds.end() ); // Check if we've loaded all chats if (full_chat_list->chatIds.size() >= full_chat_list->totalCount) { all_chats_loaded = 1; pthread_cond_signal(&chat_list_cond); // Notify waiting JNI method } else { // Fetch remaining chats (adjust limit as needed) auto next_get_chats = new TdApi::GetChats(); next_get_chats->offsetOrder = fetched_chats->chatIds.back(); next_get_chats->offsetChatId = 0; next_get_chats->limit = 100; td::Client::execute(next_get_chats); } pthread_mutex_unlock(&chat_list_mutex); } // Handle other updates (like errors) here to avoid hanging if (update->get_id() == TdApi::Error::ID) { pthread_mutex_lock(&chat_list_mutex); all_chats_loaded = 1; // Signal error to unblock waiting thread pthread_cond_signal(&chat_list_cond); pthread_mutex_unlock(&chat_list_mutex); } delete update; // Always clean up TDLib objects }
Step 3: Implement Synchronous getChatList() JNI Method
Now create the JNI method that triggers the fetch and waits for completion:
JNIEXPORT jobjectArray JNICALL Java_com_yourpackage_TelegramClient_getChatList(JNIEnv* env, jobject thiz) { pthread_mutex_lock(&chat_list_mutex); // Reset state for new request all_chats_loaded = 0; if (full_chat_list) { delete full_chat_list; full_chat_list = nullptr; } pthread_mutex_unlock(&chat_list_mutex); // Send initial GetChats request auto initial_get_chats = new TdApi::GetChats(); initial_get_chats->offsetOrder = 9223372036854775807LL; // Start with latest chats initial_get_chats->offsetChatId = 0; initial_get_chats->limit = 100; td::Client::execute(initial_get_chats); // Wait until all chats are loaded or error occurs pthread_mutex_lock(&chat_list_mutex); while (!all_chats_loaded) { pthread_cond_wait(&chat_list_cond, &chat_list_mutex); } // Convert chat IDs to Java long array jobjectArray chat_ids = nullptr; if (full_chat_list && !full_chat_list->chatIds.empty()) { chat_ids = (*env)->NewLongArray(env, full_chat_list->chatIds.size()); (*env)->SetLongArrayRegion( env, chat_ids, 0, full_chat_list->chatIds.size(), full_chat_list->chatIds.data() ); } // Clean up delete full_chat_list; full_chat_list = nullptr; all_chats_loaded = 0; pthread_mutex_unlock(&chat_list_mutex); return chat_ids; }
Key Notes for Production
- Error Handling: Add proper checks for TDLib errors (like network issues) to avoid infinite waits.
- Thread Safety: Always guard shared variables with mutexes—TDLib's callbacks run in a separate thread.
- Memory Management: Be diligent about deleting TDLib objects and JNI local references to prevent leaks.
内容的提问来源于stack exchange,提问作者Stanislav Zazhogin

