1818#include " server-chat.h"
1919#include " utils.hpp"
2020#include " jni_helpers.hpp"
21+ #include " log_helpers.hpp"
2122
2223#include < atomic>
2324#include < chrono>
@@ -102,6 +103,42 @@ jobject o_log_format_json = nullptr;
102103jobject o_log_format_text = nullptr ;
103104jobject o_log_callback = nullptr ;
104105
106+ // ---------------------------------------------------------------------------
107+ // JNI global-ref lifecycle tables
108+ //
109+ // Every entry here is promoted to a JNI global ref in JNI_OnLoad and released
110+ // in JNI_OnUnload. Keep these in sync with the declarations above; ordering
111+ // within each table only matters for the human reader.
112+ // ---------------------------------------------------------------------------
113+ static jclass *const g_global_class_refs[] = {
114+ &c_llama_model, &c_string, &c_hash_map, &c_map, &c_set,
115+ &c_entry, &c_iterator, &c_integer, &c_float, &c_biconsumer,
116+ &c_llama_error, &c_log_level, &c_log_format, &c_error_oom,
117+ };
118+
119+ static jobject *const g_global_object_refs[] = {
120+ &o_utf_8,
121+ &o_log_level_debug, &o_log_level_info, &o_log_level_warn, &o_log_level_error,
122+ &o_log_format_json, &o_log_format_text,
123+ };
124+
125+ // Maps every object that is fetched from a Java static field on load to the
126+ // (class, field) pair it should be looked up from.
127+ struct static_object_binding {
128+ jobject *target;
129+ jclass *cls;
130+ jfieldID *field;
131+ };
132+
133+ static const static_object_binding g_static_object_bindings[] = {
134+ {&o_log_level_debug, &c_log_level, &f_log_level_debug},
135+ {&o_log_level_info, &c_log_level, &f_log_level_info},
136+ {&o_log_level_warn, &c_log_level, &f_log_level_warn},
137+ {&o_log_level_error, &c_log_level, &f_log_level_error},
138+ {&o_log_format_json, &c_log_format, &f_log_format_json},
139+ {&o_log_format_text, &c_log_format, &f_log_format_text},
140+ };
141+
105142/* *
106143 * Returns the jllama_context wrapper for the Java LlamaModel object.
107144 * Used by the delete path and any method that needs jctx directly.
@@ -390,38 +427,14 @@ JNIEnv *get_jni_env() {
390427bool log_json;
391428std::function<void (ggml_log_level, const char *, void *)> log_callback;
392429
393- /* *
394- * Format a log message as JSON.
395- */
396- std::string format_log_as_json (ggml_log_level level, const char *text) {
397- std::string level_str;
398- switch (level) {
399- case GGML_LOG_LEVEL_ERROR :
400- level_str = " ERROR" ;
401- break ;
402- case GGML_LOG_LEVEL_WARN :
403- level_str = " WARN" ;
404- break ;
405- case GGML_LOG_LEVEL_INFO :
406- level_str = " INFO" ;
407- break ;
408- default :
409- case GGML_LOG_LEVEL_DEBUG :
410- level_str = " DEBUG" ;
411- break ;
412- }
413- nlohmann::json log_obj = {{" timestamp" , std::time (nullptr )}, {" level" , level_str}, {" message" , text}};
414- return log_obj.dump ();
415- }
416-
417430/* *
418431 * Invoke the log callback if there is any. When JSON mode is enabled,
419432 * the message is formatted as a JSON object before forwarding.
420433 */
421434void log_callback_trampoline (ggml_log_level level, const char *text, void *user_data) {
422435 if (log_callback != nullptr ) {
423436 if (log_json) {
424- std::string json_text = format_log_as_json (level, text);
437+ std::string json_text = format_log_as_json (level, text, std::time ( nullptr ) );
425438 log_callback (level, json_text.c_str (), user_data);
426439 } else {
427440 log_callback (level, text, user_data);
@@ -477,21 +490,12 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
477490 goto error;
478491 }
479492
480- // create references
481- c_llama_model = (jclass)env->NewGlobalRef (c_llama_model);
482- c_string = (jclass)env->NewGlobalRef (c_string);
483- c_hash_map = (jclass)env->NewGlobalRef (c_hash_map);
484- c_map = (jclass)env->NewGlobalRef (c_map);
485- c_set = (jclass)env->NewGlobalRef (c_set);
486- c_entry = (jclass)env->NewGlobalRef (c_entry);
487- c_iterator = (jclass)env->NewGlobalRef (c_iterator);
488- c_integer = (jclass)env->NewGlobalRef (c_integer);
489- c_float = (jclass)env->NewGlobalRef (c_float);
490- c_biconsumer = (jclass)env->NewGlobalRef (c_biconsumer);
491- c_llama_error = (jclass)env->NewGlobalRef (c_llama_error);
492- c_log_level = (jclass)env->NewGlobalRef (c_log_level);
493- c_log_format = (jclass)env->NewGlobalRef (c_log_format);
494- c_error_oom = (jclass)env->NewGlobalRef (c_error_oom);
493+ // Promote local class refs (from FindClass) to global refs in one pass.
494+ // c_standard_charsets is intentionally omitted: only used to look up
495+ // f_utf_8 in this function and never referenced again.
496+ for (jclass *p : g_global_class_refs) {
497+ *p = (jclass)env->NewGlobalRef (*p);
498+ }
495499
496500 // find constructors
497501 cc_hash_map = env->GetMethodID (c_hash_map, " <init>" , " ()V" );
@@ -536,25 +540,18 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
536540 }
537541
538542 o_utf_8 = env->NewStringUTF (" UTF-8" );
539- o_log_level_debug = env->GetStaticObjectField (c_log_level, f_log_level_debug);
540- o_log_level_info = env->GetStaticObjectField (c_log_level, f_log_level_info);
541- o_log_level_warn = env->GetStaticObjectField (c_log_level, f_log_level_warn);
542- o_log_level_error = env->GetStaticObjectField (c_log_level, f_log_level_error);
543- o_log_format_json = env->GetStaticObjectField (c_log_format, f_log_format_json);
544- o_log_format_text = env->GetStaticObjectField (c_log_format, f_log_format_text);
543+ for (const auto &b : g_static_object_bindings) {
544+ *b.target = env->GetStaticObjectField (*b.cls , *b.field );
545+ }
545546
546547 if (!(o_utf_8 && o_log_level_debug && o_log_level_info && o_log_level_warn && o_log_level_error &&
547548 o_log_format_json && o_log_format_text)) {
548549 goto error;
549550 }
550551
551- o_utf_8 = env->NewGlobalRef (o_utf_8);
552- o_log_level_debug = env->NewGlobalRef (o_log_level_debug);
553- o_log_level_info = env->NewGlobalRef (o_log_level_info);
554- o_log_level_warn = env->NewGlobalRef (o_log_level_warn);
555- o_log_level_error = env->NewGlobalRef (o_log_level_error);
556- o_log_format_json = env->NewGlobalRef (o_log_format_json);
557- o_log_format_text = env->NewGlobalRef (o_log_format_text);
552+ for (jobject *p : g_global_object_refs) {
553+ *p = env->NewGlobalRef (*p);
554+ }
558555
559556 if (env->ExceptionCheck ()) {
560557 env->ExceptionDescribe ();
@@ -587,28 +584,12 @@ JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) {
587584 return ;
588585 }
589586
590- env->DeleteGlobalRef (c_llama_model);
591- env->DeleteGlobalRef (c_string);
592- env->DeleteGlobalRef (c_hash_map);
593- env->DeleteGlobalRef (c_map);
594- env->DeleteGlobalRef (c_set);
595- env->DeleteGlobalRef (c_entry);
596- env->DeleteGlobalRef (c_iterator);
597- env->DeleteGlobalRef (c_integer);
598- env->DeleteGlobalRef (c_float);
599- env->DeleteGlobalRef (c_biconsumer);
600- env->DeleteGlobalRef (c_llama_error);
601- env->DeleteGlobalRef (c_log_level);
602- env->DeleteGlobalRef (c_log_format);
603- env->DeleteGlobalRef (c_error_oom);
604-
605- env->DeleteGlobalRef (o_utf_8);
606- env->DeleteGlobalRef (o_log_level_debug);
607- env->DeleteGlobalRef (o_log_level_info);
608- env->DeleteGlobalRef (o_log_level_warn);
609- env->DeleteGlobalRef (o_log_level_error);
610- env->DeleteGlobalRef (o_log_format_json);
611- env->DeleteGlobalRef (o_log_format_text);
587+ for (jclass *p : g_global_class_refs) {
588+ env->DeleteGlobalRef (*p);
589+ }
590+ for (jobject *p : g_global_object_refs) {
591+ env->DeleteGlobalRef (*p);
592+ }
612593
613594 if (o_log_callback != nullptr ) {
614595 env->DeleteGlobalRef (o_log_callback);
@@ -769,8 +750,7 @@ JNIEXPORT jint JNICALL Java_net_ladenthin_llama_LlamaModel_requestCompletion(JNI
769750
770751JNIEXPORT void JNICALL Java_net_ladenthin_llama_LlamaModel_releaseTask (JNIEnv *env, jobject obj, jint id_task) {
771752 REQUIRE_SERVER_CONTEXT ();
772- std::lock_guard<std::mutex> lk (jctx->readers_mutex );
773- jctx->readers .erase (id_task);
753+ erase_reader (jctx, id_task);
774754}
775755
776756JNIEXPORT jstring JNICALL Java_net_ladenthin_llama_LlamaModel_receiveCompletionJson (JNIEnv *env, jobject obj,
@@ -791,17 +771,15 @@ JNIEXPORT jstring JNICALL Java_net_ladenthin_llama_LlamaModel_receiveCompletionJ
791771 server_task_result_ptr result = rd->next ([] { return false ; });
792772
793773 if (!result_ok_or_throw (env, result)) {
794- std::lock_guard<std::mutex> lk (jctx->readers_mutex );
795- jctx->readers .erase (id_task);
774+ erase_reader (jctx, id_task);
796775 return nullptr ;
797776 }
798777
799778 json response = result->to_json ();
800779 response[" stop" ] = result->is_stop ();
801780
802781 if (result->is_stop ()) {
803- std::lock_guard<std::mutex> lk (jctx->readers_mutex );
804- jctx->readers .erase (id_task);
782+ erase_reader (jctx, id_task);
805783 }
806784
807785 return json_to_jstring_impl (env, response);
@@ -810,9 +788,7 @@ JNIEXPORT jstring JNICALL Java_net_ladenthin_llama_LlamaModel_receiveCompletionJ
810788JNIEXPORT jfloatArray JNICALL Java_net_ladenthin_llama_LlamaModel_embed (JNIEnv *env, jobject obj, jstring jprompt) {
811789 REQUIRE_SERVER_CONTEXT (nullptr );
812790
813- if (!jctx->params .embedding ) {
814- env->ThrowNew (c_llama_error,
815- " Model was not loaded with embedding support (see ModelParameters#setEmbedding(boolean))" );
791+ if (!require_embedding_support (env, jctx->params .embedding , c_llama_error)) {
816792 return nullptr ;
817793 }
818794
@@ -972,8 +948,7 @@ JNIEXPORT void JNICALL Java_net_ladenthin_llama_LlamaModel_delete(JNIEnv *env, j
972948
973949JNIEXPORT void JNICALL Java_net_ladenthin_llama_LlamaModel_cancelCompletion (JNIEnv *env, jobject obj, jint id_task) {
974950 REQUIRE_SERVER_CONTEXT ();
975- std::lock_guard<std::mutex> lk (jctx->readers_mutex );
976- jctx->readers .erase (id_task);
951+ erase_reader (jctx, id_task);
977952}
978953
979954JNIEXPORT void JNICALL Java_net_ladenthin_llama_LlamaModel_setLogger (JNIEnv *env, jclass clazz, jobject log_format,
@@ -1076,9 +1051,7 @@ JNIEXPORT jstring JNICALL Java_net_ladenthin_llama_LlamaModel_handleEmbeddings(J
10761051 jstring jparams, jboolean joaiCompat) {
10771052 REQUIRE_SERVER_CONTEXT (nullptr );
10781053
1079- if (!jctx->params .embedding ) {
1080- env->ThrowNew (c_llama_error,
1081- " Model was not loaded with embedding support (see ModelParameters#setEmbedding(boolean))" );
1054+ if (!require_embedding_support (env, jctx->params .embedding , c_llama_error)) {
10821055 return nullptr ;
10831056 }
10841057
0 commit comments