Repository navigation
Support running generated programs from multiple threads - #909
Open
yutaro-sakamoto wants to merge 11 commits into
Open
yutaro-sakamoto wants to merge 11 commits into
yutaro-sakamoto wants to merge 11 commits into
Conversation
codegen.c kept the inside_check/inside_stack bookkeeping from the original C code generator, but only the decrements survived the rewrite to Java code generation: nothing ever increments inside_check. On non-GCC builds (the MSVC-built cobj.exe) the counter goes negative as soon as a runtime check is attached to a reference (-debug with a variable subscript, reference modification or SEARCH ALL), and inside_stack[inside_check - 1] then writes out of bounds, crashing the compiler. GCC builds never compile these blocks, which is why Linux was unaffected. Remove the machinery entirely: the generated Java must not depend on the compiler that built cobj. Un-skip the Windows tests that were failing due to this crash. Fixes opensourcecobol#829, opensourcecobol#831, opensourcecobol#832, opensourcecobol#833, opensourcecobol#834 (sub-issues of opensourcecobol#828)
With -java-package the jar arguments were assembled as package_dir + file_path_delimitor + class, mixing the '/' separators from the package path with '\' on Windows (e.g. com/abc\prog.class), which produces malformed entry names in the archive. The jar tool accepts '/' everywhere, so join the paths with '/' unconditionally. The class file cleanup after archiving had the opposite problem: cmd.exe's del does not accept '/' separators. Build the remove paths with the native separator, let cd handle the output directory, and drop a leftover '#aaa' token that del would treat as an argument. Un-skip the Windows jar test. Fixes opensourcecobol#830 (sub-issue of opensourcecobol#828)
Removing the inside_check block exposed a variableScope finding for 'code' in joutput_stmt; declare it in the handler block where it is used.
Every program in a translation unit becomes its own .java/.class file named after the program ID, so two programs whose IDs differ only in letter case overwrite each other's files on a case-insensitive filesystem and the result silently misbehaves. On Windows, detect the collision before code generation and fail with an explicit error. The two affected tests stay skipped on Windows because the behavior they exercise is impossible there by design; a new Windows-only test verifies the diagnostic instead. Fixes opensourcecobol#835 (sub-issue of opensourcecobol#828)
On case-insensitive file systems programs whose PROGRAM-IDs differ only in letter case would generate class files with the same name; cobj now reports a compile error there, so document the restriction in the usage section.
* Use "cd /d" on Windows: cmd.exe's plain "cd" does not switch drives, so with -o pointing at another drive the jar and del commands would run against the wrong directory * Replace the unbounded strcpy into package_dir_native with snprintf and give package_name_to_path an explicit output bound, so an overlong -java-package argument can no longer overflow the buffers
Generated programs and libcobj assumed one COBOL run unit per JVM: the runtime kept the module stack, exception codes, CALL cache, work areas, open files, EXTERNAL items and ESQL sessions in static fields, and STOP RUN called System.exit. Running programs from several threads of one JVM, as Tomcat or Spring Boot request threads do, therefore corrupted their state or stopped the server. - Keep the run-unit state of libcobj per thread (ThreadLocal); read the process-wide configuration once in cob_init (CobolUtil.ensureInitialized) and release a thread's state with CobolRunUnit.end(). - STOP RUN unwinds to the root program of the run unit and returns; only the generated main() exits the process. - Threads of one JVM share sequential, line sequential and relative files under the same rules as separate processes (JvmFileLockRegistry: the lock sits on a registry channel beyond EOF, and channels are never closed while the JVM holds the lock). INDEXED files record a lock owner per run unit, so record locks work between threads. - The ESQL backend is created per thread; connections left open are rolled back and closed when the run unit ends. - One DISPLAY statement is written atomically. - Replace the static Calendar behind FUNCTION CURRENT-DATE with an immutable snapshot taken in cob_init, and stop localtime() from rewriting the shared COB_DATE value. - Add the multithread and esql-multithread test suites, JUnit tests and a Spring Boot smoke test job, and document the model in doc/multithreading.md. Programs compiled by an earlier cobj must be recompiled: the generated code now calls accessor methods instead of the removed public static fields.
The Gradle wrapper of libcobj on develop (8.2.1) cannot build Spring Boot 3.4, which failed the smoke test job while configuring the project. Set up Gradle 8.14.3 for the sample in CI and document the requirement.
setup-gradle extracts the Gradle distribution with unzip, which the ubuntu:24.04 container does not have.
yutaro-sakamoto
marked this pull request as ready for review
September 30, 2026 11:04
Contributor
Author
|
@h-himo @n-isaka @tsh-hashimoto @kio-watanabe また明日以降、このリポジトリに新しくPRを出したり、問い合わせに対応したりできるかは、私の立場上不透明です。 |
Contributor
Author
|
@h-himo @n-isaka @tsh-hashimoto @kio-watanabe |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
概要
変換後のJavaプログラムと
libcobjを、TomcatやSpring Bootのようなマルチスレッド環境で使えるようにします。これまでは「1つのJVM = 1つのCOBOL実行単位」を前提としていたため、ランタイムの状態がstaticフィールドにあり、複数のスレッドから同時にCOBOLプログラムを実行すると状態が混線し、STOP RUNはSystem.exitでサーバごと終了していました。変更点
ThreadLocal化しました。プロセス共通の設定はcob_initで1回だけ読み込みます(CobolUtil.ensureInitialized())。CobolRunUnit.end()でスレッドの実行単位を終了し、状態を解放できます。System.exitせず、実行単位のルートのプログラムまで巻き戻して正常に復帰します。プロセスを終了するのは生成されたmain()だけです。JvmFileLockRegistry)。OSのロックは台帳のチャネル上、ファイル終端のはるか先の範囲に保持するので、保持者のCLOSEでロックが途切れず、Windowsの必須ロックで自分のI/Oが妨げられることもありません。Linuxではファイルのどのチャネルを閉じてもJVMのロックが外れるため、ロック保持中はチャネルを閉じずに預かり、次のOPENで再利用します。INDEXEDファイルはロック所有者を実行単位ごとに記録します。Calendarを、cob_init時点の不変のスナップショットに置き換えました(値は従来と同じです)。tests/multithread、tests/esql-multithread、JUnitテスト、Spring Bootの実機スモークテスト(500並列リクエスト)を追加しました。doc/multithreading.md(日英)ほか。互換性
cobjで変換したプログラムは、変換し直す必要があります。生成コードが参照していた公開staticフィールド(CobolRuntimeException.codeなど)を、スレッドごとの状態にアクセスするメソッドに置き換えたためです。補足
developにはINDEXEDファイルのUNLOCKがまだ実装されていないため、indexed-record-lock.atはUNLOCKを使わず、別のレコードの読み込みやCLOSEでロックを解放する流れにしています。tests/Makefile.inは、この環境でautomakeが動かないため、Makefile.amの変更を同じ形式で反映しています(差分は該当行のみです)。Summary
This PR makes the generated Java programs and
libcobjusable from several threads of one JVM, such as the request threads of Tomcat or Spring Boot. Until now the runtime assumed one COBOL run unit per JVM: its state lived in static fields, so programs running concurrently corrupted each other's state, and STOP RUN calledSystem.exit, stopping the whole server.Changes
ThreadLocals. Process-wide configuration is read once incob_init(CobolUtil.ensureInitialized()).CobolRunUnit.end()ends the run unit of a thread and releases its state.System.exit: it unwinds to the root program of the run unit and returns normally. Only the generatedmain()exits the process.JvmFileLockRegistry). The OS lock is held on a registry-owned channel over a range far beyond EOF, so a holder's CLOSE does not drop it, and the mandatory locks of Windows do not block the holders' own I/O. On Linux, closing any channel of a file drops all the JVM's locks on it, so the registry parks those channels while the lock is held and reuses them for the next OPEN. INDEXED files record the lock owner per run unit.Calendarbehind FUNCTION CURRENT-DATE is replaced with an immutable snapshot taken incob_init; the returned values are unchanged.tests/multithreadandtests/esql-multithreadsuites, JUnit tests, and a Spring Boot smoke test job that sends 500 concurrent requests.doc/multithreading.md(English and Japanese) and related documents.Compatibility
cobjmust be recompiled: the generated code now calls accessor methods for the per-thread state instead of the public static fields it used to read (such asCobolRuntimeException.code).Notes
developdoes not implementUNLOCKfor INDEXED files yet, soindexed-record-lock.atreleases record locks by reading another record or closing the file instead of usingUNLOCK.tests/Makefile.inmirrors theMakefile.amchanges in the same format, because automake does not run in this environment; the diff is limited to the affected lines.