undefect. CWE-407 — 63 sites patched across 27 ecosystems

Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com

Patches, unit tests, benchmarks, whitepaper, and outreach briefs.
Public domain — no copyright claimed. Use freely.
This commit is contained in:
russell@unturf.com 2026-03-26 17:11:57 -04:00
commit 0a580b313d
70422 changed files with 17213626 additions and 0 deletions

View file

@ -0,0 +1,77 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.loader;
import java.io.File;
import java.util.ArrayList;
class ClassLoaderHelper {
private ClassLoaderHelper() {}
/**
* Shared objects may be loaded from .a archive object on AIX and .so may not exist.
* This method returns false so that loading of shared library continues if
* libname.so is not present.
*/
static boolean loadLibraryOnlyIfPresent() {
return false;
}
/**
* AIX implementation of JVM_LoadLibrary handles the alternate path name mapping.
* If loading of the given library name with ".so" suffix fails, it will attempt
* to load the library of the same name with ".a" suffix as the alternate name.
* This method simply returns null. It could implement the alternate name
* converting ".so" with ".a" suffix but redundant.
*/
static File mapAlternativeName(File lib) {
return null;
}
/**
* Parse a PATH env variable.
*
* Empty elements will be replaced by dot.
*/
static String[] parsePath(String ldPath) {
char ps = File.pathSeparatorChar;
ArrayList<String> paths = new ArrayList<>();
int pathStart = 0;
int pathEnd;
while ((pathEnd = ldPath.indexOf(ps, pathStart)) >= 0) {
paths.add((pathStart < pathEnd) ?
ldPath.substring(pathStart, pathEnd) : ".");
pathStart = pathEnd + 1;
}
int ldLen = ldPath.length();
paths.add((pathStart < ldLen) ?
ldPath.substring(pathStart, ldLen) : ".");
return paths.toArray(new String[paths.size()]);
}
}

View file

@ -0,0 +1,91 @@
/*
* Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.ch;
import java.nio.channels.*;
import java.nio.channels.spi.AsynchronousChannelProvider;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadFactory;
import java.io.IOException;
public class AixAsynchronousChannelProvider
extends AsynchronousChannelProvider
{
private static volatile AixPollPort defaultPort;
private AixPollPort defaultEventPort() throws IOException {
if (defaultPort == null) {
synchronized (AixAsynchronousChannelProvider.class) {
if (defaultPort == null) {
defaultPort = new AixPollPort(this, ThreadPool.getDefault()).start();
}
}
}
return defaultPort;
}
public AixAsynchronousChannelProvider() {
}
@Override
public AsynchronousChannelGroup openAsynchronousChannelGroup(int nThreads, ThreadFactory factory)
throws IOException
{
return new AixPollPort(this, ThreadPool.create(nThreads, factory)).start();
}
@Override
public AsynchronousChannelGroup openAsynchronousChannelGroup(ExecutorService executor, int initialSize)
throws IOException
{
return new AixPollPort(this, ThreadPool.wrap(executor, initialSize)).start();
}
private Port toPort(AsynchronousChannelGroup group) throws IOException {
if (group == null) {
return defaultEventPort();
} else {
if (!(group instanceof AixPollPort))
throw new IllegalChannelGroupException();
return (Port)group;
}
}
@Override
public AsynchronousServerSocketChannel openAsynchronousServerSocketChannel(AsynchronousChannelGroup group)
throws IOException
{
return new UnixAsynchronousServerSocketChannelImpl(toPort(group));
}
@Override
public AsynchronousSocketChannel openAsynchronousSocketChannel(AsynchronousChannelGroup group)
throws IOException
{
return new UnixAsynchronousSocketChannelImpl(toPort(group));
}
}

View file

@ -0,0 +1,459 @@
/*
* Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2024 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.ch;
import java.io.FileDescriptor;
import java.io.IOException;
import java.nio.channels.spi.AsynchronousChannelProvider;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.RejectedExecutionException;
import java.util.HashSet;
import java.util.Iterator;
import sun.nio.ch.IOUtil;
import sun.nio.ch.Pollset;
/**
* AsynchronousChannelGroup implementation based on the AIX pollset framework.
*/
final class AixPollPort
extends Port
{
static {
IOUtil.load();
Pollset.init();
}
// pollset ID
private final int pollset;
// true if port is closed
private boolean closed;
// socket pair used for wakeup
private final int sp[];
// socket pair used to indicate pending pollsetCtl calls
// Background info: pollsetCtl blocks when another thread is in a pollsetPoll call.
private final int ctlSp[];
// number of wakeups pending
private final AtomicInteger wakeupCount = new AtomicInteger();
// address of the poll array passed to pollset_poll
private final long address;
// maximum number of events to poll at a time
private static final int MAX_EVENTS_TO_POLL = 512;
// encapsulates an event for a channel
static class Event {
final PollableChannel channel;
final int events;
Event(PollableChannel channel, int events) {
this.channel = channel;
this.events = events;
}
PollableChannel channel() { return channel; }
int events() { return events; }
}
// queue of events for cases that a polling thread dequeues more than one
// event
private final ArrayBlockingQueue<Event> queue;
private final Event NEED_TO_POLL = new Event(null, 0);
private final Event EXECUTE_TASK_OR_SHUTDOWN = new Event(null, 0);
private final Event CONTINUE_AFTER_CTL_EVENT = new Event(null, 0);
// encapsulates a pollset control event for a file descriptor
static class ControlEvent {
final int fd;
final int events;
final boolean removeOnly;
int error = 0;
ControlEvent(int fd, int events, boolean removeOnly) {
this.fd = fd;
this.events = events;
this.removeOnly = removeOnly;
}
int fd() { return fd; }
int events() { return events; }
boolean removeOnly() { return removeOnly; }
int error() { return error; }
void setError(int error) { this.error = error; }
}
// queue of control events that need to be processed
// (this object is also used for synchronization)
private final HashSet<ControlEvent> controlQueue = new HashSet<ControlEvent>();
// lock used to check whether a poll operation is ongoing
private final ReentrantLock controlLock = new ReentrantLock();
AixPollPort(AsynchronousChannelProvider provider, ThreadPool pool)
throws IOException
{
super(provider, pool);
// open pollset
this.pollset = Pollset.pollsetCreate();
// create socket pair for wakeup mechanism
int[] sv = new int[2];
try {
Pollset.socketpair(sv);
// register one end with pollset
Pollset.pollsetCtl(pollset, Pollset.PS_ADD, sv[0], Net.POLLIN);
} catch (IOException x) {
Pollset.pollsetDestroy(pollset);
throw x;
}
this.sp = sv;
// create socket pair for pollset control mechanism
sv = new int[2];
try {
Pollset.socketpair(sv);
// make the reading part of the socket nonblocking, so the drain (drain_all) method works
IOUtil.configureBlocking(IOUtil.newFD(sv[0]), false);
// register one end with pollset
Pollset.pollsetCtl(pollset, Pollset.PS_ADD, sv[0], Net.POLLIN);
} catch (IOException x) {
Pollset.pollsetDestroy(pollset);
throw x;
}
this.ctlSp = sv;
// allocate the poll array
this.address = Pollset.allocatePollArray(MAX_EVENTS_TO_POLL);
// create the queue and offer the special event to ensure that the first
// threads polls
this.queue = new ArrayBlockingQueue<Event>(MAX_EVENTS_TO_POLL);
this.queue.offer(NEED_TO_POLL);
}
AixPollPort start() {
startThreads(new EventHandlerTask());
return this;
}
/**
* Release all resources
*/
private void implClose() {
synchronized (this) {
if (closed)
return;
closed = true;
}
Pollset.freePollArray(address);
Pollset.close0(sp[0]);
Pollset.close0(sp[1]);
Pollset.close0(ctlSp[0]);
Pollset.close0(ctlSp[1]);
Pollset.pollsetDestroy(pollset);
}
void wakeup() {
if (wakeupCount.incrementAndGet() == 1) {
// write byte to socketpair to force wakeup
try {
Pollset.interrupt(sp[1]);
} catch (IOException x) {
throw new AssertionError(x);
}
}
}
@Override
void executeOnHandlerTask(Runnable task) {
synchronized (this) {
if (closed)
throw new RejectedExecutionException();
offerTask(task);
wakeup();
}
}
@Override
void shutdownHandlerTasks() {
/*
* If no tasks are running then just release resources; otherwise
* write to the one end of the socketpair to wakeup any polling threads.
*/
int nThreads = threadCount();
if (nThreads == 0) {
implClose();
} else {
// send interrupt to each thread
while (nThreads-- > 0) {
wakeup();
}
}
}
// invoke by clients to register a file descriptor
@Override
void startPoll(int fd, int events) {
queueControlEvent(new ControlEvent(fd, events, false));
}
// Callback method for implementations that need special handling when fd is removed
@Override
protected void preUnregister(int fd) {
queueControlEvent(new ControlEvent(fd, 0, true));
}
// Add control event into queue and wait for completion.
// In case the control lock is free, this method also tries to apply the control change directly.
private void queueControlEvent(ControlEvent ev) {
// pollsetCtl blocks when a poll call is ongoing. This is very probable.
// Therefore we let the polling thread do the pollsetCtl call.
synchronized (controlQueue) {
controlQueue.add(ev);
// write byte to socketpair to force wakeup
try {
Pollset.interrupt(ctlSp[1]);
} catch (IOException x) {
throw new AssertionError(x);
}
do {
// Directly empty queue if no poll call is ongoing.
if (controlLock.tryLock()) {
try {
processControlQueue();
} finally {
controlLock.unlock();
}
} else {
try {
// Do not starve in case the polling thread returned before
// we could write to ctlSp[1] but the polling thread did not
// release the control lock until we checked. Therefore, use
// a timed wait for the time being.
controlQueue.wait(100);
} catch (InterruptedException e) {
// ignore exception and try again
}
}
} while (controlQueue.contains(ev));
}
if (ev.error() != 0) {
throw new AssertionError();
}
}
// Process all events currently stored in the control queue.
private void processControlQueue() {
// On Aix it is only possible to set the event
// bits on the first call of pollsetCtl. Later
// calls only add bits, but cannot remove them.
// Therefore, we always remove the file
// descriptor ignoring the error and then add it.
Iterator<ControlEvent> iter = controlQueue.iterator();
while (iter.hasNext()) {
ControlEvent ev = iter.next();
Pollset.pollsetCtl(pollset, Pollset.PS_DELETE, ev.fd(), 0);
if (!ev.removeOnly()) {
ev.setError(Pollset.pollsetCtl(pollset, Pollset.PS_MOD, ev.fd(), ev.events()));
}
iter.remove();
}
controlQueue.notifyAll();
}
/*
* Task to process events from pollset and dispatch to the channel's
* onEvent handler.
*
* Events are retrieved from pollset in batch and offered to a BlockingQueue
* where they are consumed by handler threads. A special "NEED_TO_POLL"
* event is used to signal one consumer to re-poll when all events have
* been consumed.
*/
private class EventHandlerTask implements Runnable {
private Event poll() throws IOException {
try {
for (;;) {
int n;
controlLock.lock();
try {
int m;
m = n = Pollset.pollsetPoll(pollset, address,
MAX_EVENTS_TO_POLL, Pollset.PS_NO_TIMEOUT);
while (m-- > 0) {
long eventAddress = Pollset.getEvent(address, m);
int fd = Pollset.getDescriptor(eventAddress);
// To emulate one shot semantic we need to remove
// the file descriptor here.
if (fd != sp[0] && fd != ctlSp[0]) {
synchronized (controlQueue) {
Pollset.pollsetCtl(pollset, Pollset.PS_DELETE, fd, 0);
}
}
}
} finally {
controlLock.unlock();
}
/*
* 'n' events have been read. Here we map them to their
* corresponding channel in batch and queue n-1 so that
* they can be handled by other handler threads. The last
* event is handled by this thread (and so is not queued).
*/
fdToChannelLock.readLock().lock();
try {
while (n-- > 0) {
long eventAddress = Pollset.getEvent(address, n);
int fd = Pollset.getDescriptor(eventAddress);
// wakeup
if (fd == sp[0]) {
if (wakeupCount.decrementAndGet() == 0) {
// no more wakeups so drain pipe
Pollset.drain1(sp[0]);
}
// queue special event if there are more events
// to handle.
if (n > 0) {
queue.offer(EXECUTE_TASK_OR_SHUTDOWN);
continue;
}
return EXECUTE_TASK_OR_SHUTDOWN;
}
// wakeup to process control event
if (fd == ctlSp[0]) {
synchronized (controlQueue) {
IOUtil.drain(ctlSp[0]);
processControlQueue();
}
if (n > 0) {
continue;
}
return CONTINUE_AFTER_CTL_EVENT;
}
PollableChannel channel = fdToChannel.get(fd);
if (channel != null) {
int events = Pollset.getRevents(eventAddress);
Event ev = new Event(channel, events);
// n-1 events are queued; This thread handles
// the last one except for the wakeup
if (n > 0) {
queue.offer(ev);
} else {
return ev;
}
}
}
} finally {
fdToChannelLock.readLock().unlock();
}
}
} finally {
// to ensure that some thread will poll when all events have
// been consumed
queue.offer(NEED_TO_POLL);
}
}
public void run() {
Invoker.GroupAndInvokeCount myGroupAndInvokeCount =
Invoker.getGroupAndInvokeCount();
final boolean isPooledThread = (myGroupAndInvokeCount != null);
boolean replaceMe = false;
Event ev;
try {
for (;;) {
// reset invoke count
if (isPooledThread)
myGroupAndInvokeCount.resetInvokeCount();
try {
replaceMe = false;
ev = queue.take();
// no events and this thread has been "selected" to
// poll for more.
if (ev == NEED_TO_POLL) {
try {
ev = poll();
} catch (IOException x) {
x.printStackTrace();
return;
}
}
} catch (InterruptedException x) {
continue;
}
// continue after we processed a control event
if (ev == CONTINUE_AFTER_CTL_EVENT) {
continue;
}
// handle wakeup to execute task or shutdown
if (ev == EXECUTE_TASK_OR_SHUTDOWN) {
Runnable task = pollTask();
if (task == null) {
// shutdown request
return;
}
// run task (may throw error/exception)
replaceMe = true;
task.run();
continue;
}
// process event
try {
ev.channel().onEvent(ev.events(), isPooledThread);
} catch (Error | RuntimeException x) {
replaceMe = true;
throw x;
}
}
} finally {
// last handler to exit when shutdown releases resources
int remaining = threadExit(this, replaceMe);
if (remaining == 0 && isShutdown()) {
implClose();
}
}
}
}
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.ch;
import java.nio.channels.spi.AsynchronousChannelProvider;
/**
* Creates this platform's default AsynchronousChannelProvider
*/
public class DefaultAsynchronousChannelProvider {
/**
* Prevent instantiation.
*/
private DefaultAsynchronousChannelProvider() { }
/**
* Returns the default AsynchronousChannelProvider.
*/
public static AsynchronousChannelProvider create() {
return new AixAsynchronousChannelProvider();
}
}

View file

@ -0,0 +1,57 @@
/*
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.ch;
import java.io.IOException;
/**
* Default PollerProvider for AIX.
*/
class DefaultPollerProvider extends PollerProvider {
DefaultPollerProvider(Poller.Mode mode) {
if (mode != Poller.Mode.SYSTEM_THREADS) {
throw new UnsupportedOperationException();
}
super(mode);
}
DefaultPollerProvider() {
this(Poller.Mode.SYSTEM_THREADS);
}
@Override
Poller readPoller(boolean subPoller) throws IOException {
if (subPoller)
throw new UnsupportedOperationException();
return new PollsetPoller(true);
}
@Override
Poller writePoller(boolean subPoller) throws IOException {
if (subPoller)
throw new UnsupportedOperationException();
return new PollsetPoller(false);
}
}

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.ch;
/**
* Creates this platform's default SelectorProvider
*/
public class DefaultSelectorProvider {
private static final SelectorProviderImpl INSTANCE = new PollSelectorProvider();
/**
* Prevent instantiation.
*/
private DefaultSelectorProvider() { }
/**
* Returns the default SelectorProvider implementation.
*/
public static SelectorProviderImpl get() {
return INSTANCE;
}
}

View file

@ -0,0 +1,55 @@
/*
* Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.ch;
import java.io.FileDescriptor;
import java.io.IOException;
public class FileDispatcherImpl extends UnixFileDispatcherImpl {
FileDispatcherImpl() {
super();
}
int force(FileDescriptor fd, boolean metaData) throws IOException {
return force0(fd, metaData);
}
protected long transferTo(FileDescriptor src, long position,
long count, FileDescriptor dst,
boolean append)
{
return transferTo0(src, position, count, dst, append);
}
// --- native methods ---
static native int force0(FileDescriptor fd, boolean metaData)
throws IOException;
private static native long transferTo0(FileDescriptor src, long position,
long count, FileDescriptor dst,
boolean append);
}

View file

@ -0,0 +1,124 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, IBM Corp.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.ch;
import java.io.IOException;
import jdk.internal.misc.Unsafe;
public class Pollset {
private static final Unsafe unsafe = Unsafe.getUnsafe();
/**
* struct pollfd {
* int fd;
* short events;
* short revents;
* }
*/
public static final int SIZEOF_POLLFD = eventSize();
public static final int OFFSETOF_EVENTS = eventsOffset();
public static final int OFFSETOF_REVENTS = reventsOffset();
public static final int OFFSETOF_FD = fdOffset();
// opcodes
public static final int PS_ADD = 0x0;
public static final int PS_MOD = 0x1;
public static final int PS_DELETE = 0x2;
// event
public static final int PS_POLLPRI = 0x4;
// revent errcodes
public static final char PS_POLLNVAL = 0x8000;
public static final char PS_POLLERR = 0x4000;
public static final int PS_NO_TIMEOUT = -1;
/**
* Allocates a poll array to handle up to {@code count} events.
*/
public static long allocatePollArray(int count) {
return unsafe.allocateMemory(count * SIZEOF_POLLFD);
}
/**
* Free a poll array
*/
public static void freePollArray(long address) {
unsafe.freeMemory(address);
}
/**
* Returns event[i];
*/
public static long getEvent(long address, int i) {
return address + (SIZEOF_POLLFD * i);
}
/**
* Returns event->fd
*/
public static int getDescriptor(long eventAddress) {
return unsafe.getInt(eventAddress + OFFSETOF_FD);
}
/**
* Returns event->events
*/
public static int getEvents(long eventAddress) {
return unsafe.getChar(eventAddress + OFFSETOF_EVENTS);
}
/**
* Returns event->revents
*/
public static char getRevents(long eventAddress) {
return unsafe.getChar(eventAddress + OFFSETOF_REVENTS);
}
public static boolean isReventsError(long eventAddress) {
char revents = getRevents(eventAddress);
return (revents & PS_POLLNVAL) != 0 || (revents & PS_POLLERR) != 0;
}
// -- Native methods --
public static native int pollsetCreate() throws IOException;
public static native int pollsetCtl(int pollset, int opcode, int fd, int events);
public static native int pollsetPoll(int pollset, long pollAddress, int numfds, int timeout)
throws IOException;
public static native void pollsetDestroy(int pollset);
public static native void init();
public static native int eventSize();
public static native int eventsOffset();
public static native int reventsOffset();
public static native int fdOffset();
public static native void socketpair(int[] sv) throws IOException;
public static native void interrupt(int fd) throws IOException;
public static native void drain1(int fd) throws IOException;
public static native void close0(int fd);
}

View file

@ -0,0 +1,114 @@
/*
* Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, IBM Corp.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.ch;
import java.io.IOException;
import java.time.Instant;
import sun.nio.ch.Pollset;
/**
* Poller implementation based on the AIX Pollset library.
*/
class PollsetPoller extends Poller {
private static final int MAX_EVENTS_TO_POLL;
static {
Pollset.init(); /* Dynamically loads pollset C functions */
MAX_EVENTS_TO_POLL = 512;
}
private final int event;
private final int setid;
private final long pollBuffer;
PollsetPoller(boolean read) throws IOException {
this.event = (read) ? Net.POLLIN : Net.POLLOUT;
this.setid = Pollset.pollsetCreate();
this.pollBuffer = Pollset.allocatePollArray(MAX_EVENTS_TO_POLL);
}
@Override
void close() {
Pollset.pollsetDestroy(setid);
Pollset.freePollArray(pollBuffer);
}
@Override
int fdVal() {
return setid;
}
@Override
void implStartPoll(int fd) throws IOException {
int ret = Pollset.pollsetCtl(setid, Pollset.PS_MOD, fd, Pollset.PS_POLLPRI | event);
if (ret != 0) {
throw new IOException("Unable to register fd " + fd);
}
}
@Override
void implStopPoll(int fd, boolean polled) {
int ret = Pollset.pollsetCtl(setid, Pollset.PS_DELETE, fd, 0);
assert ret == 0;
}
/**
* Main poll method. The AIX Pollset library does not appear to pick up changes to the pollset
* (the set of fds being polled) while blocked on a call to this method. These changes happen
* regularly in the poll-loop thread and update thread from Poller.java.
* To address this difficulty, we break poll calls into 100ms sub-calls and emulate the timout.
*/
@Override
int poll(int timeout) throws IOException {
int n;
switch (timeout) {
case 0:
n = pollInner(0);
break;
case Pollset.PS_NO_TIMEOUT:
do { n = pollInner(100); } while (n == 0);
break;
default:
Instant end = Instant.now().plusMillis(timeout);
do { n = pollInner(100); } while (n == 0 && Instant.now().isBefore(end));
break;
}
return n;
}
int pollInner(int subInterval) throws IOException {
int n = Pollset.pollsetPoll(setid, pollBuffer, MAX_EVENTS_TO_POLL, subInterval);
for (int i=0; i<n; i++) {
long eventAddress = Pollset.getEvent(pollBuffer, i);
int fd = Pollset.getDescriptor(eventAddress);
polled(fd);
}
return n;
}
}

View file

@ -0,0 +1,119 @@
/*
* Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.fs;
import java.nio.file.attribute.*;
import java.util.*;
import java.io.IOException;
/**
* AIX implementation of FileStore
*/
class AixFileStore
extends UnixFileStore
{
AixFileStore(UnixPath file) throws IOException {
super(file);
}
AixFileStore(UnixFileSystem fs, UnixMountEntry entry) throws IOException {
super(fs, entry);
}
/**
* Finds, and returns, the mount entry for the file system where the file
* resides.
*/
@Override
UnixMountEntry findMountEntry() throws IOException {
AixFileSystem fs = (AixFileSystem)file().getFileSystem();
// step 1: get realpath
UnixPath path = null;
try {
byte[] rp = UnixNativeDispatcher.realpath(file());
path = new UnixPath(fs, rp);
} catch (UnixException x) {
x.rethrowAsIOException(file());
}
// step 2: find mount point
UnixPath parent = path.getParent();
while (parent != null) {
UnixFileAttributes attrs = null;
try {
attrs = UnixFileAttributes.get(parent, true);
} catch (UnixException x) {
x.rethrowAsIOException(parent);
}
if (attrs.dev() != dev())
break;
path = parent;
parent = parent.getParent();
}
// step 3: lookup mounted file systems
byte[] dir = path.asByteArray();
for (UnixMountEntry entry: fs.getMountEntries()) {
if (Arrays.equals(dir, entry.dir()))
return entry;
}
throw new IOException("Mount point not found");
}
@Override
public boolean supportsFileAttributeView(Class<? extends FileAttributeView> type) {
// support UserDefinedAttributeView if extended attributes enabled
if (type == UserDefinedFileAttributeView.class) {
// lookup fstypes.properties
FeatureStatus status = checkIfFeaturePresent("user_xattr");
if (status == FeatureStatus.PRESENT)
return true;
if (status == FeatureStatus.NOT_PRESENT)
return false;
// typical AIX file system types that support xattr (JFS2 with EA enabled)
String fstype = entry().fstype();
if ("jfs2".equals(fstype)) {
UnixPath dir = new UnixPath(file().getFileSystem(), entry().dir());
return isExtendedAttributesEnabled(dir);
}
}
return super.supportsFileAttributeView(type);
}
@Override
public boolean supportsFileAttributeView(String name) {
if (name.equals("user"))
return supportsFileAttributeView(UserDefinedFileAttributeView.class);
return super.supportsFileAttributeView(name);
}
}

View file

@ -0,0 +1,95 @@
/*
* Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.fs;
import java.nio.file.*;
import java.nio.file.attribute.*;
import java.io.IOException;
import java.util.*;
import static sun.nio.fs.AixNativeDispatcher.*;
/**
* AIX implementation of FileSystem
*/
class AixFileSystem extends UnixFileSystem {
AixFileSystem(UnixFileSystemProvider provider, String dir) {
super(provider, dir);
}
@Override
public WatchService newWatchService()
throws IOException
{
return new PollingWatchService();
}
// lazy initialization of the list of supported attribute views
private static class SupportedFileFileAttributeViewsHolder {
static final Set<String> supportedFileAttributeViews =
supportedFileAttributeViews();
private static Set<String> supportedFileAttributeViews() {
Set<String> result = new HashSet<String>();
result.addAll(UnixFileSystem.standardFileAttributeViews());
result.add("user");
return Collections.unmodifiableSet(result);
}
}
@Override
public Set<String> supportedFileAttributeViews() {
return SupportedFileFileAttributeViewsHolder.supportedFileAttributeViews;
}
@Override
void copyNonPosixAttributes(int ofd, int nfd) {
// TODO: Implement if needed.
}
/**
* Returns object to iterate over the mount entries returned by mntctl
*/
@Override
Iterable<UnixMountEntry> getMountEntries() {
UnixMountEntry[] entries = null;
try {
entries = getmntctl();
} catch (UnixException x) {
// nothing we can do
}
if (entries == null) {
return Collections.emptyList();
}
return Arrays.asList(entries);
}
@Override
FileStore getFileStore(UnixMountEntry entry) throws IOException {
return new AixFileStore(this, entry);
}
}

View file

@ -0,0 +1,92 @@
/*
* Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2025 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.fs;
import java.nio.file.*;
import java.nio.file.attribute.*;
import java.io.IOException;
/**
* AIX implementation of FileSystemProvider
*/
class AixFileSystemProvider extends UnixFileSystemProvider {
public AixFileSystemProvider() {
super();
}
@Override
AixFileSystem newFileSystem(String dir) {
return new AixFileSystem(this, dir);
}
/**
* @see sun.nio.fs.UnixFileSystemProvider#getFileStore(sun.nio.fs.UnixPath)
*/
@Override
AixFileStore getFileStore(UnixPath path) throws IOException {
return new AixFileStore(path);
}
private static boolean supportsUserDefinedFileAttributeView(UnixPath file) {
try {
FileStore store = new AixFileStore(file);
return store.supportsFileAttributeView(UserDefinedFileAttributeView.class);
} catch (IOException e) {
return false;
}
}
@Override
@SuppressWarnings("unchecked")
public <V extends FileAttributeView> V getFileAttributeView(Path obj,
Class<V> type,
LinkOption... options)
{
if (type == UserDefinedFileAttributeView.class) {
UnixPath file = UnixPath.toUnixPath(obj);
return supportsUserDefinedFileAttributeView(file) ?
(V) new AixUserDefinedFileAttributeView(file, Util.followLinks(options))
: null;
}
return super.getFileAttributeView(obj, type, options);
}
@Override
public DynamicFileAttributeView getFileAttributeView(Path obj,
String name,
LinkOption... options)
{
if (name.equals("user")) {
UnixPath file = UnixPath.toUnixPath(obj);
return supportsUserDefinedFileAttributeView(file) ?
new AixUserDefinedFileAttributeView(file, Util.followLinks(options))
: null;
}
return super.getFileAttributeView(obj, name, options);
}
}

View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 2008, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.fs;
/**
* AIX specific system calls.
*/
class AixNativeDispatcher extends UnixNativeDispatcher {
private AixNativeDispatcher() { }
/**
* Special implementation of 'getextmntent' (see SolarisNativeDispatcher)
* that returns all entries at once.
*/
static native UnixMountEntry[] getmntctl() throws UnixException;
// initialize
private static native void init();
static {
jdk.internal.loader.BootLoader.loadLibrary("nio");
init();
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.fs;
class AixUserDefinedFileAttributeView
extends UnixUserDefinedFileAttributeView
{
AixUserDefinedFileAttributeView(UnixPath file, boolean followLinks) {
super(file, followLinks);
}
@Override
protected int maxNameLength() {
return 255;
}
}

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.fs;
import java.nio.file.FileSystem;
/**
* Creates this platform's default FileSystemProvider.
*/
public class DefaultFileSystemProvider {
private static final AixFileSystemProvider INSTANCE
= new AixFileSystemProvider();
private DefaultFileSystemProvider() { }
/**
* Returns the platform's default file system provider.
*/
public static AixFileSystemProvider instance() {
return INSTANCE;
}
/**
* Returns the platform's default file system.
*/
public static FileSystem theFileSystem() {
return INSTANCE.theFileSystem();
}
}

View file

@ -0,0 +1,585 @@
#
#
# Copyright (c) 1994, 2014, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation. Oracle designates this
# particular file as subject to the "Classpath" exception as provided
# by Oracle in the LICENSE file that accompanied this code.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
# or visit www.oracle.com if you need additional information or have any
# questions.
#
#
# Portions Copyright (c) 2014 IBM Corporation
#
# This table describes mappings between AIX time zone IDs and Java time zone
# IDs. Fields are separated by a single TAB ('\t'). Lines must be in the ascending
# order in ASCII. (non-ASCII characters can't be used.)
# NOTE
# This table format is not a public interface of any Java
# platforms. No applications should depend on this file in any form.
# This file has been generated using programs. Do not edit this file manually.
#
# Solaris Java
ACST-9:30ACDT Australia/Adelaide
AST4 America/Curacao
AST4ADT America/Halifax
AST9 Pacific/Gambier
AST9ADT America/Anchorage
AZOREST1 Atlantic/Cape_Verde
AZOREST1AZOREDT Atlantic/Azores
Africa/Abidjan Africa/Abidjan
Africa/Accra Africa/Accra
Africa/Addis_Ababa Africa/Addis_Ababa
Africa/Algiers Africa/Algiers
Africa/Asmera Africa/Asmera
Africa/Bamako GMT
Africa/Bangui Africa/Bangui
Africa/Banjul Africa/Banjul
Africa/Bissau Africa/Bissau
Africa/Blantyre Africa/Blantyre
Africa/Brazzaville Africa/Luanda
Africa/Bujumbura Africa/Bujumbura
Africa/Cairo Africa/Cairo
Africa/Casablanca Africa/Casablanca
Africa/Ceuta Europe/Paris
Africa/Conakry Africa/Conakry
Africa/Dakar Africa/Dakar
Africa/Dar_es_Salaam Africa/Dar_es_Salaam
Africa/Djibouti Africa/Djibouti
Africa/Douala Africa/Douala
Africa/El_Aaiun Africa/Casablanca
Africa/Freetown Africa/Freetown
Africa/Gaborone Africa/Gaborone
Africa/Harare Africa/Harare
Africa/Johannesburg Africa/Johannesburg
Africa/Kampala Africa/Kampala
Africa/Khartoum Africa/Khartoum
Africa/Kigali Africa/Kigali
Africa/Kinshasa Africa/Kinshasa
Africa/Lagos Africa/Lagos
Africa/Libreville Africa/Libreville
Africa/Lome Africa/Lome
Africa/Luanda Africa/Luanda
Africa/Lubumbashi Africa/Lubumbashi
Africa/Lusaka Africa/Lusaka
Africa/Malabo Africa/Malabo
Africa/Maputo Africa/Maputo
Africa/Maseru Africa/Maseru
Africa/Mbabane Africa/Mbabane
Africa/Mogadishu Africa/Mogadishu
Africa/Monrovia Africa/Monrovia
Africa/Nairobi Africa/Nairobi
Africa/Ndjamena Africa/Ndjamena
Africa/Niamey Africa/Niamey
Africa/Nouakchott Africa/Nouakchott
Africa/Ouagadougou Africa/Ouagadougou
Africa/Porto-Novo Africa/Porto-Novo
Africa/Sao_Tome Africa/Sao_Tome
Africa/Timbuktu Africa/Timbuktu
Africa/Tripoli Africa/Tripoli
Africa/Tunis Africa/Tunis
Africa/Windhoek Africa/Windhoek
America/Adak America/Adak
America/Anchorage America/Anchorage
America/Anguilla America/Anguilla
America/Antigua America/Antigua
America/Araguaina America/Sao_Paulo
America/Argentina/Buenos_Aires America/Argentina/Buenos_Aires
America/Argentina/Catamarca America/Argentina/Catamarca
America/Argentina/ComodRivadavia America/Argentina/Catamarca
America/Argentina/Cordoba America/Argentina/Cordoba
America/Argentina/Jujuy America/Argentina/Jujuy
America/Argentina/La_Rioja America/Argentina/La_Rioja
America/Argentina/Mendoza America/Argentina/Mendoza
America/Argentina/Rio_Gallegos America/Argentina/Rio_Gallegos
America/Argentina/Salta America/Argentina/Salta
America/Argentina/San_Juan America/Argentina/San_Juan
America/Argentina/San_Luis America/Argentina/San_Luis
America/Argentina/Tucuman America/Argentina/Tucuman
America/Argentina/Ushuaia America/Argentina/Ushuaia
America/Aruba America/Aruba
America/Asuncion America/Asuncion
America/Atka America/Adak
America/Barbados America/Barbados
America/Belize America/Belize
America/Bogota America/Bogota
America/Boise America/Denver
America/Buenos_Aires America/Argentina/Buenos_Aires
America/Cancun America/Chicago
America/Caracas America/Caracas
America/Catamarca America/Argentina/Catamarca
America/Cayenne America/Cayenne
America/Cayman America/Cayman
America/Chicago America/Chicago
America/Chihuahua America/Denver
America/Coral_Harbour America/Atikokan
America/Cordoba America/Argentina/Cordoba
America/Costa_Rica America/Costa_Rica
America/Cuiaba America/Cuiaba
America/Curacao America/Curacao
America/Dawson America/Los_Angeles
America/Dawson_Creek America/Dawson_Creek
America/Denver America/Denver
America/Detroit America/New_York
America/Dominica America/Dominica
America/Edmonton America/Edmonton
America/El_Salvador America/El_Salvador
America/Ensenada America/Los_Angeles
America/Fort_Wayne America/Indiana/Indianapolis
America/Fortaleza America/Fortaleza
America/Glace_Bay America/Halifax
America/Godthab America/Godthab
America/Goose_Bay America/Thule
America/Grand_Turk America/Grand_Turk
America/Grenada America/Grenada
America/Guadeloupe America/Guadeloupe
America/Guatemala America/Guatemala
America/Guayaquil America/Guayaquil
America/Guyana America/Guyana
America/Halifax America/Halifax
America/Havana America/Havana
America/Indiana/Indianapolis America/Indianapolis
America/Indianapolis America/Indiana/Indianapolis
America/Inuvik America/Denver
America/Iqaluit America/New_York
America/Jamaica America/Jamaica
America/Jujuy America/Argentina/Jujuy
America/Juneau America/Anchorage
America/Knox_IN America/Indiana/Knox
America/La_Paz America/La_Paz
America/Lima America/Lima
America/Los_Angeles America/Los_Angeles
America/Louisville America/Kentucky/Louisville
America/Managua America/Managua
America/Manaus America/Manaus
America/Marigot America/Guadeloupe
America/Martinique America/Martinique
America/Mazatlan America/Mazatlan
America/Mendoza America/Argentina/Mendoza
America/Menominee America/Winnipeg
America/Mexico_City America/Mexico_City
America/Miquelon America/Miquelon
America/Moncton America/Moncton
America/Montevideo America/Montevideo
America/Montreal America/Montreal
America/Montserrat America/Montserrat
America/Nassau America/Nassau
America/New_York America/New_York
America/Nipigon America/New_York
America/Nome America/Anchorage
America/Noronha America/Noronha
America/Panama America/Panama
America/Pangnirtung America/Thule
America/Paramaribo America/Paramaribo
America/Phoenix America/Phoenix
America/Port-au-Prince America/Port-au-Prince
America/Port_of_Spain America/Port_of_Spain
America/Porto_Acre America/Rio_Branco
America/Puerto_Rico America/Puerto_Rico
America/Rainy_River America/Chicago
America/Rankin_Inlet America/Chicago
America/Regina America/Regina
America/Rio_Branco America/Rio_Branco
America/Rosario America/Argentina/Cordoba
America/Santiago America/Santiago
America/Santo_Domingo America/Santo_Domingo
America/Sao_Paulo America/Sao_Paulo
America/Scoresbysund America/Scoresbysund
America/Shiprock America/Denver
America/St_Barthelemy America/Guadeloupe
America/St_Johns America/St_Johns
America/St_Kitts America/St_Kitts
America/St_Lucia America/St_Lucia
America/St_Thomas America/St_Thomas
America/St_Vincent America/St_Vincent
America/Tegucigalpa America/Tegucigalpa
America/Thule America/Thule
America/Thunder_Bay America/New_York
America/Tijuana America/Tijuana
America/Toronto America/Toronto
America/Tortola America/Tortola
America/Vancouver America/Vancouver
America/Virgin America/St_Thomas
America/Whitehorse America/Los_Angeles
America/Winnipeg America/Winnipeg
America/Yakutat America/Anchorage
America/Yellowknife America/Denver
Antarctica/Casey Antarctica/Casey
Antarctica/DumontDUrville Antarctica/DumontDUrville
Antarctica/Mawson Antarctica/Mawson
Antarctica/McMurdo Antarctica/McMurdo
Antarctica/Palmer Antarctica/Palmer
Antarctica/South_Pole Antarctica/McMurdo
Arctic/Longyearbyen Europe/Oslo
Asia/Aden Asia/Aden
Asia/Almaty Asia/Almaty
Asia/Amman Asia/Amman
Asia/Anadyr Asia/Anadyr
Asia/Aqtau Asia/Aqtau
Asia/Aqtobe Asia/Aqtobe
Asia/Ashkhabad Asia/Ashkhabad
Asia/Baghdad Asia/Baghdad
Asia/Bahrain Asia/Bahrain
Asia/Baku Asia/Baku
Asia/Bangkok Asia/Bangkok
Asia/Beirut Asia/Beirut
Asia/Bishkek Asia/Bishkek
Asia/Brunei Asia/Brunei
Asia/Calcutta Asia/Calcutta
Asia/Chungking Asia/Shanghai
Asia/Colombo Asia/Colombo
Asia/Dacca Asia/Dacca
Asia/Damascus Asia/Damascus
Asia/Dhaka Asia/Dhaka
Asia/Dubai Asia/Dubai
Asia/Dushanbe Asia/Dushanbe
Asia/Gaza Asia/Amman
Asia/Harbin Asia/Shanghai
Asia/Hong_Kong Asia/Hong_Kong
Asia/Irkutsk Asia/Irkutsk
Asia/Istanbul Europe/Istanbul
Asia/Jakarta Asia/Jakarta
Asia/Jayapura Asia/Jayapura
Asia/Jerusalem Asia/Jerusalem
Asia/Kabul Asia/Kabul
Asia/Kamchatka Asia/Kamchatka
Asia/Karachi Asia/Karachi
Asia/Kashgar Asia/Shanghai
Asia/Katmandu Asia/Katmandu
Asia/Kolkata Asia/Kolkata
Asia/Krasnoyarsk Asia/Krasnoyarsk
Asia/Kuala_Lumpur Asia/Kuala_Lumpur
Asia/Kuwait Asia/Kuwait
Asia/Macao Asia/Macao
Asia/Magadan Asia/Magadan
Asia/Manila Asia/Manila
Asia/Muscat Asia/Muscat
Asia/Nicosia Asia/Nicosia
Asia/Novosibirsk Asia/Novosibirsk
Asia/Omsk Asia/Novosibirsk
Asia/Phnom_Penh Asia/Phnom_Penh
Asia/Pyongyang Asia/Pyongyang
Asia/Qatar Asia/Qatar
Asia/Rangoon Asia/Rangoon
Asia/Riyadh Asia/Riyadh
Asia/Saigon Asia/Ho_Chi_Minh
Asia/Seoul Asia/Seoul
Asia/Shanghai Asia/Shanghai
Asia/Singapore Asia/Singapore
Asia/Taipei Asia/Taipei
Asia/Tashkent Asia/Tashkent
Asia/Tbilisi Asia/Tbilisi
Asia/Tehran Asia/Tehran
Asia/Tel_Aviv Asia/Jerusalem
Asia/Thimbu Asia/Thimbu
Asia/Tokyo Asia/Tokyo
Asia/Ujung_Pandang Asia/Ujung_Pandang
Asia/Ulan_Bator Asia/Ulaanbaatar
Asia/Urumqi Asia/Shanghai
Asia/Vientiane Asia/Vientiane
Asia/Vladivostok Asia/Vladivostok
Asia/Yakutsk Asia/Yakutsk
Asia/Yekaterinburg Asia/Yekaterinburg
Asia/Yerevan Asia/Yerevan
Atlantic/Azores Atlantic/Azores
Atlantic/Bermuda Atlantic/Bermuda
Atlantic/Canary Atlantic/Canary
Atlantic/Cape_Verde Atlantic/Cape_Verde
Atlantic/Faeroe Atlantic/Faeroe
Atlantic/Jan_Mayen Atlantic/Jan_Mayen
Atlantic/Madeira Europe/London
Atlantic/Reykjavik Atlantic/Reykjavik
Atlantic/South_Georgia Atlantic/South_Georgia
Atlantic/St_Helena Atlantic/St_Helena
Atlantic/Stanley Atlantic/Stanley
Australia/ACT Australia/Sydney
Australia/Adelaide Australia/Adelaide
Australia/Brisbane Australia/Brisbane
Australia/Broken_Hill Australia/Broken_Hill
Australia/Canberra Australia/Sydney
Australia/Darwin Australia/Darwin
Australia/Hobart Australia/Hobart
Australia/LHI Australia/Lord_Howe
Australia/Lord_Howe Australia/Lord_Howe
Australia/Melbourne Australia/Sydney
Australia/NSW Australia/Sydney
Australia/North Australia/Darwin
Australia/Perth Australia/Perth
Australia/Queensland Australia/Brisbane
Australia/South Australia/Adelaide
Australia/Sydney Australia/Sydney
Australia/Tasmania Australia/Hobart
Australia/Victoria Australia/Melbourne
Australia/West Australia/Perth
Australia/Yancowinna Australia/Broken_Hill
BRT3BRST America/Sao_Paulo
BST11 Pacific/Apia
BST11BDT Pacific/Apia
Brazil/Acre America/Rio_Branco
Brazil/DeNoronha America/Noronha
Brazil/East America/Sao_Paulo
Brazil/West America/Manaus
CET Europe/Paris
CET-1CEST Europe/Paris
CET-1CEST-2 Europe/Berlin
CET-1CET-2 Europe/Paris
CST6 America/Costa_Rica
CST6CDT America/Chicago
CUT0 UTC
CUT0GDT Europe/London
Canada/Atlantic America/Halifax
Canada/Central America/Winnipeg
Canada/East-Saskatchewan America/Regina
Canada/Eastern America/Montreal
Canada/Mountain America/Edmonton
Canada/Newfoundland America/St_Johns
Canada/Pacific America/Vancouver
Canada/Saskatchewan America/Regina
Canada/Yukon America/Whitehorse
Chile/Continental America/Santiago
Chile/EasterIsland Pacific/Easter
Cuba America/Havana
EET Europe/Istanbul
EET-10 Australia/Brisbane
EET-10EETDT Australia/Sydney
EST America/Indianapolis
EST5 America/Indianapolis
EST5EDT America/New_York
Egypt Africa/Cairo
Eire Europe/Dublin
Etc/GMT GMT
Etc/GMT0 GMT
Etc/Greenwich GMT
Etc/UCT UTC
Etc/UTC UTC
Etc/Universal UTC
Etc/Zulu UTC
Europe/Amsterdam Europe/Amsterdam
Europe/Andorra Europe/Andorra
Europe/Athens Europe/Athens
Europe/Belfast Europe/London
Europe/Belgrade Europe/Belgrade
Europe/Berlin Europe/Berlin
Europe/Bratislava Europe/Prague
Europe/Brussels Europe/Brussels
Europe/Bucharest Europe/Bucharest
Europe/Budapest Europe/Budapest
Europe/Chisinau Europe/Chisinau
Europe/Copenhagen Europe/Copenhagen
Europe/Dublin Europe/Dublin
Europe/Gibraltar Europe/Gibraltar
Europe/Guernsey Europe/London
Europe/Helsinki Europe/Helsinki
Europe/Isle_of_Man Europe/London
Europe/Istanbul Europe/Istanbul
Europe/Jersey Europe/London
Europe/Kaliningrad Europe/Kaliningrad
Europe/Kiev Europe/Kiev
Europe/Lisbon Europe/Lisbon
Europe/Ljubljana Europe/Belgrade
Europe/London Europe/London
Europe/Luxembourg Europe/Luxembourg
Europe/Madrid Europe/Madrid
Europe/Malta Europe/Malta
Europe/Mariehamn Europe/Helsinki
Europe/Minsk Europe/Minsk
Europe/Monaco Europe/Monaco
Europe/Moscow Europe/Moscow
Europe/Nicosia Asia/Nicosia
Europe/Oslo Europe/Oslo
Europe/Paris Europe/Paris
Europe/Podgorica Europe/Belgrade
Europe/Prague Europe/Prague
Europe/Riga Europe/Riga
Europe/Rome Europe/Rome
Europe/Samara Europe/Samara
Europe/San_Marino Europe/Rome
Europe/Sarajevo Europe/Belgrade
Europe/Simferopol Europe/Simferopol
Europe/Skopje Europe/Belgrade
Europe/Sofia Europe/Sofia
Europe/Stockholm Europe/Stockholm
Europe/Tallinn Europe/Tallinn
Europe/Tirane Europe/Tirane
Europe/Vaduz Europe/Vaduz
Europe/Vatican Europe/Rome
Europe/Vienna Europe/Vienna
Europe/Vilnius Europe/Vilnius
Europe/Warsaw Europe/Warsaw
Europe/Zagreb Europe/Belgrade
Europe/Zurich Europe/Zurich
FALKST2 Atlantic/South_Georgia
FALKST2FALKDT Atlantic/South_Georgia
Factory GMT
GB Europe/London
GB-Eire Europe/London
GMT GMT
GMT0 GMT
GMT0BST Europe/London
GMT0BST-1 Europe/London
GMT0WET Europe/Lisbon
GRNLNDST3 America/Buenos_Aires
GRNLNDST3GRNLNDDT America/Godthab
Greenwich GMT
HST Pacific/Honolulu
HST10 Pacific/Honolulu
HST10HDT America/Adak
Hongkong Asia/Hong_Kong
Iceland Atlantic/Reykjavik
Indian/Antananarivo Indian/Antananarivo
Indian/Chagos Indian/Chagos
Indian/Christmas Indian/Christmas
Indian/Cocos Indian/Cocos
Indian/Comoro Indian/Comoro
Indian/Kerguelen Indian/Kerguelen
Indian/Mahe Indian/Mahe
Indian/Maldives Indian/Maldives
Indian/Mauritius Indian/Mauritius
Indian/Mayotte Indian/Mayotte
Indian/Reunion Indian/Reunion
Iran Asia/Tehran
Israel Asia/Jerusalem
JST-9 Asia/Tokyo
JST-9JSTDT Asia/Tokyo
Jamaica America/Jamaica
Japan Asia/Tokyo
KORST-9 Asia/Seoul
KORST-9KORDT Asia/Seoul
Kwajalein Pacific/Kwajalein
Libya Africa/Tripoli
MEST-2 Europe/Istanbul
MEST-2MEDT Europe/Istanbul
MEST-3 Asia/Riyadh
MEST-3MEDT Europe/Moscow
MET Europe/Paris
MET-11 Pacific/Guadalcanal
MET-11METDT Asia/Magadan
MET-1MEST Europe/Paris
MET-1MST-2 Europe/Berlin
MEZ-1MESZ Europe/Berlin
MEZ-1MESZ-2 Europe/Berlin
MSK-3MSD Europe/Moscow
MST America/Phoenix
MST7 America/Phoenix
MST7MDT America/Denver
Mexico/BajaNorte America/Tijuana
Mexico/BajaSur America/Mazatlan
Mexico/General America/Mexico_City
Mideast/Riyadh87 Asia/Riyadh87
Mideast/Riyadh88 Asia/Riyadh88
Mideast/Riyadh89 Asia/Riyadh89
NFT-1 Africa/Algiers
NFT-1DFT Europe/Paris
NFT-1DST Europe/Paris
NZ Pacific/Auckland
NZ-CHAT Pacific/Chatham
NZST-12 Pacific/Fiji
NZST-12NZDT Pacific/Auckland
Navajo America/Denver
PAKST-5 Asia/Karachi
PAKST-5PAKDT Asia/Yekaterinburg
PRC Asia/Shanghai
PST8 Pacific/Pitcairn
PST8PDT America/Los_Angeles
PST8PDT7 America/Tijuana
Pacific/Apia Pacific/Apia
Pacific/Auckland Pacific/Auckland
Pacific/Chatham Pacific/Chatham
Pacific/Easter Pacific/Easter
Pacific/Efate Pacific/Efate
Pacific/Enderbury Pacific/Enderbury
Pacific/Fakaofo Pacific/Fakaofo
Pacific/Fiji Pacific/Fiji
Pacific/Funafuti Pacific/Funafuti
Pacific/Galapagos Pacific/Galapagos
Pacific/Gambier Pacific/Gambier
Pacific/Guadalcanal Pacific/Guadalcanal
Pacific/Guam Pacific/Guam
Pacific/Honolulu Pacific/Honolulu
Pacific/Kiritimati Pacific/Kiritimati
Pacific/Kosrae Pacific/Kosrae
Pacific/Majuro Pacific/Majuro
Pacific/Marquesas Pacific/Marquesas
Pacific/Nauru Pacific/Nauru
Pacific/Niue Pacific/Niue
Pacific/Norfolk Pacific/Norfolk
Pacific/Noumea Pacific/Noumea
Pacific/Pago_Pago Pacific/Pago_Pago
Pacific/Palau Pacific/Palau
Pacific/Pitcairn Pacific/Pitcairn
Pacific/Ponape Pacific/Ponape
Pacific/Port_Moresby Pacific/Port_Moresby
Pacific/Rarotonga Pacific/Rarotonga
Pacific/Saipan Pacific/Saipan
Pacific/Samoa Pacific/Pago_Pago
Pacific/Tahiti Pacific/Tahiti
Pacific/Tarawa Pacific/Tarawa
Pacific/Tongatapu Pacific/Tongatapu
Pacific/Truk Pacific/Truk
Pacific/Wake Pacific/Wake
Pacific/Wallis Pacific/Wallis
Poland Europe/Warsaw
Portugal Europe/Lisbon
ROC Asia/Taipei
ROK Asia/Seoul
SAUST-3 Asia/Riyadh
SAUST-3SAUDT Europe/Moscow
Singapore Asia/Singapore
SystemV/AST4ADT America/Thule
SystemV/CST6CDT America/Chicago
SystemV/EST5EDT America/New_York
SystemV/MST7MDT America/Denver
SystemV/PST8PDT America/Los_Angeles
SystemV/YST9YDT America/Anchorage
TAIST-8 Asia/Taipei
TAIST-8TAIDT Asia/Irkutsk
TASHST-6 Asia/Dacca
TASHST-6TASHDT Asia/Novosibirsk
THAIST-7 Asia/Bangkok
THAIST-7THAIDT Asia/Krasnoyarsk
Turkey Europe/Istanbul
UCT UTC
US/Alaska America/Anchorage
US/Aleutian America/Adak
US/Arizona America/Phoenix
US/Central America/Chicago
US/East-Indiana America/Indiana/Indianapolis
US/Eastern America/New_York
US/Hawaii Pacific/Honolulu
US/Indiana-Starke America/Indiana/Knox
US/Michigan America/New_York
US/Mountain America/Denver
US/Pacific America/Los_Angeles
US/Samoa Pacific/Pago_Pago
USAST-2 Africa/Johannesburg
USAST-2USADT Europe/Istanbul
UTC UTC
UYT3UYST America/Montevideo
Universal UTC
W-SU Europe/Moscow
WAUST-8 Australia/Perth
WAUST-8WAUDT Australia/Perth
WET WET
WET-2 Africa/Johannesburg
WET-2WET Europe/Helsinki
WST-4 Asia/Dubai
WST-4WDT Europe/Samara
Zulu UTC

View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 1996, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2026 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef AIX_INCLUDE_DL_INFO_H
#define AIX_INCLUDE_DL_INFO_H
/* struct for dladdr
* Differences between AIX dladdr and Linux dladdr:
*
* 1) Dl_info.dli_fbase: can never work, is not included in our struct
* A loaded image on AIX is divided in multiple segments, at least two
* (text and data) but potentially also far more. This is because the loader may
* load each member into an own segment, as for instance happens with the libC.a
* 2) Dl_info.dli_sname: This only works for code symbols (functions); for data, a
* zero-length string is returned ("").
* 3) Dl_info.dli_saddr: For code, this will return the entry point of the function,
* not the function descriptor.
*/
typedef struct {
const char *dli_fname; /* file path of loaded library */
const char *dli_sname; /* symbol name; "" if not known */
void *dli_saddr; /* address of *entry* of function; not function descriptor; */
} Dl_info;
#endif

View file

@ -0,0 +1,246 @@
/*
* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2025 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "jni.h"
#include "jni_util.h"
#include "ProcessHandleImpl_unix.h"
#include <sys/procfs.h>
#include <procinfo.h>
#include <string.h>
/*
* Implementation of native ProcessHandleImpl functions for AIX.
* See ProcessHandleImpl_unix.c for more details.
*/
void os_initNative(JNIEnv *env, jclass clazz) {}
/*
* Return pids of active processes, and optionally parent pids and
* start times for each process.
* For a specific non-zero pid, only the direct children are returned.
* If the pid is zero, all active processes are returned.
* Use getprocs64 to accumulate any process following the rules above.
* The resulting pids are stored into an array of longs named jarray.
* The number of pids is returned if they all fit.
* If the parentArray is non-null, store also the parent pid.
* In this case the parentArray must have the same length as the result pid array.
* Of course in the case of a given non-zero pid all entries in the parentArray
* will contain this pid, so this array does only make sense in the case of a given
* zero pid.
* If the jstimesArray is non-null, store also the start time of the pid.
* In this case the jstimesArray must have the same length as the result pid array.
* If the array(s) (is|are) too short, excess pids are not stored and
* the desired length is returned.
*/
jint os_getChildren(JNIEnv *env, jlong jpid, jlongArray jarray,
jlongArray jparentArray, jlongArray jstimesArray) {
pid_t pid = (pid_t) jpid;
jlong* pids = NULL;
jlong* ppids = NULL;
jlong* stimes = NULL;
jsize parentArraySize = 0;
jsize arraySize = 0;
jsize stimesSize = 0;
jsize count = 0;
arraySize = (*env)->GetArrayLength(env, jarray);
JNU_CHECK_EXCEPTION_RETURN(env, -1);
if (jparentArray != NULL) {
parentArraySize = (*env)->GetArrayLength(env, jparentArray);
JNU_CHECK_EXCEPTION_RETURN(env, -1);
if (arraySize != parentArraySize) {
JNU_ThrowIllegalArgumentException(env, "array sizes not equal");
return 0;
}
}
if (jstimesArray != NULL) {
stimesSize = (*env)->GetArrayLength(env, jstimesArray);
JNU_CHECK_EXCEPTION_RETURN(env, -1);
if (arraySize != stimesSize) {
JNU_ThrowIllegalArgumentException(env, "array sizes not equal");
return 0;
}
}
const int chunk = 100;
struct procentry64 ProcessBuffer[chunk];
pid_t idxptr = 0;
int i, num = 0;
do { // Block to break out of on Exception
pids = (*env)->GetLongArrayElements(env, jarray, NULL);
if (pids == NULL) {
break;
}
if (jparentArray != NULL) {
ppids = (*env)->GetLongArrayElements(env, jparentArray, NULL);
if (ppids == NULL) {
break;
}
}
if (jstimesArray != NULL) {
stimes = (*env)->GetLongArrayElements(env, jstimesArray, NULL);
if (stimes == NULL) {
break;
}
}
while ((num = getprocs64(ProcessBuffer, sizeof(struct procentry64), NULL,
sizeof(struct fdsinfo64), &idxptr, chunk)) != -1) {
for (i = 0; i < num; i++) {
pid_t childpid = (pid_t) ProcessBuffer[i].pi_pid;
pid_t ppid = (pid_t) ProcessBuffer[i].pi_ppid;
// Get the parent pid, and start time
if (pid == 0 || ppid == pid) {
if (count < arraySize) {
// Only store if it fits
pids[count] = (jlong) childpid;
if (ppids != NULL) {
// Store the parentPid
ppids[count] = (jlong) ppid;
}
if (stimes != NULL) {
// Store the process start time
stimes[count] = ((jlong) ProcessBuffer[i].pi_start) * 1000;;
}
}
count++; // Count to tabulate size needed
}
}
if (num < chunk) {
break;
}
}
} while (0);
if (pids != NULL) {
(*env)->ReleaseLongArrayElements(env, jarray, pids, 0);
}
if (ppids != NULL) {
(*env)->ReleaseLongArrayElements(env, jparentArray, ppids, 0);
}
if (stimes != NULL) {
(*env)->ReleaseLongArrayElements(env, jstimesArray, stimes, 0);
}
if (num == -1) {
JNU_ThrowByNameWithLastError(env,
"java/lang/RuntimeException", "Unable to retrieve Process info");
return -1;
}
// If more pids than array had size for; count will be greater than array size
return count;
}
pid_t os_getParentPidAndTimings(JNIEnv *env, pid_t pid, jlong *total, jlong *start) {
pid_t the_pid = pid;
struct procentry64 ProcessBuffer;
if (getprocs64(&ProcessBuffer, sizeof(ProcessBuffer), NULL, sizeof(struct fdsinfo64), &the_pid, 1) <= 0) {
return -1;
}
// Validate the pid before returning the info
if (kill(pid, 0) < 0) {
return -1;
}
*total = ((ProcessBuffer.pi_ru.ru_utime.tv_sec + ProcessBuffer.pi_ru.ru_stime.tv_sec) * 1000000000L) +
((ProcessBuffer.pi_ru.ru_utime.tv_usec + ProcessBuffer.pi_ru.ru_stime.tv_usec));
*start = ProcessBuffer.pi_start * (jlong)1000;
return (pid_t) ProcessBuffer.pi_ppid;
}
/**
* Helper function to get the 'psinfo_t' data from "/proc/%d/psinfo".
* Returns 0 on success and -1 on error.
*/
static int getPsinfo(pid_t pid, psinfo_t *psinfo) {
FILE* fp;
char fn[32];
size_t ret;
/*
* Try to open /proc/%d/psinfo
*/
snprintf(fn, sizeof fn, "/proc/%d/psinfo", pid);
fp = fopen(fn, "r");
if (fp == NULL) {
return -1;
}
ret = fread(psinfo, 1, sizeof(psinfo_t), fp);
fclose(fp);
if (ret < sizeof(psinfo_t)) {
return -1;
}
return 0;
}
void os_getCmdlineAndUserInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
psinfo_t psinfo;
char prargs[PRARGSZ + 1];
jstring cmdexe = NULL;
/*
* Now try to open /proc/%d/psinfo
*/
if (getPsinfo(pid, &psinfo) < 0) {
unix_fillArgArray(env, jinfo, 0, NULL, NULL, cmdexe, NULL);
return;
}
unix_getUserInfo(env, jinfo, psinfo.pr_uid);
JNU_CHECK_EXCEPTION(env);
/*
* Now read psinfo.pr_psargs which contains the first PRARGSZ characters of the
* argument list (i.e. arg[0] arg[1] ...). Unfortunately, PRARGSZ is usually set
* to 80 characters only. Nevertheless it's better than nothing :)
*/
strncpy(prargs, psinfo.pr_psargs, PRARGSZ);
prargs[PRARGSZ] = '\0';
if (prargs[0] == '\0') {
/* If psinfo.pr_psargs didn't contain any strings, use psinfo.pr_fname
* (which only contains the last component of exec()ed pathname) as a
* last resort. This is true for AIX kernel processes for example.
*/
strncpy(prargs, psinfo.pr_fname, PRARGSZ);
prargs[PRARGSZ] = '\0';
}
unix_fillArgArray(env, jinfo, 0, NULL, NULL, cmdexe,
prargs[0] == '\0' ? NULL : prargs);
}

View file

@ -0,0 +1,87 @@
/*
* Copyright (c) 2016, 2019 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include <stdio.h>
#include <string.h>
#include <sys/ldr.h>
#include "java_md_aix.h"
static unsigned char dladdr_buffer[0x8000];
static int fill_dll_info(void) {
return loadquery(L_GETINFO, dladdr_buffer, sizeof(dladdr_buffer));
}
static int dladdr_dont_reload(void *addr, Dl_info *info) {
const struct ld_info *p = (struct ld_info *)dladdr_buffer;
memset((void *)info, 0, sizeof(Dl_info));
for (;;) {
if (addr >= p->ldinfo_textorg &&
(char*)addr < (char*)(p->ldinfo_textorg) + p->ldinfo_textsize) {
info->dli_fname = p->ldinfo_filename;
return 1;
}
if (!p->ldinfo_next) {
break;
}
p = (struct ld_info *)(((char *)p) + p->ldinfo_next);
}
return 0;
}
int dladdr(void *addr, Dl_info *info) {
static int loaded = 0;
int rc = 0;
void *addr0;
if (!addr) {
return rc;
}
if (!loaded) {
if (fill_dll_info() == -1)
return rc;
loaded = 1;
}
// first try with addr on cached data
rc = dladdr_dont_reload(addr, info);
// addr could be an AIX function descriptor, so try dereferenced version
if (rc == 0) {
addr0 = *((void **)addr);
rc = dladdr_dont_reload(addr0, info);
}
// if we had no success until now, maybe loadquery info is outdated.
// refresh and retry
if (rc == 0) {
if (fill_dll_info() == -1)
return rc;
rc = dladdr_dont_reload(addr, info);
if (rc == 0) {
rc = dladdr_dont_reload(addr0, info);
}
}
return rc;
}

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2016, 2026 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef JAVA_MD_AIX_H
#define JAVA_MD_AIX_H
/*
* Very limited AIX port of dladdr() for libjli.so.
*
* We try to mimic dladdr(3) on Linux (see https://linux.die.net/man/3/dladdr)
* dladdr(3) is not POSIX but a GNU extension, and is not available on AIX.
*
* We only support Dl_info.dli_fname here as this is the only thing that is
* used of it by libjli.so. A more comprehensive port of dladdr can be found
* in the hotspot implementation which is not available at this place, though.
*/
#include "dl_info.h"
int dladdr(void *addr, Dl_info *info);
#endif /* JAVA_MD_AIX_H */

View file

@ -0,0 +1,232 @@
/*
* Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "jni.h"
#include "jni_util.h"
#include "jvm.h"
#include "jlong.h"
#include "java_nio_MappedMemoryUtils.h"
#include <assert.h>
#include <sys/mman.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <sys/procfs.h>
#include <unistd.h>
typedef char mincore_vec_t;
static long calculate_number_of_pages_in_range(void* address, size_t len, size_t pagesize) {
uintptr_t address_unaligned = (uintptr_t) address;
uintptr_t address_aligned = address_unaligned & (~(pagesize - 1));
size_t len2 = len + (address_unaligned - address_aligned);
long numPages = (len2 + pagesize - 1) / pagesize;
return numPages;
}
jboolean JNICALL MappedMemoryUtils_isLoaded0(JNIEnv *env, jobject obj, jlong address,
jlong len, jlong numPages)
{
jboolean loaded = JNI_TRUE;
int result = 0;
long i = 0;
void *a = (void *) jlong_to_ptr(address);
mincore_vec_t* vec = NULL;
/* See JDK-8186665 */
size_t pagesize = (size_t)sysconf(_SC_PAGESIZE);
if ((long)pagesize == -1) {
return JNI_FALSE;
}
numPages = (jlong) calculate_number_of_pages_in_range(a, len, pagesize);
/* Include space for one sentinel byte at the end of the buffer
* to catch overflows. */
vec = (mincore_vec_t*) malloc(numPages + 1);
if (vec == NULL) {
JNU_ThrowOutOfMemoryError(env, NULL);
return JNI_FALSE;
}
vec[numPages] = '\x7f'; /* Write sentinel. */
result = mincore(a, (size_t)len, vec);
assert(vec[numPages] == '\x7f'); /* Check sentinel. */
if (result == -1) {
JNU_ThrowIOExceptionWithLastError(env, "mincore failed");
free(vec);
return JNI_FALSE;
}
for (i=0; i<numPages; i++) {
if (vec[i] == 0) {
loaded = JNI_FALSE;
break;
}
}
free(vec);
return loaded;
}
void JNICALL MappedMemoryUtils_load0(JNIEnv *env, jobject obj, jlong address,
jlong len)
{
char *a = (char *)jlong_to_ptr(address);
int result = madvise((caddr_t)a, (size_t)len, MADV_WILLNEED);
if (result == -1) {
JNU_ThrowIOExceptionWithMessageAndLastError(env, "madvise with advise MADV_WILLNEED failed");
}
}
void JNICALL MappedMemoryUtils_unload0(JNIEnv *env, jobject obj, jlong address,
jlong len)
{
char *a = (char *)jlong_to_ptr(address);
int result = madvise((caddr_t)a, (size_t)len, MADV_DONTNEED);
if (result == -1) {
JNU_ThrowIOExceptionWithMessageAndLastError(env, "madvise with advise MADV_DONTNEED failed");
}
}
static void set_error_if_shared(JNIEnv* env, prmap_t* map_entry)
{
if (map_entry->pr_mflags & MA_SHARED) {
// MA_SHARED => MAP_SHARED => !MAP_PRIVATE. This error is valid and should be thrown.
JNU_ThrowIOExceptionWithMessageAndLastError(env, "msync with parameter MS_SYNC failed (MAP_SHARED)");
return;
} else {
// O.W. MAP_PRIVATE or no flag was specified and EINVAL is the expected behaviour.
return;
}
}
static void check_proc_map_array(JNIEnv* env, FILE* proc_file, prmap_t* map_entry, void* end_address)
{
while (!feof(proc_file)) {
memset(map_entry, '\0', sizeof(prmap_t));
fread((char*)map_entry, sizeof(prmap_t), 1, proc_file);
if (ferror(proc_file)) {
JNU_ThrowIOExceptionWithMessageAndLastError(env,
"msync with parameter MS_SYNC failed (could not read /proc/<pid>/map)");
return;
} else if (map_entry->pr_vaddr <= end_address &&
(uint64_t)end_address <= (uint64_t)map_entry->pr_vaddr + map_entry->pr_size) {
set_error_if_shared(env, map_entry);
return;
}
}
JNU_ThrowIOExceptionWithMessageAndLastError(env,
"msync with parameter MS_SYNC failed (address not found)");
}
// '/proc/' + <pid> + '/map' + '\0'
#define PFNAME_LEN 32
static void check_aix_einval(JNIEnv* env, void* end_address)
{
// If EINVAL is set for a mmap address on AIX, additional validation is required.
// AIX will set EINVAL when msync is called on a mmap address that didn't receive MAP_SHARED
// as a flag (since MAP_PRIVATE is the default).
// https://www.ibm.com/docs/en/aix/7.2?topic=m-msync-subroutine
FILE* proc_file;
{
char* fname = (char*) malloc(sizeof(char) * PFNAME_LEN);
if (fname == NULL) {
JNU_ThrowOutOfMemoryError(env, NULL);
return;
}
pid_t the_pid = getpid();
jio_snprintf(fname, PFNAME_LEN, "/proc/%d/map", the_pid);
proc_file = fopen(fname, "r");
free(fname);
}
if (!proc_file) {
JNU_ThrowIOExceptionWithMessageAndLastError(env,
"msync with parameter MS_SYNC failed (could not open /proc/<pid>/map)");
return;
}
{
prmap_t* map_entry = (prmap_t*) malloc(sizeof(prmap_t));
if (map_entry == NULL) {
JNU_ThrowOutOfMemoryError(env, NULL);
fclose(proc_file);
return;
}
check_proc_map_array(env, proc_file, map_entry, end_address);
free(map_entry);
}
fclose(proc_file);
}
// Normally we would just let msync handle this, but since we'll be (potentially) ignoring
// the error code returned by msync, we check the args before the call instead.
static int validate_msync_address(size_t address)
{
size_t pagesize = (size_t)sysconf(_SC_PAGESIZE);
if (address % pagesize != 0) {
errno = EINVAL;
return -1;
}
return 0;
}
void JNICALL MappedMemoryUtils_force0(JNIEnv *env, jobject obj, jobject fdo,
jlong address, jlong len)
{
void* a = (void *)jlong_to_ptr(address);
if (validate_msync_address((size_t)a) > 0) {
JNU_ThrowIOExceptionWithMessageAndLastError(env,
"msync with parameter MS_SYNC failed (arguments invalid)");
return;
}
int result = msync(a, (size_t)len, MS_SYNC);
if (result == -1) {
void* end_address = (void*)jlong_to_ptr(address + len);
if (errno == EINVAL) {
check_aix_einval(env, end_address);
return;
}
JNU_ThrowIOExceptionWithMessageAndLastError(env, "msync with parameter MS_SYNC failed");
}
}
#define FD "Ljava/io/FileDescriptor;"
static JNINativeMethod methods[] = {
{"isLoaded0", "(JJJ)Z", (void *)&MappedMemoryUtils_isLoaded0},
{"load0", "(JJ)V", (void *)&MappedMemoryUtils_load0},
{"unload0", "(JJ)V", (void *)&MappedMemoryUtils_unload0},
{"force0", "(" FD "JJ)V", (void *)&MappedMemoryUtils_force0},
};
JNIEXPORT void JNICALL
Java_java_nio_MappedMemoryUtils_registerNatives(JNIEnv *env, jclass cls)
{
(*env)->RegisterNatives(env, cls,
methods, sizeof(methods)/sizeof(methods[0]));
}

View file

@ -0,0 +1,129 @@
/*
* Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include "jni.h"
#include "jni_util.h"
#include "jlong.h"
#include "nio.h"
#include "nio_util.h"
#include "java_lang_Integer.h"
#include <assert.h>
static jlong
handle(JNIEnv *env, jlong rv, char *msg)
{
if (rv >= 0)
return rv;
if (errno == EINTR)
return IOS_INTERRUPTED;
JNU_ThrowIOExceptionWithLastError(env, msg);
return IOS_THROWN;
}
// AIX
JNIEXPORT jint JNICALL
Java_sun_nio_ch_FileDispatcherImpl_force0(JNIEnv *env, jobject this,
jobject fdo, jboolean md)
{
jint fd = fdval(env, fdo);
int result = 0;
if (md == JNI_FALSE) {
result = fdatasync(fd);
} else {
/* Calling fsync on a file descriptor that is opened only for
* reading results in an error ("EBADF: The FileDescriptor parameter is
* not a valid file descriptor open for writing.").
* However, at this point it is not possibly anymore to read the
* 'writable' attribute of the corresponding file channel so we have to
* use 'fcntl'.
*/
int getfl = fcntl(fd, F_GETFL);
if (getfl >= 0 && (getfl & O_ACCMODE) == O_RDONLY) {
return 0;
}
result = fsync(fd);
}
return handle(env, result, "Force failed");
}
JNIEXPORT jlong JNICALL
Java_sun_nio_ch_FileDispatcherImpl_transferTo0(JNIEnv *env, jobject this,
jobject srcFDO,
jlong position, jlong count,
jobject dstFDO, jboolean append)
{
jint srcFD = fdval(env, srcFDO);
jint dstFD = fdval(env, dstFDO);
jlong max = (jlong)java_lang_Integer_MAX_VALUE;
struct sf_parms sf_iobuf;
jlong result;
if (position > max)
return IOS_UNSUPPORTED_CASE;
if (count > max)
count = max;
memset(&sf_iobuf, 0, sizeof(sf_iobuf));
sf_iobuf.file_descriptor = srcFD;
sf_iobuf.file_offset = (off_t)position;
sf_iobuf.file_bytes = count;
result = send_file(&dstFD, &sf_iobuf, SF_SYNC_CACHE);
/* AIX send_file() will return 0 when this operation complete successfully,
* return 1 when partial bytes transferred and return -1 when an error has
* occurred.
*/
if (result == -1) {
if (errno == EWOULDBLOCK)
return IOS_UNAVAILABLE;
if ((errno == EINVAL) && ((ssize_t)count >= 0))
return IOS_UNSUPPORTED_CASE;
if (errno == EINTR)
return IOS_INTERRUPTED;
if (errno == ENOTSOCK)
return IOS_UNSUPPORTED;
JNU_ThrowIOExceptionWithLastError(env, "Transfer failed");
return IOS_THROWN;
}
if (sf_iobuf.bytes_sent > 0)
return (jlong)sf_iobuf.bytes_sent;
return IOS_UNSUPPORTED_CASE;
}

View file

@ -0,0 +1,168 @@
/*
* Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2024 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "jni.h"
#include "jni_util.h"
#include "jvm.h"
#include "jlong.h"
#include "sun_nio_ch_Pollset.h"
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <sys/pollset.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
typedef pollset_t pollset_create_func(int maxfd);
typedef int pollset_destroy_func(pollset_t ps);
typedef int pollset_ctl_func(pollset_t ps, struct poll_ctl *pollctl_array, int array_length);
typedef int pollset_poll_func(pollset_t ps, struct pollfd *polldata_array, int array_length, int timeout);
static pollset_create_func* _pollset_create = NULL;
static pollset_destroy_func* _pollset_destroy = NULL;
static pollset_ctl_func* _pollset_ctl = NULL;
static pollset_poll_func* _pollset_poll = NULL;
JNIEXPORT void JNICALL
Java_sun_nio_ch_Pollset_init(JNIEnv* env, jclass this) {
_pollset_create = (pollset_create_func*) dlsym(RTLD_DEFAULT, "pollset_create");
_pollset_destroy = (pollset_destroy_func*) dlsym(RTLD_DEFAULT, "pollset_destroy");
_pollset_ctl = (pollset_ctl_func*) dlsym(RTLD_DEFAULT, "pollset_ctl");
_pollset_poll = (pollset_poll_func*) dlsym(RTLD_DEFAULT, "pollset_poll");
if (_pollset_create == NULL || _pollset_destroy == NULL ||
_pollset_ctl == NULL || _pollset_poll == NULL) {
JNU_ThrowInternalError(env, "unable to get address of pollset functions");
}
}
JNIEXPORT jint JNICALL
Java_sun_nio_ch_Pollset_eventSize(JNIEnv* env, jclass this) {
return sizeof(struct pollfd);
}
JNIEXPORT jint JNICALL
Java_sun_nio_ch_Pollset_eventsOffset(JNIEnv* env, jclass this) {
return offsetof(struct pollfd, events);
}
JNIEXPORT jint JNICALL
Java_sun_nio_ch_Pollset_reventsOffset(JNIEnv* env, jclass this) {
return offsetof(struct pollfd, revents);
}
JNIEXPORT jint JNICALL
Java_sun_nio_ch_Pollset_fdOffset(JNIEnv* env, jclass this) {
return offsetof(struct pollfd, fd);
}
JNIEXPORT jint JNICALL
Java_sun_nio_ch_Pollset_pollsetCreate(JNIEnv *env, jclass c) {
/* pollset_create can take the maximum number of fds, but we
* cannot predict this number so we leave it at OPEN_MAX. */
pollset_t ps = _pollset_create(-1);
if (ps < 0) {
JNU_ThrowIOExceptionWithLastError(env, "pollset_create failed");
}
return (int)ps;
}
JNIEXPORT jint JNICALL
Java_sun_nio_ch_Pollset_pollsetCtl(JNIEnv *env, jclass c, jint ps,
jint opcode, jint fd, jint events) {
struct poll_ctl event;
int res;
event.cmd = opcode;
event.events = events;
event.fd = fd;
RESTARTABLE(_pollset_ctl((pollset_t)ps, &event, 1 /* length */), res);
return (res == 0) ? 0 : errno;
}
JNIEXPORT jint JNICALL
Java_sun_nio_ch_Pollset_pollsetPoll(JNIEnv *env, jclass c,
jint ps, jlong address, jint numfds, jint timeout) {
struct pollfd *events = jlong_to_ptr(address);
int res;
RESTARTABLE(_pollset_poll(ps, events, numfds, timeout), res);
if (res < 0) {
perror("pollset_poll failed");
JNU_ThrowIOExceptionWithLastError(env, "pollset_poll failed");
}
return res;
}
JNIEXPORT void JNICALL
Java_sun_nio_ch_Pollset_pollsetDestroy(JNIEnv *env, jclass c, jint ps) {
int res;
RESTARTABLE(_pollset_destroy((pollset_t)ps), res);
}
JNIEXPORT void JNICALL
Java_sun_nio_ch_Pollset_socketpair(JNIEnv* env, jclass clazz, jintArray sv) {
int sp[2];
if (socketpair(PF_UNIX, SOCK_STREAM, 0, sp) == -1) {
JNU_ThrowIOExceptionWithLastError(env, "socketpair failed");
} else {
jint res[2];
res[0] = (jint)sp[0];
res[1] = (jint)sp[1];
(*env)->SetIntArrayRegion(env, sv, 0, 2, &res[0]);
}
}
JNIEXPORT void JNICALL
Java_sun_nio_ch_Pollset_interrupt(JNIEnv *env, jclass c, jint fd) {
int res;
int buf[1];
buf[0] = 1;
RESTARTABLE(write(fd, buf, 1), res);
if (res < 0) {
JNU_ThrowIOExceptionWithLastError(env, "write failed");
}
}
JNIEXPORT void JNICALL
Java_sun_nio_ch_Pollset_drain1(JNIEnv *env, jclass cl, jint fd) {
int res;
char buf[1];
RESTARTABLE(read(fd, buf, 1), res);
if (res < 0) {
JNU_ThrowIOExceptionWithLastError(env, "drain1 failed");
}
}
JNIEXPORT void JNICALL
Java_sun_nio_ch_Pollset_close0(JNIEnv *env, jclass c, jint fd) {
int res;
RESTARTABLE(close(fd), res);
}

View file

@ -0,0 +1,235 @@
/*
* Copyright (c) 2008, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2023 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/mntctl.h>
#include "jni.h"
#include "jni_util.h"
#include "sun_nio_fs_AixNativeDispatcher.h"
static jfieldID entry_name;
static jfieldID entry_dir;
static jfieldID entry_fstype;
static jfieldID entry_options;
static jclass entry_cls;
/**
* Call this to throw an internal UnixException when a system/library
* call fails
*/
static void throwUnixException(JNIEnv* env, int errnum) {
jobject x = JNU_NewObjectByName(env, "sun/nio/fs/UnixException",
"(I)V", errnum);
if (x != NULL) {
(*env)->Throw(env, x);
}
}
/**
* Initialization
*/
JNIEXPORT void JNICALL
Java_sun_nio_fs_AixNativeDispatcher_init(JNIEnv* env, jclass this)
{
jclass clazz;
clazz = (*env)->FindClass(env, "sun/nio/fs/UnixMountEntry");
CHECK_NULL(clazz);
entry_name = (*env)->GetFieldID(env, clazz, "name", "[B");
CHECK_NULL(entry_name);
entry_dir = (*env)->GetFieldID(env, clazz, "dir", "[B");
CHECK_NULL(entry_dir);
entry_fstype = (*env)->GetFieldID(env, clazz, "fstype", "[B");
CHECK_NULL(entry_fstype);
entry_options = (*env)->GetFieldID(env, clazz, "opts", "[B");
CHECK_NULL(entry_options);
entry_cls = (*env)->NewGlobalRef(env, clazz);
if (entry_cls == NULL) {
JNU_ThrowOutOfMemoryError(env, NULL);
return;
}
}
/**
* Special implementation of getextmntent (see SolarisNativeDispatcher.c)
* that returns all entries at once.
*/
JNIEXPORT jobjectArray JNICALL
Java_sun_nio_fs_AixNativeDispatcher_getmntctl(JNIEnv* env, jclass this)
{
int must_free_buf = 0;
char stack_buf[1024];
char* buffer = stack_buf;
size_t buffer_size = 1024;
int num_entries;
int i;
jobjectArray ret;
struct vmount * vm;
for (i = 0; i < 5; i++) {
num_entries = mntctl(MCTL_QUERY, buffer_size, buffer);
if (num_entries != 0) {
break;
}
if (must_free_buf) {
free(buffer);
}
buffer_size *= 8;
buffer = malloc(buffer_size);
if (buffer == NULL) {
throwUnixException(env, errno);
return NULL;
}
must_free_buf = 1;
}
/* Treat zero entries like errors. */
if (num_entries <= 0) {
if (must_free_buf) {
free(buffer);
}
throwUnixException(env, errno);
return NULL;
}
ret = (*env)->NewObjectArray(env, num_entries, entry_cls, NULL);
if (ret == NULL) {
if (must_free_buf) {
free(buffer);
}
return NULL;
}
vm = (struct vmount*)buffer;
for (i = 0; i < num_entries; i++) {
jsize len;
jbyteArray bytes;
const char* fstype;
/* We set all relevant attributes so there is no need to call constructor. */
jobject entry = (*env)->AllocObject(env, entry_cls);
if (entry == NULL) {
if (must_free_buf) {
free(buffer);
}
return NULL;
}
(*env)->SetObjectArrayElement(env, ret, i, entry);
/* vm->vmt_data[...].vmt_size is 32 bit aligned and also includes NULL byte. */
/* Since we only need the characters, it is necessary to check string size manually. */
len = strlen((char*)vm + vm->vmt_data[VMT_OBJECT].vmt_off);
bytes = (*env)->NewByteArray(env, len);
if (bytes == NULL) {
if (must_free_buf) {
free(buffer);
}
return NULL;
}
(*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte*)((char *)vm + vm->vmt_data[VMT_OBJECT].vmt_off));
(*env)->SetObjectField(env, entry, entry_name, bytes);
len = strlen((char*)vm + vm->vmt_data[VMT_STUB].vmt_off);
bytes = (*env)->NewByteArray(env, len);
if (bytes == NULL) {
if (must_free_buf) {
free(buffer);
}
return NULL;
}
(*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte*)((char *)vm + vm->vmt_data[VMT_STUB].vmt_off));
(*env)->SetObjectField(env, entry, entry_dir, bytes);
switch (vm->vmt_gfstype) {
case MNT_J2:
fstype = "jfs2";
break;
case MNT_NAMEFS:
fstype = "namefs";
break;
case MNT_NFS:
fstype = "nfs";
break;
case MNT_JFS:
fstype = "jfs";
break;
case MNT_CDROM:
fstype = "cdrom";
break;
case MNT_PROCFS:
fstype = "procfs";
break;
case MNT_NFS3:
fstype = "nfs3";
break;
case MNT_AUTOFS:
fstype = "autofs";
break;
case MNT_UDF:
fstype = "udfs";
break;
case MNT_NFS4:
fstype = "nfs4";
break;
case MNT_CIFS:
fstype = "smbfs";
break;
default:
fstype = "unknown";
}
len = strlen(fstype);
bytes = (*env)->NewByteArray(env, len);
if (bytes == NULL) {
if (must_free_buf) {
free(buffer);
}
return NULL;
}
(*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte*)fstype);
(*env)->SetObjectField(env, entry, entry_fstype, bytes);
len = strlen((char*)vm + vm->vmt_data[VMT_ARGS].vmt_off);
bytes = (*env)->NewByteArray(env, len);
if (bytes == NULL) {
if (must_free_buf) {
free(buffer);
}
return NULL;
}
(*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte*)((char *)vm + vm->vmt_data[VMT_ARGS].vmt_off));
(*env)->SetObjectField(env, entry, entry_options, bytes);
/* goto the next vmount structure: */
vm = (struct vmount *)((char *)vm + vm->vmt_length);
}
if (must_free_buf) {
free(buffer);
}
return ret;
}

View file

@ -0,0 +1,208 @@
/*
* Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, IBM Corp.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// Note: the include below is not strictly required, as dependencies will be pulled using linker flags.
// Adding at least one #include removes unwanted warnings on some platforms.
#include <string.h>
#include <math.h>
#include "jni_util.h"
DEF_STATIC_JNI_OnLoad
// Addresses of functions to be referenced using static linking.
void* funcs[] = {
//string.h
&strlen,
&strcat,
//math.h
&abs,
&fabs,
&fabsf,
&fabsl,
&fmod,
&fmodf,
&fmodl,
&remainder,
&remainderf,
&remainderl,
&remquo,
&remquof,
&remquol,
&fma,
&fmaf,
&fmal,
&fmax,
&fmaxf,
&fmaxl,
&fmin,
&fminf,
&fminl,
&fdim,
&fdimf,
&fdiml,
&nan,
&nanf,
&nanl,
&exp,
&expf,
&expl,
&exp2,
&exp2f,
&exp2l,
&expm1,
&expm1f,
&expm1l,
&log,
&logf,
&logl,
&log10,
&log10f,
&log10l,
&log2,
&log2f,
&log2l,
&log1p,
&log1pf,
&log1pl,
&pow,
&powf,
&powl,
&sqrt,
&sqrtf,
&sqrtl,
&cbrt,
&cbrtf,
&cbrtl,
&hypot,
&hypotf,
&hypotl,
&sin,
&sinf,
&sinl,
&cos,
&cosf,
&cosl,
&tan,
&tanf,
&tanl,
&asin,
&asinf,
&asinl,
&acos,
&acosf,
&acosl,
&atan,
&atanf,
&atanl,
&atan2,
&atan2f,
&atan2l,
&sinh,
&sinhf,
&sinhl,
&cosh,
&coshf,
&coshl,
&tanh,
&tanhf,
&tanhl,
&asinh,
&asinhf,
&asinhl,
&acosh,
&acoshf,
&acoshl,
&atanh,
&atanhf,
&atanhl,
&erf,
&erff,
&erfl,
&erfc,
&erfcf,
&erfcl,
&tgamma,
&tgammaf,
&tgammal,
&lgamma,
&lgammaf,
&lgammal,
&ceil,
&ceilf,
&ceill,
&floor,
&floorf,
&floorl,
&trunc,
&truncf,
&truncl,
&round,
&roundf,
&roundl,
&lround,
&lroundf,
&lroundl,
&llround,
&llroundf,
&llroundl,
&nearbyint,
&nearbyintf,
&nearbyintl,
&rintf,
&rintl,
&lrint,
&lrintf,
&lrintl,
&llrint,
&llrintf,
&llrintl,
&frexpf,
&ldexpf,
&modff,
&scalbn,
&scalbnf,
&scalbnl,
&scalbln,
&scalblnf,
&scalblnl,
&ilogb,
&ilogbf,
&ilogbl,
&logb,
&logbf,
&logbl,
&nextafter,
&nextafterf,
&nextafterl,
&nexttoward,
&nexttowardf,
&nexttowardl,
&copysign,
&copysignf,
&copysignl,
&isnan
};

View file

@ -0,0 +1,120 @@
/*
* Copyright (c) 2020, Red Hat Inc.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.platform;
/**
* Data structure to hold info from /proc/self/cgroup,
* /proc/cgroups and /proc/self/mountinfo
*
* man 7 cgroups
*
* @see CgroupSubsystemFactory
*/
public class CgroupInfo {
private final String name;
private final int hierarchyId;
private final boolean enabled;
private String mountPoint;
private String mountRoot;
private String cgroupPath;
private CgroupInfo(String name, int hierarchyId, boolean enabled) {
this.name = name;
this.hierarchyId = hierarchyId;
this.enabled = enabled;
}
public String getName() {
return name;
}
public int getHierarchyId() {
return hierarchyId;
}
public boolean isEnabled() {
return enabled;
}
public String getMountPoint() {
return mountPoint;
}
public void setMountPoint(String mountPoint) {
this.mountPoint = mountPoint;
}
public String getMountRoot() {
return mountRoot;
}
public void setMountRoot(String mountRoot) {
this.mountRoot = mountRoot;
}
public String getCgroupPath() {
return cgroupPath;
}
public void setCgroupPath(String cgroupPath) {
this.cgroupPath = cgroupPath;
}
/*
* Creates a CgroupInfo instance from a line in /proc/cgroups.
* Comment token (hash) is handled by the caller.
*
* Example (annotated):
*
* #subsys_name hierarchy num_cgroups enabled
* cpuset 10 1 1 (a)
* cpu 7 8 1 (b)
* [...]
*
* Line (a) would yield:
* info = new CgroupInfo("cpuset", 10, true);
* return info;
* Line (b) results in:
* info = new CgroupInfo("cpu", 7, true);
* return info;
*
*
* See CgroupSubsystemFactory.determineType()
*
*/
static CgroupInfo fromCgroupsLine(String line) {
String[] tokens = line.split("\\s+");
if (tokens.length != 4) {
return null;
}
// discard 3'rd field, num_cgroups
return new CgroupInfo(tokens[0] /* name */,
Integer.parseInt(tokens[1]) /* hierarchyId */,
(Integer.parseInt(tokens[3]) == 1) /* enabled */);
}
}

View file

@ -0,0 +1,206 @@
/*
* Copyright (c) 2020, 2021, Red Hat Inc.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.platform;
import java.util.Objects;
public class CgroupMetrics implements Metrics {
private final CgroupSubsystem subsystem;
CgroupMetrics(CgroupSubsystem subsystem) {
this.subsystem = Objects.requireNonNull(subsystem);
}
@Override
public boolean isContainerized() {
return isContainerized0();
}
@Override
public String getProvider() {
return subsystem.getProvider();
}
@Override
public long getCpuUsage() {
return subsystem.getCpuUsage();
}
@Override
public long[] getPerCpuUsage() {
return subsystem.getPerCpuUsage();
}
@Override
public long getCpuUserUsage() {
return subsystem.getCpuUserUsage();
}
@Override
public long getCpuSystemUsage() {
return subsystem.getCpuSystemUsage();
}
@Override
public long getCpuPeriod() {
return subsystem.getCpuPeriod();
}
@Override
public long getCpuQuota() {
return subsystem.getCpuQuota();
}
@Override
public long getCpuShares() {
return subsystem.getCpuShares();
}
@Override
public long getCpuNumPeriods() {
return subsystem.getCpuNumPeriods();
}
@Override
public long getCpuNumThrottled() {
return subsystem.getCpuNumThrottled();
}
@Override
public long getCpuThrottledTime() {
return subsystem.getCpuThrottledTime();
}
@Override
public long getEffectiveCpuCount() {
return subsystem.getEffectiveCpuCount();
}
@Override
public int[] getCpuSetCpus() {
return subsystem.getCpuSetCpus();
}
@Override
public int[] getEffectiveCpuSetCpus() {
return subsystem.getEffectiveCpuSetCpus();
}
@Override
public int[] getCpuSetMems() {
return subsystem.getCpuSetMems();
}
@Override
public int[] getEffectiveCpuSetMems() {
return subsystem.getEffectiveCpuSetMems();
}
public long getMemoryFailCount() {
return subsystem.getMemoryFailCount();
}
@Override
public long getMemoryLimit() {
long subsMem = subsystem.getMemoryLimit();
long systemTotal = getTotalMemorySize0();
assert(systemTotal > 0);
// Catch the cgroup memory limit exceeding host physical memory.
// Treat this as unlimited.
if (subsMem >= systemTotal) {
return CgroupSubsystem.LONG_RETVAL_UNLIMITED;
}
return subsMem;
}
@Override
public long getMemoryUsage() {
return subsystem.getMemoryUsage();
}
@Override
public long getTcpMemoryUsage() {
return subsystem.getTcpMemoryUsage();
}
@Override
public long getMemoryAndSwapLimit() {
long totalSystemMemSwap = getTotalMemorySize0() + getTotalSwapSize0();
assert(totalSystemMemSwap > 0);
// Catch the cgroup memory and swap limit exceeding host physical swap
// and memory. Treat this case as unlimited.
long subsSwapMem = subsystem.getMemoryAndSwapLimit();
if (subsSwapMem >= totalSystemMemSwap) {
return CgroupSubsystem.LONG_RETVAL_UNLIMITED;
}
return subsSwapMem;
}
@Override
public long getMemoryAndSwapUsage() {
return subsystem.getMemoryAndSwapUsage();
}
@Override
public long getMemorySoftLimit() {
return subsystem.getMemorySoftLimit();
}
@Override
public long getPidsMax() {
return subsystem.getPidsMax();
}
@Override
public long getPidsCurrent() {
return subsystem.getPidsCurrent();
}
@Override
public long getBlkIOServiceCount() {
return subsystem.getBlkIOServiceCount();
}
@Override
public long getBlkIOServiced() {
return subsystem.getBlkIOServiced();
}
public static Metrics getInstance() {
if (!isUseContainerSupport()) {
// Return null on -XX:-UseContainerSupport
return null;
}
return CgroupSubsystemFactory.create();
}
private static native boolean isUseContainerSupport();
private static native boolean isContainerized0();
private static native long getTotalMemorySize0();
private static native long getTotalSwapSize0();
}

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 2020, Red Hat Inc.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.platform;
/**
* Marker interface for cgroup-based metrics
*
*/
public interface CgroupSubsystem extends Metrics {
default boolean isContainerized() {
return false; // This default impl is never used
}
/**
* Returned for metrics of type long if the underlying implementation
* has determined that no limit is being imposed.
*/
public static final long LONG_RETVAL_UNLIMITED = -1;
public static final String MAX_VAL = "max";
public static long limitFromString(String strVal) {
if (strVal == null || MAX_VAL.equals(strVal)) {
return CgroupSubsystem.LONG_RETVAL_UNLIMITED;
}
return Long.parseLong(strVal);
}
}

View file

@ -0,0 +1,254 @@
/*
* Copyright (c) 2020, 2022, Red Hat Inc.
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.platform;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Stream;
/**
* Cgroup version agnostic controller logic
*
*/
public interface CgroupSubsystemController {
public static final String EMPTY_STR = "";
public String path();
/**
* getStringValue
*
* Return the first line of the file "param" argument from the controller.
*
* TODO: Consider using weak references for caching BufferedReader object.
*
* @param controller
* @param param
* @return Returns the contents of the file specified by param or null if
* an error occurs.
*/
public static String getStringValue(CgroupSubsystemController controller, String param) {
if (controller == null) return null;
Path filePath = Path.of(controller.path(), param);
try (Stream<String> lines = Files.lines(filePath)) {
Optional<String> firstLine = lines.findFirst();
return firstLine.orElse(null);
} catch (UncheckedIOException | IOException e) {
return null;
}
}
/**
* Get an entry from file "param" within the "controller" directory path
* which matches string "match". Applies "conversion" to the matching line.
*
* @param controller
* @param param
* @param match
* @param conversion
* @param defaultRetval
* @return The long value as derived by applying "conversion" to the matching
* line or "defaultRetval" if there was an error or no match found.
*/
public static long getLongValueMatchingLine(CgroupSubsystemController controller,
String param,
String match,
Function<String, Long> conversion,
long defaultRetval) {
long retval = defaultRetval;
if (controller == null) {
return retval;
}
try {
Path filePath = Path.of(controller.path(), param);
List<String> lines = Files.readAllLines(filePath);
for (String line : lines) {
if (line.startsWith(match)) {
retval = conversion.apply(line);
break;
}
}
} catch (IOException e) {
// Ignore. Default is unlimited.
}
return retval;
}
/**
* Get a long value from directory "controller" and file "param", by
* applying "conversion" to the string value within the file.
*
* @param controller
* @param param
* @param conversion
* @param defaultRetval
* @return The converted long value or "defaultRetval" if there was an
* error.
*/
public static long getLongValue(CgroupSubsystemController controller,
String param,
Function<String, Long> conversion,
long defaultRetval) {
String strval = getStringValue(controller, param);
if (strval == null) return defaultRetval;
return conversion.apply(strval);
}
/**
* Get a double value from file "param" within "controller".
*
* @param controller
* @param param
* @param defaultRetval
* @return The double value or "defaultRetval" if there was an error.
*/
public static double getDoubleValue(CgroupSubsystemController controller, String param, double defaultRetval) {
String strval = getStringValue(controller, param);
if (strval == null) return defaultRetval;
double retval = Double.parseDouble(strval);
return retval;
}
/**
* getLongEntry
*
* Return the long value from the line containing the string "entryname"
* within file "param" in the "controller".
*
* TODO: Consider using weak references for caching BufferedReader object.
*
* @param controller
* @param param
* @param entryname
* @return long value or "defaultRetval" if there was an error or no match
* was found.
*/
public static long getLongEntry(CgroupSubsystemController controller, String param, String entryname, long defaultRetval) {
if (controller == null) return defaultRetval;
try (Stream<String> lines = Files.lines(Path.of(controller.path(), param))) {
Optional<String> result = lines.map(line -> line.split(" "))
.filter(line -> (line.length == 2 &&
line[0].equals(entryname)))
.map(line -> line[1])
.findFirst();
return result.isPresent() ? Long.parseLong(result.get()) : defaultRetval;
} catch (UncheckedIOException | IOException e) {
return defaultRetval;
}
}
/**
* stringRangeToIntArray
*
* Convert a string in the form of 1,3-4,6 to an array of
* integers containing all the numbers in the range.
*
* @param range
* @return int[] containing a sorted list of numbers as represented by
* the string range. Returns null if there was an error or the input
* was an empty string.
*/
public static int[] stringRangeToIntArray(String range) {
if (range == null || EMPTY_STR.equals(range)) return null;
ArrayList<Integer> results = new ArrayList<>();
String strs[] = range.split(",");
for (String str : strs) {
if (str.contains("-")) {
String lohi[] = str.split("-");
// validate format
if (lohi.length != 2) {
continue;
}
int lo = Integer.parseInt(lohi[0]);
int hi = Integer.parseInt(lohi[1]);
for (int i = lo; i <= hi; i++) {
results.add(i);
}
}
else {
results.add(Integer.parseInt(str));
}
}
// sort results
results.sort(null);
// convert ArrayList to primitive int array
int[] ints = new int[results.size()];
int i = 0;
for (Integer n : results) {
ints[i++] = n;
}
return ints;
}
/**
* Convert a number from its string representation to a long.
*
* @param strval
* @param overflowRetval
* @param defaultRetval
* @return The converted long value. "overflowRetval" is returned if the
* string representation exceeds the range of type long.
* "defaultRetval" is returned if another type of error occurred
* during conversion.
*/
public static long convertStringToLong(String strval, long overflowRetval, long defaultRetval) {
long retval = defaultRetval;
if (strval == null) return retval;
try {
retval = Long.parseLong(strval);
} catch (NumberFormatException e) {
// For some properties (e.g. memory.limit_in_bytes, cgroups v1) we may overflow
// the range of signed long. In this case, return overflowRetval
BigInteger b = new BigInteger(strval);
if (b.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0) {
return overflowRetval;
}
}
return retval;
}
}

View file

@ -0,0 +1,403 @@
/*
* Copyright (c) 2020, 2022, Red Hat Inc.
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.platform;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import jdk.internal.platform.cgroupv1.CgroupV1Subsystem;
import jdk.internal.platform.cgroupv2.CgroupV2Subsystem;
public class CgroupSubsystemFactory {
private static final String CPU_CTRL = "cpu";
private static final String CPUACCT_CTRL = "cpuacct";
private static final String CPUSET_CTRL = "cpuset";
private static final String BLKIO_CTRL = "blkio";
private static final String MEMORY_CTRL = "memory";
private static final String PIDS_CTRL = "pids";
/*
* From https://www.kernel.org/doc/Documentation/filesystems/proc.txt
*
* 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
* (1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11)
*
* (1) mount ID: unique identifier of the mount (may be reused after umount)
* (2) parent ID: ID of parent (or of self for the top of the mount tree)
* (3) major:minor: value of st_dev for files on filesystem
* (4) root: root of the mount within the filesystem
* (5) mount point: mount point relative to the process's root
* (6) mount options: per mount options
* (7) optional fields: zero or more fields of the form "tag[:value]"
* (8) separator: marks the end of the optional fields
* (9) filesystem type: name of filesystem of the form "type[.subtype]"
* (10) mount source: filesystem specific information or "none"
* (11) super options: per super block options
*/
private static final Pattern MOUNTINFO_PATTERN = Pattern.compile(
"^[^\\s]+\\s+[^\\s]+\\s+[^\\s]+\\s+" + // (1), (2), (3)
"([^\\s]+)\\s+([^\\s]+)\\s+" + // (4), (5) - group 1, 2: root, mount point
"[^-]+-\\s+" + // (6), (7), (8)
"([^\\s]+)\\s+" + // (9) - group 3: filesystem type
".*$"); // (10), (11)
static CgroupMetrics create() {
Optional<CgroupTypeResult> optResult = null;
try {
optResult = determineType("/proc/self/mountinfo", "/proc/cgroups", "/proc/self/cgroup");
} catch (IOException | UncheckedIOException e) {
return null;
}
return create(optResult);
}
public static CgroupMetrics create(Optional<CgroupTypeResult> optResult) {
if (optResult.isEmpty()) {
return null;
}
CgroupTypeResult result = optResult.get();
// If no controller is enabled, return no metrics.
if (!result.isAnyControllersEnabled()) {
return null;
}
// Warn about mixed cgroups v1 and cgroups v2 controllers. The code is
// not ready to deal with that on a per-controller basis. Return no metrics
// in that case
if (result.isAnyCgroupV1Controllers() && result.isAnyCgroupV2Controllers()) {
Logger logger = System.getLogger("jdk.internal.platform");
logger.log(Level.DEBUG, "Mixed cgroupv1 and cgroupv2 not supported. Metrics disabled.");
return null;
}
Map<String, CgroupInfo> infos = result.getInfos();
if (result.isCgroupV2()) {
// For unified it doesn't matter which controller we pick.
CgroupInfo anyController = infos.values().iterator().next();
CgroupSubsystem subsystem = CgroupV2Subsystem.getInstance(Objects.requireNonNull(anyController));
return new CgroupMetrics(subsystem);
} else {
CgroupV1Subsystem subsystem = CgroupV1Subsystem.getInstance(infos);
return subsystem != null ? new CgroupV1MetricsImpl(subsystem) : null;
}
}
/*
* Determine the type of the cgroup system (v1 - legacy or hybrid - or, v2 - unified)
* based on three files:
*
* (1) mountInfo (i.e. /proc/self/mountinfo)
* (2) cgroups (i.e. /proc/cgroups)
* (3) selfCgroup (i.e. /proc/self/cgroup)
*
* File 'cgroups' is inspected for the hierarchy ID of the mounted cgroup pseudo
* filesystem. The hierarchy ID, in turn, helps us distinguish cgroups v2 and
* cgroup v1. For a system with zero hierarchy ID, but with >= 1 relevant cgroup
* controllers mounted in 'mountInfo' we can infer it's cgroups v2. Anything else
* will be cgroup v1 (hybrid or legacy). File 'selfCgroup' is being used for
* figuring out the mount path of the controller in the cgroup hierarchy.
*/
public static Optional<CgroupTypeResult> determineType(String mountInfo,
String cgroups,
String selfCgroup) throws IOException {
final Map<String, CgroupInfo> infos = new HashMap<>();
List<String> lines = Files.readAllLines(Path.of(cgroups));
for (String line : lines) {
if (line.startsWith("#")) {
continue;
}
CgroupInfo info = CgroupInfo.fromCgroupsLine(line);
switch (info.getName()) {
// Only the following controllers are important to Java. All
// other controllers (such as freezer) are ignored and
// are not considered in the checks below for
// anyCgroupsV1Controller/anyCgroupsV2Controller.
case CPU_CTRL: infos.put(CPU_CTRL, info); break;
case CPUACCT_CTRL: infos.put(CPUACCT_CTRL, info); break;
case CPUSET_CTRL: infos.put(CPUSET_CTRL, info); break;
case MEMORY_CTRL: infos.put(MEMORY_CTRL, info); break;
case BLKIO_CTRL: infos.put(BLKIO_CTRL, info); break;
case PIDS_CTRL: infos.put(PIDS_CTRL, info); break;
}
}
// For cgroups v2 all controllers need to have zero hierarchy id
// and /proc/self/mountinfo needs to have at least one cgroup filesystem
// mounted. Note that hybrid hierarchy has controllers mounted via
// cgroup v1. In that case hierarchy id's will be non-zero.
boolean isCgroupsV2 = true;
boolean anyControllersEnabled = false;
boolean anyCgroupsV2Controller = false;
boolean anyCgroupsV1Controller = false;
for (CgroupInfo info: infos.values()) {
anyCgroupsV1Controller = anyCgroupsV1Controller || info.getHierarchyId() != 0;
anyCgroupsV2Controller = anyCgroupsV2Controller || info.getHierarchyId() == 0;
isCgroupsV2 = isCgroupsV2 && info.getHierarchyId() == 0;
anyControllersEnabled = anyControllersEnabled || info.isEnabled();
}
// If there are no mounted, relevant cgroup controllers in 'mountinfo' and only
// 0 hierarchy IDs in file 'cgroups' have been seen, we are on a cgroups v1 system.
// However, continuing in that case does not make sense as we'd need
// information from mountinfo for the mounted controller paths which we wouldn't
// find anyway in that case.
lines = Files.readAllLines(Path.of(mountInfo));
boolean anyCgroupMounted = false;
for (String line: lines) {
boolean cgroupsControllerFound = amendCgroupInfos(line, infos, isCgroupsV2);
anyCgroupMounted = anyCgroupMounted || cgroupsControllerFound;
}
if (!anyCgroupMounted) {
return Optional.empty();
}
// Map a cgroup version specific 'action' to a line in 'selfCgroup' (i.e.
// /proc/self/cgroups) , split on the ':' token, so as to set the appropriate
// path to the cgroup controller in cgroup data structures 'infos'.
// See:
// setCgroupV1Path() for the action run for cgroups v1 systems
// setCgroupV2Path() for the action run for cgroups v2 systems
try (Stream<String> selfCgroupLines = Files.lines(Path.of(selfCgroup))) {
Consumer<String[]> action = (tokens -> setCgroupV1Path(infos, tokens));
if (isCgroupsV2) {
action = (tokens -> setCgroupV2Path(infos, tokens));
}
// The limit value of 3 is because /proc/self/cgroup contains three
// colon-separated tokens per line. The last token, cgroup path, might
// contain a ':'.
selfCgroupLines.map(line -> line.split(":", 3)).forEach(action);
}
CgroupTypeResult result = new CgroupTypeResult(isCgroupsV2,
anyControllersEnabled,
anyCgroupsV2Controller,
anyCgroupsV1Controller,
Collections.unmodifiableMap(infos));
return Optional.of(result);
}
/*
* Sets the path to the cgroup controller for cgroups v2 based on a line
* in /proc/self/cgroup file (represented as the 'tokens' array).
*
* Example:
*
* 0::/
*
* => tokens = [ "0", "", "/" ]
*/
private static void setCgroupV2Path(Map<String, CgroupInfo> infos,
String[] tokens) {
String name = tokens[1];
if (!name.equals("")) {
// This must be a v1 controller that we have ignored (e.g., freezer)
assert infos.get(name) == null;
return;
}
int hierarchyId = Integer.parseInt(tokens[0]);
String cgroupPath = tokens[2];
for (CgroupInfo info: infos.values()) {
assert hierarchyId == info.getHierarchyId() && hierarchyId == 0;
info.setCgroupPath(cgroupPath);
}
}
/*
* Sets the path to the cgroup controller for cgroups v1 based on a line
* in /proc/self/cgroup file (represented as the 'tokens' array).
*
* Note that multiple controllers might be joined at a single path.
*
* Example:
*
* 7:cpu,cpuacct:/system.slice/docker-74ad896fb40bbefe0f181069e4417505fffa19052098f27edf7133f31423bc0b.scope
*
* => tokens = [ "7", "cpu,cpuacct", "/system.slice/docker-74ad896fb40bbefe0f181069e4417505fffa19052098f27edf7133f31423bc0b.scope" ]
*/
private static void setCgroupV1Path(Map<String, CgroupInfo> infos,
String[] tokens) {
String controllerName = tokens[1];
String cgroupPath = tokens[2];
if (controllerName != null && cgroupPath != null) {
for (String cName: controllerName.split(",")) {
switch (cName) {
case MEMORY_CTRL: // fall through
case CPUSET_CTRL:
case CPUACCT_CTRL:
case CPU_CTRL:
case BLKIO_CTRL:
case PIDS_CTRL:
CgroupInfo info = infos.get(cName);
info.setCgroupPath(cgroupPath);
break;
// Ignore not recognized controllers
default:
break;
}
}
}
}
/**
* Amends cgroup infos with mount path and mount root. The passed in
* 'mntInfoLine' represents a single line in, for example,
* /proc/self/mountinfo. Each line is matched with MOUNTINFO_PATTERN
* (see above), so as to extract the relevant tokens from the line.
*
* Host example cgroups v1:
*
* 44 30 0:41 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,seclabel,devices
*
* Container example cgroups v1:
*
* 1901 1894 0:37 /system.slice/docker-2291eeb92093f9d761aaf971782b575e9be56bd5930d4b5759b51017df3c1387.scope /sys/fs/cgroup/cpu,cpuacct ro,nosuid,nodev,noexec,relatime master:12 - cgroup cgroup rw,seclabel,cpu,cpuacct
*
* Container example cgroups v2:
*
* 1043 1034 0:27 / /sys/fs/cgroup ro,nosuid,nodev,noexec,relatime - cgroup2 cgroup2 rw,seclabel,nsdelegate
*
*
* @return {@code true} iff a relevant controller has been found at the
* given line
*/
private static boolean amendCgroupInfos(String mntInfoLine,
Map<String, CgroupInfo> infos,
boolean isCgroupsV2) {
Matcher lineMatcher = MOUNTINFO_PATTERN.matcher(mntInfoLine.trim());
boolean cgroupv1ControllerFound = false;
boolean cgroupv2ControllerFound = false;
if (lineMatcher.matches()) {
String mountRoot = lineMatcher.group(1);
String mountPath = lineMatcher.group(2);
String fsType = lineMatcher.group(3);
if (fsType.equals("cgroup")) {
Path p = Path.of(mountPath);
String[] controllerNames = p.getFileName().toString().split(",");
for (String controllerName: controllerNames) {
switch (controllerName) {
case MEMORY_CTRL: // fall-through
case CPU_CTRL:
case CPUACCT_CTRL:
case CPUSET_CTRL:
case PIDS_CTRL:
case BLKIO_CTRL: {
CgroupInfo info = infos.get(controllerName);
setMountPoints(info, mountPath, mountRoot);
cgroupv1ControllerFound = true;
break;
}
default:
// Ignore controllers which we don't recognize
break;
}
}
} else if (fsType.equals("cgroup2")) {
if (isCgroupsV2) { // will be false for hybrid
// All controllers have the same mount point and root mount
// for unified hierarchy.
for (CgroupInfo info: infos.values()) {
setMountPoints(info, mountPath, mountRoot);
}
}
cgroupv2ControllerFound = true;
}
}
return cgroupv1ControllerFound || cgroupv2ControllerFound;
}
private static void setMountPoints(CgroupInfo info, String mountPath, String mountRoot) {
if (info.getMountPoint() != null) {
// On some systems duplicate controllers get mounted in addition to
// the main cgroup controllers (which are under /sys/fs/cgroup). In that
// case pick the main one and discard others as the limits
// are associated with the ones in /sys/fs/cgroup.
if (!info.getMountPoint().startsWith("/sys/fs/cgroup")) {
info.setMountPoint(mountPath);
info.setMountRoot(mountRoot);
}
} else {
info.setMountPoint(mountPath);
info.setMountRoot(mountRoot);
}
}
public static final class CgroupTypeResult {
private final boolean isCgroupV2;
private final boolean anyControllersEnabled;
private final boolean anyCgroupV2Controllers;
private final boolean anyCgroupV1Controllers;
private final Map<String, CgroupInfo> infos;
private CgroupTypeResult(boolean isCgroupV2,
boolean anyControllersEnabled,
boolean anyCgroupV2Controllers,
boolean anyCgroupV1Controllers,
Map<String, CgroupInfo> infos) {
this.isCgroupV2 = isCgroupV2;
this.anyControllersEnabled = anyControllersEnabled;
this.anyCgroupV1Controllers = anyCgroupV1Controllers;
this.anyCgroupV2Controllers = anyCgroupV2Controllers;
this.infos = infos;
}
public boolean isCgroupV2() {
return isCgroupV2;
}
public boolean isAnyControllersEnabled() {
return anyControllersEnabled;
}
public boolean isAnyCgroupV2Controllers() {
return anyCgroupV2Controllers;
}
public boolean isAnyCgroupV1Controllers() {
return anyCgroupV1Controllers;
}
public Map<String, CgroupInfo> getInfos() {
return infos;
}
}
}

View file

@ -0,0 +1,148 @@
/*
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.platform;
/**
*
* Cgroup v1 extensions to the Metrics interface. Linux, only.
*
*/
public interface CgroupV1Metrics extends Metrics {
/**
* Returns the largest amount of physical memory, in bytes, that
* have been allocated in the Isolation Group.
*
* @return The largest amount of memory in bytes or -1 if this
* metric is not available. Returns -2 if this metric is not
* supported.
*
*/
public long getMemoryMaxUsage();
/**
* Returns the number of times that kernel memory requests in the
* Isolation Group have exceeded the kernel memory limit.
*
* @return The number of exceeded requests or -1 if metric
* is not available.
*
*/
public long getKernelMemoryFailCount();
/**
* Returns the largest amount of kernel physical memory, in bytes, that
* have been allocated in the Isolation Group.
*
* @return The largest amount of memory in bytes or -1 if this
* metric is not available.
*
*/
public long getKernelMemoryMaxUsage();
/**
* Returns the amount of kernel physical memory, in bytes, that
* is currently allocated in the current Isolation Group.
*
* @return The amount of memory in bytes allocated or -1 if this
* metric is not available.
*
*/
public long getKernelMemoryUsage();
/**
* Returns the number of times that networking memory requests in the
* Isolation Group have exceeded the kernel memory limit.
*
* @return The number of exceeded requests or -1 if the metric
* is not available.
*
*/
public long getTcpMemoryFailCount();
/**
* Returns the largest amount of networking physical memory, in bytes,
* that have been allocated in the Isolation Group.
*
* @return The largest amount of memory in bytes or -1 if this
* metric is not available.
*
*/
public long getTcpMemoryMaxUsage();
/**
* Returns the number of times that user memory requests in the
* Isolation Group have exceeded the memory + swap limit.
*
* @return The number of exceeded requests or -1 if the metric
* is not available.
*
*/
public long getMemoryAndSwapFailCount();
/**
* Returns the largest amount of physical memory and swap space,
* in bytes, that have been allocated in the Isolation Group.
*
* @return The largest amount of memory in bytes or -1 if this
* metric is not available.
*
*/
public long getMemoryAndSwapMaxUsage();
/**
* Returns the state of the Operating System Out of Memory termination
* policy.
*
* @return Returns true if operating system will terminate processes
* in the Isolation Group that exceed the amount of available
* memory, otherwise false. null will be returned if this
* capability is not available on the current operating system.
*
*/
public Boolean isMemoryOOMKillEnabled();
/**
* Returns the (attempts per second * 1000), if enabled, that the
* operating system tries to satisfy a memory request for any
* process in the current Isolation Group when no free memory is
* readily available. Use {@link #isCpuSetMemoryPressureEnabled()} to
* determine if this support is enabled.
*
* @return Memory pressure or 0 if not enabled or -1 if metric is not
* available.
*
*/
public double getCpuSetMemoryPressure();
/**
* Returns the state of the memory pressure detection support.
*
* @return true if support is available and enabled. false otherwise.
*
*/
public Boolean isCpuSetMemoryPressureEnabled();
}

View file

@ -0,0 +1,96 @@
/*
* Copyright (c) 2020, Red Hat Inc.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.platform;
/**
* Cgroup v1 Metrics extensions
*
*/
public class CgroupV1MetricsImpl extends CgroupMetrics implements CgroupV1Metrics {
private final CgroupV1Metrics metrics;
CgroupV1MetricsImpl(CgroupV1Metrics metrics) {
super((CgroupSubsystem)metrics);
this.metrics = metrics;
}
@Override
public long getMemoryMaxUsage() {
return metrics.getMemoryMaxUsage();
}
@Override
public long getKernelMemoryFailCount() {
return metrics.getKernelMemoryFailCount();
}
@Override
public long getKernelMemoryMaxUsage() {
return metrics.getKernelMemoryMaxUsage();
}
@Override
public long getKernelMemoryUsage() {
return metrics.getKernelMemoryUsage();
}
@Override
public long getTcpMemoryFailCount() {
return metrics.getTcpMemoryFailCount();
}
@Override
public long getTcpMemoryMaxUsage() {
return metrics.getTcpMemoryMaxUsage();
}
@Override
public long getMemoryAndSwapFailCount() {
return metrics.getMemoryAndSwapFailCount();
}
@Override
public long getMemoryAndSwapMaxUsage() {
return metrics.getMemoryAndSwapMaxUsage();
}
@Override
public Boolean isMemoryOOMKillEnabled() {
return metrics.isMemoryOOMKillEnabled();
}
@Override
public double getCpuSetMemoryPressure() {
return metrics.getCpuSetMemoryPressure();
}
@Override
public Boolean isCpuSetMemoryPressureEnabled() {
return metrics.isCpuSetMemoryPressureEnabled();
}
}

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.platform;
public class SystemMetrics {
public static Metrics instance() {
return CgroupMetrics.getInstance();
}
}

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2020, Red Hat Inc.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.platform.cgroupv1;
public class CgroupV1MemorySubSystemController extends CgroupV1SubsystemController {
private boolean hierarchical;
private boolean swapenabled;
public CgroupV1MemorySubSystemController(String root, String mountPoint) {
super(root, mountPoint);
}
boolean isHierarchical() {
return hierarchical;
}
void setHierarchical(boolean hierarchical) {
this.hierarchical = hierarchical;
}
boolean isSwapEnabled() {
return swapenabled;
}
void setSwapEnabled(boolean swapenabled) {
this.swapenabled = swapenabled;
}
}

View file

@ -0,0 +1,429 @@
/*
* Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.platform.cgroupv1;
import java.util.Map;
import jdk.internal.platform.CgroupInfo;
import jdk.internal.platform.CgroupSubsystem;
import jdk.internal.platform.CgroupSubsystemController;
import jdk.internal.platform.CgroupV1Metrics;
public class CgroupV1Subsystem implements CgroupSubsystem, CgroupV1Metrics {
private CgroupV1MemorySubSystemController memory;
private CgroupV1SubsystemController cpu;
private CgroupV1SubsystemController cpuacct;
private CgroupV1SubsystemController cpuset;
private CgroupV1SubsystemController blkio;
private CgroupV1SubsystemController pids;
private static volatile CgroupV1Subsystem INSTANCE;
private static final String PROVIDER_NAME = "cgroupv1";
private CgroupV1Subsystem() {}
/**
* Get a singleton instance of CgroupV1Subsystem. Initially, it creates a new
* object by retrieving the pre-parsed information from cgroup interface
* files from the provided 'infos' map.
*
* See CgroupSubsystemFactory.determineType() where the actual parsing of
* cgroup interface files happens.
*
* @return A singleton CgroupV1Subsystem instance, never null
*/
public static CgroupV1Subsystem getInstance(Map<String, CgroupInfo> infos) {
if (INSTANCE == null) {
CgroupV1Subsystem tmpSubsystem = initSubSystem(infos);
synchronized (CgroupV1Subsystem.class) {
if (INSTANCE == null) {
INSTANCE = tmpSubsystem;
}
}
}
return INSTANCE;
}
private static CgroupV1Subsystem initSubSystem(Map<String, CgroupInfo> infos) {
CgroupV1Subsystem subsystem = new CgroupV1Subsystem();
boolean anyActiveControllers = false;
/*
* Find the cgroup mount points for subsystem controllers
* by looking up relevant data in the infos map
*/
for (CgroupInfo info: infos.values()) {
switch (info.getName()) {
case "memory": {
if (info.getMountRoot() != null && info.getMountPoint() != null) {
CgroupV1MemorySubSystemController controller = new CgroupV1MemorySubSystemController(info.getMountRoot(), info.getMountPoint());
controller.setPath(info.getCgroupPath());
boolean isHierarchial = getHierarchical(controller);
controller.setHierarchical(isHierarchial);
boolean isSwapEnabled = getSwapEnabled(controller);
controller.setSwapEnabled(isSwapEnabled);
subsystem.setMemorySubSystem(controller);
anyActiveControllers = true;
}
break;
}
case "cpuset": {
if (info.getMountRoot() != null && info.getMountPoint() != null) {
CgroupV1SubsystemController controller = new CgroupV1SubsystemController(info.getMountRoot(), info.getMountPoint());
controller.setPath(info.getCgroupPath());
subsystem.setCpuSetController(controller);
anyActiveControllers = true;
}
break;
}
case "cpuacct": {
if (info.getMountRoot() != null && info.getMountPoint() != null) {
CgroupV1SubsystemController controller = new CgroupV1SubsystemController(info.getMountRoot(), info.getMountPoint());
controller.setPath(info.getCgroupPath());
subsystem.setCpuAcctController(controller);
anyActiveControllers = true;
}
break;
}
case "cpu": {
if (info.getMountRoot() != null && info.getMountPoint() != null) {
CgroupV1SubsystemController controller = new CgroupV1SubsystemController(info.getMountRoot(), info.getMountPoint());
controller.setPath(info.getCgroupPath());
subsystem.setCpuController(controller);
anyActiveControllers = true;
}
break;
}
case "blkio": {
if (info.getMountRoot() != null && info.getMountPoint() != null) {
CgroupV1SubsystemController controller = new CgroupV1SubsystemController(info.getMountRoot(), info.getMountPoint());
controller.setPath(info.getCgroupPath());
subsystem.setBlkIOController(controller);
anyActiveControllers = true;
}
break;
}
case "pids": {
if (info.getMountRoot() != null && info.getMountPoint() != null) {
CgroupV1SubsystemController controller = new CgroupV1SubsystemController(info.getMountRoot(), info.getMountPoint());
controller.setPath(info.getCgroupPath());
subsystem.setPidsController(controller);
anyActiveControllers = true;
}
break;
}
default:
throw new AssertionError("Unrecognized controller in infos: " + info.getName());
}
}
// Return Metrics object if we found any subsystems.
if (anyActiveControllers) {
return subsystem;
}
return null;
}
private static boolean getSwapEnabled(CgroupV1MemorySubSystemController controller) {
long memswBytes = getLongValue(controller, "memory.memsw.limit_in_bytes");
long swappiness = getLongValue(controller, "memory.swappiness");
return (memswBytes > 0 && swappiness > 0);
}
private static boolean getHierarchical(CgroupV1MemorySubSystemController controller) {
long hierarchical = getLongValue(controller, "memory.use_hierarchy");
return hierarchical > 0;
}
private void setMemorySubSystem(CgroupV1MemorySubSystemController memory) {
this.memory = memory;
}
private void setCpuController(CgroupV1SubsystemController cpu) {
this.cpu = cpu;
}
private void setCpuAcctController(CgroupV1SubsystemController cpuacct) {
this.cpuacct = cpuacct;
}
private void setCpuSetController(CgroupV1SubsystemController cpuset) {
this.cpuset = cpuset;
}
private void setBlkIOController(CgroupV1SubsystemController blkio) {
this.blkio = blkio;
}
private void setPidsController(CgroupV1SubsystemController pids) {
this.pids = pids;
}
private static long getLongValue(CgroupSubsystemController controller,
String param) {
return CgroupSubsystemController.getLongValue(controller,
param,
CgroupV1SubsystemController::convertStringToLong,
CgroupSubsystem.LONG_RETVAL_UNLIMITED);
}
public String getProvider() {
return PROVIDER_NAME;
}
/*****************************************************************
* CPU Accounting Subsystem
****************************************************************/
public long getCpuUsage() {
return getLongValue(cpuacct, "cpuacct.usage");
}
public long[] getPerCpuUsage() {
String usagelist = CgroupSubsystemController.getStringValue(cpuacct, "cpuacct.usage_percpu");
if (usagelist == null) {
return null;
}
String list[] = usagelist.split(" ");
long percpu[] = new long[list.length];
for (int i = 0; i < list.length; i++) {
percpu[i] = Long.parseLong(list[i]);
}
return percpu;
}
public long getCpuUserUsage() {
return CgroupV1SubsystemController.getLongEntry(cpuacct, "cpuacct.stat", "user");
}
public long getCpuSystemUsage() {
return CgroupV1SubsystemController.getLongEntry(cpuacct, "cpuacct.stat", "system");
}
/*****************************************************************
* CPU Subsystem
****************************************************************/
public long getCpuPeriod() {
return getLongValue(cpu, "cpu.cfs_period_us");
}
public long getCpuQuota() {
return getLongValue(cpu, "cpu.cfs_quota_us");
}
public long getCpuShares() {
long retval = getLongValue(cpu, "cpu.shares");
if (retval == 0 || retval == 1024)
return CgroupSubsystem.LONG_RETVAL_UNLIMITED;
else
return retval;
}
public long getCpuNumPeriods() {
return CgroupV1SubsystemController.getLongEntry(cpu, "cpu.stat", "nr_periods");
}
public long getCpuNumThrottled() {
return CgroupV1SubsystemController.getLongEntry(cpu, "cpu.stat", "nr_throttled");
}
public long getCpuThrottledTime() {
return CgroupV1SubsystemController.getLongEntry(cpu, "cpu.stat", "throttled_time");
}
public long getEffectiveCpuCount() {
return Runtime.getRuntime().availableProcessors();
}
/*****************************************************************
* CPUSet Subsystem
****************************************************************/
public int[] getCpuSetCpus() {
return CgroupSubsystemController.stringRangeToIntArray(CgroupSubsystemController.getStringValue(cpuset, "cpuset.cpus"));
}
public int[] getEffectiveCpuSetCpus() {
return CgroupSubsystemController.stringRangeToIntArray(CgroupSubsystemController.getStringValue(cpuset, "cpuset.effective_cpus"));
}
public int[] getCpuSetMems() {
return CgroupSubsystemController.stringRangeToIntArray(CgroupSubsystemController.getStringValue(cpuset, "cpuset.mems"));
}
public int[] getEffectiveCpuSetMems() {
return CgroupSubsystemController.stringRangeToIntArray(CgroupSubsystemController.getStringValue(cpuset, "cpuset.effective_mems"));
}
public double getCpuSetMemoryPressure() {
return CgroupV1SubsystemController.getDoubleValue(cpuset, "cpuset.memory_pressure");
}
public Boolean isCpuSetMemoryPressureEnabled() {
long val = getLongValue(cpuset, "cpuset.memory_pressure_enabled");
return (val == 1);
}
/*****************************************************************
* Memory Subsystem
****************************************************************/
public long getMemoryFailCount() {
return getLongValue(memory, "memory.failcnt");
}
public long getMemoryLimit() {
long retval = getLongValue(memory, "memory.limit_in_bytes");
if (retval > CgroupV1SubsystemController.UNLIMITED_MIN) {
if (memory.isHierarchical()) {
// memory.limit_in_bytes returned unlimited, attempt
// hierarchical memory limit
String match = "hierarchical_memory_limit";
retval = CgroupV1SubsystemController.getLongValueMatchingLine(memory,
"memory.stat",
match);
}
}
return CgroupV1SubsystemController.longValOrUnlimited(retval);
}
public long getMemoryMaxUsage() {
return getLongValue(memory, "memory.max_usage_in_bytes");
}
public long getMemoryUsage() {
return getLongValue(memory, "memory.usage_in_bytes");
}
public long getKernelMemoryFailCount() {
return getLongValue(memory, "memory.kmem.failcnt");
}
public long getKernelMemoryMaxUsage() {
return getLongValue(memory, "memory.kmem.max_usage_in_bytes");
}
public long getKernelMemoryUsage() {
return getLongValue(memory, "memory.kmem.usage_in_bytes");
}
public long getTcpMemoryFailCount() {
return getLongValue(memory, "memory.kmem.tcp.failcnt");
}
public long getTcpMemoryMaxUsage() {
return getLongValue(memory, "memory.kmem.tcp.max_usage_in_bytes");
}
public long getTcpMemoryUsage() {
return getLongValue(memory, "memory.kmem.tcp.usage_in_bytes");
}
public long getMemoryAndSwapFailCount() {
if (memory != null && !memory.isSwapEnabled()) {
return getMemoryFailCount();
}
return getLongValue(memory, "memory.memsw.failcnt");
}
public long getMemoryAndSwapLimit() {
if (memory != null && !memory.isSwapEnabled()) {
return getMemoryLimit();
}
long retval = getLongValue(memory, "memory.memsw.limit_in_bytes");
if (retval > CgroupV1SubsystemController.UNLIMITED_MIN) {
if (memory.isHierarchical()) {
// memory.memsw.limit_in_bytes returned unlimited, attempt
// hierarchical memory limit
String match = "hierarchical_memsw_limit";
retval = CgroupV1SubsystemController.getLongValueMatchingLine(memory,
"memory.stat",
match);
}
}
return CgroupV1SubsystemController.longValOrUnlimited(retval);
}
public long getMemoryAndSwapMaxUsage() {
if (memory != null && !memory.isSwapEnabled()) {
return getMemoryMaxUsage();
}
return getLongValue(memory, "memory.memsw.max_usage_in_bytes");
}
public long getMemoryAndSwapUsage() {
if (memory != null && !memory.isSwapEnabled()) {
return getMemoryUsage();
}
return getLongValue(memory, "memory.memsw.usage_in_bytes");
}
public Boolean isMemoryOOMKillEnabled() {
long val = CgroupV1SubsystemController.getLongEntry(memory, "memory.oom_control", "oom_kill_disable");
return (val == 0);
}
public long getMemorySoftLimit() {
return CgroupV1SubsystemController.longValOrUnlimited(getLongValue(memory, "memory.soft_limit_in_bytes"));
}
/*****************************************************************
* pids subsystem
****************************************************************/
public long getPidsMax() {
String pidsMaxStr = CgroupSubsystemController.getStringValue(pids, "pids.max");
return CgroupSubsystem.limitFromString(pidsMaxStr);
}
public long getPidsCurrent() {
return getLongValue(pids, "pids.current");
}
/*****************************************************************
* BlKIO Subsystem
****************************************************************/
public long getBlkIOServiceCount() {
return CgroupV1SubsystemController.getLongEntry(blkio, "blkio.throttle.io_service_bytes", "Total");
}
public long getBlkIOServiced() {
return CgroupV1SubsystemController.getLongEntry(blkio, "blkio.throttle.io_serviced", "Total");
}
}

View file

@ -0,0 +1,130 @@
/*
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.platform.cgroupv1;
import java.lang.System.Logger.Level;
import java.nio.file.Path;
import java.nio.file.Files;
import jdk.internal.platform.CgroupSubsystem;
import jdk.internal.platform.CgroupSubsystemController;
public class CgroupV1SubsystemController implements CgroupSubsystemController {
private static final double DOUBLE_RETVAL_UNLIMITED = CgroupSubsystem.LONG_RETVAL_UNLIMITED;
// Values returned larger than this number are unlimited.
static long UNLIMITED_MIN = 0x7FFFFFFFFF000000L;
String root;
String mountPoint;
String path;
public CgroupV1SubsystemController(String root, String mountPoint) {
this.root = root;
this.mountPoint = mountPoint;
}
public void setPath(String cgroupPath) {
if (root != null && cgroupPath != null) {
String path = mountPoint;
if (root.equals("/")) {
// host processes and containers with cgroupns=private
if (!cgroupPath.equals("/")) {
path += cgroupPath;
}
} else {
// containers with cgroupns=host, default setting is _root==cgroup_path
if (!cgroupPath.equals(root)) {
if (!cgroupPath.equals("") && !cgroupPath.equals("/")) {
// When moved to a subgroup, between subgroups, the path suffix will change.
Path cgp = Path.of(cgroupPath);
int nameCount = cgp.getNameCount();
for (int i=0; i < nameCount; i++) {
Path dir = Path.of(mountPoint, cgp.toString());
if (Files.isDirectory(dir)) {
path = dir.toString();
if (i > 0) {
System.getLogger("jdk.internal.platform").log(Level.DEBUG, String.format(
"Cgroup v1 path reduced to: %s.", cgp));
}
break;
}
int currentNameCount = cgp.getNameCount();
cgp = (currentNameCount > 1) ? cgp.subpath(1, currentNameCount) : Path.of("");
}
}
}
}
this.path = path;
}
}
@Override
public String path() {
return path;
}
public static long getLongEntry(CgroupSubsystemController controller, String param, String entryname) {
return CgroupSubsystemController.getLongEntry(controller,
param,
entryname,
CgroupSubsystem.LONG_RETVAL_UNLIMITED /* retval on error */);
}
public static double getDoubleValue(CgroupSubsystemController controller, String param) {
return CgroupSubsystemController.getDoubleValue(controller,
param,
DOUBLE_RETVAL_UNLIMITED /* retval on error */);
}
public static long convertStringToLong(String strval) {
return CgroupSubsystemController.convertStringToLong(strval,
Long.MAX_VALUE /* overflow value */,
CgroupSubsystem.LONG_RETVAL_UNLIMITED /* retval on error */);
}
public static long longValOrUnlimited(long value) {
return value > UNLIMITED_MIN ? CgroupSubsystem.LONG_RETVAL_UNLIMITED : value;
}
public static long getLongValueMatchingLine(CgroupSubsystemController controller,
String param,
String match) {
return CgroupSubsystemController.getLongValueMatchingLine(controller,
param,
match,
CgroupV1SubsystemController::convertHierachicalLimitLine,
CgroupSubsystem.LONG_RETVAL_UNLIMITED);
}
public static long convertHierachicalLimitLine(String line) {
String[] tokens = line.split("\\s");
if (tokens.length == 2) {
String strVal = tokens[1];
return CgroupV1SubsystemController.convertStringToLong(strVal);
}
return CgroupV1SubsystemController.UNLIMITED_MIN + 1; // unlimited
}
}

View file

@ -0,0 +1,406 @@
/*
* Copyright (c) 2020, 2022, Red Hat Inc.
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.platform.cgroupv2;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jdk.internal.platform.CgroupInfo;
import jdk.internal.platform.CgroupSubsystem;
import jdk.internal.platform.CgroupSubsystemController;
public class CgroupV2Subsystem implements CgroupSubsystem {
private static volatile CgroupV2Subsystem INSTANCE;
private static final long[] LONG_ARRAY_NOT_SUPPORTED = null;
private static final int[] INT_ARRAY_UNAVAILABLE = null;
private final CgroupSubsystemController unified;
private static final String PROVIDER_NAME = "cgroupv2";
private static final int PER_CPU_SHARES = 1024;
private static final Object EMPTY_STR = "";
private static final long NO_SWAP = 0;
private CgroupV2Subsystem(CgroupSubsystemController unified) {
this.unified = unified;
}
private long getLongVal(String file, long defaultValue) {
return CgroupSubsystemController.getLongValue(unified,
file,
CgroupV2SubsystemController::convertStringToLong,
defaultValue);
}
private long getLongVal(String file) {
return getLongVal(file, CgroupSubsystem.LONG_RETVAL_UNLIMITED);
}
/**
* Get the singleton instance of a cgroups v2 subsystem. On initialization,
* a new object from the given cgroup information 'anyController' is being
* created. Note that the cgroup information has been parsed from cgroup
* interface files ahead of time.
*
* See CgroupSubsystemFactory.determineType() for the cgroup interface
* files parsing logic.
*
* @return A singleton CgroupSubsystem instance, never null.
*/
public static CgroupSubsystem getInstance(CgroupInfo anyController) {
if (INSTANCE == null) {
CgroupSubsystemController unified = new CgroupV2SubsystemController(
anyController.getMountPoint(),
anyController.getCgroupPath());
CgroupV2Subsystem tmpCgroupSystem = new CgroupV2Subsystem(unified);
synchronized (CgroupV2Subsystem.class) {
if (INSTANCE == null) {
INSTANCE = tmpCgroupSystem;
}
}
}
return INSTANCE;
}
@Override
public String getProvider() {
return PROVIDER_NAME;
}
@Override
public long getCpuUsage() {
long micros = CgroupV2SubsystemController.getLongEntry(unified, "cpu.stat", "usage_usec");
if (micros < 0) {
return micros;
}
return TimeUnit.MICROSECONDS.toNanos(micros);
}
@Override
public long[] getPerCpuUsage() {
return LONG_ARRAY_NOT_SUPPORTED;
}
@Override
public long getCpuUserUsage() {
long micros = CgroupV2SubsystemController.getLongEntry(unified, "cpu.stat", "user_usec");
if (micros < 0) {
return micros;
}
return TimeUnit.MICROSECONDS.toNanos(micros);
}
@Override
public long getCpuSystemUsage() {
long micros = CgroupV2SubsystemController.getLongEntry(unified, "cpu.stat", "system_usec");
if (micros < 0) {
return micros;
}
return TimeUnit.MICROSECONDS.toNanos(micros);
}
@Override
public long getCpuPeriod() {
return getFromCpuMax(1 /* $PERIOD index */);
}
@Override
public long getCpuQuota() {
return getFromCpuMax(0 /* $MAX index */);
}
private long getFromCpuMax(int tokenIdx) {
String cpuMaxRaw = CgroupSubsystemController.getStringValue(unified, "cpu.max");
if (cpuMaxRaw == null) {
// likely file not found
return CgroupSubsystem.LONG_RETVAL_UNLIMITED;
}
// $MAX $PERIOD
String[] tokens = cpuMaxRaw.split("\\s+");
if (tokens.length != 2) {
return CgroupSubsystem.LONG_RETVAL_UNLIMITED;
}
String quota = tokens[tokenIdx];
return CgroupSubsystem.limitFromString(quota);
}
@Override
public long getCpuShares() {
long sharesRaw = getLongVal("cpu.weight");
// cg v2 value must be in range [1,10000]
if (sharesRaw == 100 || sharesRaw <= 0 || sharesRaw > 10000) {
return CgroupSubsystem.LONG_RETVAL_UNLIMITED;
}
int shares = (int)sharesRaw;
// CPU shares (OCI) value needs to get translated into
// a proper Cgroups v2 value. See:
// https://github.com/containers/crun/blob/1.24/crun.1.md#cpu-controller
//
// Use the inverse of (x == OCI value, y == cgroupsv2 value):
// y = 10^(log2(x)^2/612 + 125/612 * log2(x) - 7.0/34.0)
//
// By re-arranging it to the standard quadratic form:
// log2(x)^2 + 125 * log2(x) - (126 + 612 * log_10(y)) = 0
//
// Therefore, log2(x) = (-125 + sqrt( 125^2 - 4 * (-(126 + 612 * log_10(y)))))/2
//
// As a result we have the inverse (we can discount substraction of the
// square root value since those values result in very small numbers and the
// cpu shares values - OCI - are in range [2-262144])
//
// x = 2^((-125 + sqrt(16129 + 2448* log10(y)))/2)
//
double logMultiplicand = Math.log10(shares);
double discriminant = 16129 + 2448 * logMultiplicand;
double squareRoot = Math.sqrt(discriminant);
double exponent = (-125 + squareRoot)/2;
double scaledValue = Math.pow(2, exponent);
int x = (int)scaledValue;
if ( x <= PER_CPU_SHARES ) {
// Return the back-mapped value.
return x;
}
int f = x/PER_CPU_SHARES;
int lower_multiple = f * PER_CPU_SHARES;
int upper_multiple = (f + 1) * PER_CPU_SHARES;
int distance_lower = Math.max(lower_multiple, x) - Math.min(lower_multiple, x);
int distance_upper = Math.max(upper_multiple, x) - Math.min(upper_multiple, x);
x = distance_lower <= distance_upper ? lower_multiple : upper_multiple;
return x;
}
@Override
public long getCpuNumPeriods() {
return CgroupV2SubsystemController.getLongEntry(unified, "cpu.stat", "nr_periods");
}
@Override
public long getCpuNumThrottled() {
return CgroupV2SubsystemController.getLongEntry(unified, "cpu.stat", "nr_throttled");
}
@Override
public long getCpuThrottledTime() {
long micros = CgroupV2SubsystemController.getLongEntry(unified, "cpu.stat", "throttled_usec");
if (micros < 0) {
return micros;
}
return TimeUnit.MICROSECONDS.toNanos(micros);
}
@Override
public long getEffectiveCpuCount() {
return Runtime.getRuntime().availableProcessors();
}
@Override
public int[] getCpuSetCpus() {
String cpuSetVal = CgroupSubsystemController.getStringValue(unified, "cpuset.cpus");
return getCpuSet(cpuSetVal);
}
@Override
public int[] getEffectiveCpuSetCpus() {
String effCpuSetVal = CgroupSubsystemController.getStringValue(unified, "cpuset.cpus.effective");
return getCpuSet(effCpuSetVal);
}
@Override
public int[] getCpuSetMems() {
String cpuSetMems = CgroupSubsystemController.getStringValue(unified, "cpuset.mems");
return getCpuSet(cpuSetMems);
}
@Override
public int[] getEffectiveCpuSetMems() {
String effCpuSetMems = CgroupSubsystemController.getStringValue(unified, "cpuset.mems.effective");
return getCpuSet(effCpuSetMems);
}
private int[] getCpuSet(String cpuSetVal) {
if (cpuSetVal == null || EMPTY_STR.equals(cpuSetVal)) {
return INT_ARRAY_UNAVAILABLE;
}
return CgroupSubsystemController.stringRangeToIntArray(cpuSetVal);
}
@Override
public long getMemoryFailCount() {
return CgroupV2SubsystemController.getLongEntry(unified, "memory.events", "max");
}
@Override
public long getMemoryLimit() {
String strVal = CgroupSubsystemController.getStringValue(unified, "memory.max");
return CgroupSubsystem.limitFromString(strVal);
}
@Override
public long getMemoryUsage() {
return getLongVal("memory.current");
}
@Override
public long getTcpMemoryUsage() {
return CgroupV2SubsystemController.getLongEntry(unified, "memory.stat", "sock");
}
/**
* Note that for cgroups v2 the actual limits set for swap and
* memory live in two different files, memory.swap.max and memory.max
* respectively. In order to properly report a cgroup v1 like
* compound value we need to sum the two values. Setting a swap limit
* without also setting a memory limit is not allowed.
*/
@Override
public long getMemoryAndSwapLimit() {
String strVal = CgroupSubsystemController.getStringValue(unified, "memory.swap.max");
// We only get a null string when file memory.swap.max doesn't exist.
// In that case we return the memory limit without any swap.
if (strVal == null) {
return getMemoryLimit();
}
long swapLimit = CgroupSubsystem.limitFromString(strVal);
if (swapLimit >= 0) {
long memoryLimit = getMemoryLimit();
assert memoryLimit >= 0;
return memoryLimit + swapLimit;
}
return swapLimit;
}
/**
* Note that for cgroups v2 the actual values set for swap usage and
* memory usage live in two different files, memory.current and memory.swap.current
* respectively. In order to properly report a cgroup v1 like
* compound value we need to sum the two values. Setting a swap limit
* without also setting a memory limit is not allowed.
*/
@Override
public long getMemoryAndSwapUsage() {
long memoryUsage = getMemoryUsage();
if (memoryUsage >= 0) {
// If file memory.swap.current doesn't exist, only return the regular
// memory usage (without swap). Thus, use default value of NO_SWAP.
long swapUsage = getLongVal("memory.swap.current", NO_SWAP);
return memoryUsage + swapUsage;
}
return memoryUsage; // case of no memory limits
}
@Override
public long getMemorySoftLimit() {
String softLimitStr = CgroupSubsystemController.getStringValue(unified, "memory.low");
return CgroupSubsystem.limitFromString(softLimitStr);
}
@Override
public long getPidsMax() {
String pidsMaxStr = CgroupSubsystemController.getStringValue(unified, "pids.max");
return CgroupSubsystem.limitFromString(pidsMaxStr);
}
@Override
public long getPidsCurrent() {
return getLongVal("pids.current");
}
@Override
public long getBlkIOServiceCount() {
return sumTokensIOStat(CgroupV2Subsystem::lineToRandWIOs);
}
@Override
public long getBlkIOServiced() {
return sumTokensIOStat(CgroupV2Subsystem::lineToRBytesAndWBytesIO);
}
private long sumTokensIOStat(Function<String, Long> mapFunc) {
try (Stream<String> lines = Files.lines(Path.of(unified.path(), "io.stat"))) {
return lines.map(mapFunc)
.collect(Collectors.summingLong(e -> e));
} catch (UncheckedIOException | IOException e) {
return CgroupSubsystem.LONG_RETVAL_UNLIMITED;
}
}
private static String[] getRWIOMatchTokenNames() {
return new String[] { "rios", "wios" };
}
private static String[] getRWBytesIOMatchTokenNames() {
return new String[] { "rbytes", "wbytes" };
}
public static Long lineToRandWIOs(String line) {
String[] matchNames = getRWIOMatchTokenNames();
return ioStatLineToLong(line, matchNames);
}
public static Long lineToRBytesAndWBytesIO(String line) {
String[] matchNames = getRWBytesIOMatchTokenNames();
return ioStatLineToLong(line, matchNames);
}
private static Long ioStatLineToLong(String line, String[] matchNames) {
if (line == null || EMPTY_STR.equals(line)) {
return Long.valueOf(0);
}
String[] tokens = line.split("\\s+");
long retval = 0;
for (String t: tokens) {
String[] valKeys = t.split("=");
if (valKeys.length != 2) {
// ignore device ids $MAJ:$MIN
continue;
}
for (String match: matchNames) {
if (match.equals(valKeys[0])) {
retval += longOrZero(valKeys[1]);
}
}
}
return Long.valueOf(retval);
}
private static long longOrZero(String val) {
long lVal = 0;
try {
lVal = Long.parseLong(val);
} catch (NumberFormatException e) {
// keep at 0
}
return lVal;
}
}

View file

@ -0,0 +1,58 @@
/*
* Copyright (c) 2020, Red Hat Inc.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.platform.cgroupv2;
import java.nio.file.Paths;
import jdk.internal.platform.CgroupSubsystem;
import jdk.internal.platform.CgroupSubsystemController;
public class CgroupV2SubsystemController implements CgroupSubsystemController {
private final String path;
public CgroupV2SubsystemController(String mountPath, String cgroupPath) {
this.path = Paths.get(mountPath, cgroupPath).toString();
}
@Override
public String path() {
return path;
}
public static long convertStringToLong(String strval) {
return CgroupSubsystemController.convertStringToLong(strval,
CgroupSubsystem.LONG_RETVAL_UNLIMITED /* overflow retval */,
CgroupSubsystem.LONG_RETVAL_UNLIMITED /* default retval on error */);
}
public static long getLongEntry(CgroupSubsystemController controller, String param, String entryname) {
return CgroupSubsystemController.getLongEntry(controller,
param,
entryname,
CgroupSubsystem.LONG_RETVAL_UNLIMITED /* retval on error */);
}
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.ch;
import java.nio.channels.spi.AsynchronousChannelProvider;
/**
* Creates this platform's default AsynchronousChannelProvider
*/
public class DefaultAsynchronousChannelProvider {
/**
* Prevent instantiation.
*/
private DefaultAsynchronousChannelProvider() { }
/**
* Returns the default AsynchronousChannelProvider.
*/
public static AsynchronousChannelProvider create() {
return new LinuxAsynchronousChannelProvider();
}
}

View file

@ -0,0 +1,64 @@
/*
* Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.ch;
import java.io.IOException;
import jdk.internal.vm.ContinuationSupport;
/**
* Default PollerProvider for Linux.
*/
class DefaultPollerProvider extends PollerProvider {
DefaultPollerProvider(Poller.Mode mode) {
super(mode);
}
DefaultPollerProvider() {
var mode = ContinuationSupport.isSupported()
? Poller.Mode.VTHREAD_POLLERS
: Poller.Mode.SYSTEM_THREADS;
this(mode);
}
@Override
int defaultReadPollers() {
int ncpus = Runtime.getRuntime().availableProcessors();
return switch (pollerMode()) {
case SYSTEM_THREADS -> Math.max(Integer.highestOneBit(ncpus / 4), 1);
case VTHREAD_POLLERS -> Math.min(Integer.highestOneBit(ncpus), 32);
default -> super.defaultReadPollers();
};
}
@Override
Poller readPoller(boolean subPoller) throws IOException {
return new EPollPoller(pollerMode(), subPoller, true);
}
@Override
Poller writePoller(boolean subPoller) throws IOException {
return new EPollPoller(pollerMode(), subPoller, false);
}
}

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.ch;
/**
* Creates this platform's default SelectorProvider
*/
public class DefaultSelectorProvider {
private static final SelectorProviderImpl INSTANCE = new EPollSelectorProvider();
/**
* Prevent instantiation.
*/
private DefaultSelectorProvider() { }
/**
* Returns the default SelectorProvider implementation.
*/
public static SelectorProviderImpl get() {
return INSTANCE;
}
}

View file

@ -0,0 +1,121 @@
/*
* Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.ch;
import java.io.IOException;
import jdk.internal.misc.Unsafe;
/**
* Provides access to the Linux epoll facility.
*/
class EPoll {
private EPoll() { }
private static final Unsafe unsafe = Unsafe.getUnsafe();
/**
* typedef union epoll_data {
* void *ptr;
* int fd;
* __uint32_t u32;
* __uint64_t u64;
* } epoll_data_t;
*
* struct epoll_event {
* __uint32_t events;
* epoll_data_t data;
* }
*/
static {
IOUtil.load();
}
private static final int SIZEOF_EPOLLEVENT = eventSize();
private static final int OFFSETOF_EVENTS = eventsOffset();
private static final int OFFSETOF_FD = dataOffset();
// opcodes
static final int EPOLL_CTL_ADD = 1;
static final int EPOLL_CTL_DEL = 2;
static final int EPOLL_CTL_MOD = 3;
// events
static final int EPOLLIN = 0x1;
static final int EPOLLOUT = 0x4;
// flags
static final int EPOLLONESHOT = (1 << 30);
/**
* Allocates a poll array to handle up to {@code count} events.
*/
static long allocatePollArray(int count) {
return unsafe.allocateMemory(count * SIZEOF_EPOLLEVENT);
}
/**
* Free a poll array
*/
static void freePollArray(long address) {
unsafe.freeMemory(address);
}
/**
* Returns event[i];
*/
static long getEvent(long address, int i) {
return address + (SIZEOF_EPOLLEVENT*i);
}
/**
* Returns event->data.fd
*/
static int getDescriptor(long eventAddress) {
return unsafe.getInt(eventAddress + OFFSETOF_FD);
}
/**
* Returns event->events
*/
static int getEvents(long eventAddress) {
return unsafe.getInt(eventAddress + OFFSETOF_EVENTS);
}
// -- Native methods --
private static native int eventSize();
private static native int eventsOffset();
private static native int dataOffset();
static native int create() throws IOException;
static native int ctl(int epfd, int opcode, int fd, int events);
static native int wait(int epfd, long pollAddress, int numfds, int timeout)
throws IOException;
}

View file

@ -0,0 +1,157 @@
/*
* Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.ch;
import java.io.IOException;
import java.lang.ref.Cleaner.Cleanable;
import jdk.internal.ref.CleanerFactory;
import static sun.nio.ch.EPoll.*;
/**
* Poller implementation based on the epoll facility.
*/
class EPollPoller extends Poller {
private static final int ENOENT = 2;
private final int epfd;
private final int event;
private final int maxEvents;
private final long address;
private final EventFD eventfd; // wakeup event, used for shutdown
// close action, and cleaner if this is subpoller
private final Runnable closer;
private final Cleanable cleaner;
EPollPoller(Poller.Mode mode, boolean subPoller, boolean read) throws IOException {
boolean wakeable = (mode == Mode.POLLER_PER_CARRIER) && subPoller;
int maxEvents = (subPoller) ? 16 : 64;
int epfd = EPoll.create();
long address = 0L;
EventFD eventfd = null;
try {
address = EPoll.allocatePollArray(maxEvents);
// register one end of the pipe with epoll to allow for wakeup
if (wakeable) {
eventfd = new EventFD();
IOUtil.configureBlocking(eventfd.efd(), false);
EPoll.ctl(epfd, EPOLL_CTL_ADD, eventfd.efd(), EPOLLIN);
}
} catch (Throwable e) {
FileDispatcherImpl.closeIntFD(epfd);
if (address != 0L) EPoll.freePollArray(address);
if (eventfd != null) eventfd.close();
throw e;
}
this.epfd = epfd;
this.event = (read) ? EPOLLIN : EPOLLOUT;
this.maxEvents = maxEvents;
this.address = address;
this.eventfd = eventfd;
// create action to close epoll instance, register cleaner when wakeable
this.closer = closer(epfd, address, eventfd);
if (wakeable) {
this.cleaner = CleanerFactory.cleaner().register(this, closer);
} else {
this.cleaner = null;
}
}
/**
* Returns an action to close the epoll instance and release other resources.
*/
private static Runnable closer(int epfd, long address, EventFD eventfd) {
return () -> {
try {
FileDispatcherImpl.closeIntFD(epfd);
EPoll.freePollArray(address);
if (eventfd != null) eventfd.close();
} catch (IOException _) { }
};
}
@Override
void close() {
if (cleaner != null) {
cleaner.clean();
} else {
closer.run();
}
}
@Override
int fdVal() {
return epfd;
}
@Override
void implStartPoll(int fdVal) throws IOException {
// re-enable if already registered but disabled (previously polled)
int err = EPoll.ctl(epfd, EPOLL_CTL_MOD, fdVal, (event | EPOLLONESHOT));
if (err == ENOENT)
err = EPoll.ctl(epfd, EPOLL_CTL_ADD, fdVal, (event | EPOLLONESHOT));
if (err != 0)
throw new IOException("epoll_ctl failed: " + err);
}
@Override
void implStopPoll(int fdVal, boolean polled) {
// event is disabled if already polled
if (!polled) {
EPoll.ctl(epfd, EPOLL_CTL_DEL, fdVal, 0);
}
}
@Override
void wakeupPoller() throws IOException {
if (eventfd == null) {
throw new UnsupportedOperationException();
}
eventfd.set();
}
@Override
int poll(int timeout) throws IOException {
int n = EPoll.wait(epfd, address, maxEvents, timeout);
int polled = 0;
int i = 0;
while (i < n) {
long eventAddress = EPoll.getEvent(address, i);
int fd = EPoll.getDescriptor(eventAddress);
if (eventfd == null || fd != eventfd.efd()) {
polled(fd);
polled++;
}
i++;
}
return polled;
}
}

View file

@ -0,0 +1,320 @@
/*
* Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.ch;
import java.nio.channels.spi.AsynchronousChannelProvider;
import java.io.IOException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
import static sun.nio.ch.EPoll.EPOLLIN;
import static sun.nio.ch.EPoll.EPOLLONESHOT;
import static sun.nio.ch.EPoll.EPOLL_CTL_ADD;
import static sun.nio.ch.EPoll.EPOLL_CTL_MOD;
/**
* AsynchronousChannelGroup implementation based on the Linux epoll facility.
*/
final class EPollPort
extends Port
{
// maximum number of events to poll at a time
private static final int MAX_EPOLL_EVENTS = 512;
// errors
private static final int ENOENT = 2;
// epoll file descriptor
private final int epfd;
// address of the poll array passed to epoll_wait
private final long address;
// true if epoll closed
private boolean closed;
// socket pair used for wakeup
private final int sp[];
// number of wakeups pending
private final AtomicInteger wakeupCount = new AtomicInteger();
// encapsulates an event for a channel
static class Event {
final PollableChannel channel;
final int events;
Event(PollableChannel channel, int events) {
this.channel = channel;
this.events = events;
}
PollableChannel channel() { return channel; }
int events() { return events; }
}
// queue of events for cases that a polling thread dequeues more than one
// event
private final ArrayBlockingQueue<Event> queue;
private final Event NEED_TO_POLL = new Event(null, 0);
private final Event EXECUTE_TASK_OR_SHUTDOWN = new Event(null, 0);
EPollPort(AsynchronousChannelProvider provider, ThreadPool pool)
throws IOException
{
super(provider, pool);
this.epfd = EPoll.create();
this.address = EPoll.allocatePollArray(MAX_EPOLL_EVENTS);
// create socket pair for wakeup mechanism
try {
long fds = IOUtil.makePipe(true);
this.sp = new int[]{(int) (fds >>> 32), (int) fds};
} catch (IOException ioe) {
EPoll.freePollArray(address);
FileDispatcherImpl.closeIntFD(epfd);
throw ioe;
}
// register one end with epoll
EPoll.ctl(epfd, EPOLL_CTL_ADD, sp[0], EPOLLIN);
// create the queue and offer the special event to ensure that the first
// threads polls
this.queue = new ArrayBlockingQueue<>(MAX_EPOLL_EVENTS);
this.queue.offer(NEED_TO_POLL);
}
EPollPort start() {
startThreads(new EventHandlerTask());
return this;
}
/**
* Release all resources
*/
private void implClose() {
synchronized (this) {
if (closed)
return;
closed = true;
}
try { FileDispatcherImpl.closeIntFD(epfd); } catch (IOException ioe) { }
try { FileDispatcherImpl.closeIntFD(sp[0]); } catch (IOException ioe) { }
try { FileDispatcherImpl.closeIntFD(sp[1]); } catch (IOException ioe) { }
EPoll.freePollArray(address);
}
private void wakeup() {
if (wakeupCount.incrementAndGet() == 1) {
// write byte to socketpair to force wakeup
try {
IOUtil.write1(sp[1], (byte)0);
} catch (IOException x) {
throw new AssertionError(x);
}
}
}
@Override
void executeOnHandlerTask(Runnable task) {
synchronized (this) {
if (closed)
throw new RejectedExecutionException();
offerTask(task);
wakeup();
}
}
@Override
void shutdownHandlerTasks() {
/*
* If no tasks are running then just release resources; otherwise
* write to the one end of the socketpair to wakeup any polling threads.
*/
int nThreads = threadCount();
if (nThreads == 0) {
implClose();
} else {
// send wakeup to each thread
while (nThreads-- > 0) {
wakeup();
}
}
}
// invoke by clients to register a file descriptor
@Override
void startPoll(int fd, int events) {
// update events (or add to epoll on first usage)
int err = EPoll.ctl(epfd, EPOLL_CTL_MOD, fd, (events | EPOLLONESHOT));
if (err == ENOENT)
err = EPoll.ctl(epfd, EPOLL_CTL_ADD, fd, (events | EPOLLONESHOT));
if (err != 0)
throw new AssertionError(); // should not happen
}
/**
* Task to process events from epoll and dispatch to the channel's
* onEvent handler.
*
* Events are retrieved from epoll in batch and offered to a BlockingQueue
* where they are consumed by handler threads. A special "NEED_TO_POLL"
* event is used to signal one consumer to re-poll when all events have
* been consumed.
*/
private class EventHandlerTask implements Runnable {
private Event poll() throws IOException {
try {
for (;;) {
int n;
do {
n = EPoll.wait(epfd, address, MAX_EPOLL_EVENTS, -1);
} while (n == IOStatus.INTERRUPTED);
/**
* 'n' events have been read. Here we map them to their
* corresponding channel in batch and queue n-1 so that
* they can be handled by other handler threads. The last
* event is handled by this thread (and so is not queued).
*/
fdToChannelLock.readLock().lock();
try {
while (n-- > 0) {
long eventAddress = EPoll.getEvent(address, n);
int fd = EPoll.getDescriptor(eventAddress);
// wakeup
if (fd == sp[0]) {
if (wakeupCount.decrementAndGet() == 0) {
// consume one wakeup byte, never more as this
// would interfere with shutdown when there is
// a wakeup byte queued to wake each thread
int nread;
do {
nread = IOUtil.drain1(sp[0]);
} while (nread == IOStatus.INTERRUPTED);
}
// queue special event if there are more events
// to handle.
if (n > 0) {
queue.offer(EXECUTE_TASK_OR_SHUTDOWN);
continue;
}
return EXECUTE_TASK_OR_SHUTDOWN;
}
PollableChannel channel = fdToChannel.get(fd);
if (channel != null) {
int events = EPoll.getEvents(eventAddress);
Event ev = new Event(channel, events);
// n-1 events are queued; This thread handles
// the last one except for the wakeup
if (n > 0) {
queue.offer(ev);
} else {
return ev;
}
}
}
} finally {
fdToChannelLock.readLock().unlock();
}
}
} finally {
// to ensure that some thread will poll when all events have
// been consumed
queue.offer(NEED_TO_POLL);
}
}
public void run() {
Invoker.GroupAndInvokeCount myGroupAndInvokeCount =
Invoker.getGroupAndInvokeCount();
final boolean isPooledThread = (myGroupAndInvokeCount != null);
boolean replaceMe = false;
Event ev;
try {
for (;;) {
// reset invoke count
if (isPooledThread)
myGroupAndInvokeCount.resetInvokeCount();
try {
replaceMe = false;
ev = queue.take();
// no events and this thread has been "selected" to
// poll for more.
if (ev == NEED_TO_POLL) {
try {
ev = poll();
} catch (IOException x) {
x.printStackTrace();
return;
}
}
} catch (InterruptedException x) {
continue;
}
// handle wakeup to execute task or shutdown
if (ev == EXECUTE_TASK_OR_SHUTDOWN) {
Runnable task = pollTask();
if (task == null) {
// shutdown request
return;
}
// run task (may throw error/exception)
replaceMe = true;
task.run();
continue;
}
// process event
try {
ev.channel().onEvent(ev.events(), isPooledThread);
} catch (Error | RuntimeException x) {
replaceMe = true;
throw x;
}
}
} finally {
// last handler to exit when shutdown releases resources
int remaining = threadExit(this, replaceMe);
if (remaining == 0 && isShutdown()) {
implClose();
}
}
}
}
}

View file

@ -0,0 +1,300 @@
/*
* Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.ch;
import java.io.IOException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.spi.SelectorProvider;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import static sun.nio.ch.EPoll.EPOLLIN;
import static sun.nio.ch.EPoll.EPOLL_CTL_ADD;
import static sun.nio.ch.EPoll.EPOLL_CTL_DEL;
import static sun.nio.ch.EPoll.EPOLL_CTL_MOD;
/**
* Linux epoll based Selector implementation
*/
class EPollSelectorImpl extends SelectorImpl {
// maximum number of events to poll in one call to epoll_wait
private static final int NUM_EPOLLEVENTS = Math.min(IOUtil.fdLimit(), 1024);
// epoll file descriptor
private final int epfd;
// address of poll array when polling with epoll_wait
private final long pollArrayAddress;
// eventfd object used for interrupt
private final EventFD eventfd;
// maps file descriptor to selection key, synchronize on selector
private final Map<Integer, SelectionKeyImpl> fdToKey = new HashMap<>();
// pending new registrations/updates, queued by setEventOps
private final Object updateLock = new Object();
private final Deque<SelectionKeyImpl> updateKeys = new ArrayDeque<>();
// interrupt triggering and clearing
private final Object interruptLock = new Object();
private boolean interruptTriggered;
EPollSelectorImpl(SelectorProvider sp) throws IOException {
super(sp);
this.epfd = EPoll.create();
this.pollArrayAddress = EPoll.allocatePollArray(NUM_EPOLLEVENTS);
try {
this.eventfd = new EventFD();
IOUtil.configureBlocking(IOUtil.newFD(eventfd.efd()), false);
} catch (IOException ioe) {
EPoll.freePollArray(pollArrayAddress);
FileDispatcherImpl.closeIntFD(epfd);
throw ioe;
}
// register the eventfd object for wakeups
EPoll.ctl(epfd, EPOLL_CTL_ADD, eventfd.efd(), EPOLLIN);
}
@Override
protected int doSelect(Consumer<SelectionKey> action, long timeout)
throws IOException
{
assert Thread.holdsLock(this);
// epoll_wait timeout is int
int to = (int) Math.min(timeout, Integer.MAX_VALUE);
boolean blocking = (to != 0);
boolean timedPoll = (to > 0);
int numEntries;
processUpdateQueue();
processDeregisterQueue();
if (Thread.currentThread().isVirtual()) {
numEntries = (timedPoll)
? timedPoll(TimeUnit.MILLISECONDS.toNanos(to))
: untimedPoll(blocking);
} else {
try {
begin(blocking);
do {
long startTime = timedPoll ? System.nanoTime() : 0;
numEntries = EPoll.wait(epfd, pollArrayAddress, NUM_EPOLLEVENTS, to);
if (numEntries == IOStatus.INTERRUPTED && timedPoll) {
// timed poll interrupted so need to adjust timeout
long adjust = System.nanoTime() - startTime;
to -= (int) TimeUnit.NANOSECONDS.toMillis(adjust);
if (to <= 0) {
// timeout expired so no retry
numEntries = 0;
}
}
} while (numEntries == IOStatus.INTERRUPTED);
} finally {
end(blocking);
}
}
assert IOStatus.check(numEntries);
processDeregisterQueue();
return processEvents(numEntries, action);
}
/**
* If blocking, parks the current virtual thread until a file descriptor is polled
* or the thread is interrupted.
*/
private int untimedPoll(boolean block) throws IOException {
int numEntries = EPoll.wait(epfd, pollArrayAddress, NUM_EPOLLEVENTS, 0);
if (block) {
while (numEntries == 0 && !Thread.currentThread().isInterrupted()) {
Poller.pollSelector(epfd, 0);
numEntries = EPoll.wait(epfd, pollArrayAddress, NUM_EPOLLEVENTS, 0);
}
}
return numEntries;
}
/**
* Parks the current virtual thread until a file descriptor is polled, or the thread
* is interrupted, for up to the specified waiting time.
*/
private int timedPoll(long nanos) throws IOException {
long startNanos = System.nanoTime();
int numEntries = EPoll.wait(epfd, pollArrayAddress, NUM_EPOLLEVENTS, 0);
while (numEntries == 0 && !Thread.currentThread().isInterrupted()) {
long remainingNanos = nanos - (System.nanoTime() - startNanos);
if (remainingNanos <= 0) {
// timeout
break;
}
Poller.pollSelector(epfd, remainingNanos);
numEntries = EPoll.wait(epfd, pollArrayAddress, NUM_EPOLLEVENTS, 0);
}
return numEntries;
}
/**
* Process changes to the interest ops.
*/
private void processUpdateQueue() {
assert Thread.holdsLock(this);
synchronized (updateLock) {
SelectionKeyImpl ski;
while ((ski = updateKeys.pollFirst()) != null) {
if (ski.isValid()) {
int fd = ski.getFDVal();
// add to fdToKey if needed
SelectionKeyImpl previous = fdToKey.putIfAbsent(fd, ski);
assert (previous == null) || (previous == ski);
int newEvents = ski.translateInterestOps();
int registeredEvents = ski.registeredEvents();
if (newEvents != registeredEvents) {
if (newEvents == 0) {
// remove from epoll
EPoll.ctl(epfd, EPOLL_CTL_DEL, fd, 0);
} else {
if (registeredEvents == 0) {
// add to epoll
EPoll.ctl(epfd, EPOLL_CTL_ADD, fd, newEvents);
} else {
// modify events
EPoll.ctl(epfd, EPOLL_CTL_MOD, fd, newEvents);
}
}
ski.registeredEvents(newEvents);
}
}
}
}
}
/**
* Process the polled events.
* If the interrupt fd has been selected, drain it and clear the interrupt.
*/
private int processEvents(int numEntries, Consumer<SelectionKey> action)
throws IOException
{
assert Thread.holdsLock(this);
boolean interrupted = false;
int numKeysUpdated = 0;
for (int i=0; i<numEntries; i++) {
long event = EPoll.getEvent(pollArrayAddress, i);
int fd = EPoll.getDescriptor(event);
if (fd == eventfd.efd()) {
interrupted = true;
} else {
SelectionKeyImpl ski = fdToKey.get(fd);
if (ski != null) {
int rOps = EPoll.getEvents(event);
numKeysUpdated += processReadyEvents(rOps, ski, action);
}
}
}
if (interrupted) {
clearInterrupt();
}
return numKeysUpdated;
}
@Override
protected void implClose() throws IOException {
assert Thread.holdsLock(this);
// prevent further wakeup
synchronized (interruptLock) {
interruptTriggered = true;
}
FileDispatcherImpl.closeIntFD(epfd);
EPoll.freePollArray(pollArrayAddress);
eventfd.close();
}
@Override
protected void implDereg(SelectionKeyImpl ski) throws IOException {
assert !ski.isValid();
assert Thread.holdsLock(this);
int fd = ski.getFDVal();
if (fdToKey.remove(fd) != null) {
if (ski.registeredEvents() != 0) {
EPoll.ctl(epfd, EPOLL_CTL_DEL, fd, 0);
ski.registeredEvents(0);
}
} else {
assert ski.registeredEvents() == 0;
}
}
@Override
public void setEventOps(SelectionKeyImpl ski) {
synchronized (updateLock) {
updateKeys.addLast(ski);
}
}
@Override
public Selector wakeup() {
synchronized (interruptLock) {
if (!interruptTriggered) {
try {
eventfd.set();
} catch (IOException ioe) {
throw new InternalError(ioe);
}
interruptTriggered = true;
}
}
return this;
}
private void clearInterrupt() throws IOException {
synchronized (interruptLock) {
eventfd.reset();
interruptTriggered = false;
}
}
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.ch;
import java.io.IOException;
import java.nio.channels.*;
import java.nio.channels.spi.*;
public class EPollSelectorProvider
extends SelectorProviderImpl
{
public AbstractSelector openSelector() throws IOException {
return new EPollSelectorImpl(this);
}
public Channel inheritedChannel() throws IOException {
return InheritedChannel.getChannel();
}
}

View file

@ -0,0 +1,73 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.ch;
import java.io.IOException;
/*
* Provides access to the Linux eventfd object.
*/
final class EventFD {
private final int efd;
/**
* Creates a blocking eventfd object with initial value zero.
*/
EventFD() throws IOException {
efd = eventfd0();
}
int efd() {
return efd;
}
void set() throws IOException {
set0(efd);
}
void reset() throws IOException {
IOUtil.drain(efd);
}
void close() throws IOException {
FileDispatcherImpl.closeIntFD(efd);
}
private static native int eventfd0() throws IOException;
/**
* Writes the value 1 to the eventfd object as a long in the
* native byte order of the platform.
*
* @param the integral eventfd file descriptor
* @return the number of bytes written; should equal 8
*/
private static native int set0(int efd) throws IOException;
static {
IOUtil.load();
}
}

View file

@ -0,0 +1,64 @@
/*
* Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.ch;
import java.io.FileDescriptor;
class FileDispatcherImpl extends UnixFileDispatcherImpl {
FileDispatcherImpl() {
super();
}
int maxDirectTransferSize() {
return 0x7ffff000; // 2,147,479,552 maximum for sendfile()
}
long transferTo(FileDescriptor src, long position, long count,
FileDescriptor dst, boolean append) {
return transferTo0(src, position, count, dst, append);
}
long transferFrom(FileDescriptor src, FileDescriptor dst,
long position, long count, boolean append) {
return transferFrom0(src, dst, position, count, append);
}
// -- Native methods --
static native long transferTo0(FileDescriptor src, long position,
long count, FileDescriptor dst,
boolean append);
static native long transferFrom0(FileDescriptor src, FileDescriptor dst,
long position, long count, boolean append);
static native void init0();
static {
IOUtil.load();
init0();
}
}

View file

@ -0,0 +1,90 @@
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.ch;
import java.nio.channels.*;
import java.nio.channels.spi.AsynchronousChannelProvider;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadFactory;
import java.io.IOException;
public class LinuxAsynchronousChannelProvider
extends AsynchronousChannelProvider
{
private static volatile EPollPort defaultPort;
private EPollPort defaultEventPort() throws IOException {
if (defaultPort == null) {
synchronized (LinuxAsynchronousChannelProvider.class) {
if (defaultPort == null) {
defaultPort = new EPollPort(this, ThreadPool.getDefault()).start();
}
}
}
return defaultPort;
}
public LinuxAsynchronousChannelProvider() {
}
@Override
public AsynchronousChannelGroup openAsynchronousChannelGroup(int nThreads, ThreadFactory factory)
throws IOException
{
return new EPollPort(this, ThreadPool.create(nThreads, factory)).start();
}
@Override
public AsynchronousChannelGroup openAsynchronousChannelGroup(ExecutorService executor, int initialSize)
throws IOException
{
return new EPollPort(this, ThreadPool.wrap(executor, initialSize)).start();
}
private Port toPort(AsynchronousChannelGroup group) throws IOException {
if (group == null) {
return defaultEventPort();
} else {
if (!(group instanceof EPollPort))
throw new IllegalChannelGroupException();
return (Port)group;
}
}
@Override
public AsynchronousServerSocketChannel openAsynchronousServerSocketChannel(AsynchronousChannelGroup group)
throws IOException
{
return new UnixAsynchronousServerSocketChannelImpl(toPort(group));
}
@Override
public AsynchronousSocketChannel openAsynchronousSocketChannel(AsynchronousChannelGroup group)
throws IOException
{
return new UnixAsynchronousSocketChannelImpl(toPort(group));
}
}

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.fs;
import java.nio.file.FileSystem;
/**
* Creates this platform's default FileSystemProvider.
*/
public class DefaultFileSystemProvider {
private static final LinuxFileSystemProvider INSTANCE
= new LinuxFileSystemProvider();
private DefaultFileSystemProvider() { }
/**
* Returns the platform's default file system provider.
*/
public static LinuxFileSystemProvider instance() {
return INSTANCE;
}
/**
* Returns the platform's default file system.
*/
public static FileSystem theFileSystem() {
return INSTANCE.theFileSystem();
}
}

View file

@ -0,0 +1,273 @@
/*
* Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.fs;
import java.nio.file.attribute.*;
import java.util.Map;
import java.util.Set;
import java.io.IOException;
import jdk.internal.misc.Unsafe;
import static sun.nio.fs.UnixNativeDispatcher.*;
import static sun.nio.fs.UnixConstants.*;
/**
* Linux implementation of DosFileAttributeView for use on file systems such
* as ext3 that have extended attributes enabled and SAMBA configured to store
* DOS attributes.
*/
class LinuxDosFileAttributeView
extends UnixFileAttributeViews.Basic implements DosFileAttributeView
{
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final String READONLY_NAME = "readonly";
private static final String ARCHIVE_NAME = "archive";
private static final String SYSTEM_NAME = "system";
private static final String HIDDEN_NAME = "hidden";
private static final String DOS_XATTR_NAME = "user.DOSATTRIB";
private static final byte[] DOS_XATTR_NAME_AS_BYTES = Util.toBytes(DOS_XATTR_NAME);
private static final int DOS_XATTR_READONLY = 0x01;
private static final int DOS_XATTR_HIDDEN = 0x02;
private static final int DOS_XATTR_SYSTEM = 0x04;
private static final int DOS_XATTR_ARCHIVE = 0x20;
// the names of the DOS attributes (includes basic)
private static final Set<String> dosAttributeNames =
Util.newSet(basicAttributeNames, READONLY_NAME, ARCHIVE_NAME, SYSTEM_NAME, HIDDEN_NAME);
LinuxDosFileAttributeView(UnixPath file, boolean followLinks) {
super(file, followLinks);
}
@Override
public String name() {
return "dos";
}
@Override
public void setAttribute(String attribute, Object value)
throws IOException
{
if (attribute.equals(READONLY_NAME)) {
setReadOnly((Boolean)value);
return;
}
if (attribute.equals(ARCHIVE_NAME)) {
setArchive((Boolean)value);
return;
}
if (attribute.equals(SYSTEM_NAME)) {
setSystem((Boolean)value);
return;
}
if (attribute.equals(HIDDEN_NAME)) {
setHidden((Boolean)value);
return;
}
super.setAttribute(attribute, value);
}
@Override
public Map<String,Object> readAttributes(String[] attributes)
throws IOException
{
AttributesBuilder builder =
AttributesBuilder.create(dosAttributeNames, attributes);
DosFileAttributes attrs = readAttributes();
addRequestedBasicAttributes(attrs, builder);
if (builder.match(READONLY_NAME))
builder.add(READONLY_NAME, attrs.isReadOnly());
if (builder.match(ARCHIVE_NAME))
builder.add(ARCHIVE_NAME, attrs.isArchive());
if (builder.match(SYSTEM_NAME))
builder.add(SYSTEM_NAME, attrs.isSystem());
if (builder.match(HIDDEN_NAME))
builder.add(HIDDEN_NAME, attrs.isHidden());
return builder.unmodifiableMap();
}
@Override
public DosFileAttributes readAttributes() throws IOException {
int fd = -1;
try {
fd = file.openForAttributeAccess(followLinks);
final UnixFileAttributes attrs = UnixFileAttributes.get(fd);
final int dosAttribute = getDosAttribute(fd);
return new DosFileAttributes() {
@Override
public FileTime lastModifiedTime() {
return attrs.lastModifiedTime();
}
@Override
public FileTime lastAccessTime() {
return attrs.lastAccessTime();
}
@Override
public FileTime creationTime() {
return attrs.creationTime();
}
@Override
public boolean isRegularFile() {
return attrs.isRegularFile();
}
@Override
public boolean isDirectory() {
return attrs.isDirectory();
}
@Override
public boolean isSymbolicLink() {
return attrs.isSymbolicLink();
}
@Override
public boolean isOther() {
return attrs.isOther();
}
@Override
public long size() {
return attrs.size();
}
@Override
public Object fileKey() {
return attrs.fileKey();
}
@Override
public boolean isReadOnly() {
return (dosAttribute & DOS_XATTR_READONLY) != 0;
}
@Override
public boolean isHidden() {
return (dosAttribute & DOS_XATTR_HIDDEN) != 0;
}
@Override
public boolean isArchive() {
return (dosAttribute & DOS_XATTR_ARCHIVE) != 0;
}
@Override
public boolean isSystem() {
return (dosAttribute & DOS_XATTR_SYSTEM) != 0;
}
};
} catch (UnixException x) {
x.rethrowAsIOException(file);
return null; // keep compiler happy
} finally {
close(fd, e -> null);
}
}
@Override
public void setReadOnly(boolean value) throws IOException {
updateDosAttribute(DOS_XATTR_READONLY, value);
}
@Override
public void setHidden(boolean value) throws IOException {
updateDosAttribute(DOS_XATTR_HIDDEN, value);
}
@Override
public void setArchive(boolean value) throws IOException {
updateDosAttribute(DOS_XATTR_ARCHIVE, value);
}
@Override
public void setSystem(boolean value) throws IOException {
updateDosAttribute(DOS_XATTR_SYSTEM, value);
}
/**
* Reads the value of the user.DOSATTRIB extended attribute
*/
private int getDosAttribute(int fd) throws UnixException {
final int size = 24;
try (NativeBuffer buffer = NativeBuffers.getNativeBuffer(size)) {
int len = LinuxNativeDispatcher
.fgetxattr(fd, DOS_XATTR_NAME_AS_BYTES, buffer.address(), size);
if (len > 0) {
// ignore null terminator
if (unsafe.getByte(buffer.address()+len-1) == 0)
len--;
// convert to String and parse
byte[] buf = new byte[len];
unsafe.copyMemory(null, buffer.address(), buf,
Unsafe.ARRAY_BYTE_BASE_OFFSET, len);
String value = Util.toString(buf);
// should be something like 0x20
if (value.length() >= 3 && value.startsWith("0x")) {
try {
return Integer.parseInt(value.substring(2), 16);
} catch (NumberFormatException x) {
// ignore
}
}
}
throw new UnixException("Value of " + DOS_XATTR_NAME + " attribute is invalid");
} catch (UnixException x) {
// default value when attribute does not exist
if (x.errno() == ENODATA)
return 0;
throw x;
}
}
/**
* Updates the value of the user.DOSATTRIB extended attribute
*/
private void updateDosAttribute(int flag, boolean enable) throws IOException {
int fd = -1;
try {
fd = file.openForAttributeAccess(followLinks);
int oldValue = getDosAttribute(fd);
int newValue = oldValue;
if (enable) {
newValue |= flag;
} else {
newValue &= ~flag;
}
if (newValue != oldValue) {
byte[] value = Util.toBytes("0x" + Integer.toHexString(newValue));
try (NativeBuffer buffer = NativeBuffers.asNativeBuffer(value)) {
LinuxNativeDispatcher.fsetxattr(fd, DOS_XATTR_NAME_AS_BYTES,
buffer.address(), value.length+1);
}
}
} catch (UnixException x) {
x.rethrowAsIOException(file);
} finally {
close(fd, e -> null);
}
}
}

View file

@ -0,0 +1,159 @@
/*
* Copyright (c) 2008, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.fs;
import java.io.IOException;
import java.nio.file.attribute.DosFileAttributeView;
import java.nio.file.attribute.FileAttributeView;
import java.nio.file.attribute.PosixFileAttributeView;
import java.nio.file.attribute.UserDefinedFileAttributeView;
import java.util.Arrays;
import java.util.List;
/**
* Linux implementation of FileStore
*/
class LinuxFileStore
extends UnixFileStore
{
// used when checking if extended attributes are enabled or not
private volatile boolean xattrChecked;
private volatile boolean xattrEnabled;
LinuxFileStore(UnixPath file) throws IOException {
super(file);
}
LinuxFileStore(UnixFileSystem fs, UnixMountEntry entry) throws IOException {
super(fs, entry);
}
/**
* Finds, and returns, the mount entry for the file system where the file
* resides.
*/
@Override
UnixMountEntry findMountEntry() throws IOException {
LinuxFileSystem fs = (LinuxFileSystem)file().getFileSystem();
// step 1: get realpath
UnixPath path = null;
try {
byte[] rp = UnixNativeDispatcher.realpath(file());
path = new UnixPath(fs, rp);
} catch (UnixException x) {
x.rethrowAsIOException(file());
}
// step 2: find mount point
List<UnixMountEntry> procMountsEntries =
fs.getMountEntries("/proc/mounts");
UnixPath parent = path.getParent();
while (parent != null) {
UnixFileAttributes attrs = null;
try {
attrs = UnixFileAttributes.get(parent, true);
} catch (UnixException x) {
x.rethrowAsIOException(parent);
}
if (attrs.dev() != dev()) {
// step 3: lookup mounted file systems (use /proc/mounts to
// ensure we find the file system even when not in /etc/mtab)
byte[] dir = path.asByteArray();
for (UnixMountEntry entry : procMountsEntries) {
if (Arrays.equals(dir, entry.dir()))
return entry;
}
}
path = parent;
parent = parent.getParent();
}
// step 3: lookup mounted file systems (use /proc/mounts to
// ensure we find the file system even when not in /etc/mtab)
byte[] dir = path.asByteArray();
for (UnixMountEntry entry : procMountsEntries) {
if (Arrays.equals(dir, entry.dir()))
return entry;
}
throw new IOException("Mount point not found");
}
@Override
public boolean supportsFileAttributeView(Class<? extends FileAttributeView> type) {
// support DosFileAttributeView and UserDefinedAttributeView if extended
// attributes enabled
if (type == DosFileAttributeView.class ||
type == UserDefinedFileAttributeView.class)
{
// lookup fstypes.properties
FeatureStatus status = checkIfFeaturePresent("user_xattr");
if (status == FeatureStatus.PRESENT)
return true;
if (status == FeatureStatus.NOT_PRESENT)
return false;
// if file system is mounted with user_xattr option then assume
// extended attributes are enabled
if ((entry().hasOption("user_xattr")))
return true;
// check for explicit disabling of extended attributes
if (entry().hasOption("nouser_xattr")) {
return false;
}
// user_xattr option not present but we special-case ext4 as we
// know that extended attributes are enabled by default
if (entry().fstype().equals("ext4")) {
return true;
}
// not ext4 so probe mount point
if (!xattrChecked) {
UnixPath dir = new UnixPath(file().getFileSystem(), entry().dir());
xattrEnabled = isExtendedAttributesEnabled(dir);
xattrChecked = true;
}
return xattrEnabled;
}
// POSIX attributes not supported on FAT32
if (type == PosixFileAttributeView.class && entry().fstype().equals("vfat"))
return false;
return super.supportsFileAttributeView(type);
}
@Override
public boolean supportsFileAttributeView(String name) {
if (name.equals("dos"))
return supportsFileAttributeView(DosFileAttributeView.class);
if (name.equals("user"))
return supportsFileAttributeView(UserDefinedFileAttributeView.class);
return super.supportsFileAttributeView(name);
}
}

View file

@ -0,0 +1,161 @@
/*
* Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.fs;
import java.nio.file.FileStore;
import java.nio.file.WatchService;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static sun.nio.fs.LinuxNativeDispatcher.*;
import static sun.nio.fs.UnixConstants.*;
/**
* Linux implementation of FileSystem
*/
class LinuxFileSystem extends UnixFileSystem {
LinuxFileSystem(UnixFileSystemProvider provider, String dir) {
super(provider, dir);
}
@Override
public WatchService newWatchService()
throws IOException
{
// assume 2.6.13 or newer
return new LinuxWatchService(this);
}
// lazy initialization of the list of supported attribute views
private static class SupportedFileFileAttributeViewsHolder {
static final Set<String> supportedFileAttributeViews =
supportedFileAttributeViews();
private static Set<String> supportedFileAttributeViews() {
Set<String> result = new HashSet<>();
result.addAll(standardFileAttributeViews());
// additional Linux-specific views
result.add("dos");
result.add("user");
return Collections.unmodifiableSet(result);
}
}
@Override
public Set<String> supportedFileAttributeViews() {
return SupportedFileFileAttributeViewsHolder.supportedFileAttributeViews;
}
@Override
void copyNonPosixAttributes(int ofd, int nfd) {
UnixUserDefinedFileAttributeView.copyExtendedAttributes(ofd, nfd);
}
/**
* Returns object to iterate over the mount entries in the given fstab file.
*/
List<UnixMountEntry> getMountEntries(String fstab) {
ArrayList<UnixMountEntry> entries = new ArrayList<>();
try {
long fp = setmntent(Util.toBytes(fstab), Util.toBytes("r"));
int maxLineSize = 1024;
try {
for (;;) {
int lineSize = getlinelen(fp);
if (lineSize == -1)
break;
if (lineSize > maxLineSize)
maxLineSize = lineSize;
}
} catch (UnixException x) {
// nothing we need to do
} finally {
rewind(fp);
}
try {
for (;;) {
UnixMountEntry entry = new UnixMountEntry();
// count in NUL character at the end
int res = getmntent(fp, entry, maxLineSize + 1);
if (res < 0)
break;
entries.add(entry);
}
} finally {
endmntent(fp);
}
} catch (UnixException x) {
// nothing we can do
}
return entries;
}
/**
* Returns object to iterate over the mount entries in /etc/mtab
*/
@Override
List<UnixMountEntry> getMountEntries() {
return getMountEntries("/etc/mtab");
}
@Override
FileStore getFileStore(UnixMountEntry entry) throws IOException {
return new LinuxFileStore(this, entry);
}
// --- file copying ---
@Override
void bufferedCopy(int dst, int src, long address,
int size, long addressToPollForCancel)
throws UnixException
{
int advice = POSIX_FADV_SEQUENTIAL | // sequential data access
POSIX_FADV_NOREUSE | // will access only once
POSIX_FADV_WILLNEED; // will access in near future
posix_fadvise(src, 0, 0, advice);
super.bufferedCopy(dst, src, address, size, addressToPollForCancel);
}
@Override
int directCopy(int dst, int src, long addressToPollForCancel)
throws UnixException
{
int advice = POSIX_FADV_SEQUENTIAL | // sequential data access
POSIX_FADV_NOREUSE | // will access only once
POSIX_FADV_WILLNEED; // will access in near future
posix_fadvise(src, 0, 0, advice);
return directCopy0(dst, src, addressToPollForCancel);
}
}

View file

@ -0,0 +1,112 @@
/*
* Copyright (c) 2008, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.fs;
import java.nio.file.*;
import java.nio.file.attribute.*;
import java.nio.file.spi.FileTypeDetector;
import java.io.IOException;
import jdk.internal.util.StaticProperty;
/**
* Linux implementation of FileSystemProvider
*/
class LinuxFileSystemProvider extends UnixFileSystemProvider {
public LinuxFileSystemProvider() {
super();
}
@Override
LinuxFileSystem newFileSystem(String dir) {
return new LinuxFileSystem(this, dir);
}
@Override
LinuxFileStore getFileStore(UnixPath path) throws IOException {
return new LinuxFileStore(path);
}
@Override
@SuppressWarnings("unchecked")
public <V extends FileAttributeView> V getFileAttributeView(Path obj,
Class<V> type,
LinkOption... options)
{
if (type == DosFileAttributeView.class) {
return (V) new LinuxDosFileAttributeView(UnixPath.toUnixPath(obj),
Util.followLinks(options));
}
if (type == UserDefinedFileAttributeView.class) {
return (V) new LinuxUserDefinedFileAttributeView(UnixPath.toUnixPath(obj),
Util.followLinks(options));
}
return super.getFileAttributeView(obj, type, options);
}
@Override
public DynamicFileAttributeView getFileAttributeView(Path obj,
String name,
LinkOption... options)
{
if (name.equals("dos")) {
return new LinuxDosFileAttributeView(UnixPath.toUnixPath(obj),
Util.followLinks(options));
}
if (name.equals("user")) {
return new LinuxUserDefinedFileAttributeView(UnixPath.toUnixPath(obj),
Util.followLinks(options));
}
return super.getFileAttributeView(obj, name, options);
}
@Override
@SuppressWarnings("unchecked")
public <A extends BasicFileAttributes> A readAttributes(Path file,
Class<A> type,
LinkOption... options)
throws IOException
{
if (type == DosFileAttributes.class) {
DosFileAttributeView view =
getFileAttributeView(file, DosFileAttributeView.class, options);
return (A) view.readAttributes();
} else {
return super.readAttributes(file, type, options);
}
}
@Override
FileTypeDetector getFileTypeDetector() {
String userHome = StaticProperty.userHome();
Path userMimeTypes = Path.of(userHome, ".mime.types");
Path etcMimeTypes = Path.of("/etc/mime.types");
return chain(new MimeTypesFileTypeDetector(userMimeTypes),
new MimeTypesFileTypeDetector(etcMimeTypes));
}
}

View file

@ -0,0 +1,96 @@
/*
* Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.fs;
/**
* Linux specific system calls.
*/
class LinuxNativeDispatcher extends UnixNativeDispatcher {
private LinuxNativeDispatcher() { }
/**
* FILE *setmntent(const char *filename, const char *type);
*/
static long setmntent(byte[] filename, byte[] type) throws UnixException {
try (NativeBuffer pathBuffer = NativeBuffers.asNativeBuffer(filename);
NativeBuffer typeBuffer = NativeBuffers.asNativeBuffer(type)) {
return setmntent0(pathBuffer.address(), typeBuffer.address());
}
}
private static native long setmntent0(long pathAddress, long typeAddress)
throws UnixException;
/**
* int getmntent(FILE *fp, struct mnttab *mp, int len);
*/
static int getmntent(long fp, UnixMountEntry entry, int buflen) throws UnixException {
try (NativeBuffer buffer = NativeBuffers.getNativeBuffer(buflen)) {
return getmntent0(fp, entry, buffer.address(), buflen);
}
}
static native int getmntent0(long fp, UnixMountEntry entry, long buffer, int bufLen)
throws UnixException;
/**
* int endmntent(FILE* filep);
*/
static native void endmntent(long stream) throws UnixException;
/**
* int posix_fadvise(int fd, off_t offset, off_t len, int advice);
*/
static native int posix_fadvise(int fd, long offset, long len, int advice)
throws UnixException;
/**
* Copies data between file descriptors {@code src} and {@code dst} using
* a platform-specific function or system call possibly having kernel
* support.
*
* @param dst destination file descriptor
* @param src source file descriptor
* @param addressToPollForCancel address to check for cancellation
* (a non-zero value written to this address indicates cancel)
*
* @return 0 on success, UNAVAILABLE if the platform function would block,
* UNSUPPORTED_CASE if the call does not work with the given
* parameters, or UNSUPPORTED if direct copying is not supported
* on this platform
*/
static native int directCopy0(int dst, int src, long addressToPollForCancel)
throws UnixException;
// initialize
private static native void init();
static {
jdk.internal.loader.BootLoader.loadLibrary("nio");
init();
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.fs;
class LinuxUserDefinedFileAttributeView
extends UnixUserDefinedFileAttributeView
{
LinuxUserDefinedFileAttributeView(UnixPath file, boolean followLinks) {
super(file, followLinks);
}
@Override
protected int maxNameLength() {
return 255;
}
}

View file

@ -0,0 +1,455 @@
/*
* Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.fs;
import java.nio.file.*;
import java.util.*;
import java.io.IOException;
import jdk.internal.misc.Unsafe;
import static sun.nio.fs.UnixNativeDispatcher.*;
import static sun.nio.fs.UnixConstants.*;
/**
* Linux implementation of WatchService based on inotify.
*
* In summary a background thread polls inotify plus a socket used for the wakeup
* mechanism. Requests to add or remove a watch, or close the watch service,
* cause the thread to wakeup and process the request. Events are processed
* by the thread which causes it to signal/queue the corresponding watch keys.
*/
class LinuxWatchService
extends AbstractWatchService
{
private static final Unsafe unsafe = Unsafe.getUnsafe();
// background thread to read change events
private final Poller poller;
LinuxWatchService(UnixFileSystem fs) throws IOException {
// initialize inotify
int ifd = - 1;
try {
ifd = inotifyInit();
} catch (UnixException x) {
String msg = (x.errno() == EMFILE) ?
"User limit of inotify instances reached or too many open files" :
x.errorString();
throw new IOException(msg);
}
// configure inotify to be non-blocking
// create socketpair used in the close mechanism
int sp[] = new int[2];
try {
configureBlocking(ifd, false);
socketpair(sp);
configureBlocking(sp[0], false);
} catch (UnixException x) {
UnixNativeDispatcher.close(ifd, e -> null);
throw new IOException(x.errorString());
}
this.poller = new Poller(fs, this, ifd, sp);
this.poller.start();
}
@Override
WatchKey register(Path dir,
WatchEvent.Kind<?>[] events,
WatchEvent.Modifier... modifiers)
throws IOException
{
// delegate to poller
return poller.register(dir, events, modifiers);
}
@Override
void implClose() throws IOException {
// delegate to poller
poller.close();
}
/**
* WatchKey implementation
*/
private static class LinuxWatchKey extends AbstractWatchKey {
// inotify descriptor
private final int ifd;
// watch descriptor
private volatile int wd;
LinuxWatchKey(UnixPath dir, LinuxWatchService watcher, int ifd, int wd) {
super(dir, watcher);
this.ifd = ifd;
this.wd = wd;
}
int descriptor() {
return wd;
}
void invalidate(boolean remove) {
if (remove) {
try {
inotifyRmWatch(ifd, wd);
} catch (UnixException x) {
// ignore
}
}
wd = -1;
}
@Override
public boolean isValid() {
return (wd != -1);
}
@Override
public void cancel() {
if (isValid()) {
// delegate to poller
((LinuxWatchService)watcher()).poller.cancel(this);
}
}
}
/**
* Background thread to read from inotify
*/
private static class Poller extends AbstractPoller {
/**
* struct inotify_event {
* int wd;
* uint32_t mask;
* uint32_t len;
* char name __flexarr; // present if len > 0
* } act_t;
*/
private static final int SIZEOF_INOTIFY_EVENT = eventSize();
private static final int[] offsets = eventOffsets();
private static final int OFFSETOF_WD = offsets[0];
private static final int OFFSETOF_MASK = offsets[1];
private static final int OFFSETOF_LEN = offsets[3];
private static final int OFFSETOF_NAME = offsets[4];
private static final int IN_MODIFY = 0x00000002;
private static final int IN_ATTRIB = 0x00000004;
private static final int IN_MOVED_FROM = 0x00000040;
private static final int IN_MOVED_TO = 0x00000080;
private static final int IN_CREATE = 0x00000100;
private static final int IN_DELETE = 0x00000200;
private static final int IN_UNMOUNT = 0x00002000;
private static final int IN_Q_OVERFLOW = 0x00004000;
private static final int IN_IGNORED = 0x00008000;
// sizeof buffer for when polling inotify
private static final int BUFFER_SIZE = 8192;
private final UnixFileSystem fs;
private final LinuxWatchService watcher;
// inotify file descriptor
private final int ifd;
// socketpair used to shutdown polling thread
private final int socketpair[];
// maps watch descriptor to Key
private final Map<Integer,LinuxWatchKey> wdToKey;
// address of read buffer
private final long address;
Poller(UnixFileSystem fs, LinuxWatchService watcher, int ifd, int[] sp) {
this.fs = fs;
this.watcher = watcher;
this.ifd = ifd;
this.socketpair = sp;
this.wdToKey = new HashMap<>();
this.address = unsafe.allocateMemory(BUFFER_SIZE);
}
@Override
void wakeup() throws IOException {
// write to socketpair to wakeup polling thread
try {
write(socketpair[1], address, 1);
} catch (UnixException x) {
throw new IOException(x.errorString());
}
}
@Override
Object implRegister(Path obj,
Set<? extends WatchEvent.Kind<?>> events,
WatchEvent.Modifier... modifiers)
{
UnixPath dir = (UnixPath)obj;
int mask = 0;
for (WatchEvent.Kind<?> event: events) {
if (event == StandardWatchEventKinds.ENTRY_CREATE) {
mask |= IN_CREATE | IN_MOVED_TO;
continue;
}
if (event == StandardWatchEventKinds.ENTRY_DELETE) {
mask |= IN_DELETE | IN_MOVED_FROM;
continue;
}
if (event == StandardWatchEventKinds.ENTRY_MODIFY) {
mask |= IN_MODIFY | IN_ATTRIB;
continue;
}
}
// no modifiers supported at this time
for (WatchEvent.Modifier modifier : modifiers) {
if (modifier == null)
return new NullPointerException();
if (!ExtendedOptions.SENSITIVITY_HIGH.matches(modifier) &&
!ExtendedOptions.SENSITIVITY_MEDIUM.matches(modifier) &&
!ExtendedOptions.SENSITIVITY_LOW.matches(modifier)) {
return new UnsupportedOperationException("Modifier not supported");
}
}
// check file is directory
UnixFileAttributes attrs = null;
try {
attrs = UnixFileAttributes.get(dir, true);
} catch (UnixException x) {
return x.asIOException(dir);
}
if (!attrs.isDirectory()) {
return new NotDirectoryException(dir.getPathForExceptionMessage());
}
// register with inotify (replaces existing mask if already registered)
int wd;
try (NativeBuffer buffer =
NativeBuffers.asNativeBuffer(dir.getByteArrayForSysCalls())) {
wd = inotifyAddWatch(ifd, buffer.address(), mask);
} catch (UnixException x) {
if (x.errno() == ENOSPC) {
return new IOException("User limit of inotify watches reached");
}
return x.asIOException(dir);
}
// ensure watch descriptor is in map
LinuxWatchKey key = wdToKey.get(wd);
if (key == null) {
key = new LinuxWatchKey(dir, watcher, ifd, wd);
wdToKey.put(wd, key);
}
return key;
}
// cancel single key
@Override
void implCancelKey(WatchKey obj) {
LinuxWatchKey key = (LinuxWatchKey)obj;
if (key.isValid()) {
wdToKey.remove(key.descriptor());
key.invalidate(true);
}
}
// close watch service
@Override
void implCloseAll() {
// invalidate all keys
for (Map.Entry<Integer,LinuxWatchKey> entry: wdToKey.entrySet()) {
entry.getValue().invalidate(true);
}
wdToKey.clear();
// free resources
unsafe.freeMemory(address);
UnixNativeDispatcher.close(socketpair[0], e -> null);
UnixNativeDispatcher.close(socketpair[1], e -> null);
UnixNativeDispatcher.close(ifd, e -> null);
}
/**
* Poller main loop
*/
@Override
public void run() {
try {
for (;;) {
int nReady, bytesRead;
// wait for close or inotify event
nReady = poll(ifd, socketpair[0]);
// read from inotify
try {
bytesRead = read(ifd, address, BUFFER_SIZE);
} catch (UnixException x) {
if (x.errno() != EAGAIN && x.errno() != EWOULDBLOCK)
throw x;
bytesRead = 0;
}
// iterate over buffer to decode events
int offset = 0;
while (offset < bytesRead) {
long event = address + offset;
int wd = unsafe.getInt(event + OFFSETOF_WD);
int mask = unsafe.getInt(event + OFFSETOF_MASK);
int len = unsafe.getInt(event + OFFSETOF_LEN);
// file name
UnixPath name = null;
if (len > 0) {
int actual = len;
// null-terminated and maybe additional null bytes to
// align the next event
while (actual > 0) {
long last = event + OFFSETOF_NAME + actual - 1;
if (unsafe.getByte(last) != 0)
break;
actual--;
}
if (actual > 0) {
byte[] buf = new byte[actual];
unsafe.copyMemory(null, event + OFFSETOF_NAME,
buf, Unsafe.ARRAY_BYTE_BASE_OFFSET, actual);
name = new UnixPath(fs, buf);
}
}
// process event
processEvent(wd, mask, name);
offset += (SIZEOF_INOTIFY_EVENT + len);
}
// process any pending requests
if ((nReady > 1) || (nReady == 1 && bytesRead == 0)) {
try {
read(socketpair[0], address, BUFFER_SIZE);
boolean shutdown = processRequests();
if (shutdown)
break;
} catch (UnixException x) {
if (x.errno() != EAGAIN && x.errno() != EWOULDBLOCK)
throw x;
}
}
}
} catch (UnixException x) {
x.printStackTrace();
}
}
/**
* map inotify event to WatchEvent.Kind
*/
private WatchEvent.Kind<?> maskToEventKind(int mask) {
if ((mask & IN_MODIFY) > 0)
return StandardWatchEventKinds.ENTRY_MODIFY;
if ((mask & IN_ATTRIB) > 0)
return StandardWatchEventKinds.ENTRY_MODIFY;
if ((mask & IN_CREATE) > 0)
return StandardWatchEventKinds.ENTRY_CREATE;
if ((mask & IN_MOVED_TO) > 0)
return StandardWatchEventKinds.ENTRY_CREATE;
if ((mask & IN_DELETE) > 0)
return StandardWatchEventKinds.ENTRY_DELETE;
if ((mask & IN_MOVED_FROM) > 0)
return StandardWatchEventKinds.ENTRY_DELETE;
return null;
}
/**
* Process event from inotify
*/
private void processEvent(int wd, int mask, final UnixPath name) {
// overflow - signal all keys
if ((mask & IN_Q_OVERFLOW) > 0) {
for (Map.Entry<Integer,LinuxWatchKey> entry: wdToKey.entrySet()) {
entry.getValue()
.signalEvent(StandardWatchEventKinds.OVERFLOW, null);
}
return;
}
// lookup wd to get key
LinuxWatchKey key = wdToKey.get(wd);
if (key == null)
return; // should not happen
// file deleted
if ((mask & IN_IGNORED) > 0) {
wdToKey.remove(wd);
key.invalidate(false);
key.signal();
return;
}
// event for directory itself
if (name == null)
return;
// map to event and queue to key
WatchEvent.Kind<?> kind = maskToEventKind(mask);
if (kind != null) {
key.signalEvent(kind, name);
}
}
}
// -- native methods --
// sizeof inotify_event
private static native int eventSize();
// offsets of inotify_event
private static native int[] eventOffsets();
private static native int inotifyInit() throws UnixException;
private static native int inotifyAddWatch(int fd, long pathAddress, int mask)
throws UnixException;
private static native void inotifyRmWatch(int fd, int wd)
throws UnixException;
private static native void configureBlocking(int fd, boolean blocking)
throws UnixException;
private static native void socketpair(int[] sv) throws UnixException;
private static native int poll(int fd1, int fd2) throws UnixException;
static {
jdk.internal.loader.BootLoader.loadLibrary("nio");
}
}

View file

@ -0,0 +1,64 @@
/*
* Copyright (c) 2020, 2024, Red Hat, Inc.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include <unistd.h>
#include <sys/sysinfo.h>
#include "jni.h"
#include "jvm.h"
#include "jdk_internal_platform_CgroupMetrics.h"
JNIEXPORT jboolean JNICALL
Java_jdk_internal_platform_CgroupMetrics_isUseContainerSupport(JNIEnv *env, jclass ignored)
{
return JVM_IsUseContainerSupport();
}
JNIEXPORT jboolean JNICALL
Java_jdk_internal_platform_CgroupMetrics_isContainerized0(JNIEnv *env, jclass ignored)
{
return JVM_IsContainerized();
}
JNIEXPORT jlong JNICALL
Java_jdk_internal_platform_CgroupMetrics_getTotalMemorySize0
(JNIEnv *env, jclass ignored)
{
jlong pages = sysconf(_SC_PHYS_PAGES);
jlong page_size = sysconf(_SC_PAGESIZE);
return pages * page_size;
}
JNIEXPORT jlong JNICALL
Java_jdk_internal_platform_CgroupMetrics_getTotalSwapSize0
(JNIEnv *env, jclass ignored)
{
struct sysinfo si;
int retval = sysinfo(&si);
if (retval < 0) {
return 0; // syinfo failed, treat as no swap
}
return (jlong)(si.totalswap * si.mem_unit);
}

View file

@ -0,0 +1,393 @@
/*
* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "jni.h"
#include "jni_util.h"
#include "java_lang_ProcessHandleImpl.h"
#include "java_lang_ProcessHandleImpl_Info.h"
#include "ProcessHandleImpl_unix.h"
#include <dirent.h>
#include <fcntl.h>
#include <limits.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <ctype.h>
/*
* Implementation of native ProcessHandleImpl functions for Linux.
* See ProcessHandleImpl_unix.c for more details.
*/
/* Signatures for internal OS specific functions. */
static long long getBoottime(JNIEnv *env);
/* A static offset in milliseconds since boot. */
static long long bootTime_ms;
static long clock_ticks_per_second;
static int pageSize;
void os_initNative(JNIEnv *env, jclass clazz) {
bootTime_ms = getBoottime(env);
clock_ticks_per_second = sysconf(_SC_CLK_TCK);
pageSize = sysconf(_SC_PAGESIZE);
}
/*
* Return pids of active processes, and optionally parent pids and
* start times for each process.
* For a specific non-zero pid, only the direct children are returned.
* If the pid is zero, all active processes are returned.
* Reads /proc and accumulates any process following the rules above.
* The resulting pids are stored into an array of longs named jarray.
* The number of pids is returned if they all fit.
* If the parentArray is non-null, store also the parent pid.
* In this case the parentArray must have the same length as the result pid array.
* Of course in the case of a given non-zero pid all entries in the parentArray
* will contain this pid, so this array does only make sense in the case of a given
* zero pid.
* If the jstimesArray is non-null, store also the start time of the pid.
* In this case the jstimesArray must have the same length as the result pid array.
* If the array(s) (is|are) too short, excess pids are not stored and
* the desired length is returned.
*/
jint os_getChildren(JNIEnv *env, jlong jpid, jlongArray jarray,
jlongArray jparentArray, jlongArray jstimesArray) {
DIR* dir;
struct dirent* ptr;
pid_t pid = (pid_t) jpid;
jlong* pids = NULL;
jlong* ppids = NULL;
jlong* stimes = NULL;
jsize parentArraySize = 0;
jsize arraySize = 0;
jsize stimesSize = 0;
jsize count = 0;
arraySize = (*env)->GetArrayLength(env, jarray);
JNU_CHECK_EXCEPTION_RETURN(env, -1);
if (jparentArray != NULL) {
parentArraySize = (*env)->GetArrayLength(env, jparentArray);
JNU_CHECK_EXCEPTION_RETURN(env, -1);
if (arraySize != parentArraySize) {
JNU_ThrowIllegalArgumentException(env, "array sizes not equal");
return 0;
}
}
if (jstimesArray != NULL) {
stimesSize = (*env)->GetArrayLength(env, jstimesArray);
JNU_CHECK_EXCEPTION_RETURN(env, -1);
if (arraySize != stimesSize) {
JNU_ThrowIllegalArgumentException(env, "array sizes not equal");
return 0;
}
}
/*
* To locate the children we scan /proc looking for files that have a
* position integer as a filename.
*/
if ((dir = opendir("/proc")) == NULL) {
JNU_ThrowByNameWithMessageAndLastError(env,
"java/lang/RuntimeException", "Unable to open /proc");
return -1;
}
do { // Block to break out of on Exception
pids = (*env)->GetLongArrayElements(env, jarray, NULL);
if (pids == NULL) {
break;
}
if (jparentArray != NULL) {
ppids = (*env)->GetLongArrayElements(env, jparentArray, NULL);
if (ppids == NULL) {
break;
}
}
if (jstimesArray != NULL) {
stimes = (*env)->GetLongArrayElements(env, jstimesArray, NULL);
if (stimes == NULL) {
break;
}
}
while ((ptr = readdir(dir)) != NULL) {
pid_t ppid = 0;
jlong totalTime = 0L;
jlong startTime = 0L;
/* skip files that aren't numbers */
pid_t childpid = (pid_t) atoi(ptr->d_name);
if ((int) childpid <= 0) {
continue;
}
// Get the parent pid, and start time
ppid = os_getParentPidAndTimings(env, childpid, &totalTime, &startTime);
if (ppid >= 0 && (pid == 0 || ppid == pid)) {
if (count < arraySize) {
// Only store if it fits
pids[count] = (jlong) childpid;
if (ppids != NULL) {
// Store the parentPid
ppids[count] = (jlong) ppid;
}
if (stimes != NULL) {
// Store the process start time
stimes[count] = startTime;
}
}
count++; // Count to tabulate size needed
}
}
} while (0);
if (pids != NULL) {
(*env)->ReleaseLongArrayElements(env, jarray, pids, 0);
}
if (ppids != NULL) {
(*env)->ReleaseLongArrayElements(env, jparentArray, ppids, 0);
}
if (stimes != NULL) {
(*env)->ReleaseLongArrayElements(env, jstimesArray, stimes, 0);
}
closedir(dir);
// If more pids than array had size for; count will be greater than array size
return count;
}
/**
* Read /proc/<pid>/stat and return the ppid, total cputime and start time.
* -1 is fail; >= 0 is parent pid
* 'total' will contain the running time of 'pid' in nanoseconds.
* 'start' will contain the start time of 'pid' in milliseconds since epoch.
*/
pid_t os_getParentPidAndTimings(JNIEnv *env, pid_t pid,
jlong *totalTime, jlong* startTime) {
FILE* fp;
char buffer[2048];
int statlen;
char fn[32];
char* s;
int parentPid;
long unsigned int utime = 0; // clock tics
long unsigned int stime = 0; // clock tics
long long unsigned int start = 0; // microseconds
/*
* Try to stat and then open /proc/%d/stat
*/
snprintf(fn, sizeof fn, "/proc/%d/stat", pid);
fp = fopen(fn, "r");
if (fp == NULL) {
return -1; // fail, no such /proc/pid/stat
}
/*
* The format is: pid (command) state ppid ...
* As the command could be anything we must find the right most
* ")" and then skip the white spaces that follow it.
*/
statlen = fread(buffer, 1, (sizeof buffer - 1), fp);
fclose(fp);
if (statlen < 0) {
return -1; // parent pid is not available
}
buffer[statlen] = '\0';
s = strchr(buffer, '(');
if (s == NULL) {
return -1; // parent pid is not available
}
// Found start of command, skip to end
s++;
s = strrchr(s, ')');
if (s == NULL) {
return -1; // parent pid is not available
}
s++;
// Scan the needed fields from status, retaining only ppid(4),
// utime (14), stime(15), starttime(22)
if (4 != sscanf(s, " %*c %d %*d %*d %*d %*d %*d %*u %*u %*u %*u %lu %lu %*d %*d %*d %*d %*d %*d %llu",
&parentPid, &utime, &stime, &start)) {
return 0; // not all values parsed; return error
}
*totalTime = (utime + stime) * (jlong)(1000000000 / clock_ticks_per_second);
*startTime = bootTime_ms + ((start * 1000) / clock_ticks_per_second);
return parentPid;
}
void os_getCmdlineAndUserInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
int fd;
int cmdlen = 0;
char *cmdline = NULL, *cmdEnd = NULL; // used for command line args and exe
char *args = NULL;
jstring cmdexe = NULL;
char fn[32];
struct stat stat_buf;
/*
* Stat /proc/<pid> to get the user id
*/
snprintf(fn, sizeof fn, "/proc/%d", pid);
if (stat(fn, &stat_buf) == 0) {
unix_getUserInfo(env, jinfo, stat_buf.st_uid);
JNU_CHECK_EXCEPTION(env);
}
/*
* Try to open /proc/<pid>/cmdline
*/
strncat(fn, "/cmdline", sizeof fn - strnlen(fn, sizeof fn) - 1);
if ((fd = open(fn, O_RDONLY)) < 0) {
return;
}
do { // Block to break out of on errors
int i, truncated = 0;
int count;
char *s;
/*
* The path name read by readlink() is limited to PATH_MAX characters.
* The content of /proc/<pid>/cmdline is limited to PAGE_SIZE characters.
*/
cmdline = (char*)malloc((PATH_MAX > pageSize ? PATH_MAX : pageSize) + 1);
if (cmdline == NULL) {
break;
}
/*
* On Linux, the full path to the executable command is the link in
* /proc/<pid>/exe. But it is only readable for processes we own.
*/
snprintf(fn, sizeof fn, "/proc/%d/exe", pid);
if ((cmdlen = readlink(fn, cmdline, PATH_MAX)) > 0) {
// null terminate and create String to store for command
cmdline[cmdlen] = '\0';
cmdexe = JNU_NewStringPlatform(env, cmdline);
(*env)->ExceptionClear(env); // unconditionally clear any exception
}
/*
* The command-line arguments appear as a set of strings separated by
* null bytes ('\0'), with a further null byte after the last
* string. The last string is only null terminated if the whole command
* line is not exceeding (PAGE_SIZE - 1) characters.
*/
cmdlen = 0;
s = cmdline;
while ((count = read(fd, s, pageSize - cmdlen)) > 0) {
cmdlen += count;
s += count;
}
if (count < 0) {
break;
}
// We have to null-terminate because the process may have changed argv[]
// or because the content in /proc/<pid>/cmdline is truncated.
cmdline[cmdlen] = '\0';
if (cmdlen == pageSize && cmdline[pageSize - 1] != '\0') {
truncated = 1;
} else if (cmdlen == 0) {
// /proc/<pid>/cmdline was empty. This usually happens for kernel processes
// like '[kthreadd]'. We could try to read /proc/<pid>/comm in the future.
}
if (cmdlen > 0 && (cmdexe == NULL || truncated)) {
// We have no exact command or the arguments are truncated.
// In this case we save the command line from /proc/<pid>/cmdline.
args = (char*)malloc(pageSize + 1);
if (args != NULL) {
memcpy(args, cmdline, cmdlen + 1);
for (i = 0; i < cmdlen; i++) {
if (args[i] == '\0') {
args[i] = ' ';
}
}
}
}
i = 0;
if (!truncated) {
// Count the arguments
cmdEnd = &cmdline[cmdlen];
for (s = cmdline; *s != '\0' && (s < cmdEnd); i++) {
s += strnlen(s, (cmdEnd - s)) + 1;
}
}
unix_fillArgArray(env, jinfo, i, cmdline, cmdEnd, cmdexe, args);
} while (0);
if (cmdline != NULL) {
free(cmdline);
}
if (args != NULL) {
free(args);
}
if (fd >= 0) {
close(fd);
}
}
/**
* Read the boottime from /proc/stat.
*/
static long long getBoottime(JNIEnv *env) {
FILE *fp;
char *line = NULL;
size_t len = 0;
long long bootTime = 0;
fp = fopen("/proc/stat", "r");
if (fp == NULL) {
return -1;
}
while (getline(&line, &len, fp) != -1) {
if (sscanf(line, "btime %llu", &bootTime) == 1) {
break;
}
}
free(line);
if (fp != 0) {
fclose(fp);
}
return bootTime * 1000;
}

View file

@ -0,0 +1,96 @@
/*
* Copyright (c) 2008, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include <dlfcn.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/epoll.h>
#include "jni.h"
#include "jni_util.h"
#include "jvm.h"
#include "jlong.h"
#include "nio.h"
#include "nio_util.h"
#include "sun_nio_ch_EPoll.h"
JNIEXPORT jint JNICALL
Java_sun_nio_ch_EPoll_eventSize(JNIEnv* env, jclass clazz)
{
return sizeof(struct epoll_event);
}
JNIEXPORT jint JNICALL
Java_sun_nio_ch_EPoll_eventsOffset(JNIEnv* env, jclass clazz)
{
return offsetof(struct epoll_event, events);
}
JNIEXPORT jint JNICALL
Java_sun_nio_ch_EPoll_dataOffset(JNIEnv* env, jclass clazz)
{
return offsetof(struct epoll_event, data);
}
JNIEXPORT jint JNICALL
Java_sun_nio_ch_EPoll_create(JNIEnv *env, jclass clazz) {
int epfd = epoll_create1(EPOLL_CLOEXEC);
if (epfd < 0) {
JNU_ThrowIOExceptionWithLastError(env, "epoll_create1 failed");
}
return epfd;
}
JNIEXPORT jint JNICALL
Java_sun_nio_ch_EPoll_ctl(JNIEnv *env, jclass clazz, jint epfd,
jint opcode, jint fd, jint events)
{
struct epoll_event event;
int res;
event.events = events;
event.data.fd = fd;
res = epoll_ctl(epfd, (int)opcode, (int)fd, &event);
return (res == 0) ? 0 : errno;
}
JNIEXPORT jint JNICALL
Java_sun_nio_ch_EPoll_wait(JNIEnv *env, jclass clazz, jint epfd,
jlong address, jint numfds, jint timeout)
{
struct epoll_event *events = jlong_to_ptr(address);
int res = epoll_wait(epfd, events, numfds, timeout);
if (res < 0) {
if (errno == EINTR) {
return IOS_INTERRUPTED;
} else {
JNU_ThrowIOExceptionWithLastError(env, "epoll_wait failed");
return IOS_THROWN;
}
}
return res;
}

View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include <sys/eventfd.h>
#include "jni.h"
#include "jni_util.h"
#include "jvm.h"
#include "jlong.h"
#include "nio.h"
#include "nio_util.h"
#include "sun_nio_ch_EventFD.h"
JNIEXPORT jint JNICALL
Java_sun_nio_ch_EventFD_eventfd0(JNIEnv *env, jclass klazz)
{
int efd = eventfd((uint64_t)0, 0);
if (efd == -1) {
JNU_ThrowIOExceptionWithLastError(env, "eventfd failed");
return IOS_THROWN;
}
return efd;
}
JNIEXPORT jint JNICALL
Java_sun_nio_ch_EventFD_set0(JNIEnv *env, jclass klazz, jint efd)
{
uint64_t one = 1ULL;
return convertReturnVal(env, write(efd, (void*)&one, sizeof(uint64_t)),
JNI_FALSE);
}

View file

@ -0,0 +1,131 @@
/*
* Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include <sys/sendfile.h>
#include <dlfcn.h>
#include "jni.h"
#include "nio.h"
#include "nio_util.h"
#include "sun_nio_ch_FileDispatcherImpl.h"
typedef ssize_t copy_file_range_func(int, loff_t*, int, loff_t*, size_t,
unsigned int);
static copy_file_range_func* my_copy_file_range_func = NULL;
JNIEXPORT void JNICALL
Java_sun_nio_ch_FileDispatcherImpl_init0(JNIEnv *env, jclass klass)
{
my_copy_file_range_func =
(copy_file_range_func*) dlsym(RTLD_DEFAULT, "copy_file_range");
}
JNIEXPORT jlong JNICALL
Java_sun_nio_ch_FileDispatcherImpl_transferFrom0(JNIEnv *env, jobject this,
jobject srcFDO, jobject dstFDO,
jlong position, jlong count,
jboolean append)
{
if (my_copy_file_range_func == NULL)
return IOS_UNSUPPORTED;
// copy_file_range fails with EBADF when appending
if (append == JNI_TRUE)
return IOS_UNSUPPORTED_CASE;
jint srcFD = fdval(env, srcFDO);
jint dstFD = fdval(env, dstFDO);
loff_t offset = (loff_t)position;
size_t len = (size_t)count;
jlong n = my_copy_file_range_func(srcFD, NULL, dstFD, &offset, len, 0);
if (n < 0) {
if (errno == EAGAIN)
return IOS_UNAVAILABLE;
if (errno == ENOSYS || errno == EOPNOTSUPP)
return IOS_UNSUPPORTED_CASE;
if ((errno == EBADF || errno == EINVAL || errno == EXDEV) &&
((ssize_t)count >= 0))
return IOS_UNSUPPORTED_CASE;
if (errno == EINTR) {
return IOS_INTERRUPTED;
}
JNU_ThrowIOExceptionWithLastError(env, "Transfer failed");
return IOS_THROWN;
}
return n;
}
JNIEXPORT jlong JNICALL
Java_sun_nio_ch_FileDispatcherImpl_transferTo0(JNIEnv *env, jobject this,
jobject srcFDO,
jlong position, jlong count,
jobject dstFDO, jboolean append)
{
jint srcFD = fdval(env, srcFDO);
jint dstFD = fdval(env, dstFDO);
// copy_file_range fails with EBADF when appending, and sendfile
// fails with EINVAL
if (append == JNI_TRUE)
return IOS_UNSUPPORTED_CASE;
loff_t offset = (loff_t)position;
jlong n;
if (my_copy_file_range_func != NULL) {
size_t len = (size_t)count;
n = my_copy_file_range_func(srcFD, &offset, dstFD, NULL, len, 0);
if (n < 0) {
switch (errno) {
case EINTR:
return IOS_INTERRUPTED;
case EINVAL:
case ENOSYS:
case EXDEV:
case EOPNOTSUPP:
// ignore and try sendfile()
break;
default:
JNU_ThrowIOExceptionWithLastError(env, "Copy failed");
return IOS_THROWN;
}
}
if (n >= 0)
return n;
}
n = sendfile(dstFD, srcFD, &offset, (size_t)count);
if (n < 0) {
if (errno == EAGAIN)
return IOS_UNAVAILABLE;
if ((errno == EINVAL) && ((ssize_t)count >= 0))
return IOS_UNSUPPORTED_CASE;
if (errno == EINTR) {
return IOS_INTERRUPTED;
}
JNU_ThrowIOExceptionWithLastError(env, "Transfer failed");
return IOS_THROWN;
}
return n;
}

View file

@ -0,0 +1,231 @@
/*
* Copyright (c) 2008, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "jni.h"
#include "jni_util.h"
#include "jvm.h"
#include "jlong.h"
#include "nio.h"
#include <stdio.h>
#include <string.h>
#include <dlfcn.h>
#include <errno.h>
#include <mntent.h>
#include <fcntl.h>
#include <sys/sendfile.h>
#include "sun_nio_fs_LinuxNativeDispatcher.h"
static jfieldID entry_name;
static jfieldID entry_dir;
static jfieldID entry_fstype;
static jfieldID entry_options;
typedef ssize_t copy_file_range_func(int, loff_t*, int, loff_t*, size_t,
unsigned int);
static copy_file_range_func* my_copy_file_range_func = NULL;
static void throwUnixException(JNIEnv* env, int errnum) {
jobject x = JNU_NewObjectByName(env, "sun/nio/fs/UnixException",
"(I)V", errnum);
if (x != NULL) {
(*env)->Throw(env, x);
}
}
JNIEXPORT void JNICALL
Java_sun_nio_fs_LinuxNativeDispatcher_init(JNIEnv *env, jclass clazz)
{
clazz = (*env)->FindClass(env, "sun/nio/fs/UnixMountEntry");
CHECK_NULL(clazz);
entry_name = (*env)->GetFieldID(env, clazz, "name", "[B");
CHECK_NULL(entry_name);
entry_dir = (*env)->GetFieldID(env, clazz, "dir", "[B");
CHECK_NULL(entry_dir);
entry_fstype = (*env)->GetFieldID(env, clazz, "fstype", "[B");
CHECK_NULL(entry_fstype);
entry_options = (*env)->GetFieldID(env, clazz, "opts", "[B");
CHECK_NULL(entry_options);
my_copy_file_range_func =
(copy_file_range_func*) dlsym(RTLD_DEFAULT, "copy_file_range");
}
JNIEXPORT jlong JNICALL
Java_sun_nio_fs_LinuxNativeDispatcher_setmntent0(JNIEnv* env, jclass this, jlong pathAddress,
jlong modeAddress)
{
FILE* fp = NULL;
const char* path = (const char*)jlong_to_ptr(pathAddress);
const char* mode = (const char*)jlong_to_ptr(modeAddress);
do {
fp = setmntent(path, mode);
} while (fp == NULL && errno == EINTR);
if (fp == NULL) {
throwUnixException(env, errno);
}
return ptr_to_jlong(fp);
}
JNIEXPORT jint JNICALL
Java_sun_nio_fs_LinuxNativeDispatcher_getmntent0(JNIEnv* env, jclass this,
jlong value, jobject entry, jlong buffer, jint bufLen)
{
struct mntent ent;
char * buf = (char*)jlong_to_ptr(buffer);
struct mntent* m;
FILE* fp = jlong_to_ptr(value);
jsize len;
jbyteArray bytes;
char* name;
char* dir;
char* fstype;
char* options;
m = getmntent_r(fp, &ent, buf, (int)bufLen);
if (m == NULL)
return -1;
name = m->mnt_fsname;
dir = m->mnt_dir;
fstype = m->mnt_type;
options = m->mnt_opts;
len = strlen(name);
bytes = (*env)->NewByteArray(env, len);
if (bytes == NULL)
return -1;
(*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte*)name);
(*env)->SetObjectField(env, entry, entry_name, bytes);
len = strlen(dir);
bytes = (*env)->NewByteArray(env, len);
if (bytes == NULL)
return -1;
(*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte*)dir);
(*env)->SetObjectField(env, entry, entry_dir, bytes);
len = strlen(fstype);
bytes = (*env)->NewByteArray(env, len);
if (bytes == NULL)
return -1;
(*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte*)fstype);
(*env)->SetObjectField(env, entry, entry_fstype, bytes);
len = strlen(options);
bytes = (*env)->NewByteArray(env, len);
if (bytes == NULL)
return -1;
(*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte*)options);
(*env)->SetObjectField(env, entry, entry_options, bytes);
return 0;
}
JNIEXPORT void JNICALL
Java_sun_nio_fs_LinuxNativeDispatcher_endmntent(JNIEnv* env, jclass this, jlong stream)
{
FILE* fp = jlong_to_ptr(stream);
// The endmntent() function always returns 1.
endmntent(fp);
}
JNIEXPORT jint JNICALL
Java_sun_nio_fs_LinuxNativeDispatcher_posix_1fadvise(JNIEnv* env, jclass this,
jint fd, jlong offset, jlong len, jint advice)
{
return posix_fadvise((int)fd, (off_t)offset, (off_t)len, (int)advice);
}
// Copy all bytes from src to dst, within the kernel if possible,
// and return zero, otherwise return the appropriate status code.
//
// Return value
// 0 on success
// IOS_UNAVAILABLE if the platform function would block
// IOS_UNSUPPORTED_CASE if the call does not work with the given parameters
// IOS_UNSUPPORTED if direct copying is not supported on this platform
// IOS_THROWN if a Java exception is thrown
//
JNIEXPORT jint JNICALL
Java_sun_nio_fs_LinuxNativeDispatcher_directCopy0
(JNIEnv* env, jclass this, jint dst, jint src, jlong cancelAddress)
{
volatile jint* cancel = (jint*)jlong_to_ptr(cancelAddress);
// Transfer within the kernel
const size_t count = cancel != NULL ?
1048576 : // 1 MB to give cancellation a chance
0x7ffff000; // maximum number of bytes that sendfile() can transfer
ssize_t bytes_sent;
if (my_copy_file_range_func != NULL) {
do {
RESTARTABLE(my_copy_file_range_func(src, NULL, dst, NULL, count, 0),
bytes_sent);
if (bytes_sent < 0) {
switch (errno) {
case EINVAL:
case ENOSYS:
case EXDEV:
case EOPNOTSUPP:
// ignore and try sendfile()
break;
default:
JNU_ThrowIOExceptionWithLastError(env, "Copy failed");
return IOS_THROWN;
}
}
if (cancel != NULL && *cancel != 0) {
throwUnixException(env, ECANCELED);
return IOS_THROWN;
}
} while (bytes_sent > 0);
if (bytes_sent == 0)
return 0;
}
do {
RESTARTABLE(sendfile(dst, src, NULL, count), bytes_sent);
if (bytes_sent < 0) {
if (errno == EAGAIN)
return IOS_UNAVAILABLE;
if (errno == EINVAL || errno == ENOSYS)
return IOS_UNSUPPORTED_CASE;
throwUnixException(env, errno);
return IOS_THROWN;
}
if (cancel != NULL && *cancel != 0) {
throwUnixException(env, ECANCELED);
return IOS_THROWN;
}
} while (bytes_sent > 0);
return 0;
}

View file

@ -0,0 +1,153 @@
/*
* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "jni.h"
#include "jni_util.h"
#include "jvm.h"
#include "jlong.h"
#include <stdlib.h>
#include <dlfcn.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <poll.h>
#include <sys/inotify.h>
#include "sun_nio_fs_LinuxWatchService.h"
static void throwUnixException(JNIEnv* env, int errnum) {
jobject x = JNU_NewObjectByName(env, "sun/nio/fs/UnixException",
"(I)V", errnum);
if (x != NULL) {
(*env)->Throw(env, x);
}
}
JNIEXPORT jint JNICALL
Java_sun_nio_fs_LinuxWatchService_eventSize(JNIEnv *env, jclass clazz)
{
return (jint)sizeof(struct inotify_event);
}
JNIEXPORT jintArray JNICALL
Java_sun_nio_fs_LinuxWatchService_eventOffsets(JNIEnv *env, jclass clazz)
{
jintArray result = (*env)->NewIntArray(env, 5);
if (result != NULL) {
jint arr[5];
arr[0] = (jint)offsetof(struct inotify_event, wd);
arr[1] = (jint)offsetof(struct inotify_event, mask);
arr[2] = (jint)offsetof(struct inotify_event, cookie);
arr[3] = (jint)offsetof(struct inotify_event, len);
arr[4] = (jint)offsetof(struct inotify_event, name);
(*env)->SetIntArrayRegion(env, result, 0, 5, arr);
}
return result;
}
JNIEXPORT jint JNICALL
Java_sun_nio_fs_LinuxWatchService_inotifyInit
(JNIEnv* env, jclass clazz)
{
int ifd = inotify_init();
if (ifd == -1) {
throwUnixException(env, errno);
}
return (jint)ifd;
}
JNIEXPORT jint JNICALL
Java_sun_nio_fs_LinuxWatchService_inotifyAddWatch
(JNIEnv* env, jclass clazz, jint fd, jlong address, jint mask)
{
int wfd = -1;
const char* path = (const char*)jlong_to_ptr(address);
wfd = inotify_add_watch((int)fd, path, mask);
if (wfd == -1) {
throwUnixException(env, errno);
}
return (jint)wfd;
}
JNIEXPORT void JNICALL
Java_sun_nio_fs_LinuxWatchService_inotifyRmWatch
(JNIEnv* env, jclass clazz, jint fd, jint wd)
{
int err = inotify_rm_watch((int)fd, (int)wd);
if (err == -1)
throwUnixException(env, errno);
}
JNIEXPORT void JNICALL
Java_sun_nio_fs_LinuxWatchService_configureBlocking
(JNIEnv* env, jclass clazz, jint fd, jboolean blocking)
{
int flags = fcntl(fd, F_GETFL);
if ((blocking == JNI_FALSE) && !(flags & O_NONBLOCK))
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
else if ((blocking == JNI_TRUE) && (flags & O_NONBLOCK))
fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
}
JNIEXPORT void JNICALL
Java_sun_nio_fs_LinuxWatchService_socketpair
(JNIEnv* env, jclass clazz, jintArray sv)
{
int sp[2];
if (socketpair(PF_UNIX, SOCK_STREAM, 0, sp) == -1) {
throwUnixException(env, errno);
} else {
jint res[2];
res[0] = (jint)sp[0];
res[1] = (jint)sp[1];
(*env)->SetIntArrayRegion(env, sv, 0, 2, &res[0]);
}
}
JNIEXPORT jint JNICALL
Java_sun_nio_fs_LinuxWatchService_poll
(JNIEnv* env, jclass clazz, jint fd1, jint fd2)
{
struct pollfd ufds[2];
int n;
ufds[0].fd = fd1;
ufds[0].events = POLLIN;
ufds[1].fd = fd2;
ufds[1].events = POLLIN;
n = poll(&ufds[0], 2, -1);
if (n == -1) {
if (errno == EINTR) {
n = 0;
} else {
throwUnixException(env, errno);
}
}
return (jint)n;
}

View file

@ -0,0 +1,367 @@
/*
* Copyright (c) 2021, 2023, Intel Corporation. All rights reserved.
* Copyright (c) 2021 Serge Sans Paille. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
// This implementation is based on x86-simd-sort(https://github.com/intel/x86-simd-sort)
#ifndef AVX2_QSORT_32BIT
#define AVX2_QSORT_32BIT
#include "avx2-emu-funcs.hpp"
#include "xss-common-qsort.h"
/*
* Constants used in sorting 8 elements in a ymm registers. Based on Bitonic
* sorting network (see
* https://en.wikipedia.org/wiki/Bitonic_sorter#/media/File:BitonicSort.svg)
*/
// ymm 7, 6, 5, 4, 3, 2, 1, 0
#define NETWORK_32BIT_AVX2_1 4, 5, 6, 7, 0, 1, 2, 3
#define NETWORK_32BIT_AVX2_2 0, 1, 2, 3, 4, 5, 6, 7
#define NETWORK_32BIT_AVX2_3 5, 4, 7, 6, 1, 0, 3, 2
#define NETWORK_32BIT_AVX2_4 3, 2, 1, 0, 7, 6, 5, 4
/*
* Assumes ymm is random and performs a full sorting network defined in
* https://en.wikipedia.org/wiki/Bitonic_sorter#/media/File:BitonicSort.svg
*/
template <typename vtype, typename reg_t = typename vtype::reg_t>
X86_SIMD_SORT_INLINE reg_t sort_ymm_32bit(reg_t ymm) {
const typename vtype::opmask_t oxAA = _mm256_set_epi32(
0xFFFFFFFF, 0, 0xFFFFFFFF, 0, 0xFFFFFFFF, 0, 0xFFFFFFFF, 0);
const typename vtype::opmask_t oxCC = _mm256_set_epi32(
0xFFFFFFFF, 0xFFFFFFFF, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0, 0);
const typename vtype::opmask_t oxF0 = _mm256_set_epi32(
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0, 0, 0, 0);
const typename vtype::ymmi_t rev_index = vtype::seti(NETWORK_32BIT_AVX2_2);
ymm = cmp_merge<vtype>(
ymm, vtype::template shuffle<SHUFFLE_MASK(2, 3, 0, 1)>(ymm), oxAA);
ymm = cmp_merge<vtype>(
ymm, vtype::permutexvar(vtype::seti(NETWORK_32BIT_AVX2_1), ymm), oxCC);
ymm = cmp_merge<vtype>(
ymm, vtype::template shuffle<SHUFFLE_MASK(2, 3, 0, 1)>(ymm), oxAA);
ymm = cmp_merge<vtype>(ymm, vtype::permutexvar(rev_index, ymm), oxF0);
ymm = cmp_merge<vtype>(
ymm, vtype::permutexvar(vtype::seti(NETWORK_32BIT_AVX2_3), ymm), oxCC);
ymm = cmp_merge<vtype>(
ymm, vtype::template shuffle<SHUFFLE_MASK(2, 3, 0, 1)>(ymm), oxAA);
return ymm;
}
struct avx2_32bit_swizzle_ops;
template <>
struct avx2_vector<int32_t> {
using type_t = int32_t;
using reg_t = __m256i;
using ymmi_t = __m256i;
using opmask_t = __m256i;
static const uint8_t numlanes = 8;
#ifdef XSS_MINIMAL_NETWORK_SORT
static constexpr int network_sort_threshold = numlanes;
#else
static constexpr int network_sort_threshold = 256;
#endif
static constexpr int partition_unroll_factor = 4;
using swizzle_ops = avx2_32bit_swizzle_ops;
static type_t type_max() { return X86_SIMD_SORT_MAX_INT32; }
static type_t type_min() { return X86_SIMD_SORT_MIN_INT32; }
static reg_t zmm_max() {
return _mm256_set1_epi32(type_max());
} // TODO: this should broadcast bits as is?
static opmask_t get_partial_loadmask(uint64_t num_to_read) {
auto mask = ((0x1ull << num_to_read) - 0x1ull);
return convert_int_to_avx2_mask(mask);
}
static ymmi_t seti(int v1, int v2, int v3, int v4, int v5, int v6, int v7,
int v8) {
return _mm256_set_epi32(v1, v2, v3, v4, v5, v6, v7, v8);
}
static opmask_t kxor_opmask(opmask_t x, opmask_t y) {
return _mm256_xor_si256(x, y);
}
static opmask_t ge(reg_t x, reg_t y) {
opmask_t equal = eq(x, y);
opmask_t greater = _mm256_cmpgt_epi32(x, y);
return _mm256_castps_si256(_mm256_or_ps(_mm256_castsi256_ps(equal),
_mm256_castsi256_ps(greater)));
}
static opmask_t gt(reg_t x, reg_t y) { return _mm256_cmpgt_epi32(x, y); }
static opmask_t eq(reg_t x, reg_t y) { return _mm256_cmpeq_epi32(x, y); }
template <int scale>
static reg_t mask_i64gather(reg_t src, opmask_t mask, __m256i index,
void const *base) {
return _mm256_mask_i32gather_epi32(src, base, index, mask, scale);
}
template <int scale>
static reg_t i64gather(__m256i index, void const *base) {
return _mm256_i32gather_epi32((int const *)base, index, scale);
}
static reg_t loadu(void const *mem) {
return _mm256_loadu_si256((reg_t const *)mem);
}
static reg_t max(reg_t x, reg_t y) { return _mm256_max_epi32(x, y); }
static void mask_compressstoreu(void *mem, opmask_t mask, reg_t x) {
return avx2_emu_mask_compressstoreu32<type_t>(mem, mask, x);
}
static reg_t maskz_loadu(opmask_t mask, void const *mem) {
return _mm256_maskload_epi32((const int *)mem, mask);
}
static reg_t mask_loadu(reg_t x, opmask_t mask, void const *mem) {
reg_t dst = _mm256_maskload_epi32((type_t *)mem, mask);
return mask_mov(x, mask, dst);
}
static reg_t mask_mov(reg_t x, opmask_t mask, reg_t y) {
return _mm256_castps_si256(_mm256_blendv_ps(_mm256_castsi256_ps(x),
_mm256_castsi256_ps(y),
_mm256_castsi256_ps(mask)));
}
static void mask_storeu(void *mem, opmask_t mask, reg_t x) {
return _mm256_maskstore_epi32((type_t *)mem, mask, x);
}
static reg_t min(reg_t x, reg_t y) { return _mm256_min_epi32(x, y); }
static reg_t permutexvar(__m256i idx, reg_t ymm) {
return _mm256_permutevar8x32_epi32(ymm, idx);
// return avx2_emu_permutexvar_epi32(idx, ymm);
}
static reg_t permutevar(reg_t ymm, __m256i idx) {
return _mm256_permutevar8x32_epi32(ymm, idx);
}
static reg_t reverse(reg_t ymm) {
const __m256i rev_index = _mm256_set_epi32(NETWORK_32BIT_AVX2_2);
return permutexvar(rev_index, ymm);
}
static type_t reducemax(reg_t v) {
return avx2_emu_reduce_max32<type_t>(v);
}
static type_t reducemin(reg_t v) {
return avx2_emu_reduce_min32<type_t>(v);
}
static reg_t set1(type_t v) { return _mm256_set1_epi32(v); }
template <uint8_t mask>
static reg_t shuffle(reg_t ymm) {
return _mm256_shuffle_epi32(ymm, mask);
}
static void storeu(void *mem, reg_t x) {
_mm256_storeu_si256((__m256i *)mem, x);
}
static reg_t sort_vec(reg_t x) {
return sort_ymm_32bit<avx2_vector<type_t>>(x);
}
static reg_t cast_from(__m256i v) { return v; }
static __m256i cast_to(reg_t v) { return v; }
static int double_compressstore(type_t *left_addr, type_t *right_addr,
opmask_t k, reg_t reg) {
return avx2_double_compressstore32<type_t>(left_addr, right_addr, k,
reg);
}
};
template <>
struct avx2_vector<float> {
using type_t = float;
using reg_t = __m256;
using ymmi_t = __m256i;
using opmask_t = __m256i;
static const uint8_t numlanes = 8;
#ifdef XSS_MINIMAL_NETWORK_SORT
static constexpr int network_sort_threshold = numlanes;
#else
static constexpr int network_sort_threshold = 256;
#endif
static constexpr int partition_unroll_factor = 4;
using swizzle_ops = avx2_32bit_swizzle_ops;
static type_t type_max() { return X86_SIMD_SORT_INFINITYF; }
static type_t type_min() { return -X86_SIMD_SORT_INFINITYF; }
static reg_t zmm_max() { return _mm256_set1_ps(type_max()); }
static ymmi_t seti(int v1, int v2, int v3, int v4, int v5, int v6, int v7,
int v8) {
return _mm256_set_epi32(v1, v2, v3, v4, v5, v6, v7, v8);
}
static reg_t maskz_loadu(opmask_t mask, void const *mem) {
return _mm256_maskload_ps((const float *)mem, mask);
}
static opmask_t ge(reg_t x, reg_t y) {
return _mm256_castps_si256(_mm256_cmp_ps(x, y, _CMP_GE_OQ));
}
static opmask_t gt(reg_t x, reg_t y) {
return _mm256_castps_si256(_mm256_cmp_ps(x, y, _CMP_GT_OQ));
}
static opmask_t eq(reg_t x, reg_t y) {
return _mm256_castps_si256(_mm256_cmp_ps(x, y, _CMP_EQ_OQ));
}
static opmask_t get_partial_loadmask(uint64_t num_to_read) {
auto mask = ((0x1ull << num_to_read) - 0x1ull);
return convert_int_to_avx2_mask(mask);
}
static int32_t convert_mask_to_int(opmask_t mask) {
return convert_avx2_mask_to_int(mask);
}
template <int type>
static opmask_t fpclass(reg_t x) {
if constexpr (type == (0x01 | 0x80)) {
return _mm256_castps_si256(_mm256_cmp_ps(x, x, _CMP_UNORD_Q));
} else {
static_assert(type == (0x01 | 0x80), "should not reach here");
}
}
template <int scale>
static reg_t mask_i64gather(reg_t src, opmask_t mask, __m256i index,
void const *base) {
return _mm256_mask_i32gather_ps(src, base, index,
_mm256_castsi256_ps(mask), scale);
;
}
template <int scale>
static reg_t i64gather(__m256i index, void const *base) {
return _mm256_i32gather_ps((float *)base, index, scale);
}
static reg_t loadu(void const *mem) {
return _mm256_loadu_ps((float const *)mem);
}
static reg_t max(reg_t x, reg_t y) { return _mm256_max_ps(x, y); }
static void mask_compressstoreu(void *mem, opmask_t mask, reg_t x) {
return avx2_emu_mask_compressstoreu32<type_t>(mem, mask, x);
}
static reg_t mask_loadu(reg_t x, opmask_t mask, void const *mem) {
reg_t dst = _mm256_maskload_ps((type_t *)mem, mask);
return mask_mov(x, mask, dst);
}
static reg_t mask_mov(reg_t x, opmask_t mask, reg_t y) {
return _mm256_blendv_ps(x, y, _mm256_castsi256_ps(mask));
}
static void mask_storeu(void *mem, opmask_t mask, reg_t x) {
return _mm256_maskstore_ps((type_t *)mem, mask, x);
}
static reg_t min(reg_t x, reg_t y) { return _mm256_min_ps(x, y); }
static reg_t permutexvar(__m256i idx, reg_t ymm) {
return _mm256_permutevar8x32_ps(ymm, idx);
}
static reg_t permutevar(reg_t ymm, __m256i idx) {
return _mm256_permutevar8x32_ps(ymm, idx);
}
static reg_t reverse(reg_t ymm) {
const __m256i rev_index = _mm256_set_epi32(NETWORK_32BIT_AVX2_2);
return permutexvar(rev_index, ymm);
}
static type_t reducemax(reg_t v) {
return avx2_emu_reduce_max32<type_t>(v);
}
static type_t reducemin(reg_t v) {
return avx2_emu_reduce_min32<type_t>(v);
}
static reg_t set1(type_t v) { return _mm256_set1_ps(v); }
template <uint8_t mask>
static reg_t shuffle(reg_t ymm) {
return _mm256_castsi256_ps(
_mm256_shuffle_epi32(_mm256_castps_si256(ymm), mask));
}
static void storeu(void *mem, reg_t x) {
_mm256_storeu_ps((float *)mem, x);
}
static reg_t sort_vec(reg_t x) {
return sort_ymm_32bit<avx2_vector<type_t>>(x);
}
static reg_t cast_from(__m256i v) { return _mm256_castsi256_ps(v); }
static __m256i cast_to(reg_t v) { return _mm256_castps_si256(v); }
static int double_compressstore(type_t *left_addr, type_t *right_addr,
opmask_t k, reg_t reg) {
return avx2_double_compressstore32<type_t>(left_addr, right_addr, k,
reg);
}
};
struct avx2_32bit_swizzle_ops {
template <typename vtype, int scale>
X86_SIMD_SORT_INLINE typename vtype::reg_t swap_n(
typename vtype::reg_t reg) {
__m256i v = vtype::cast_to(reg);
if constexpr (scale == 2) {
__m256 vf = _mm256_castsi256_ps(v);
vf = _mm256_permute_ps(vf, 0b10110001);
v = _mm256_castps_si256(vf);
} else if constexpr (scale == 4) {
__m256 vf = _mm256_castsi256_ps(v);
vf = _mm256_permute_ps(vf, 0b01001110);
v = _mm256_castps_si256(vf);
} else if constexpr (scale == 8) {
v = _mm256_permute2x128_si256(v, v, 0b00000001);
} else {
static_assert(scale == -1, "should not be reached");
}
return vtype::cast_from(v);
}
template <typename vtype, int scale>
X86_SIMD_SORT_INLINE typename vtype::reg_t reverse_n(
typename vtype::reg_t reg) {
__m256i v = vtype::cast_to(reg);
if constexpr (scale == 2) {
return swap_n<vtype, 2>(reg);
} else if constexpr (scale == 4) {
constexpr uint64_t mask = 0b00011011;
__m256 vf = _mm256_castsi256_ps(v);
vf = _mm256_permute_ps(vf, mask);
v = _mm256_castps_si256(vf);
} else if constexpr (scale == 8) {
return vtype::reverse(reg);
} else {
static_assert(scale == -1, "should not be reached");
}
return vtype::cast_from(v);
}
template <typename vtype, int scale>
X86_SIMD_SORT_INLINE typename vtype::reg_t merge_n(
typename vtype::reg_t reg, typename vtype::reg_t other) {
__m256i v1 = vtype::cast_to(reg);
__m256i v2 = vtype::cast_to(other);
if constexpr (scale == 2) {
v1 = _mm256_blend_epi32(v1, v2, 0b01010101);
} else if constexpr (scale == 4) {
v1 = _mm256_blend_epi32(v1, v2, 0b00110011);
} else if constexpr (scale == 8) {
v1 = _mm256_blend_epi32(v1, v2, 0b00001111);
} else {
static_assert(scale == -1, "should not be reached");
}
return vtype::cast_from(v1);
}
};
#endif // AVX2_QSORT_32BIT

View file

@ -0,0 +1,183 @@
/*
* Copyright (c) 2021, 2023, Intel Corporation. All rights reserved.
* Copyright (c) 2021 Serge Sans Paille. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
// This implementation is based on x86-simd-sort(https://github.com/intel/x86-simd-sort)
#ifndef AVX2_EMU_FUNCS
#define AVX2_EMU_FUNCS
#include <array>
#include <utility>
#include "xss-common-qsort.h"
constexpr auto avx2_mask_helper_lut32 = [] {
std::array<std::array<int32_t, 8>, 256> lut{};
for (int64_t i = 0; i <= 0xFF; i++) {
std::array<int32_t, 8> entry{};
for (int j = 0; j < 8; j++) {
if (((i >> j) & 1) == 1)
entry[j] = 0xFFFFFFFF;
else
entry[j] = 0;
}
lut[i] = entry;
}
return lut;
}();
constexpr auto avx2_compressstore_lut32_gen = [] {
std::array<std::array<std::array<int32_t, 8>, 256>, 2> lutPair{};
auto &permLut = lutPair[0];
auto &leftLut = lutPair[1];
for (int64_t i = 0; i <= 0xFF; i++) {
std::array<int32_t, 8> indices{};
std::array<int32_t, 8> leftEntry = {0, 0, 0, 0, 0, 0, 0, 0};
int right = 7;
int left = 0;
for (int j = 0; j < 8; j++) {
bool ge = (i >> j) & 1;
if (ge) {
indices[right] = j;
right--;
} else {
indices[left] = j;
leftEntry[left] = 0xFFFFFFFF;
left++;
}
}
permLut[i] = indices;
leftLut[i] = leftEntry;
}
return lutPair;
}();
constexpr auto avx2_compressstore_lut32_perm = avx2_compressstore_lut32_gen[0];
constexpr auto avx2_compressstore_lut32_left = avx2_compressstore_lut32_gen[1];
X86_SIMD_SORT_INLINE
__m256i convert_int_to_avx2_mask(int32_t m) {
return _mm256_loadu_si256(
(const __m256i *)avx2_mask_helper_lut32[m].data());
}
X86_SIMD_SORT_INLINE
int32_t convert_avx2_mask_to_int(__m256i m) {
return _mm256_movemask_ps(_mm256_castsi256_ps(m));
}
// Emulators for intrinsics missing from AVX2 compared to AVX512
template <typename T>
T avx2_emu_reduce_max32(typename avx2_vector<T>::reg_t x) {
using vtype = avx2_vector<T>;
using reg_t = typename vtype::reg_t;
reg_t inter1 =
vtype::max(x, vtype::template shuffle<SHUFFLE_MASK(2, 3, 0, 1)>(x));
reg_t inter2 = vtype::max(
inter1, vtype::template shuffle<SHUFFLE_MASK(1, 0, 3, 2)>(inter1));
T arr[vtype::numlanes];
vtype::storeu(arr, inter2);
return std::max(arr[0], arr[7]);
}
template <typename T>
T avx2_emu_reduce_min32(typename avx2_vector<T>::reg_t x) {
using vtype = avx2_vector<T>;
using reg_t = typename vtype::reg_t;
reg_t inter1 =
vtype::min(x, vtype::template shuffle<SHUFFLE_MASK(2, 3, 0, 1)>(x));
reg_t inter2 = vtype::min(
inter1, vtype::template shuffle<SHUFFLE_MASK(1, 0, 3, 2)>(inter1));
T arr[vtype::numlanes];
vtype::storeu(arr, inter2);
return std::min(arr[0], arr[7]);
}
template <typename T>
void avx2_emu_mask_compressstoreu32(void *base_addr,
typename avx2_vector<T>::opmask_t k,
typename avx2_vector<T>::reg_t reg) {
using vtype = avx2_vector<T>;
T *leftStore = (T *)base_addr;
int32_t shortMask = convert_avx2_mask_to_int(k);
const __m256i perm = _mm256_loadu_si256(
(const __m256i *)avx2_compressstore_lut32_perm[shortMask].data());
const __m256i left = _mm256_loadu_si256(
(const __m256i *)avx2_compressstore_lut32_left[shortMask].data());
typename vtype::reg_t temp = vtype::permutevar(reg, perm);
vtype::mask_storeu(leftStore, left, temp);
}
template <typename T>
int avx2_double_compressstore32(void *left_addr, void *right_addr,
typename avx2_vector<T>::opmask_t k,
typename avx2_vector<T>::reg_t reg) {
using vtype = avx2_vector<T>;
T *leftStore = (T *)left_addr;
T *rightStore = (T *)right_addr;
int32_t shortMask = convert_avx2_mask_to_int(k);
const __m256i perm = _mm256_loadu_si256(
(const __m256i *)avx2_compressstore_lut32_perm[shortMask].data());
typename vtype::reg_t temp = vtype::permutevar(reg, perm);
vtype::storeu(leftStore, temp);
vtype::storeu(rightStore, temp);
return _mm_popcnt_u32(shortMask);
}
template <typename T>
typename avx2_vector<T>::reg_t avx2_emu_max(typename avx2_vector<T>::reg_t x,
typename avx2_vector<T>::reg_t y) {
using vtype = avx2_vector<T>;
typename vtype::opmask_t nlt = vtype::gt(x, y);
return _mm256_castpd_si256(_mm256_blendv_pd(_mm256_castsi256_pd(y),
_mm256_castsi256_pd(x),
_mm256_castsi256_pd(nlt)));
}
template <typename T>
typename avx2_vector<T>::reg_t avx2_emu_min(typename avx2_vector<T>::reg_t x,
typename avx2_vector<T>::reg_t y) {
using vtype = avx2_vector<T>;
typename vtype::opmask_t nlt = vtype::gt(x, y);
return _mm256_castpd_si256(_mm256_blendv_pd(_mm256_castsi256_pd(x),
_mm256_castsi256_pd(y),
_mm256_castsi256_pd(nlt)));
}
#endif

View file

@ -0,0 +1,66 @@
/*
* Copyright (c) 2023 Intel Corporation. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#include "simdsort-support.hpp"
#ifdef __SIMDSORT_SUPPORTED_LINUX
#pragma GCC target("avx2")
#include "avx2-32bit-qsort.hpp"
#include "classfile_constants.h"
#define DLL_PUBLIC __attribute__((visibility("default")))
#define INSERTION_SORT_THRESHOLD_32BIT 16
extern "C" {
DLL_PUBLIC void avx2_sort(void *array, int elem_type, int32_t from_index, int32_t to_index) {
switch(elem_type) {
case JVM_T_INT:
avx2_fast_sort((int32_t*)array, from_index, to_index, INSERTION_SORT_THRESHOLD_32BIT);
break;
case JVM_T_FLOAT:
avx2_fast_sort((float*)array, from_index, to_index, INSERTION_SORT_THRESHOLD_32BIT);
break;
default:
assert(false, "Unexpected type");
}
}
DLL_PUBLIC void avx2_partition(void *array, int elem_type, int32_t from_index, int32_t to_index, int32_t *pivot_indices, int32_t index_pivot1, int32_t index_pivot2) {
switch(elem_type) {
case JVM_T_INT:
avx2_fast_partition((int32_t*)array, from_index, to_index, pivot_indices, index_pivot1, index_pivot2);
break;
case JVM_T_FLOAT:
avx2_fast_partition((float*)array, from_index, to_index, pivot_indices, index_pivot1, index_pivot2);
break;
default:
assert(false, "Unexpected type");
}
}
}
#endif

View file

@ -0,0 +1,337 @@
/*
* Copyright (c) 2021, 2023, Intel Corporation. All rights reserved.
* Copyright (c) 2021 Serge Sans Paille. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
// This implementation is based on x86-simd-sort(https://github.com/intel/x86-simd-sort)
#ifndef AVX512_QSORT_32BIT
#define AVX512_QSORT_32BIT
#include "xss-common-qsort.h"
/*
* Constants used in sorting 16 elements in a ZMM registers. Based on Bitonic
* sorting network (see
* https://en.wikipedia.org/wiki/Bitonic_sorter#/media/File:BitonicSort.svg)
*/
#define NETWORK_32BIT_1 14, 15, 12, 13, 10, 11, 8, 9, 6, 7, 4, 5, 2, 3, 0, 1
#define NETWORK_32BIT_2 12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3
#define NETWORK_32BIT_3 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7
#define NETWORK_32BIT_4 13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6, 1, 0, 3, 2
#define NETWORK_32BIT_5 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
#define NETWORK_32BIT_6 11, 10, 9, 8, 15, 14, 13, 12, 3, 2, 1, 0, 7, 6, 5, 4
#define NETWORK_32BIT_7 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8
template <typename vtype, typename reg_t>
X86_SIMD_SORT_INLINE reg_t sort_zmm_32bit(reg_t zmm);
struct avx512_32bit_swizzle_ops;
template <>
struct zmm_vector<int32_t> {
using type_t = int32_t;
using reg_t = __m512i;
using halfreg_t = __m256i;
using opmask_t = __mmask16;
static const uint8_t numlanes = 16;
#ifdef XSS_MINIMAL_NETWORK_SORT
static constexpr int network_sort_threshold = numlanes;
#else
static constexpr int network_sort_threshold = 512;
#endif
static constexpr int partition_unroll_factor = 8;
using swizzle_ops = avx512_32bit_swizzle_ops;
static type_t type_max() { return X86_SIMD_SORT_MAX_INT32; }
static type_t type_min() { return X86_SIMD_SORT_MIN_INT32; }
static reg_t zmm_max() { return _mm512_set1_epi32(type_max()); }
static opmask_t knot_opmask(opmask_t x) { return _mm512_knot(x); }
static opmask_t ge(reg_t x, reg_t y) {
return _mm512_cmp_epi32_mask(x, y, _MM_CMPINT_NLT);
}
static opmask_t gt(reg_t x, reg_t y) {
return _mm512_cmp_epi32_mask(x, y, _MM_CMPINT_GT);
}
static opmask_t get_partial_loadmask(uint64_t num_to_read) {
return ((0x1ull << num_to_read) - 0x1ull);
}
template <int scale>
static halfreg_t i64gather(__m512i index, void const *base) {
return _mm512_i64gather_epi32(index, base, scale);
}
static reg_t merge(halfreg_t y1, halfreg_t y2) {
reg_t z1 = _mm512_castsi256_si512(y1);
return _mm512_inserti32x8(z1, y2, 1);
}
static reg_t loadu(void const *mem) { return _mm512_loadu_si512(mem); }
static void mask_compressstoreu(void *mem, opmask_t mask, reg_t x) {
return _mm512_mask_compressstoreu_epi32(mem, mask, x);
}
static reg_t mask_loadu(reg_t x, opmask_t mask, void const *mem) {
return _mm512_mask_loadu_epi32(x, mask, mem);
}
static reg_t mask_mov(reg_t x, opmask_t mask, reg_t y) {
return _mm512_mask_mov_epi32(x, mask, y);
}
static void mask_storeu(void *mem, opmask_t mask, reg_t x) {
return _mm512_mask_storeu_epi32(mem, mask, x);
}
static reg_t min(reg_t x, reg_t y) { return _mm512_min_epi32(x, y); }
static reg_t max(reg_t x, reg_t y) { return _mm512_max_epi32(x, y); }
static reg_t permutexvar(__m512i idx, reg_t zmm) {
return _mm512_permutexvar_epi32(idx, zmm);
}
static type_t reducemax(reg_t v) { return _mm512_reduce_max_epi32(v); }
static type_t reducemin(reg_t v) { return _mm512_reduce_min_epi32(v); }
static reg_t set1(type_t v) { return _mm512_set1_epi32(v); }
template <uint8_t mask>
static reg_t shuffle(reg_t zmm) {
return _mm512_shuffle_epi32(zmm, (_MM_PERM_ENUM)mask);
}
static void storeu(void *mem, reg_t x) {
return _mm512_storeu_si512(mem, x);
}
static halfreg_t max(halfreg_t x, halfreg_t y) {
return _mm256_max_epi32(x, y);
}
static halfreg_t min(halfreg_t x, halfreg_t y) {
return _mm256_min_epi32(x, y);
}
static reg_t reverse(reg_t zmm) {
const auto rev_index = _mm512_set_epi32(NETWORK_32BIT_5);
return permutexvar(rev_index, zmm);
}
static reg_t sort_vec(reg_t x) {
return sort_zmm_32bit<zmm_vector<type_t>>(x);
}
static reg_t cast_from(__m512i v) { return v; }
static __m512i cast_to(reg_t v) { return v; }
static int double_compressstore(type_t *left_addr, type_t *right_addr,
opmask_t k, reg_t reg) {
return avx512_double_compressstore<zmm_vector<type_t>>(
left_addr, right_addr, k, reg);
}
};
template <>
struct zmm_vector<float> {
using type_t = float;
using reg_t = __m512;
using halfreg_t = __m256;
using opmask_t = __mmask16;
static const uint8_t numlanes = 16;
#ifdef XSS_MINIMAL_NETWORK_SORT
static constexpr int network_sort_threshold = numlanes;
#else
static constexpr int network_sort_threshold = 512;
#endif
static constexpr int partition_unroll_factor = 8;
using swizzle_ops = avx512_32bit_swizzle_ops;
static type_t type_max() { return X86_SIMD_SORT_INFINITYF; }
static type_t type_min() { return -X86_SIMD_SORT_INFINITYF; }
static reg_t zmm_max() { return _mm512_set1_ps(type_max()); }
static opmask_t knot_opmask(opmask_t x) { return _mm512_knot(x); }
static opmask_t ge(reg_t x, reg_t y) {
return _mm512_cmp_ps_mask(x, y, _CMP_GE_OQ);
}
static opmask_t gt(reg_t x, reg_t y) {
return _mm512_cmp_ps_mask(x, y, _CMP_GT_OQ);
}
static opmask_t get_partial_loadmask(uint64_t num_to_read) {
return ((0x1ull << num_to_read) - 0x1ull);
}
static int32_t convert_mask_to_int(opmask_t mask) { return mask; }
template <int type>
static opmask_t fpclass(reg_t x) {
return _mm512_fpclass_ps_mask(x, type);
}
template <int scale>
static halfreg_t i64gather(__m512i index, void const *base) {
return _mm512_i64gather_ps(index, base, scale);
}
static reg_t merge(halfreg_t y1, halfreg_t y2) {
reg_t z1 = _mm512_castsi512_ps(
_mm512_castsi256_si512(_mm256_castps_si256(y1)));
return _mm512_insertf32x8(z1, y2, 1);
}
static reg_t loadu(void const *mem) { return _mm512_loadu_ps(mem); }
static reg_t max(reg_t x, reg_t y) { return _mm512_max_ps(x, y); }
static void mask_compressstoreu(void *mem, opmask_t mask, reg_t x) {
return _mm512_mask_compressstoreu_ps(mem, mask, x);
}
static reg_t maskz_loadu(opmask_t mask, void const *mem) {
return _mm512_maskz_loadu_ps(mask, mem);
}
static reg_t mask_loadu(reg_t x, opmask_t mask, void const *mem) {
return _mm512_mask_loadu_ps(x, mask, mem);
}
static reg_t mask_mov(reg_t x, opmask_t mask, reg_t y) {
return _mm512_mask_mov_ps(x, mask, y);
}
static void mask_storeu(void *mem, opmask_t mask, reg_t x) {
return _mm512_mask_storeu_ps(mem, mask, x);
}
static reg_t min(reg_t x, reg_t y) { return _mm512_min_ps(x, y); }
static reg_t permutexvar(__m512i idx, reg_t zmm) {
return _mm512_permutexvar_ps(idx, zmm);
}
static type_t reducemax(reg_t v) { return _mm512_reduce_max_ps(v); }
static type_t reducemin(reg_t v) { return _mm512_reduce_min_ps(v); }
static reg_t set1(type_t v) { return _mm512_set1_ps(v); }
template <uint8_t mask>
static reg_t shuffle(reg_t zmm) {
return _mm512_shuffle_ps(zmm, zmm, (_MM_PERM_ENUM)mask);
}
static void storeu(void *mem, reg_t x) { return _mm512_storeu_ps(mem, x); }
static halfreg_t max(halfreg_t x, halfreg_t y) {
return _mm256_max_ps(x, y);
}
static halfreg_t min(halfreg_t x, halfreg_t y) {
return _mm256_min_ps(x, y);
}
static reg_t reverse(reg_t zmm) {
const auto rev_index = _mm512_set_epi32(NETWORK_32BIT_5);
return permutexvar(rev_index, zmm);
}
static reg_t sort_vec(reg_t x) {
return sort_zmm_32bit<zmm_vector<type_t>>(x);
}
static reg_t cast_from(__m512i v) { return _mm512_castsi512_ps(v); }
static __m512i cast_to(reg_t v) { return _mm512_castps_si512(v); }
static int double_compressstore(type_t *left_addr, type_t *right_addr,
opmask_t k, reg_t reg) {
return avx512_double_compressstore<zmm_vector<type_t>>(
left_addr, right_addr, k, reg);
}
};
/*
* Assumes zmm is random and performs a full sorting network defined in
* https://en.wikipedia.org/wiki/Bitonic_sorter#/media/File:BitonicSort.svg
*/
template <typename vtype, typename reg_t = typename vtype::reg_t>
X86_SIMD_SORT_INLINE reg_t sort_zmm_32bit(reg_t zmm) {
zmm = cmp_merge<vtype>(
zmm, vtype::template shuffle<SHUFFLE_MASK(2, 3, 0, 1)>(zmm), 0xAAAA);
zmm = cmp_merge<vtype>(
zmm, vtype::template shuffle<SHUFFLE_MASK(0, 1, 2, 3)>(zmm), 0xCCCC);
zmm = cmp_merge<vtype>(
zmm, vtype::template shuffle<SHUFFLE_MASK(2, 3, 0, 1)>(zmm), 0xAAAA);
zmm = cmp_merge<vtype>(
zmm, vtype::permutexvar(_mm512_set_epi32(NETWORK_32BIT_3), zmm),
0xF0F0);
zmm = cmp_merge<vtype>(
zmm, vtype::template shuffle<SHUFFLE_MASK(1, 0, 3, 2)>(zmm), 0xCCCC);
zmm = cmp_merge<vtype>(
zmm, vtype::template shuffle<SHUFFLE_MASK(2, 3, 0, 1)>(zmm), 0xAAAA);
zmm = cmp_merge<vtype>(
zmm, vtype::permutexvar(_mm512_set_epi32(NETWORK_32BIT_5), zmm),
0xFF00);
zmm = cmp_merge<vtype>(
zmm, vtype::permutexvar(_mm512_set_epi32(NETWORK_32BIT_6), zmm),
0xF0F0);
zmm = cmp_merge<vtype>(
zmm, vtype::template shuffle<SHUFFLE_MASK(1, 0, 3, 2)>(zmm), 0xCCCC);
zmm = cmp_merge<vtype>(
zmm, vtype::template shuffle<SHUFFLE_MASK(2, 3, 0, 1)>(zmm), 0xAAAA);
return zmm;
}
struct avx512_32bit_swizzle_ops {
template <typename vtype, int scale>
X86_SIMD_SORT_INLINE typename vtype::reg_t swap_n(
typename vtype::reg_t reg) {
__m512i v = vtype::cast_to(reg);
if constexpr (scale == 2) {
v = _mm512_shuffle_epi32(v, (_MM_PERM_ENUM)0b10110001);
} else if constexpr (scale == 4) {
v = _mm512_shuffle_epi32(v, (_MM_PERM_ENUM)0b01001110);
} else if constexpr (scale == 8) {
v = _mm512_shuffle_i64x2(v, v, 0b10110001);
} else if constexpr (scale == 16) {
v = _mm512_shuffle_i64x2(v, v, 0b01001110);
} else {
static_assert(scale == -1, "should not be reached");
}
return vtype::cast_from(v);
}
template <typename vtype, int scale>
X86_SIMD_SORT_INLINE typename vtype::reg_t reverse_n(
typename vtype::reg_t reg) {
__m512i v = vtype::cast_to(reg);
if constexpr (scale == 2) {
return swap_n<vtype, 2>(reg);
} else if constexpr (scale == 4) {
__m512i mask = _mm512_set_epi32(12, 13, 14, 15, 8, 9, 10, 11, 4, 5,
6, 7, 0, 1, 2, 3);
v = _mm512_permutexvar_epi32(mask, v);
} else if constexpr (scale == 8) {
__m512i mask = _mm512_set_epi32(8, 9, 10, 11, 12, 13, 14, 15, 0, 1,
2, 3, 4, 5, 6, 7);
v = _mm512_permutexvar_epi32(mask, v);
} else if constexpr (scale == 16) {
return vtype::reverse(reg);
} else {
static_assert(scale == -1, "should not be reached");
}
return vtype::cast_from(v);
}
template <typename vtype, int scale>
X86_SIMD_SORT_INLINE typename vtype::reg_t merge_n(
typename vtype::reg_t reg, typename vtype::reg_t other) {
__m512i v1 = vtype::cast_to(reg);
__m512i v2 = vtype::cast_to(other);
if constexpr (scale == 2) {
v1 = _mm512_mask_blend_epi32(0b0101010101010101, v1, v2);
} else if constexpr (scale == 4) {
v1 = _mm512_mask_blend_epi32(0b0011001100110011, v1, v2);
} else if constexpr (scale == 8) {
v1 = _mm512_mask_blend_epi32(0b0000111100001111, v1, v2);
} else if constexpr (scale == 16) {
v1 = _mm512_mask_blend_epi32(0b0000000011111111, v1, v2);
} else {
static_assert(scale == -1, "should not be reached");
}
return vtype::cast_from(v1);
}
};
#endif // AVX512_QSORT_32BIT

View file

@ -0,0 +1,343 @@
/*
* Copyright (c) 2021, 2023, Intel Corporation. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
// This implementation is based on x86-simd-sort(https://github.com/intel/x86-simd-sort)
#ifndef AVX512_QSORT_64BIT
#define AVX512_QSORT_64BIT
#include "xss-common-includes.h"
#include "xss-common-qsort.h"
/*
* Constants used in sorting 8 elements in a ZMM registers. Based on Bitonic
* sorting network (see
* https://en.wikipedia.org/wiki/Bitonic_sorter#/media/File:BitonicSort.svg)
*/
// ZMM 7, 6, 5, 4, 3, 2, 1, 0
#define NETWORK_64BIT_1 4, 5, 6, 7, 0, 1, 2, 3
#define NETWORK_64BIT_2 0, 1, 2, 3, 4, 5, 6, 7
#define NETWORK_64BIT_3 5, 4, 7, 6, 1, 0, 3, 2
#define NETWORK_64BIT_4 3, 2, 1, 0, 7, 6, 5, 4
template <typename vtype, typename reg_t>
X86_SIMD_SORT_INLINE reg_t sort_zmm_64bit(reg_t zmm);
struct avx512_64bit_swizzle_ops;
template <>
struct zmm_vector<int64_t> {
using type_t = int64_t;
using reg_t = __m512i;
using regi_t = __m512i;
using halfreg_t = __m512i;
using opmask_t = __mmask8;
static const uint8_t numlanes = 8;
#ifdef XSS_MINIMAL_NETWORK_SORT
static constexpr int network_sort_threshold = numlanes;
#else
static constexpr int network_sort_threshold = 256;
#endif
static constexpr int partition_unroll_factor = 8;
using swizzle_ops = avx512_64bit_swizzle_ops;
static type_t type_max() { return X86_SIMD_SORT_MAX_INT64; }
static type_t type_min() { return X86_SIMD_SORT_MIN_INT64; }
static reg_t zmm_max() {
return _mm512_set1_epi64(type_max());
} // TODO: this should broadcast bits as is?
static regi_t seti(int v1, int v2, int v3, int v4, int v5, int v6, int v7,
int v8) {
return _mm512_set_epi64(v1, v2, v3, v4, v5, v6, v7, v8);
}
static reg_t set(type_t v1, type_t v2, type_t v3, type_t v4, type_t v5,
type_t v6, type_t v7, type_t v8) {
return _mm512_set_epi64(v1, v2, v3, v4, v5, v6, v7, v8);
}
static opmask_t kxor_opmask(opmask_t x, opmask_t y) {
return _kxor_mask8(x, y);
}
static opmask_t knot_opmask(opmask_t x) { return _knot_mask8(x); }
static opmask_t le(reg_t x, reg_t y) {
return _mm512_cmp_epi64_mask(x, y, _MM_CMPINT_LE);
}
static opmask_t ge(reg_t x, reg_t y) {
return _mm512_cmp_epi64_mask(x, y, _MM_CMPINT_NLT);
}
static opmask_t gt(reg_t x, reg_t y) {
return _mm512_cmp_epi64_mask(x, y, _MM_CMPINT_GT);
}
static opmask_t get_partial_loadmask(uint64_t num_to_read) {
return ((0x1ull << num_to_read) - 0x1ull);
}
static opmask_t eq(reg_t x, reg_t y) {
return _mm512_cmp_epi64_mask(x, y, _MM_CMPINT_EQ);
}
template <int scale>
static reg_t mask_i64gather(reg_t src, opmask_t mask, __m512i index,
void const *base) {
return _mm512_mask_i64gather_epi64(src, mask, index, base, scale);
}
template <int scale>
static reg_t mask_i64gather(reg_t src, opmask_t mask, __m256i index,
void const *base) {
return _mm512_mask_i32gather_epi64(src, mask, index, base, scale);
}
static reg_t i64gather(type_t *arr, arrsize_t *ind) {
return set(arr[ind[7]], arr[ind[6]], arr[ind[5]], arr[ind[4]],
arr[ind[3]], arr[ind[2]], arr[ind[1]], arr[ind[0]]);
}
static reg_t loadu(void const *mem) { return _mm512_loadu_si512(mem); }
static reg_t max(reg_t x, reg_t y) { return _mm512_max_epi64(x, y); }
static void mask_compressstoreu(void *mem, opmask_t mask, reg_t x) {
return _mm512_mask_compressstoreu_epi64(mem, mask, x);
}
static reg_t maskz_loadu(opmask_t mask, void const *mem) {
return _mm512_maskz_loadu_epi64(mask, mem);
}
static reg_t mask_loadu(reg_t x, opmask_t mask, void const *mem) {
return _mm512_mask_loadu_epi64(x, mask, mem);
}
static reg_t mask_mov(reg_t x, opmask_t mask, reg_t y) {
return _mm512_mask_mov_epi64(x, mask, y);
}
static void mask_storeu(void *mem, opmask_t mask, reg_t x) {
return _mm512_mask_storeu_epi64(mem, mask, x);
}
static reg_t min(reg_t x, reg_t y) { return _mm512_min_epi64(x, y); }
static reg_t permutexvar(__m512i idx, reg_t zmm) {
return _mm512_permutexvar_epi64(idx, zmm);
}
static type_t reducemax(reg_t v) { return _mm512_reduce_max_epi64(v); }
static type_t reducemin(reg_t v) { return _mm512_reduce_min_epi64(v); }
static reg_t set1(type_t v) { return _mm512_set1_epi64(v); }
template <uint8_t mask>
static reg_t shuffle(reg_t zmm) {
__m512d temp = _mm512_castsi512_pd(zmm);
return _mm512_castpd_si512(
_mm512_shuffle_pd(temp, temp, (_MM_PERM_ENUM)mask));
}
static void storeu(void *mem, reg_t x) { _mm512_storeu_si512(mem, x); }
static reg_t reverse(reg_t zmm) {
const regi_t rev_index = seti(NETWORK_64BIT_2);
return permutexvar(rev_index, zmm);
}
static reg_t sort_vec(reg_t x) {
return sort_zmm_64bit<zmm_vector<type_t>>(x);
}
static reg_t cast_from(__m512i v) { return v; }
static __m512i cast_to(reg_t v) { return v; }
static int double_compressstore(type_t *left_addr, type_t *right_addr,
opmask_t k, reg_t reg) {
return avx512_double_compressstore<zmm_vector<type_t>>(
left_addr, right_addr, k, reg);
}
};
template <>
struct zmm_vector<double> {
using type_t = double;
using reg_t = __m512d;
using regi_t = __m512i;
using halfreg_t = __m512d;
using opmask_t = __mmask8;
static const uint8_t numlanes = 8;
#ifdef XSS_MINIMAL_NETWORK_SORT
static constexpr int network_sort_threshold = numlanes;
#else
static constexpr int network_sort_threshold = 256;
#endif
static constexpr int partition_unroll_factor = 8;
using swizzle_ops = avx512_64bit_swizzle_ops;
static type_t type_max() { return X86_SIMD_SORT_INFINITY; }
static type_t type_min() { return -X86_SIMD_SORT_INFINITY; }
static reg_t zmm_max() { return _mm512_set1_pd(type_max()); }
static regi_t seti(int v1, int v2, int v3, int v4, int v5, int v6, int v7,
int v8) {
return _mm512_set_epi64(v1, v2, v3, v4, v5, v6, v7, v8);
}
static reg_t set(type_t v1, type_t v2, type_t v3, type_t v4, type_t v5,
type_t v6, type_t v7, type_t v8) {
return _mm512_set_pd(v1, v2, v3, v4, v5, v6, v7, v8);
}
static reg_t maskz_loadu(opmask_t mask, void const *mem) {
return _mm512_maskz_loadu_pd(mask, mem);
}
static opmask_t knot_opmask(opmask_t x) { return _knot_mask8(x); }
static opmask_t ge(reg_t x, reg_t y) {
return _mm512_cmp_pd_mask(x, y, _CMP_GE_OQ);
}
static opmask_t gt(reg_t x, reg_t y) {
return _mm512_cmp_pd_mask(x, y, _CMP_GT_OQ);
}
static opmask_t eq(reg_t x, reg_t y) {
return _mm512_cmp_pd_mask(x, y, _CMP_EQ_OQ);
}
static opmask_t get_partial_loadmask(uint64_t num_to_read) {
return ((0x1ull << num_to_read) - 0x1ull);
}
static int32_t convert_mask_to_int(opmask_t mask) { return mask; }
template <int type>
static opmask_t fpclass(reg_t x) {
return _mm512_fpclass_pd_mask(x, type);
}
template <int scale>
static reg_t mask_i64gather(reg_t src, opmask_t mask, __m512i index,
void const *base) {
return _mm512_mask_i64gather_pd(src, mask, index, base, scale);
}
template <int scale>
static reg_t mask_i64gather(reg_t src, opmask_t mask, __m256i index,
void const *base) {
return _mm512_mask_i32gather_pd(src, mask, index, base, scale);
}
static reg_t i64gather(type_t *arr, arrsize_t *ind) {
return set(arr[ind[7]], arr[ind[6]], arr[ind[5]], arr[ind[4]],
arr[ind[3]], arr[ind[2]], arr[ind[1]], arr[ind[0]]);
}
static reg_t loadu(void const *mem) { return _mm512_loadu_pd(mem); }
static reg_t max(reg_t x, reg_t y) { return _mm512_max_pd(x, y); }
static void mask_compressstoreu(void *mem, opmask_t mask, reg_t x) {
return _mm512_mask_compressstoreu_pd(mem, mask, x);
}
static reg_t mask_loadu(reg_t x, opmask_t mask, void const *mem) {
return _mm512_mask_loadu_pd(x, mask, mem);
}
static reg_t mask_mov(reg_t x, opmask_t mask, reg_t y) {
return _mm512_mask_mov_pd(x, mask, y);
}
static void mask_storeu(void *mem, opmask_t mask, reg_t x) {
return _mm512_mask_storeu_pd(mem, mask, x);
}
static reg_t min(reg_t x, reg_t y) { return _mm512_min_pd(x, y); }
static reg_t permutexvar(__m512i idx, reg_t zmm) {
return _mm512_permutexvar_pd(idx, zmm);
}
static type_t reducemax(reg_t v) { return _mm512_reduce_max_pd(v); }
static type_t reducemin(reg_t v) { return _mm512_reduce_min_pd(v); }
static reg_t set1(type_t v) { return _mm512_set1_pd(v); }
template <uint8_t mask>
static reg_t shuffle(reg_t zmm) {
return _mm512_shuffle_pd(zmm, zmm, (_MM_PERM_ENUM)mask);
}
static void storeu(void *mem, reg_t x) { _mm512_storeu_pd(mem, x); }
static reg_t reverse(reg_t zmm) {
const regi_t rev_index = seti(NETWORK_64BIT_2);
return permutexvar(rev_index, zmm);
}
static reg_t sort_vec(reg_t x) {
return sort_zmm_64bit<zmm_vector<type_t>>(x);
}
static reg_t cast_from(__m512i v) { return _mm512_castsi512_pd(v); }
static __m512i cast_to(reg_t v) { return _mm512_castpd_si512(v); }
static int double_compressstore(type_t *left_addr, type_t *right_addr,
opmask_t k, reg_t reg) {
return avx512_double_compressstore<zmm_vector<type_t>>(
left_addr, right_addr, k, reg);
}
};
/*
* Assumes zmm is random and performs a full sorting network defined in
* https://en.wikipedia.org/wiki/Bitonic_sorter#/media/File:BitonicSort.svg
*/
template <typename vtype, typename reg_t = typename vtype::reg_t>
X86_SIMD_SORT_INLINE reg_t sort_zmm_64bit(reg_t zmm) {
const typename vtype::regi_t rev_index = vtype::seti(NETWORK_64BIT_2);
zmm = cmp_merge<vtype>(
zmm, vtype::template shuffle<SHUFFLE_MASK(1, 1, 1, 1)>(zmm), 0xAA);
zmm = cmp_merge<vtype>(
zmm, vtype::permutexvar(vtype::seti(NETWORK_64BIT_1), zmm), 0xCC);
zmm = cmp_merge<vtype>(
zmm, vtype::template shuffle<SHUFFLE_MASK(1, 1, 1, 1)>(zmm), 0xAA);
zmm = cmp_merge<vtype>(zmm, vtype::permutexvar(rev_index, zmm), 0xF0);
zmm = cmp_merge<vtype>(
zmm, vtype::permutexvar(vtype::seti(NETWORK_64BIT_3), zmm), 0xCC);
zmm = cmp_merge<vtype>(
zmm, vtype::template shuffle<SHUFFLE_MASK(1, 1, 1, 1)>(zmm), 0xAA);
return zmm;
}
struct avx512_64bit_swizzle_ops {
template <typename vtype, int scale>
X86_SIMD_SORT_INLINE typename vtype::reg_t swap_n(
typename vtype::reg_t reg) {
__m512i v = vtype::cast_to(reg);
if constexpr (scale == 2) {
v = _mm512_shuffle_epi32(v, (_MM_PERM_ENUM)0b01001110);
} else if constexpr (scale == 4) {
v = _mm512_shuffle_i64x2(v, v, 0b10110001);
} else if constexpr (scale == 8) {
v = _mm512_shuffle_i64x2(v, v, 0b01001110);
} else {
static_assert(scale == -1, "should not be reached");
}
return vtype::cast_from(v);
}
template <typename vtype, int scale>
X86_SIMD_SORT_INLINE typename vtype::reg_t reverse_n(
typename vtype::reg_t reg) {
__m512i v = vtype::cast_to(reg);
if constexpr (scale == 2) {
return swap_n<vtype, 2>(reg);
} else if constexpr (scale == 4) {
constexpr uint64_t mask = 0b00011011;
v = _mm512_permutex_epi64(v, mask);
} else if constexpr (scale == 8) {
return vtype::reverse(reg);
} else {
static_assert(scale == -1, "should not be reached");
}
return vtype::cast_from(v);
}
template <typename vtype, int scale>
X86_SIMD_SORT_INLINE typename vtype::reg_t merge_n(
typename vtype::reg_t reg, typename vtype::reg_t other) {
__m512i v1 = vtype::cast_to(reg);
__m512i v2 = vtype::cast_to(other);
if constexpr (scale == 2) {
v1 = _mm512_mask_blend_epi64(0b01010101, v1, v2);
} else if constexpr (scale == 4) {
v1 = _mm512_mask_blend_epi64(0b00110011, v1, v2);
} else if constexpr (scale == 8) {
v1 = _mm512_mask_blend_epi64(0b00001111, v1, v2);
} else {
static_assert(scale == -1, "should not be reached");
}
return vtype::cast_from(v1);
}
};
#endif

View file

@ -0,0 +1,79 @@
/*
* Copyright (c) 2023 Intel Corporation. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#include "simdsort-support.hpp"
#ifdef __SIMDSORT_SUPPORTED_LINUX
#pragma GCC target("avx512dq", "avx512f")
#include "avx512-32bit-qsort.hpp"
#include "avx512-64bit-qsort.hpp"
#include "classfile_constants.h"
#define DLL_PUBLIC __attribute__((visibility("default")))
#define INSERTION_SORT_THRESHOLD_32BIT 16
#define INSERTION_SORT_THRESHOLD_64BIT 20
extern "C" {
DLL_PUBLIC void avx512_sort(void *array, int elem_type, int32_t from_index, int32_t to_index) {
switch(elem_type) {
case JVM_T_INT:
avx512_fast_sort((int32_t*)array, from_index, to_index, INSERTION_SORT_THRESHOLD_32BIT);
break;
case JVM_T_LONG:
avx512_fast_sort((int64_t*)array, from_index, to_index, INSERTION_SORT_THRESHOLD_64BIT);
break;
case JVM_T_FLOAT:
avx512_fast_sort((float*)array, from_index, to_index, INSERTION_SORT_THRESHOLD_32BIT);
break;
case JVM_T_DOUBLE:
avx512_fast_sort((double*)array, from_index, to_index, INSERTION_SORT_THRESHOLD_64BIT);
break;
default:
assert(false, "Unexpected type");
}
}
DLL_PUBLIC void avx512_partition(void *array, int elem_type, int32_t from_index, int32_t to_index, int32_t *pivot_indices, int32_t index_pivot1, int32_t index_pivot2) {
switch(elem_type) {
case JVM_T_INT:
avx512_fast_partition((int32_t*)array, from_index, to_index, pivot_indices, index_pivot1, index_pivot2);
break;
case JVM_T_LONG:
avx512_fast_partition((int64_t*)array, from_index, to_index, pivot_indices, index_pivot1, index_pivot2);
break;
case JVM_T_FLOAT:
avx512_fast_partition((float*)array, from_index, to_index, pivot_indices, index_pivot1, index_pivot2);
break;
case JVM_T_DOUBLE:
avx512_fast_partition((double*)array, from_index, to_index, pivot_indices, index_pivot1, index_pivot2);
break;
default:
assert(false, "Unexpected type");
}
}
}
#endif

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2023 Intel Corporation. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#ifndef SIMDSORT_SUPPORT_HPP
#define SIMDSORT_SUPPORT_HPP
#include <stdio.h>
#include <stdlib.h>
#undef assert
#define assert(cond, msg) { if (!(cond)) { fprintf(stderr, "assert fails %s %d: %s\n", __FILE__, __LINE__, msg); abort(); }}
// GCC >= 9.1 is needed to build AVX2 portions of libsimdsort using C++17 features
#if defined(_LP64) && (defined(__GNUC__) && ((__GNUC__ > 9) || ((__GNUC__ == 9) && (__GNUC_MINOR__ >= 1))))
#define __SIMDSORT_SUPPORTED_LINUX
#endif
#endif //SIMDSORT_SUPPORT_HPP

View file

@ -0,0 +1,101 @@
/*
* Copyright (c) 2021, 2023, Intel Corporation. All rights reserved.
* Copyright (c) 2021 Serge Sans Paille. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
// This implementation is based on x86-simd-sort(https://github.com/intel/x86-simd-sort)
#ifndef XSS_COMMON_INCLUDES
#define XSS_COMMON_INCLUDES
#include <algorithm>
#include <cmath>
#include <cstdint>
#include <cstring>
/*
Workaround for the bug in GCC12 (that was fixed in GCC 12.3.1).
More details are available at:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105593
*/
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#pragma GCC diagnostic ignored "-Wuninitialized"
#include <immintrin.h>
#pragma GCC diagnostic pop
#include <limits>
#include <vector>
#define X86_SIMD_SORT_INFINITY std::numeric_limits<double>::infinity()
#define X86_SIMD_SORT_INFINITYF std::numeric_limits<float>::infinity()
#define X86_SIMD_SORT_INFINITYH 0x7c00
#define X86_SIMD_SORT_NEGINFINITYH 0xfc00
#define X86_SIMD_SORT_MAX_UINT16 std::numeric_limits<uint16_t>::max()
#define X86_SIMD_SORT_MAX_INT16 std::numeric_limits<int16_t>::max()
#define X86_SIMD_SORT_MIN_INT16 std::numeric_limits<int16_t>::min()
#define X86_SIMD_SORT_MAX_UINT32 std::numeric_limits<uint32_t>::max()
#define X86_SIMD_SORT_MAX_INT32 std::numeric_limits<int32_t>::max()
#define X86_SIMD_SORT_MIN_INT32 std::numeric_limits<int32_t>::min()
#define X86_SIMD_SORT_MAX_UINT64 std::numeric_limits<uint64_t>::max()
#define X86_SIMD_SORT_MAX_INT64 std::numeric_limits<int64_t>::max()
#define X86_SIMD_SORT_MIN_INT64 std::numeric_limits<int64_t>::min()
#define ZMM_MAX_DOUBLE _mm512_set1_pd(X86_SIMD_SORT_INFINITY)
#define ZMM_MAX_UINT64 _mm512_set1_epi64(X86_SIMD_SORT_MAX_UINT64)
#define ZMM_MAX_INT64 _mm512_set1_epi64(X86_SIMD_SORT_MAX_INT64)
#define ZMM_MAX_FLOAT _mm512_set1_ps(X86_SIMD_SORT_INFINITYF)
#define ZMM_MAX_UINT _mm512_set1_epi32(X86_SIMD_SORT_MAX_UINT32)
#define ZMM_MAX_INT _mm512_set1_epi32(X86_SIMD_SORT_MAX_INT32)
#define ZMM_MAX_HALF _mm512_set1_epi16(X86_SIMD_SORT_INFINITYH)
#define YMM_MAX_HALF _mm256_set1_epi16(X86_SIMD_SORT_INFINITYH)
#define ZMM_MAX_UINT16 _mm512_set1_epi16(X86_SIMD_SORT_MAX_UINT16)
#define ZMM_MAX_INT16 _mm512_set1_epi16(X86_SIMD_SORT_MAX_INT16)
#define SHUFFLE_MASK(a, b, c, d) (a << 6) | (b << 4) | (c << 2) | d
#define PRAGMA(x) _Pragma(#x)
#define UNUSED(x) (void)(x)
/* Compiler specific macros specific */
#if defined(__GNUC__)
#define X86_SIMD_SORT_INLINE static inline
#define X86_SIMD_SORT_FINLINE static inline __attribute__((always_inline))
#else
#define X86_SIMD_SORT_INLINE static
#define X86_SIMD_SORT_FINLINE static
#endif
#if __GNUC__ >= 8
#define X86_SIMD_SORT_UNROLL_LOOP(num) PRAGMA(GCC unroll num)
#else
#define X86_SIMD_SORT_UNROLL_LOOP(num)
#endif
typedef size_t arrsize_t;
template <typename type>
struct zmm_vector;
template <typename type>
struct ymm_vector;
template <typename type>
struct avx2_vector;
#endif // XSS_COMMON_INCLUDES

View file

@ -0,0 +1,528 @@
/*
* Copyright (c) 2021, 2023, Intel Corporation. All rights reserved.
* Copyright (c) 2021 Serge Sans Paille. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
// This implementation is based on x86-simd-sort(https://github.com/intel/x86-simd-sort)
#ifndef XSS_COMMON_QSORT
#define XSS_COMMON_QSORT
/*
* Quicksort using AVX-512. The ideas and code are based on these two research
* papers [1] and [2]. On a high level, the idea is to vectorize quicksort
* partitioning using AVX-512 compressstore instructions. If the array size is
* < 128, then use Bitonic sorting network implemented on 512-bit registers.
* The precise network definitions depend on the dtype and are defined in
* separate files: avx512-16bit-qsort.hpp, avx512-32bit-qsort.hpp and
* avx512-64bit-qsort.hpp. Article [4] is a good resource for bitonic sorting
* network. The core implementations of the vectorized qsort functions
* avx512_qsort<T>(T*, arrsize_t) are modified versions of avx2 quicksort
* presented in the paper [2] and source code associated with that paper [3].
*
* [1] Fast and Robust Vectorized In-Place Sorting of Primitive Types
* https://drops.dagstuhl.de/opus/volltexte/2021/13775/
*
* [2] A Novel Hybrid Quicksort Algorithm Vectorized using AVX-512 on Intel
* Skylake https://arxiv.org/pdf/1704.08579.pdf
*
* [3] https://github.com/simd-sorting/fast-and-robust: SPDX-License-Identifier:
* MIT
*
* [4] https://mitp-content-server.mit.edu/books/content/sectbyfn?collid=books_pres_0&fn=Chapter%2027.pdf&id=8030
*
*/
#include "xss-common-includes.h"
#include "xss-pivot-selection.hpp"
#include "xss-network-qsort.hpp"
template <typename T>
bool is_a_nan(T elem) {
return std::isnan(elem);
}
template <typename T>
X86_SIMD_SORT_INLINE T get_pivot_scalar(T *arr, const int64_t left, const int64_t right) {
// median of 8 equally spaced elements
int64_t NUM_ELEMENTS = 8;
int64_t MID = NUM_ELEMENTS / 2;
int64_t size = (right - left) / NUM_ELEMENTS;
T temp[NUM_ELEMENTS];
for (int64_t i = 0; i < NUM_ELEMENTS; i++) temp[i] = arr[left + (i * size)];
std::sort(temp, temp + NUM_ELEMENTS);
return temp[MID];
}
template <typename vtype, typename T = typename vtype::type_t>
bool comparison_func_ge(const T &a, const T &b) {
return a < b;
}
template <typename vtype, typename T = typename vtype::type_t>
bool comparison_func_gt(const T &a, const T &b) {
return a <= b;
}
/*
* COEX == Compare and Exchange two registers by swapping min and max values
*/
template <typename vtype, typename mm_t>
X86_SIMD_SORT_INLINE void COEX(mm_t &a, mm_t &b) {
mm_t temp = a;
a = vtype::min(a, b);
b = vtype::max(temp, b);
}
template <typename vtype, typename reg_t = typename vtype::reg_t,
typename opmask_t = typename vtype::opmask_t>
X86_SIMD_SORT_INLINE reg_t cmp_merge(reg_t in1, reg_t in2, opmask_t mask) {
reg_t min = vtype::min(in2, in1);
reg_t max = vtype::max(in2, in1);
return vtype::mask_mov(min, mask, max); // 0 -> min, 1 -> max
}
template <typename vtype, typename type_t, typename reg_t>
int avx512_double_compressstore(type_t *left_addr, type_t *right_addr,
typename vtype::opmask_t k, reg_t reg) {
int amount_ge_pivot = _mm_popcnt_u32((int)k);
vtype::mask_compressstoreu(left_addr, vtype::knot_opmask(k), reg);
vtype::mask_compressstoreu(right_addr + vtype::numlanes - amount_ge_pivot,
k, reg);
return amount_ge_pivot;
}
// Generic function dispatches to AVX2 or AVX512 code
template <typename vtype, typename type_t,
typename reg_t = typename vtype::reg_t>
X86_SIMD_SORT_INLINE arrsize_t partition_vec(type_t *l_store, type_t *r_store,
const reg_t curr_vec,
const reg_t pivot_vec,
reg_t &smallest_vec,
reg_t &biggest_vec, bool use_gt) {
//typename vtype::opmask_t ge_mask = vtype::ge(curr_vec, pivot_vec);
typename vtype::opmask_t mask;
if (use_gt) mask = vtype::gt(curr_vec, pivot_vec);
else mask = vtype::ge(curr_vec, pivot_vec);
int amount_ge_pivot =
vtype::double_compressstore(l_store, r_store, mask, curr_vec);
smallest_vec = vtype::min(curr_vec, smallest_vec);
biggest_vec = vtype::max(curr_vec, biggest_vec);
return amount_ge_pivot;
}
/*
* Parition an array based on the pivot and returns the index of the
* first element that is greater than or equal to the pivot.
*/
template <typename vtype, typename type_t>
X86_SIMD_SORT_INLINE arrsize_t partition_avx512(type_t *arr, arrsize_t left,
arrsize_t right, type_t pivot,
type_t *smallest,
type_t *biggest,
bool use_gt) {
auto comparison_func = use_gt ? comparison_func_gt<vtype> : comparison_func_ge<vtype>;
/* make array length divisible by vtype::numlanes , shortening the array */
for (int32_t i = (right - left) % vtype::numlanes; i > 0; --i) {
*smallest = std::min(*smallest, arr[left], comparison_func);
*biggest = std::max(*biggest, arr[left], comparison_func);
if (!comparison_func(arr[left], pivot)) {
std::swap(arr[left], arr[--right]);
} else {
++left;
}
}
if (left == right)
return left; /* less than vtype::numlanes elements in the array */
using reg_t = typename vtype::reg_t;
reg_t pivot_vec = vtype::set1(pivot);
reg_t min_vec = vtype::set1(*smallest);
reg_t max_vec = vtype::set1(*biggest);
if (right - left == vtype::numlanes) {
reg_t vec = vtype::loadu(arr + left);
arrsize_t unpartitioned = right - left - vtype::numlanes;
arrsize_t l_store = left;
arrsize_t amount_ge_pivot =
partition_vec<vtype>(arr + l_store, arr + l_store + unpartitioned,
vec, pivot_vec, min_vec, max_vec, use_gt);
l_store += (vtype::numlanes - amount_ge_pivot);
*smallest = vtype::reducemin(min_vec);
*biggest = vtype::reducemax(max_vec);
return l_store;
}
// first and last vtype::numlanes values are partitioned at the end
reg_t vec_left = vtype::loadu(arr + left);
reg_t vec_right = vtype::loadu(arr + (right - vtype::numlanes));
// store points of the vectors
arrsize_t unpartitioned = right - left - vtype::numlanes;
arrsize_t l_store = left;
// indices for loading the elements
left += vtype::numlanes;
right -= vtype::numlanes;
while (right - left != 0) {
reg_t curr_vec;
/*
* if fewer elements are stored on the right side of the array,
* then next elements are loaded from the right side,
* otherwise from the left side
*/
if ((l_store + unpartitioned + vtype::numlanes) - right <
left - l_store) {
right -= vtype::numlanes;
curr_vec = vtype::loadu(arr + right);
} else {
curr_vec = vtype::loadu(arr + left);
left += vtype::numlanes;
}
// partition the current vector and save it on both sides of the array
arrsize_t amount_ge_pivot =
partition_vec<vtype>(arr + l_store, arr + l_store + unpartitioned,
curr_vec, pivot_vec, min_vec, max_vec, use_gt);
l_store += (vtype::numlanes - amount_ge_pivot);
unpartitioned -= vtype::numlanes;
}
/* partition and save vec_left and vec_right */
arrsize_t amount_ge_pivot =
partition_vec<vtype>(arr + l_store, arr + l_store + unpartitioned,
vec_left, pivot_vec, min_vec, max_vec, use_gt);
l_store += (vtype::numlanes - amount_ge_pivot);
unpartitioned -= vtype::numlanes;
amount_ge_pivot =
partition_vec<vtype>(arr + l_store, arr + l_store + unpartitioned,
vec_right, pivot_vec, min_vec, max_vec, use_gt);
l_store += (vtype::numlanes - amount_ge_pivot);
unpartitioned -= vtype::numlanes;
*smallest = vtype::reducemin(min_vec);
*biggest = vtype::reducemax(max_vec);
return l_store;
}
template <typename vtype, int num_unroll,
typename type_t = typename vtype::type_t>
X86_SIMD_SORT_INLINE arrsize_t
partition_avx512_unrolled(type_t *arr, arrsize_t left, arrsize_t right,
type_t pivot, type_t *smallest, type_t *biggest, bool use_gt) {
if constexpr (num_unroll == 0) {
return partition_avx512<vtype>(arr, left, right, pivot, smallest,
biggest, use_gt);
}
/* Use regular partition_avx512 for smaller arrays */
if (right - left < 3 * num_unroll * vtype::numlanes) {
return partition_avx512<vtype>(arr, left, right, pivot, smallest,
biggest, use_gt);
}
auto comparison_func = use_gt ? comparison_func_gt<vtype> : comparison_func_ge<vtype>;
/* make array length divisible by vtype::numlanes, shortening the array */
for (int32_t i = ((right - left) % (vtype::numlanes)); i > 0; --i) {
*smallest = std::min(*smallest, arr[left], comparison_func);
*biggest = std::max(*biggest, arr[left], comparison_func);
if (!comparison_func(arr[left], pivot)) {
std::swap(arr[left], arr[--right]);
} else {
++left;
}
}
arrsize_t unpartitioned = right - left - vtype::numlanes;
arrsize_t l_store = left;
using reg_t = typename vtype::reg_t;
reg_t pivot_vec = vtype::set1(pivot);
reg_t min_vec = vtype::set1(*smallest);
reg_t max_vec = vtype::set1(*biggest);
/* Calculate and load more registers to make the rest of the array a
* multiple of num_unroll. These registers will be partitioned at the very
* end. */
int vecsToPartition = ((right - left) / vtype::numlanes) % num_unroll;
reg_t vec_align[num_unroll];
for (int i = 0; i < vecsToPartition; i++) {
vec_align[i] = vtype::loadu(arr + left + i * vtype::numlanes);
}
left += vecsToPartition * vtype::numlanes;
/* We will now have atleast 3*num_unroll registers worth of data to
* process. Load left and right vtype::numlanes*num_unroll values into
* registers to make space for in-place parition. The vec_left and
* vec_right registers are partitioned at the end */
reg_t vec_left[num_unroll], vec_right[num_unroll];
X86_SIMD_SORT_UNROLL_LOOP(8)
for (int ii = 0; ii < num_unroll; ++ii) {
vec_left[ii] = vtype::loadu(arr + left + vtype::numlanes * ii);
vec_right[ii] =
vtype::loadu(arr + (right - vtype::numlanes * (num_unroll - ii)));
}
/* indices for loading the elements */
left += num_unroll * vtype::numlanes;
right -= num_unroll * vtype::numlanes;
while (right - left != 0) {
reg_t curr_vec[num_unroll];
/*
* if fewer elements are stored on the right side of the array,
* then next elements are loaded from the right side,
* otherwise from the left side
*/
if ((l_store + unpartitioned + vtype::numlanes) - right <
left - l_store) {
right -= num_unroll * vtype::numlanes;
X86_SIMD_SORT_UNROLL_LOOP(8)
for (int ii = 0; ii < num_unroll; ++ii) {
curr_vec[ii] = vtype::loadu(arr + right + ii * vtype::numlanes);
/*
* error: '_mm_prefetch' needs target feature mmx on clang-cl
*/
#if !(defined(_MSC_VER) && defined(__clang__))
_mm_prefetch((char *)(arr + right + ii * vtype::numlanes -
num_unroll * vtype::numlanes),
_MM_HINT_T0);
#endif
}
} else {
X86_SIMD_SORT_UNROLL_LOOP(8)
for (int ii = 0; ii < num_unroll; ++ii) {
curr_vec[ii] = vtype::loadu(arr + left + ii * vtype::numlanes);
/*
* error: '_mm_prefetch' needs target feature mmx on clang-cl
*/
#if !(defined(_MSC_VER) && defined(__clang__))
_mm_prefetch((char *)(arr + left + ii * vtype::numlanes +
num_unroll * vtype::numlanes),
_MM_HINT_T0);
#endif
}
left += num_unroll * vtype::numlanes;
}
/* partition the current vector and save it on both sides of the array
* */
X86_SIMD_SORT_UNROLL_LOOP(8)
for (int ii = 0; ii < num_unroll; ++ii) {
arrsize_t amount_ge_pivot = partition_vec<vtype>(
arr + l_store, arr + l_store + unpartitioned, curr_vec[ii],
pivot_vec, min_vec, max_vec, use_gt);
l_store += (vtype::numlanes - amount_ge_pivot);
unpartitioned -= vtype::numlanes;
}
}
/* partition and save vec_left[num_unroll] and vec_right[num_unroll] */
X86_SIMD_SORT_UNROLL_LOOP(8)
for (int ii = 0; ii < num_unroll; ++ii) {
arrsize_t amount_ge_pivot =
partition_vec<vtype>(arr + l_store, arr + l_store + unpartitioned,
vec_left[ii], pivot_vec, min_vec, max_vec, use_gt);
l_store += (vtype::numlanes - amount_ge_pivot);
unpartitioned -= vtype::numlanes;
}
X86_SIMD_SORT_UNROLL_LOOP(8)
for (int ii = 0; ii < num_unroll; ++ii) {
arrsize_t amount_ge_pivot =
partition_vec<vtype>(arr + l_store, arr + l_store + unpartitioned,
vec_right[ii], pivot_vec, min_vec, max_vec, use_gt);
l_store += (vtype::numlanes - amount_ge_pivot);
unpartitioned -= vtype::numlanes;
}
/* partition and save vec_align[vecsToPartition] */
X86_SIMD_SORT_UNROLL_LOOP(8)
for (int ii = 0; ii < vecsToPartition; ++ii) {
arrsize_t amount_ge_pivot =
partition_vec<vtype>(arr + l_store, arr + l_store + unpartitioned,
vec_align[ii], pivot_vec, min_vec, max_vec, use_gt);
l_store += (vtype::numlanes - amount_ge_pivot);
unpartitioned -= vtype::numlanes;
}
*smallest = vtype::reducemin(min_vec);
*biggest = vtype::reducemax(max_vec);
return l_store;
}
template <typename vtype, int maxN>
void sort_n(typename vtype::type_t *arr, int N);
template <typename vtype, typename type_t>
static void qsort_(type_t *arr, arrsize_t left, arrsize_t right,
arrsize_t max_iters) {
/*
* Resort to std::sort if quicksort isnt making any progress
*/
if (max_iters <= 0) {
std::sort(arr + left, arr + right + 1, comparison_func_ge<vtype>);
return;
}
/*
* Base case: use bitonic networks to sort arrays <=
* vtype::network_sort_threshold
*/
if (right + 1 - left <= vtype::network_sort_threshold) {
sort_n<vtype, vtype::network_sort_threshold>(
arr + left, (int32_t)(right + 1 - left));
return;
}
type_t pivot = get_pivot_blocks<vtype, type_t>(arr, left, right);
type_t smallest = vtype::type_max();
type_t biggest = vtype::type_min();
arrsize_t pivot_index =
partition_avx512_unrolled<vtype, vtype::partition_unroll_factor>(
arr, left, right + 1, pivot, &smallest, &biggest, false);
if (pivot != smallest)
qsort_<vtype>(arr, left, pivot_index - 1, max_iters - 1);
if (pivot != biggest) qsort_<vtype>(arr, pivot_index, right, max_iters - 1);
}
// Hooks for OpenJDK sort
// to_index (exclusive)
template <typename vtype, typename type_t>
static int64_t vectorized_partition(type_t *arr, int64_t from_index, int64_t to_index, type_t pivot, bool use_gt) {
type_t smallest = vtype::type_max();
type_t biggest = vtype::type_min();
int64_t pivot_index = partition_avx512_unrolled<vtype, 2>(
arr, from_index, to_index, pivot, &smallest, &biggest, use_gt);
return pivot_index;
}
// partitioning functions
template <typename vtype, typename T>
X86_SIMD_SORT_INLINE void simd_dual_pivot_partition(T *arr, int64_t from_index, int64_t to_index, int32_t *pivot_indices, int64_t index_pivot1, int64_t index_pivot2){
const T pivot1 = arr[index_pivot1];
const T pivot2 = arr[index_pivot2];
const int64_t low = from_index;
const int64_t high = to_index;
const int64_t start = low + 1;
const int64_t end = high - 1;
std::swap(arr[index_pivot1], arr[low]);
std::swap(arr[index_pivot2], arr[end]);
const int64_t pivot_index2 = vectorized_partition<vtype, T>(arr, start, end, pivot2, true); // use_gt = true
std::swap(arr[end], arr[pivot_index2]);
int64_t upper = pivot_index2;
// if all other elements are greater than pivot2 (and pivot1), no need to do further partitioning
if (upper == start) {
pivot_indices[0] = low;
pivot_indices[1] = upper;
return;
}
const int64_t pivot_index1 = vectorized_partition<vtype, T>(arr, start, upper, pivot1, false); // use_ge (use_gt = false)
int64_t lower = pivot_index1 - 1;
std::swap(arr[low], arr[lower]);
pivot_indices[0] = lower;
pivot_indices[1] = upper;
}
template <typename vtype, typename T>
X86_SIMD_SORT_INLINE void simd_single_pivot_partition(T *arr, int64_t from_index, int64_t to_index, int32_t *pivot_indices, int64_t index_pivot) {
const T pivot = arr[index_pivot];
const int64_t low = from_index;
const int64_t high = to_index;
const int64_t end = high - 1;
const int64_t pivot_index1 = vectorized_partition<vtype, T>(arr, low, high, pivot, false); // use_gt = false (use_ge)
int64_t lower = pivot_index1;
const int64_t pivot_index2 = vectorized_partition<vtype, T>(arr, pivot_index1, high, pivot, true); // use_gt = true
int64_t upper = pivot_index2;
pivot_indices[0] = lower;
pivot_indices[1] = upper;
}
template <typename vtype, typename T>
X86_SIMD_SORT_INLINE void simd_fast_partition(T *arr, int64_t from_index, int64_t to_index, int32_t *pivot_indices, int64_t index_pivot1, int64_t index_pivot2) {
if (index_pivot1 != index_pivot2) {
simd_dual_pivot_partition<vtype, T>(arr, from_index, to_index, pivot_indices, index_pivot1, index_pivot2);
}
else {
simd_single_pivot_partition<vtype, T>(arr, from_index, to_index, pivot_indices, index_pivot1);
}
}
template <typename T>
X86_SIMD_SORT_INLINE void insertion_sort(T *arr, int32_t from_index, int32_t to_index) {
for (int i, k = from_index; ++k < to_index; ) {
T ai = arr[i = k];
if (ai < arr[i - 1]) {
while (--i >= from_index && ai < arr[i]) {
arr[i + 1] = arr[i];
}
arr[i + 1] = ai;
}
}
}
template <typename vtype, typename T>
X86_SIMD_SORT_INLINE void simd_fast_sort(T *arr, arrsize_t from_index, arrsize_t to_index, const arrsize_t INS_SORT_THRESHOLD)
{
arrsize_t arrsize = to_index - from_index;
if (arrsize <= INS_SORT_THRESHOLD) {
insertion_sort<T>(arr, from_index, to_index);
} else {
qsort_<vtype, T>(arr, from_index, to_index - 1, 2 * (arrsize_t)log2(arrsize));
}
}
#define DEFINE_METHODS(ISA, VTYPE) \
template <typename T> \
X86_SIMD_SORT_INLINE void ISA##_fast_sort( \
T *arr, arrsize_t from_index, arrsize_t to_index, const arrsize_t INS_SORT_THRESHOLD) \
{ \
simd_fast_sort<VTYPE, T>(arr, from_index, to_index, INS_SORT_THRESHOLD); \
} \
template <typename T> \
X86_SIMD_SORT_INLINE void ISA##_fast_partition( \
T *arr, int64_t from_index, int64_t to_index, int32_t *pivot_indices, int64_t index_pivot1, int64_t index_pivot2) \
{ \
simd_fast_partition<VTYPE, T>(arr, from_index, to_index, pivot_indices, index_pivot1, index_pivot2); \
}
DEFINE_METHODS(avx2, avx2_vector<T>)
DEFINE_METHODS(avx512, zmm_vector<T>)
#endif // XSS_COMMON_QSORT

View file

@ -0,0 +1,209 @@
/*
* Copyright (c) 2021, 2023, Intel Corporation. All rights reserved.
* Copyright (c) 2021 Serge Sans Paille. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
// This implementation is based on x86-simd-sort(https://github.com/intel/x86-simd-sort)
#ifndef XSS_NETWORK_QSORT
#define XSS_NETWORK_QSORT
#include "xss-common-qsort.h"
#include "xss-optimal-networks.hpp"
template <typename vtype, int numVecs, typename reg_t = typename vtype::reg_t>
X86_SIMD_SORT_FINLINE void bitonic_sort_n_vec(reg_t *regs) {
if constexpr (numVecs == 1) {
UNUSED(regs);
return;
} else if constexpr (numVecs == 2) {
COEX<vtype>(regs[0], regs[1]);
} else if constexpr (numVecs == 4) {
optimal_sort_4<vtype>(regs);
} else if constexpr (numVecs == 8) {
optimal_sort_8<vtype>(regs);
} else if constexpr (numVecs == 16) {
optimal_sort_16<vtype>(regs);
} else if constexpr (numVecs == 32) {
optimal_sort_32<vtype>(regs);
} else {
static_assert(numVecs == -1, "should not reach here");
}
}
/*
* Swizzle ops explained:
* swap_n<scale>: swap neighbouring blocks of size <scale/2> within block of
* size <scale> reg i = [7,6,5,4,3,2,1,0] swap_n<2>: =
* [[6,7],[4,5],[2,3],[0,1]] swap_n<4>: = [[5,4,7,6],[1,0,3,2]] swap_n<8>: =
* [[3,2,1,0,7,6,5,4]] reverse_n<scale>: reverse elements within block of size
* <scale> reg i = [7,6,5,4,3,2,1,0] rev_n<2>: =
* [[6,7],[4,5],[2,3],[0,1]] rev_n<4>: = [[4,5,6,7],[0,1,2,3]] rev_n<8>: =
* [[0,1,2,3,4,5,6,7]] merge_n<scale>: merge blocks of <scale/2> elements from
* two regs reg b,a = [a,a,a,a,a,a,a,a], [b,b,b,b,b,b,b,b] merge_n<2> =
* [a,b,a,b,a,b,a,b] merge_n<4> = [a,a,b,b,a,a,b,b] merge_n<8> =
* [a,a,a,a,b,b,b,b]
*/
template <typename vtype, int numVecs, int scale, bool first = true>
X86_SIMD_SORT_FINLINE void internal_merge_n_vec(typename vtype::reg_t *reg) {
using reg_t = typename vtype::reg_t;
using swizzle = typename vtype::swizzle_ops;
if constexpr (scale <= 1) {
UNUSED(reg);
return;
} else {
if constexpr (first) {
// Use reverse then merge
X86_SIMD_SORT_UNROLL_LOOP(64)
for (int i = 0; i < numVecs; i++) {
reg_t &v = reg[i];
reg_t rev = swizzle::template reverse_n<vtype, scale>(v);
COEX<vtype>(rev, v);
v = swizzle::template merge_n<vtype, scale>(v, rev);
}
} else {
// Use swap then merge
X86_SIMD_SORT_UNROLL_LOOP(64)
for (int i = 0; i < numVecs; i++) {
reg_t &v = reg[i];
reg_t swap = swizzle::template swap_n<vtype, scale>(v);
COEX<vtype>(swap, v);
v = swizzle::template merge_n<vtype, scale>(v, swap);
}
}
internal_merge_n_vec<vtype, numVecs, scale / 2, false>(reg);
}
}
template <typename vtype, int numVecs, int scale,
typename reg_t = typename vtype::reg_t>
X86_SIMD_SORT_FINLINE void merge_substep_n_vec(reg_t *regs) {
using swizzle = typename vtype::swizzle_ops;
if constexpr (numVecs <= 1) {
UNUSED(regs);
return;
}
// Reverse upper half of vectors
X86_SIMD_SORT_UNROLL_LOOP(64)
for (int i = numVecs / 2; i < numVecs; i++) {
regs[i] = swizzle::template reverse_n<vtype, scale>(regs[i]);
}
// Do compare exchanges
X86_SIMD_SORT_UNROLL_LOOP(64)
for (int i = 0; i < numVecs / 2; i++) {
COEX<vtype>(regs[i], regs[numVecs - 1 - i]);
}
merge_substep_n_vec<vtype, numVecs / 2, scale>(regs);
merge_substep_n_vec<vtype, numVecs / 2, scale>(regs + numVecs / 2);
}
template <typename vtype, int numVecs, int scale,
typename reg_t = typename vtype::reg_t>
X86_SIMD_SORT_FINLINE void merge_step_n_vec(reg_t *regs) {
// Do cross vector merges
merge_substep_n_vec<vtype, numVecs, scale>(regs);
// Do internal vector merges
internal_merge_n_vec<vtype, numVecs, scale>(regs);
}
template <typename vtype, int numVecs, int numPer = 2,
typename reg_t = typename vtype::reg_t>
X86_SIMD_SORT_FINLINE void merge_n_vec(reg_t *regs) {
if constexpr (numPer > vtype::numlanes) {
UNUSED(regs);
return;
} else {
merge_step_n_vec<vtype, numVecs, numPer>(regs);
merge_n_vec<vtype, numVecs, numPer * 2>(regs);
}
}
template <typename vtype, int numVecs, typename reg_t = typename vtype::reg_t>
X86_SIMD_SORT_INLINE void sort_n_vec(typename vtype::type_t *arr, int N) {
static_assert(numVecs > 0, "numVecs should be > 0");
if constexpr (numVecs > 1) {
if (N * 2 <= numVecs * vtype::numlanes) {
sort_n_vec<vtype, numVecs / 2>(arr, N);
return;
}
}
reg_t vecs[numVecs];
// Generate masks for loading and storing
typename vtype::opmask_t ioMasks[numVecs - numVecs / 2];
X86_SIMD_SORT_UNROLL_LOOP(64)
for (int i = numVecs / 2, j = 0; i < numVecs; i++, j++) {
uint64_t num_to_read =
std::min((uint64_t)std::max(0, N - i * vtype::numlanes),
(uint64_t)vtype::numlanes);
ioMasks[j] = vtype::get_partial_loadmask(num_to_read);
}
// Unmasked part of the load
X86_SIMD_SORT_UNROLL_LOOP(64)
for (int i = 0; i < numVecs / 2; i++) {
vecs[i] = vtype::loadu(arr + i * vtype::numlanes);
}
// Masked part of the load
X86_SIMD_SORT_UNROLL_LOOP(64)
for (int i = numVecs / 2, j = 0; i < numVecs; i++, j++) {
vecs[i] = vtype::mask_loadu(vtype::zmm_max(), ioMasks[j],
arr + i * vtype::numlanes);
}
/* Run the initial sorting network to sort the columns of the [numVecs x
* num_lanes] matrix
*/
bitonic_sort_n_vec<vtype, numVecs>(vecs);
// Merge the vectors using bitonic merging networks
merge_n_vec<vtype, numVecs>(vecs);
// Unmasked part of the store
X86_SIMD_SORT_UNROLL_LOOP(64)
for (int i = 0; i < numVecs / 2; i++) {
vtype::storeu(arr + i * vtype::numlanes, vecs[i]);
}
// Masked part of the store
X86_SIMD_SORT_UNROLL_LOOP(64)
for (int i = numVecs / 2, j = 0; i < numVecs; i++, j++) {
vtype::mask_storeu(arr + i * vtype::numlanes, ioMasks[j], vecs[i]);
}
}
template <typename vtype, int maxN>
X86_SIMD_SORT_INLINE void sort_n(typename vtype::type_t *arr, int N) {
constexpr int numVecs = maxN / vtype::numlanes;
constexpr bool isMultiple = (maxN == (vtype::numlanes * numVecs));
constexpr bool powerOfTwo = (numVecs != 0 && !(numVecs & (numVecs - 1)));
static_assert(powerOfTwo == true && isMultiple == true,
"maxN must be vtype::numlanes times a power of 2");
sort_n_vec<vtype, numVecs>(arr, N);
}
#endif

View file

@ -0,0 +1,342 @@
/*
* Copyright (c) 2021, 2023, Intel Corporation. All rights reserved.
* Copyright (c) 2021 Serge Sans Paille. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
// This implementation is based on x86-simd-sort(https://github.com/intel/x86-simd-sort) All of these sources
// files are generated from the optimal networks described in
// https://bertdobbelaere.github.io/sorting_networks.html
template <typename vtype, typename reg_t = typename vtype::reg_t>
X86_SIMD_SORT_FINLINE void optimal_sort_4(reg_t *vecs) {
COEX<vtype>(vecs[0], vecs[2]);
COEX<vtype>(vecs[1], vecs[3]);
COEX<vtype>(vecs[0], vecs[1]);
COEX<vtype>(vecs[2], vecs[3]);
COEX<vtype>(vecs[1], vecs[2]);
}
template <typename vtype, typename reg_t = typename vtype::reg_t>
X86_SIMD_SORT_FINLINE void optimal_sort_8(reg_t *vecs) {
COEX<vtype>(vecs[0], vecs[2]);
COEX<vtype>(vecs[1], vecs[3]);
COEX<vtype>(vecs[4], vecs[6]);
COEX<vtype>(vecs[5], vecs[7]);
COEX<vtype>(vecs[0], vecs[4]);
COEX<vtype>(vecs[1], vecs[5]);
COEX<vtype>(vecs[2], vecs[6]);
COEX<vtype>(vecs[3], vecs[7]);
COEX<vtype>(vecs[0], vecs[1]);
COEX<vtype>(vecs[2], vecs[3]);
COEX<vtype>(vecs[4], vecs[5]);
COEX<vtype>(vecs[6], vecs[7]);
COEX<vtype>(vecs[2], vecs[4]);
COEX<vtype>(vecs[3], vecs[5]);
COEX<vtype>(vecs[1], vecs[4]);
COEX<vtype>(vecs[3], vecs[6]);
COEX<vtype>(vecs[1], vecs[2]);
COEX<vtype>(vecs[3], vecs[4]);
COEX<vtype>(vecs[5], vecs[6]);
}
template <typename vtype, typename reg_t = typename vtype::reg_t>
X86_SIMD_SORT_FINLINE void optimal_sort_16(reg_t *vecs) {
COEX<vtype>(vecs[0], vecs[13]);
COEX<vtype>(vecs[1], vecs[12]);
COEX<vtype>(vecs[2], vecs[15]);
COEX<vtype>(vecs[3], vecs[14]);
COEX<vtype>(vecs[4], vecs[8]);
COEX<vtype>(vecs[5], vecs[6]);
COEX<vtype>(vecs[7], vecs[11]);
COEX<vtype>(vecs[9], vecs[10]);
COEX<vtype>(vecs[0], vecs[5]);
COEX<vtype>(vecs[1], vecs[7]);
COEX<vtype>(vecs[2], vecs[9]);
COEX<vtype>(vecs[3], vecs[4]);
COEX<vtype>(vecs[6], vecs[13]);
COEX<vtype>(vecs[8], vecs[14]);
COEX<vtype>(vecs[10], vecs[15]);
COEX<vtype>(vecs[11], vecs[12]);
COEX<vtype>(vecs[0], vecs[1]);
COEX<vtype>(vecs[2], vecs[3]);
COEX<vtype>(vecs[4], vecs[5]);
COEX<vtype>(vecs[6], vecs[8]);
COEX<vtype>(vecs[7], vecs[9]);
COEX<vtype>(vecs[10], vecs[11]);
COEX<vtype>(vecs[12], vecs[13]);
COEX<vtype>(vecs[14], vecs[15]);
COEX<vtype>(vecs[0], vecs[2]);
COEX<vtype>(vecs[1], vecs[3]);
COEX<vtype>(vecs[4], vecs[10]);
COEX<vtype>(vecs[5], vecs[11]);
COEX<vtype>(vecs[6], vecs[7]);
COEX<vtype>(vecs[8], vecs[9]);
COEX<vtype>(vecs[12], vecs[14]);
COEX<vtype>(vecs[13], vecs[15]);
COEX<vtype>(vecs[1], vecs[2]);
COEX<vtype>(vecs[3], vecs[12]);
COEX<vtype>(vecs[4], vecs[6]);
COEX<vtype>(vecs[5], vecs[7]);
COEX<vtype>(vecs[8], vecs[10]);
COEX<vtype>(vecs[9], vecs[11]);
COEX<vtype>(vecs[13], vecs[14]);
COEX<vtype>(vecs[1], vecs[4]);
COEX<vtype>(vecs[2], vecs[6]);
COEX<vtype>(vecs[5], vecs[8]);
COEX<vtype>(vecs[7], vecs[10]);
COEX<vtype>(vecs[9], vecs[13]);
COEX<vtype>(vecs[11], vecs[14]);
COEX<vtype>(vecs[2], vecs[4]);
COEX<vtype>(vecs[3], vecs[6]);
COEX<vtype>(vecs[9], vecs[12]);
COEX<vtype>(vecs[11], vecs[13]);
COEX<vtype>(vecs[3], vecs[5]);
COEX<vtype>(vecs[6], vecs[8]);
COEX<vtype>(vecs[7], vecs[9]);
COEX<vtype>(vecs[10], vecs[12]);
COEX<vtype>(vecs[3], vecs[4]);
COEX<vtype>(vecs[5], vecs[6]);
COEX<vtype>(vecs[7], vecs[8]);
COEX<vtype>(vecs[9], vecs[10]);
COEX<vtype>(vecs[11], vecs[12]);
COEX<vtype>(vecs[6], vecs[7]);
COEX<vtype>(vecs[8], vecs[9]);
}
template <typename vtype, typename reg_t = typename vtype::reg_t>
X86_SIMD_SORT_FINLINE void optimal_sort_32(reg_t *vecs) {
COEX<vtype>(vecs[0], vecs[1]);
COEX<vtype>(vecs[2], vecs[3]);
COEX<vtype>(vecs[4], vecs[5]);
COEX<vtype>(vecs[6], vecs[7]);
COEX<vtype>(vecs[8], vecs[9]);
COEX<vtype>(vecs[10], vecs[11]);
COEX<vtype>(vecs[12], vecs[13]);
COEX<vtype>(vecs[14], vecs[15]);
COEX<vtype>(vecs[16], vecs[17]);
COEX<vtype>(vecs[18], vecs[19]);
COEX<vtype>(vecs[20], vecs[21]);
COEX<vtype>(vecs[22], vecs[23]);
COEX<vtype>(vecs[24], vecs[25]);
COEX<vtype>(vecs[26], vecs[27]);
COEX<vtype>(vecs[28], vecs[29]);
COEX<vtype>(vecs[30], vecs[31]);
COEX<vtype>(vecs[0], vecs[2]);
COEX<vtype>(vecs[1], vecs[3]);
COEX<vtype>(vecs[4], vecs[6]);
COEX<vtype>(vecs[5], vecs[7]);
COEX<vtype>(vecs[8], vecs[10]);
COEX<vtype>(vecs[9], vecs[11]);
COEX<vtype>(vecs[12], vecs[14]);
COEX<vtype>(vecs[13], vecs[15]);
COEX<vtype>(vecs[16], vecs[18]);
COEX<vtype>(vecs[17], vecs[19]);
COEX<vtype>(vecs[20], vecs[22]);
COEX<vtype>(vecs[21], vecs[23]);
COEX<vtype>(vecs[24], vecs[26]);
COEX<vtype>(vecs[25], vecs[27]);
COEX<vtype>(vecs[28], vecs[30]);
COEX<vtype>(vecs[29], vecs[31]);
COEX<vtype>(vecs[0], vecs[4]);
COEX<vtype>(vecs[1], vecs[5]);
COEX<vtype>(vecs[2], vecs[6]);
COEX<vtype>(vecs[3], vecs[7]);
COEX<vtype>(vecs[8], vecs[12]);
COEX<vtype>(vecs[9], vecs[13]);
COEX<vtype>(vecs[10], vecs[14]);
COEX<vtype>(vecs[11], vecs[15]);
COEX<vtype>(vecs[16], vecs[20]);
COEX<vtype>(vecs[17], vecs[21]);
COEX<vtype>(vecs[18], vecs[22]);
COEX<vtype>(vecs[19], vecs[23]);
COEX<vtype>(vecs[24], vecs[28]);
COEX<vtype>(vecs[25], vecs[29]);
COEX<vtype>(vecs[26], vecs[30]);
COEX<vtype>(vecs[27], vecs[31]);
COEX<vtype>(vecs[0], vecs[8]);
COEX<vtype>(vecs[1], vecs[9]);
COEX<vtype>(vecs[2], vecs[10]);
COEX<vtype>(vecs[3], vecs[11]);
COEX<vtype>(vecs[4], vecs[12]);
COEX<vtype>(vecs[5], vecs[13]);
COEX<vtype>(vecs[6], vecs[14]);
COEX<vtype>(vecs[7], vecs[15]);
COEX<vtype>(vecs[16], vecs[24]);
COEX<vtype>(vecs[17], vecs[25]);
COEX<vtype>(vecs[18], vecs[26]);
COEX<vtype>(vecs[19], vecs[27]);
COEX<vtype>(vecs[20], vecs[28]);
COEX<vtype>(vecs[21], vecs[29]);
COEX<vtype>(vecs[22], vecs[30]);
COEX<vtype>(vecs[23], vecs[31]);
COEX<vtype>(vecs[0], vecs[16]);
COEX<vtype>(vecs[1], vecs[8]);
COEX<vtype>(vecs[2], vecs[4]);
COEX<vtype>(vecs[3], vecs[12]);
COEX<vtype>(vecs[5], vecs[10]);
COEX<vtype>(vecs[6], vecs[9]);
COEX<vtype>(vecs[7], vecs[14]);
COEX<vtype>(vecs[11], vecs[13]);
COEX<vtype>(vecs[15], vecs[31]);
COEX<vtype>(vecs[17], vecs[24]);
COEX<vtype>(vecs[18], vecs[20]);
COEX<vtype>(vecs[19], vecs[28]);
COEX<vtype>(vecs[21], vecs[26]);
COEX<vtype>(vecs[22], vecs[25]);
COEX<vtype>(vecs[23], vecs[30]);
COEX<vtype>(vecs[27], vecs[29]);
COEX<vtype>(vecs[1], vecs[2]);
COEX<vtype>(vecs[3], vecs[5]);
COEX<vtype>(vecs[4], vecs[8]);
COEX<vtype>(vecs[6], vecs[22]);
COEX<vtype>(vecs[7], vecs[11]);
COEX<vtype>(vecs[9], vecs[25]);
COEX<vtype>(vecs[10], vecs[12]);
COEX<vtype>(vecs[13], vecs[14]);
COEX<vtype>(vecs[17], vecs[18]);
COEX<vtype>(vecs[19], vecs[21]);
COEX<vtype>(vecs[20], vecs[24]);
COEX<vtype>(vecs[23], vecs[27]);
COEX<vtype>(vecs[26], vecs[28]);
COEX<vtype>(vecs[29], vecs[30]);
COEX<vtype>(vecs[1], vecs[17]);
COEX<vtype>(vecs[2], vecs[18]);
COEX<vtype>(vecs[3], vecs[19]);
COEX<vtype>(vecs[4], vecs[20]);
COEX<vtype>(vecs[5], vecs[10]);
COEX<vtype>(vecs[7], vecs[23]);
COEX<vtype>(vecs[8], vecs[24]);
COEX<vtype>(vecs[11], vecs[27]);
COEX<vtype>(vecs[12], vecs[28]);
COEX<vtype>(vecs[13], vecs[29]);
COEX<vtype>(vecs[14], vecs[30]);
COEX<vtype>(vecs[21], vecs[26]);
COEX<vtype>(vecs[3], vecs[17]);
COEX<vtype>(vecs[4], vecs[16]);
COEX<vtype>(vecs[5], vecs[21]);
COEX<vtype>(vecs[6], vecs[18]);
COEX<vtype>(vecs[7], vecs[9]);
COEX<vtype>(vecs[8], vecs[20]);
COEX<vtype>(vecs[10], vecs[26]);
COEX<vtype>(vecs[11], vecs[23]);
COEX<vtype>(vecs[13], vecs[25]);
COEX<vtype>(vecs[14], vecs[28]);
COEX<vtype>(vecs[15], vecs[27]);
COEX<vtype>(vecs[22], vecs[24]);
COEX<vtype>(vecs[1], vecs[4]);
COEX<vtype>(vecs[3], vecs[8]);
COEX<vtype>(vecs[5], vecs[16]);
COEX<vtype>(vecs[7], vecs[17]);
COEX<vtype>(vecs[9], vecs[21]);
COEX<vtype>(vecs[10], vecs[22]);
COEX<vtype>(vecs[11], vecs[19]);
COEX<vtype>(vecs[12], vecs[20]);
COEX<vtype>(vecs[14], vecs[24]);
COEX<vtype>(vecs[15], vecs[26]);
COEX<vtype>(vecs[23], vecs[28]);
COEX<vtype>(vecs[27], vecs[30]);
COEX<vtype>(vecs[2], vecs[5]);
COEX<vtype>(vecs[7], vecs[8]);
COEX<vtype>(vecs[9], vecs[18]);
COEX<vtype>(vecs[11], vecs[17]);
COEX<vtype>(vecs[12], vecs[16]);
COEX<vtype>(vecs[13], vecs[22]);
COEX<vtype>(vecs[14], vecs[20]);
COEX<vtype>(vecs[15], vecs[19]);
COEX<vtype>(vecs[23], vecs[24]);
COEX<vtype>(vecs[26], vecs[29]);
COEX<vtype>(vecs[2], vecs[4]);
COEX<vtype>(vecs[6], vecs[12]);
COEX<vtype>(vecs[9], vecs[16]);
COEX<vtype>(vecs[10], vecs[11]);
COEX<vtype>(vecs[13], vecs[17]);
COEX<vtype>(vecs[14], vecs[18]);
COEX<vtype>(vecs[15], vecs[22]);
COEX<vtype>(vecs[19], vecs[25]);
COEX<vtype>(vecs[20], vecs[21]);
COEX<vtype>(vecs[27], vecs[29]);
COEX<vtype>(vecs[5], vecs[6]);
COEX<vtype>(vecs[8], vecs[12]);
COEX<vtype>(vecs[9], vecs[10]);
COEX<vtype>(vecs[11], vecs[13]);
COEX<vtype>(vecs[14], vecs[16]);
COEX<vtype>(vecs[15], vecs[17]);
COEX<vtype>(vecs[18], vecs[20]);
COEX<vtype>(vecs[19], vecs[23]);
COEX<vtype>(vecs[21], vecs[22]);
COEX<vtype>(vecs[25], vecs[26]);
COEX<vtype>(vecs[3], vecs[5]);
COEX<vtype>(vecs[6], vecs[7]);
COEX<vtype>(vecs[8], vecs[9]);
COEX<vtype>(vecs[10], vecs[12]);
COEX<vtype>(vecs[11], vecs[14]);
COEX<vtype>(vecs[13], vecs[16]);
COEX<vtype>(vecs[15], vecs[18]);
COEX<vtype>(vecs[17], vecs[20]);
COEX<vtype>(vecs[19], vecs[21]);
COEX<vtype>(vecs[22], vecs[23]);
COEX<vtype>(vecs[24], vecs[25]);
COEX<vtype>(vecs[26], vecs[28]);
COEX<vtype>(vecs[3], vecs[4]);
COEX<vtype>(vecs[5], vecs[6]);
COEX<vtype>(vecs[7], vecs[8]);
COEX<vtype>(vecs[9], vecs[10]);
COEX<vtype>(vecs[11], vecs[12]);
COEX<vtype>(vecs[13], vecs[14]);
COEX<vtype>(vecs[15], vecs[16]);
COEX<vtype>(vecs[17], vecs[18]);
COEX<vtype>(vecs[19], vecs[20]);
COEX<vtype>(vecs[21], vecs[22]);
COEX<vtype>(vecs[23], vecs[24]);
COEX<vtype>(vecs[25], vecs[26]);
COEX<vtype>(vecs[27], vecs[28]);
}

View file

@ -0,0 +1,88 @@
/*
* Copyright (c) 2021, 2023, Intel Corporation. All rights reserved.
* Copyright (c) 2021 Serge Sans Paille. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
// This implementation is based on x86-simd-sort(https://github.com/intel/x86-simd-sort)
template <typename vtype, typename mm_t>
X86_SIMD_SORT_INLINE void COEX(mm_t &a, mm_t &b);
template <typename vtype, typename type_t>
X86_SIMD_SORT_INLINE type_t get_pivot(type_t *arr, const arrsize_t left,
const arrsize_t right) {
using reg_t = typename vtype::reg_t;
type_t samples[vtype::numlanes];
arrsize_t delta = (right - left) / vtype::numlanes;
for (int i = 0; i < vtype::numlanes; i++) {
samples[i] = arr[left + i * delta];
}
reg_t rand_vec = vtype::loadu(samples);
reg_t sort = vtype::sort_vec(rand_vec);
return ((type_t *)&sort)[vtype::numlanes / 2];
}
template <typename vtype, typename type_t>
X86_SIMD_SORT_INLINE type_t get_pivot_blocks(type_t *arr, const arrsize_t left,
const arrsize_t right) {
if (right - left <= 1024) {
return get_pivot<vtype>(arr, left, right);
}
using reg_t = typename vtype::reg_t;
constexpr int numVecs = 5;
arrsize_t width = (right - vtype::numlanes) - left;
arrsize_t delta = width / numVecs;
reg_t vecs[numVecs];
// Load data
for (int i = 0; i < numVecs; i++) {
vecs[i] = vtype::loadu(arr + left + delta * i);
}
// Implement sorting network (from
// https://bertdobbelaere.github.io/sorting_networks.html)
COEX<vtype>(vecs[0], vecs[3]);
COEX<vtype>(vecs[1], vecs[4]);
COEX<vtype>(vecs[0], vecs[2]);
COEX<vtype>(vecs[1], vecs[3]);
COEX<vtype>(vecs[0], vecs[1]);
COEX<vtype>(vecs[2], vecs[4]);
COEX<vtype>(vecs[1], vecs[2]);
COEX<vtype>(vecs[3], vecs[4]);
COEX<vtype>(vecs[2], vecs[3]);
// Calculate median of the middle vector
reg_t &vec = vecs[numVecs / 2];
vec = vtype::sort_vec(vec);
type_t data[vtype::numlanes];
vtype::storeu(data, vec);
return data[vtype::numlanes / 2];
}

View file

@ -0,0 +1,88 @@
/*
* Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package apple.security;
import java.security.*;
import static sun.security.util.SecurityConstants.PROVIDER_VER;
/*
* The Apple Security Provider.
*/
/**
* Defines the Apple provider.
*
* This provider only exists to provide access to the Apple keychain-based KeyStore implementation
*/
@SuppressWarnings("serial") // JDK implementation class
public final class AppleProvider extends Provider {
private static final String info = "Apple Provider";
private static final class ProviderService extends Provider.Service {
ProviderService(Provider p, String type, String algo, String cn) {
super(p, type, algo, cn, null, null);
}
@Override
public Object newInstance(Object ctrParamObj)
throws NoSuchAlgorithmException {
String type = getType();
if (ctrParamObj != null) {
throw new InvalidParameterException
("constructorParameter not used with " + type + " engines");
}
String algo = getAlgorithm();
try {
if (type.equals("KeyStore")) {
if (algo.equals("KeychainStore")) {
return new KeychainStore.USER();
} else if (algo.equals("KeychainStore-ROOT")) {
return new KeychainStore.ROOT();
}
}
} catch (Exception ex) {
throw new NoSuchAlgorithmException("Error constructing " +
type + " for " + algo + " using Apple", ex);
}
throw new ProviderException("No impl for " + algo +
" " + type);
}
}
public AppleProvider() {
/* We are the Apple provider */
super("Apple", PROVIDER_VER, info);
final Provider p = this;
putService(new ProviderService(p, "KeyStore",
"KeychainStore", "apple.security.KeychainStore$USER"));
putService(new ProviderService(p, "KeyStore",
"KeychainStore-ROOT", "apple.security.KeychainStore$ROOT"));
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,155 @@
/*
* Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.net;
import java.util.Enumeration;
import java.io.IOException;
/**
* Choose a network interface to be the default for
* outgoing IPv6 traffic that does not specify a scope_id (and which needs one).
* We choose the first interface that is up and is (in order of preference):
* 1. neither loopback nor point to point
* 2. point to point
* 3. loopback
* 4. none.
* Platforms that do not require a default interface implement a dummy
* that returns null.
*/
class DefaultInterface {
private static final NetworkInterface defaultInterface =
chooseDefaultInterface();
static NetworkInterface getDefault() {
return defaultInterface;
}
/**
* Choose a default interface. This method returns the first interface that
* is both "up" and supports multicast. This method chooses an interface in
* order of preference, using the following algorithm:
*
* <pre>
* Interfaces that are down, or don't support multicasting, are skipped.
* In steps 1-4 below, PPP and loopback interfaces are skipped.
*
* 1. The first interface that has at least an IPv4 address, and an IPv6 address,
* and a non link-local IP address, is picked.
*
* 2. If none is found, then the first interface that has at least an
* IPv4 address, and an IPv6 address is picked.
*
* 3. If none is found, then the first interface that has at least a
* non link local IP address is picked.
*
* 4. If none is found, then the first non loopback and non PPP interface
* is picked.
*
* 5. If none is found then first PPP interface is picked.
*
* 6. If none is found, then the first loopback interface is picked.
*
* 7. If none is found, then null is returned.
* </pre>
*
* @return the chosen interface or {@code null} if there isn't a suitable
* default
*/
private static NetworkInterface chooseDefaultInterface() {
Enumeration<NetworkInterface> nifs;
try {
nifs = NetworkInterface.getNetworkInterfaces();
} catch (IOException ignore) {
// unable to enumerate network interfaces
return null;
}
NetworkInterface preferred = null;
NetworkInterface dual = null;
NetworkInterface nonLinkLocal = null;
NetworkInterface ppp = null;
NetworkInterface loopback = null;
while (nifs.hasMoreElements()) {
NetworkInterface ni = nifs.nextElement();
try {
if (!ni.isUp() || !ni.supportsMulticast())
continue;
boolean ip4 = false, ip6 = false, isNonLinkLocal = false;
Enumeration<InetAddress> addrs = ni.getInetAddresses();
while (addrs.hasMoreElements()) {
InetAddress addr = addrs.nextElement();
if (!addr.isAnyLocalAddress()) {
if (addr instanceof Inet4Address) {
ip4 = true;
} else if (addr instanceof Inet6Address) {
ip6 = true;
}
if (!addr.isLinkLocalAddress()) {
isNonLinkLocal = true;
}
}
}
boolean isLoopback = ni.isLoopback();
boolean isPPP = ni.isPointToPoint();
if (!isLoopback && !isPPP) {
// found an interface that is not the loopback or a
// point-to-point interface
if (preferred == null) {
preferred = ni;
}
if (ip4 && ip6) {
if (isNonLinkLocal) return ni;
if (dual == null) dual = ni;
}
if (nonLinkLocal == null) {
if (isNonLinkLocal) nonLinkLocal = ni;
}
}
if (ppp == null && isPPP)
ppp = ni;
if (loopback == null && isLoopback)
loopback = ni;
} catch (IOException skip) { }
}
if (dual != null) {
return dual;
} else if (nonLinkLocal != null) {
return nonLinkLocal;
} else if (preferred != null) {
return preferred;
} else {
return (ppp != null) ? ppp : loopback;
}
}
}

View file

@ -0,0 +1,85 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// Generated by jextract
package jdk.internal.ffi.generated.errno;
import java.lang.foreign.*;
import java.lang.invoke.*;
import java.util.*;
import java.util.stream.*;
import static java.lang.foreign.ValueLayout.*;
@SuppressWarnings("restricted")
public class errno_h$shared {
errno_h$shared() {
// Should not be called directly
}
public static final OfBoolean C_BOOL = (OfBoolean) Linker.nativeLinker().canonicalLayouts().get("bool");
public static final OfByte C_CHAR =(OfByte)Linker.nativeLinker().canonicalLayouts().get("char");
public static final OfShort C_SHORT = (OfShort) Linker.nativeLinker().canonicalLayouts().get("short");
public static final OfInt C_INT = (OfInt) Linker.nativeLinker().canonicalLayouts().get("int");
public static final OfLong C_LONG_LONG = (OfLong) Linker.nativeLinker().canonicalLayouts().get("long long");
public static final OfFloat C_FLOAT = (OfFloat) Linker.nativeLinker().canonicalLayouts().get("float");
public static final OfDouble C_DOUBLE = (OfDouble) Linker.nativeLinker().canonicalLayouts().get("double");
public static final AddressLayout C_POINTER = ((AddressLayout) Linker.nativeLinker().canonicalLayouts().get("void*"))
.withTargetLayout(MemoryLayout.sequenceLayout(Long.MAX_VALUE, C_CHAR));
public static final OfLong C_LONG = (OfLong) Linker.nativeLinker().canonicalLayouts().get("long");
static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls");
static void traceDowncall(String name, Object... args) {
String traceArgs = Arrays.stream(args)
.map(Object::toString)
.collect(Collectors.joining(", "));
System.out.printf("%s(%s)\n", name, traceArgs);
}
static MethodHandle upcallHandle(Class<?> fi, String name, FunctionDescriptor fdesc) {
try {
return MethodHandles.lookup().findVirtual(fi, name, fdesc.toMethodType());
} catch (ReflectiveOperationException ex) {
throw new AssertionError(ex);
}
}
static MemoryLayout align(MemoryLayout layout, long align) {
return switch (layout) {
case PaddingLayout p -> p;
case ValueLayout v -> v.withByteAlignment(align);
case GroupLayout g -> {
MemoryLayout[] alignedMembers = g.memberLayouts().stream()
.map(m -> align(m, align)).toArray(MemoryLayout[]::new);
yield g instanceof StructLayout ?
MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers);
}
case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align));
};
}
}

View file

@ -0,0 +1,116 @@
/*
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// Generated by jextract
package jdk.internal.ffi.generated.errno;
import java.lang.foreign.*;
import java.lang.invoke.*;
@SuppressWarnings("restricted")
public class errno_h extends errno_h$shared {
errno_h() {
// Should not be called directly
}
static final Arena LIBRARY_ARENA = Arena.ofAuto();
static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.loaderLookup()
.or(Linker.nativeLinker().defaultLookup());
private static final int EINTR = (int)4L;
/**
* {@snippet lang=c :
* #define EINTR 4
* }
*/
public static int EINTR() {
return EINTR;
}
private static class strerror_r {
public static final FunctionDescriptor DESC = FunctionDescriptor.of(
C_INT,
C_INT,
C_POINTER,
C_LONG
);
public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("strerror_r");
public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC);
}
/**
* Function descriptor for:
* {@snippet lang=c :
* int strerror_r(int __errnum, char *__strerrbuf, size_t __buflen)
* }
*/
public static FunctionDescriptor strerror_r$descriptor() {
return strerror_r.DESC;
}
/**
* Downcall method handle for:
* {@snippet lang=c :
* int strerror_r(int __errnum, char *__strerrbuf, size_t __buflen)
* }
*/
public static MethodHandle strerror_r$handle() {
return strerror_r.HANDLE;
}
/**
* Address for:
* {@snippet lang=c :
* int strerror_r(int __errnum, char *__strerrbuf, size_t __buflen)
* }
*/
public static MemorySegment strerror_r$address() {
return strerror_r.ADDR;
}
/**
* {@snippet lang=c :
* int strerror_r(int __errnum, char *__strerrbuf, size_t __buflen)
* }
*/
public static int strerror_r(int __errnum, MemorySegment __strerrbuf, long __buflen) {
var mh$ = strerror_r.HANDLE;
try {
if (TRACE_DOWNCALLS) {
traceDowncall("strerror_r", __errnum, __strerrbuf, __buflen);
}
return (int)mh$.invokeExact(__errnum, __strerrbuf, __buflen);
} catch (Error | RuntimeException ex) {
throw ex;
} catch (Throwable ex$) {
throw new AssertionError("should not reach here", ex$);
}
}
}

View file

@ -0,0 +1,43 @@
/*
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* Defines native structures for errno APIs.
* Generated with the following jextract command:
* {@snippet lang = "Shell Script":
*
* HEADER_NAME=errno.h
* echo "#include <errno.h>" > $HEADER_NAME
* echo "#include <string.h>" >> $HEADER_NAME
*
* jextract --target-package jdk.internal.ffi.generated.errno \
* --include-constant EINTR \
* --include-function strerror_r \
* $HEADER_NAME
* }
*
*/
package jdk.internal.ffi.generated.errno;

View file

@ -0,0 +1,378 @@
/*
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// Generated by jextract
package jdk.internal.ffi.generated.kqueue;
import java.lang.foreign.*;
import java.util.function.*;
import static java.lang.foreign.MemoryLayout.PathElement.*;
import static java.lang.foreign.ValueLayout.*;
/**
* {@snippet lang=c :
* struct kevent {
* uintptr_t ident;
* int16_t filter;
* uint16_t flags;
* uint32_t fflags;
* intptr_t data;
* void *udata;
* }
* }
*/
@SuppressWarnings("restricted")
public class kevent {
kevent() {
// Should not be called directly
}
private static final GroupLayout $LAYOUT = MemoryLayout.structLayout(
kqueue_h.align(kqueue_h.C_LONG, 4).withName("ident"),
kqueue_h.C_SHORT.withName("filter"),
kqueue_h.C_SHORT.withName("flags"),
kqueue_h.C_INT.withName("fflags"),
kqueue_h.align(kqueue_h.C_LONG, 4).withName("data"),
kqueue_h.align(kqueue_h.C_POINTER, 4).withName("udata")
).withName("kevent");
/**
* The layout of this struct
*/
public static final GroupLayout layout() {
return $LAYOUT;
}
private static final OfLong ident$LAYOUT = (OfLong)$LAYOUT.select(groupElement("ident"));
/**
* Layout for field:
* {@snippet lang=c :
* uintptr_t ident
* }
*/
public static final OfLong ident$layout() {
return ident$LAYOUT;
}
private static final long ident$OFFSET = $LAYOUT.byteOffset(groupElement("ident"));
/**
* Offset for field:
* {@snippet lang=c :
* uintptr_t ident
* }
*/
public static final long ident$offset() {
return ident$OFFSET;
}
/**
* Getter for field:
* {@snippet lang=c :
* uintptr_t ident
* }
*/
public static long ident(MemorySegment struct) {
return struct.get(ident$LAYOUT, ident$OFFSET);
}
/**
* Setter for field:
* {@snippet lang=c :
* uintptr_t ident
* }
*/
public static void ident(MemorySegment struct, long fieldValue) {
struct.set(ident$LAYOUT, ident$OFFSET, fieldValue);
}
private static final OfShort filter$LAYOUT = (OfShort)$LAYOUT.select(groupElement("filter"));
/**
* Layout for field:
* {@snippet lang=c :
* int16_t filter
* }
*/
public static final OfShort filter$layout() {
return filter$LAYOUT;
}
private static final long filter$OFFSET = $LAYOUT.byteOffset(groupElement("filter"));
/**
* Offset for field:
* {@snippet lang=c :
* int16_t filter
* }
*/
public static final long filter$offset() {
return filter$OFFSET;
}
/**
* Getter for field:
* {@snippet lang=c :
* int16_t filter
* }
*/
public static short filter(MemorySegment struct) {
return struct.get(filter$LAYOUT, filter$OFFSET);
}
/**
* Setter for field:
* {@snippet lang=c :
* int16_t filter
* }
*/
public static void filter(MemorySegment struct, short fieldValue) {
struct.set(filter$LAYOUT, filter$OFFSET, fieldValue);
}
private static final OfShort flags$LAYOUT = (OfShort)$LAYOUT.select(groupElement("flags"));
/**
* Layout for field:
* {@snippet lang=c :
* uint16_t flags
* }
*/
public static final OfShort flags$layout() {
return flags$LAYOUT;
}
private static final long flags$OFFSET = $LAYOUT.byteOffset(groupElement("flags"));
/**
* Offset for field:
* {@snippet lang=c :
* uint16_t flags
* }
*/
public static final long flags$offset() {
return flags$OFFSET;
}
/**
* Getter for field:
* {@snippet lang=c :
* uint16_t flags
* }
*/
public static short flags(MemorySegment struct) {
return struct.get(flags$LAYOUT, flags$OFFSET);
}
/**
* Setter for field:
* {@snippet lang=c :
* uint16_t flags
* }
*/
public static void flags(MemorySegment struct, short fieldValue) {
struct.set(flags$LAYOUT, flags$OFFSET, fieldValue);
}
private static final OfInt fflags$LAYOUT = (OfInt)$LAYOUT.select(groupElement("fflags"));
/**
* Layout for field:
* {@snippet lang=c :
* uint32_t fflags
* }
*/
public static final OfInt fflags$layout() {
return fflags$LAYOUT;
}
private static final long fflags$OFFSET = $LAYOUT.byteOffset(groupElement("fflags"));
/**
* Offset for field:
* {@snippet lang=c :
* uint32_t fflags
* }
*/
public static final long fflags$offset() {
return fflags$OFFSET;
}
/**
* Getter for field:
* {@snippet lang=c :
* uint32_t fflags
* }
*/
public static int fflags(MemorySegment struct) {
return struct.get(fflags$LAYOUT, fflags$OFFSET);
}
/**
* Setter for field:
* {@snippet lang=c :
* uint32_t fflags
* }
*/
public static void fflags(MemorySegment struct, int fieldValue) {
struct.set(fflags$LAYOUT, fflags$OFFSET, fieldValue);
}
private static final OfLong data$LAYOUT = (OfLong)$LAYOUT.select(groupElement("data"));
/**
* Layout for field:
* {@snippet lang=c :
* intptr_t data
* }
*/
public static final OfLong data$layout() {
return data$LAYOUT;
}
private static final long data$OFFSET = $LAYOUT.byteOffset(groupElement("data"));
/**
* Offset for field:
* {@snippet lang=c :
* intptr_t data
* }
*/
public static final long data$offset() {
return data$OFFSET;
}
/**
* Getter for field:
* {@snippet lang=c :
* intptr_t data
* }
*/
public static long data(MemorySegment struct) {
return struct.get(data$LAYOUT, data$OFFSET);
}
/**
* Setter for field:
* {@snippet lang=c :
* intptr_t data
* }
*/
public static void data(MemorySegment struct, long fieldValue) {
struct.set(data$LAYOUT, data$OFFSET, fieldValue);
}
private static final AddressLayout udata$LAYOUT = (AddressLayout)$LAYOUT.select(groupElement("udata"));
/**
* Layout for field:
* {@snippet lang=c :
* void *udata
* }
*/
public static final AddressLayout udata$layout() {
return udata$LAYOUT;
}
private static final long udata$OFFSET = $LAYOUT.byteOffset(groupElement("udata"));
/**
* Offset for field:
* {@snippet lang=c :
* void *udata
* }
*/
public static final long udata$offset() {
return udata$OFFSET;
}
/**
* Getter for field:
* {@snippet lang=c :
* void *udata
* }
*/
public static MemorySegment udata(MemorySegment struct) {
return struct.get(udata$LAYOUT, udata$OFFSET);
}
/**
* Setter for field:
* {@snippet lang=c :
* void *udata
* }
*/
public static void udata(MemorySegment struct, MemorySegment fieldValue) {
struct.set(udata$LAYOUT, udata$OFFSET, fieldValue);
}
/**
* Obtains a slice of {@code arrayParam} which selects the array element at {@code index}.
* The returned segment has address {@code arrayParam.address() + index * layout().byteSize()}
*/
public static MemorySegment asSlice(MemorySegment array, long index) {
return array.asSlice(layout().byteSize() * index);
}
/**
* The size (in bytes) of this struct
*/
public static long sizeof() { return layout().byteSize(); }
/**
* Allocate a segment of size {@code layout().byteSize()} using {@code allocator}
*/
public static MemorySegment allocate(SegmentAllocator allocator) {
return allocator.allocate(layout());
}
/**
* Allocate an array of size {@code elementCount} using {@code allocator}.
* The returned segment has size {@code elementCount * layout().byteSize()}.
*/
public static MemorySegment allocateArray(long elementCount, SegmentAllocator allocator) {
return allocator.allocate(MemoryLayout.sequenceLayout(elementCount, layout()));
}
/**
* Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any).
* The returned segment has size {@code layout().byteSize()}
*/
public static MemorySegment reinterpret(MemorySegment addr, Arena arena, Consumer<MemorySegment> cleanup) {
return reinterpret(addr, 1, arena, cleanup);
}
/**
* Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any).
* The returned segment has size {@code elementCount * layout().byteSize()}
*/
public static MemorySegment reinterpret(MemorySegment addr, long elementCount, Arena arena, Consumer<MemorySegment> cleanup) {
return addr.reinterpret(layout().byteSize() * elementCount, arena, cleanup);
}
}

View file

@ -0,0 +1,85 @@
/*
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// Generated by jextract
package jdk.internal.ffi.generated.kqueue;
import java.lang.foreign.*;
import java.lang.invoke.*;
import java.util.*;
import java.util.stream.*;
import static java.lang.foreign.ValueLayout.*;
@SuppressWarnings("restricted")
public class kqueue_h$shared {
kqueue_h$shared() {
// Should not be called directly
}
public static final OfBoolean C_BOOL = (OfBoolean) Linker.nativeLinker().canonicalLayouts().get("bool");
public static final OfByte C_CHAR =(OfByte)Linker.nativeLinker().canonicalLayouts().get("char");
public static final OfShort C_SHORT = (OfShort) Linker.nativeLinker().canonicalLayouts().get("short");
public static final OfInt C_INT = (OfInt) Linker.nativeLinker().canonicalLayouts().get("int");
public static final OfLong C_LONG_LONG = (OfLong) Linker.nativeLinker().canonicalLayouts().get("long long");
public static final OfFloat C_FLOAT = (OfFloat) Linker.nativeLinker().canonicalLayouts().get("float");
public static final OfDouble C_DOUBLE = (OfDouble) Linker.nativeLinker().canonicalLayouts().get("double");
public static final AddressLayout C_POINTER = ((AddressLayout) Linker.nativeLinker().canonicalLayouts().get("void*"))
.withTargetLayout(MemoryLayout.sequenceLayout(Long.MAX_VALUE, C_CHAR));
public static final OfLong C_LONG = (OfLong) Linker.nativeLinker().canonicalLayouts().get("long");
static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls");
static void traceDowncall(String name, Object... args) {
String traceArgs = Arrays.stream(args)
.map(Object::toString)
.collect(Collectors.joining(", "));
System.out.printf("%s(%s)\n", name, traceArgs);
}
static MethodHandle upcallHandle(Class<?> fi, String name, FunctionDescriptor fdesc) {
try {
return MethodHandles.lookup().findVirtual(fi, name, fdesc.toMethodType());
} catch (ReflectiveOperationException ex) {
throw new AssertionError(ex);
}
}
static MemoryLayout align(MemoryLayout layout, long align) {
return switch (layout) {
case PaddingLayout p -> p;
case ValueLayout v -> v.withByteAlignment(align);
case GroupLayout g -> {
MemoryLayout[] alignedMembers = g.memberLayouts().stream()
.map(m -> align(m, align)).toArray(MemoryLayout[]::new);
yield g instanceof StructLayout ?
MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers);
}
case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align));
};
}
}

View file

@ -0,0 +1,222 @@
/*
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// Generated by jextract
package jdk.internal.ffi.generated.kqueue;
import java.lang.foreign.*;
import java.lang.invoke.*;
@SuppressWarnings("restricted")
public class kqueue_h extends kqueue_h$shared {
kqueue_h() {
// Should not be called directly
}
static final Arena LIBRARY_ARENA = Arena.ofAuto();
static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.loaderLookup()
.or(Linker.nativeLinker().defaultLookup());
private static final int EV_ADD = (int)1L;
/**
* {@snippet lang=c :
* #define EV_ADD 1
* }
*/
public static int EV_ADD() {
return EV_ADD;
}
private static final int EV_DELETE = (int)2L;
/**
* {@snippet lang=c :
* #define EV_DELETE 2
* }
*/
public static int EV_DELETE() {
return EV_DELETE;
}
private static final int EV_ONESHOT = (int)16L;
/**
* {@snippet lang=c :
* #define EV_ONESHOT 16
* }
*/
public static int EV_ONESHOT() {
return EV_ONESHOT;
}
private static final int EV_CLEAR = (int)32L;
/**
* {@snippet lang=c :
* #define EV_CLEAR 32
* }
*/
public static int EV_CLEAR() {
return EV_CLEAR;
}
private static class kqueue {
public static final FunctionDescriptor DESC = FunctionDescriptor.of(
kqueue_h.C_INT );
public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("kqueue");
public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC);
}
/**
* Function descriptor for:
* {@snippet lang=c :
* int kqueue()
* }
*/
public static FunctionDescriptor kqueue$descriptor() {
return kqueue.DESC;
}
/**
* Downcall method handle for:
* {@snippet lang=c :
* int kqueue()
* }
*/
public static MethodHandle kqueue$handle() {
return kqueue.HANDLE;
}
/**
* Address for:
* {@snippet lang=c :
* int kqueue()
* }
*/
public static MemorySegment kqueue$address() {
return kqueue.ADDR;
}
/**
* {@snippet lang=c :
* int kqueue()
* }
*/
public static int kqueue() {
var mh$ = kqueue.HANDLE;
try {
if (TRACE_DOWNCALLS) {
traceDowncall("kqueue");
}
return (int)mh$.invokeExact();
} catch (Error | RuntimeException ex) {
throw ex;
} catch (Throwable ex$) {
throw new AssertionError("should not reach here", ex$);
}
}
private static class kevent {
public static final FunctionDescriptor DESC = FunctionDescriptor.of(
kqueue_h.C_INT,
kqueue_h.C_INT,
kqueue_h.C_POINTER,
kqueue_h.C_INT,
kqueue_h.C_POINTER,
kqueue_h.C_INT,
kqueue_h.C_POINTER
);
public static final MemorySegment ADDR = SYMBOL_LOOKUP.findOrThrow("kevent");
public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC);
}
/**
* Function descriptor for:
* {@snippet lang=c :
* int kevent(int kq, const struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout)
* }
*/
public static FunctionDescriptor kevent$descriptor() {
return kevent.DESC;
}
/**
* Downcall method handle for:
* {@snippet lang=c :
* int kevent(int kq, const struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout)
* }
*/
public static MethodHandle kevent$handle() {
return kevent.HANDLE;
}
/**
* Address for:
* {@snippet lang=c :
* int kevent(int kq, const struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout)
* }
*/
public static MemorySegment kevent$address() {
return kevent.ADDR;
}
/**
* {@snippet lang=c :
* int kevent(int kq, const struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout)
* }
*/
public static int kevent(int kq, MemorySegment changelist, int nchanges, MemorySegment eventlist, int nevents, MemorySegment timeout) {
var mh$ = kevent.HANDLE;
try {
if (TRACE_DOWNCALLS) {
traceDowncall("kevent", kq, changelist, nchanges, eventlist, nevents, timeout);
}
return (int)mh$.invokeExact(kq, changelist, nchanges, eventlist, nevents, timeout);
} catch (Error | RuntimeException ex) {
throw ex;
} catch (Throwable ex$) {
throw new AssertionError("should not reach here", ex$);
}
}
private static final int EVFILT_READ = (int)-1L;
/**
* {@snippet lang=c :
* #define EVFILT_READ -1
* }
*/
public static int EVFILT_READ() {
return EVFILT_READ;
}
private static final int EVFILT_WRITE = (int)-2L;
/**
* {@snippet lang=c :
* #define EVFILT_WRITE -2
* }
*/
public static int EVFILT_WRITE() {
return EVFILT_WRITE;
}
}

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* Defines native structures for Kqueue socket APIs.
* Generated with the following jextract command:
* {@snippet lang = "Shell Script":
*
* HEADER_NAME=kqueue.h
* echo "#include <sys/types.h> " > $HEADER_NAME
* echo "#include <sys/event.h>" >> $HEADER_NAME
*
*
* jextract --target-package jdk.internal.ffi.generated.kqueue \
* --include-constant EV_CLEAR \
* --include-constant EV_ONESHOT \
* --include-constant EV_DELETE \
* --include-constant EV_ADD \
* --include-constant EVFILT_WRITE \
* --include-constant EVFILT_READ \
* --include-function kevent \
* --include-function kqueue \
* --include-struct kevent \
* $HEADER_NAME
* }
*
*/
package jdk.internal.ffi.generated.kqueue;

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* Defines native structures for timespec APIs.
* Generated with the following jextract command:
* {@snippet lang = "Shell Script":
*
* HEADER_NAME=timespec.h
* echo "#include <time.h>" > $HEADER_NAME
*
* jextract --target-package jdk.internal.ffi.generated.timespec \
* --include-struct timespec \
* $HEADER_NAME
* }
*
*/
package jdk.internal.ffi.generated.timespec;

View file

@ -0,0 +1,194 @@
/*
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// Generated by jextract
package jdk.internal.ffi.generated.timespec;
import java.lang.foreign.*;
import java.util.function.*;
import static java.lang.foreign.MemoryLayout.PathElement.*;
import static java.lang.foreign.ValueLayout.*;
/**
* {@snippet lang=c :
* struct timespec {
* __darwin_time_t tv_sec;
* long tv_nsec;
* }
* }
*/
@SuppressWarnings("restricted")
public class timespec {
timespec() {
// Should not be called directly
}
private static final GroupLayout $LAYOUT = MemoryLayout.structLayout(
timespec_h.C_LONG.withName("tv_sec"),
timespec_h.C_LONG.withName("tv_nsec")
).withName("timespec");
/**
* The layout of this struct
*/
public static final GroupLayout layout() {
return $LAYOUT;
}
private static final OfLong tv_sec$LAYOUT = (OfLong)$LAYOUT.select(groupElement("tv_sec"));
/**
* Layout for field:
* {@snippet lang=c :
* __darwin_time_t tv_sec
* }
*/
public static final OfLong tv_sec$layout() {
return tv_sec$LAYOUT;
}
private static final long tv_sec$OFFSET = $LAYOUT.byteOffset(groupElement("tv_sec"));
/**
* Offset for field:
* {@snippet lang=c :
* __darwin_time_t tv_sec
* }
*/
public static final long tv_sec$offset() {
return tv_sec$OFFSET;
}
/**
* Getter for field:
* {@snippet lang=c :
* __darwin_time_t tv_sec
* }
*/
public static long tv_sec(MemorySegment struct) {
return struct.get(tv_sec$LAYOUT, tv_sec$OFFSET);
}
/**
* Setter for field:
* {@snippet lang=c :
* __darwin_time_t tv_sec
* }
*/
public static void tv_sec(MemorySegment struct, long fieldValue) {
struct.set(tv_sec$LAYOUT, tv_sec$OFFSET, fieldValue);
}
private static final OfLong tv_nsec$LAYOUT = (OfLong)$LAYOUT.select(groupElement("tv_nsec"));
/**
* Layout for field:
* {@snippet lang=c :
* long tv_nsec
* }
*/
public static final OfLong tv_nsec$layout() {
return tv_nsec$LAYOUT;
}
private static final long tv_nsec$OFFSET = $LAYOUT.byteOffset(groupElement("tv_nsec"));
/**
* Offset for field:
* {@snippet lang=c :
* long tv_nsec
* }
*/
public static final long tv_nsec$offset() {
return tv_nsec$OFFSET;
}
/**
* Getter for field:
* {@snippet lang=c :
* long tv_nsec
* }
*/
public static long tv_nsec(MemorySegment struct) {
return struct.get(tv_nsec$LAYOUT, tv_nsec$OFFSET);
}
/**
* Setter for field:
* {@snippet lang=c :
* long tv_nsec
* }
*/
public static void tv_nsec(MemorySegment struct, long fieldValue) {
struct.set(tv_nsec$LAYOUT, tv_nsec$OFFSET, fieldValue);
}
/**
* Obtains a slice of {@code arrayParam} which selects the array element at {@code index}.
* The returned segment has address {@code arrayParam.address() + index * layout().byteSize()}
*/
public static MemorySegment asSlice(MemorySegment array, long index) {
return array.asSlice(layout().byteSize() * index);
}
/**
* The size (in bytes) of this struct
*/
public static long sizeof() { return layout().byteSize(); }
/**
* Allocate a segment of size {@code layout().byteSize()} using {@code allocator}
*/
public static MemorySegment allocate(SegmentAllocator allocator) {
return allocator.allocate(layout());
}
/**
* Allocate an array of size {@code elementCount} using {@code allocator}.
* The returned segment has size {@code elementCount * layout().byteSize()}.
*/
public static MemorySegment allocateArray(long elementCount, SegmentAllocator allocator) {
return allocator.allocate(MemoryLayout.sequenceLayout(elementCount, layout()));
}
/**
* Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any).
* The returned segment has size {@code layout().byteSize()}
*/
public static MemorySegment reinterpret(MemorySegment addr, Arena arena, Consumer<MemorySegment> cleanup) {
return reinterpret(addr, 1, arena, cleanup);
}
/**
* Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any).
* The returned segment has size {@code elementCount * layout().byteSize()}
*/
public static MemorySegment reinterpret(MemorySegment addr, long elementCount, Arena arena, Consumer<MemorySegment> cleanup) {
return addr.reinterpret(layout().byteSize() * elementCount, arena, cleanup);
}
}

View file

@ -0,0 +1,85 @@
/*
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// Generated by jextract
package jdk.internal.ffi.generated.timespec;
import java.lang.foreign.*;
import java.lang.invoke.*;
import java.util.*;
import java.util.stream.*;
import static java.lang.foreign.ValueLayout.*;
@SuppressWarnings("restricted")
public class timespec_h$shared {
timespec_h$shared() {
// Should not be called directly
}
public static final OfBoolean C_BOOL = (OfBoolean) Linker.nativeLinker().canonicalLayouts().get("bool");
public static final OfByte C_CHAR =(OfByte)Linker.nativeLinker().canonicalLayouts().get("char");
public static final OfShort C_SHORT = (OfShort) Linker.nativeLinker().canonicalLayouts().get("short");
public static final OfInt C_INT = (OfInt) Linker.nativeLinker().canonicalLayouts().get("int");
public static final OfLong C_LONG_LONG = (OfLong) Linker.nativeLinker().canonicalLayouts().get("long long");
public static final OfFloat C_FLOAT = (OfFloat) Linker.nativeLinker().canonicalLayouts().get("float");
public static final OfDouble C_DOUBLE = (OfDouble) Linker.nativeLinker().canonicalLayouts().get("double");
public static final AddressLayout C_POINTER = ((AddressLayout) Linker.nativeLinker().canonicalLayouts().get("void*"))
.withTargetLayout(MemoryLayout.sequenceLayout(Long.MAX_VALUE, C_CHAR));
public static final OfLong C_LONG = (OfLong) Linker.nativeLinker().canonicalLayouts().get("long");
static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls");
static void traceDowncall(String name, Object... args) {
String traceArgs = Arrays.stream(args)
.map(Object::toString)
.collect(Collectors.joining(", "));
System.out.printf("%s(%s)\n", name, traceArgs);
}
static MethodHandle upcallHandle(Class<?> fi, String name, FunctionDescriptor fdesc) {
try {
return MethodHandles.lookup().findVirtual(fi, name, fdesc.toMethodType());
} catch (ReflectiveOperationException ex) {
throw new AssertionError(ex);
}
}
static MemoryLayout align(MemoryLayout layout, long align) {
return switch (layout) {
case PaddingLayout p -> p;
case ValueLayout v -> v.withByteAlignment(align);
case GroupLayout g -> {
MemoryLayout[] alignedMembers = g.memberLayouts().stream()
.map(m -> align(m, align)).toArray(MemoryLayout[]::new);
yield g instanceof StructLayout ?
MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers);
}
case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align));
};
}
}

View file

@ -0,0 +1,43 @@
/*
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// Generated by jextract
package jdk.internal.ffi.generated.timespec;
import java.lang.foreign.*;
public class timespec_h extends timespec_h$shared {
timespec_h() {
// Should not be called directly
}
static final Arena LIBRARY_ARENA = Arena.ofAuto();
static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.loaderLookup()
.or(Linker.nativeLinker().defaultLookup());
}

View file

@ -0,0 +1,90 @@
/*
* Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.loader;
import java.io.File;
import java.util.ArrayList;
import jdk.internal.util.OSVersion;
class ClassLoaderHelper {
// SDK 10.15 and earlier always reports 10.16 instead of 11.x.x
private static final boolean hasDynamicLoaderCache = OSVersion.current()
.compareTo(new OSVersion(10, 16)) >= 0;
private ClassLoaderHelper() {}
/**
* Returns true if loading a native library only if
* it's present on the file system.
*
* @implNote
* On macOS 11.x or later which supports dynamic linker cache,
* the dynamic library is not present on the filesystem. The
* library cannot determine if a dynamic library exists on a
* given path or not and so this method returns false.
*/
static boolean loadLibraryOnlyIfPresent() {
return !hasDynamicLoaderCache;
}
/**
* Returns an alternate path name for the given file
* such that if the original pathname did not exist, then the
* file may be located at the alternate location.
* For mac, this replaces the final .dylib suffix with .jnilib
*/
static File mapAlternativeName(File lib) {
String name = lib.toString();
int index = name.lastIndexOf('.');
if (index < 0) {
return null;
}
return new File(name.substring(0, index) + ".jnilib");
}
/**
* Parse a PATH env variable.
*
* Empty elements will be replaced by dot.
*/
static String[] parsePath(String ldPath) {
char ps = File.pathSeparatorChar;
ArrayList<String> paths = new ArrayList<>();
int pathStart = 0;
int pathEnd;
while ((pathEnd = ldPath.indexOf(ps, pathStart)) >= 0) {
paths.add((pathStart < pathEnd) ?
ldPath.substring(pathStart, pathEnd) : ".");
pathStart = pathEnd + 1;
}
int ldLen = ldPath.length();
paths.add((pathStart < ldLen) ?
ldPath.substring(pathStart, ldLen) : ".");
return paths.toArray(new String[paths.size()]);
}
}

View file

@ -0,0 +1,90 @@
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.ch;
import java.nio.channels.*;
import java.nio.channels.spi.AsynchronousChannelProvider;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadFactory;
import java.io.IOException;
public class BsdAsynchronousChannelProvider
extends AsynchronousChannelProvider
{
private static volatile KQueuePort defaultPort;
private KQueuePort defaultEventPort() throws IOException {
if (defaultPort == null) {
synchronized (BsdAsynchronousChannelProvider.class) {
if (defaultPort == null) {
defaultPort = new KQueuePort(this, ThreadPool.getDefault()).start();
}
}
}
return defaultPort;
}
public BsdAsynchronousChannelProvider() {
}
@Override
public AsynchronousChannelGroup openAsynchronousChannelGroup(int nThreads, ThreadFactory factory)
throws IOException
{
return new KQueuePort(this, ThreadPool.create(nThreads, factory)).start();
}
@Override
public AsynchronousChannelGroup openAsynchronousChannelGroup(ExecutorService executor, int initialSize)
throws IOException
{
return new KQueuePort(this, ThreadPool.wrap(executor, initialSize)).start();
}
private Port toPort(AsynchronousChannelGroup group) throws IOException {
if (group == null) {
return defaultEventPort();
} else {
if (!(group instanceof KQueuePort))
throw new IllegalChannelGroupException();
return (Port)group;
}
}
@Override
public AsynchronousServerSocketChannel openAsynchronousServerSocketChannel(AsynchronousChannelGroup group)
throws IOException
{
return new UnixAsynchronousServerSocketChannelImpl(toPort(group));
}
@Override
public AsynchronousSocketChannel openAsynchronousSocketChannel(AsynchronousChannelGroup group)
throws IOException
{
return new UnixAsynchronousSocketChannelImpl(toPort(group));
}
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.ch;
import java.nio.channels.spi.AsynchronousChannelProvider;
/**
* Creates this platform's default AsynchronousChannelProvider
*/
public class DefaultAsynchronousChannelProvider {
/**
* Prevent instantiation.
*/
private DefaultAsynchronousChannelProvider() { }
/**
* Returns the default AsynchronousChannelProvider.
*/
public static AsynchronousChannelProvider create() {
return new BsdAsynchronousChannelProvider();
}
}

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.ch;
import java.io.IOException;
/**
* Default PollerProvider for macOS.
*/
class DefaultPollerProvider extends PollerProvider {
DefaultPollerProvider(Poller.Mode mode) {
super(mode);
}
DefaultPollerProvider() {
this(Poller.Mode.SYSTEM_THREADS);
}
@Override
Poller readPoller(boolean subPoller) throws IOException {
return new KQueuePoller(pollerMode(), subPoller, true);
}
@Override
Poller writePoller(boolean subPoller) throws IOException {
return new KQueuePoller(pollerMode(), subPoller, false);
}
}

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.ch;
/**
* Creates this platform's default SelectorProvider
*/
public class DefaultSelectorProvider {
private static final SelectorProviderImpl INSTANCE = new KQueueSelectorProvider();
/**
* Prevent instantiation.
*/
private DefaultSelectorProvider() { }
/**
* Returns the default SelectorProvider implementation.
*/
public static SelectorProviderImpl get() {
return INSTANCE;
}
}

View file

@ -0,0 +1,57 @@
/*
* Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.ch;
import java.io.FileDescriptor;
import java.io.IOException;
class FileDispatcherImpl extends UnixFileDispatcherImpl {
FileDispatcherImpl() {
super();
}
int force(FileDescriptor fd, boolean metaData) throws IOException {
return force0(fd, metaData);
}
boolean canTransferToFromOverlappedMap() {
return false;
}
long transferTo(FileDescriptor src, long position, long count,
FileDescriptor dst, boolean append) {
return transferTo0(src, position, count, dst, append);
}
// -- Native methods --
static native int force0(FileDescriptor fd, boolean metaData)
throws IOException;
static native long transferTo0(FileDescriptor src, long position,
long count, FileDescriptor dst,
boolean append);
}

View file

@ -0,0 +1,120 @@
/*
* Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.ch;
import java.io.IOException;
import jdk.internal.misc.Unsafe;
/**
* Provides access to the BSD kqueue facility.
*/
class KQueue {
private KQueue() { }
private static final Unsafe unsafe = Unsafe.getUnsafe();
/**
* struct kevent {
* uintptr_t ident; // identifier for this event, usually the fd
* int16_t filter; // filter for event
* uint16_t flags; // general flags
* uint32_t fflags; // filter-specific flags
* intptr_t data; // filter-specific data
* void *udata; // opaque user data identifier
* };
*/
static {
IOUtil.load();
}
private static final int SIZEOF_KQUEUEEVENT = keventSize();
private static final int OFFSET_IDENT = identOffset();
private static final int OFFSET_FILTER = filterOffset();
private static final int OFFSET_FLAGS = flagsOffset();
// filters
static final int EVFILT_READ = -1;
static final int EVFILT_WRITE = -2;
// flags
static final int EV_ADD = 0x0001;
static final int EV_DELETE = 0x0002;
static final int EV_ONESHOT = 0x0010;
static final int EV_CLEAR = 0x0020;
/**
* Allocates a poll array to handle up to {@code count} events.
*/
static long allocatePollArray(int count) {
return unsafe.allocateMemory(count * SIZEOF_KQUEUEEVENT);
}
/**
* Free a poll array
*/
static void freePollArray(long address) {
unsafe.freeMemory(address);
}
/**
* Returns kevent[i].
*/
static long getEvent(long address, int i) {
return address + (SIZEOF_KQUEUEEVENT*i);
}
/**
* Returns the file descriptor from a kevent (assuming it is in the ident field)
*/
static int getDescriptor(long address) {
return unsafe.getInt(address + OFFSET_IDENT);
}
static short getFilter(long address) {
return unsafe.getShort(address + OFFSET_FILTER);
}
static short getFlags(long address) {
return unsafe.getShort(address + OFFSET_FLAGS);
}
// -- Native methods --
private static native int keventSize();
private static native int identOffset();
private static native int filterOffset();
private static native int flagsOffset();
static native int create() throws IOException;
static native int register(int kqfd, int fd, int filter, int flags);
static native int poll(int kqfd, long pollAddress, int nevents, long timeout)
throws IOException;
}

View file

@ -0,0 +1,158 @@
/*
* Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.ch;
import java.io.IOException;
import java.lang.ref.Cleaner.Cleanable;
import jdk.internal.ref.CleanerFactory;
import static sun.nio.ch.KQueue.*;
/**
* Poller implementation based on the kqueue facility.
*/
class KQueuePoller extends Poller {
private final int kqfd;
private final int filter;
private final int maxEvents;
private final long address;
// file descriptors used for wakeup during shutdown
private final int fd0;
private final int fd1;
// close action, and cleaner if this is subpoller
private final Runnable closer;
private final Cleanable cleaner;
KQueuePoller(Poller.Mode mode, boolean subPoller, boolean read) throws IOException {
boolean wakeable = (mode == Mode.POLLER_PER_CARRIER) && subPoller;
int maxEvents = (subPoller) ? 16 : 64;
int kqfd = KQueue.create();
long address = 0L;
int fd0 = -1;
int fd1 = -1;
try {
address = KQueue.allocatePollArray(maxEvents);
// register one end of the pipe with kqueue to allow for wakeup
if (wakeable) {
long fds = IOUtil.makePipe(false);
fd0 = (int) (fds >>> 32);
fd1 = (int) fds;
KQueue.register(kqfd, fd0, EVFILT_READ, EV_ADD);
}
} catch (Throwable e) {
FileDispatcherImpl.closeIntFD(kqfd);
if (address != 0L) KQueue.freePollArray(address);
if (fd0 >= 0) FileDispatcherImpl.closeIntFD(fd0);
if (fd1 >= 0) FileDispatcherImpl.closeIntFD(fd1);
throw e;
}
this.kqfd = kqfd;
this.filter = (read) ? EVFILT_READ : EVFILT_WRITE;
this.maxEvents = maxEvents;
this.address = address;
this.fd0 = fd0;
this.fd1 = fd1;
// create action to close kqueue, register cleaner when wakeable
this.closer = closer(kqfd, address, fd0, fd1);
if (wakeable) {
this.cleaner = CleanerFactory.cleaner().register(this, closer);
} else {
this.cleaner = null;
}
}
/**
* Returns an action to close the kqueue and release other resources.
*/
private static Runnable closer(int kqfd, long address, int fd0, int fd1) {
return () -> {
try {
FileDispatcherImpl.closeIntFD(kqfd);
KQueue.freePollArray(address);
if (fd0 >= 0) FileDispatcherImpl.closeIntFD(fd0);
if (fd1 >= 0) FileDispatcherImpl.closeIntFD(fd1);
} catch (IOException _) { }
};
}
@Override
void close() {
if (cleaner != null) {
cleaner.clean();
} else {
closer.run();
}
}
@Override
int fdVal() {
return kqfd;
}
@Override
void implStartPoll(int fdVal) throws IOException {
int err = KQueue.register(kqfd, fdVal, filter, (EV_ADD|EV_ONESHOT));
if (err != 0)
throw new IOException("kevent failed: " + err);
}
@Override
void implStopPoll(int fdVal, boolean polled) {
// event was deleted if already polled
if (!polled) {
KQueue.register(kqfd, fdVal, filter, EV_DELETE);
}
}
@Override
void wakeupPoller() throws IOException {
if (fd1 < 0) {
throw new UnsupportedOperationException();
}
IOUtil.write1(fd1, (byte)0);
}
@Override
int poll(int timeout) throws IOException {
int n = KQueue.poll(kqfd, address, maxEvents, timeout);
int polled = 0;
int i = 0;
while (i < n) {
long keventAddress = KQueue.getEvent(address, i);
int fdVal = KQueue.getDescriptor(keventAddress);
if (fdVal != fd0) {
polled(fdVal);
polled++;
}
i++;
}
return polled;
}
}

View file

@ -0,0 +1,327 @@
/*
* Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.ch;
import java.nio.channels.spi.AsynchronousChannelProvider;
import java.io.IOException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
import static sun.nio.ch.KQueue.EVFILT_READ;
import static sun.nio.ch.KQueue.EVFILT_WRITE;
import static sun.nio.ch.KQueue.EV_ADD;
import static sun.nio.ch.KQueue.EV_ONESHOT;
/**
* AsynchronousChannelGroup implementation based on the BSD kqueue facility.
*/
final class KQueuePort
extends Port
{
// maximum number of events to poll at a time
private static final int MAX_KEVENTS_TO_POLL = 512;
// kqueue file descriptor
private final int kqfd;
// address of the poll array passed to kqueue_wait
private final long address;
// true if kqueue closed
private boolean closed;
// socket pair used for wakeup
private final int sp[];
// number of wakeups pending
private final AtomicInteger wakeupCount = new AtomicInteger();
// encapsulates an event for a channel
static class Event {
final PollableChannel channel;
final int events;
Event(PollableChannel channel, int events) {
this.channel = channel;
this.events = events;
}
PollableChannel channel() { return channel; }
int events() { return events; }
}
// queue of events for cases that a polling thread dequeues more than one
// event
private final ArrayBlockingQueue<Event> queue;
private final Event NEED_TO_POLL = new Event(null, 0);
private final Event EXECUTE_TASK_OR_SHUTDOWN = new Event(null, 0);
KQueuePort(AsynchronousChannelProvider provider, ThreadPool pool)
throws IOException
{
super(provider, pool);
this.kqfd = KQueue.create();
this.address = KQueue.allocatePollArray(MAX_KEVENTS_TO_POLL);
// create socket pair for wakeup mechanism
try {
long fds = IOUtil.makePipe(true);
this.sp = new int[]{(int) (fds >>> 32), (int) fds};
} catch (IOException ioe) {
KQueue.freePollArray(address);
FileDispatcherImpl.closeIntFD(kqfd);
throw ioe;
}
// register one end with kqueue
KQueue.register(kqfd, sp[0], EVFILT_READ, EV_ADD);
// create the queue and offer the special event to ensure that the first
// threads polls
this.queue = new ArrayBlockingQueue<>(MAX_KEVENTS_TO_POLL);
this.queue.offer(NEED_TO_POLL);
}
KQueuePort start() {
startThreads(new EventHandlerTask());
return this;
}
/**
* Release all resources
*/
private void implClose() {
synchronized (this) {
if (closed)
return;
closed = true;
}
try { FileDispatcherImpl.closeIntFD(kqfd); } catch (IOException ioe) { }
try { FileDispatcherImpl.closeIntFD(sp[0]); } catch (IOException ioe) { }
try { FileDispatcherImpl.closeIntFD(sp[1]); } catch (IOException ioe) { }
KQueue.freePollArray(address);
}
private void wakeup() {
if (wakeupCount.incrementAndGet() == 1) {
// write byte to socketpair to force wakeup
try {
IOUtil.write1(sp[1], (byte)0);
} catch (IOException x) {
throw new AssertionError(x);
}
}
}
@Override
void executeOnHandlerTask(Runnable task) {
synchronized (this) {
if (closed)
throw new RejectedExecutionException();
offerTask(task);
wakeup();
}
}
@Override
void shutdownHandlerTasks() {
/*
* If no tasks are running then just release resources; otherwise
* write to the one end of the socketpair to wakeup any polling threads.
*/
int nThreads = threadCount();
if (nThreads == 0) {
implClose();
} else {
// send wakeup to each thread
while (nThreads-- > 0) {
wakeup();
}
}
}
// invoked by clients to register a file descriptor
@Override
void startPoll(int fd, int events) {
// We use a separate filter for read and write events.
// TBD: Measure cost of EV_ONESHOT vs. EV_CLEAR, either will do here.
int err = 0;
int flags = (EV_ADD|EV_ONESHOT);
if ((events & Net.POLLIN) > 0)
err = KQueue.register(kqfd, fd, EVFILT_READ, flags);
if (err == 0 && (events & Net.POLLOUT) > 0)
err = KQueue.register(kqfd, fd, EVFILT_WRITE, flags);
if (err != 0)
throw new InternalError("kevent failed: " + err); // should not happen
}
/**
* Task to process events from kqueue and dispatch to the channel's
* onEvent handler.
*
* Events are retrieved from kqueue in batch and offered to a BlockingQueue
* where they are consumed by handler threads. A special "NEED_TO_POLL"
* event is used to signal one consumer to re-poll when all events have
* been consumed.
*/
private class EventHandlerTask implements Runnable {
private Event poll() throws IOException {
try {
for (;;) {
int n;
do {
n = KQueue.poll(kqfd, address, MAX_KEVENTS_TO_POLL, -1L);
} while (n == IOStatus.INTERRUPTED);
/**
* 'n' events have been read. Here we map them to their
* corresponding channel in batch and queue n-1 so that
* they can be handled by other handler threads. The last
* event is handled by this thread (and so is not queued).
*/
fdToChannelLock.readLock().lock();
try {
while (n-- > 0) {
long keventAddress = KQueue.getEvent(address, n);
int fd = KQueue.getDescriptor(keventAddress);
// wakeup
if (fd == sp[0]) {
if (wakeupCount.decrementAndGet() == 0) {
// consume one wakeup byte, never more as this
// would interfere with shutdown when there is
// a wakeup byte queued to wake each thread
int nread;
do {
nread = IOUtil.drain1(sp[0]);
} while (nread == IOStatus.INTERRUPTED);
}
// queue special event if there are more events
// to handle.
if (n > 0) {
queue.offer(EXECUTE_TASK_OR_SHUTDOWN);
continue;
}
return EXECUTE_TASK_OR_SHUTDOWN;
}
PollableChannel channel = fdToChannel.get(fd);
if (channel != null) {
int filter = KQueue.getFilter(keventAddress);
int events = 0;
if (filter == EVFILT_READ)
events = Net.POLLIN;
else if (filter == EVFILT_WRITE)
events = Net.POLLOUT;
Event ev = new Event(channel, events);
// n-1 events are queued; This thread handles
// the last one except for the wakeup
if (n > 0) {
queue.offer(ev);
} else {
return ev;
}
}
}
} finally {
fdToChannelLock.readLock().unlock();
}
}
} finally {
// to ensure that some thread will poll when all events have
// been consumed
queue.offer(NEED_TO_POLL);
}
}
public void run() {
Invoker.GroupAndInvokeCount myGroupAndInvokeCount =
Invoker.getGroupAndInvokeCount();
final boolean isPooledThread = (myGroupAndInvokeCount != null);
boolean replaceMe = false;
Event ev;
try {
for (;;) {
// reset invoke count
if (isPooledThread)
myGroupAndInvokeCount.resetInvokeCount();
try {
replaceMe = false;
ev = queue.take();
// no events and this thread has been "selected" to
// poll for more.
if (ev == NEED_TO_POLL) {
try {
ev = poll();
} catch (IOException x) {
x.printStackTrace();
return;
}
}
} catch (InterruptedException x) {
continue;
}
// handle wakeup to execute task or shutdown
if (ev == EXECUTE_TASK_OR_SHUTDOWN) {
Runnable task = pollTask();
if (task == null) {
// shutdown request
return;
}
// run task (may throw error/exception)
replaceMe = true;
task.run();
continue;
}
// process event
try {
ev.channel().onEvent(ev.events(), isPooledThread);
} catch (Error | RuntimeException x) {
replaceMe = true;
throw x;
}
}
} finally {
// last handler to exit when shutdown releases resources
int remaining = threadExit(this, replaceMe);
if (remaining == 0 && isShutdown()) {
implClose();
}
}
}
}
}

View file

@ -0,0 +1,342 @@
/*
* Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.nio.ch;
import java.io.IOException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.spi.SelectorProvider;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import static sun.nio.ch.KQueue.EVFILT_READ;
import static sun.nio.ch.KQueue.EVFILT_WRITE;
import static sun.nio.ch.KQueue.EV_ADD;
import static sun.nio.ch.KQueue.EV_DELETE;
/**
* KQueue based Selector implementation for macOS
*/
class KQueueSelectorImpl extends SelectorImpl {
// maximum number of events to poll in one call to kqueue
private static final int MAX_KEVENTS = 256;
// kqueue file descriptor
private final int kqfd;
// address of poll array (event list) when polling for pending events
private final long pollArrayAddress;
// file descriptors used for interrupt
private final int fd0;
private final int fd1;
// maps file descriptor to selection key, synchronize on selector
private final Map<Integer, SelectionKeyImpl> fdToKey = new HashMap<>();
// pending new registrations/updates, queued by setEventOps
private final Object updateLock = new Object();
private final Deque<SelectionKeyImpl> updateKeys = new ArrayDeque<>();
// interrupt triggering and clearing
private final Object interruptLock = new Object();
private boolean interruptTriggered;
// used by updateSelectedKeys to handle cases where the same file
// descriptor is polled by more than one filter
private int pollCount;
KQueueSelectorImpl(SelectorProvider sp) throws IOException {
super(sp);
this.kqfd = KQueue.create();
this.pollArrayAddress = KQueue.allocatePollArray(MAX_KEVENTS);
try {
long fds = IOUtil.makePipe(false);
this.fd0 = (int) (fds >>> 32);
this.fd1 = (int) fds;
} catch (IOException ioe) {
KQueue.freePollArray(pollArrayAddress);
FileDispatcherImpl.closeIntFD(kqfd);
throw ioe;
}
// register one end of the socket pair for wakeups
KQueue.register(kqfd, fd0, EVFILT_READ, EV_ADD);
}
@Override
protected int doSelect(Consumer<SelectionKey> action, long timeout)
throws IOException
{
assert Thread.holdsLock(this);
long to = Math.min(timeout, Integer.MAX_VALUE); // max kqueue timeout
boolean blocking = (to != 0);
boolean timedPoll = (to > 0);
int numEntries;
processUpdateQueue();
processDeregisterQueue();
if (Thread.currentThread().isVirtual()) {
numEntries = (timedPoll)
? timedPoll(TimeUnit.MILLISECONDS.toNanos(to))
: untimedPoll(blocking);
} else {
try {
begin(blocking);
do {
long startTime = timedPoll ? System.nanoTime() : 0;
numEntries = KQueue.poll(kqfd, pollArrayAddress, MAX_KEVENTS, to);
if (numEntries == IOStatus.INTERRUPTED && timedPoll) {
// timed poll interrupted so need to adjust timeout
long adjust = System.nanoTime() - startTime;
to -= TimeUnit.NANOSECONDS.toMillis(adjust);
if (to <= 0) {
// timeout expired so no retry
numEntries = 0;
}
}
} while (numEntries == IOStatus.INTERRUPTED);
} finally {
end(blocking);
}
}
assert IOStatus.check(numEntries);
processDeregisterQueue();
return processEvents(numEntries, action);
}
/**
* If blocking, parks the current virtual thread until a file descriptor is polled
* or the thread is interrupted.
*/
private int untimedPoll(boolean block) throws IOException {
int numEntries = KQueue.poll(kqfd, pollArrayAddress, MAX_KEVENTS, 0);
if (block) {
while (numEntries == 0 && !Thread.currentThread().isInterrupted()) {
Poller.pollSelector(kqfd, 0);
numEntries = KQueue.poll(kqfd, pollArrayAddress, MAX_KEVENTS, 0);
}
}
return numEntries;
}
/**
* Parks the current virtual thread until a file descriptor is polled, or the thread
* is interrupted, for up to the specified waiting time.
*/
private int timedPoll(long nanos) throws IOException {
long startNanos = System.nanoTime();
int numEntries = KQueue.poll(kqfd, pollArrayAddress, MAX_KEVENTS, 0);
while (numEntries == 0 && !Thread.currentThread().isInterrupted()) {
long remainingNanos = nanos - (System.nanoTime() - startNanos);
if (remainingNanos <= 0) {
// timeout
break;
}
Poller.pollSelector(kqfd, remainingNanos);
numEntries = KQueue.poll(kqfd, pollArrayAddress, MAX_KEVENTS, 0);
}
return numEntries;
}
/**
* Process changes to the interest ops.
*/
private void processUpdateQueue() {
assert Thread.holdsLock(this);
synchronized (updateLock) {
SelectionKeyImpl ski;
while ((ski = updateKeys.pollFirst()) != null) {
if (ski.isValid()) {
int fd = ski.getFDVal();
// add to fdToKey if needed
SelectionKeyImpl previous = fdToKey.putIfAbsent(fd, ski);
assert (previous == null) || (previous == ski);
int newEvents = ski.translateInterestOps();
int registeredEvents = ski.registeredEvents();
// DatagramChannelImpl::disconnect has reset socket
if (ski.getAndClearReset() && registeredEvents != 0) {
KQueue.register(kqfd, fd, EVFILT_READ, EV_DELETE);
registeredEvents = 0;
}
if (newEvents != registeredEvents) {
// add or delete interest in read events
if ((registeredEvents & Net.POLLIN) != 0) {
if ((newEvents & Net.POLLIN) == 0) {
KQueue.register(kqfd, fd, EVFILT_READ, EV_DELETE);
}
} else if ((newEvents & Net.POLLIN) != 0) {
KQueue.register(kqfd, fd, EVFILT_READ, EV_ADD);
}
// add or delete interest in write events
if ((registeredEvents & Net.POLLOUT) != 0) {
if ((newEvents & Net.POLLOUT) == 0) {
KQueue.register(kqfd, fd, EVFILT_WRITE, EV_DELETE);
}
} else if ((newEvents & Net.POLLOUT) != 0) {
KQueue.register(kqfd, fd, EVFILT_WRITE, EV_ADD);
}
ski.registeredEvents(newEvents);
}
}
}
}
}
/**
* Process the polled events.
* If the interrupt fd has been selected, drain it and clear the interrupt.
*/
private int processEvents(int numEntries, Consumer<SelectionKey> action)
throws IOException
{
assert Thread.holdsLock(this);
int numKeysUpdated = 0;
boolean interrupted = false;
// A file descriptor may be registered with kqueue with more than one
// filter and so there may be more than one event for a fd. The poll
// count is incremented here and compared against the SelectionKey's
// "lastPolled" field. This ensures that the ready ops is updated rather
// than replaced when a file descriptor is polled by both the read and
// write filter.
pollCount++;
for (int i = 0; i < numEntries; i++) {
long kevent = KQueue.getEvent(pollArrayAddress, i);
int fd = KQueue.getDescriptor(kevent);
if (fd == fd0) {
interrupted = true;
} else {
SelectionKeyImpl ski = fdToKey.get(fd);
if (ski != null) {
int rOps = 0;
short filter = KQueue.getFilter(kevent);
if (filter == EVFILT_READ) {
rOps |= Net.POLLIN;
} else if (filter == EVFILT_WRITE) {
rOps |= Net.POLLOUT;
}
int updated = processReadyEvents(rOps, ski, action);
if (updated > 0 && ski.lastPolled != pollCount) {
numKeysUpdated++;
ski.lastPolled = pollCount;
}
}
}
}
if (interrupted) {
clearInterrupt();
}
return numKeysUpdated;
}
@Override
protected void implClose() throws IOException {
assert !isOpen();
assert Thread.holdsLock(this);
// prevent further wakeup
synchronized (interruptLock) {
interruptTriggered = true;
}
FileDispatcherImpl.closeIntFD(kqfd);
KQueue.freePollArray(pollArrayAddress);
FileDispatcherImpl.closeIntFD(fd0);
FileDispatcherImpl.closeIntFD(fd1);
}
@Override
protected void implDereg(SelectionKeyImpl ski) throws IOException {
assert !ski.isValid();
assert Thread.holdsLock(this);
int fd = ski.getFDVal();
int registeredEvents = ski.registeredEvents();
if (fdToKey.remove(fd) != null) {
if (registeredEvents != 0) {
if ((registeredEvents & Net.POLLIN) != 0)
KQueue.register(kqfd, fd, EVFILT_READ, EV_DELETE);
if ((registeredEvents & Net.POLLOUT) != 0)
KQueue.register(kqfd, fd, EVFILT_WRITE, EV_DELETE);
ski.registeredEvents(0);
}
} else {
assert registeredEvents == 0;
}
}
@Override
public void setEventOps(SelectionKeyImpl ski) {
synchronized (updateLock) {
updateKeys.addLast(ski);
}
}
@Override
public Selector wakeup() {
synchronized (interruptLock) {
if (!interruptTriggered) {
try {
IOUtil.write1(fd1, (byte)0);
} catch (IOException ioe) {
throw new InternalError(ioe);
}
interruptTriggered = true;
}
}
return this;
}
private void clearInterrupt() throws IOException {
synchronized (interruptLock) {
IOUtil.drain(fd0);
interruptTriggered = false;
}
}
}

Some files were not shown because too many files have changed in this diff Show more