Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,16 @@
<artifactId>log4j-core</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>5.16.0</version>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna-platform</artifactId>
<version>5.16.0</version>
</dependency>
</dependencies>

<profiles>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* BSD 2-Clause License
*
* Copyright (c) 2023, Swat.engineering
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package engineering.swat.watch.impl.mac;

import java.nio.file.WatchEvent;
import java.nio.file.WatchEvent.Kind;

import org.checkerframework.checker.nullness.qual.Nullable;

/**
* <p>
* Handler for native events, intended to be used in a {@link NativeEventStream}
* callback to construct {@link WatchEvent}s (and propagate them for downstream
* consumption).
* </p>
*
* <p>
* In each invocation, the types of {@code kind} and {@code context} depend
* specifically on the given native event: they're {@code Kind<Path>} and
* {@code Path} for non-overflows, but they're {@code Kind<Object>} and
* {@code Object} for overflows. This precision is needed to construct
* {@link WatchEvent}s, where the types of {@code kind} and {@code context} need
* to be correlated. Note: {@link java.util.function.BiConsumer} doesn't give
* the required precision (i.e., its type parameters are initialized only once
* for all invocations).
* </p>
*/
@FunctionalInterface
interface NativeEventHandler {
<T> void handle(Kind<T> kind, @Nullable T context);
}
285 changes: 285 additions & 0 deletions src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
/*
* BSD 2-Clause License
*
* Copyright (c) 2023, Swat.engineering
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package engineering.swat.watch.impl.mac;

import static engineering.swat.watch.impl.mac.apis.FileSystemEvents.FSEventStreamCreateFlag.FILE_EVENTS;
import static engineering.swat.watch.impl.mac.apis.FileSystemEvents.FSEventStreamCreateFlag.NO_DEFER;
import static engineering.swat.watch.impl.mac.apis.FileSystemEvents.FSEventStreamCreateFlag.WATCH_ROOT;
import static engineering.swat.watch.impl.mac.apis.FileSystemEvents.FSEventStreamEventFlag.ITEM_CREATED;
import static engineering.swat.watch.impl.mac.apis.FileSystemEvents.FSEventStreamEventFlag.ITEM_INODE_META_MOD;
import static engineering.swat.watch.impl.mac.apis.FileSystemEvents.FSEventStreamEventFlag.ITEM_MODIFIED;
import static engineering.swat.watch.impl.mac.apis.FileSystemEvents.FSEventStreamEventFlag.ITEM_REMOVED;
import static engineering.swat.watch.impl.mac.apis.FileSystemEvents.FSEventStreamEventFlag.MUST_SCAN_SUB_DIRS;
import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
import static java.nio.file.StandardWatchEventKinds.OVERFLOW;

import java.io.Closeable;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Arrays;

import org.checkerframework.checker.nullness.qual.Nullable;

import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.platform.mac.CoreFoundation;
import com.sun.jna.platform.mac.CoreFoundation.CFArrayRef;
import com.sun.jna.platform.mac.CoreFoundation.CFIndex;
import com.sun.jna.platform.mac.CoreFoundation.CFStringRef;

import engineering.swat.watch.impl.mac.apis.DispatchObjects;
import engineering.swat.watch.impl.mac.apis.DispatchQueue;
import engineering.swat.watch.impl.mac.apis.FileSystemEvents;
import engineering.swat.watch.impl.mac.apis.FileSystemEvents.FSEventStreamCallback;

// Note: This file is designed to be the only place in this package where JNA is
// used and/or the native APIs are invoked. If the need to do so arises outside
// this file, consider extending this file to offer the required services
// without exposing JNA and/or the native APIs.

/**
* <p>
* Stream of native events for a path, issued by macOS. It's a facade-like
* object that hides the low-level native APIs behind a higher-level interface.
* </p>
*
* <p>
* Note: Methods {@link #open()} and {@link #close()} synchronize on this object
* to avoid races. The synchronization overhead is expected to be negligible, as
* these methods are expected to be rarely invoked.
* </p>
*/
class NativeEventStream implements Closeable {

// Native APIs
private static final CoreFoundation CF = CoreFoundation.INSTANCE;
private static final DispatchObjects DO = DispatchObjects.INSTANCE;
private static final DispatchQueue DQ = DispatchQueue.INSTANCE;
private static final FileSystemEvents FSE = FileSystemEvents.INSTANCE;

Check warning on line 85 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L82-L85

Added lines #L82 - L85 were not covered by tests

// Native memory
private @Nullable FSEventStreamCallback callback; // Keep reference to avoid premature GC'ing
private @Nullable Pointer stream;
private @Nullable Pointer queue;
Copy link
Member

Choose a reason for hiding this comment

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

I think they should be volatile, as their value will get ready by multiple threads, and you don't want thread-local caches. Since they could point towards bad pointers.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

All reads/writes to these fields are inside synchronized blocks. Comment added to clarify.

// Note: These fields aren't volatile, as all reads/write from/to them are
// inside synchronized blocks. Be careful to not break this invariant.

private final Path path;
private final NativeEventHandler handler;
private volatile boolean closed;

public NativeEventStream(Path path, NativeEventHandler handler) throws IOException {
this.path = path.toRealPath(); // Resolve symbolic links
Copy link
Member

Choose a reason for hiding this comment

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

shouldn't we also store the original requested path? So that our events generated are in the context of the original path, and not the resolved path?

or otherwise, we should make it an argument requirement?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

All paths returned to consumers of the stream are relative to the root of the watch scope (so they can be used as contexts in WatchEvents). However, macOS always seems to be using real paths in events, so to be able to relativize, the real path of the root is needed.

For instance, the original path could be this (i.e., new TestDirectory().getTestDirectory()):

/var/folders/k3/fpm2wy9s6l16x1cchqr9k7wh0000gn/T/java-watch-test9831264884788018313

But, the paths in the incoming events are these:

/private/var/folders/k3/fpm2wy9s6l16x1cchqr9k7wh0000gn/T/java-watch-test9831264884788018313/a.txt
/private/var/folders/k3/fpm2wy9s6l16x1cchqr9k7wh0000gn/T/java-watch-test9831264884788018313/b.txt
/private/var/folders/k3/fpm2wy9s6l16x1cchqr9k7wh0000gn/T/java-watch-test9831264884788018313/d.txt

So, we cannot relativize to the original path. Instead, we need to compute path.toRealPath():

/private/var/folders/k3/fpm2wy9s6l16x1cchqr9k7wh0000gn/T/java-watch-test9831264884788018313

Then, we can relativize to path.toRealPath():

a.txt
b.txt
d.txt

this.handler = handler;
this.closed = true;
}

Check warning on line 102 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L98-L102

Added lines #L98 - L102 were not covered by tests

public synchronized void open() {
if (!closed) {
return;

Check warning on line 106 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L106

Added line #L106 was not covered by tests
} else {
closed = false;

Check warning on line 108 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L108

Added line #L108 was not covered by tests
}

// Allocate native memory
callback = createCallback();
stream = createFSEventStream(callback);
queue = createDispatchQueue();

Check warning on line 114 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L112-L114

Added lines #L112 - L114 were not covered by tests

// Start the stream
var streamNonNull = stream;

Check warning on line 117 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L117

Added line #L117 was not covered by tests
if (streamNonNull != null) {
FSE.FSEventStreamSetDispatchQueue(streamNonNull, queue);
FSE.FSEventStreamStart(streamNonNull);

Check warning on line 120 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L119-L120

Added lines #L119 - L120 were not covered by tests
}
}

Check warning on line 122 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L122

Added line #L122 was not covered by tests

private FSEventStreamCallback createCallback() {
return new FSEventStreamCallback() {

Check warning on line 125 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L125

Added line #L125 was not covered by tests
@Override
public void callback(Pointer streamRef, Pointer clientCallBackInfo,
long numEvents, Pointer eventPaths, Pointer eventFlags, Pointer eventIds) {
// This function is called each time native events are issued by
// macOS. The purpose of this function is to perform the minimal
Copy link
Member

Choose a reason for hiding this comment

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

I might be wrong, but it reads like we should make sure that this function does not consume a lot of time? As it's a native callback?

If that is true, I think we should publish the event to a ConcurrentQueue, as we don't know how much work happens inside of the handler

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Alternative solution as discussed: make the class package private.

// amount of processing to hide the native APIs from downstream
// consumers, who are offered native events via `handler`.

var paths = eventPaths.getStringArray(0, (int) numEvents);
var flags = eventFlags.getIntArray(0, (int) numEvents);

Check warning on line 135 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L134-L135

Added lines #L134 - L135 were not covered by tests

for (var i = 0; i < numEvents; i++) {
var context = path.relativize(Path.of(paths[i]));

Check warning on line 138 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L138

Added line #L138 was not covered by tests

// Note: Multiple "physical" native events might be
// coalesced into a single "logical" native event, so the
// following series of checks should be if-statements
// (instead of if/else-statements).
if (any(flags[i], ITEM_CREATED.mask)) {
handler.handle(ENTRY_CREATE, context);

Check warning on line 145 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L145

Added line #L145 was not covered by tests
}
if (any(flags[i], ITEM_REMOVED.mask)) {
handler.handle(ENTRY_DELETE, context);

Check warning on line 148 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L148

Added line #L148 was not covered by tests
}
if (any(flags[i], ITEM_MODIFIED.mask | ITEM_INODE_META_MOD.mask)) {
handler.handle(ENTRY_MODIFY, context);

Check warning on line 151 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L151

Added line #L151 was not covered by tests
}
if (any(flags[i], MUST_SCAN_SUB_DIRS.mask)) {
handler.handle(OVERFLOW, null);

Check warning on line 154 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L154

Added line #L154 was not covered by tests
}
}
}

Check warning on line 157 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L157

Added line #L157 was not covered by tests

private boolean any(int bits, int mask) {
return (bits & mask) != 0;
}
};
}

private Pointer createFSEventStream(FSEventStreamCallback callback) {
try (var pathsToWatch = new Strings(path.toString())) {
var allocator = CF.CFAllocatorGetDefault();
var context = Pointer.NULL;
var sinceWhen = FSE.FSEventsGetCurrentEventId();
var latency = 0.15;
var flags = NO_DEFER.mask | WATCH_ROOT.mask | FILE_EVENTS.mask;
return FSE.FSEventStreamCreate(allocator, callback, context, pathsToWatch.toCFArray(), sinceWhen, latency, flags);

Check warning on line 172 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L166-L172

Added lines #L166 - L172 were not covered by tests
Copy link
Member

Choose a reason for hiding this comment

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

nit: instead of all the local variables, it can also be written as:

return FSE.FSEventStreamCreate(
     CF.CFAllocatorGetDefault(),
     Pointer.NULL,
....

}
}

private Pointer createDispatchQueue() {
var label = "engineering.swat.watch";
var attr = Pointer.NULL;
return DQ.dispatch_queue_create(label, attr);

Check warning on line 179 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L177-L179

Added lines #L177 - L179 were not covered by tests
Copy link
Member

Choose a reason for hiding this comment

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

nit: inline the parameters

}

// -- Closeable --

@Override
public synchronized void close() {
if (closed) {
return;

Check warning on line 187 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L187

Added line #L187 was not covered by tests
} else {
closed = true;

Check warning on line 189 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L189

Added line #L189 was not covered by tests
}

var streamNonNull = stream;
var queueNonNull = queue;

Check warning on line 193 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L192-L193

Added lines #L192 - L193 were not covered by tests
if (streamNonNull != null && queueNonNull != null) {

// Stop the stream
FSE.FSEventStreamStop(streamNonNull);
FSE.FSEventStreamSetDispatchQueue(streamNonNull, Pointer.NULL);
FSE.FSEventStreamInvalidate(streamNonNull);

Check warning on line 199 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L197-L199

Added lines #L197 - L199 were not covered by tests

// Deallocate native memory
DO.dispatch_release(queueNonNull);
FSE.FSEventStreamRelease(streamNonNull);
queue = null;
stream = null;
callback = null;

Check warning on line 206 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L202-L206

Added lines #L202 - L206 were not covered by tests
}
}

Check warning on line 208 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L208

Added line #L208 was not covered by tests
}

/**
* Array of strings in native memory, needed to create a new native event stream
* (i.e., the {@code pathsToWatch} argument of {@code FSEventStreamCreate} is an
* array of strings).
*/
class Strings implements AutoCloseable {

// Native APIs
private static final CoreFoundation CF = CoreFoundation.INSTANCE;

Check warning on line 219 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L219

Added line #L219 was not covered by tests

// Native memory
private final CFStringRef[] strings;
private final CFArrayRef array;

private volatile boolean closed = false;

Check warning on line 225 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L225

Added line #L225 was not covered by tests

public Strings(String... strings) {

Check warning on line 227 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L227

Added line #L227 was not covered by tests
// Allocate native memory
this.strings = createCFStrings(strings);
this.array = createCFArray(this.strings);
}

Check warning on line 231 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L229-L231

Added lines #L229 - L231 were not covered by tests

public CFArrayRef toCFArray() {
if (closed) {
throw new IllegalStateException("Strings are already deallocated");

Check warning on line 235 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L235

Added line #L235 was not covered by tests
} else {
return array;

Check warning on line 237 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L237

Added line #L237 was not covered by tests
}
}

private static CFStringRef[] createCFStrings(String[] pathsToWatch) {
return Arrays.stream(pathsToWatch)
.map(CFStringRef::createCFString)
.toArray(CFStringRef[]::new);

Check warning on line 244 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L242-L244

Added lines #L242 - L244 were not covered by tests
}

private static CFArrayRef createCFArray(CFStringRef[] strings) {
var n = strings.length;
var size = Native.getNativeSize(CFStringRef.class);

Check warning on line 249 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L248-L249

Added lines #L248 - L249 were not covered by tests

// Create a temporary array of pointers to the strings (automatically
// freed when `values` goes out of scope)
var values = new Memory(n * size);

Check warning on line 253 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L253

Added line #L253 was not covered by tests
for (int i = 0; i < n; i++) {
values.setPointer(i * size, strings[i].getPointer());

Check warning on line 255 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L255

Added line #L255 was not covered by tests
}

// Create a permanent array based on the temporary array
var alloc = CF.CFAllocatorGetDefault();
var numValues = new CFIndex(n);
var callBacks = Pointer.NULL;
return CF.CFArrayCreate(alloc, values, numValues, callBacks);

Check warning on line 262 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L259-L262

Added lines #L259 - L262 were not covered by tests
}

// -- AutoCloseable --

@Override
public void close() {
if (closed) {
throw new IllegalStateException("Strings are already deallocated");

Check warning on line 270 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L270

Added line #L270 was not covered by tests
} else {
closed = true;

Check warning on line 272 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L272

Added line #L272 was not covered by tests
}

// Deallocate native memory
for (var s : strings) {
if (s != null) {
s.release();

Check warning on line 278 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L278

Added line #L278 was not covered by tests
}
}
if (array != null) {
array.release();

Check warning on line 282 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L282

Added line #L282 was not covered by tests
}
}

Check warning on line 284 in src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/engineering/swat/watch/impl/mac/NativeEventStream.java#L284

Added line #L284 was not covered by tests
}
Loading