Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
112 commits
Select commit Hold shift + click to select a range
0140eff
Initial commit of plugin engine-datafusion
mch2 Jul 31, 2025
b7c5cfb
Fixed the build failures, added javadocs (#19398)
vinaykpud Sep 24, 2025
e700056
Add JNI layer and rust methods to execute substrait plan (#19399)
vinaykpud Sep 24, 2025
90d5676
Add License, make precommit checks work
vinaykpud Sep 24, 2025
073afec
Ensure precommit succeeds
vinaykpud Sep 24, 2025
2284dcd
Added Integration of Search with Datafusion plugin
vinaykpud Sep 25, 2025
1eaca09
Add extensions for csv codec
bharath-techie Aug 7, 2025
a1e4ae6
adding libs , data source plugin and data source aware plugin
bharath-techie Aug 15, 2025
a739841
search interface changes
bharath-techie Aug 25, 2025
e17bce1
Search and indexing engine integration changes - inprogesss commit
bharath-techie Sep 3, 2025
f2dc283
in-progress read engine / query phase abstractions
bharath-techie Sep 4, 2025
3508b17
Tying searcher and reader with rust
bharath-techie Sep 5, 2025
72e75f1
Add changes for searcher integration
bharath-techie Sep 25, 2025
17c2a2c
Fix datafusion rust
alchemist51 Sep 26, 2025
7a935b2
added global row id optimizer and tests for query phase
Sep 26, 2025
7698cf3
Feature/datafusion (#38)
bharath-techie Aug 19, 2025
947291d
Fixing tests
bharath-techie Sep 26, 2025
a146b3e
Fix Listing Cache
alchemist51 Sep 28, 2025
546ef45
Integrate aggregators to convert result from datafusion (#19441)
expani Sep 29, 2025
fffbc30
Changes in dataformat for CSVEngine
alchemist51 Sep 29, 2025
51eef8f
Working changes (#44)
alchemist51 Sep 29, 2025
3accadb
Changes to make plugin contexts work with source parse
bharath-techie Sep 29, 2025
86da8d6
Fixing end to end flow for pure aggregations (#19494)
expani Oct 3, 2025
a8ddfcb
Indexing integration
bharath-techie Oct 3, 2025
9f1eed6
removing CSV codec and integrating with parquet module
bharath-techie Oct 3, 2025
802b335
fixes for publishToMavenLocal
bharath-techie Oct 6, 2025
ca6283c
add readme
bharath-techie Oct 7, 2025
33fb888
Added support for average metric aggregation (#19559)
expani Oct 8, 2025
3e074dd
Fixing rust build for parquet-data-format (#19611)
raghuvanshraj Oct 13, 2025
d80272f
Commiter integration and build fixes (#19612)
bharath-techie Oct 13, 2025
c00d031
Uptading the implementation of ArrowRegistry and ParquetField class (…
darjisagar7 Oct 22, 2025
0541444
TermAggregation changes and handling average with projects, filters a…
expani Oct 23, 2025
c61849d
Adding Ip, TokenCount, Size, ScaledFloat, Binary and DocCount Parquet…
darjisagar7 Oct 24, 2025
5b6569c
Adding some base classes for Merge implementation (#19780)
dhwanilpatel Oct 27, 2025
0fc133f
override getMerger method in CompositeIndexingExecutionEngine (#19791)
Shailesh-Kumar-Singh Oct 28, 2025
bd20858
Added MergeScheduler/handler and TieredMergePolicy based on Parquet f…
dhwanilpatel Oct 29, 2025
129122d
Updating the FieldType to be nullable (#19807)
darjisagar7 Oct 29, 2025
69a73a4
Parquet Merge Implementation - Merge Rust Implementation + Interface …
Shailesh-Kumar-Singh Oct 29, 2025
9ede8b8
Integrating merge code with composite engine (#19812)
dhwanilpatel Oct 30, 2025
7642179
Adding RowId in the Composite Document Input (#19823)
darjisagar7 Oct 30, 2025
86b2b76
Removing files which got merged from catalog snapshot (#19822)
dhwanilpatel Oct 30, 2025
e02c8eb
Commenting out compile error and fixed a bug in Average Aggregation …
expani Oct 30, 2025
9e05822
Handling all numeric types for simple metric aggregations (#19834)
expani Oct 31, 2025
ce5e475
Fix for concurrent queue not removing writers while polling (#19836)
shank9918 Oct 31, 2025
c87fa23
return MergeResult [RowIdMapping + Merged parquet file metadata] afte…
Shailesh-Kumar-Singh Oct 31, 2025
c49cfa7
Fix: Adding the missing rowId generator file (#19838)
darjisagar7 Oct 31, 2025
e6b66e0
Enabling merges (#19839)
dhwanilpatel Oct 31, 2025
45d6b75
updating the row id name form _row_id to ___row_id (#19840)
darjisagar7 Oct 31, 2025
8fbbc41
Native memory leak fix + indexing perf regression fix + ZSTD change (…
rayshrey Oct 31, 2025
9989ff2
Update datafusion build script in jenkins (#19852)
peterzhuamazon Oct 31, 2025
656ec5b
local recovery via catalogsnapshot (#19841)
bharath-techie Oct 31, 2025
a1d3868
Dynamic index name (#19853)
alchemist51 Oct 31, 2025
347d0ca
Fix mod (#19855)
alchemist51 Oct 31, 2025
02fc4f6
Making terms agg work with string (#19874)
expani Nov 3, 2025
e2cf8b7
Add custom install.sh (#19876)
peterzhuamazon Nov 3, 2025
4b61351
cargo changes and logging
bharath-techie Nov 4, 2025
75ccc05
Add basic implementation of force merge API (#19881)
dhwanilpatel Nov 4, 2025
1b6d5d9
Adding clean target for parquet-data-format module (#19883)
raghuvanshraj Nov 4, 2025
c009515
Adding Metadata fields in the Parquet (#19886)
darjisagar7 Nov 4, 2025
f43a7d6
removing commons lang dependency
bharath-techie Nov 4, 2025
f691ca5
Remove scripts dir in feature/datafusion branch (#19877)
peterzhuamazon Nov 4, 2025
53f76ef
TermsAgg and CompositeAgg Response Handling (#19893)
expani Nov 5, 2025
869892b
Remove concurrency issue for background merge (#19895)
dhwanilpatel Nov 5, 2025
c1fd20d
Decoupling the merge from the refresh thread (#19879)
darjisagar7 Nov 5, 2025
f76c5da
fix server unit tests build
bharath-techie Nov 5, 2025
c6a42c2
[feature/datafusion] Removed special handling for populating doc_coun…
expani Nov 5, 2025
6cf2c40
Fixing merge policy issue (#19908)
dhwanilpatel Nov 6, 2025
5b626f5
Adding the support for the Merge Settings For ParquetTieredMergePolic…
darjisagar7 Nov 6, 2025
4987cb8
Handling NumericTermsAgg and MultiTermsAgg (#19911)
expani Nov 6, 2025
4ed75f4
Memory leak fix (#19925)
alchemist51 Nov 7, 2025
31308ab
absolute rowId and derived source integration for query_then_fetch (#…
animodak7 Nov 11, 2025
ac4466d
[Feature/datafusion] Add SingleNodeTestCase with clickbench queries (…
mch2 Nov 11, 2025
d0bade5
Translog manager & checkpoint tracker integration in CompositeEngine …
shank9918 Nov 11, 2025
2250240
DataFusionReaderManager UTs (#19910)
abhita Nov 11, 2025
73975ab
Remapping WriterFileSet path while recovering CatalogSnapshot from la…
shank9918 Nov 11, 2025
f3bc3bd
Feature/datafusion (#19972)
shank9918 Nov 12, 2025
459ead6
IndexingMemoryController Integration for VSRs and ArrowWriters for pa…
raghuvanshraj Nov 12, 2025
2ee604f
Ensuring Datafusion errors don't crash the JVM (#19977)
expani Nov 12, 2025
c1a285f
Add loggers to capture lifecycle and made cyclic loggers debug (#19980)
expani Nov 13, 2025
17d274f
Gracefully closing CompositeEngine and updating signature of loadWrit…
shank9918 Nov 13, 2025
9b49abb
Fixing the infinite loop issue and avoiding the merge if the file is …
darjisagar7 Nov 13, 2025
3816113
Formatting fix (#19994)
alchemist51 Nov 14, 2025
7b3234d
Add support for deletion of stale files (#19998)
rayshrey Nov 14, 2025
f439f7a
Restructure native handling and integrate with the Cleaner interface …
Bukhtawar Nov 17, 2025
a311a1b
Setting up JNI microbenchmarking for parquet-data-format module (#20018)
raghuvanshraj Nov 17, 2025
564dcde
Native library loader fix (#20020)
alchemist51 Nov 17, 2025
ede94a0
Add memory pool for datafusion (#19992)
alchemist51 Nov 17, 2025
2ca0f80
Aligned platform directory with build script (#20028)
expani Nov 17, 2025
46b7228
Changing path to match gradle native output dir (#20029)
expani Nov 17, 2025
a844c24
Add parquet benchmarks in missing javadoc (#20043)
alchemist51 Nov 18, 2025
42b1fe4
github action for datafusion e2e (#19760)
alchemist51 Nov 18, 2025
9fdfb35
Add CacheManager for DataFusion (#19645)
abhita Nov 18, 2025
1960e07
add stream and allocator closure post fetch phase (#20048)
mch2 Nov 19, 2025
9ce258a
Injecting count sub agg if absent to ensure doc_count is correct (#20…
expani Nov 19, 2025
9f94eef
Preserving order of Segment in CatalogSnapshot (#20045)
dhwanilpatel Nov 19, 2025
fba04d6
Remove unused rust native calls and interface methods (#20041)
alchemist51 Nov 19, 2025
a85f603
Support log byte merge policy (#20053)
dhwanilpatel Nov 19, 2025
b1a1d30
Close searcher on context close and fix unit tests. (#20051)
mch2 Nov 19, 2025
f5a7030
Fix path reference while adding to cache (#20052)
abhita Nov 20, 2025
e980a19
Don't create catalog snapshot in refresh if no changes in file (#20060)
dhwanilpatel Nov 21, 2025
bda4e93
Consume same writer generation count in merge and write (#20070)
dhwanilpatel Nov 21, 2025
863ac1d
CatalogSnapshotManager refactoring and integration of postDelete hook…
rayshrey Nov 21, 2025
20fe987
Handle LocalDateTime for min and max aggregator (#20074)
vinaykpud Nov 22, 2025
cf5fad4
partial aggs changes for distinct count (#20008)
sandeshkr419 Nov 24, 2025
09569ef
update e2e workflow to use sql plugin's ppl integ tests. (#20079)
mch2 Nov 24, 2025
6d744bf
disabling row ID optimizer (#20086)
mch2 Nov 24, 2025
c3a8415
[Feature/Datafusion] Changes for tokio io/cpu runtime + search flow a…
bharath-techie Nov 25, 2025
23e4717
Support debug mode for parquet and datafusion plugin (#20110)
alchemist51 Nov 27, 2025
a786542
Refactoring merge code to act at composite level instead of Parquet (…
dhwanilpatel Nov 28, 2025
ad3dd8b
Adding JNI abstractions for logging in rust through RustLoggerBridge …
raghuvanshraj Nov 28, 2025
3e0daa3
Refactor stream handling for DF execution (#20076)
Bukhtawar Nov 30, 2025
481409d
Enable Metadata Cache Complete Warm-up (#20117)
abhita Dec 1, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@
*.crt binary
*.p12 binary
*.ttf binary
*.parquet binary
*.txt text=auto
CHANGELOG.md merge=union
88 changes: 88 additions & 0 deletions .github/workflows/datafusion-e2e-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
name: DataFusion E2E Integration Test

on:
pull_request:
branches:
- feature/datafusion
push:
branches:
- feature/datafusion
workflow_dispatch:

jobs:
test:
runs-on: ubuntu-latest
env:
RUSTFLAGS: "-A unused_variables -A unused_mut"

steps:
- name: Checkout OpenSearch
uses: actions/checkout@v4
with:
path: OpenSearch

- name: Set up JDK
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '21'

- name: Setup Rust
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
components: rustfmt

- name: Install Protocol Buffers
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y protobuf-compiler

- name: Install Protocol Buffers
if: runner.os == 'macOS'
run: brew install protobuf

- name: Install Protocol Buffers
if: runner.os == 'Windows'
run: choco install protoc

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3

- name: Publish OpenSearch to Maven Local
working-directory: OpenSearch
run: ./gradlew publishToMavenLocal -x test -PrustDebug=true

- name: Checkout SQL Plugin
uses: actions/checkout@v4
with:
repository: vinaykpud/sql
ref: feature/substrait-plan
path: opensearch-sql

- name: Publish SQL Plugin to Maven Local
working-directory: opensearch-sql
run: ./gradlew publishToMavenLocal -x test

- name: Run DataFusionReaderManager Tests
working-directory: OpenSearch
run: ./gradlew :plugins:engine-datafusion:test --tests "org.opensearch.datafusion.DataFusionReaderManagerTests"

- name: Run IndexFileDeleter Tests
working-directory: OpenSearch
run: ./gradlew :server:test --tests "org.opensearch.index.engine.exec.coord.IndexFileDeleterTests"

- name: Run OpenSearch with DataFusion Plugin
working-directory: OpenSearch
run: |
./gradlew run \
--preserve-data \
-PremotePlugins="['org.opensearch.plugin:opensearch-job-scheduler:3.3.0.0-SNAPSHOT', 'org.opensearch.plugin:opensearch-sql-plugin:3.3.0.0-SNAPSHOT']" \
-PinstalledPlugins="['engine-datafusion']" &

# Wait for OpenSearch to start
timeout 300 bash -c 'until curl -s http://localhost:9200; do sleep 5; done'

- name: Run SQL CalcitePPLClickBenchIT
working-directory: opensearch-sql
run: ./gradlew :integ-test:integTest --tests "org.opensearch.sql.calcite.clickbench.CalcitePPLClickBenchIT" -Dtests.method="testDataFusion" -Dtests.cluster=localhost:9200 -Dtests.rest.cluster=localhost:9200 -DignorePrometheus=true -Dtests.clustername=opensearch -Dtests.output=true
18 changes: 18 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ CLAUDE.md
build-idea/
out/

modules/parquet-data-format/src/main/rust/target/*
libs/dataformat-csv/jni/target/*
libs/dataformat-csv/src/main/resources/*
plugins/dataformat-csv/src/main/resources/*
libs/dataformat-csv/jni/Cargo.lock

# include shared intellij config
!.idea/inspectionProfiles/Project_Default.xml
!.idea/runConfigurations/Debug_OpenSearch.xml
Expand Down Expand Up @@ -68,3 +74,15 @@ testfixtures_shared/

# build files generated
doc-tools/missing-doclet/bin/
/plugins/dataformat-csv/jni/target
/plugins/dataformat-csv/jni/Cargo.lock

/modules/parquet-data-format/src/main/rust/target
/modules/parquet-data-format/src/main/rust/debug
/modules/parquet-data-format/src/main/resources/native/
/modules/parquet-data-format/jni/target/debug
/modules/parquet-data-format/jni/target/.rustc_info.json

/modules/parquet-data-format/jni/target/release
**/Cargo.lock

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The pattern **/Cargo.lock will ignore all Cargo.lock files. Cargo.lock files are crucial for ensuring reproducible builds by locking dependency versions. They should typically be checked into version control for applications and binaries. Ignoring them can lead to different developers or CI environments using different dependency versions, which can introduce subtle bugs.

/modules/parquet-data-format/jni/
24 changes: 14 additions & 10 deletions .idea/runConfigurations/Debug_OpenSearch.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ public void beforeStart() {
firstNode.setting("discovery.seed_hosts", LOCALHOST_ADDRESS_PREFIX + DEFAULT_TRANSPORT_PORT);
cluster.setPreserveDataDir(preserveData);
for (OpenSearchNode node : cluster.getNodes()) {
// TODO : remove this - this disables assertions
node.jvmArgs(" -da ");
Comment on lines +171 to +172

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The code adds node.jvmArgs(" -da ");, which disables assertions for the test cluster nodes. Assertions are a valuable tool for catching bugs and incorrect assumptions during testing. Disabling them can hide potential issues. The TODO comment indicates this is temporary, but it's important to address it before merging.

Suggested change
// TODO : remove this - this disables assertions
node.jvmArgs(" -da ");
// TODO : remove this - this disables assertions

if (node != firstNode) {
node.setHttpPort(String.valueOf(httpPort));
httpPort++;
Expand Down
13 changes: 13 additions & 0 deletions distribution/src/config/jvm.options
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,19 @@ ${error.file}
21-:-javaagent:agent/opensearch-agent.jar
21-:--add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED

# Heap size settings
# -Xms32g
# -Xmx32g

# Enable native memory tracking
-XX:NativeMemoryTracking=summary

# Allow jcmd to attach to the process (required for 'jcmd VM.native_memory' commands)
-XX:+UnlockDiagnosticVMOptions

# Enabling debug logs for Allocators in Arrow
-Darrow.memory.debug.allocator=true

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The -Darrow.memory.debug.allocator=true option is being enabled by default. This is a debugging flag for Apache Arrow that can have a significant performance impact. It should not be enabled by default in a production configuration. Consider removing it or leaving it commented out with an explanation.


# For cases with high memory-mapped file counts, a lower value can improve stability and
# prevent issues like "leaked" maps or performance degradation. A value of 1 effectively
# disables the shared Arena pooling and uses a confined Arena for each MMapDirectory
Expand Down
4 changes: 4 additions & 0 deletions distribution/src/config/opensearch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,7 @@ ${path.logs}
# Once there is no observed impact on performance, this feature flag can be removed.
#
#opensearch.experimental.optimization.datetime_formatter_caching.enabled: false
#
#
# Limits the memory pool for datafusion which it uses for query execution.
#datafusion.search.memory_pool: 1GB
7 changes: 6 additions & 1 deletion gradle/missing-javadoc.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,12 @@ configure([
project(":test:fixtures:hdfs-fixture"),
project(":test:fixtures:s3-fixture"),
project(":test:framework"),
project(":test:logger-usage")
project(":test:logger-usage"),
project(":libs:opensearch-vectorized-exec-spi"), // TODO
project(":plugins:engine-datafusion"), //TODO
project(":server"),
project(":modules:parquet-data-format"),
project(":modules:parquet-data-format:benchmarks")
Comment on lines +164 to +168

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Several new projects are being excluded from Javadoc checks. While this might be acceptable during initial development, it's important to add Javadocs for public APIs before the feature is finalized to ensure maintainability. The TODO comments suggest this is temporary, which is good, but this should be tracked for completion.

]) {
project.tasks.withType(MissingJavadocTask) {
isExcluded = true
Expand Down
24 changes: 24 additions & 0 deletions gradle/run.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,30 @@ testClusters {
}
}
}

if (findProperty("remotePlugins")) {
remotePlugins = Eval.me(remotePlugins)
for (String coords : remotePlugins) {
if (coords.startsWith('/') || coords.startsWith('file:')) {
// Direct file path
plugin(project.layout.file(project.provider { new File(coords) }))
} else {
// Maven coordinates
def config = project.configurations.detachedConfiguration(
project.dependencies.create(coords + '@zip')
)
config.resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
project.repositories.mavenLocal()
project.repositories {
maven {
name = 'OpenSearch Snapshots'
url = 'https://central.sonatype.com/repository/maven-snapshots/'
}
}
plugin(project.layout.file(project.provider { config.singleFile }))
}
}
}
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,20 +85,20 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
Set.of(PublicApi.class, ExperimentalApi.class, DeprecatedApi.class)
);

for (var element : elements) {
validate(element);

if (!checkPackage(element)) {
continue;
}

// Skip all not-public elements
checkPublicVisibility(null, element);

if (element instanceof TypeElement) {
process((TypeElement) element);
}
}
// for (var element : elements) {
// validate(element);
//
// if (!checkPackage(element)) {
// continue;
// }
//
// // Skip all not-public elements
// checkPublicVisibility(null, element);
//
// if (element instanceof TypeElement) {
// process((TypeElement) element);
// }
// }
Comment on lines +88 to +101

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The entire processing loop for API annotations (@PublicApi, @ExperimentalApi, @DeprecatedApi) has been commented out. This effectively disables all API compatibility and visibility checks, which is critical for maintaining a stable public API for OpenSearch. This should be re-enabled and any resulting errors should be fixed.

Suggested change
// for (var element : elements) {
// validate(element);
//
// if (!checkPackage(element)) {
// continue;
// }
//
// // Skip all not-public elements
// checkPublicVisibility(null, element);
//
// if (element instanceof TypeElement) {
// process((TypeElement) element);
// }
// }
for (var element : elements) {
validate(element);
if (!checkPackage(element)) {
continue;
}
// Skip all not-public elements
checkPublicVisibility(null, element);
if (element instanceof TypeElement) {
process((TypeElement) element);
}
}


return false;
}
Expand Down
30 changes: 30 additions & 0 deletions libs/vectorized-exec-spi/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

apply plugin: 'opensearch.build'

description = 'Vectorized engine common interfaces for OpenSearch'

dependencies {
api project(':libs:opensearch-core')
api project(':libs:opensearch-common')

testImplementation(project(":test:framework")) {
exclude group: 'org.opensearch', module: 'vectorized-exec-spi'
}
}

tasks.named('forbiddenApisMain').configure {
replaceSignatureFiles 'jdk-signatures'
}

jarHell.enabled = false

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

jarHell.enabled = false disables the Jar Hell check, which detects classpath conflicts like duplicate classes or different versions of the same library. Disabling this is risky as it can lead to runtime errors (NoSuchMethodError, LinkageError, etc.) that are hard to debug. It's highly recommended to enable this check and resolve any dependency conflicts that are found.


test {
systemProperty 'tests.security.manager', 'false'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.vectorized.execution.jni;

import java.lang.ref.Cleaner;
import java.util.concurrent.atomic.AtomicBoolean;

/**
* Base class for type-safe native pointer wrappers.
* Provides automatic resource management and prevents use-after-close errors.
* Subclasses must implement {@link #doClose()} to release native resources.
* Cleaner is used to ensure resources are cleaned up even if the object is not explicitly closed.
*/
public abstract class NativeHandle implements AutoCloseable {

protected final long ptr;
private final AtomicBoolean closed = new AtomicBoolean(false);
protected static final long NULL_POINTER = 0L;
private final Cleaner.Cleanable cleanable;

private static final Cleaner CLEANER = Cleaner.create();

/**
* Creates a new native handle.
* @param ptr the native pointer (must not be 0)
* @throws IllegalArgumentException if ptr is 0
*/
protected NativeHandle(long ptr) {
if (ptr == NULL_POINTER) {
throw new IllegalArgumentException("Null native pointer");
}
this.ptr = ptr;
this.cleanable = CLEANER.register(this, new CleanupAction(ptr, this::doClose));
}

/**
* Ensures the handle is still open.
* @throws IllegalStateException if the handle has been closed
*/
public void ensureOpen() {
if (closed.get()) {
throw new IllegalStateException("Handle already closed");
}
}

/**
* Gets the native pointer value.
* @return the native pointer
* @throws IllegalStateException if the handle has been closed
*/
public long getPointer() {
ensureOpen();
return ptr;
}

@Override
public void close() {
if (closed.compareAndSet(false, true)) {
cleanable.clean();
}
}

/**
* Releases the native resource.
* Called once when the handle is closed.
* Subclasses must implement this to free native memory.
*/
protected abstract void doClose();

/**
* Cleans up the native resource.
* Called by the cleaner when the handle is garbage collected.
*/
private static final class CleanupAction implements Runnable {
private final long ptr;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The ptr field in the CleanupAction class is redundant. It is initialized in the constructor but never used within the run() method. The doClose runnable, when executed, will call the doClose() method on the NativeHandle instance, which already has access to its own ptr field. Removing this unused field will make the code cleaner.

Suggested change
private final long ptr;
private final Runnable doClose;

private final Runnable doClose;

CleanupAction(long ptr, Runnable doClose) {
this.ptr = ptr;
this.doClose = doClose;
}

@Override
public void run() {
doClose.run();
}
}
}
Loading
Loading