View Javadoc
1   /*
2    * junixsocket
3    *
4    * Copyright 2009-2024 Christian Kohlschütter
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.newsclub.net.unix;
19  
20  import java.io.Closeable;
21  import java.io.FileDescriptor;
22  import java.io.IOException;
23  import java.nio.ByteBuffer;
24  import java.nio.channels.Pipe;
25  import java.nio.channels.spi.SelectorProvider;
26  
27  import com.kohlschutter.annotations.compiletime.SuppressFBWarnings;
28  
29  /**
30   * A {@link Pipe}, natively implemented.
31   *
32   * @author Christian Kohlschütter
33   */
34  public final class AFPipe extends Pipe implements Closeable {
35    static final AFSupplier<Integer> DUMMY_TIMEOUT = () -> 0; // wait forever
36    private final AFCore sourceCore;
37    private final AFCore sinkCore;
38    private final SourceChannel sourceChannel;
39    private final SinkChannel sinkChannel;
40    private final int options;
41  
42    AFPipe(AFSelectorProvider<?> provider, boolean selectable) throws IOException {
43      super();
44  
45      NativeUnixSocket.ensureSupported();
46  
47      this.sourceCore = new AFCore(this, (FileDescriptor) null);
48      this.sinkCore = new AFCore(this, (FileDescriptor) null);
49  
50      boolean isSocket = NativeUnixSocket.initPipe(sourceCore.fd, sinkCore.fd, selectable);
51      this.options = isSocket ? 0 : NativeUnixSocket.OPT_NON_SOCKET;
52  
53      this.sourceChannel = new SourceChannel(provider);
54      this.sinkChannel = new SinkChannel(provider);
55    }
56  
57    @SuppressFBWarnings("EI_EXPOSE_REP")
58    @Override
59    public SourceChannel source() {
60      return sourceChannel;
61    }
62  
63    @SuppressFBWarnings("EI_EXPOSE_REP")
64    @Override
65    public SinkChannel sink() {
66      return sinkChannel;
67    }
68  
69    FileDescriptor sourceFD() {
70      return sourceCore.fd;
71    }
72  
73    FileDescriptor sinkFD() {
74      return sinkCore.fd;
75    }
76  
77    @Override
78    public void close() throws IOException {
79      try { // NOPMD.UseTryWithResources
80        source().close();
81      } finally {
82        sink().close();
83      }
84    }
85  
86    /**
87     * A channel representing the readable end of a {@link Pipe}, with access to the
88     * {@link FileDescriptor}.
89     */
90    public final class SourceChannel extends java.nio.channels.Pipe.SourceChannel implements
91        FileDescriptorAccess {
92      SourceChannel(SelectorProvider provider) {
93        super(provider);
94      }
95  
96      @Override
97      public long read(ByteBuffer[] dsts, int offset, int length) throws IOException {
98        if (length == 0) {
99          return 0;
100       }
101       return read(dsts[offset]);
102     }
103 
104     @Override
105     public long read(ByteBuffer[] dsts) throws IOException {
106       return read(dsts, 0, dsts.length);
107     }
108 
109     @Override
110     public int read(ByteBuffer dst) throws IOException {
111       return sourceCore.read(dst, DUMMY_TIMEOUT, null, options);
112     }
113 
114     @Override
115     protected void implConfigureBlocking(boolean block) throws IOException {
116       sourceCore.implConfigureBlocking(block);
117     }
118 
119     @Override
120     protected void implCloseSelectableChannel() throws IOException {
121       sourceCore.close();
122     }
123 
124     @Override
125     public FileDescriptor getFileDescriptor() throws IOException {
126       return sourceCore.fd;
127     }
128   }
129 
130   /**
131    * A channel representing the writable end of a {@link Pipe}, with access to the
132    * {@link FileDescriptor}.
133    */
134   public final class SinkChannel extends java.nio.channels.Pipe.SinkChannel implements
135       FileDescriptorAccess {
136     SinkChannel(SelectorProvider provider) {
137       super(provider);
138     }
139 
140     @Override
141     public long write(ByteBuffer[] srcs, int offset, int length) throws IOException {
142       if (length == 0) {
143         return 0;
144       }
145       return write(srcs[offset]);
146     }
147 
148     @Override
149     public long write(ByteBuffer[] srcs) throws IOException {
150       return write(srcs, 0, srcs.length);
151     }
152 
153     @Override
154     public int write(ByteBuffer src) throws IOException {
155       return sinkCore.write(src, DUMMY_TIMEOUT, null, options);
156     }
157 
158     @Override
159     protected void implConfigureBlocking(boolean block) throws IOException {
160       sinkCore.implConfigureBlocking(block);
161     }
162 
163     @Override
164     protected void implCloseSelectableChannel() throws IOException {
165       sinkCore.close();
166     }
167 
168     @Override
169     public FileDescriptor getFileDescriptor() throws IOException {
170       return sinkCore.fd;
171     }
172   }
173 
174   /**
175    * Returns the options bitmask that is to be passed to native receive/send calls.
176    *
177    * @return The options.
178    */
179   int getOptions() {
180     return options;
181   }
182 
183   /**
184    * Opens an {@link AFPipe}.
185    *
186    * @return The new pipe
187    * @throws IOException on error.
188    */
189   public static AFPipe open() throws IOException {
190     return AFUNIXSelectorProvider.provider().openPipe();
191   }
192 }