PageRenderTime 28ms CodeModel.GetById 39ms RepoModel.GetById 1ms app.codeStats 0ms

/src/frontend/org/voltdb/utils/DirectIoFileChannel.java

https://github.com/VoltDB/voltdb
Java | 152 lines | 95 code | 18 blank | 39 comment | 12 complexity | 8013ba45ab9032618afddc74382de381 MD5 | raw file
  1. /* This file is part of VoltDB.
  2. * Copyright (C) 2022 Volt Active Data Inc.
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU Affero General Public License as
  6. * published by the Free Software Foundation, either version 3 of the
  7. * License, or (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU Affero General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Affero General Public License
  15. * along with VoltDB. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. package org.voltdb.utils;
  18. import java.io.FileDescriptor;
  19. import java.io.IOException;
  20. import java.lang.reflect.InvocationTargetException;
  21. import java.lang.reflect.Method;
  22. import java.nio.channels.FileChannel;
  23. import java.nio.file.Files;
  24. import java.nio.file.OpenOption;
  25. import java.nio.file.Path;
  26. import java.nio.file.Paths;
  27. import java.nio.file.StandardOpenOption;
  28. import java.util.Set;
  29. import org.voltcore.logging.VoltLogger;
  30. import com.google_voltpatches.common.base.Throwables;
  31. import com.google_voltpatches.common.collect.ImmutableSet;
  32. import com.sun.nio.file.ExtendedOpenOption;
  33. import io.netty.channel.epoll.Epoll;
  34. import io.netty.channel.unix.Errors;
  35. import sun.misc.JavaIOFileDescriptorAccess;
  36. import sun.misc.SharedSecrets;
  37. import sun.nio.ch.FileChannelImpl;
  38. public class DirectIoFileChannel {
  39. private static final VoltLogger s_log = new VoltLogger("HOST");
  40. // Open options for DIRECT IO support. Only exists in java 10+
  41. private static final Set<OpenOption> s_directOpenOptions;
  42. // Shared secret to set the file descriptor
  43. private static final JavaIOFileDescriptorAccess s_fdAccess;
  44. // Method which retrieves the raw byte representation of a unix path
  45. private static final Method s_getPathAsByteArray;
  46. static {
  47. // Little hack to load netty native libraries so that nice IOExceptions can be thrown
  48. Epoll.isAvailable();
  49. Set<OpenOption> openOptions = null;
  50. JavaIOFileDescriptorAccess fdAccess = null;
  51. Method getPathAsByteArray = null;
  52. try {
  53. OpenOption direct = ExtendedOpenOption.valueOf("DIRECT");
  54. openOptions = ImmutableSet.of(StandardOpenOption.WRITE, StandardOpenOption.CREATE, direct);
  55. } catch (IllegalArgumentException e) {
  56. fdAccess = SharedSecrets.getJavaIOFileDescriptorAccess();
  57. try {
  58. Class<?> clazz = PosixAdvise.class.getClassLoader().loadClass("sun.nio.fs.UnixPath");
  59. getPathAsByteArray = clazz.getDeclaredMethod("asByteArray");
  60. getPathAsByteArray.setAccessible(true);
  61. } catch (ClassNotFoundException | NoSuchMethodException e1) {
  62. throw new RuntimeException("Unable to load java classes for direct IO", e1);
  63. }
  64. }
  65. s_directOpenOptions = openOptions;
  66. s_fdAccess = fdAccess;
  67. s_getPathAsByteArray = getPathAsByteArray;
  68. }
  69. /**
  70. * Utility method for testing if directIO is supported for {@code path}. If {@code path} is a directory then a
  71. * temporary file will be created inside of the directory. If {@code path} is not a file it will be created and then
  72. * deleted. If {@code path} is a file then it will be opened and closed.
  73. *
  74. * @param path being tested
  75. * @return {@code true} if directIO is supported
  76. * @throws IllegalArgumentException if {@code path}'s parent directory does not exist or is a non regular file
  77. */
  78. public static boolean supported(Path path) throws IllegalArgumentException {
  79. Path file = path;
  80. boolean delete = true;
  81. if (Files.isDirectory(path)) {
  82. file = Paths.get(path.toString(), "directIOTest-" + System.nanoTime());
  83. } else if (Files.isRegularFile(path)) {
  84. delete = false;
  85. } else if (Files.exists(path)) {
  86. throw new IllegalArgumentException("Path exists but is not a regular file or directory: " + path);
  87. } else if (!Files.isDirectory(path.getParent())) {
  88. throw new IllegalArgumentException("Parent directory does not exist: " + path);
  89. }
  90. try (FileChannel fc = open(file)) {
  91. if (delete) {
  92. Files.delete(file);
  93. }
  94. return true;
  95. } catch (IOException | IllegalArgumentException e) {
  96. if (s_log.isDebugEnabled()) {
  97. s_log.debug("Path does not support direct IO: " + path, e);
  98. }
  99. return false;
  100. }
  101. }
  102. /**
  103. * Open and create a new direct IO file channel if it does not exist. If the file already it will not be truncated
  104. * and file position will be at 0.
  105. *
  106. * @param path to file
  107. * @return {@link FileChannel} opened to {@code path}
  108. * @throws IOException if there was an error while opening
  109. */
  110. public static FileChannel open(Path path) throws IOException {
  111. if (s_directOpenOptions != null) {
  112. return FileChannel.open(path, s_directOpenOptions);
  113. }
  114. // This is a rudimentary approximation of what java does to open a channel with direct IO enabled
  115. try {
  116. int fd = nativeOpen((byte[]) s_getPathAsByteArray.invoke(path));
  117. if (fd < 0) {
  118. // This method is guaranteed to throw so this should never get to the return null
  119. Errors.ioResult(DirectIoFileChannel.class.getName() + ".open(Path)", fd);
  120. return null;
  121. }
  122. FileDescriptor fileDescriptor = new FileDescriptor();
  123. s_fdAccess.set(fileDescriptor, fd);
  124. return FileChannelImpl.open(fileDescriptor, path.toString(), false, true, null);
  125. } catch (IllegalAccessException e) {
  126. throw new RuntimeException(e);
  127. } catch (InvocationTargetException e) {
  128. Throwables.throwIfUnchecked(e.getCause());
  129. throw new RuntimeException(e);
  130. }
  131. }
  132. private static native int nativeOpen(byte[] path);
  133. private DirectIoFileChannel() {}
  134. }